editor.rs

    1#![allow(rustdoc::private_intra_doc_links)]
    2//! This is the place where everything editor-related is stored (data-wise) and displayed (ui-wise).
    3//! The main point of interest in this crate is [`Editor`] type, which is used in every other Zed part as a user input element.
    4//! It comes in different flavors: single line, multiline and a fixed height one.
    5//!
    6//! Editor contains of multiple large submodules:
    7//! * [`element`] — the place where all rendering happens
    8//! * [`display_map`] - chunks up text in the editor into the logical blocks, establishes coordinates and mapping between each of them.
    9//!   Contains all metadata related to text transformations (folds, fake inlay text insertions, soft wraps, tab markup, etc.).
   10//!
   11//! All other submodules and structs are mostly concerned with holding editor data about the way it displays current buffer region(s).
   12//!
   13//! If you're looking to improve Vim mode, you should check out Vim crate that wraps Editor and overrides its behavior.
   14pub mod actions;
   15pub mod blink_manager;
   16mod bracket_colorization;
   17mod clangd_ext;
   18pub mod code_context_menus;
   19pub mod display_map;
   20mod document_colors;
   21mod document_symbols;
   22mod editor_settings;
   23mod element;
   24mod folding_ranges;
   25mod git;
   26mod highlight_matching_bracket;
   27mod hover_links;
   28pub mod hover_popover;
   29mod indent_guides;
   30mod inlays;
   31pub mod items;
   32mod jsx_tag_auto_close;
   33mod linked_editing_ranges;
   34mod lsp_ext;
   35mod mouse_context_menu;
   36pub mod movement;
   37mod persistence;
   38mod rust_analyzer_ext;
   39pub mod scroll;
   40mod selections_collection;
   41pub mod semantic_tokens;
   42mod split;
   43pub mod split_editor_view;
   44pub mod tasks;
   45
   46#[cfg(test)]
   47mod code_completion_tests;
   48#[cfg(test)]
   49mod edit_prediction_tests;
   50#[cfg(test)]
   51mod editor_tests;
   52mod signature_help;
   53#[cfg(any(test, feature = "test-support"))]
   54pub mod test;
   55
   56pub(crate) use actions::*;
   57pub use display_map::{
   58    ChunkRenderer, ChunkRendererContext, DisplayPoint, FoldPlaceholder, HighlightKey,
   59    SemanticTokenHighlight,
   60};
   61pub use edit_prediction_types::Direction;
   62pub use editor_settings::{
   63    CompletionDetailAlignment, CurrentLineHighlight, DiffViewStyle, DocumentColorsRenderMode,
   64    EditorSettings, HideMouseMode, ScrollBeyondLastLine, ScrollbarAxes, SearchSettings,
   65    ShowMinimap,
   66};
   67pub use element::{
   68    CursorLayout, EditorElement, HighlightedRange, HighlightedRangeLine, PointForPosition,
   69    render_breadcrumb_text,
   70};
   71pub use git::blame::BlameRenderer;
   72pub use hover_popover::hover_markdown_style;
   73pub use inlays::Inlay;
   74pub use items::MAX_TAB_TITLE_LEN;
   75pub use lsp::CompletionContext;
   76pub use lsp_ext::lsp_tasks;
   77pub use multi_buffer::{
   78    Anchor, AnchorRangeExt, BufferOffset, ExcerptId, ExcerptRange, MBTextSummary, MultiBuffer,
   79    MultiBufferOffset, MultiBufferOffsetUtf16, MultiBufferSnapshot, PathKey, RowInfo, ToOffset,
   80    ToPoint,
   81};
   82pub use split::{SplittableEditor, ToggleSplitDiff};
   83pub use split_editor_view::SplitEditorView;
   84pub use text::Bias;
   85
   86use ::git::{Restore, blame::BlameEntry, commit::ParsedCommitMessage, status::FileStatus};
   87use aho_corasick::{AhoCorasick, AhoCorasickBuilder, BuildError};
   88use anyhow::{Context as _, Result, anyhow, bail};
   89use blink_manager::BlinkManager;
   90use buffer_diff::DiffHunkStatus;
   91use client::{Collaborator, ParticipantIndex, parse_zed_link};
   92use clock::ReplicaId;
   93use code_context_menus::{
   94    AvailableCodeAction, CodeActionContents, CodeActionsItem, CodeActionsMenu, CodeContextMenu,
   95    CompletionsMenu, ContextMenuOrigin,
   96};
   97use collections::{BTreeMap, HashMap, HashSet, VecDeque};
   98use convert_case::{Case, Casing};
   99use dap::TelemetrySpawnLocation;
  100use display_map::*;
  101use document_colors::LspColorData;
  102use edit_prediction_types::{
  103    EditPredictionDelegate, EditPredictionDelegateHandle, EditPredictionDiscardReason,
  104    EditPredictionGranularity, SuggestionDisplayType,
  105};
  106use editor_settings::{GoToDefinitionFallback, Minimap as MinimapSettings};
  107use element::{AcceptEditPredictionBinding, LineWithInvisibles, PositionMap, layout_line};
  108use futures::{
  109    FutureExt,
  110    future::{self, Shared, join},
  111};
  112use fuzzy::{StringMatch, StringMatchCandidate};
  113use git::blame::{GitBlame, GlobalBlameRenderer};
  114use gpui::{
  115    Action, Animation, AnimationExt, AnyElement, App, AppContext, AsyncWindowContext,
  116    AvailableSpace, Background, Bounds, ClickEvent, ClipboardEntry, ClipboardItem, Context,
  117    DispatchPhase, Edges, Entity, EntityId, EntityInputHandler, EventEmitter, FocusHandle,
  118    FocusOutEvent, Focusable, FontId, FontStyle, FontWeight, Global, HighlightStyle, Hsla,
  119    KeyContext, Modifiers, MouseButton, MouseDownEvent, MouseMoveEvent, PaintQuad, ParentElement,
  120    Pixels, PressureStage, Render, ScrollHandle, SharedString, SharedUri, Size, Stateful, Styled,
  121    Subscription, Task, TextRun, TextStyle, TextStyleRefinement, UTF16Selection, UnderlineStyle,
  122    UniformListScrollHandle, WeakEntity, WeakFocusHandle, Window, div, point, prelude::*,
  123    pulsating_between, px, relative, size,
  124};
  125use hover_links::{HoverLink, HoveredLinkState, find_file};
  126use hover_popover::{HoverState, hide_hover};
  127use indent_guides::ActiveIndentGuidesState;
  128use inlays::{InlaySplice, inlay_hints::InlayHintRefreshReason};
  129use itertools::{Either, Itertools};
  130use language::{
  131    AutoindentMode, BlockCommentConfig, BracketMatch, BracketPair, Buffer, BufferRow,
  132    BufferSnapshot, Capability, CharClassifier, CharKind, CharScopeContext, CodeLabel, CursorShape,
  133    DiagnosticEntryRef, DiffOptions, EditPredictionsMode, EditPreview, HighlightedText, IndentKind,
  134    IndentSize, Language, LanguageName, LanguageRegistry, LanguageScope, OffsetRangeExt,
  135    OutlineItem, Point, Runnable, Selection, SelectionGoal, TextObject, TransactionId,
  136    TreeSitterOptions, WordsQuery,
  137    language_settings::{
  138        self, LanguageSettings, LspInsertMode, RewrapBehavior, WordsCompletionMode,
  139        all_language_settings, language_settings,
  140    },
  141    point_from_lsp, point_to_lsp, text_diff_with_options,
  142};
  143use linked_editing_ranges::refresh_linked_ranges;
  144use lsp::{
  145    CodeActionKind, CompletionItemKind, CompletionTriggerKind, InsertTextFormat, InsertTextMode,
  146    LanguageServerId,
  147};
  148use markdown::Markdown;
  149use mouse_context_menu::MouseContextMenu;
  150use movement::TextLayoutDetails;
  151use multi_buffer::{
  152    ExcerptInfo, ExpandExcerptDirection, MultiBufferDiffHunk, MultiBufferPoint, MultiBufferRow,
  153};
  154use parking_lot::Mutex;
  155use persistence::DB;
  156use project::{
  157    BreakpointWithPosition, CodeAction, Completion, CompletionDisplayOptions, CompletionIntent,
  158    CompletionResponse, CompletionSource, DisableAiSettings, DocumentHighlight, InlayHint, InlayId,
  159    InvalidationStrategy, Location, LocationLink, LspAction, PrepareRenameResponse, Project,
  160    ProjectItem, ProjectPath, ProjectTransaction, TaskSourceKind,
  161    debugger::{
  162        breakpoint_store::{
  163            Breakpoint, BreakpointEditAction, BreakpointSessionState, BreakpointState,
  164            BreakpointStore, BreakpointStoreEvent,
  165        },
  166        session::{Session, SessionEvent},
  167    },
  168    git_store::GitStoreEvent,
  169    lsp_store::{
  170        BufferSemanticTokens, CacheInlayHints, CompletionDocumentation, FormatTrigger,
  171        LspFormatTarget, OpenLspBufferHandle, RefreshForServer,
  172    },
  173    project_settings::{DiagnosticSeverity, GoToDiagnosticSeverityFilter, ProjectSettings},
  174};
  175use rand::seq::SliceRandom;
  176use regex::Regex;
  177use rpc::{ErrorCode, ErrorExt, proto::PeerId};
  178use scroll::{Autoscroll, OngoingScroll, ScrollAnchor, ScrollManager, SharedScrollAnchor};
  179use selections_collection::{MutableSelectionsCollection, SelectionsCollection};
  180use serde::{Deserialize, Serialize};
  181use settings::{
  182    GitGutterSetting, RelativeLineNumbers, Settings, SettingsLocation, SettingsStore,
  183    update_settings_file,
  184};
  185use smallvec::{SmallVec, smallvec};
  186use snippet::Snippet;
  187use std::{
  188    any::{Any, TypeId},
  189    borrow::Cow,
  190    cell::{OnceCell, RefCell},
  191    cmp::{self, Ordering, Reverse},
  192    collections::hash_map,
  193    iter::{self, Peekable},
  194    mem,
  195    num::NonZeroU32,
  196    ops::{ControlFlow, Deref, DerefMut, Not, Range, RangeInclusive},
  197    path::{Path, PathBuf},
  198    rc::Rc,
  199    sync::Arc,
  200    time::{Duration, Instant},
  201};
  202use task::{ResolvedTask, RunnableTag, TaskTemplate, TaskVariables};
  203use text::{BufferId, FromAnchor, OffsetUtf16, Rope, ToOffset as _, ToPoint as _};
  204use theme::{
  205    AccentColors, ActiveTheme, GlobalTheme, PlayerColor, StatusColors, SyntaxTheme, Theme,
  206    ThemeSettings, observe_buffer_font_size_adjustment,
  207};
  208use ui::{
  209    Avatar, ButtonSize, ButtonStyle, ContextMenu, Disclosure, IconButton, IconButtonShape,
  210    IconName, IconSize, Indicator, Key, Tooltip, h_flex, prelude::*, scrollbars::ScrollbarAutoHide,
  211};
  212use ui_input::ErasedEditor;
  213use util::{RangeExt, ResultExt, TryFutureExt, maybe, post_inc};
  214use workspace::{
  215    CollaboratorId, Item as WorkspaceItem, ItemId, ItemNavHistory, NavigationEntry, OpenInTerminal,
  216    OpenTerminal, Pane, RestoreOnStartupBehavior, SERIALIZATION_THROTTLE_TIME, SplitDirection,
  217    TabBarSettings, Toast, ViewId, Workspace, WorkspaceId, WorkspaceSettings,
  218    item::{BreadcrumbText, ItemBufferKind, ItemHandle, PreviewTabsSettings, SaveOptions},
  219    notifications::{DetachAndPromptErr, NotificationId, NotifyTaskExt},
  220    searchable::SearchEvent,
  221};
  222use zed_actions::editor::{MoveDown, MoveUp};
  223
  224use crate::{
  225    code_context_menus::CompletionsMenuSource,
  226    editor_settings::MultiCursorModifier,
  227    hover_links::{find_url, find_url_from_range},
  228    inlays::{
  229        InlineValueCache,
  230        inlay_hints::{LspInlayHintData, inlay_hint_settings},
  231    },
  232    scroll::{ScrollOffset, ScrollPixelOffset},
  233    selections_collection::resolve_selections_wrapping_blocks,
  234    semantic_tokens::SemanticTokenState,
  235    signature_help::{SignatureHelpHiddenBy, SignatureHelpState},
  236};
  237
  238pub const FILE_HEADER_HEIGHT: u32 = 2;
  239pub const BUFFER_HEADER_PADDING: Rems = rems(0.25);
  240pub const MULTI_BUFFER_EXCERPT_HEADER_HEIGHT: u32 = 1;
  241const CURSOR_BLINK_INTERVAL: Duration = Duration::from_millis(500);
  242const MAX_LINE_LEN: usize = 1024;
  243const MIN_NAVIGATION_HISTORY_ROW_DELTA: i64 = 10;
  244const MAX_SELECTION_HISTORY_LEN: usize = 1024;
  245pub(crate) const CURSORS_VISIBLE_FOR: Duration = Duration::from_millis(2000);
  246#[doc(hidden)]
  247pub const CODE_ACTIONS_DEBOUNCE_TIMEOUT: Duration = Duration::from_millis(250);
  248pub const SELECTION_HIGHLIGHT_DEBOUNCE_TIMEOUT: Duration = Duration::from_millis(100);
  249
  250pub(crate) const CODE_ACTION_TIMEOUT: Duration = Duration::from_secs(5);
  251pub(crate) const FORMAT_TIMEOUT: Duration = Duration::from_secs(5);
  252pub(crate) const SCROLL_CENTER_TOP_BOTTOM_DEBOUNCE_TIMEOUT: Duration = Duration::from_secs(1);
  253pub const LSP_REQUEST_DEBOUNCE_TIMEOUT: Duration = Duration::from_millis(50);
  254
  255pub(crate) const EDIT_PREDICTION_KEY_CONTEXT: &str = "edit_prediction";
  256pub(crate) const EDIT_PREDICTION_CONFLICT_KEY_CONTEXT: &str = "edit_prediction_conflict";
  257pub(crate) const MINIMAP_FONT_SIZE: AbsoluteLength = AbsoluteLength::Pixels(px(2.));
  258
  259pub type RenderDiffHunkControlsFn = Arc<
  260    dyn Fn(
  261        u32,
  262        &DiffHunkStatus,
  263        Range<Anchor>,
  264        bool,
  265        Pixels,
  266        &Entity<Editor>,
  267        &mut Window,
  268        &mut App,
  269    ) -> AnyElement,
  270>;
  271
  272enum ReportEditorEvent {
  273    Saved { auto_saved: bool },
  274    EditorOpened,
  275    Closed,
  276}
  277
  278impl ReportEditorEvent {
  279    pub fn event_type(&self) -> &'static str {
  280        match self {
  281            Self::Saved { .. } => "Editor Saved",
  282            Self::EditorOpened => "Editor Opened",
  283            Self::Closed => "Editor Closed",
  284        }
  285    }
  286}
  287
  288pub enum ActiveDebugLine {}
  289pub enum DebugStackFrameLine {}
  290
  291pub enum ConflictsOuter {}
  292pub enum ConflictsOurs {}
  293pub enum ConflictsTheirs {}
  294pub enum ConflictsOursMarker {}
  295pub enum ConflictsTheirsMarker {}
  296
  297pub struct HunkAddedColor;
  298pub struct HunkRemovedColor;
  299
  300#[derive(Debug, Copy, Clone, PartialEq, Eq)]
  301pub enum Navigated {
  302    Yes,
  303    No,
  304}
  305
  306impl Navigated {
  307    pub fn from_bool(yes: bool) -> Navigated {
  308        if yes { Navigated::Yes } else { Navigated::No }
  309    }
  310}
  311
  312#[derive(Debug, Clone, PartialEq, Eq)]
  313enum DisplayDiffHunk {
  314    Folded {
  315        display_row: DisplayRow,
  316    },
  317    Unfolded {
  318        is_created_file: bool,
  319        diff_base_byte_range: Range<usize>,
  320        display_row_range: Range<DisplayRow>,
  321        multi_buffer_range: Range<Anchor>,
  322        status: DiffHunkStatus,
  323        word_diffs: Vec<Range<MultiBufferOffset>>,
  324    },
  325}
  326
  327pub enum HideMouseCursorOrigin {
  328    TypingAction,
  329    MovementAction,
  330}
  331
  332pub fn init(cx: &mut App) {
  333    cx.set_global(GlobalBlameRenderer(Arc::new(())));
  334    cx.set_global(breadcrumbs::RenderBreadcrumbText(render_breadcrumb_text));
  335
  336    workspace::register_project_item::<Editor>(cx);
  337    workspace::FollowableViewRegistry::register::<Editor>(cx);
  338    workspace::register_serializable_item::<Editor>(cx);
  339
  340    cx.observe_new(
  341        |workspace: &mut Workspace, _: Option<&mut Window>, _cx: &mut Context<Workspace>| {
  342            workspace.register_action(Editor::new_file);
  343            workspace.register_action(Editor::new_file_split);
  344            workspace.register_action(Editor::new_file_vertical);
  345            workspace.register_action(Editor::new_file_horizontal);
  346            workspace.register_action(Editor::cancel_language_server_work);
  347            workspace.register_action(Editor::toggle_focus);
  348        },
  349    )
  350    .detach();
  351
  352    cx.on_action(move |_: &workspace::NewFile, cx| {
  353        let app_state = workspace::AppState::global(cx);
  354        if let Some(app_state) = app_state.upgrade() {
  355            workspace::open_new(
  356                Default::default(),
  357                app_state,
  358                cx,
  359                |workspace, window, cx| {
  360                    Editor::new_file(workspace, &Default::default(), window, cx)
  361                },
  362            )
  363            .detach_and_log_err(cx);
  364        }
  365    })
  366    .on_action(move |_: &workspace::NewWindow, cx| {
  367        let app_state = workspace::AppState::global(cx);
  368        if let Some(app_state) = app_state.upgrade() {
  369            workspace::open_new(
  370                Default::default(),
  371                app_state,
  372                cx,
  373                |workspace, window, cx| {
  374                    cx.activate(true);
  375                    Editor::new_file(workspace, &Default::default(), window, cx)
  376                },
  377            )
  378            .detach_and_log_err(cx);
  379        }
  380    });
  381    _ = ui_input::ERASED_EDITOR_FACTORY.set(|window, cx| {
  382        Arc::new(ErasedEditorImpl(
  383            cx.new(|cx| Editor::single_line(window, cx)),
  384        )) as Arc<dyn ErasedEditor>
  385    });
  386    _ = multi_buffer::EXCERPT_CONTEXT_LINES.set(multibuffer_context_lines);
  387}
  388
  389pub fn set_blame_renderer(renderer: impl BlameRenderer + 'static, cx: &mut App) {
  390    cx.set_global(GlobalBlameRenderer(Arc::new(renderer)));
  391}
  392
  393pub trait DiagnosticRenderer {
  394    fn render_group(
  395        &self,
  396        diagnostic_group: Vec<DiagnosticEntryRef<'_, Point>>,
  397        buffer_id: BufferId,
  398        snapshot: EditorSnapshot,
  399        editor: WeakEntity<Editor>,
  400        language_registry: Option<Arc<LanguageRegistry>>,
  401        cx: &mut App,
  402    ) -> Vec<BlockProperties<Anchor>>;
  403
  404    fn render_hover(
  405        &self,
  406        diagnostic_group: Vec<DiagnosticEntryRef<'_, Point>>,
  407        range: Range<Point>,
  408        buffer_id: BufferId,
  409        language_registry: Option<Arc<LanguageRegistry>>,
  410        cx: &mut App,
  411    ) -> Option<Entity<markdown::Markdown>>;
  412
  413    fn open_link(
  414        &self,
  415        editor: &mut Editor,
  416        link: SharedString,
  417        window: &mut Window,
  418        cx: &mut Context<Editor>,
  419    );
  420}
  421
  422pub(crate) struct GlobalDiagnosticRenderer(pub Arc<dyn DiagnosticRenderer>);
  423
  424impl GlobalDiagnosticRenderer {
  425    fn global(cx: &App) -> Option<Arc<dyn DiagnosticRenderer>> {
  426        cx.try_global::<Self>().map(|g| g.0.clone())
  427    }
  428}
  429
  430impl gpui::Global for GlobalDiagnosticRenderer {}
  431pub fn set_diagnostic_renderer(renderer: impl DiagnosticRenderer + 'static, cx: &mut App) {
  432    cx.set_global(GlobalDiagnosticRenderer(Arc::new(renderer)));
  433}
  434
  435pub struct SearchWithinRange;
  436
  437trait InvalidationRegion {
  438    fn ranges(&self) -> &[Range<Anchor>];
  439}
  440
  441#[derive(Clone, Debug, PartialEq)]
  442pub enum SelectPhase {
  443    Begin {
  444        position: DisplayPoint,
  445        add: bool,
  446        click_count: usize,
  447    },
  448    BeginColumnar {
  449        position: DisplayPoint,
  450        reset: bool,
  451        mode: ColumnarMode,
  452        goal_column: u32,
  453    },
  454    Extend {
  455        position: DisplayPoint,
  456        click_count: usize,
  457    },
  458    Update {
  459        position: DisplayPoint,
  460        goal_column: u32,
  461        scroll_delta: gpui::Point<f32>,
  462    },
  463    End,
  464}
  465
  466#[derive(Clone, Debug, PartialEq)]
  467pub enum ColumnarMode {
  468    FromMouse,
  469    FromSelection,
  470}
  471
  472#[derive(Clone, Debug)]
  473pub enum SelectMode {
  474    Character,
  475    Word(Range<Anchor>),
  476    Line(Range<Anchor>),
  477    All,
  478}
  479
  480#[derive(Copy, Clone, Default, PartialEq, Eq, Debug)]
  481pub enum SizingBehavior {
  482    /// The editor will layout itself using `size_full` and will include the vertical
  483    /// scroll margin as requested by user settings.
  484    #[default]
  485    Default,
  486    /// The editor will layout itself using `size_full`, but will not have any
  487    /// vertical overscroll.
  488    ExcludeOverscrollMargin,
  489    /// The editor will request a vertical size according to its content and will be
  490    /// layouted without a vertical scroll margin.
  491    SizeByContent,
  492}
  493
  494#[derive(Clone, PartialEq, Eq, Debug)]
  495pub enum EditorMode {
  496    SingleLine,
  497    AutoHeight {
  498        min_lines: usize,
  499        max_lines: Option<usize>,
  500    },
  501    Full {
  502        /// When set to `true`, the editor will scale its UI elements with the buffer font size.
  503        scale_ui_elements_with_buffer_font_size: bool,
  504        /// When set to `true`, the editor will render a background for the active line.
  505        show_active_line_background: bool,
  506        /// Determines the sizing behavior for this editor
  507        sizing_behavior: SizingBehavior,
  508    },
  509    Minimap {
  510        parent: WeakEntity<Editor>,
  511    },
  512}
  513
  514impl EditorMode {
  515    pub fn full() -> Self {
  516        Self::Full {
  517            scale_ui_elements_with_buffer_font_size: true,
  518            show_active_line_background: true,
  519            sizing_behavior: SizingBehavior::Default,
  520        }
  521    }
  522
  523    #[inline]
  524    pub fn is_full(&self) -> bool {
  525        matches!(self, Self::Full { .. })
  526    }
  527
  528    #[inline]
  529    pub fn is_single_line(&self) -> bool {
  530        matches!(self, Self::SingleLine { .. })
  531    }
  532
  533    #[inline]
  534    fn is_minimap(&self) -> bool {
  535        matches!(self, Self::Minimap { .. })
  536    }
  537}
  538
  539#[derive(Copy, Clone, Debug)]
  540pub enum SoftWrap {
  541    /// Prefer not to wrap at all.
  542    ///
  543    /// Note: this is currently internal, as actually limited by [`crate::MAX_LINE_LEN`] until it wraps.
  544    /// The mode is used inside git diff hunks, where it's seems currently more useful to not wrap as much as possible.
  545    GitDiff,
  546    /// Prefer a single line generally, unless an overly long line is encountered.
  547    None,
  548    /// Soft wrap lines that exceed the editor width.
  549    EditorWidth,
  550    /// Soft wrap lines at the preferred line length.
  551    Column(u32),
  552    /// Soft wrap line at the preferred line length or the editor width (whichever is smaller).
  553    Bounded(u32),
  554}
  555
  556#[derive(Clone)]
  557pub struct EditorStyle {
  558    pub background: Hsla,
  559    pub border: Hsla,
  560    pub local_player: PlayerColor,
  561    pub text: TextStyle,
  562    pub scrollbar_width: Pixels,
  563    pub syntax: Arc<SyntaxTheme>,
  564    pub status: StatusColors,
  565    pub inlay_hints_style: HighlightStyle,
  566    pub edit_prediction_styles: EditPredictionStyles,
  567    pub unnecessary_code_fade: f32,
  568    pub show_underlines: bool,
  569}
  570
  571impl Default for EditorStyle {
  572    fn default() -> Self {
  573        Self {
  574            background: Hsla::default(),
  575            border: Hsla::default(),
  576            local_player: PlayerColor::default(),
  577            text: TextStyle::default(),
  578            scrollbar_width: Pixels::default(),
  579            syntax: Default::default(),
  580            // HACK: Status colors don't have a real default.
  581            // We should look into removing the status colors from the editor
  582            // style and retrieve them directly from the theme.
  583            status: StatusColors::dark(),
  584            inlay_hints_style: HighlightStyle::default(),
  585            edit_prediction_styles: EditPredictionStyles {
  586                insertion: HighlightStyle::default(),
  587                whitespace: HighlightStyle::default(),
  588            },
  589            unnecessary_code_fade: Default::default(),
  590            show_underlines: true,
  591        }
  592    }
  593}
  594
  595pub fn make_inlay_hints_style(cx: &App) -> HighlightStyle {
  596    let show_background = language_settings::language_settings(None, None, cx)
  597        .inlay_hints
  598        .show_background;
  599
  600    let mut style = cx.theme().syntax().get("hint");
  601
  602    if style.color.is_none() {
  603        style.color = Some(cx.theme().status().hint);
  604    }
  605
  606    if !show_background {
  607        style.background_color = None;
  608        return style;
  609    }
  610
  611    if style.background_color.is_none() {
  612        style.background_color = Some(cx.theme().status().hint_background);
  613    }
  614
  615    style
  616}
  617
  618pub fn make_suggestion_styles(cx: &App) -> EditPredictionStyles {
  619    EditPredictionStyles {
  620        insertion: HighlightStyle {
  621            color: Some(cx.theme().status().predictive),
  622            ..HighlightStyle::default()
  623        },
  624        whitespace: HighlightStyle {
  625            background_color: Some(cx.theme().status().created_background),
  626            ..HighlightStyle::default()
  627        },
  628    }
  629}
  630
  631type CompletionId = usize;
  632
  633pub(crate) enum EditDisplayMode {
  634    TabAccept,
  635    DiffPopover,
  636    Inline,
  637}
  638
  639enum EditPrediction {
  640    Edit {
  641        edits: Vec<(Range<Anchor>, Arc<str>)>,
  642        /// Predicted cursor position as (anchor, offset_from_anchor).
  643        /// The anchor is in multibuffer coordinates; after applying edits,
  644        /// resolve the anchor and add the offset to get the final cursor position.
  645        cursor_position: Option<(Anchor, usize)>,
  646        edit_preview: Option<EditPreview>,
  647        display_mode: EditDisplayMode,
  648        snapshot: BufferSnapshot,
  649    },
  650    /// Move to a specific location in the active editor
  651    MoveWithin {
  652        target: Anchor,
  653        snapshot: BufferSnapshot,
  654    },
  655    /// Move to a specific location in a different editor (not the active one)
  656    MoveOutside {
  657        target: language::Anchor,
  658        snapshot: BufferSnapshot,
  659    },
  660}
  661
  662struct EditPredictionState {
  663    inlay_ids: Vec<InlayId>,
  664    completion: EditPrediction,
  665    completion_id: Option<SharedString>,
  666    invalidation_range: Option<Range<Anchor>>,
  667}
  668
  669enum EditPredictionSettings {
  670    Disabled,
  671    Enabled {
  672        show_in_menu: bool,
  673        preview_requires_modifier: bool,
  674    },
  675}
  676
  677#[derive(Debug, Clone)]
  678struct InlineDiagnostic {
  679    message: SharedString,
  680    group_id: usize,
  681    is_primary: bool,
  682    start: Point,
  683    severity: lsp::DiagnosticSeverity,
  684}
  685
  686pub enum MenuEditPredictionsPolicy {
  687    Never,
  688    ByProvider,
  689}
  690
  691pub enum EditPredictionPreview {
  692    /// Modifier is not pressed
  693    Inactive { released_too_fast: bool },
  694    /// Modifier pressed
  695    Active {
  696        since: Instant,
  697        previous_scroll_position: Option<SharedScrollAnchor>,
  698    },
  699}
  700
  701impl EditPredictionPreview {
  702    pub fn released_too_fast(&self) -> bool {
  703        match self {
  704            EditPredictionPreview::Inactive { released_too_fast } => *released_too_fast,
  705            EditPredictionPreview::Active { .. } => false,
  706        }
  707    }
  708
  709    pub fn set_previous_scroll_position(&mut self, scroll_position: Option<SharedScrollAnchor>) {
  710        if let EditPredictionPreview::Active {
  711            previous_scroll_position,
  712            ..
  713        } = self
  714        {
  715            *previous_scroll_position = scroll_position;
  716        }
  717    }
  718}
  719
  720pub struct ContextMenuOptions {
  721    pub min_entries_visible: usize,
  722    pub max_entries_visible: usize,
  723    pub placement: Option<ContextMenuPlacement>,
  724}
  725
  726#[derive(Debug, Clone, PartialEq, Eq)]
  727pub enum ContextMenuPlacement {
  728    Above,
  729    Below,
  730}
  731
  732#[derive(Copy, Clone, Eq, PartialEq, PartialOrd, Ord, Debug, Default)]
  733struct EditorActionId(usize);
  734
  735impl EditorActionId {
  736    pub fn post_inc(&mut self) -> Self {
  737        let answer = self.0;
  738
  739        *self = Self(answer + 1);
  740
  741        Self(answer)
  742    }
  743}
  744
  745// type GetFieldEditorTheme = dyn Fn(&theme::Theme) -> theme::FieldEditor;
  746// type OverrideTextStyle = dyn Fn(&EditorStyle) -> Option<HighlightStyle>;
  747
  748type BackgroundHighlight = (
  749    Arc<dyn Fn(&usize, &Theme) -> Hsla + Send + Sync>,
  750    Arc<[Range<Anchor>]>,
  751);
  752type GutterHighlight = (fn(&App) -> Hsla, Vec<Range<Anchor>>);
  753
  754#[derive(Default)]
  755struct ScrollbarMarkerState {
  756    scrollbar_size: Size<Pixels>,
  757    dirty: bool,
  758    markers: Arc<[PaintQuad]>,
  759    pending_refresh: Option<Task<Result<()>>>,
  760}
  761
  762impl ScrollbarMarkerState {
  763    fn should_refresh(&self, scrollbar_size: Size<Pixels>) -> bool {
  764        self.pending_refresh.is_none() && (self.scrollbar_size != scrollbar_size || self.dirty)
  765    }
  766}
  767
  768#[derive(Clone, Copy, PartialEq, Eq)]
  769pub enum MinimapVisibility {
  770    Disabled,
  771    Enabled {
  772        /// The configuration currently present in the users settings.
  773        setting_configuration: bool,
  774        /// Whether to override the currently set visibility from the users setting.
  775        toggle_override: bool,
  776    },
  777}
  778
  779impl MinimapVisibility {
  780    fn for_mode(mode: &EditorMode, cx: &App) -> Self {
  781        if mode.is_full() {
  782            Self::Enabled {
  783                setting_configuration: EditorSettings::get_global(cx).minimap.minimap_enabled(),
  784                toggle_override: false,
  785            }
  786        } else {
  787            Self::Disabled
  788        }
  789    }
  790
  791    fn hidden(&self) -> Self {
  792        match *self {
  793            Self::Enabled {
  794                setting_configuration,
  795                ..
  796            } => Self::Enabled {
  797                setting_configuration,
  798                toggle_override: setting_configuration,
  799            },
  800            Self::Disabled => Self::Disabled,
  801        }
  802    }
  803
  804    fn disabled(&self) -> bool {
  805        matches!(*self, Self::Disabled)
  806    }
  807
  808    fn settings_visibility(&self) -> bool {
  809        match *self {
  810            Self::Enabled {
  811                setting_configuration,
  812                ..
  813            } => setting_configuration,
  814            _ => false,
  815        }
  816    }
  817
  818    fn visible(&self) -> bool {
  819        match *self {
  820            Self::Enabled {
  821                setting_configuration,
  822                toggle_override,
  823            } => setting_configuration ^ toggle_override,
  824            _ => false,
  825        }
  826    }
  827
  828    fn toggle_visibility(&self) -> Self {
  829        match *self {
  830            Self::Enabled {
  831                toggle_override,
  832                setting_configuration,
  833            } => Self::Enabled {
  834                setting_configuration,
  835                toggle_override: !toggle_override,
  836            },
  837            Self::Disabled => Self::Disabled,
  838        }
  839    }
  840}
  841
  842#[derive(Debug, Clone, Copy, PartialEq, Eq)]
  843pub enum BufferSerialization {
  844    All,
  845    NonDirtyBuffers,
  846}
  847
  848impl BufferSerialization {
  849    fn new(restore_unsaved_buffers: bool) -> Self {
  850        if restore_unsaved_buffers {
  851            Self::All
  852        } else {
  853            Self::NonDirtyBuffers
  854        }
  855    }
  856}
  857
  858#[derive(Clone, Debug)]
  859struct RunnableTasks {
  860    templates: Vec<(TaskSourceKind, TaskTemplate)>,
  861    offset: multi_buffer::Anchor,
  862    // We need the column at which the task context evaluation should take place (when we're spawning it via gutter).
  863    column: u32,
  864    // Values of all named captures, including those starting with '_'
  865    extra_variables: HashMap<String, String>,
  866    // Full range of the tagged region. We use it to determine which `extra_variables` to grab for context resolution in e.g. a modal.
  867    context_range: Range<BufferOffset>,
  868}
  869
  870impl RunnableTasks {
  871    fn resolve<'a>(
  872        &'a self,
  873        cx: &'a task::TaskContext,
  874    ) -> impl Iterator<Item = (TaskSourceKind, ResolvedTask)> + 'a {
  875        self.templates.iter().filter_map(|(kind, template)| {
  876            template
  877                .resolve_task(&kind.to_id_base(), cx)
  878                .map(|task| (kind.clone(), task))
  879        })
  880    }
  881}
  882
  883#[derive(Clone)]
  884pub struct ResolvedTasks {
  885    templates: SmallVec<[(TaskSourceKind, ResolvedTask); 1]>,
  886    position: Anchor,
  887}
  888
  889/// Addons allow storing per-editor state in other crates (e.g. Vim)
  890pub trait Addon: 'static {
  891    fn extend_key_context(&self, _: &mut KeyContext, _: &App) {}
  892
  893    fn render_buffer_header_controls(
  894        &self,
  895        _: &ExcerptInfo,
  896        _: &Window,
  897        _: &App,
  898    ) -> Option<AnyElement> {
  899        None
  900    }
  901
  902    fn override_status_for_buffer_id(&self, _: BufferId, _: &App) -> Option<FileStatus> {
  903        None
  904    }
  905
  906    fn to_any(&self) -> &dyn std::any::Any;
  907
  908    fn to_any_mut(&mut self) -> Option<&mut dyn std::any::Any> {
  909        None
  910    }
  911}
  912
  913struct ChangeLocation {
  914    current: Option<Vec<Anchor>>,
  915    original: Vec<Anchor>,
  916}
  917impl ChangeLocation {
  918    fn locations(&self) -> &[Anchor] {
  919        self.current.as_ref().unwrap_or(&self.original)
  920    }
  921}
  922
  923/// A set of caret positions, registered when the editor was edited.
  924pub struct ChangeList {
  925    changes: Vec<ChangeLocation>,
  926    /// Currently "selected" change.
  927    position: Option<usize>,
  928}
  929
  930impl ChangeList {
  931    pub fn new() -> Self {
  932        Self {
  933            changes: Vec::new(),
  934            position: None,
  935        }
  936    }
  937
  938    /// Moves to the next change in the list (based on the direction given) and returns the caret positions for the next change.
  939    /// If reaches the end of the list in the direction, returns the corresponding change until called for a different direction.
  940    pub fn next_change(&mut self, count: usize, direction: Direction) -> Option<&[Anchor]> {
  941        if self.changes.is_empty() {
  942            return None;
  943        }
  944
  945        let prev = self.position.unwrap_or(self.changes.len());
  946        let next = if direction == Direction::Prev {
  947            prev.saturating_sub(count)
  948        } else {
  949            (prev + count).min(self.changes.len() - 1)
  950        };
  951        self.position = Some(next);
  952        self.changes.get(next).map(|change| change.locations())
  953    }
  954
  955    /// Adds a new change to the list, resetting the change list position.
  956    pub fn push_to_change_list(&mut self, group: bool, new_positions: Vec<Anchor>) {
  957        self.position.take();
  958        if let Some(last) = self.changes.last_mut()
  959            && group
  960        {
  961            last.current = Some(new_positions)
  962        } else {
  963            self.changes.push(ChangeLocation {
  964                original: new_positions,
  965                current: None,
  966            });
  967        }
  968    }
  969
  970    pub fn last(&self) -> Option<&[Anchor]> {
  971        self.changes.last().map(|change| change.locations())
  972    }
  973
  974    pub fn last_before_grouping(&self) -> Option<&[Anchor]> {
  975        self.changes.last().map(|change| change.original.as_slice())
  976    }
  977
  978    pub fn invert_last_group(&mut self) {
  979        if let Some(last) = self.changes.last_mut()
  980            && let Some(current) = last.current.as_mut()
  981        {
  982            mem::swap(&mut last.original, current);
  983        }
  984    }
  985}
  986
  987#[derive(Clone)]
  988struct InlineBlamePopoverState {
  989    scroll_handle: ScrollHandle,
  990    commit_message: Option<ParsedCommitMessage>,
  991    markdown: Entity<Markdown>,
  992}
  993
  994struct InlineBlamePopover {
  995    position: gpui::Point<Pixels>,
  996    hide_task: Option<Task<()>>,
  997    popover_bounds: Option<Bounds<Pixels>>,
  998    popover_state: InlineBlamePopoverState,
  999    keyboard_grace: bool,
 1000}
 1001
 1002enum SelectionDragState {
 1003    /// State when no drag related activity is detected.
 1004    None,
 1005    /// State when the mouse is down on a selection that is about to be dragged.
 1006    ReadyToDrag {
 1007        selection: Selection<Anchor>,
 1008        click_position: gpui::Point<Pixels>,
 1009        mouse_down_time: Instant,
 1010    },
 1011    /// State when the mouse is dragging the selection in the editor.
 1012    Dragging {
 1013        selection: Selection<Anchor>,
 1014        drop_cursor: Selection<Anchor>,
 1015        hide_drop_cursor: bool,
 1016    },
 1017}
 1018
 1019enum ColumnarSelectionState {
 1020    FromMouse {
 1021        selection_tail: Anchor,
 1022        display_point: Option<DisplayPoint>,
 1023    },
 1024    FromSelection {
 1025        selection_tail: Anchor,
 1026    },
 1027}
 1028
 1029/// Represents a breakpoint indicator that shows up when hovering over lines in the gutter that don't have
 1030/// a breakpoint on them.
 1031#[derive(Clone, Copy, Debug, PartialEq, Eq)]
 1032struct PhantomBreakpointIndicator {
 1033    display_row: DisplayRow,
 1034    /// There's a small debounce between hovering over the line and showing the indicator.
 1035    /// We don't want to show the indicator when moving the mouse from editor to e.g. project panel.
 1036    is_active: bool,
 1037    collides_with_existing_breakpoint: bool,
 1038}
 1039
 1040/// Represents a diff review button indicator that shows up when hovering over lines in the gutter
 1041/// in diff view mode.
 1042#[derive(Clone, Copy, Debug, PartialEq, Eq)]
 1043pub(crate) struct PhantomDiffReviewIndicator {
 1044    /// The starting anchor of the selection (or the only row if not dragging).
 1045    pub start: Anchor,
 1046    /// The ending anchor of the selection. Equal to start_anchor for single-line selection.
 1047    pub end: Anchor,
 1048    /// There's a small debounce between hovering over the line and showing the indicator.
 1049    /// We don't want to show the indicator when moving the mouse from editor to e.g. project panel.
 1050    pub is_active: bool,
 1051}
 1052
 1053#[derive(Clone, Debug)]
 1054pub(crate) struct DiffReviewDragState {
 1055    pub start_anchor: Anchor,
 1056    pub current_anchor: Anchor,
 1057}
 1058
 1059impl DiffReviewDragState {
 1060    pub fn row_range(&self, snapshot: &DisplaySnapshot) -> std::ops::RangeInclusive<DisplayRow> {
 1061        let start = self.start_anchor.to_display_point(snapshot).row();
 1062        let current = self.current_anchor.to_display_point(snapshot).row();
 1063
 1064        (start..=current).sorted()
 1065    }
 1066}
 1067
 1068/// Identifies a specific hunk in the diff buffer.
 1069/// Used as a key to group comments by their location.
 1070#[derive(Clone, Debug)]
 1071pub struct DiffHunkKey {
 1072    /// The file path (relative to worktree) this hunk belongs to.
 1073    pub file_path: Arc<util::rel_path::RelPath>,
 1074    /// An anchor at the start of the hunk. This tracks position as the buffer changes.
 1075    pub hunk_start_anchor: Anchor,
 1076}
 1077
 1078/// A review comment stored locally before being sent to the Agent panel.
 1079#[derive(Clone)]
 1080pub struct StoredReviewComment {
 1081    /// Unique identifier for this comment (for edit/delete operations).
 1082    pub id: usize,
 1083    /// The comment text entered by the user.
 1084    pub comment: String,
 1085    /// Anchors for the code range being reviewed.
 1086    pub range: Range<Anchor>,
 1087    /// Timestamp when the comment was created (for chronological ordering).
 1088    pub created_at: Instant,
 1089    /// Whether this comment is currently being edited inline.
 1090    pub is_editing: bool,
 1091}
 1092
 1093impl StoredReviewComment {
 1094    pub fn new(id: usize, comment: String, anchor_range: Range<Anchor>) -> Self {
 1095        Self {
 1096            id,
 1097            comment,
 1098            range: anchor_range,
 1099            created_at: Instant::now(),
 1100            is_editing: false,
 1101        }
 1102    }
 1103}
 1104
 1105/// Represents an active diff review overlay that appears when clicking the "Add Review" button.
 1106pub(crate) struct DiffReviewOverlay {
 1107    pub anchor_range: Range<Anchor>,
 1108    /// The block ID for the overlay.
 1109    pub block_id: CustomBlockId,
 1110    /// The editor entity for the review input.
 1111    pub prompt_editor: Entity<Editor>,
 1112    /// The hunk key this overlay belongs to.
 1113    pub hunk_key: DiffHunkKey,
 1114    /// Whether the comments section is expanded.
 1115    pub comments_expanded: bool,
 1116    /// Editors for comments currently being edited inline.
 1117    /// Key: comment ID, Value: Editor entity for inline editing.
 1118    pub inline_edit_editors: HashMap<usize, Entity<Editor>>,
 1119    /// Subscriptions for inline edit editors' action handlers.
 1120    /// Key: comment ID, Value: Subscription keeping the Newline action handler alive.
 1121    pub inline_edit_subscriptions: HashMap<usize, Subscription>,
 1122    /// The current user's avatar URI for display in comment rows.
 1123    pub user_avatar_uri: Option<SharedUri>,
 1124    /// Subscription to keep the action handler alive.
 1125    _subscription: Subscription,
 1126}
 1127
 1128/// Zed's primary implementation of text input, allowing users to edit a [`MultiBuffer`].
 1129///
 1130/// See the [module level documentation](self) for more information.
 1131pub struct Editor {
 1132    focus_handle: FocusHandle,
 1133    last_focused_descendant: Option<WeakFocusHandle>,
 1134    /// The text buffer being edited
 1135    buffer: Entity<MultiBuffer>,
 1136    /// Map of how text in the buffer should be displayed.
 1137    /// Handles soft wraps, folds, fake inlay text insertions, etc.
 1138    pub display_map: Entity<DisplayMap>,
 1139    placeholder_display_map: Option<Entity<DisplayMap>>,
 1140    pub selections: SelectionsCollection,
 1141    pub scroll_manager: ScrollManager,
 1142    /// When inline assist editors are linked, they all render cursors because
 1143    /// typing enters text into each of them, even the ones that aren't focused.
 1144    pub(crate) show_cursor_when_unfocused: bool,
 1145    columnar_selection_state: Option<ColumnarSelectionState>,
 1146    add_selections_state: Option<AddSelectionsState>,
 1147    select_next_state: Option<SelectNextState>,
 1148    select_prev_state: Option<SelectNextState>,
 1149    selection_history: SelectionHistory,
 1150    defer_selection_effects: bool,
 1151    deferred_selection_effects_state: Option<DeferredSelectionEffectsState>,
 1152    autoclose_regions: Vec<AutocloseRegion>,
 1153    snippet_stack: InvalidationStack<SnippetState>,
 1154    select_syntax_node_history: SelectSyntaxNodeHistory,
 1155    ime_transaction: Option<TransactionId>,
 1156    pub diagnostics_max_severity: DiagnosticSeverity,
 1157    active_diagnostics: ActiveDiagnostic,
 1158    show_inline_diagnostics: bool,
 1159    inline_diagnostics_update: Task<()>,
 1160    inline_diagnostics_enabled: bool,
 1161    diagnostics_enabled: bool,
 1162    word_completions_enabled: bool,
 1163    inline_diagnostics: Vec<(Anchor, InlineDiagnostic)>,
 1164    soft_wrap_mode_override: Option<language_settings::SoftWrap>,
 1165    hard_wrap: Option<usize>,
 1166    project: Option<Entity<Project>>,
 1167    semantics_provider: Option<Rc<dyn SemanticsProvider>>,
 1168    completion_provider: Option<Rc<dyn CompletionProvider>>,
 1169    collaboration_hub: Option<Box<dyn CollaborationHub>>,
 1170    blink_manager: Entity<BlinkManager>,
 1171    show_cursor_names: bool,
 1172    hovered_cursors: HashMap<HoveredCursor, Task<()>>,
 1173    pub show_local_selections: bool,
 1174    mode: EditorMode,
 1175    show_breadcrumbs: bool,
 1176    show_gutter: bool,
 1177    show_scrollbars: ScrollbarAxes,
 1178    minimap_visibility: MinimapVisibility,
 1179    offset_content: bool,
 1180    disable_expand_excerpt_buttons: bool,
 1181    delegate_expand_excerpts: bool,
 1182    delegate_stage_and_restore: bool,
 1183    delegate_open_excerpts: bool,
 1184    enable_lsp_data: bool,
 1185    enable_runnables: bool,
 1186    show_line_numbers: Option<bool>,
 1187    use_relative_line_numbers: Option<bool>,
 1188    show_git_diff_gutter: Option<bool>,
 1189    show_code_actions: Option<bool>,
 1190    show_runnables: Option<bool>,
 1191    show_breakpoints: Option<bool>,
 1192    show_diff_review_button: bool,
 1193    show_wrap_guides: Option<bool>,
 1194    show_indent_guides: Option<bool>,
 1195    buffers_with_disabled_indent_guides: HashSet<BufferId>,
 1196    highlight_order: usize,
 1197    highlighted_rows: HashMap<TypeId, Vec<RowHighlight>>,
 1198    background_highlights: HashMap<HighlightKey, BackgroundHighlight>,
 1199    gutter_highlights: HashMap<TypeId, GutterHighlight>,
 1200    scrollbar_marker_state: ScrollbarMarkerState,
 1201    active_indent_guides_state: ActiveIndentGuidesState,
 1202    nav_history: Option<ItemNavHistory>,
 1203    context_menu: RefCell<Option<CodeContextMenu>>,
 1204    context_menu_options: Option<ContextMenuOptions>,
 1205    mouse_context_menu: Option<MouseContextMenu>,
 1206    completion_tasks: Vec<(CompletionId, Task<()>)>,
 1207    inline_blame_popover: Option<InlineBlamePopover>,
 1208    inline_blame_popover_show_task: Option<Task<()>>,
 1209    signature_help_state: SignatureHelpState,
 1210    auto_signature_help: Option<bool>,
 1211    find_all_references_task_sources: Vec<Anchor>,
 1212    next_completion_id: CompletionId,
 1213    available_code_actions: Option<(Location, Rc<[AvailableCodeAction]>)>,
 1214    code_actions_task: Option<Task<Result<()>>>,
 1215    quick_selection_highlight_task: Option<(Range<Anchor>, Task<()>)>,
 1216    debounced_selection_highlight_task: Option<(Range<Anchor>, Task<()>)>,
 1217    debounced_selection_highlight_complete: bool,
 1218    document_highlights_task: Option<Task<()>>,
 1219    linked_editing_range_task: Option<Task<Option<()>>>,
 1220    linked_edit_ranges: linked_editing_ranges::LinkedEditingRanges,
 1221    pending_rename: Option<RenameState>,
 1222    searchable: bool,
 1223    cursor_shape: CursorShape,
 1224    /// Whether the cursor is offset one character to the left when something is
 1225    /// selected (needed for vim visual mode)
 1226    cursor_offset_on_selection: bool,
 1227    current_line_highlight: Option<CurrentLineHighlight>,
 1228    /// Whether to collapse search match ranges to just their start position.
 1229    /// When true, navigating to a match positions the cursor at the match
 1230    /// without selecting the matched text.
 1231    collapse_matches: bool,
 1232    autoindent_mode: Option<AutoindentMode>,
 1233    workspace: Option<(WeakEntity<Workspace>, Option<WorkspaceId>)>,
 1234    input_enabled: bool,
 1235    use_modal_editing: bool,
 1236    read_only: bool,
 1237    leader_id: Option<CollaboratorId>,
 1238    remote_id: Option<ViewId>,
 1239    pub hover_state: HoverState,
 1240    pending_mouse_down: Option<Rc<RefCell<Option<MouseDownEvent>>>>,
 1241    prev_pressure_stage: Option<PressureStage>,
 1242    gutter_hovered: bool,
 1243    hovered_link_state: Option<HoveredLinkState>,
 1244    edit_prediction_provider: Option<RegisteredEditPredictionDelegate>,
 1245    code_action_providers: Vec<Rc<dyn CodeActionProvider>>,
 1246    active_edit_prediction: Option<EditPredictionState>,
 1247    /// Used to prevent flickering as the user types while the menu is open
 1248    stale_edit_prediction_in_menu: Option<EditPredictionState>,
 1249    edit_prediction_settings: EditPredictionSettings,
 1250    edit_predictions_hidden_for_vim_mode: bool,
 1251    show_edit_predictions_override: Option<bool>,
 1252    show_completions_on_input_override: Option<bool>,
 1253    menu_edit_predictions_policy: MenuEditPredictionsPolicy,
 1254    edit_prediction_preview: EditPredictionPreview,
 1255    edit_prediction_indent_conflict: bool,
 1256    edit_prediction_requires_modifier_in_indent_conflict: bool,
 1257    next_inlay_id: usize,
 1258    next_color_inlay_id: usize,
 1259    _subscriptions: Vec<Subscription>,
 1260    pixel_position_of_newest_cursor: Option<gpui::Point<Pixels>>,
 1261    gutter_dimensions: GutterDimensions,
 1262    style: Option<EditorStyle>,
 1263    text_style_refinement: Option<TextStyleRefinement>,
 1264    next_editor_action_id: EditorActionId,
 1265    editor_actions: Rc<
 1266        RefCell<BTreeMap<EditorActionId, Box<dyn Fn(&Editor, &mut Window, &mut Context<Self>)>>>,
 1267    >,
 1268    use_autoclose: bool,
 1269    use_auto_surround: bool,
 1270    auto_replace_emoji_shortcode: bool,
 1271    jsx_tag_auto_close_enabled_in_any_buffer: bool,
 1272    show_git_blame_gutter: bool,
 1273    show_git_blame_inline: bool,
 1274    show_git_blame_inline_delay_task: Option<Task<()>>,
 1275    git_blame_inline_enabled: bool,
 1276    render_diff_hunk_controls: RenderDiffHunkControlsFn,
 1277    buffer_serialization: Option<BufferSerialization>,
 1278    show_selection_menu: Option<bool>,
 1279    blame: Option<Entity<GitBlame>>,
 1280    blame_subscription: Option<Subscription>,
 1281    custom_context_menu: Option<
 1282        Box<
 1283            dyn 'static
 1284                + Fn(
 1285                    &mut Self,
 1286                    DisplayPoint,
 1287                    &mut Window,
 1288                    &mut Context<Self>,
 1289                ) -> Option<Entity<ui::ContextMenu>>,
 1290        >,
 1291    >,
 1292    last_bounds: Option<Bounds<Pixels>>,
 1293    last_position_map: Option<Rc<PositionMap>>,
 1294    expect_bounds_change: Option<Bounds<Pixels>>,
 1295    tasks: BTreeMap<(BufferId, BufferRow), RunnableTasks>,
 1296    tasks_update_task: Option<Task<()>>,
 1297    breakpoint_store: Option<Entity<BreakpointStore>>,
 1298    gutter_breakpoint_indicator: (Option<PhantomBreakpointIndicator>, Option<Task<()>>),
 1299    pub(crate) gutter_diff_review_indicator: (Option<PhantomDiffReviewIndicator>, Option<Task<()>>),
 1300    pub(crate) diff_review_drag_state: Option<DiffReviewDragState>,
 1301    /// Active diff review overlays. Multiple overlays can be open simultaneously
 1302    /// when hunks have comments stored.
 1303    pub(crate) diff_review_overlays: Vec<DiffReviewOverlay>,
 1304    /// Stored review comments grouped by hunk.
 1305    /// Uses a Vec instead of HashMap because DiffHunkKey contains an Anchor
 1306    /// which doesn't implement Hash/Eq in a way suitable for HashMap keys.
 1307    stored_review_comments: Vec<(DiffHunkKey, Vec<StoredReviewComment>)>,
 1308    /// Counter for generating unique comment IDs.
 1309    next_review_comment_id: usize,
 1310    hovered_diff_hunk_row: Option<DisplayRow>,
 1311    pull_diagnostics_task: Task<()>,
 1312    in_project_search: bool,
 1313    previous_search_ranges: Option<Arc<[Range<Anchor>]>>,
 1314    breadcrumb_header: Option<String>,
 1315    focused_block: Option<FocusedBlock>,
 1316    next_scroll_position: NextScrollCursorCenterTopBottom,
 1317    addons: HashMap<TypeId, Box<dyn Addon>>,
 1318    registered_buffers: HashMap<BufferId, OpenLspBufferHandle>,
 1319    load_diff_task: Option<Shared<Task<()>>>,
 1320    /// Whether we are temporarily displaying a diff other than git's
 1321    temporary_diff_override: bool,
 1322    selection_mark_mode: bool,
 1323    toggle_fold_multiple_buffers: Task<()>,
 1324    _scroll_cursor_center_top_bottom_task: Task<()>,
 1325    serialize_selections: Task<()>,
 1326    serialize_folds: Task<()>,
 1327    mouse_cursor_hidden: bool,
 1328    minimap: Option<Entity<Self>>,
 1329    hide_mouse_mode: HideMouseMode,
 1330    pub change_list: ChangeList,
 1331    inline_value_cache: InlineValueCache,
 1332    number_deleted_lines: bool,
 1333
 1334    selection_drag_state: SelectionDragState,
 1335    colors: Option<LspColorData>,
 1336    post_scroll_update: Task<()>,
 1337    refresh_colors_task: Task<()>,
 1338    use_document_folding_ranges: bool,
 1339    refresh_folding_ranges_task: Task<()>,
 1340    inlay_hints: Option<LspInlayHintData>,
 1341    folding_newlines: Task<()>,
 1342    select_next_is_case_sensitive: Option<bool>,
 1343    pub lookup_key: Option<Box<dyn Any + Send + Sync>>,
 1344    on_local_selections_changed:
 1345        Option<Box<dyn Fn(Point, &mut Window, &mut Context<Self>) + 'static>>,
 1346    suppress_selection_callback: bool,
 1347    applicable_language_settings: HashMap<Option<LanguageName>, LanguageSettings>,
 1348    accent_data: Option<AccentData>,
 1349    fetched_tree_sitter_chunks: HashMap<ExcerptId, HashSet<Range<BufferRow>>>,
 1350    semantic_token_state: SemanticTokenState,
 1351    pub(crate) refresh_matching_bracket_highlights_task: Task<()>,
 1352    refresh_document_symbols_task: Shared<Task<()>>,
 1353    lsp_document_symbols: HashMap<BufferId, Vec<OutlineItem<text::Anchor>>>,
 1354    refresh_outline_symbols_at_cursor_at_cursor_task: Task<()>,
 1355    outline_symbols_at_cursor: Option<(BufferId, Vec<OutlineItem<Anchor>>)>,
 1356    sticky_headers_task: Task<()>,
 1357    sticky_headers: Option<Vec<OutlineItem<Anchor>>>,
 1358}
 1359
 1360#[derive(Debug, PartialEq)]
 1361struct AccentData {
 1362    colors: AccentColors,
 1363    overrides: Vec<SharedString>,
 1364}
 1365
 1366fn debounce_value(debounce_ms: u64) -> Option<Duration> {
 1367    if debounce_ms > 0 {
 1368        Some(Duration::from_millis(debounce_ms))
 1369    } else {
 1370        None
 1371    }
 1372}
 1373
 1374#[derive(Copy, Clone, Debug, PartialEq, Eq, Default)]
 1375enum NextScrollCursorCenterTopBottom {
 1376    #[default]
 1377    Center,
 1378    Top,
 1379    Bottom,
 1380}
 1381
 1382impl NextScrollCursorCenterTopBottom {
 1383    fn next(&self) -> Self {
 1384        match self {
 1385            Self::Center => Self::Top,
 1386            Self::Top => Self::Bottom,
 1387            Self::Bottom => Self::Center,
 1388        }
 1389    }
 1390}
 1391
 1392#[derive(Clone)]
 1393pub struct EditorSnapshot {
 1394    pub mode: EditorMode,
 1395    show_gutter: bool,
 1396    offset_content: bool,
 1397    show_line_numbers: Option<bool>,
 1398    number_deleted_lines: bool,
 1399    show_git_diff_gutter: Option<bool>,
 1400    show_code_actions: Option<bool>,
 1401    show_runnables: Option<bool>,
 1402    show_breakpoints: Option<bool>,
 1403    git_blame_gutter_max_author_length: Option<usize>,
 1404    pub display_snapshot: DisplaySnapshot,
 1405    pub placeholder_display_snapshot: Option<DisplaySnapshot>,
 1406    is_focused: bool,
 1407    scroll_anchor: SharedScrollAnchor,
 1408    ongoing_scroll: OngoingScroll,
 1409    current_line_highlight: CurrentLineHighlight,
 1410    gutter_hovered: bool,
 1411    semantic_tokens_enabled: bool,
 1412}
 1413
 1414#[derive(Default, Debug, Clone, Copy)]
 1415pub struct GutterDimensions {
 1416    pub left_padding: Pixels,
 1417    pub right_padding: Pixels,
 1418    pub width: Pixels,
 1419    pub margin: Pixels,
 1420    pub git_blame_entries_width: Option<Pixels>,
 1421}
 1422
 1423impl GutterDimensions {
 1424    fn default_with_margin(font_id: FontId, font_size: Pixels, cx: &App) -> Self {
 1425        Self {
 1426            margin: Self::default_gutter_margin(font_id, font_size, cx),
 1427            ..Default::default()
 1428        }
 1429    }
 1430
 1431    fn default_gutter_margin(font_id: FontId, font_size: Pixels, cx: &App) -> Pixels {
 1432        -cx.text_system().descent(font_id, font_size)
 1433    }
 1434    /// The full width of the space taken up by the gutter.
 1435    pub fn full_width(&self) -> Pixels {
 1436        self.margin + self.width
 1437    }
 1438
 1439    /// The width of the space reserved for the fold indicators,
 1440    /// use alongside 'justify_end' and `gutter_width` to
 1441    /// right align content with the line numbers
 1442    pub fn fold_area_width(&self) -> Pixels {
 1443        self.margin + self.right_padding
 1444    }
 1445}
 1446
 1447struct CharacterDimensions {
 1448    em_width: Pixels,
 1449    em_advance: Pixels,
 1450    line_height: Pixels,
 1451}
 1452
 1453#[derive(Debug)]
 1454pub struct RemoteSelection {
 1455    pub replica_id: ReplicaId,
 1456    pub selection: Selection<Anchor>,
 1457    pub cursor_shape: CursorShape,
 1458    pub collaborator_id: CollaboratorId,
 1459    pub line_mode: bool,
 1460    pub user_name: Option<SharedString>,
 1461    pub color: PlayerColor,
 1462}
 1463
 1464#[derive(Clone, Debug)]
 1465struct SelectionHistoryEntry {
 1466    selections: Arc<[Selection<Anchor>]>,
 1467    select_next_state: Option<SelectNextState>,
 1468    select_prev_state: Option<SelectNextState>,
 1469    add_selections_state: Option<AddSelectionsState>,
 1470}
 1471
 1472#[derive(Copy, Clone, Default, Debug, PartialEq, Eq)]
 1473enum SelectionHistoryMode {
 1474    #[default]
 1475    Normal,
 1476    Undoing,
 1477    Redoing,
 1478    Skipping,
 1479}
 1480
 1481#[derive(Clone, PartialEq, Eq, Hash)]
 1482struct HoveredCursor {
 1483    replica_id: ReplicaId,
 1484    selection_id: usize,
 1485}
 1486
 1487#[derive(Debug)]
 1488/// SelectionEffects controls the side-effects of updating the selection.
 1489///
 1490/// The default behaviour does "what you mostly want":
 1491/// - it pushes to the nav history if the cursor moved by >10 lines
 1492/// - it re-triggers completion requests
 1493/// - it scrolls to fit
 1494///
 1495/// You might want to modify these behaviours. For example when doing a "jump"
 1496/// like go to definition, we always want to add to nav history; but when scrolling
 1497/// in vim mode we never do.
 1498///
 1499/// Similarly, you might want to disable scrolling if you don't want the viewport to
 1500/// move.
 1501#[derive(Clone)]
 1502pub struct SelectionEffects {
 1503    nav_history: Option<bool>,
 1504    completions: bool,
 1505    scroll: Option<Autoscroll>,
 1506}
 1507
 1508impl Default for SelectionEffects {
 1509    fn default() -> Self {
 1510        Self {
 1511            nav_history: None,
 1512            completions: true,
 1513            scroll: Some(Autoscroll::fit()),
 1514        }
 1515    }
 1516}
 1517impl SelectionEffects {
 1518    pub fn scroll(scroll: Autoscroll) -> Self {
 1519        Self {
 1520            scroll: Some(scroll),
 1521            ..Default::default()
 1522        }
 1523    }
 1524
 1525    pub fn no_scroll() -> Self {
 1526        Self {
 1527            scroll: None,
 1528            ..Default::default()
 1529        }
 1530    }
 1531
 1532    pub fn completions(self, completions: bool) -> Self {
 1533        Self {
 1534            completions,
 1535            ..self
 1536        }
 1537    }
 1538
 1539    pub fn nav_history(self, nav_history: bool) -> Self {
 1540        Self {
 1541            nav_history: Some(nav_history),
 1542            ..self
 1543        }
 1544    }
 1545}
 1546
 1547struct DeferredSelectionEffectsState {
 1548    changed: bool,
 1549    effects: SelectionEffects,
 1550    old_cursor_position: Anchor,
 1551    history_entry: SelectionHistoryEntry,
 1552}
 1553
 1554#[derive(Default)]
 1555struct SelectionHistory {
 1556    #[allow(clippy::type_complexity)]
 1557    selections_by_transaction:
 1558        HashMap<TransactionId, (Arc<[Selection<Anchor>]>, Option<Arc<[Selection<Anchor>]>>)>,
 1559    mode: SelectionHistoryMode,
 1560    undo_stack: VecDeque<SelectionHistoryEntry>,
 1561    redo_stack: VecDeque<SelectionHistoryEntry>,
 1562}
 1563
 1564impl SelectionHistory {
 1565    #[track_caller]
 1566    fn insert_transaction(
 1567        &mut self,
 1568        transaction_id: TransactionId,
 1569        selections: Arc<[Selection<Anchor>]>,
 1570    ) {
 1571        if selections.is_empty() {
 1572            log::error!(
 1573                "SelectionHistory::insert_transaction called with empty selections. Caller: {}",
 1574                std::panic::Location::caller()
 1575            );
 1576            return;
 1577        }
 1578        self.selections_by_transaction
 1579            .insert(transaction_id, (selections, None));
 1580    }
 1581
 1582    #[allow(clippy::type_complexity)]
 1583    fn transaction(
 1584        &self,
 1585        transaction_id: TransactionId,
 1586    ) -> Option<&(Arc<[Selection<Anchor>]>, Option<Arc<[Selection<Anchor>]>>)> {
 1587        self.selections_by_transaction.get(&transaction_id)
 1588    }
 1589
 1590    #[allow(clippy::type_complexity)]
 1591    fn transaction_mut(
 1592        &mut self,
 1593        transaction_id: TransactionId,
 1594    ) -> Option<&mut (Arc<[Selection<Anchor>]>, Option<Arc<[Selection<Anchor>]>>)> {
 1595        self.selections_by_transaction.get_mut(&transaction_id)
 1596    }
 1597
 1598    fn push(&mut self, entry: SelectionHistoryEntry) {
 1599        if !entry.selections.is_empty() {
 1600            match self.mode {
 1601                SelectionHistoryMode::Normal => {
 1602                    self.push_undo(entry);
 1603                    self.redo_stack.clear();
 1604                }
 1605                SelectionHistoryMode::Undoing => self.push_redo(entry),
 1606                SelectionHistoryMode::Redoing => self.push_undo(entry),
 1607                SelectionHistoryMode::Skipping => {}
 1608            }
 1609        }
 1610    }
 1611
 1612    fn push_undo(&mut self, entry: SelectionHistoryEntry) {
 1613        if self
 1614            .undo_stack
 1615            .back()
 1616            .is_none_or(|e| e.selections != entry.selections)
 1617        {
 1618            self.undo_stack.push_back(entry);
 1619            if self.undo_stack.len() > MAX_SELECTION_HISTORY_LEN {
 1620                self.undo_stack.pop_front();
 1621            }
 1622        }
 1623    }
 1624
 1625    fn push_redo(&mut self, entry: SelectionHistoryEntry) {
 1626        if self
 1627            .redo_stack
 1628            .back()
 1629            .is_none_or(|e| e.selections != entry.selections)
 1630        {
 1631            self.redo_stack.push_back(entry);
 1632            if self.redo_stack.len() > MAX_SELECTION_HISTORY_LEN {
 1633                self.redo_stack.pop_front();
 1634            }
 1635        }
 1636    }
 1637}
 1638
 1639#[derive(Clone, Copy)]
 1640pub struct RowHighlightOptions {
 1641    pub autoscroll: bool,
 1642    pub include_gutter: bool,
 1643}
 1644
 1645impl Default for RowHighlightOptions {
 1646    fn default() -> Self {
 1647        Self {
 1648            autoscroll: Default::default(),
 1649            include_gutter: true,
 1650        }
 1651    }
 1652}
 1653
 1654struct RowHighlight {
 1655    index: usize,
 1656    range: Range<Anchor>,
 1657    color: Hsla,
 1658    options: RowHighlightOptions,
 1659    type_id: TypeId,
 1660}
 1661
 1662#[derive(Clone, Debug)]
 1663struct AddSelectionsState {
 1664    groups: Vec<AddSelectionsGroup>,
 1665}
 1666
 1667#[derive(Clone, Debug)]
 1668struct AddSelectionsGroup {
 1669    above: bool,
 1670    stack: Vec<usize>,
 1671}
 1672
 1673#[derive(Clone)]
 1674struct SelectNextState {
 1675    query: AhoCorasick,
 1676    wordwise: bool,
 1677    done: bool,
 1678}
 1679
 1680impl std::fmt::Debug for SelectNextState {
 1681    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
 1682        f.debug_struct(std::any::type_name::<Self>())
 1683            .field("wordwise", &self.wordwise)
 1684            .field("done", &self.done)
 1685            .finish()
 1686    }
 1687}
 1688
 1689#[derive(Debug)]
 1690struct AutocloseRegion {
 1691    selection_id: usize,
 1692    range: Range<Anchor>,
 1693    pair: BracketPair,
 1694}
 1695
 1696#[derive(Debug)]
 1697struct SnippetState {
 1698    ranges: Vec<Vec<Range<Anchor>>>,
 1699    active_index: usize,
 1700    choices: Vec<Option<Vec<String>>>,
 1701}
 1702
 1703#[doc(hidden)]
 1704pub struct RenameState {
 1705    pub range: Range<Anchor>,
 1706    pub old_name: Arc<str>,
 1707    pub editor: Entity<Editor>,
 1708    block_id: CustomBlockId,
 1709}
 1710
 1711struct InvalidationStack<T>(Vec<T>);
 1712
 1713struct RegisteredEditPredictionDelegate {
 1714    provider: Arc<dyn EditPredictionDelegateHandle>,
 1715    _subscription: Subscription,
 1716}
 1717
 1718#[derive(Debug, PartialEq, Eq)]
 1719pub struct ActiveDiagnosticGroup {
 1720    pub active_range: Range<Anchor>,
 1721    pub active_message: String,
 1722    pub group_id: usize,
 1723    pub blocks: HashSet<CustomBlockId>,
 1724}
 1725
 1726#[derive(Debug, PartialEq, Eq)]
 1727
 1728pub(crate) enum ActiveDiagnostic {
 1729    None,
 1730    All,
 1731    Group(ActiveDiagnosticGroup),
 1732}
 1733
 1734#[derive(Serialize, Deserialize, Clone, Debug)]
 1735pub struct ClipboardSelection {
 1736    /// The number of bytes in this selection.
 1737    pub len: usize,
 1738    /// Whether this was a full-line selection.
 1739    pub is_entire_line: bool,
 1740    /// The indentation of the first line when this content was originally copied.
 1741    pub first_line_indent: u32,
 1742    #[serde(default)]
 1743    pub file_path: Option<PathBuf>,
 1744    #[serde(default)]
 1745    pub line_range: Option<RangeInclusive<u32>>,
 1746}
 1747
 1748impl ClipboardSelection {
 1749    pub fn for_buffer(
 1750        len: usize,
 1751        is_entire_line: bool,
 1752        range: Range<Point>,
 1753        buffer: &MultiBufferSnapshot,
 1754        project: Option<&Entity<Project>>,
 1755        cx: &App,
 1756    ) -> Self {
 1757        let first_line_indent = buffer
 1758            .indent_size_for_line(MultiBufferRow(range.start.row))
 1759            .len;
 1760
 1761        let file_path = util::maybe!({
 1762            let project = project?.read(cx);
 1763            let file = buffer.file_at(range.start)?;
 1764            let project_path = ProjectPath {
 1765                worktree_id: file.worktree_id(cx),
 1766                path: file.path().clone(),
 1767            };
 1768            project.absolute_path(&project_path, cx)
 1769        });
 1770
 1771        let line_range = file_path.as_ref().and_then(|_| {
 1772            let (_, start_point, start_excerpt_id) = buffer.point_to_buffer_point(range.start)?;
 1773            let (_, end_point, end_excerpt_id) = buffer.point_to_buffer_point(range.end)?;
 1774            if start_excerpt_id == end_excerpt_id {
 1775                Some(start_point.row..=end_point.row)
 1776            } else {
 1777                None
 1778            }
 1779        });
 1780
 1781        Self {
 1782            len,
 1783            is_entire_line,
 1784            first_line_indent,
 1785            file_path,
 1786            line_range,
 1787        }
 1788    }
 1789}
 1790
 1791// selections, scroll behavior, was newest selection reversed
 1792type SelectSyntaxNodeHistoryState = (
 1793    Box<[Selection<MultiBufferOffset>]>,
 1794    SelectSyntaxNodeScrollBehavior,
 1795    bool,
 1796);
 1797
 1798#[derive(Default)]
 1799struct SelectSyntaxNodeHistory {
 1800    stack: Vec<SelectSyntaxNodeHistoryState>,
 1801    // disable temporarily to allow changing selections without losing the stack
 1802    pub disable_clearing: bool,
 1803}
 1804
 1805impl SelectSyntaxNodeHistory {
 1806    pub fn try_clear(&mut self) {
 1807        if !self.disable_clearing {
 1808            self.stack.clear();
 1809        }
 1810    }
 1811
 1812    pub fn push(&mut self, selection: SelectSyntaxNodeHistoryState) {
 1813        self.stack.push(selection);
 1814    }
 1815
 1816    pub fn pop(&mut self) -> Option<SelectSyntaxNodeHistoryState> {
 1817        self.stack.pop()
 1818    }
 1819}
 1820
 1821enum SelectSyntaxNodeScrollBehavior {
 1822    CursorTop,
 1823    FitSelection,
 1824    CursorBottom,
 1825}
 1826
 1827#[derive(Debug, Clone, Copy)]
 1828pub(crate) struct NavigationData {
 1829    cursor_anchor: Anchor,
 1830    cursor_position: Point,
 1831    scroll_anchor: ScrollAnchor,
 1832    scroll_top_row: u32,
 1833}
 1834
 1835#[derive(Debug, Clone, Copy, PartialEq, Eq)]
 1836pub enum GotoDefinitionKind {
 1837    Symbol,
 1838    Declaration,
 1839    Type,
 1840    Implementation,
 1841}
 1842
 1843pub enum FormatTarget {
 1844    Buffers(HashSet<Entity<Buffer>>),
 1845    Ranges(Vec<Range<MultiBufferPoint>>),
 1846}
 1847
 1848pub(crate) struct FocusedBlock {
 1849    id: BlockId,
 1850    focus_handle: WeakFocusHandle,
 1851}
 1852
 1853#[derive(Clone, Debug)]
 1854pub enum JumpData {
 1855    MultiBufferRow {
 1856        row: MultiBufferRow,
 1857        line_offset_from_top: u32,
 1858    },
 1859    MultiBufferPoint {
 1860        excerpt_id: ExcerptId,
 1861        position: Point,
 1862        anchor: text::Anchor,
 1863        line_offset_from_top: u32,
 1864    },
 1865}
 1866
 1867pub enum MultibufferSelectionMode {
 1868    First,
 1869    All,
 1870}
 1871
 1872#[derive(Clone, Copy, Debug, Default)]
 1873pub struct RewrapOptions {
 1874    pub override_language_settings: bool,
 1875    pub preserve_existing_whitespace: bool,
 1876}
 1877
 1878impl Editor {
 1879    pub fn single_line(window: &mut Window, cx: &mut Context<Self>) -> Self {
 1880        let buffer = cx.new(|cx| Buffer::local("", cx));
 1881        let buffer = cx.new(|cx| MultiBuffer::singleton(buffer, cx));
 1882        Self::new(EditorMode::SingleLine, buffer, None, window, cx)
 1883    }
 1884
 1885    pub fn multi_line(window: &mut Window, cx: &mut Context<Self>) -> Self {
 1886        let buffer = cx.new(|cx| Buffer::local("", cx));
 1887        let buffer = cx.new(|cx| MultiBuffer::singleton(buffer, cx));
 1888        Self::new(EditorMode::full(), buffer, None, window, cx)
 1889    }
 1890
 1891    pub fn auto_height(
 1892        min_lines: usize,
 1893        max_lines: usize,
 1894        window: &mut Window,
 1895        cx: &mut Context<Self>,
 1896    ) -> Self {
 1897        let buffer = cx.new(|cx| Buffer::local("", cx));
 1898        let buffer = cx.new(|cx| MultiBuffer::singleton(buffer, cx));
 1899        Self::new(
 1900            EditorMode::AutoHeight {
 1901                min_lines,
 1902                max_lines: Some(max_lines),
 1903            },
 1904            buffer,
 1905            None,
 1906            window,
 1907            cx,
 1908        )
 1909    }
 1910
 1911    /// Creates a new auto-height editor with a minimum number of lines but no maximum.
 1912    /// The editor grows as tall as needed to fit its content.
 1913    pub fn auto_height_unbounded(
 1914        min_lines: usize,
 1915        window: &mut Window,
 1916        cx: &mut Context<Self>,
 1917    ) -> Self {
 1918        let buffer = cx.new(|cx| Buffer::local("", cx));
 1919        let buffer = cx.new(|cx| MultiBuffer::singleton(buffer, cx));
 1920        Self::new(
 1921            EditorMode::AutoHeight {
 1922                min_lines,
 1923                max_lines: None,
 1924            },
 1925            buffer,
 1926            None,
 1927            window,
 1928            cx,
 1929        )
 1930    }
 1931
 1932    pub fn for_buffer(
 1933        buffer: Entity<Buffer>,
 1934        project: Option<Entity<Project>>,
 1935        window: &mut Window,
 1936        cx: &mut Context<Self>,
 1937    ) -> Self {
 1938        let buffer = cx.new(|cx| MultiBuffer::singleton(buffer, cx));
 1939        Self::new(EditorMode::full(), buffer, project, window, cx)
 1940    }
 1941
 1942    pub fn for_multibuffer(
 1943        buffer: Entity<MultiBuffer>,
 1944        project: Option<Entity<Project>>,
 1945        window: &mut Window,
 1946        cx: &mut Context<Self>,
 1947    ) -> Self {
 1948        Self::new(EditorMode::full(), buffer, project, window, cx)
 1949    }
 1950
 1951    pub fn clone(&self, window: &mut Window, cx: &mut Context<Self>) -> Self {
 1952        let mut clone = Self::new(
 1953            self.mode.clone(),
 1954            self.buffer.clone(),
 1955            self.project.clone(),
 1956            window,
 1957            cx,
 1958        );
 1959        let my_snapshot = self.display_map.update(cx, |display_map, cx| {
 1960            let snapshot = display_map.snapshot(cx);
 1961            clone.display_map.update(cx, |display_map, cx| {
 1962                display_map.set_state(&snapshot, cx);
 1963            });
 1964            snapshot
 1965        });
 1966        let clone_snapshot = clone.display_map.update(cx, |map, cx| map.snapshot(cx));
 1967        clone.folds_did_change(cx);
 1968        clone.selections.clone_state(&self.selections);
 1969        clone
 1970            .scroll_manager
 1971            .clone_state(&self.scroll_manager, &my_snapshot, &clone_snapshot, cx);
 1972        clone.searchable = self.searchable;
 1973        clone.read_only = self.read_only;
 1974        clone
 1975    }
 1976
 1977    pub fn new(
 1978        mode: EditorMode,
 1979        buffer: Entity<MultiBuffer>,
 1980        project: Option<Entity<Project>>,
 1981        window: &mut Window,
 1982        cx: &mut Context<Self>,
 1983    ) -> Self {
 1984        Editor::new_internal(mode, buffer, project, None, window, cx)
 1985    }
 1986
 1987    pub fn refresh_sticky_headers(
 1988        &mut self,
 1989        display_snapshot: &DisplaySnapshot,
 1990        cx: &mut Context<Editor>,
 1991    ) {
 1992        if !self.mode.is_full() {
 1993            return;
 1994        }
 1995        let multi_buffer = display_snapshot.buffer_snapshot();
 1996        let scroll_anchor = self
 1997            .scroll_manager
 1998            .native_anchor(display_snapshot, cx)
 1999            .anchor;
 2000        let Some((excerpt_id, _, buffer)) = multi_buffer.as_singleton() else {
 2001            return;
 2002        };
 2003        let buffer = buffer.clone();
 2004
 2005        let buffer_visible_start = scroll_anchor.text_anchor.to_point(&buffer);
 2006        let max_row = buffer.max_point().row;
 2007        let start_row = buffer_visible_start.row.min(max_row);
 2008        let end_row = (buffer_visible_start.row + 10).min(max_row);
 2009
 2010        let syntax = self.style(cx).syntax.clone();
 2011        let background_task = cx.background_spawn(async move {
 2012            buffer
 2013                .outline_items_containing(
 2014                    Point::new(start_row, 0)..Point::new(end_row, 0),
 2015                    true,
 2016                    Some(syntax.as_ref()),
 2017                )
 2018                .into_iter()
 2019                .map(|outline_item| OutlineItem {
 2020                    depth: outline_item.depth,
 2021                    range: Anchor::range_in_buffer(excerpt_id, outline_item.range),
 2022                    source_range_for_text: Anchor::range_in_buffer(
 2023                        excerpt_id,
 2024                        outline_item.source_range_for_text,
 2025                    ),
 2026                    text: outline_item.text,
 2027                    highlight_ranges: outline_item.highlight_ranges,
 2028                    name_ranges: outline_item.name_ranges,
 2029                    body_range: outline_item
 2030                        .body_range
 2031                        .map(|range| Anchor::range_in_buffer(excerpt_id, range)),
 2032                    annotation_range: outline_item
 2033                        .annotation_range
 2034                        .map(|range| Anchor::range_in_buffer(excerpt_id, range)),
 2035                })
 2036                .collect()
 2037        });
 2038        self.sticky_headers_task = cx.spawn(async move |this, cx| {
 2039            let sticky_headers = background_task.await;
 2040            this.update(cx, |this, cx| {
 2041                this.sticky_headers = Some(sticky_headers);
 2042                cx.notify();
 2043            })
 2044            .ok();
 2045        });
 2046    }
 2047
 2048    fn new_internal(
 2049        mode: EditorMode,
 2050        multi_buffer: Entity<MultiBuffer>,
 2051        project: Option<Entity<Project>>,
 2052        display_map: Option<Entity<DisplayMap>>,
 2053        window: &mut Window,
 2054        cx: &mut Context<Self>,
 2055    ) -> Self {
 2056        debug_assert!(
 2057            display_map.is_none() || mode.is_minimap(),
 2058            "Providing a display map for a new editor is only intended for the minimap and might have unintended side effects otherwise!"
 2059        );
 2060
 2061        let full_mode = mode.is_full();
 2062        let is_minimap = mode.is_minimap();
 2063        let diagnostics_max_severity = if full_mode {
 2064            EditorSettings::get_global(cx)
 2065                .diagnostics_max_severity
 2066                .unwrap_or(DiagnosticSeverity::Hint)
 2067        } else {
 2068            DiagnosticSeverity::Off
 2069        };
 2070        let style = window.text_style();
 2071        let font_size = style.font_size.to_pixels(window.rem_size());
 2072        let editor = cx.entity().downgrade();
 2073        let fold_placeholder = FoldPlaceholder {
 2074            constrain_width: false,
 2075            render: Arc::new(move |fold_id, fold_range, cx| {
 2076                let editor = editor.clone();
 2077                FoldPlaceholder::fold_element(fold_id, cx)
 2078                    .cursor_pointer()
 2079                    .child("")
 2080                    .on_mouse_down(MouseButton::Left, |_, _, cx| cx.stop_propagation())
 2081                    .on_click(move |_, _window, cx| {
 2082                        editor
 2083                            .update(cx, |editor, cx| {
 2084                                editor.unfold_ranges(
 2085                                    &[fold_range.start..fold_range.end],
 2086                                    true,
 2087                                    false,
 2088                                    cx,
 2089                                );
 2090                                cx.stop_propagation();
 2091                            })
 2092                            .ok();
 2093                    })
 2094                    .into_any()
 2095            }),
 2096            merge_adjacent: true,
 2097            ..FoldPlaceholder::default()
 2098        };
 2099        let display_map = display_map.unwrap_or_else(|| {
 2100            cx.new(|cx| {
 2101                DisplayMap::new(
 2102                    multi_buffer.clone(),
 2103                    style.font(),
 2104                    font_size,
 2105                    None,
 2106                    FILE_HEADER_HEIGHT,
 2107                    MULTI_BUFFER_EXCERPT_HEADER_HEIGHT,
 2108                    fold_placeholder,
 2109                    diagnostics_max_severity,
 2110                    cx,
 2111                )
 2112            })
 2113        });
 2114
 2115        let selections = SelectionsCollection::new();
 2116
 2117        let blink_manager = cx.new(|cx| {
 2118            let mut blink_manager = BlinkManager::new(
 2119                CURSOR_BLINK_INTERVAL,
 2120                |cx| EditorSettings::get_global(cx).cursor_blink,
 2121                cx,
 2122            );
 2123            if is_minimap {
 2124                blink_manager.disable(cx);
 2125            }
 2126            blink_manager
 2127        });
 2128
 2129        let soft_wrap_mode_override =
 2130            matches!(mode, EditorMode::SingleLine).then(|| language_settings::SoftWrap::None);
 2131
 2132        let mut project_subscriptions = Vec::new();
 2133        if full_mode && let Some(project) = project.as_ref() {
 2134            project_subscriptions.push(cx.subscribe_in(
 2135                project,
 2136                window,
 2137                |editor, _, event, window, cx| match event {
 2138                    project::Event::RefreshCodeLens => {
 2139                        // we always query lens with actions, without storing them, always refreshing them
 2140                    }
 2141                    project::Event::RefreshInlayHints {
 2142                        server_id,
 2143                        request_id,
 2144                    } => {
 2145                        editor.refresh_inlay_hints(
 2146                            InlayHintRefreshReason::RefreshRequested {
 2147                                server_id: *server_id,
 2148                                request_id: *request_id,
 2149                            },
 2150                            cx,
 2151                        );
 2152                    }
 2153                    project::Event::RefreshSemanticTokens {
 2154                        server_id,
 2155                        request_id,
 2156                    } => {
 2157                        editor.refresh_semantic_tokens(
 2158                            None,
 2159                            Some(RefreshForServer {
 2160                                server_id: *server_id,
 2161                                request_id: *request_id,
 2162                            }),
 2163                            cx,
 2164                        );
 2165                    }
 2166                    project::Event::LanguageServerRemoved(_) => {
 2167                        editor.registered_buffers.clear();
 2168                        editor.register_visible_buffers(cx);
 2169                        editor.invalidate_semantic_tokens(None);
 2170                        editor.update_lsp_data(None, window, cx);
 2171                        editor.refresh_inlay_hints(InlayHintRefreshReason::ServerRemoved, cx);
 2172                        if editor.tasks_update_task.is_none() {
 2173                            editor.tasks_update_task = Some(editor.refresh_runnables(window, cx));
 2174                        }
 2175                    }
 2176                    project::Event::LanguageServerAdded(..) => {
 2177                        if editor.tasks_update_task.is_none() {
 2178                            editor.tasks_update_task = Some(editor.refresh_runnables(window, cx));
 2179                        }
 2180                    }
 2181                    project::Event::SnippetEdit(id, snippet_edits) => {
 2182                        // todo(lw): Non singletons
 2183                        if let Some(buffer) = editor.buffer.read(cx).as_singleton() {
 2184                            let snapshot = buffer.read(cx).snapshot();
 2185                            let focus_handle = editor.focus_handle(cx);
 2186                            if snapshot.remote_id() == *id && focus_handle.is_focused(window) {
 2187                                for (range, snippet) in snippet_edits {
 2188                                    let buffer_range =
 2189                                        language::range_from_lsp(*range).to_offset(&snapshot);
 2190                                    editor
 2191                                        .insert_snippet(
 2192                                            &[MultiBufferOffset(buffer_range.start)
 2193                                                ..MultiBufferOffset(buffer_range.end)],
 2194                                            snippet.clone(),
 2195                                            window,
 2196                                            cx,
 2197                                        )
 2198                                        .ok();
 2199                                }
 2200                            }
 2201                        }
 2202                    }
 2203                    project::Event::LanguageServerBufferRegistered { buffer_id, .. } => {
 2204                        let buffer_id = *buffer_id;
 2205                        if editor.buffer().read(cx).buffer(buffer_id).is_some() {
 2206                            editor.register_buffer(buffer_id, cx);
 2207                            editor.update_lsp_data(Some(buffer_id), window, cx);
 2208                            editor.refresh_inlay_hints(InlayHintRefreshReason::NewLinesShown, cx);
 2209                            refresh_linked_ranges(editor, window, cx);
 2210                            editor.refresh_code_actions(window, cx);
 2211                            editor.refresh_document_highlights(cx);
 2212                        }
 2213                    }
 2214
 2215                    project::Event::EntryRenamed(transaction, project_path, abs_path) => {
 2216                        let Some(workspace) = editor.workspace() else {
 2217                            return;
 2218                        };
 2219                        let Some(active_editor) = workspace.read(cx).active_item_as::<Self>(cx)
 2220                        else {
 2221                            return;
 2222                        };
 2223
 2224                        if active_editor.entity_id() == cx.entity_id() {
 2225                            let entity_id = cx.entity_id();
 2226                            workspace.update(cx, |this, cx| {
 2227                                this.panes_mut()
 2228                                    .iter_mut()
 2229                                    .filter(|pane| pane.entity_id() != entity_id)
 2230                                    .for_each(|p| {
 2231                                        p.update(cx, |pane, _| {
 2232                                            pane.nav_history_mut().rename_item(
 2233                                                entity_id,
 2234                                                project_path.clone(),
 2235                                                abs_path.clone().into(),
 2236                                            );
 2237                                        })
 2238                                    });
 2239                            });
 2240
 2241                            Self::open_transaction_for_hidden_buffers(
 2242                                workspace,
 2243                                transaction.clone(),
 2244                                "Rename".to_string(),
 2245                                window,
 2246                                cx,
 2247                            );
 2248                        }
 2249                    }
 2250
 2251                    project::Event::WorkspaceEditApplied(transaction) => {
 2252                        let Some(workspace) = editor.workspace() else {
 2253                            return;
 2254                        };
 2255                        let Some(active_editor) = workspace.read(cx).active_item_as::<Self>(cx)
 2256                        else {
 2257                            return;
 2258                        };
 2259
 2260                        if active_editor.entity_id() == cx.entity_id() {
 2261                            Self::open_transaction_for_hidden_buffers(
 2262                                workspace,
 2263                                transaction.clone(),
 2264                                "LSP Edit".to_string(),
 2265                                window,
 2266                                cx,
 2267                            );
 2268                        }
 2269                    }
 2270
 2271                    _ => {}
 2272                },
 2273            ));
 2274            if let Some(task_inventory) = project
 2275                .read(cx)
 2276                .task_store()
 2277                .read(cx)
 2278                .task_inventory()
 2279                .cloned()
 2280            {
 2281                project_subscriptions.push(cx.observe_in(
 2282                    &task_inventory,
 2283                    window,
 2284                    |editor, _, window, cx| {
 2285                        editor.tasks_update_task = Some(editor.refresh_runnables(window, cx));
 2286                    },
 2287                ));
 2288            };
 2289
 2290            project_subscriptions.push(cx.subscribe_in(
 2291                &project.read(cx).breakpoint_store(),
 2292                window,
 2293                |editor, _, event, window, cx| match event {
 2294                    BreakpointStoreEvent::ClearDebugLines => {
 2295                        editor.clear_row_highlights::<ActiveDebugLine>();
 2296                        editor.refresh_inline_values(cx);
 2297                    }
 2298                    BreakpointStoreEvent::SetDebugLine => {
 2299                        if editor.go_to_active_debug_line(window, cx) {
 2300                            cx.stop_propagation();
 2301                        }
 2302
 2303                        editor.refresh_inline_values(cx);
 2304                    }
 2305                    _ => {}
 2306                },
 2307            ));
 2308            let git_store = project.read(cx).git_store().clone();
 2309            let project = project.clone();
 2310            project_subscriptions.push(cx.subscribe(&git_store, move |this, _, event, cx| {
 2311                if let GitStoreEvent::RepositoryAdded = event {
 2312                    this.load_diff_task = Some(
 2313                        update_uncommitted_diff_for_buffer(
 2314                            cx.entity(),
 2315                            &project,
 2316                            this.buffer.read(cx).all_buffers(),
 2317                            this.buffer.clone(),
 2318                            cx,
 2319                        )
 2320                        .shared(),
 2321                    );
 2322                }
 2323            }));
 2324        }
 2325
 2326        let buffer_snapshot = multi_buffer.read(cx).snapshot(cx);
 2327
 2328        let inlay_hint_settings =
 2329            inlay_hint_settings(selections.newest_anchor().head(), &buffer_snapshot, cx);
 2330        let focus_handle = cx.focus_handle();
 2331        if !is_minimap {
 2332            cx.on_focus(&focus_handle, window, Self::handle_focus)
 2333                .detach();
 2334            cx.on_focus_in(&focus_handle, window, Self::handle_focus_in)
 2335                .detach();
 2336            cx.on_focus_out(&focus_handle, window, Self::handle_focus_out)
 2337                .detach();
 2338            cx.on_blur(&focus_handle, window, Self::handle_blur)
 2339                .detach();
 2340            cx.observe_pending_input(window, Self::observe_pending_input)
 2341                .detach();
 2342        }
 2343
 2344        let show_indent_guides =
 2345            if matches!(mode, EditorMode::SingleLine | EditorMode::Minimap { .. }) {
 2346                Some(false)
 2347            } else {
 2348                None
 2349            };
 2350
 2351        let breakpoint_store = match (&mode, project.as_ref()) {
 2352            (EditorMode::Full { .. }, Some(project)) => Some(project.read(cx).breakpoint_store()),
 2353            _ => None,
 2354        };
 2355
 2356        let mut code_action_providers = Vec::new();
 2357        let mut load_uncommitted_diff = None;
 2358        if let Some(project) = project.clone() {
 2359            load_uncommitted_diff = Some(
 2360                update_uncommitted_diff_for_buffer(
 2361                    cx.entity(),
 2362                    &project,
 2363                    multi_buffer.read(cx).all_buffers(),
 2364                    multi_buffer.clone(),
 2365                    cx,
 2366                )
 2367                .shared(),
 2368            );
 2369            code_action_providers.push(Rc::new(project) as Rc<_>);
 2370        }
 2371
 2372        let mut editor = Self {
 2373            focus_handle,
 2374            show_cursor_when_unfocused: false,
 2375            last_focused_descendant: None,
 2376            buffer: multi_buffer.clone(),
 2377            display_map: display_map.clone(),
 2378            placeholder_display_map: None,
 2379            selections,
 2380            scroll_manager: ScrollManager::new(cx),
 2381            columnar_selection_state: None,
 2382            add_selections_state: None,
 2383            select_next_state: None,
 2384            select_prev_state: None,
 2385            selection_history: SelectionHistory::default(),
 2386            defer_selection_effects: false,
 2387            deferred_selection_effects_state: None,
 2388            autoclose_regions: Vec::new(),
 2389            snippet_stack: InvalidationStack::default(),
 2390            select_syntax_node_history: SelectSyntaxNodeHistory::default(),
 2391            ime_transaction: None,
 2392            active_diagnostics: ActiveDiagnostic::None,
 2393            show_inline_diagnostics: ProjectSettings::get_global(cx).diagnostics.inline.enabled,
 2394            inline_diagnostics_update: Task::ready(()),
 2395            inline_diagnostics: Vec::new(),
 2396            soft_wrap_mode_override,
 2397            diagnostics_max_severity,
 2398            hard_wrap: None,
 2399            completion_provider: project.clone().map(|project| Rc::new(project) as _),
 2400            semantics_provider: project.clone().map(|project| Rc::new(project) as _),
 2401            collaboration_hub: project.clone().map(|project| Box::new(project) as _),
 2402            project,
 2403            blink_manager: blink_manager.clone(),
 2404            show_local_selections: true,
 2405            show_scrollbars: ScrollbarAxes {
 2406                horizontal: full_mode,
 2407                vertical: full_mode,
 2408            },
 2409            minimap_visibility: MinimapVisibility::for_mode(&mode, cx),
 2410            offset_content: !matches!(mode, EditorMode::SingleLine),
 2411            show_breadcrumbs: EditorSettings::get_global(cx).toolbar.breadcrumbs,
 2412            show_gutter: full_mode,
 2413            show_line_numbers: (!full_mode).then_some(false),
 2414            use_relative_line_numbers: None,
 2415            disable_expand_excerpt_buttons: !full_mode,
 2416            delegate_expand_excerpts: false,
 2417            delegate_stage_and_restore: false,
 2418            delegate_open_excerpts: false,
 2419            enable_lsp_data: true,
 2420            enable_runnables: true,
 2421            show_git_diff_gutter: None,
 2422            show_code_actions: None,
 2423            show_runnables: None,
 2424            show_breakpoints: None,
 2425            show_diff_review_button: false,
 2426            show_wrap_guides: None,
 2427            show_indent_guides,
 2428            buffers_with_disabled_indent_guides: HashSet::default(),
 2429            highlight_order: 0,
 2430            highlighted_rows: HashMap::default(),
 2431            background_highlights: HashMap::default(),
 2432            gutter_highlights: HashMap::default(),
 2433            scrollbar_marker_state: ScrollbarMarkerState::default(),
 2434            active_indent_guides_state: ActiveIndentGuidesState::default(),
 2435            nav_history: None,
 2436            context_menu: RefCell::new(None),
 2437            context_menu_options: None,
 2438            mouse_context_menu: None,
 2439            completion_tasks: Vec::new(),
 2440            inline_blame_popover: None,
 2441            inline_blame_popover_show_task: None,
 2442            signature_help_state: SignatureHelpState::default(),
 2443            auto_signature_help: None,
 2444            find_all_references_task_sources: Vec::new(),
 2445            next_completion_id: 0,
 2446            next_inlay_id: 0,
 2447            code_action_providers,
 2448            available_code_actions: None,
 2449            code_actions_task: None,
 2450            quick_selection_highlight_task: None,
 2451            debounced_selection_highlight_task: None,
 2452            debounced_selection_highlight_complete: false,
 2453            document_highlights_task: None,
 2454            linked_editing_range_task: None,
 2455            pending_rename: None,
 2456            searchable: !is_minimap,
 2457            cursor_shape: EditorSettings::get_global(cx)
 2458                .cursor_shape
 2459                .unwrap_or_default(),
 2460            cursor_offset_on_selection: false,
 2461            current_line_highlight: None,
 2462            autoindent_mode: Some(AutoindentMode::EachLine),
 2463            collapse_matches: false,
 2464            workspace: None,
 2465            input_enabled: !is_minimap,
 2466            use_modal_editing: full_mode,
 2467            read_only: is_minimap,
 2468            use_autoclose: true,
 2469            use_auto_surround: true,
 2470            auto_replace_emoji_shortcode: false,
 2471            jsx_tag_auto_close_enabled_in_any_buffer: false,
 2472            leader_id: None,
 2473            remote_id: None,
 2474            hover_state: HoverState::default(),
 2475            pending_mouse_down: None,
 2476            prev_pressure_stage: None,
 2477            hovered_link_state: None,
 2478            edit_prediction_provider: None,
 2479            active_edit_prediction: None,
 2480            stale_edit_prediction_in_menu: None,
 2481            edit_prediction_preview: EditPredictionPreview::Inactive {
 2482                released_too_fast: false,
 2483            },
 2484            inline_diagnostics_enabled: full_mode,
 2485            diagnostics_enabled: full_mode,
 2486            word_completions_enabled: full_mode,
 2487            inline_value_cache: InlineValueCache::new(inlay_hint_settings.show_value_hints),
 2488            gutter_hovered: false,
 2489            pixel_position_of_newest_cursor: None,
 2490            last_bounds: None,
 2491            last_position_map: None,
 2492            expect_bounds_change: None,
 2493            gutter_dimensions: GutterDimensions::default(),
 2494            style: None,
 2495            show_cursor_names: false,
 2496            hovered_cursors: HashMap::default(),
 2497            next_editor_action_id: EditorActionId::default(),
 2498            editor_actions: Rc::default(),
 2499            edit_predictions_hidden_for_vim_mode: false,
 2500            show_edit_predictions_override: None,
 2501            show_completions_on_input_override: None,
 2502            menu_edit_predictions_policy: MenuEditPredictionsPolicy::ByProvider,
 2503            edit_prediction_settings: EditPredictionSettings::Disabled,
 2504            edit_prediction_indent_conflict: false,
 2505            edit_prediction_requires_modifier_in_indent_conflict: true,
 2506            custom_context_menu: None,
 2507            show_git_blame_gutter: false,
 2508            show_git_blame_inline: false,
 2509            show_selection_menu: None,
 2510            show_git_blame_inline_delay_task: None,
 2511            git_blame_inline_enabled: full_mode
 2512                && ProjectSettings::get_global(cx).git.inline_blame.enabled,
 2513            render_diff_hunk_controls: Arc::new(render_diff_hunk_controls),
 2514            buffer_serialization: is_minimap.not().then(|| {
 2515                BufferSerialization::new(
 2516                    ProjectSettings::get_global(cx)
 2517                        .session
 2518                        .restore_unsaved_buffers,
 2519                )
 2520            }),
 2521            blame: None,
 2522            blame_subscription: None,
 2523            tasks: BTreeMap::default(),
 2524
 2525            breakpoint_store,
 2526            gutter_breakpoint_indicator: (None, None),
 2527            gutter_diff_review_indicator: (None, None),
 2528            diff_review_drag_state: None,
 2529            diff_review_overlays: Vec::new(),
 2530            stored_review_comments: Vec::new(),
 2531            next_review_comment_id: 0,
 2532            hovered_diff_hunk_row: None,
 2533            _subscriptions: (!is_minimap)
 2534                .then(|| {
 2535                    vec![
 2536                        cx.observe(&multi_buffer, Self::on_buffer_changed),
 2537                        cx.subscribe_in(&multi_buffer, window, Self::on_buffer_event),
 2538                        cx.observe_in(&display_map, window, Self::on_display_map_changed),
 2539                        cx.observe(&blink_manager, |_, _, cx| cx.notify()),
 2540                        cx.observe_global_in::<SettingsStore>(window, Self::settings_changed),
 2541                        cx.observe_global_in::<GlobalTheme>(window, Self::theme_changed),
 2542                        observe_buffer_font_size_adjustment(cx, |_, cx| cx.notify()),
 2543                        cx.observe_window_activation(window, |editor, window, cx| {
 2544                            let active = window.is_window_active();
 2545                            editor.blink_manager.update(cx, |blink_manager, cx| {
 2546                                if active {
 2547                                    blink_manager.enable(cx);
 2548                                } else {
 2549                                    blink_manager.disable(cx);
 2550                                }
 2551                            });
 2552                            if active {
 2553                                editor.show_mouse_cursor(cx);
 2554                            }
 2555                        }),
 2556                    ]
 2557                })
 2558                .unwrap_or_default(),
 2559            tasks_update_task: None,
 2560            pull_diagnostics_task: Task::ready(()),
 2561            colors: None,
 2562            refresh_colors_task: Task::ready(()),
 2563            use_document_folding_ranges: false,
 2564            refresh_folding_ranges_task: Task::ready(()),
 2565            inlay_hints: None,
 2566            next_color_inlay_id: 0,
 2567            post_scroll_update: Task::ready(()),
 2568            linked_edit_ranges: Default::default(),
 2569            in_project_search: false,
 2570            previous_search_ranges: None,
 2571            breadcrumb_header: None,
 2572            focused_block: None,
 2573            next_scroll_position: NextScrollCursorCenterTopBottom::default(),
 2574            addons: HashMap::default(),
 2575            registered_buffers: HashMap::default(),
 2576            _scroll_cursor_center_top_bottom_task: Task::ready(()),
 2577            selection_mark_mode: false,
 2578            toggle_fold_multiple_buffers: Task::ready(()),
 2579            serialize_selections: Task::ready(()),
 2580            serialize_folds: Task::ready(()),
 2581            text_style_refinement: None,
 2582            load_diff_task: load_uncommitted_diff,
 2583            temporary_diff_override: false,
 2584            mouse_cursor_hidden: false,
 2585            minimap: None,
 2586            hide_mouse_mode: EditorSettings::get_global(cx)
 2587                .hide_mouse
 2588                .unwrap_or_default(),
 2589            change_list: ChangeList::new(),
 2590            mode,
 2591            selection_drag_state: SelectionDragState::None,
 2592            folding_newlines: Task::ready(()),
 2593            lookup_key: None,
 2594            select_next_is_case_sensitive: None,
 2595            on_local_selections_changed: None,
 2596            suppress_selection_callback: false,
 2597            applicable_language_settings: HashMap::default(),
 2598            semantic_token_state: SemanticTokenState::new(cx, full_mode),
 2599            accent_data: None,
 2600            fetched_tree_sitter_chunks: HashMap::default(),
 2601            number_deleted_lines: false,
 2602            refresh_matching_bracket_highlights_task: Task::ready(()),
 2603            refresh_document_symbols_task: Task::ready(()).shared(),
 2604            lsp_document_symbols: HashMap::default(),
 2605            refresh_outline_symbols_at_cursor_at_cursor_task: Task::ready(()),
 2606            outline_symbols_at_cursor: None,
 2607            sticky_headers_task: Task::ready(()),
 2608            sticky_headers: None,
 2609        };
 2610
 2611        if is_minimap {
 2612            return editor;
 2613        }
 2614
 2615        editor.applicable_language_settings = editor.fetch_applicable_language_settings(cx);
 2616        editor.accent_data = editor.fetch_accent_data(cx);
 2617
 2618        if let Some(breakpoints) = editor.breakpoint_store.as_ref() {
 2619            editor
 2620                ._subscriptions
 2621                .push(cx.observe(breakpoints, |_, _, cx| {
 2622                    cx.notify();
 2623                }));
 2624        }
 2625        editor.tasks_update_task = Some(editor.refresh_runnables(window, cx));
 2626        editor._subscriptions.extend(project_subscriptions);
 2627
 2628        editor._subscriptions.push(cx.subscribe_in(
 2629            &cx.entity(),
 2630            window,
 2631            |editor, _, e: &EditorEvent, window, cx| match e {
 2632                EditorEvent::ScrollPositionChanged { local, .. } => {
 2633                    if *local {
 2634                        editor.hide_signature_help(cx, SignatureHelpHiddenBy::Escape);
 2635                        editor.inline_blame_popover.take();
 2636                        let snapshot = editor.snapshot(window, cx);
 2637                        let new_anchor = editor
 2638                            .scroll_manager
 2639                            .native_anchor(&snapshot.display_snapshot, cx);
 2640                        editor.update_restoration_data(cx, move |data| {
 2641                            data.scroll_position = (
 2642                                new_anchor.top_row(snapshot.buffer_snapshot()),
 2643                                new_anchor.offset,
 2644                            );
 2645                        });
 2646
 2647                        editor.post_scroll_update = cx.spawn_in(window, async move |editor, cx| {
 2648                            cx.background_executor()
 2649                                .timer(Duration::from_millis(50))
 2650                                .await;
 2651                            editor
 2652                                .update_in(cx, |editor, window, cx| {
 2653                                    editor.register_visible_buffers(cx);
 2654                                    editor.colorize_brackets(false, cx);
 2655                                    editor.refresh_inlay_hints(
 2656                                        InlayHintRefreshReason::NewLinesShown,
 2657                                        cx,
 2658                                    );
 2659                                    if !editor.buffer().read(cx).is_singleton() {
 2660                                        editor.update_lsp_data(None, window, cx);
 2661                                    }
 2662                                })
 2663                                .ok();
 2664                        });
 2665                    }
 2666                    editor.refresh_sticky_headers(&editor.snapshot(window, cx), cx);
 2667                }
 2668                EditorEvent::Edited { .. } => {
 2669                    let vim_mode = vim_mode_setting::VimModeSetting::try_get(cx)
 2670                        .map(|vim_mode| vim_mode.0)
 2671                        .unwrap_or(false);
 2672                    if !vim_mode {
 2673                        let display_map = editor.display_snapshot(cx);
 2674                        let selections = editor.selections.all_adjusted_display(&display_map);
 2675                        let pop_state = editor
 2676                            .change_list
 2677                            .last()
 2678                            .map(|previous| {
 2679                                previous.len() == selections.len()
 2680                                    && previous.iter().enumerate().all(|(ix, p)| {
 2681                                        p.to_display_point(&display_map).row()
 2682                                            == selections[ix].head().row()
 2683                                    })
 2684                            })
 2685                            .unwrap_or(false);
 2686                        let new_positions = selections
 2687                            .into_iter()
 2688                            .map(|s| display_map.display_point_to_anchor(s.head(), Bias::Left))
 2689                            .collect();
 2690                        editor
 2691                            .change_list
 2692                            .push_to_change_list(pop_state, new_positions);
 2693                    }
 2694                }
 2695                _ => (),
 2696            },
 2697        ));
 2698
 2699        if let Some(dap_store) = editor
 2700            .project
 2701            .as_ref()
 2702            .map(|project| project.read(cx).dap_store())
 2703        {
 2704            let weak_editor = cx.weak_entity();
 2705
 2706            editor
 2707                ._subscriptions
 2708                .push(
 2709                    cx.observe_new::<project::debugger::session::Session>(move |_, _, cx| {
 2710                        let session_entity = cx.entity();
 2711                        weak_editor
 2712                            .update(cx, |editor, cx| {
 2713                                editor._subscriptions.push(
 2714                                    cx.subscribe(&session_entity, Self::on_debug_session_event),
 2715                                );
 2716                            })
 2717                            .ok();
 2718                    }),
 2719                );
 2720
 2721            for session in dap_store.read(cx).sessions().cloned().collect::<Vec<_>>() {
 2722                editor
 2723                    ._subscriptions
 2724                    .push(cx.subscribe(&session, Self::on_debug_session_event));
 2725            }
 2726        }
 2727
 2728        // skip adding the initial selection to selection history
 2729        editor.selection_history.mode = SelectionHistoryMode::Skipping;
 2730        editor.end_selection(window, cx);
 2731        editor.selection_history.mode = SelectionHistoryMode::Normal;
 2732
 2733        editor.scroll_manager.show_scrollbars(window, cx);
 2734        jsx_tag_auto_close::refresh_enabled_in_any_buffer(&mut editor, &multi_buffer, cx);
 2735
 2736        if full_mode {
 2737            let should_auto_hide_scrollbars = cx.should_auto_hide_scrollbars();
 2738            cx.set_global(ScrollbarAutoHide(should_auto_hide_scrollbars));
 2739
 2740            if editor.git_blame_inline_enabled {
 2741                editor.start_git_blame_inline(false, window, cx);
 2742            }
 2743
 2744            editor.go_to_active_debug_line(window, cx);
 2745
 2746            editor.minimap =
 2747                editor.create_minimap(EditorSettings::get_global(cx).minimap, window, cx);
 2748            editor.colors = Some(LspColorData::new(cx));
 2749            editor.use_document_folding_ranges = true;
 2750            editor.inlay_hints = Some(LspInlayHintData::new(inlay_hint_settings));
 2751
 2752            if let Some(buffer) = multi_buffer.read(cx).as_singleton() {
 2753                editor.register_buffer(buffer.read(cx).remote_id(), cx);
 2754            }
 2755            editor.report_editor_event(ReportEditorEvent::EditorOpened, None, cx);
 2756        }
 2757
 2758        editor
 2759    }
 2760
 2761    pub fn display_snapshot(&self, cx: &mut App) -> DisplaySnapshot {
 2762        self.display_map.update(cx, |map, cx| map.snapshot(cx))
 2763    }
 2764
 2765    pub fn deploy_mouse_context_menu(
 2766        &mut self,
 2767        position: gpui::Point<Pixels>,
 2768        context_menu: Entity<ContextMenu>,
 2769        window: &mut Window,
 2770        cx: &mut Context<Self>,
 2771    ) {
 2772        self.mouse_context_menu = Some(MouseContextMenu::new(
 2773            self,
 2774            crate::mouse_context_menu::MenuPosition::PinnedToScreen(position),
 2775            context_menu,
 2776            window,
 2777            cx,
 2778        ));
 2779    }
 2780
 2781    pub fn mouse_menu_is_focused(&self, window: &Window, cx: &App) -> bool {
 2782        self.mouse_context_menu
 2783            .as_ref()
 2784            .is_some_and(|menu| menu.context_menu.focus_handle(cx).is_focused(window))
 2785    }
 2786
 2787    pub fn is_range_selected(&mut self, range: &Range<Anchor>, cx: &mut Context<Self>) -> bool {
 2788        if self
 2789            .selections
 2790            .pending_anchor()
 2791            .is_some_and(|pending_selection| {
 2792                let snapshot = self.buffer().read(cx).snapshot(cx);
 2793                pending_selection.range().includes(range, &snapshot)
 2794            })
 2795        {
 2796            return true;
 2797        }
 2798
 2799        self.selections
 2800            .disjoint_in_range::<MultiBufferOffset>(range.clone(), &self.display_snapshot(cx))
 2801            .into_iter()
 2802            .any(|selection| {
 2803                // This is needed to cover a corner case, if we just check for an existing
 2804                // selection in the fold range, having a cursor at the start of the fold
 2805                // marks it as selected. Non-empty selections don't cause this.
 2806                let length = selection.end - selection.start;
 2807                length > 0
 2808            })
 2809    }
 2810
 2811    pub fn key_context(&self, window: &mut Window, cx: &mut App) -> KeyContext {
 2812        self.key_context_internal(self.has_active_edit_prediction(), window, cx)
 2813    }
 2814
 2815    fn key_context_internal(
 2816        &self,
 2817        has_active_edit_prediction: bool,
 2818        window: &mut Window,
 2819        cx: &mut App,
 2820    ) -> KeyContext {
 2821        let mut key_context = KeyContext::new_with_defaults();
 2822        key_context.add("Editor");
 2823        let mode = match self.mode {
 2824            EditorMode::SingleLine => "single_line",
 2825            EditorMode::AutoHeight { .. } => "auto_height",
 2826            EditorMode::Minimap { .. } => "minimap",
 2827            EditorMode::Full { .. } => "full",
 2828        };
 2829
 2830        if EditorSettings::jupyter_enabled(cx) {
 2831            key_context.add("jupyter");
 2832        }
 2833
 2834        key_context.set("mode", mode);
 2835        if self.pending_rename.is_some() {
 2836            key_context.add("renaming");
 2837        }
 2838
 2839        if let Some(snippet_stack) = self.snippet_stack.last() {
 2840            key_context.add("in_snippet");
 2841
 2842            if snippet_stack.active_index > 0 {
 2843                key_context.add("has_previous_tabstop");
 2844            }
 2845
 2846            if snippet_stack.active_index < snippet_stack.ranges.len().saturating_sub(1) {
 2847                key_context.add("has_next_tabstop");
 2848            }
 2849        }
 2850
 2851        match self.context_menu.borrow().as_ref() {
 2852            Some(CodeContextMenu::Completions(menu)) => {
 2853                if menu.visible() {
 2854                    key_context.add("menu");
 2855                    key_context.add("showing_completions");
 2856                }
 2857            }
 2858            Some(CodeContextMenu::CodeActions(menu)) => {
 2859                if menu.visible() {
 2860                    key_context.add("menu");
 2861                    key_context.add("showing_code_actions")
 2862                }
 2863            }
 2864            None => {}
 2865        }
 2866
 2867        if self.signature_help_state.has_multiple_signatures() {
 2868            key_context.add("showing_signature_help");
 2869        }
 2870
 2871        // Disable vim contexts when a sub-editor (e.g. rename/inline assistant) is focused.
 2872        if !self.focus_handle(cx).contains_focused(window, cx)
 2873            || (self.is_focused(window) || self.mouse_menu_is_focused(window, cx))
 2874        {
 2875            for addon in self.addons.values() {
 2876                addon.extend_key_context(&mut key_context, cx)
 2877            }
 2878        }
 2879
 2880        if let Some(singleton_buffer) = self.buffer.read(cx).as_singleton() {
 2881            if let Some(extension) = singleton_buffer.read(cx).file().and_then(|file| {
 2882                Some(
 2883                    file.full_path(cx)
 2884                        .extension()?
 2885                        .to_string_lossy()
 2886                        .to_lowercase(),
 2887                )
 2888            }) {
 2889                key_context.set("extension", extension);
 2890            }
 2891        } else {
 2892            key_context.add("multibuffer");
 2893        }
 2894
 2895        if has_active_edit_prediction {
 2896            if self.edit_prediction_in_conflict() {
 2897                key_context.add(EDIT_PREDICTION_CONFLICT_KEY_CONTEXT);
 2898            } else {
 2899                key_context.add(EDIT_PREDICTION_KEY_CONTEXT);
 2900                key_context.add("copilot_suggestion");
 2901            }
 2902        }
 2903
 2904        if self.selection_mark_mode {
 2905            key_context.add("selection_mode");
 2906        }
 2907
 2908        let disjoint = self.selections.disjoint_anchors();
 2909        let snapshot = self.snapshot(window, cx);
 2910        let snapshot = snapshot.buffer_snapshot();
 2911        if self.mode == EditorMode::SingleLine
 2912            && let [selection] = disjoint
 2913            && selection.start == selection.end
 2914            && selection.end.to_offset(snapshot) == snapshot.len()
 2915        {
 2916            key_context.add("end_of_input");
 2917        }
 2918
 2919        if self.has_any_expanded_diff_hunks(cx) {
 2920            key_context.add("diffs_expanded");
 2921        }
 2922
 2923        key_context
 2924    }
 2925
 2926    pub fn last_bounds(&self) -> Option<&Bounds<Pixels>> {
 2927        self.last_bounds.as_ref()
 2928    }
 2929
 2930    fn show_mouse_cursor(&mut self, cx: &mut Context<Self>) {
 2931        if self.mouse_cursor_hidden {
 2932            self.mouse_cursor_hidden = false;
 2933            cx.notify();
 2934        }
 2935    }
 2936
 2937    pub fn hide_mouse_cursor(&mut self, origin: HideMouseCursorOrigin, cx: &mut Context<Self>) {
 2938        let hide_mouse_cursor = match origin {
 2939            HideMouseCursorOrigin::TypingAction => {
 2940                matches!(
 2941                    self.hide_mouse_mode,
 2942                    HideMouseMode::OnTyping | HideMouseMode::OnTypingAndMovement
 2943                )
 2944            }
 2945            HideMouseCursorOrigin::MovementAction => {
 2946                matches!(self.hide_mouse_mode, HideMouseMode::OnTypingAndMovement)
 2947            }
 2948        };
 2949        if self.mouse_cursor_hidden != hide_mouse_cursor {
 2950            self.mouse_cursor_hidden = hide_mouse_cursor;
 2951            cx.notify();
 2952        }
 2953    }
 2954
 2955    pub fn edit_prediction_in_conflict(&self) -> bool {
 2956        if !self.show_edit_predictions_in_menu() {
 2957            return false;
 2958        }
 2959
 2960        let showing_completions = self
 2961            .context_menu
 2962            .borrow()
 2963            .as_ref()
 2964            .is_some_and(|context| matches!(context, CodeContextMenu::Completions(_)));
 2965
 2966        showing_completions
 2967            || self.edit_prediction_requires_modifier()
 2968            // Require modifier key when the cursor is on leading whitespace, to allow `tab`
 2969            // bindings to insert tab characters.
 2970            || (self.edit_prediction_requires_modifier_in_indent_conflict && self.edit_prediction_indent_conflict)
 2971    }
 2972
 2973    pub fn accept_edit_prediction_keybind(
 2974        &self,
 2975        granularity: EditPredictionGranularity,
 2976        window: &mut Window,
 2977        cx: &mut App,
 2978    ) -> AcceptEditPredictionBinding {
 2979        let key_context = self.key_context_internal(true, window, cx);
 2980        let in_conflict = self.edit_prediction_in_conflict();
 2981
 2982        let bindings =
 2983            match granularity {
 2984                EditPredictionGranularity::Word => window
 2985                    .bindings_for_action_in_context(&AcceptNextWordEditPrediction, key_context),
 2986                EditPredictionGranularity::Line => window
 2987                    .bindings_for_action_in_context(&AcceptNextLineEditPrediction, key_context),
 2988                EditPredictionGranularity::Full => {
 2989                    window.bindings_for_action_in_context(&AcceptEditPrediction, key_context)
 2990                }
 2991            };
 2992
 2993        AcceptEditPredictionBinding(bindings.into_iter().rev().find(|binding| {
 2994            !in_conflict
 2995                || binding
 2996                    .keystrokes()
 2997                    .first()
 2998                    .is_some_and(|keystroke| keystroke.modifiers().modified())
 2999        }))
 3000    }
 3001
 3002    pub fn new_file(
 3003        workspace: &mut Workspace,
 3004        _: &workspace::NewFile,
 3005        window: &mut Window,
 3006        cx: &mut Context<Workspace>,
 3007    ) {
 3008        Self::new_in_workspace(workspace, window, cx).detach_and_prompt_err(
 3009            "Failed to create buffer",
 3010            window,
 3011            cx,
 3012            |e, _, _| match e.error_code() {
 3013                ErrorCode::RemoteUpgradeRequired => Some(format!(
 3014                "The remote instance of Zed does not support this yet. It must be upgraded to {}",
 3015                e.error_tag("required").unwrap_or("the latest version")
 3016            )),
 3017                _ => None,
 3018            },
 3019        );
 3020    }
 3021
 3022    pub fn new_in_workspace(
 3023        workspace: &mut Workspace,
 3024        window: &mut Window,
 3025        cx: &mut Context<Workspace>,
 3026    ) -> Task<Result<Entity<Editor>>> {
 3027        let project = workspace.project().clone();
 3028        let create = project.update(cx, |project, cx| project.create_buffer(None, true, cx));
 3029
 3030        cx.spawn_in(window, async move |workspace, cx| {
 3031            let buffer = create.await?;
 3032            workspace.update_in(cx, |workspace, window, cx| {
 3033                let editor =
 3034                    cx.new(|cx| Editor::for_buffer(buffer, Some(project.clone()), window, cx));
 3035                workspace.add_item_to_active_pane(Box::new(editor.clone()), None, true, window, cx);
 3036                editor
 3037            })
 3038        })
 3039    }
 3040
 3041    fn new_file_vertical(
 3042        workspace: &mut Workspace,
 3043        _: &workspace::NewFileSplitVertical,
 3044        window: &mut Window,
 3045        cx: &mut Context<Workspace>,
 3046    ) {
 3047        Self::new_file_in_direction(workspace, SplitDirection::vertical(cx), window, cx)
 3048    }
 3049
 3050    fn new_file_horizontal(
 3051        workspace: &mut Workspace,
 3052        _: &workspace::NewFileSplitHorizontal,
 3053        window: &mut Window,
 3054        cx: &mut Context<Workspace>,
 3055    ) {
 3056        Self::new_file_in_direction(workspace, SplitDirection::horizontal(cx), window, cx)
 3057    }
 3058
 3059    fn new_file_split(
 3060        workspace: &mut Workspace,
 3061        action: &workspace::NewFileSplit,
 3062        window: &mut Window,
 3063        cx: &mut Context<Workspace>,
 3064    ) {
 3065        Self::new_file_in_direction(workspace, action.0, window, cx)
 3066    }
 3067
 3068    fn new_file_in_direction(
 3069        workspace: &mut Workspace,
 3070        direction: SplitDirection,
 3071        window: &mut Window,
 3072        cx: &mut Context<Workspace>,
 3073    ) {
 3074        let project = workspace.project().clone();
 3075        let create = project.update(cx, |project, cx| project.create_buffer(None, true, cx));
 3076
 3077        cx.spawn_in(window, async move |workspace, cx| {
 3078            let buffer = create.await?;
 3079            workspace.update_in(cx, move |workspace, window, cx| {
 3080                workspace.split_item(
 3081                    direction,
 3082                    Box::new(
 3083                        cx.new(|cx| Editor::for_buffer(buffer, Some(project.clone()), window, cx)),
 3084                    ),
 3085                    window,
 3086                    cx,
 3087                )
 3088            })?;
 3089            anyhow::Ok(())
 3090        })
 3091        .detach_and_prompt_err("Failed to create buffer", window, cx, |e, _, _| {
 3092            match e.error_code() {
 3093                ErrorCode::RemoteUpgradeRequired => Some(format!(
 3094                "The remote instance of Zed does not support this yet. It must be upgraded to {}",
 3095                e.error_tag("required").unwrap_or("the latest version")
 3096            )),
 3097                _ => None,
 3098            }
 3099        });
 3100    }
 3101
 3102    pub fn leader_id(&self) -> Option<CollaboratorId> {
 3103        self.leader_id
 3104    }
 3105
 3106    pub fn buffer(&self) -> &Entity<MultiBuffer> {
 3107        &self.buffer
 3108    }
 3109
 3110    pub fn project(&self) -> Option<&Entity<Project>> {
 3111        self.project.as_ref()
 3112    }
 3113
 3114    pub fn workspace(&self) -> Option<Entity<Workspace>> {
 3115        self.workspace.as_ref()?.0.upgrade()
 3116    }
 3117
 3118    /// Detaches a task and shows an error notification in the workspace if available,
 3119    /// otherwise just logs the error.
 3120    pub fn detach_and_notify_err<R, E>(
 3121        &self,
 3122        task: Task<Result<R, E>>,
 3123        window: &mut Window,
 3124        cx: &mut App,
 3125    ) where
 3126        E: std::fmt::Debug + std::fmt::Display + 'static,
 3127        R: 'static,
 3128    {
 3129        if let Some(workspace) = self.workspace() {
 3130            task.detach_and_notify_err(workspace.downgrade(), window, cx);
 3131        } else {
 3132            task.detach_and_log_err(cx);
 3133        }
 3134    }
 3135
 3136    /// Returns the workspace serialization ID if this editor should be serialized.
 3137    fn workspace_serialization_id(&self, _cx: &App) -> Option<WorkspaceId> {
 3138        self.workspace
 3139            .as_ref()
 3140            .filter(|_| self.should_serialize_buffer())
 3141            .and_then(|workspace| workspace.1)
 3142    }
 3143
 3144    pub fn title<'a>(&self, cx: &'a App) -> Cow<'a, str> {
 3145        self.buffer().read(cx).title(cx)
 3146    }
 3147
 3148    pub fn snapshot(&self, window: &Window, cx: &mut App) -> EditorSnapshot {
 3149        let git_blame_gutter_max_author_length = self
 3150            .render_git_blame_gutter(cx)
 3151            .then(|| {
 3152                if let Some(blame) = self.blame.as_ref() {
 3153                    let max_author_length =
 3154                        blame.update(cx, |blame, cx| blame.max_author_length(cx));
 3155                    Some(max_author_length)
 3156                } else {
 3157                    None
 3158                }
 3159            })
 3160            .flatten();
 3161
 3162        let display_snapshot = self.display_map.update(cx, |map, cx| map.snapshot(cx));
 3163
 3164        EditorSnapshot {
 3165            mode: self.mode.clone(),
 3166            show_gutter: self.show_gutter,
 3167            offset_content: self.offset_content,
 3168            show_line_numbers: self.show_line_numbers,
 3169            number_deleted_lines: self.number_deleted_lines,
 3170            show_git_diff_gutter: self.show_git_diff_gutter,
 3171            semantic_tokens_enabled: self.semantic_token_state.enabled(),
 3172            show_code_actions: self.show_code_actions,
 3173            show_runnables: self.show_runnables,
 3174            show_breakpoints: self.show_breakpoints,
 3175            git_blame_gutter_max_author_length,
 3176            scroll_anchor: self.scroll_manager.shared_scroll_anchor(cx),
 3177            display_snapshot,
 3178            placeholder_display_snapshot: self
 3179                .placeholder_display_map
 3180                .as_ref()
 3181                .map(|display_map| display_map.update(cx, |map, cx| map.snapshot(cx))),
 3182            ongoing_scroll: self.scroll_manager.ongoing_scroll(),
 3183            is_focused: self.focus_handle.is_focused(window),
 3184            current_line_highlight: self
 3185                .current_line_highlight
 3186                .unwrap_or_else(|| EditorSettings::get_global(cx).current_line_highlight),
 3187            gutter_hovered: self.gutter_hovered,
 3188        }
 3189    }
 3190
 3191    pub fn language_at<T: ToOffset>(&self, point: T, cx: &App) -> Option<Arc<Language>> {
 3192        self.buffer.read(cx).language_at(point, cx)
 3193    }
 3194
 3195    pub fn file_at<T: ToOffset>(&self, point: T, cx: &App) -> Option<Arc<dyn language::File>> {
 3196        self.buffer.read(cx).read(cx).file_at(point).cloned()
 3197    }
 3198
 3199    pub fn active_excerpt(
 3200        &self,
 3201        cx: &App,
 3202    ) -> Option<(ExcerptId, Entity<Buffer>, Range<text::Anchor>)> {
 3203        self.buffer
 3204            .read(cx)
 3205            .excerpt_containing(self.selections.newest_anchor().head(), cx)
 3206    }
 3207
 3208    pub fn mode(&self) -> &EditorMode {
 3209        &self.mode
 3210    }
 3211
 3212    pub fn set_mode(&mut self, mode: EditorMode) {
 3213        self.mode = mode;
 3214    }
 3215
 3216    pub fn collaboration_hub(&self) -> Option<&dyn CollaborationHub> {
 3217        self.collaboration_hub.as_deref()
 3218    }
 3219
 3220    pub fn set_collaboration_hub(&mut self, hub: Box<dyn CollaborationHub>) {
 3221        self.collaboration_hub = Some(hub);
 3222    }
 3223
 3224    pub fn set_in_project_search(&mut self, in_project_search: bool) {
 3225        self.in_project_search = in_project_search;
 3226    }
 3227
 3228    pub fn set_custom_context_menu(
 3229        &mut self,
 3230        f: impl 'static
 3231        + Fn(
 3232            &mut Self,
 3233            DisplayPoint,
 3234            &mut Window,
 3235            &mut Context<Self>,
 3236        ) -> Option<Entity<ui::ContextMenu>>,
 3237    ) {
 3238        self.custom_context_menu = Some(Box::new(f))
 3239    }
 3240
 3241    pub fn set_completion_provider(&mut self, provider: Option<Rc<dyn CompletionProvider>>) {
 3242        self.completion_provider = provider;
 3243    }
 3244
 3245    #[cfg(any(test, feature = "test-support"))]
 3246    pub fn completion_provider(&self) -> Option<Rc<dyn CompletionProvider>> {
 3247        self.completion_provider.clone()
 3248    }
 3249
 3250    pub fn semantics_provider(&self) -> Option<Rc<dyn SemanticsProvider>> {
 3251        self.semantics_provider.clone()
 3252    }
 3253
 3254    pub fn set_semantics_provider(&mut self, provider: Option<Rc<dyn SemanticsProvider>>) {
 3255        self.semantics_provider = provider;
 3256    }
 3257
 3258    pub fn set_edit_prediction_provider<T>(
 3259        &mut self,
 3260        provider: Option<Entity<T>>,
 3261        window: &mut Window,
 3262        cx: &mut Context<Self>,
 3263    ) where
 3264        T: EditPredictionDelegate,
 3265    {
 3266        self.edit_prediction_provider = provider.map(|provider| RegisteredEditPredictionDelegate {
 3267            _subscription: cx.observe_in(&provider, window, |this, _, window, cx| {
 3268                if this.focus_handle.is_focused(window) {
 3269                    this.update_visible_edit_prediction(window, cx);
 3270                }
 3271            }),
 3272            provider: Arc::new(provider),
 3273        });
 3274        self.update_edit_prediction_settings(cx);
 3275        self.refresh_edit_prediction(false, false, window, cx);
 3276    }
 3277
 3278    pub fn placeholder_text(&self, cx: &mut App) -> Option<String> {
 3279        self.placeholder_display_map
 3280            .as_ref()
 3281            .map(|display_map| display_map.update(cx, |map, cx| map.snapshot(cx)).text())
 3282    }
 3283
 3284    pub fn set_placeholder_text(
 3285        &mut self,
 3286        placeholder_text: &str,
 3287        window: &mut Window,
 3288        cx: &mut Context<Self>,
 3289    ) {
 3290        let multibuffer = cx
 3291            .new(|cx| MultiBuffer::singleton(cx.new(|cx| Buffer::local(placeholder_text, cx)), cx));
 3292
 3293        let style = window.text_style();
 3294
 3295        self.placeholder_display_map = Some(cx.new(|cx| {
 3296            DisplayMap::new(
 3297                multibuffer,
 3298                style.font(),
 3299                style.font_size.to_pixels(window.rem_size()),
 3300                None,
 3301                FILE_HEADER_HEIGHT,
 3302                MULTI_BUFFER_EXCERPT_HEADER_HEIGHT,
 3303                Default::default(),
 3304                DiagnosticSeverity::Off,
 3305                cx,
 3306            )
 3307        }));
 3308        cx.notify();
 3309    }
 3310
 3311    pub fn set_cursor_shape(&mut self, cursor_shape: CursorShape, cx: &mut Context<Self>) {
 3312        self.cursor_shape = cursor_shape;
 3313
 3314        // Disrupt blink for immediate user feedback that the cursor shape has changed
 3315        self.blink_manager.update(cx, BlinkManager::show_cursor);
 3316
 3317        cx.notify();
 3318    }
 3319
 3320    pub fn cursor_shape(&self) -> CursorShape {
 3321        self.cursor_shape
 3322    }
 3323
 3324    pub fn set_cursor_offset_on_selection(&mut self, set_cursor_offset_on_selection: bool) {
 3325        self.cursor_offset_on_selection = set_cursor_offset_on_selection;
 3326    }
 3327
 3328    pub fn set_current_line_highlight(
 3329        &mut self,
 3330        current_line_highlight: Option<CurrentLineHighlight>,
 3331    ) {
 3332        self.current_line_highlight = current_line_highlight;
 3333    }
 3334
 3335    pub fn set_collapse_matches(&mut self, collapse_matches: bool) {
 3336        self.collapse_matches = collapse_matches;
 3337    }
 3338
 3339    pub fn range_for_match<T: std::marker::Copy>(&self, range: &Range<T>) -> Range<T> {
 3340        if self.collapse_matches {
 3341            return range.start..range.start;
 3342        }
 3343        range.clone()
 3344    }
 3345
 3346    pub fn clip_at_line_ends(&mut self, cx: &mut Context<Self>) -> bool {
 3347        self.display_map.read(cx).clip_at_line_ends
 3348    }
 3349
 3350    pub fn set_clip_at_line_ends(&mut self, clip: bool, cx: &mut Context<Self>) {
 3351        if self.display_map.read(cx).clip_at_line_ends != clip {
 3352            self.display_map
 3353                .update(cx, |map, _| map.clip_at_line_ends = clip);
 3354        }
 3355    }
 3356
 3357    pub fn set_input_enabled(&mut self, input_enabled: bool) {
 3358        self.input_enabled = input_enabled;
 3359    }
 3360
 3361    pub fn set_edit_predictions_hidden_for_vim_mode(
 3362        &mut self,
 3363        hidden: bool,
 3364        window: &mut Window,
 3365        cx: &mut Context<Self>,
 3366    ) {
 3367        if hidden != self.edit_predictions_hidden_for_vim_mode {
 3368            self.edit_predictions_hidden_for_vim_mode = hidden;
 3369            if hidden {
 3370                self.update_visible_edit_prediction(window, cx);
 3371            } else {
 3372                self.refresh_edit_prediction(true, false, window, cx);
 3373            }
 3374        }
 3375    }
 3376
 3377    pub fn set_menu_edit_predictions_policy(&mut self, value: MenuEditPredictionsPolicy) {
 3378        self.menu_edit_predictions_policy = value;
 3379    }
 3380
 3381    pub fn set_autoindent(&mut self, autoindent: bool) {
 3382        if autoindent {
 3383            self.autoindent_mode = Some(AutoindentMode::EachLine);
 3384        } else {
 3385            self.autoindent_mode = None;
 3386        }
 3387    }
 3388
 3389    pub fn capability(&self, cx: &App) -> Capability {
 3390        if self.read_only {
 3391            Capability::ReadOnly
 3392        } else {
 3393            self.buffer.read(cx).capability()
 3394        }
 3395    }
 3396
 3397    pub fn read_only(&self, cx: &App) -> bool {
 3398        self.read_only || self.buffer.read(cx).read_only()
 3399    }
 3400
 3401    pub fn set_read_only(&mut self, read_only: bool) {
 3402        self.read_only = read_only;
 3403    }
 3404
 3405    pub fn set_use_autoclose(&mut self, autoclose: bool) {
 3406        self.use_autoclose = autoclose;
 3407    }
 3408
 3409    pub fn set_use_auto_surround(&mut self, auto_surround: bool) {
 3410        self.use_auto_surround = auto_surround;
 3411    }
 3412
 3413    pub fn set_auto_replace_emoji_shortcode(&mut self, auto_replace: bool) {
 3414        self.auto_replace_emoji_shortcode = auto_replace;
 3415    }
 3416
 3417    pub fn set_should_serialize(&mut self, should_serialize: bool, cx: &App) {
 3418        self.buffer_serialization = should_serialize.then(|| {
 3419            BufferSerialization::new(
 3420                ProjectSettings::get_global(cx)
 3421                    .session
 3422                    .restore_unsaved_buffers,
 3423            )
 3424        })
 3425    }
 3426
 3427    fn should_serialize_buffer(&self) -> bool {
 3428        self.buffer_serialization.is_some()
 3429    }
 3430
 3431    pub fn toggle_edit_predictions(
 3432        &mut self,
 3433        _: &ToggleEditPrediction,
 3434        window: &mut Window,
 3435        cx: &mut Context<Self>,
 3436    ) {
 3437        if self.show_edit_predictions_override.is_some() {
 3438            self.set_show_edit_predictions(None, window, cx);
 3439        } else {
 3440            let show_edit_predictions = !self.edit_predictions_enabled();
 3441            self.set_show_edit_predictions(Some(show_edit_predictions), window, cx);
 3442        }
 3443    }
 3444
 3445    pub fn set_show_completions_on_input(&mut self, show_completions_on_input: Option<bool>) {
 3446        self.show_completions_on_input_override = show_completions_on_input;
 3447    }
 3448
 3449    pub fn set_show_edit_predictions(
 3450        &mut self,
 3451        show_edit_predictions: Option<bool>,
 3452        window: &mut Window,
 3453        cx: &mut Context<Self>,
 3454    ) {
 3455        self.show_edit_predictions_override = show_edit_predictions;
 3456        self.update_edit_prediction_settings(cx);
 3457
 3458        if let Some(false) = show_edit_predictions {
 3459            self.discard_edit_prediction(EditPredictionDiscardReason::Ignored, cx);
 3460        } else {
 3461            self.refresh_edit_prediction(false, true, window, cx);
 3462        }
 3463    }
 3464
 3465    fn edit_predictions_disabled_in_scope(
 3466        &self,
 3467        buffer: &Entity<Buffer>,
 3468        buffer_position: language::Anchor,
 3469        cx: &App,
 3470    ) -> bool {
 3471        let snapshot = buffer.read(cx).snapshot();
 3472        let settings = snapshot.settings_at(buffer_position, cx);
 3473
 3474        let Some(scope) = snapshot.language_scope_at(buffer_position) else {
 3475            return false;
 3476        };
 3477
 3478        scope.override_name().is_some_and(|scope_name| {
 3479            settings
 3480                .edit_predictions_disabled_in
 3481                .iter()
 3482                .any(|s| s == scope_name)
 3483        })
 3484    }
 3485
 3486    pub fn set_use_modal_editing(&mut self, to: bool) {
 3487        self.use_modal_editing = to;
 3488    }
 3489
 3490    pub fn use_modal_editing(&self) -> bool {
 3491        self.use_modal_editing
 3492    }
 3493
 3494    fn selections_did_change(
 3495        &mut self,
 3496        local: bool,
 3497        old_cursor_position: &Anchor,
 3498        effects: SelectionEffects,
 3499        window: &mut Window,
 3500        cx: &mut Context<Self>,
 3501    ) {
 3502        window.invalidate_character_coordinates();
 3503
 3504        // Copy selections to primary selection buffer
 3505        #[cfg(any(target_os = "linux", target_os = "freebsd"))]
 3506        if local {
 3507            let selections = self
 3508                .selections
 3509                .all::<MultiBufferOffset>(&self.display_snapshot(cx));
 3510            let buffer_handle = self.buffer.read(cx).read(cx);
 3511
 3512            let mut text = String::new();
 3513            for (index, selection) in selections.iter().enumerate() {
 3514                let text_for_selection = buffer_handle
 3515                    .text_for_range(selection.start..selection.end)
 3516                    .collect::<String>();
 3517
 3518                text.push_str(&text_for_selection);
 3519                if index != selections.len() - 1 {
 3520                    text.push('\n');
 3521                }
 3522            }
 3523
 3524            if !text.is_empty() {
 3525                cx.write_to_primary(ClipboardItem::new_string(text));
 3526            }
 3527        }
 3528
 3529        let selection_anchors = self.selections.disjoint_anchors_arc();
 3530
 3531        if self.focus_handle.is_focused(window) && self.leader_id.is_none() {
 3532            self.buffer.update(cx, |buffer, cx| {
 3533                buffer.set_active_selections(
 3534                    &selection_anchors,
 3535                    self.selections.line_mode(),
 3536                    self.cursor_shape,
 3537                    cx,
 3538                )
 3539            });
 3540        }
 3541        let display_map = self
 3542            .display_map
 3543            .update(cx, |display_map, cx| display_map.snapshot(cx));
 3544        let buffer = display_map.buffer_snapshot();
 3545        if self.selections.count() == 1 {
 3546            self.add_selections_state = None;
 3547        }
 3548        self.select_next_state = None;
 3549        self.select_prev_state = None;
 3550        self.select_syntax_node_history.try_clear();
 3551        self.invalidate_autoclose_regions(&selection_anchors, buffer);
 3552        self.snippet_stack.invalidate(&selection_anchors, buffer);
 3553        self.take_rename(false, window, cx);
 3554
 3555        let newest_selection = self.selections.newest_anchor();
 3556        let new_cursor_position = newest_selection.head();
 3557        let selection_start = newest_selection.start;
 3558
 3559        if effects.nav_history.is_none() || effects.nav_history == Some(true) {
 3560            self.push_to_nav_history(
 3561                *old_cursor_position,
 3562                Some(new_cursor_position.to_point(buffer)),
 3563                false,
 3564                effects.nav_history == Some(true),
 3565                cx,
 3566            );
 3567        }
 3568
 3569        if local {
 3570            if let Some(buffer_id) = new_cursor_position.text_anchor.buffer_id {
 3571                self.register_buffer(buffer_id, cx);
 3572            }
 3573
 3574            let mut context_menu = self.context_menu.borrow_mut();
 3575            let completion_menu = match context_menu.as_ref() {
 3576                Some(CodeContextMenu::Completions(menu)) => Some(menu),
 3577                Some(CodeContextMenu::CodeActions(_)) => {
 3578                    *context_menu = None;
 3579                    None
 3580                }
 3581                None => None,
 3582            };
 3583            let completion_position = completion_menu.map(|menu| menu.initial_position);
 3584            drop(context_menu);
 3585
 3586            if effects.completions
 3587                && let Some(completion_position) = completion_position
 3588            {
 3589                let start_offset = selection_start.to_offset(buffer);
 3590                let position_matches = start_offset == completion_position.to_offset(buffer);
 3591                let continue_showing = if let Some((snap, ..)) =
 3592                    buffer.point_to_buffer_offset(completion_position)
 3593                    && !snap.capability.editable()
 3594                {
 3595                    false
 3596                } else if position_matches {
 3597                    if self.snippet_stack.is_empty() {
 3598                        buffer.char_kind_before(start_offset, Some(CharScopeContext::Completion))
 3599                            == Some(CharKind::Word)
 3600                    } else {
 3601                        // Snippet choices can be shown even when the cursor is in whitespace.
 3602                        // Dismissing the menu with actions like backspace is handled by
 3603                        // invalidation regions.
 3604                        true
 3605                    }
 3606                } else {
 3607                    false
 3608                };
 3609
 3610                if continue_showing {
 3611                    self.open_or_update_completions_menu(None, None, false, window, cx);
 3612                } else {
 3613                    self.hide_context_menu(window, cx);
 3614                }
 3615            }
 3616
 3617            hide_hover(self, cx);
 3618
 3619            if old_cursor_position.to_display_point(&display_map).row()
 3620                != new_cursor_position.to_display_point(&display_map).row()
 3621            {
 3622                self.available_code_actions.take();
 3623            }
 3624            self.refresh_code_actions(window, cx);
 3625            self.refresh_document_highlights(cx);
 3626            refresh_linked_ranges(self, window, cx);
 3627
 3628            self.refresh_selected_text_highlights(false, window, cx);
 3629            self.refresh_matching_bracket_highlights(&display_map, cx);
 3630            self.refresh_outline_symbols_at_cursor(cx);
 3631            self.update_visible_edit_prediction(window, cx);
 3632            self.edit_prediction_requires_modifier_in_indent_conflict = true;
 3633            self.inline_blame_popover.take();
 3634            if self.git_blame_inline_enabled {
 3635                self.start_inline_blame_timer(window, cx);
 3636            }
 3637        }
 3638
 3639        self.blink_manager.update(cx, BlinkManager::pause_blinking);
 3640
 3641        if local && !self.suppress_selection_callback {
 3642            if let Some(callback) = self.on_local_selections_changed.as_ref() {
 3643                let cursor_position = self.selections.newest::<Point>(&display_map).head();
 3644                callback(cursor_position, window, cx);
 3645            }
 3646        }
 3647
 3648        cx.emit(EditorEvent::SelectionsChanged { local });
 3649
 3650        let selections = &self.selections.disjoint_anchors_arc();
 3651        if selections.len() == 1 {
 3652            cx.emit(SearchEvent::ActiveMatchChanged)
 3653        }
 3654        if local && let Some((_, _, buffer_snapshot)) = buffer.as_singleton() {
 3655            let inmemory_selections = selections
 3656                .iter()
 3657                .map(|s| {
 3658                    text::ToPoint::to_point(&s.range().start.text_anchor, buffer_snapshot)
 3659                        ..text::ToPoint::to_point(&s.range().end.text_anchor, buffer_snapshot)
 3660                })
 3661                .collect();
 3662            self.update_restoration_data(cx, |data| {
 3663                data.selections = inmemory_selections;
 3664            });
 3665
 3666            if WorkspaceSettings::get(None, cx).restore_on_startup
 3667                != RestoreOnStartupBehavior::EmptyTab
 3668                && let Some(workspace_id) = self.workspace_serialization_id(cx)
 3669            {
 3670                let snapshot = self.buffer().read(cx).snapshot(cx);
 3671                let selections = selections.clone();
 3672                let background_executor = cx.background_executor().clone();
 3673                let editor_id = cx.entity().entity_id().as_u64() as ItemId;
 3674                self.serialize_selections = cx.background_spawn(async move {
 3675                    background_executor.timer(SERIALIZATION_THROTTLE_TIME).await;
 3676                    let db_selections = selections
 3677                        .iter()
 3678                        .map(|selection| {
 3679                            (
 3680                                selection.start.to_offset(&snapshot).0,
 3681                                selection.end.to_offset(&snapshot).0,
 3682                            )
 3683                        })
 3684                        .collect();
 3685
 3686                    DB.save_editor_selections(editor_id, workspace_id, db_selections)
 3687                        .await
 3688                        .with_context(|| {
 3689                            format!(
 3690                                "persisting editor selections for editor {editor_id}, \
 3691                                workspace {workspace_id:?}"
 3692                            )
 3693                        })
 3694                        .log_err();
 3695                });
 3696            }
 3697        }
 3698
 3699        cx.notify();
 3700    }
 3701
 3702    fn folds_did_change(&mut self, cx: &mut Context<Self>) {
 3703        use text::ToOffset as _;
 3704        use text::ToPoint as _;
 3705
 3706        if self.mode.is_minimap()
 3707            || WorkspaceSettings::get(None, cx).restore_on_startup
 3708                == RestoreOnStartupBehavior::EmptyTab
 3709        {
 3710            return;
 3711        }
 3712
 3713        if !self.buffer().read(cx).is_singleton() {
 3714            return;
 3715        }
 3716
 3717        let display_snapshot = self
 3718            .display_map
 3719            .update(cx, |display_map, cx| display_map.snapshot(cx));
 3720        let Some((.., snapshot)) = display_snapshot.buffer_snapshot().as_singleton() else {
 3721            return;
 3722        };
 3723        let inmemory_folds = display_snapshot
 3724            .folds_in_range(MultiBufferOffset(0)..display_snapshot.buffer_snapshot().len())
 3725            .map(|fold| {
 3726                fold.range.start.text_anchor.to_point(&snapshot)
 3727                    ..fold.range.end.text_anchor.to_point(&snapshot)
 3728            })
 3729            .collect();
 3730        self.update_restoration_data(cx, |data| {
 3731            data.folds = inmemory_folds;
 3732        });
 3733
 3734        let Some(workspace_id) = self.workspace_serialization_id(cx) else {
 3735            return;
 3736        };
 3737        let background_executor = cx.background_executor().clone();
 3738        let editor_id = cx.entity().entity_id().as_u64() as ItemId;
 3739        const FINGERPRINT_LEN: usize = 32;
 3740        let db_folds = display_snapshot
 3741            .folds_in_range(MultiBufferOffset(0)..display_snapshot.buffer_snapshot().len())
 3742            .map(|fold| {
 3743                let start = fold.range.start.text_anchor.to_offset(&snapshot);
 3744                let end = fold.range.end.text_anchor.to_offset(&snapshot);
 3745
 3746                // Extract fingerprints - content at fold boundaries for validation on restore
 3747                // Both fingerprints must be INSIDE the fold to avoid capturing surrounding
 3748                // content that might change independently.
 3749                // start_fp: first min(32, fold_len) bytes of fold content
 3750                // end_fp: last min(32, fold_len) bytes of fold content
 3751                // Clip to character boundaries to handle multibyte UTF-8 characters.
 3752                let fold_len = end - start;
 3753                let start_fp_end = snapshot
 3754                    .clip_offset(start + std::cmp::min(FINGERPRINT_LEN, fold_len), Bias::Left);
 3755                let start_fp: String = snapshot.text_for_range(start..start_fp_end).collect();
 3756                let end_fp_start = snapshot
 3757                    .clip_offset(end.saturating_sub(FINGERPRINT_LEN).max(start), Bias::Right);
 3758                let end_fp: String = snapshot.text_for_range(end_fp_start..end).collect();
 3759
 3760                (start, end, start_fp, end_fp)
 3761            })
 3762            .collect::<Vec<_>>();
 3763        self.serialize_folds = cx.background_spawn(async move {
 3764            background_executor.timer(SERIALIZATION_THROTTLE_TIME).await;
 3765            DB.save_editor_folds(editor_id, workspace_id, db_folds)
 3766                .await
 3767                .with_context(|| {
 3768                    format!(
 3769                        "persisting editor folds for editor {editor_id}, workspace {workspace_id:?}"
 3770                    )
 3771                })
 3772                .log_err();
 3773        });
 3774    }
 3775
 3776    pub fn sync_selections(
 3777        &mut self,
 3778        other: Entity<Editor>,
 3779        cx: &mut Context<Self>,
 3780    ) -> gpui::Subscription {
 3781        let other_selections = other.read(cx).selections.disjoint_anchors().to_vec();
 3782        if !other_selections.is_empty() {
 3783            self.selections
 3784                .change_with(&self.display_snapshot(cx), |selections| {
 3785                    selections.select_anchors(other_selections);
 3786                });
 3787        }
 3788
 3789        let other_subscription = cx.subscribe(&other, |this, other, other_evt, cx| {
 3790            if let EditorEvent::SelectionsChanged { local: true } = other_evt {
 3791                let other_selections = other.read(cx).selections.disjoint_anchors().to_vec();
 3792                if other_selections.is_empty() {
 3793                    return;
 3794                }
 3795                let snapshot = this.display_snapshot(cx);
 3796                this.selections.change_with(&snapshot, |selections| {
 3797                    selections.select_anchors(other_selections);
 3798                });
 3799            }
 3800        });
 3801
 3802        let this_subscription = cx.subscribe_self::<EditorEvent>(move |this, this_evt, cx| {
 3803            if let EditorEvent::SelectionsChanged { local: true } = this_evt {
 3804                let these_selections = this.selections.disjoint_anchors().to_vec();
 3805                if these_selections.is_empty() {
 3806                    return;
 3807                }
 3808                other.update(cx, |other_editor, cx| {
 3809                    let snapshot = other_editor.display_snapshot(cx);
 3810                    other_editor
 3811                        .selections
 3812                        .change_with(&snapshot, |selections| {
 3813                            selections.select_anchors(these_selections);
 3814                        })
 3815                });
 3816            }
 3817        });
 3818
 3819        Subscription::join(other_subscription, this_subscription)
 3820    }
 3821
 3822    fn unfold_buffers_with_selections(&mut self, cx: &mut Context<Self>) {
 3823        if self.buffer().read(cx).is_singleton() {
 3824            return;
 3825        }
 3826        let snapshot = self.buffer.read(cx).snapshot(cx);
 3827        let buffer_ids: HashSet<BufferId> = self
 3828            .selections
 3829            .disjoint_anchor_ranges()
 3830            .flat_map(|range| snapshot.buffer_ids_for_range(range))
 3831            .collect();
 3832        for buffer_id in buffer_ids {
 3833            self.unfold_buffer(buffer_id, cx);
 3834        }
 3835    }
 3836
 3837    /// Changes selections using the provided mutation function. Changes to `self.selections` occur
 3838    /// immediately, but when run within `transact` or `with_selection_effects_deferred` other
 3839    /// effects of selection change occur at the end of the transaction.
 3840    pub fn change_selections<R>(
 3841        &mut self,
 3842        effects: SelectionEffects,
 3843        window: &mut Window,
 3844        cx: &mut Context<Self>,
 3845        change: impl FnOnce(&mut MutableSelectionsCollection<'_, '_>) -> R,
 3846    ) -> R {
 3847        let snapshot = self.display_snapshot(cx);
 3848        if let Some(state) = &mut self.deferred_selection_effects_state {
 3849            state.effects.scroll = effects.scroll.or(state.effects.scroll);
 3850            state.effects.completions = effects.completions;
 3851            state.effects.nav_history = effects.nav_history.or(state.effects.nav_history);
 3852            let (changed, result) = self.selections.change_with(&snapshot, change);
 3853            state.changed |= changed;
 3854            return result;
 3855        }
 3856        let mut state = DeferredSelectionEffectsState {
 3857            changed: false,
 3858            effects,
 3859            old_cursor_position: self.selections.newest_anchor().head(),
 3860            history_entry: SelectionHistoryEntry {
 3861                selections: self.selections.disjoint_anchors_arc(),
 3862                select_next_state: self.select_next_state.clone(),
 3863                select_prev_state: self.select_prev_state.clone(),
 3864                add_selections_state: self.add_selections_state.clone(),
 3865            },
 3866        };
 3867        let (changed, result) = self.selections.change_with(&snapshot, change);
 3868        state.changed = state.changed || changed;
 3869        if self.defer_selection_effects {
 3870            self.deferred_selection_effects_state = Some(state);
 3871        } else {
 3872            self.apply_selection_effects(state, window, cx);
 3873        }
 3874        result
 3875    }
 3876
 3877    /// Defers the effects of selection change, so that the effects of multiple calls to
 3878    /// `change_selections` are applied at the end. This way these intermediate states aren't added
 3879    /// to selection history and the state of popovers based on selection position aren't
 3880    /// erroneously updated.
 3881    pub fn with_selection_effects_deferred<R>(
 3882        &mut self,
 3883        window: &mut Window,
 3884        cx: &mut Context<Self>,
 3885        update: impl FnOnce(&mut Self, &mut Window, &mut Context<Self>) -> R,
 3886    ) -> R {
 3887        let already_deferred = self.defer_selection_effects;
 3888        self.defer_selection_effects = true;
 3889        let result = update(self, window, cx);
 3890        if !already_deferred {
 3891            self.defer_selection_effects = false;
 3892            if let Some(state) = self.deferred_selection_effects_state.take() {
 3893                self.apply_selection_effects(state, window, cx);
 3894            }
 3895        }
 3896        result
 3897    }
 3898
 3899    fn apply_selection_effects(
 3900        &mut self,
 3901        state: DeferredSelectionEffectsState,
 3902        window: &mut Window,
 3903        cx: &mut Context<Self>,
 3904    ) {
 3905        if state.changed {
 3906            self.selection_history.push(state.history_entry);
 3907
 3908            if let Some(autoscroll) = state.effects.scroll {
 3909                self.request_autoscroll(autoscroll, cx);
 3910            }
 3911
 3912            let old_cursor_position = &state.old_cursor_position;
 3913
 3914            self.selections_did_change(true, old_cursor_position, state.effects, window, cx);
 3915
 3916            if self.should_open_signature_help_automatically(old_cursor_position, cx) {
 3917                self.show_signature_help_auto(window, cx);
 3918            }
 3919        }
 3920    }
 3921
 3922    pub fn edit<I, S, T>(&mut self, edits: I, cx: &mut Context<Self>)
 3923    where
 3924        I: IntoIterator<Item = (Range<S>, T)>,
 3925        S: ToOffset,
 3926        T: Into<Arc<str>>,
 3927    {
 3928        if self.read_only(cx) {
 3929            return;
 3930        }
 3931
 3932        self.buffer
 3933            .update(cx, |buffer, cx| buffer.edit(edits, None, cx));
 3934    }
 3935
 3936    pub fn edit_with_autoindent<I, S, T>(&mut self, edits: I, cx: &mut Context<Self>)
 3937    where
 3938        I: IntoIterator<Item = (Range<S>, T)>,
 3939        S: ToOffset,
 3940        T: Into<Arc<str>>,
 3941    {
 3942        if self.read_only(cx) {
 3943            return;
 3944        }
 3945
 3946        self.buffer.update(cx, |buffer, cx| {
 3947            buffer.edit(edits, self.autoindent_mode.clone(), cx)
 3948        });
 3949    }
 3950
 3951    pub fn edit_with_block_indent<I, S, T>(
 3952        &mut self,
 3953        edits: I,
 3954        original_indent_columns: Vec<Option<u32>>,
 3955        cx: &mut Context<Self>,
 3956    ) where
 3957        I: IntoIterator<Item = (Range<S>, T)>,
 3958        S: ToOffset,
 3959        T: Into<Arc<str>>,
 3960    {
 3961        if self.read_only(cx) {
 3962            return;
 3963        }
 3964
 3965        self.buffer.update(cx, |buffer, cx| {
 3966            buffer.edit(
 3967                edits,
 3968                Some(AutoindentMode::Block {
 3969                    original_indent_columns,
 3970                }),
 3971                cx,
 3972            )
 3973        });
 3974    }
 3975
 3976    fn select(&mut self, phase: SelectPhase, window: &mut Window, cx: &mut Context<Self>) {
 3977        self.hide_context_menu(window, cx);
 3978
 3979        match phase {
 3980            SelectPhase::Begin {
 3981                position,
 3982                add,
 3983                click_count,
 3984            } => self.begin_selection(position, add, click_count, window, cx),
 3985            SelectPhase::BeginColumnar {
 3986                position,
 3987                goal_column,
 3988                reset,
 3989                mode,
 3990            } => self.begin_columnar_selection(position, goal_column, reset, mode, window, cx),
 3991            SelectPhase::Extend {
 3992                position,
 3993                click_count,
 3994            } => self.extend_selection(position, click_count, window, cx),
 3995            SelectPhase::Update {
 3996                position,
 3997                goal_column,
 3998                scroll_delta,
 3999            } => self.update_selection(position, goal_column, scroll_delta, window, cx),
 4000            SelectPhase::End => self.end_selection(window, cx),
 4001        }
 4002    }
 4003
 4004    fn extend_selection(
 4005        &mut self,
 4006        position: DisplayPoint,
 4007        click_count: usize,
 4008        window: &mut Window,
 4009        cx: &mut Context<Self>,
 4010    ) {
 4011        let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
 4012        let tail = self
 4013            .selections
 4014            .newest::<MultiBufferOffset>(&display_map)
 4015            .tail();
 4016        let click_count = click_count.max(match self.selections.select_mode() {
 4017            SelectMode::Character => 1,
 4018            SelectMode::Word(_) => 2,
 4019            SelectMode::Line(_) => 3,
 4020            SelectMode::All => 4,
 4021        });
 4022        self.begin_selection(position, false, click_count, window, cx);
 4023
 4024        let tail_anchor = display_map.buffer_snapshot().anchor_before(tail);
 4025
 4026        let current_selection = match self.selections.select_mode() {
 4027            SelectMode::Character | SelectMode::All => tail_anchor..tail_anchor,
 4028            SelectMode::Word(range) | SelectMode::Line(range) => range.clone(),
 4029        };
 4030
 4031        let mut pending_selection = self
 4032            .selections
 4033            .pending_anchor()
 4034            .cloned()
 4035            .expect("extend_selection not called with pending selection");
 4036
 4037        if pending_selection
 4038            .start
 4039            .cmp(&current_selection.start, display_map.buffer_snapshot())
 4040            == Ordering::Greater
 4041        {
 4042            pending_selection.start = current_selection.start;
 4043        }
 4044        if pending_selection
 4045            .end
 4046            .cmp(&current_selection.end, display_map.buffer_snapshot())
 4047            == Ordering::Less
 4048        {
 4049            pending_selection.end = current_selection.end;
 4050            pending_selection.reversed = true;
 4051        }
 4052
 4053        let mut pending_mode = self.selections.pending_mode().unwrap();
 4054        match &mut pending_mode {
 4055            SelectMode::Word(range) | SelectMode::Line(range) => *range = current_selection,
 4056            _ => {}
 4057        }
 4058
 4059        let effects = if EditorSettings::get_global(cx).autoscroll_on_clicks {
 4060            SelectionEffects::scroll(Autoscroll::fit())
 4061        } else {
 4062            SelectionEffects::no_scroll()
 4063        };
 4064
 4065        self.change_selections(effects, window, cx, |s| {
 4066            s.set_pending(pending_selection.clone(), pending_mode);
 4067            s.set_is_extending(true);
 4068        });
 4069    }
 4070
 4071    fn begin_selection(
 4072        &mut self,
 4073        position: DisplayPoint,
 4074        add: bool,
 4075        click_count: usize,
 4076        window: &mut Window,
 4077        cx: &mut Context<Self>,
 4078    ) {
 4079        if !self.focus_handle.is_focused(window) {
 4080            self.last_focused_descendant = None;
 4081            window.focus(&self.focus_handle, cx);
 4082        }
 4083
 4084        let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
 4085        let buffer = display_map.buffer_snapshot();
 4086        let position = display_map.clip_point(position, Bias::Left);
 4087
 4088        let start;
 4089        let end;
 4090        let mode;
 4091        let mut auto_scroll;
 4092        match click_count {
 4093            1 => {
 4094                start = buffer.anchor_before(position.to_point(&display_map));
 4095                end = start;
 4096                mode = SelectMode::Character;
 4097                auto_scroll = true;
 4098            }
 4099            2 => {
 4100                let position = display_map
 4101                    .clip_point(position, Bias::Left)
 4102                    .to_offset(&display_map, Bias::Left);
 4103                let (range, _) = buffer.surrounding_word(position, None);
 4104                start = buffer.anchor_before(range.start);
 4105                end = buffer.anchor_before(range.end);
 4106                mode = SelectMode::Word(start..end);
 4107                auto_scroll = true;
 4108            }
 4109            3 => {
 4110                let position = display_map
 4111                    .clip_point(position, Bias::Left)
 4112                    .to_point(&display_map);
 4113                let line_start = display_map.prev_line_boundary(position).0;
 4114                let next_line_start = buffer.clip_point(
 4115                    display_map.next_line_boundary(position).0 + Point::new(1, 0),
 4116                    Bias::Left,
 4117                );
 4118                start = buffer.anchor_before(line_start);
 4119                end = buffer.anchor_before(next_line_start);
 4120                mode = SelectMode::Line(start..end);
 4121                auto_scroll = true;
 4122            }
 4123            _ => {
 4124                start = buffer.anchor_before(MultiBufferOffset(0));
 4125                end = buffer.anchor_before(buffer.len());
 4126                mode = SelectMode::All;
 4127                auto_scroll = false;
 4128            }
 4129        }
 4130        auto_scroll &= EditorSettings::get_global(cx).autoscroll_on_clicks;
 4131
 4132        let point_to_delete: Option<usize> = {
 4133            let selected_points: Vec<Selection<Point>> =
 4134                self.selections.disjoint_in_range(start..end, &display_map);
 4135
 4136            if !add || click_count > 1 {
 4137                None
 4138            } else if !selected_points.is_empty() {
 4139                Some(selected_points[0].id)
 4140            } else {
 4141                let clicked_point_already_selected =
 4142                    self.selections.disjoint_anchors().iter().find(|selection| {
 4143                        selection.start.to_point(buffer) == start.to_point(buffer)
 4144                            || selection.end.to_point(buffer) == end.to_point(buffer)
 4145                    });
 4146
 4147                clicked_point_already_selected.map(|selection| selection.id)
 4148            }
 4149        };
 4150
 4151        let selections_count = self.selections.count();
 4152        let effects = if auto_scroll {
 4153            SelectionEffects::default()
 4154        } else {
 4155            SelectionEffects::no_scroll()
 4156        };
 4157
 4158        self.change_selections(effects, window, cx, |s| {
 4159            if let Some(point_to_delete) = point_to_delete {
 4160                s.delete(point_to_delete);
 4161
 4162                if selections_count == 1 {
 4163                    s.set_pending_anchor_range(start..end, mode);
 4164                }
 4165            } else {
 4166                if !add {
 4167                    s.clear_disjoint();
 4168                }
 4169
 4170                s.set_pending_anchor_range(start..end, mode);
 4171            }
 4172        });
 4173    }
 4174
 4175    fn begin_columnar_selection(
 4176        &mut self,
 4177        position: DisplayPoint,
 4178        goal_column: u32,
 4179        reset: bool,
 4180        mode: ColumnarMode,
 4181        window: &mut Window,
 4182        cx: &mut Context<Self>,
 4183    ) {
 4184        if !self.focus_handle.is_focused(window) {
 4185            self.last_focused_descendant = None;
 4186            window.focus(&self.focus_handle, cx);
 4187        }
 4188
 4189        let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
 4190
 4191        if reset {
 4192            let pointer_position = display_map
 4193                .buffer_snapshot()
 4194                .anchor_before(position.to_point(&display_map));
 4195
 4196            self.change_selections(
 4197                SelectionEffects::scroll(Autoscroll::newest()),
 4198                window,
 4199                cx,
 4200                |s| {
 4201                    s.clear_disjoint();
 4202                    s.set_pending_anchor_range(
 4203                        pointer_position..pointer_position,
 4204                        SelectMode::Character,
 4205                    );
 4206                },
 4207            );
 4208        };
 4209
 4210        let tail = self.selections.newest::<Point>(&display_map).tail();
 4211        let selection_anchor = display_map.buffer_snapshot().anchor_before(tail);
 4212        self.columnar_selection_state = match mode {
 4213            ColumnarMode::FromMouse => Some(ColumnarSelectionState::FromMouse {
 4214                selection_tail: selection_anchor,
 4215                display_point: if reset {
 4216                    if position.column() != goal_column {
 4217                        Some(DisplayPoint::new(position.row(), goal_column))
 4218                    } else {
 4219                        None
 4220                    }
 4221                } else {
 4222                    None
 4223                },
 4224            }),
 4225            ColumnarMode::FromSelection => Some(ColumnarSelectionState::FromSelection {
 4226                selection_tail: selection_anchor,
 4227            }),
 4228        };
 4229
 4230        if !reset {
 4231            self.select_columns(position, goal_column, &display_map, window, cx);
 4232        }
 4233    }
 4234
 4235    fn update_selection(
 4236        &mut self,
 4237        position: DisplayPoint,
 4238        goal_column: u32,
 4239        scroll_delta: gpui::Point<f32>,
 4240        window: &mut Window,
 4241        cx: &mut Context<Self>,
 4242    ) {
 4243        let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
 4244
 4245        if self.columnar_selection_state.is_some() {
 4246            self.select_columns(position, goal_column, &display_map, window, cx);
 4247        } else if let Some(mut pending) = self.selections.pending_anchor().cloned() {
 4248            let buffer = display_map.buffer_snapshot();
 4249            let head;
 4250            let tail;
 4251            let mode = self.selections.pending_mode().unwrap();
 4252            match &mode {
 4253                SelectMode::Character => {
 4254                    head = position.to_point(&display_map);
 4255                    tail = pending.tail().to_point(buffer);
 4256                }
 4257                SelectMode::Word(original_range) => {
 4258                    let offset = display_map
 4259                        .clip_point(position, Bias::Left)
 4260                        .to_offset(&display_map, Bias::Left);
 4261                    let original_range = original_range.to_offset(buffer);
 4262
 4263                    let head_offset = if buffer.is_inside_word(offset, None)
 4264                        || original_range.contains(&offset)
 4265                    {
 4266                        let (word_range, _) = buffer.surrounding_word(offset, None);
 4267                        if word_range.start < original_range.start {
 4268                            word_range.start
 4269                        } else {
 4270                            word_range.end
 4271                        }
 4272                    } else {
 4273                        offset
 4274                    };
 4275
 4276                    head = head_offset.to_point(buffer);
 4277                    if head_offset <= original_range.start {
 4278                        tail = original_range.end.to_point(buffer);
 4279                    } else {
 4280                        tail = original_range.start.to_point(buffer);
 4281                    }
 4282                }
 4283                SelectMode::Line(original_range) => {
 4284                    let original_range = original_range.to_point(display_map.buffer_snapshot());
 4285
 4286                    let position = display_map
 4287                        .clip_point(position, Bias::Left)
 4288                        .to_point(&display_map);
 4289                    let line_start = display_map.prev_line_boundary(position).0;
 4290                    let next_line_start = buffer.clip_point(
 4291                        display_map.next_line_boundary(position).0 + Point::new(1, 0),
 4292                        Bias::Left,
 4293                    );
 4294
 4295                    if line_start < original_range.start {
 4296                        head = line_start
 4297                    } else {
 4298                        head = next_line_start
 4299                    }
 4300
 4301                    if head <= original_range.start {
 4302                        tail = original_range.end;
 4303                    } else {
 4304                        tail = original_range.start;
 4305                    }
 4306                }
 4307                SelectMode::All => {
 4308                    return;
 4309                }
 4310            };
 4311
 4312            if head < tail {
 4313                pending.start = buffer.anchor_before(head);
 4314                pending.end = buffer.anchor_before(tail);
 4315                pending.reversed = true;
 4316            } else {
 4317                pending.start = buffer.anchor_before(tail);
 4318                pending.end = buffer.anchor_before(head);
 4319                pending.reversed = false;
 4320            }
 4321
 4322            self.change_selections(SelectionEffects::no_scroll(), window, cx, |s| {
 4323                s.set_pending(pending.clone(), mode);
 4324            });
 4325        } else {
 4326            log::error!("update_selection dispatched with no pending selection");
 4327            return;
 4328        }
 4329
 4330        self.apply_scroll_delta(scroll_delta, window, cx);
 4331        cx.notify();
 4332    }
 4333
 4334    fn end_selection(&mut self, window: &mut Window, cx: &mut Context<Self>) {
 4335        self.columnar_selection_state.take();
 4336        if let Some(pending_mode) = self.selections.pending_mode() {
 4337            let selections = self
 4338                .selections
 4339                .all::<MultiBufferOffset>(&self.display_snapshot(cx));
 4340            self.change_selections(SelectionEffects::no_scroll(), window, cx, |s| {
 4341                s.select(selections);
 4342                s.clear_pending();
 4343                if s.is_extending() {
 4344                    s.set_is_extending(false);
 4345                } else {
 4346                    s.set_select_mode(pending_mode);
 4347                }
 4348            });
 4349        }
 4350    }
 4351
 4352    fn select_columns(
 4353        &mut self,
 4354        head: DisplayPoint,
 4355        goal_column: u32,
 4356        display_map: &DisplaySnapshot,
 4357        window: &mut Window,
 4358        cx: &mut Context<Self>,
 4359    ) {
 4360        let Some(columnar_state) = self.columnar_selection_state.as_ref() else {
 4361            return;
 4362        };
 4363
 4364        let tail = match columnar_state {
 4365            ColumnarSelectionState::FromMouse {
 4366                selection_tail,
 4367                display_point,
 4368            } => display_point.unwrap_or_else(|| selection_tail.to_display_point(display_map)),
 4369            ColumnarSelectionState::FromSelection { selection_tail } => {
 4370                selection_tail.to_display_point(display_map)
 4371            }
 4372        };
 4373
 4374        let start_row = cmp::min(tail.row(), head.row());
 4375        let end_row = cmp::max(tail.row(), head.row());
 4376        let start_column = cmp::min(tail.column(), goal_column);
 4377        let end_column = cmp::max(tail.column(), goal_column);
 4378        let reversed = start_column < tail.column();
 4379
 4380        let selection_ranges = (start_row.0..=end_row.0)
 4381            .map(DisplayRow)
 4382            .filter_map(|row| {
 4383                if (matches!(columnar_state, ColumnarSelectionState::FromMouse { .. })
 4384                    || start_column <= display_map.line_len(row))
 4385                    && !display_map.is_block_line(row)
 4386                {
 4387                    let start = display_map
 4388                        .clip_point(DisplayPoint::new(row, start_column), Bias::Left)
 4389                        .to_point(display_map);
 4390                    let end = display_map
 4391                        .clip_point(DisplayPoint::new(row, end_column), Bias::Right)
 4392                        .to_point(display_map);
 4393                    if reversed {
 4394                        Some(end..start)
 4395                    } else {
 4396                        Some(start..end)
 4397                    }
 4398                } else {
 4399                    None
 4400                }
 4401            })
 4402            .collect::<Vec<_>>();
 4403        if selection_ranges.is_empty() {
 4404            return;
 4405        }
 4406
 4407        let ranges = match columnar_state {
 4408            ColumnarSelectionState::FromMouse { .. } => {
 4409                let mut non_empty_ranges = selection_ranges
 4410                    .iter()
 4411                    .filter(|selection_range| selection_range.start != selection_range.end)
 4412                    .peekable();
 4413                if non_empty_ranges.peek().is_some() {
 4414                    non_empty_ranges.cloned().collect()
 4415                } else {
 4416                    selection_ranges
 4417                }
 4418            }
 4419            _ => selection_ranges,
 4420        };
 4421
 4422        self.change_selections(SelectionEffects::no_scroll(), window, cx, |s| {
 4423            s.select_ranges(ranges);
 4424        });
 4425        cx.notify();
 4426    }
 4427
 4428    pub fn has_non_empty_selection(&self, snapshot: &DisplaySnapshot) -> bool {
 4429        self.selections
 4430            .all_adjusted(snapshot)
 4431            .iter()
 4432            .any(|selection| !selection.is_empty())
 4433    }
 4434
 4435    pub fn has_pending_nonempty_selection(&self) -> bool {
 4436        let pending_nonempty_selection = match self.selections.pending_anchor() {
 4437            Some(Selection { start, end, .. }) => start != end,
 4438            None => false,
 4439        };
 4440
 4441        pending_nonempty_selection
 4442            || (self.columnar_selection_state.is_some()
 4443                && self.selections.disjoint_anchors().len() > 1)
 4444    }
 4445
 4446    pub fn has_pending_selection(&self) -> bool {
 4447        self.selections.pending_anchor().is_some() || self.columnar_selection_state.is_some()
 4448    }
 4449
 4450    pub fn cancel(&mut self, _: &Cancel, window: &mut Window, cx: &mut Context<Self>) {
 4451        self.selection_mark_mode = false;
 4452        self.selection_drag_state = SelectionDragState::None;
 4453
 4454        if self.dismiss_menus_and_popups(true, window, cx) {
 4455            cx.notify();
 4456            return;
 4457        }
 4458        if self.clear_expanded_diff_hunks(cx) {
 4459            cx.notify();
 4460            return;
 4461        }
 4462        if self.show_git_blame_gutter {
 4463            self.show_git_blame_gutter = false;
 4464            cx.notify();
 4465            return;
 4466        }
 4467
 4468        if self.mode.is_full()
 4469            && self.change_selections(Default::default(), window, cx, |s| s.try_cancel())
 4470        {
 4471            cx.notify();
 4472            return;
 4473        }
 4474
 4475        cx.propagate();
 4476    }
 4477
 4478    pub fn dismiss_menus_and_popups(
 4479        &mut self,
 4480        is_user_requested: bool,
 4481        window: &mut Window,
 4482        cx: &mut Context<Self>,
 4483    ) -> bool {
 4484        let mut dismissed = false;
 4485
 4486        dismissed |= self.take_rename(false, window, cx).is_some();
 4487        dismissed |= self.hide_blame_popover(true, cx);
 4488        dismissed |= hide_hover(self, cx);
 4489        dismissed |= self.hide_signature_help(cx, SignatureHelpHiddenBy::Escape);
 4490        dismissed |= self.hide_context_menu(window, cx).is_some();
 4491        dismissed |= self.mouse_context_menu.take().is_some();
 4492        dismissed |= is_user_requested
 4493            && self.discard_edit_prediction(EditPredictionDiscardReason::Rejected, cx);
 4494        dismissed |= self.snippet_stack.pop().is_some();
 4495        if self.diff_review_drag_state.is_some() {
 4496            self.cancel_diff_review_drag(cx);
 4497            dismissed = true;
 4498        }
 4499        if !self.diff_review_overlays.is_empty() {
 4500            self.dismiss_all_diff_review_overlays(cx);
 4501            dismissed = true;
 4502        }
 4503
 4504        if self.mode.is_full() && matches!(self.active_diagnostics, ActiveDiagnostic::Group(_)) {
 4505            self.dismiss_diagnostics(cx);
 4506            dismissed = true;
 4507        }
 4508
 4509        dismissed
 4510    }
 4511
 4512    fn linked_editing_ranges_for(
 4513        &self,
 4514        selection: Range<text::Anchor>,
 4515        cx: &App,
 4516    ) -> Option<HashMap<Entity<Buffer>, Vec<Range<text::Anchor>>>> {
 4517        if self.linked_edit_ranges.is_empty() {
 4518            return None;
 4519        }
 4520        let ((base_range, linked_ranges), buffer_snapshot, buffer) =
 4521            selection.end.buffer_id.and_then(|end_buffer_id| {
 4522                if selection.start.buffer_id != Some(end_buffer_id) {
 4523                    return None;
 4524                }
 4525                let buffer = self.buffer.read(cx).buffer(end_buffer_id)?;
 4526                let snapshot = buffer.read(cx).snapshot();
 4527                self.linked_edit_ranges
 4528                    .get(end_buffer_id, selection.start..selection.end, &snapshot)
 4529                    .map(|ranges| (ranges, snapshot, buffer))
 4530            })?;
 4531        use text::ToOffset as TO;
 4532        // find offset from the start of current range to current cursor position
 4533        let start_byte_offset = TO::to_offset(&base_range.start, &buffer_snapshot);
 4534
 4535        let start_offset = TO::to_offset(&selection.start, &buffer_snapshot);
 4536        let start_difference = start_offset - start_byte_offset;
 4537        let end_offset = TO::to_offset(&selection.end, &buffer_snapshot);
 4538        let end_difference = end_offset - start_byte_offset;
 4539        // Current range has associated linked ranges.
 4540        let mut linked_edits = HashMap::<_, Vec<_>>::default();
 4541        for range in linked_ranges.iter() {
 4542            let start_offset = TO::to_offset(&range.start, &buffer_snapshot);
 4543            let end_offset = start_offset + end_difference;
 4544            let start_offset = start_offset + start_difference;
 4545            if start_offset > buffer_snapshot.len() || end_offset > buffer_snapshot.len() {
 4546                continue;
 4547            }
 4548            if self.selections.disjoint_anchor_ranges().any(|s| {
 4549                if s.start.text_anchor.buffer_id != selection.start.buffer_id
 4550                    || s.end.text_anchor.buffer_id != selection.end.buffer_id
 4551                {
 4552                    return false;
 4553                }
 4554                TO::to_offset(&s.start.text_anchor, &buffer_snapshot) <= end_offset
 4555                    && TO::to_offset(&s.end.text_anchor, &buffer_snapshot) >= start_offset
 4556            }) {
 4557                continue;
 4558            }
 4559            let start = buffer_snapshot.anchor_after(start_offset);
 4560            let end = buffer_snapshot.anchor_after(end_offset);
 4561            linked_edits
 4562                .entry(buffer.clone())
 4563                .or_default()
 4564                .push(start..end);
 4565        }
 4566        Some(linked_edits)
 4567    }
 4568
 4569    pub fn handle_input(&mut self, text: &str, window: &mut Window, cx: &mut Context<Self>) {
 4570        let text: Arc<str> = text.into();
 4571
 4572        if self.read_only(cx) {
 4573            return;
 4574        }
 4575
 4576        self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
 4577
 4578        self.unfold_buffers_with_selections(cx);
 4579
 4580        let selections = self.selections.all_adjusted(&self.display_snapshot(cx));
 4581        let mut bracket_inserted = false;
 4582        let mut edits = Vec::new();
 4583        let mut linked_edits = HashMap::<_, Vec<_>>::default();
 4584        let mut new_selections = Vec::with_capacity(selections.len());
 4585        let mut new_autoclose_regions = Vec::new();
 4586        let snapshot = self.buffer.read(cx).read(cx);
 4587        let mut clear_linked_edit_ranges = false;
 4588        let mut all_selections_read_only = true;
 4589        let mut has_adjacent_edits = false;
 4590        let mut in_adjacent_group = false;
 4591
 4592        let mut regions = self
 4593            .selections_with_autoclose_regions(selections, &snapshot)
 4594            .peekable();
 4595
 4596        while let Some((selection, autoclose_region)) = regions.next() {
 4597            if snapshot
 4598                .point_to_buffer_point(selection.head())
 4599                .is_none_or(|(snapshot, ..)| !snapshot.capability.editable())
 4600            {
 4601                continue;
 4602            }
 4603            if snapshot
 4604                .point_to_buffer_point(selection.tail())
 4605                .is_none_or(|(snapshot, ..)| !snapshot.capability.editable())
 4606            {
 4607                // note, ideally we'd clip the tail to the closest writeable region towards the head
 4608                continue;
 4609            }
 4610            all_selections_read_only = false;
 4611
 4612            if let Some(scope) = snapshot.language_scope_at(selection.head()) {
 4613                // Determine if the inserted text matches the opening or closing
 4614                // bracket of any of this language's bracket pairs.
 4615                let mut bracket_pair = None;
 4616                let mut is_bracket_pair_start = false;
 4617                let mut is_bracket_pair_end = false;
 4618                if !text.is_empty() {
 4619                    let mut bracket_pair_matching_end = None;
 4620                    // `text` can be empty when a user is using IME (e.g. Chinese Wubi Simplified)
 4621                    //  and they are removing the character that triggered IME popup.
 4622                    for (pair, enabled) in scope.brackets() {
 4623                        if !pair.close && !pair.surround {
 4624                            continue;
 4625                        }
 4626
 4627                        if enabled && pair.start.ends_with(text.as_ref()) {
 4628                            let prefix_len = pair.start.len() - text.len();
 4629                            let preceding_text_matches_prefix = prefix_len == 0
 4630                                || (selection.start.column >= (prefix_len as u32)
 4631                                    && snapshot.contains_str_at(
 4632                                        Point::new(
 4633                                            selection.start.row,
 4634                                            selection.start.column - (prefix_len as u32),
 4635                                        ),
 4636                                        &pair.start[..prefix_len],
 4637                                    ));
 4638                            if preceding_text_matches_prefix {
 4639                                bracket_pair = Some(pair.clone());
 4640                                is_bracket_pair_start = true;
 4641                                break;
 4642                            }
 4643                        }
 4644                        if pair.end.as_str() == text.as_ref() && bracket_pair_matching_end.is_none()
 4645                        {
 4646                            // take first bracket pair matching end, but don't break in case a later bracket
 4647                            // pair matches start
 4648                            bracket_pair_matching_end = Some(pair.clone());
 4649                        }
 4650                    }
 4651                    if let Some(end) = bracket_pair_matching_end
 4652                        && bracket_pair.is_none()
 4653                    {
 4654                        bracket_pair = Some(end);
 4655                        is_bracket_pair_end = true;
 4656                    }
 4657                }
 4658
 4659                if let Some(bracket_pair) = bracket_pair {
 4660                    let snapshot_settings = snapshot.language_settings_at(selection.start, cx);
 4661                    let autoclose = self.use_autoclose && snapshot_settings.use_autoclose;
 4662                    let auto_surround =
 4663                        self.use_auto_surround && snapshot_settings.use_auto_surround;
 4664                    if selection.is_empty() {
 4665                        if is_bracket_pair_start {
 4666                            // If the inserted text is a suffix of an opening bracket and the
 4667                            // selection is preceded by the rest of the opening bracket, then
 4668                            // insert the closing bracket.
 4669                            let following_text_allows_autoclose = snapshot
 4670                                .chars_at(selection.start)
 4671                                .next()
 4672                                .is_none_or(|c| scope.should_autoclose_before(c));
 4673
 4674                            let preceding_text_allows_autoclose = selection.start.column == 0
 4675                                || snapshot
 4676                                    .reversed_chars_at(selection.start)
 4677                                    .next()
 4678                                    .is_none_or(|c| {
 4679                                        bracket_pair.start != bracket_pair.end
 4680                                            || !snapshot
 4681                                                .char_classifier_at(selection.start)
 4682                                                .is_word(c)
 4683                                    });
 4684
 4685                            let is_closing_quote = if bracket_pair.end == bracket_pair.start
 4686                                && bracket_pair.start.len() == 1
 4687                            {
 4688                                let target = bracket_pair.start.chars().next().unwrap();
 4689                                let mut byte_offset = 0u32;
 4690                                let current_line_count = snapshot
 4691                                    .reversed_chars_at(selection.start)
 4692                                    .take_while(|&c| c != '\n')
 4693                                    .filter(|c| {
 4694                                        byte_offset += c.len_utf8() as u32;
 4695                                        if *c != target {
 4696                                            return false;
 4697                                        }
 4698
 4699                                        let point = Point::new(
 4700                                            selection.start.row,
 4701                                            selection.start.column.saturating_sub(byte_offset),
 4702                                        );
 4703
 4704                                        let is_enabled = snapshot
 4705                                            .language_scope_at(point)
 4706                                            .and_then(|scope| {
 4707                                                scope
 4708                                                    .brackets()
 4709                                                    .find(|(pair, _)| {
 4710                                                        pair.start == bracket_pair.start
 4711                                                    })
 4712                                                    .map(|(_, enabled)| enabled)
 4713                                            })
 4714                                            .unwrap_or(true);
 4715
 4716                                        let is_delimiter = snapshot
 4717                                            .language_scope_at(Point::new(
 4718                                                point.row,
 4719                                                point.column + 1,
 4720                                            ))
 4721                                            .and_then(|scope| {
 4722                                                scope
 4723                                                    .brackets()
 4724                                                    .find(|(pair, _)| {
 4725                                                        pair.start == bracket_pair.start
 4726                                                    })
 4727                                                    .map(|(_, enabled)| !enabled)
 4728                                            })
 4729                                            .unwrap_or(false);
 4730
 4731                                        is_enabled && !is_delimiter
 4732                                    })
 4733                                    .count();
 4734                                current_line_count % 2 == 1
 4735                            } else {
 4736                                false
 4737                            };
 4738
 4739                            if autoclose
 4740                                && bracket_pair.close
 4741                                && following_text_allows_autoclose
 4742                                && preceding_text_allows_autoclose
 4743                                && !is_closing_quote
 4744                            {
 4745                                let anchor = snapshot.anchor_before(selection.end);
 4746                                new_selections.push((selection.map(|_| anchor), text.len()));
 4747                                new_autoclose_regions.push((
 4748                                    anchor,
 4749                                    text.len(),
 4750                                    selection.id,
 4751                                    bracket_pair.clone(),
 4752                                ));
 4753                                edits.push((
 4754                                    selection.range(),
 4755                                    format!("{}{}", text, bracket_pair.end).into(),
 4756                                ));
 4757                                bracket_inserted = true;
 4758                                continue;
 4759                            }
 4760                        }
 4761
 4762                        if let Some(region) = autoclose_region {
 4763                            // If the selection is followed by an auto-inserted closing bracket,
 4764                            // then don't insert that closing bracket again; just move the selection
 4765                            // past the closing bracket.
 4766                            let should_skip = selection.end == region.range.end.to_point(&snapshot)
 4767                                && text.as_ref() == region.pair.end.as_str()
 4768                                && snapshot.contains_str_at(region.range.end, text.as_ref());
 4769                            if should_skip {
 4770                                let anchor = snapshot.anchor_after(selection.end);
 4771                                new_selections
 4772                                    .push((selection.map(|_| anchor), region.pair.end.len()));
 4773                                continue;
 4774                            }
 4775                        }
 4776
 4777                        let always_treat_brackets_as_autoclosed = snapshot
 4778                            .language_settings_at(selection.start, cx)
 4779                            .always_treat_brackets_as_autoclosed;
 4780                        if always_treat_brackets_as_autoclosed
 4781                            && is_bracket_pair_end
 4782                            && snapshot.contains_str_at(selection.end, text.as_ref())
 4783                        {
 4784                            // Otherwise, when `always_treat_brackets_as_autoclosed` is set to `true
 4785                            // and the inserted text is a closing bracket and the selection is followed
 4786                            // by the closing bracket then move the selection past the closing bracket.
 4787                            let anchor = snapshot.anchor_after(selection.end);
 4788                            new_selections.push((selection.map(|_| anchor), text.len()));
 4789                            continue;
 4790                        }
 4791                    }
 4792                    // If an opening bracket is 1 character long and is typed while
 4793                    // text is selected, then surround that text with the bracket pair.
 4794                    else if auto_surround
 4795                        && bracket_pair.surround
 4796                        && is_bracket_pair_start
 4797                        && bracket_pair.start.chars().count() == 1
 4798                    {
 4799                        edits.push((selection.start..selection.start, text.clone()));
 4800                        edits.push((
 4801                            selection.end..selection.end,
 4802                            bracket_pair.end.as_str().into(),
 4803                        ));
 4804                        bracket_inserted = true;
 4805                        new_selections.push((
 4806                            Selection {
 4807                                id: selection.id,
 4808                                start: snapshot.anchor_after(selection.start),
 4809                                end: snapshot.anchor_before(selection.end),
 4810                                reversed: selection.reversed,
 4811                                goal: selection.goal,
 4812                            },
 4813                            0,
 4814                        ));
 4815                        continue;
 4816                    }
 4817                }
 4818            }
 4819
 4820            if self.auto_replace_emoji_shortcode
 4821                && selection.is_empty()
 4822                && text.as_ref().ends_with(':')
 4823                && let Some(possible_emoji_short_code) =
 4824                    Self::find_possible_emoji_shortcode_at_position(&snapshot, selection.start)
 4825                && !possible_emoji_short_code.is_empty()
 4826                && let Some(emoji) = emojis::get_by_shortcode(&possible_emoji_short_code)
 4827            {
 4828                let emoji_shortcode_start = Point::new(
 4829                    selection.start.row,
 4830                    selection.start.column - possible_emoji_short_code.len() as u32 - 1,
 4831                );
 4832
 4833                // Remove shortcode from buffer
 4834                edits.push((
 4835                    emoji_shortcode_start..selection.start,
 4836                    "".to_string().into(),
 4837                ));
 4838                new_selections.push((
 4839                    Selection {
 4840                        id: selection.id,
 4841                        start: snapshot.anchor_after(emoji_shortcode_start),
 4842                        end: snapshot.anchor_before(selection.start),
 4843                        reversed: selection.reversed,
 4844                        goal: selection.goal,
 4845                    },
 4846                    0,
 4847                ));
 4848
 4849                // Insert emoji
 4850                let selection_start_anchor = snapshot.anchor_after(selection.start);
 4851                new_selections.push((selection.map(|_| selection_start_anchor), 0));
 4852                edits.push((selection.start..selection.end, emoji.to_string().into()));
 4853
 4854                continue;
 4855            }
 4856
 4857            let next_is_adjacent = regions
 4858                .peek()
 4859                .is_some_and(|(next, _)| selection.end == next.start);
 4860
 4861            // If not handling any auto-close operation, then just replace the selected
 4862            // text with the given input and move the selection to the end of the
 4863            // newly inserted text.
 4864            let anchor = if in_adjacent_group || next_is_adjacent {
 4865                // After edits the right bias would shift those anchor to the next visible fragment
 4866                // but we want to resolve to the previous one
 4867                snapshot.anchor_before(selection.end)
 4868            } else {
 4869                snapshot.anchor_after(selection.end)
 4870            };
 4871
 4872            if !self.linked_edit_ranges.is_empty() {
 4873                let start_anchor = snapshot.anchor_before(selection.start);
 4874
 4875                let is_word_char = text.chars().next().is_none_or(|char| {
 4876                    let classifier = snapshot
 4877                        .char_classifier_at(start_anchor.to_offset(&snapshot))
 4878                        .scope_context(Some(CharScopeContext::LinkedEdit));
 4879                    classifier.is_word(char)
 4880                });
 4881
 4882                if is_word_char {
 4883                    if let Some(ranges) = self
 4884                        .linked_editing_ranges_for(start_anchor.text_anchor..anchor.text_anchor, cx)
 4885                    {
 4886                        for (buffer, edits) in ranges {
 4887                            linked_edits
 4888                                .entry(buffer.clone())
 4889                                .or_default()
 4890                                .extend(edits.into_iter().map(|range| (range, text.clone())));
 4891                        }
 4892                    }
 4893                } else {
 4894                    clear_linked_edit_ranges = true;
 4895                }
 4896            }
 4897
 4898            new_selections.push((selection.map(|_| anchor), 0));
 4899            edits.push((selection.start..selection.end, text.clone()));
 4900
 4901            has_adjacent_edits |= next_is_adjacent;
 4902            in_adjacent_group = next_is_adjacent;
 4903        }
 4904
 4905        if all_selections_read_only {
 4906            return;
 4907        }
 4908
 4909        drop(regions);
 4910        drop(snapshot);
 4911
 4912        self.transact(window, cx, |this, window, cx| {
 4913            if clear_linked_edit_ranges {
 4914                this.linked_edit_ranges.clear();
 4915            }
 4916            let initial_buffer_versions =
 4917                jsx_tag_auto_close::construct_initial_buffer_versions_map(this, &edits, cx);
 4918
 4919            this.buffer.update(cx, |buffer, cx| {
 4920                if has_adjacent_edits {
 4921                    buffer.edit_non_coalesce(edits, this.autoindent_mode.clone(), cx);
 4922                } else {
 4923                    buffer.edit(edits, this.autoindent_mode.clone(), cx);
 4924                }
 4925            });
 4926            for (buffer, edits) in linked_edits {
 4927                buffer.update(cx, |buffer, cx| {
 4928                    let snapshot = buffer.snapshot();
 4929                    let edits = edits
 4930                        .into_iter()
 4931                        .map(|(range, text)| {
 4932                            use text::ToPoint as TP;
 4933                            let end_point = TP::to_point(&range.end, &snapshot);
 4934                            let start_point = TP::to_point(&range.start, &snapshot);
 4935                            (start_point..end_point, text)
 4936                        })
 4937                        .sorted_by_key(|(range, _)| range.start);
 4938                    buffer.edit(edits, None, cx);
 4939                })
 4940            }
 4941            let new_anchor_selections = new_selections.iter().map(|e| &e.0);
 4942            let new_selection_deltas = new_selections.iter().map(|e| e.1);
 4943            let map = this.display_map.update(cx, |map, cx| map.snapshot(cx));
 4944            let new_selections = resolve_selections_wrapping_blocks::<MultiBufferOffset, _>(
 4945                new_anchor_selections,
 4946                &map,
 4947            )
 4948            .zip(new_selection_deltas)
 4949            .map(|(selection, delta)| Selection {
 4950                id: selection.id,
 4951                start: selection.start + delta,
 4952                end: selection.end + delta,
 4953                reversed: selection.reversed,
 4954                goal: SelectionGoal::None,
 4955            })
 4956            .collect::<Vec<_>>();
 4957
 4958            let mut i = 0;
 4959            for (position, delta, selection_id, pair) in new_autoclose_regions {
 4960                let position = position.to_offset(map.buffer_snapshot()) + delta;
 4961                let start = map.buffer_snapshot().anchor_before(position);
 4962                let end = map.buffer_snapshot().anchor_after(position);
 4963                while let Some(existing_state) = this.autoclose_regions.get(i) {
 4964                    match existing_state
 4965                        .range
 4966                        .start
 4967                        .cmp(&start, map.buffer_snapshot())
 4968                    {
 4969                        Ordering::Less => i += 1,
 4970                        Ordering::Greater => break,
 4971                        Ordering::Equal => {
 4972                            match end.cmp(&existing_state.range.end, map.buffer_snapshot()) {
 4973                                Ordering::Less => i += 1,
 4974                                Ordering::Equal => break,
 4975                                Ordering::Greater => break,
 4976                            }
 4977                        }
 4978                    }
 4979                }
 4980                this.autoclose_regions.insert(
 4981                    i,
 4982                    AutocloseRegion {
 4983                        selection_id,
 4984                        range: start..end,
 4985                        pair,
 4986                    },
 4987                );
 4988            }
 4989
 4990            let had_active_edit_prediction = this.has_active_edit_prediction();
 4991            this.change_selections(
 4992                SelectionEffects::scroll(Autoscroll::fit()).completions(false),
 4993                window,
 4994                cx,
 4995                |s| s.select(new_selections),
 4996            );
 4997
 4998            if !bracket_inserted
 4999                && let Some(on_type_format_task) =
 5000                    this.trigger_on_type_formatting(text.to_string(), window, cx)
 5001            {
 5002                on_type_format_task.detach_and_log_err(cx);
 5003            }
 5004
 5005            let editor_settings = EditorSettings::get_global(cx);
 5006            if bracket_inserted
 5007                && (editor_settings.auto_signature_help
 5008                    || editor_settings.show_signature_help_after_edits)
 5009            {
 5010                this.show_signature_help(&ShowSignatureHelp, window, cx);
 5011            }
 5012
 5013            let trigger_in_words =
 5014                this.show_edit_predictions_in_menu() || !had_active_edit_prediction;
 5015            if this.hard_wrap.is_some() {
 5016                let latest: Range<Point> = this.selections.newest(&map).range();
 5017                if latest.is_empty()
 5018                    && this
 5019                        .buffer()
 5020                        .read(cx)
 5021                        .snapshot(cx)
 5022                        .line_len(MultiBufferRow(latest.start.row))
 5023                        == latest.start.column
 5024                {
 5025                    this.rewrap_impl(
 5026                        RewrapOptions {
 5027                            override_language_settings: true,
 5028                            preserve_existing_whitespace: true,
 5029                        },
 5030                        cx,
 5031                    )
 5032                }
 5033            }
 5034            this.trigger_completion_on_input(&text, trigger_in_words, window, cx);
 5035            refresh_linked_ranges(this, window, cx);
 5036            this.refresh_edit_prediction(true, false, window, cx);
 5037            jsx_tag_auto_close::handle_from(this, initial_buffer_versions, window, cx);
 5038        });
 5039    }
 5040
 5041    fn find_possible_emoji_shortcode_at_position(
 5042        snapshot: &MultiBufferSnapshot,
 5043        position: Point,
 5044    ) -> Option<String> {
 5045        let mut chars = Vec::new();
 5046        let mut found_colon = false;
 5047        for char in snapshot.reversed_chars_at(position).take(100) {
 5048            // Found a possible emoji shortcode in the middle of the buffer
 5049            if found_colon {
 5050                if char.is_whitespace() {
 5051                    chars.reverse();
 5052                    return Some(chars.iter().collect());
 5053                }
 5054                // If the previous character is not a whitespace, we are in the middle of a word
 5055                // and we only want to complete the shortcode if the word is made up of other emojis
 5056                let mut containing_word = String::new();
 5057                for ch in snapshot
 5058                    .reversed_chars_at(position)
 5059                    .skip(chars.len() + 1)
 5060                    .take(100)
 5061                {
 5062                    if ch.is_whitespace() {
 5063                        break;
 5064                    }
 5065                    containing_word.push(ch);
 5066                }
 5067                let containing_word = containing_word.chars().rev().collect::<String>();
 5068                if util::word_consists_of_emojis(containing_word.as_str()) {
 5069                    chars.reverse();
 5070                    return Some(chars.iter().collect());
 5071                }
 5072            }
 5073
 5074            if char.is_whitespace() || !char.is_ascii() {
 5075                return None;
 5076            }
 5077            if char == ':' {
 5078                found_colon = true;
 5079            } else {
 5080                chars.push(char);
 5081            }
 5082        }
 5083        // Found a possible emoji shortcode at the beginning of the buffer
 5084        chars.reverse();
 5085        Some(chars.iter().collect())
 5086    }
 5087
 5088    pub fn newline(&mut self, _: &Newline, window: &mut Window, cx: &mut Context<Self>) {
 5089        self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
 5090        self.transact(window, cx, |this, window, cx| {
 5091            let (edits_with_flags, selection_info): (Vec<_>, Vec<_>) = {
 5092                let selections = this
 5093                    .selections
 5094                    .all::<MultiBufferOffset>(&this.display_snapshot(cx));
 5095                let multi_buffer = this.buffer.read(cx);
 5096                let buffer = multi_buffer.snapshot(cx);
 5097                selections
 5098                    .iter()
 5099                    .map(|selection| {
 5100                        let start_point = selection.start.to_point(&buffer);
 5101                        let mut existing_indent =
 5102                            buffer.indent_size_for_line(MultiBufferRow(start_point.row));
 5103                        existing_indent.len = cmp::min(existing_indent.len, start_point.column);
 5104                        let start = selection.start;
 5105                        let end = selection.end;
 5106                        let selection_is_empty = start == end;
 5107                        let language_scope = buffer.language_scope_at(start);
 5108                        let (delimiter, newline_config) = if let Some(language) = &language_scope {
 5109                            let needs_extra_newline = NewlineConfig::insert_extra_newline_brackets(
 5110                                &buffer,
 5111                                start..end,
 5112                                language,
 5113                            )
 5114                                || NewlineConfig::insert_extra_newline_tree_sitter(
 5115                                    &buffer,
 5116                                    start..end,
 5117                                );
 5118
 5119                            let mut newline_config = NewlineConfig::Newline {
 5120                                additional_indent: IndentSize::spaces(0),
 5121                                extra_line_additional_indent: if needs_extra_newline {
 5122                                    Some(IndentSize::spaces(0))
 5123                                } else {
 5124                                    None
 5125                                },
 5126                                prevent_auto_indent: false,
 5127                            };
 5128
 5129                            let comment_delimiter = maybe!({
 5130                                if !selection_is_empty {
 5131                                    return None;
 5132                                }
 5133
 5134                                if !multi_buffer.language_settings(cx).extend_comment_on_newline {
 5135                                    return None;
 5136                                }
 5137
 5138                                return comment_delimiter_for_newline(
 5139                                    &start_point,
 5140                                    &buffer,
 5141                                    language,
 5142                                );
 5143                            });
 5144
 5145                            let doc_delimiter = maybe!({
 5146                                if !selection_is_empty {
 5147                                    return None;
 5148                                }
 5149
 5150                                if !multi_buffer.language_settings(cx).extend_comment_on_newline {
 5151                                    return None;
 5152                                }
 5153
 5154                                return documentation_delimiter_for_newline(
 5155                                    &start_point,
 5156                                    &buffer,
 5157                                    language,
 5158                                    &mut newline_config,
 5159                                );
 5160                            });
 5161
 5162                            let list_delimiter = maybe!({
 5163                                if !selection_is_empty {
 5164                                    return None;
 5165                                }
 5166
 5167                                if !multi_buffer.language_settings(cx).extend_list_on_newline {
 5168                                    return None;
 5169                                }
 5170
 5171                                return list_delimiter_for_newline(
 5172                                    &start_point,
 5173                                    &buffer,
 5174                                    language,
 5175                                    &mut newline_config,
 5176                                );
 5177                            });
 5178
 5179                            (
 5180                                comment_delimiter.or(doc_delimiter).or(list_delimiter),
 5181                                newline_config,
 5182                            )
 5183                        } else {
 5184                            (
 5185                                None,
 5186                                NewlineConfig::Newline {
 5187                                    additional_indent: IndentSize::spaces(0),
 5188                                    extra_line_additional_indent: None,
 5189                                    prevent_auto_indent: false,
 5190                                },
 5191                            )
 5192                        };
 5193
 5194                        let (edit_start, new_text, prevent_auto_indent) = match &newline_config {
 5195                            NewlineConfig::ClearCurrentLine => {
 5196                                let row_start =
 5197                                    buffer.point_to_offset(Point::new(start_point.row, 0));
 5198                                (row_start, String::new(), false)
 5199                            }
 5200                            NewlineConfig::UnindentCurrentLine { continuation } => {
 5201                                let row_start =
 5202                                    buffer.point_to_offset(Point::new(start_point.row, 0));
 5203                                let tab_size = buffer.language_settings_at(start, cx).tab_size;
 5204                                let tab_size_indent = IndentSize::spaces(tab_size.get());
 5205                                let reduced_indent =
 5206                                    existing_indent.with_delta(Ordering::Less, tab_size_indent);
 5207                                let mut new_text = String::new();
 5208                                new_text.extend(reduced_indent.chars());
 5209                                new_text.push_str(continuation);
 5210                                (row_start, new_text, true)
 5211                            }
 5212                            NewlineConfig::Newline {
 5213                                additional_indent,
 5214                                extra_line_additional_indent,
 5215                                prevent_auto_indent,
 5216                            } => {
 5217                                let capacity_for_delimiter =
 5218                                    delimiter.as_deref().map(str::len).unwrap_or_default();
 5219                                let extra_line_len = extra_line_additional_indent
 5220                                    .map(|i| 1 + existing_indent.len as usize + i.len as usize)
 5221                                    .unwrap_or(0);
 5222                                let mut new_text = String::with_capacity(
 5223                                    1 + capacity_for_delimiter
 5224                                        + existing_indent.len as usize
 5225                                        + additional_indent.len as usize
 5226                                        + extra_line_len,
 5227                                );
 5228                                new_text.push('\n');
 5229                                new_text.extend(existing_indent.chars());
 5230                                new_text.extend(additional_indent.chars());
 5231                                if let Some(delimiter) = &delimiter {
 5232                                    new_text.push_str(delimiter);
 5233                                }
 5234                                if let Some(extra_indent) = extra_line_additional_indent {
 5235                                    new_text.push('\n');
 5236                                    new_text.extend(existing_indent.chars());
 5237                                    new_text.extend(extra_indent.chars());
 5238                                }
 5239                                (start, new_text, *prevent_auto_indent)
 5240                            }
 5241                        };
 5242
 5243                        let anchor = buffer.anchor_after(end);
 5244                        let new_selection = selection.map(|_| anchor);
 5245                        (
 5246                            ((edit_start..end, new_text), prevent_auto_indent),
 5247                            (newline_config.has_extra_line(), new_selection),
 5248                        )
 5249                    })
 5250                    .unzip()
 5251            };
 5252
 5253            let mut auto_indent_edits = Vec::new();
 5254            let mut edits = Vec::new();
 5255            for (edit, prevent_auto_indent) in edits_with_flags {
 5256                if prevent_auto_indent {
 5257                    edits.push(edit);
 5258                } else {
 5259                    auto_indent_edits.push(edit);
 5260                }
 5261            }
 5262            if !edits.is_empty() {
 5263                this.edit(edits, cx);
 5264            }
 5265            if !auto_indent_edits.is_empty() {
 5266                this.edit_with_autoindent(auto_indent_edits, cx);
 5267            }
 5268
 5269            let buffer = this.buffer.read(cx).snapshot(cx);
 5270            let new_selections = selection_info
 5271                .into_iter()
 5272                .map(|(extra_newline_inserted, new_selection)| {
 5273                    let mut cursor = new_selection.end.to_point(&buffer);
 5274                    if extra_newline_inserted {
 5275                        cursor.row -= 1;
 5276                        cursor.column = buffer.line_len(MultiBufferRow(cursor.row));
 5277                    }
 5278                    new_selection.map(|_| cursor)
 5279                })
 5280                .collect();
 5281
 5282            this.change_selections(Default::default(), window, cx, |s| s.select(new_selections));
 5283            this.refresh_edit_prediction(true, false, window, cx);
 5284            if let Some(task) = this.trigger_on_type_formatting("\n".to_owned(), window, cx) {
 5285                task.detach_and_log_err(cx);
 5286            }
 5287        });
 5288    }
 5289
 5290    pub fn newline_above(&mut self, _: &NewlineAbove, window: &mut Window, cx: &mut Context<Self>) {
 5291        self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
 5292
 5293        let buffer = self.buffer.read(cx);
 5294        let snapshot = buffer.snapshot(cx);
 5295
 5296        let mut edits = Vec::new();
 5297        let mut rows = Vec::new();
 5298
 5299        for (rows_inserted, selection) in self
 5300            .selections
 5301            .all_adjusted(&self.display_snapshot(cx))
 5302            .into_iter()
 5303            .enumerate()
 5304        {
 5305            let cursor = selection.head();
 5306            let row = cursor.row;
 5307
 5308            let start_of_line = snapshot.clip_point(Point::new(row, 0), Bias::Left);
 5309
 5310            let newline = "\n".to_string();
 5311            edits.push((start_of_line..start_of_line, newline));
 5312
 5313            rows.push(row + rows_inserted as u32);
 5314        }
 5315
 5316        self.transact(window, cx, |editor, window, cx| {
 5317            editor.edit(edits, cx);
 5318
 5319            editor.change_selections(Default::default(), window, cx, |s| {
 5320                let mut index = 0;
 5321                s.move_cursors_with(&mut |map, _, _| {
 5322                    let row = rows[index];
 5323                    index += 1;
 5324
 5325                    let point = Point::new(row, 0);
 5326                    let boundary = map.next_line_boundary(point).1;
 5327                    let clipped = map.clip_point(boundary, Bias::Left);
 5328
 5329                    (clipped, SelectionGoal::None)
 5330                });
 5331            });
 5332
 5333            let mut indent_edits = Vec::new();
 5334            let multibuffer_snapshot = editor.buffer.read(cx).snapshot(cx);
 5335            for row in rows {
 5336                let indents = multibuffer_snapshot.suggested_indents(row..row + 1, cx);
 5337                for (row, indent) in indents {
 5338                    if indent.len == 0 {
 5339                        continue;
 5340                    }
 5341
 5342                    let text = match indent.kind {
 5343                        IndentKind::Space => " ".repeat(indent.len as usize),
 5344                        IndentKind::Tab => "\t".repeat(indent.len as usize),
 5345                    };
 5346                    let point = Point::new(row.0, 0);
 5347                    indent_edits.push((point..point, text));
 5348                }
 5349            }
 5350            editor.edit(indent_edits, cx);
 5351            if let Some(format) = editor.trigger_on_type_formatting("\n".to_owned(), window, cx) {
 5352                format.detach_and_log_err(cx);
 5353            }
 5354        });
 5355    }
 5356
 5357    pub fn newline_below(&mut self, _: &NewlineBelow, window: &mut Window, cx: &mut Context<Self>) {
 5358        self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
 5359
 5360        let mut buffer_edits: HashMap<EntityId, (Entity<Buffer>, Vec<Point>)> = HashMap::default();
 5361        let mut rows = Vec::new();
 5362        let mut rows_inserted = 0;
 5363
 5364        for selection in self.selections.all_adjusted(&self.display_snapshot(cx)) {
 5365            let cursor = selection.head();
 5366            let row = cursor.row;
 5367
 5368            let point = Point::new(row, 0);
 5369            let Some((buffer_handle, buffer_point, _)) =
 5370                self.buffer.read(cx).point_to_buffer_point(point, cx)
 5371            else {
 5372                continue;
 5373            };
 5374
 5375            buffer_edits
 5376                .entry(buffer_handle.entity_id())
 5377                .or_insert_with(|| (buffer_handle, Vec::new()))
 5378                .1
 5379                .push(buffer_point);
 5380
 5381            rows_inserted += 1;
 5382            rows.push(row + rows_inserted);
 5383        }
 5384
 5385        self.transact(window, cx, |editor, window, cx| {
 5386            for (_, (buffer_handle, points)) in &buffer_edits {
 5387                buffer_handle.update(cx, |buffer, cx| {
 5388                    let edits: Vec<_> = points
 5389                        .iter()
 5390                        .map(|point| {
 5391                            let target = Point::new(point.row + 1, 0);
 5392                            let start_of_line = buffer.point_to_offset(target).min(buffer.len());
 5393                            (start_of_line..start_of_line, "\n")
 5394                        })
 5395                        .collect();
 5396                    buffer.edit(edits, None, cx);
 5397                });
 5398            }
 5399
 5400            editor.change_selections(Default::default(), window, cx, |s| {
 5401                let mut index = 0;
 5402                s.move_cursors_with(&mut |map, _, _| {
 5403                    let row = rows[index];
 5404                    index += 1;
 5405
 5406                    let point = Point::new(row, 0);
 5407                    let boundary = map.next_line_boundary(point).1;
 5408                    let clipped = map.clip_point(boundary, Bias::Left);
 5409
 5410                    (clipped, SelectionGoal::None)
 5411                });
 5412            });
 5413
 5414            let mut indent_edits = Vec::new();
 5415            let multibuffer_snapshot = editor.buffer.read(cx).snapshot(cx);
 5416            for row in rows {
 5417                let indents = multibuffer_snapshot.suggested_indents(row..row + 1, cx);
 5418                for (row, indent) in indents {
 5419                    if indent.len == 0 {
 5420                        continue;
 5421                    }
 5422
 5423                    let text = match indent.kind {
 5424                        IndentKind::Space => " ".repeat(indent.len as usize),
 5425                        IndentKind::Tab => "\t".repeat(indent.len as usize),
 5426                    };
 5427                    let point = Point::new(row.0, 0);
 5428                    indent_edits.push((point..point, text));
 5429                }
 5430            }
 5431            editor.edit(indent_edits, cx);
 5432            if let Some(format) = editor.trigger_on_type_formatting("\n".to_owned(), window, cx) {
 5433                format.detach_and_log_err(cx);
 5434            }
 5435        });
 5436    }
 5437
 5438    pub fn insert(&mut self, text: &str, window: &mut Window, cx: &mut Context<Self>) {
 5439        let autoindent = text.is_empty().not().then(|| AutoindentMode::Block {
 5440            original_indent_columns: Vec::new(),
 5441        });
 5442        self.insert_with_autoindent_mode(text, autoindent, window, cx);
 5443    }
 5444
 5445    fn insert_with_autoindent_mode(
 5446        &mut self,
 5447        text: &str,
 5448        autoindent_mode: Option<AutoindentMode>,
 5449        window: &mut Window,
 5450        cx: &mut Context<Self>,
 5451    ) {
 5452        if self.read_only(cx) {
 5453            return;
 5454        }
 5455
 5456        let text: Arc<str> = text.into();
 5457        self.transact(window, cx, |this, window, cx| {
 5458            let old_selections = this.selections.all_adjusted(&this.display_snapshot(cx));
 5459            let selection_anchors = this.buffer.update(cx, |buffer, cx| {
 5460                let anchors = {
 5461                    let snapshot = buffer.read(cx);
 5462                    old_selections
 5463                        .iter()
 5464                        .map(|s| {
 5465                            let anchor = snapshot.anchor_after(s.head());
 5466                            s.map(|_| anchor)
 5467                        })
 5468                        .collect::<Vec<_>>()
 5469                };
 5470                buffer.edit(
 5471                    old_selections
 5472                        .iter()
 5473                        .map(|s| (s.start..s.end, text.clone())),
 5474                    autoindent_mode,
 5475                    cx,
 5476                );
 5477                anchors
 5478            });
 5479
 5480            this.change_selections(Default::default(), window, cx, |s| {
 5481                s.select_anchors(selection_anchors);
 5482            });
 5483
 5484            cx.notify();
 5485        });
 5486    }
 5487
 5488    fn trigger_completion_on_input(
 5489        &mut self,
 5490        text: &str,
 5491        trigger_in_words: bool,
 5492        window: &mut Window,
 5493        cx: &mut Context<Self>,
 5494    ) {
 5495        let completions_source = self
 5496            .context_menu
 5497            .borrow()
 5498            .as_ref()
 5499            .and_then(|menu| match menu {
 5500                CodeContextMenu::Completions(completions_menu) => Some(completions_menu.source),
 5501                CodeContextMenu::CodeActions(_) => None,
 5502            });
 5503
 5504        match completions_source {
 5505            Some(CompletionsMenuSource::Words { .. }) => {
 5506                self.open_or_update_completions_menu(
 5507                    Some(CompletionsMenuSource::Words {
 5508                        ignore_threshold: false,
 5509                    }),
 5510                    None,
 5511                    trigger_in_words,
 5512                    window,
 5513                    cx,
 5514                );
 5515            }
 5516            _ => self.open_or_update_completions_menu(
 5517                None,
 5518                Some(text.to_owned()).filter(|x| !x.is_empty()),
 5519                true,
 5520                window,
 5521                cx,
 5522            ),
 5523        }
 5524    }
 5525
 5526    /// If any empty selections is touching the start of its innermost containing autoclose
 5527    /// region, expand it to select the brackets.
 5528    fn select_autoclose_pair(&mut self, window: &mut Window, cx: &mut Context<Self>) {
 5529        let selections = self
 5530            .selections
 5531            .all::<MultiBufferOffset>(&self.display_snapshot(cx));
 5532        let buffer = self.buffer.read(cx).read(cx);
 5533        let new_selections = self
 5534            .selections_with_autoclose_regions(selections, &buffer)
 5535            .map(|(mut selection, region)| {
 5536                if !selection.is_empty() {
 5537                    return selection;
 5538                }
 5539
 5540                if let Some(region) = region {
 5541                    let mut range = region.range.to_offset(&buffer);
 5542                    if selection.start == range.start && range.start.0 >= region.pair.start.len() {
 5543                        range.start -= region.pair.start.len();
 5544                        if buffer.contains_str_at(range.start, &region.pair.start)
 5545                            && buffer.contains_str_at(range.end, &region.pair.end)
 5546                        {
 5547                            range.end += region.pair.end.len();
 5548                            selection.start = range.start;
 5549                            selection.end = range.end;
 5550
 5551                            return selection;
 5552                        }
 5553                    }
 5554                }
 5555
 5556                let always_treat_brackets_as_autoclosed = buffer
 5557                    .language_settings_at(selection.start, cx)
 5558                    .always_treat_brackets_as_autoclosed;
 5559
 5560                if !always_treat_brackets_as_autoclosed {
 5561                    return selection;
 5562                }
 5563
 5564                if let Some(scope) = buffer.language_scope_at(selection.start) {
 5565                    for (pair, enabled) in scope.brackets() {
 5566                        if !enabled || !pair.close {
 5567                            continue;
 5568                        }
 5569
 5570                        if buffer.contains_str_at(selection.start, &pair.end) {
 5571                            let pair_start_len = pair.start.len();
 5572                            if buffer.contains_str_at(
 5573                                selection.start.saturating_sub_usize(pair_start_len),
 5574                                &pair.start,
 5575                            ) {
 5576                                selection.start -= pair_start_len;
 5577                                selection.end += pair.end.len();
 5578
 5579                                return selection;
 5580                            }
 5581                        }
 5582                    }
 5583                }
 5584
 5585                selection
 5586            })
 5587            .collect();
 5588
 5589        drop(buffer);
 5590        self.change_selections(SelectionEffects::no_scroll(), window, cx, |selections| {
 5591            selections.select(new_selections)
 5592        });
 5593    }
 5594
 5595    /// Iterate the given selections, and for each one, find the smallest surrounding
 5596    /// autoclose region. This uses the ordering of the selections and the autoclose
 5597    /// regions to avoid repeated comparisons.
 5598    fn selections_with_autoclose_regions<'a, D: ToOffset + Clone>(
 5599        &'a self,
 5600        selections: impl IntoIterator<Item = Selection<D>>,
 5601        buffer: &'a MultiBufferSnapshot,
 5602    ) -> impl Iterator<Item = (Selection<D>, Option<&'a AutocloseRegion>)> {
 5603        let mut i = 0;
 5604        let mut regions = self.autoclose_regions.as_slice();
 5605        selections.into_iter().map(move |selection| {
 5606            let range = selection.start.to_offset(buffer)..selection.end.to_offset(buffer);
 5607
 5608            let mut enclosing = None;
 5609            while let Some(pair_state) = regions.get(i) {
 5610                if pair_state.range.end.to_offset(buffer) < range.start {
 5611                    regions = &regions[i + 1..];
 5612                    i = 0;
 5613                } else if pair_state.range.start.to_offset(buffer) > range.end {
 5614                    break;
 5615                } else {
 5616                    if pair_state.selection_id == selection.id {
 5617                        enclosing = Some(pair_state);
 5618                    }
 5619                    i += 1;
 5620                }
 5621            }
 5622
 5623            (selection, enclosing)
 5624        })
 5625    }
 5626
 5627    /// Remove any autoclose regions that no longer contain their selection or have invalid anchors in ranges.
 5628    fn invalidate_autoclose_regions(
 5629        &mut self,
 5630        mut selections: &[Selection<Anchor>],
 5631        buffer: &MultiBufferSnapshot,
 5632    ) {
 5633        self.autoclose_regions.retain(|state| {
 5634            if !state.range.start.is_valid(buffer) || !state.range.end.is_valid(buffer) {
 5635                return false;
 5636            }
 5637
 5638            let mut i = 0;
 5639            while let Some(selection) = selections.get(i) {
 5640                if selection.end.cmp(&state.range.start, buffer).is_lt() {
 5641                    selections = &selections[1..];
 5642                    continue;
 5643                }
 5644                if selection.start.cmp(&state.range.end, buffer).is_gt() {
 5645                    break;
 5646                }
 5647                if selection.id == state.selection_id {
 5648                    return true;
 5649                } else {
 5650                    i += 1;
 5651                }
 5652            }
 5653            false
 5654        });
 5655    }
 5656
 5657    fn completion_query(buffer: &MultiBufferSnapshot, position: impl ToOffset) -> Option<String> {
 5658        let offset = position.to_offset(buffer);
 5659        let (word_range, kind) =
 5660            buffer.surrounding_word(offset, Some(CharScopeContext::Completion));
 5661        if offset > word_range.start && kind == Some(CharKind::Word) {
 5662            Some(
 5663                buffer
 5664                    .text_for_range(word_range.start..offset)
 5665                    .collect::<String>(),
 5666            )
 5667        } else {
 5668            None
 5669        }
 5670    }
 5671
 5672    pub fn visible_excerpts(
 5673        &self,
 5674        lsp_related_only: bool,
 5675        cx: &mut Context<Editor>,
 5676    ) -> HashMap<ExcerptId, (Entity<Buffer>, clock::Global, Range<usize>)> {
 5677        let project = self.project().cloned();
 5678        let display_snapshot = self.display_map.update(cx, |map, cx| map.snapshot(cx));
 5679        let multi_buffer = self.buffer().read(cx);
 5680        let multi_buffer_snapshot = multi_buffer.snapshot(cx);
 5681        let multi_buffer_visible_start = self
 5682            .scroll_manager
 5683            .native_anchor(&display_snapshot, cx)
 5684            .anchor
 5685            .to_point(&multi_buffer_snapshot);
 5686        let multi_buffer_visible_end = multi_buffer_snapshot.clip_point(
 5687            multi_buffer_visible_start
 5688                + Point::new(self.visible_line_count().unwrap_or(0.).ceil() as u32, 0),
 5689            Bias::Left,
 5690        );
 5691        multi_buffer_snapshot
 5692            .range_to_buffer_ranges(multi_buffer_visible_start..=multi_buffer_visible_end)
 5693            .into_iter()
 5694            .filter(|(_, excerpt_visible_range, _)| !excerpt_visible_range.is_empty())
 5695            .filter_map(|(buffer, excerpt_visible_range, excerpt_id)| {
 5696                if !lsp_related_only {
 5697                    return Some((
 5698                        excerpt_id,
 5699                        (
 5700                            multi_buffer.buffer(buffer.remote_id()).unwrap(),
 5701                            buffer.version().clone(),
 5702                            excerpt_visible_range.start.0..excerpt_visible_range.end.0,
 5703                        ),
 5704                    ));
 5705                }
 5706
 5707                let project = project.as_ref()?.read(cx);
 5708                let buffer_file = project::File::from_dyn(buffer.file())?;
 5709                let buffer_worktree = project.worktree_for_id(buffer_file.worktree_id(cx), cx)?;
 5710                let worktree_entry = buffer_worktree
 5711                    .read(cx)
 5712                    .entry_for_id(buffer_file.project_entry_id()?)?;
 5713                if worktree_entry.is_ignored {
 5714                    None
 5715                } else {
 5716                    Some((
 5717                        excerpt_id,
 5718                        (
 5719                            multi_buffer.buffer(buffer.remote_id()).unwrap(),
 5720                            buffer.version().clone(),
 5721                            excerpt_visible_range.start.0..excerpt_visible_range.end.0,
 5722                        ),
 5723                    ))
 5724                }
 5725            })
 5726            .collect()
 5727    }
 5728
 5729    pub fn text_layout_details(&self, window: &mut Window, cx: &mut App) -> TextLayoutDetails {
 5730        TextLayoutDetails {
 5731            text_system: window.text_system().clone(),
 5732            editor_style: self.style.clone().unwrap(),
 5733            rem_size: window.rem_size(),
 5734            scroll_anchor: self.scroll_manager.shared_scroll_anchor(cx),
 5735            visible_rows: self.visible_line_count(),
 5736            vertical_scroll_margin: self.scroll_manager.vertical_scroll_margin,
 5737        }
 5738    }
 5739
 5740    fn trigger_on_type_formatting(
 5741        &self,
 5742        input: String,
 5743        window: &mut Window,
 5744        cx: &mut Context<Self>,
 5745    ) -> Option<Task<Result<()>>> {
 5746        if input.chars().count() != 1 {
 5747            return None;
 5748        }
 5749
 5750        let project = self.project()?;
 5751        let position = self.selections.newest_anchor().head();
 5752        let (buffer, buffer_position) = self
 5753            .buffer
 5754            .read(cx)
 5755            .text_anchor_for_position(position, cx)?;
 5756
 5757        let settings = language_settings::language_settings(
 5758            buffer
 5759                .read(cx)
 5760                .language_at(buffer_position)
 5761                .map(|l| l.name()),
 5762            buffer.read(cx).file(),
 5763            cx,
 5764        );
 5765        if !settings.use_on_type_format {
 5766            return None;
 5767        }
 5768
 5769        // OnTypeFormatting returns a list of edits, no need to pass them between Zed instances,
 5770        // hence we do LSP request & edit on host side only — add formats to host's history.
 5771        let push_to_lsp_host_history = true;
 5772        // If this is not the host, append its history with new edits.
 5773        let push_to_client_history = project.read(cx).is_via_collab();
 5774
 5775        let on_type_formatting = project.update(cx, |project, cx| {
 5776            project.on_type_format(
 5777                buffer.clone(),
 5778                buffer_position,
 5779                input,
 5780                push_to_lsp_host_history,
 5781                cx,
 5782            )
 5783        });
 5784        Some(cx.spawn_in(window, async move |editor, cx| {
 5785            if let Some(transaction) = on_type_formatting.await? {
 5786                if push_to_client_history {
 5787                    buffer.update(cx, |buffer, _| {
 5788                        buffer.push_transaction(transaction, Instant::now());
 5789                        buffer.finalize_last_transaction();
 5790                    });
 5791                }
 5792                editor.update(cx, |editor, cx| {
 5793                    editor.refresh_document_highlights(cx);
 5794                })?;
 5795            }
 5796            Ok(())
 5797        }))
 5798    }
 5799
 5800    pub fn show_word_completions(
 5801        &mut self,
 5802        _: &ShowWordCompletions,
 5803        window: &mut Window,
 5804        cx: &mut Context<Self>,
 5805    ) {
 5806        self.open_or_update_completions_menu(
 5807            Some(CompletionsMenuSource::Words {
 5808                ignore_threshold: true,
 5809            }),
 5810            None,
 5811            false,
 5812            window,
 5813            cx,
 5814        );
 5815    }
 5816
 5817    pub fn show_completions(
 5818        &mut self,
 5819        _: &ShowCompletions,
 5820        window: &mut Window,
 5821        cx: &mut Context<Self>,
 5822    ) {
 5823        self.open_or_update_completions_menu(None, None, false, window, cx);
 5824    }
 5825
 5826    fn open_or_update_completions_menu(
 5827        &mut self,
 5828        requested_source: Option<CompletionsMenuSource>,
 5829        trigger: Option<String>,
 5830        trigger_in_words: bool,
 5831        window: &mut Window,
 5832        cx: &mut Context<Self>,
 5833    ) {
 5834        if self.pending_rename.is_some() {
 5835            return;
 5836        }
 5837
 5838        let completions_source = self
 5839            .context_menu
 5840            .borrow()
 5841            .as_ref()
 5842            .and_then(|menu| match menu {
 5843                CodeContextMenu::Completions(completions_menu) => Some(completions_menu.source),
 5844                CodeContextMenu::CodeActions(_) => None,
 5845            });
 5846
 5847        let multibuffer_snapshot = self.buffer.read(cx).read(cx);
 5848
 5849        // Typically `start` == `end`, but with snippet tabstop choices the default choice is
 5850        // inserted and selected. To handle that case, the start of the selection is used so that
 5851        // the menu starts with all choices.
 5852        let position = self
 5853            .selections
 5854            .newest_anchor()
 5855            .start
 5856            .bias_right(&multibuffer_snapshot);
 5857        if position.diff_base_anchor.is_some() {
 5858            return;
 5859        }
 5860        let buffer_position = multibuffer_snapshot.anchor_before(position);
 5861        let Some(buffer) = buffer_position
 5862            .text_anchor
 5863            .buffer_id
 5864            .and_then(|buffer_id| self.buffer.read(cx).buffer(buffer_id))
 5865        else {
 5866            return;
 5867        };
 5868        let buffer_snapshot = buffer.read(cx).snapshot();
 5869
 5870        let menu_is_open = matches!(
 5871            self.context_menu.borrow().as_ref(),
 5872            Some(CodeContextMenu::Completions(_))
 5873        );
 5874
 5875        let language = buffer_snapshot
 5876            .language_at(buffer_position.text_anchor)
 5877            .map(|language| language.name());
 5878
 5879        let language_settings = language_settings(language.clone(), buffer_snapshot.file(), cx);
 5880        let completion_settings = language_settings.completions.clone();
 5881
 5882        let show_completions_on_input = self
 5883            .show_completions_on_input_override
 5884            .unwrap_or(language_settings.show_completions_on_input);
 5885        if !menu_is_open && trigger.is_some() && !show_completions_on_input {
 5886            return;
 5887        }
 5888
 5889        let query: Option<Arc<String>> =
 5890            Self::completion_query(&multibuffer_snapshot, buffer_position)
 5891                .map(|query| query.into());
 5892
 5893        drop(multibuffer_snapshot);
 5894
 5895        // Hide the current completions menu when query is empty. Without this, cached
 5896        // completions from before the trigger char may be reused (#32774).
 5897        if query.is_none() && menu_is_open {
 5898            self.hide_context_menu(window, cx);
 5899        }
 5900
 5901        let mut ignore_word_threshold = false;
 5902        let provider = match requested_source {
 5903            Some(CompletionsMenuSource::Normal) | None => self.completion_provider.clone(),
 5904            Some(CompletionsMenuSource::Words { ignore_threshold }) => {
 5905                ignore_word_threshold = ignore_threshold;
 5906                None
 5907            }
 5908            Some(CompletionsMenuSource::SnippetChoices)
 5909            | Some(CompletionsMenuSource::SnippetsOnly) => {
 5910                log::error!("bug: SnippetChoices requested_source is not handled");
 5911                None
 5912            }
 5913        };
 5914
 5915        let sort_completions = provider
 5916            .as_ref()
 5917            .is_some_and(|provider| provider.sort_completions());
 5918
 5919        let filter_completions = provider
 5920            .as_ref()
 5921            .is_none_or(|provider| provider.filter_completions());
 5922
 5923        let was_snippets_only = matches!(
 5924            completions_source,
 5925            Some(CompletionsMenuSource::SnippetsOnly)
 5926        );
 5927
 5928        if let Some(CodeContextMenu::Completions(menu)) = self.context_menu.borrow_mut().as_mut() {
 5929            if filter_completions {
 5930                menu.filter(
 5931                    query.clone().unwrap_or_default(),
 5932                    buffer_position.text_anchor,
 5933                    &buffer,
 5934                    provider.clone(),
 5935                    window,
 5936                    cx,
 5937                );
 5938            }
 5939            // When `is_incomplete` is false, no need to re-query completions when the current query
 5940            // is a suffix of the initial query.
 5941            let was_complete = !menu.is_incomplete;
 5942            if was_complete && !was_snippets_only {
 5943                // If the new query is a suffix of the old query (typing more characters) and
 5944                // the previous result was complete, the existing completions can be filtered.
 5945                //
 5946                // Note that snippet completions are always complete.
 5947                let query_matches = match (&menu.initial_query, &query) {
 5948                    (Some(initial_query), Some(query)) => query.starts_with(initial_query.as_ref()),
 5949                    (None, _) => true,
 5950                    _ => false,
 5951                };
 5952                if query_matches {
 5953                    let position_matches = if menu.initial_position == position {
 5954                        true
 5955                    } else {
 5956                        let snapshot = self.buffer.read(cx).read(cx);
 5957                        menu.initial_position.to_offset(&snapshot) == position.to_offset(&snapshot)
 5958                    };
 5959                    if position_matches {
 5960                        return;
 5961                    }
 5962                }
 5963            }
 5964        };
 5965
 5966        let Anchor {
 5967            excerpt_id: buffer_excerpt_id,
 5968            text_anchor: buffer_position,
 5969            ..
 5970        } = buffer_position;
 5971
 5972        let (word_replace_range, word_to_exclude) = if let (word_range, Some(CharKind::Word)) =
 5973            buffer_snapshot.surrounding_word(buffer_position, None)
 5974        {
 5975            let word_to_exclude = buffer_snapshot
 5976                .text_for_range(word_range.clone())
 5977                .collect::<String>();
 5978            (
 5979                buffer_snapshot.anchor_before(word_range.start)
 5980                    ..buffer_snapshot.anchor_after(buffer_position),
 5981                Some(word_to_exclude),
 5982            )
 5983        } else {
 5984            (buffer_position..buffer_position, None)
 5985        };
 5986
 5987        let show_completion_documentation = buffer_snapshot
 5988            .settings_at(buffer_position, cx)
 5989            .show_completion_documentation;
 5990
 5991        // The document can be large, so stay in reasonable bounds when searching for words,
 5992        // otherwise completion pop-up might be slow to appear.
 5993        const WORD_LOOKUP_ROWS: u32 = 5_000;
 5994        let buffer_row = text::ToPoint::to_point(&buffer_position, &buffer_snapshot).row;
 5995        let min_word_search = buffer_snapshot.clip_point(
 5996            Point::new(buffer_row.saturating_sub(WORD_LOOKUP_ROWS), 0),
 5997            Bias::Left,
 5998        );
 5999        let max_word_search = buffer_snapshot.clip_point(
 6000            Point::new(buffer_row + WORD_LOOKUP_ROWS, 0).min(buffer_snapshot.max_point()),
 6001            Bias::Right,
 6002        );
 6003        let word_search_range = buffer_snapshot.point_to_offset(min_word_search)
 6004            ..buffer_snapshot.point_to_offset(max_word_search);
 6005
 6006        let skip_digits = query
 6007            .as_ref()
 6008            .is_none_or(|query| !query.chars().any(|c| c.is_digit(10)));
 6009
 6010        let load_provider_completions = provider.as_ref().is_some_and(|provider| {
 6011            trigger.as_ref().is_none_or(|trigger| {
 6012                provider.is_completion_trigger(
 6013                    &buffer,
 6014                    position.text_anchor,
 6015                    trigger,
 6016                    trigger_in_words,
 6017                    cx,
 6018                )
 6019            })
 6020        });
 6021
 6022        let provider_responses = if let Some(provider) = &provider
 6023            && load_provider_completions
 6024        {
 6025            let trigger_character =
 6026                trigger.filter(|trigger| buffer.read(cx).completion_triggers().contains(trigger));
 6027            let completion_context = CompletionContext {
 6028                trigger_kind: match &trigger_character {
 6029                    Some(_) => CompletionTriggerKind::TRIGGER_CHARACTER,
 6030                    None => CompletionTriggerKind::INVOKED,
 6031                },
 6032                trigger_character,
 6033            };
 6034
 6035            provider.completions(
 6036                buffer_excerpt_id,
 6037                &buffer,
 6038                buffer_position,
 6039                completion_context,
 6040                window,
 6041                cx,
 6042            )
 6043        } else {
 6044            Task::ready(Ok(Vec::new()))
 6045        };
 6046
 6047        let load_word_completions = if !self.word_completions_enabled {
 6048            false
 6049        } else if requested_source
 6050            == Some(CompletionsMenuSource::Words {
 6051                ignore_threshold: true,
 6052            })
 6053        {
 6054            true
 6055        } else {
 6056            load_provider_completions
 6057                && completion_settings.words != WordsCompletionMode::Disabled
 6058                && (ignore_word_threshold || {
 6059                    let words_min_length = completion_settings.words_min_length;
 6060                    // check whether word has at least `words_min_length` characters
 6061                    let query_chars = query.iter().flat_map(|q| q.chars());
 6062                    query_chars.take(words_min_length).count() == words_min_length
 6063                })
 6064        };
 6065
 6066        let mut words = if load_word_completions {
 6067            cx.background_spawn({
 6068                let buffer_snapshot = buffer_snapshot.clone();
 6069                async move {
 6070                    buffer_snapshot.words_in_range(WordsQuery {
 6071                        fuzzy_contents: None,
 6072                        range: word_search_range,
 6073                        skip_digits,
 6074                    })
 6075                }
 6076            })
 6077        } else {
 6078            Task::ready(BTreeMap::default())
 6079        };
 6080
 6081        let snippets = if let Some(provider) = &provider
 6082            && provider.show_snippets()
 6083            && let Some(project) = self.project()
 6084        {
 6085            let char_classifier = buffer_snapshot
 6086                .char_classifier_at(buffer_position)
 6087                .scope_context(Some(CharScopeContext::Completion));
 6088            project.update(cx, |project, cx| {
 6089                snippet_completions(project, &buffer, buffer_position, char_classifier, cx)
 6090            })
 6091        } else {
 6092            Task::ready(Ok(CompletionResponse {
 6093                completions: Vec::new(),
 6094                display_options: Default::default(),
 6095                is_incomplete: false,
 6096            }))
 6097        };
 6098
 6099        let snippet_sort_order = EditorSettings::get_global(cx).snippet_sort_order;
 6100
 6101        let id = post_inc(&mut self.next_completion_id);
 6102        let task = cx.spawn_in(window, async move |editor, cx| {
 6103            let Ok(()) = editor.update(cx, |this, _| {
 6104                this.completion_tasks.retain(|(task_id, _)| *task_id >= id);
 6105            }) else {
 6106                return;
 6107            };
 6108
 6109            // TODO: Ideally completions from different sources would be selectively re-queried, so
 6110            // that having one source with `is_incomplete: true` doesn't cause all to be re-queried.
 6111            let mut completions = Vec::new();
 6112            let mut is_incomplete = false;
 6113            let mut display_options: Option<CompletionDisplayOptions> = None;
 6114            if let Some(provider_responses) = provider_responses.await.log_err()
 6115                && !provider_responses.is_empty()
 6116            {
 6117                for response in provider_responses {
 6118                    completions.extend(response.completions);
 6119                    is_incomplete = is_incomplete || response.is_incomplete;
 6120                    match display_options.as_mut() {
 6121                        None => {
 6122                            display_options = Some(response.display_options);
 6123                        }
 6124                        Some(options) => options.merge(&response.display_options),
 6125                    }
 6126                }
 6127                if completion_settings.words == WordsCompletionMode::Fallback {
 6128                    words = Task::ready(BTreeMap::default());
 6129                }
 6130            }
 6131            let display_options = display_options.unwrap_or_default();
 6132
 6133            let mut words = words.await;
 6134            if let Some(word_to_exclude) = &word_to_exclude {
 6135                words.remove(word_to_exclude);
 6136            }
 6137            for lsp_completion in &completions {
 6138                words.remove(&lsp_completion.new_text);
 6139            }
 6140            completions.extend(words.into_iter().map(|(word, word_range)| Completion {
 6141                replace_range: word_replace_range.clone(),
 6142                new_text: word.clone(),
 6143                label: CodeLabel::plain(word, None),
 6144                match_start: None,
 6145                snippet_deduplication_key: None,
 6146                icon_path: None,
 6147                documentation: None,
 6148                source: CompletionSource::BufferWord {
 6149                    word_range,
 6150                    resolved: false,
 6151                },
 6152                insert_text_mode: Some(InsertTextMode::AS_IS),
 6153                confirm: None,
 6154            }));
 6155
 6156            completions.extend(
 6157                snippets
 6158                    .await
 6159                    .into_iter()
 6160                    .flat_map(|response| response.completions),
 6161            );
 6162
 6163            let menu = if completions.is_empty() {
 6164                None
 6165            } else {
 6166                let Ok((mut menu, matches_task)) = editor.update(cx, |editor, cx| {
 6167                    let languages = editor
 6168                        .workspace
 6169                        .as_ref()
 6170                        .and_then(|(workspace, _)| workspace.upgrade())
 6171                        .map(|workspace| workspace.read(cx).app_state().languages.clone());
 6172                    let menu = CompletionsMenu::new(
 6173                        id,
 6174                        requested_source.unwrap_or(if load_provider_completions {
 6175                            CompletionsMenuSource::Normal
 6176                        } else {
 6177                            CompletionsMenuSource::SnippetsOnly
 6178                        }),
 6179                        sort_completions,
 6180                        show_completion_documentation,
 6181                        position,
 6182                        query.clone(),
 6183                        is_incomplete,
 6184                        buffer.clone(),
 6185                        completions.into(),
 6186                        editor
 6187                            .context_menu()
 6188                            .borrow_mut()
 6189                            .as_ref()
 6190                            .map(|menu| menu.primary_scroll_handle()),
 6191                        display_options,
 6192                        snippet_sort_order,
 6193                        languages,
 6194                        language,
 6195                        cx,
 6196                    );
 6197
 6198                    let query = if filter_completions { query } else { None };
 6199                    let matches_task = menu.do_async_filtering(
 6200                        query.unwrap_or_default(),
 6201                        buffer_position,
 6202                        &buffer,
 6203                        cx,
 6204                    );
 6205                    (menu, matches_task)
 6206                }) else {
 6207                    return;
 6208                };
 6209
 6210                let matches = matches_task.await;
 6211
 6212                let Ok(()) = editor.update_in(cx, |editor, window, cx| {
 6213                    // Newer menu already set, so exit.
 6214                    if let Some(CodeContextMenu::Completions(prev_menu)) =
 6215                        editor.context_menu.borrow().as_ref()
 6216                        && prev_menu.id > id
 6217                    {
 6218                        return;
 6219                    };
 6220
 6221                    // Only valid to take prev_menu because either the new menu is immediately set
 6222                    // below, or the menu is hidden.
 6223                    if let Some(CodeContextMenu::Completions(prev_menu)) =
 6224                        editor.context_menu.borrow_mut().take()
 6225                    {
 6226                        let position_matches =
 6227                            if prev_menu.initial_position == menu.initial_position {
 6228                                true
 6229                            } else {
 6230                                let snapshot = editor.buffer.read(cx).read(cx);
 6231                                prev_menu.initial_position.to_offset(&snapshot)
 6232                                    == menu.initial_position.to_offset(&snapshot)
 6233                            };
 6234                        if position_matches {
 6235                            // Preserve markdown cache before `set_filter_results` because it will
 6236                            // try to populate the documentation cache.
 6237                            menu.preserve_markdown_cache(prev_menu);
 6238                        }
 6239                    };
 6240
 6241                    menu.set_filter_results(matches, provider, window, cx);
 6242                }) else {
 6243                    return;
 6244                };
 6245
 6246                menu.visible().then_some(menu)
 6247            };
 6248
 6249            editor
 6250                .update_in(cx, |editor, window, cx| {
 6251                    if editor.focus_handle.is_focused(window)
 6252                        && let Some(menu) = menu
 6253                    {
 6254                        *editor.context_menu.borrow_mut() =
 6255                            Some(CodeContextMenu::Completions(menu));
 6256
 6257                        crate::hover_popover::hide_hover(editor, cx);
 6258                        if editor.show_edit_predictions_in_menu() {
 6259                            editor.update_visible_edit_prediction(window, cx);
 6260                        } else {
 6261                            editor
 6262                                .discard_edit_prediction(EditPredictionDiscardReason::Ignored, cx);
 6263                        }
 6264
 6265                        cx.notify();
 6266                        return;
 6267                    }
 6268
 6269                    if editor.completion_tasks.len() <= 1 {
 6270                        // If there are no more completion tasks and the last menu was empty, we should hide it.
 6271                        let was_hidden = editor.hide_context_menu(window, cx).is_none();
 6272                        // If it was already hidden and we don't show edit predictions in the menu,
 6273                        // we should also show the edit prediction when available.
 6274                        if was_hidden && editor.show_edit_predictions_in_menu() {
 6275                            editor.update_visible_edit_prediction(window, cx);
 6276                        }
 6277                    }
 6278                })
 6279                .ok();
 6280        });
 6281
 6282        self.completion_tasks.push((id, task));
 6283    }
 6284
 6285    #[cfg(feature = "test-support")]
 6286    pub fn current_completions(&self) -> Option<Vec<project::Completion>> {
 6287        let menu = self.context_menu.borrow();
 6288        if let CodeContextMenu::Completions(menu) = menu.as_ref()? {
 6289            let completions = menu.completions.borrow();
 6290            Some(completions.to_vec())
 6291        } else {
 6292            None
 6293        }
 6294    }
 6295
 6296    pub fn with_completions_menu_matching_id<R>(
 6297        &self,
 6298        id: CompletionId,
 6299        f: impl FnOnce(Option<&mut CompletionsMenu>) -> R,
 6300    ) -> R {
 6301        let mut context_menu = self.context_menu.borrow_mut();
 6302        let Some(CodeContextMenu::Completions(completions_menu)) = &mut *context_menu else {
 6303            return f(None);
 6304        };
 6305        if completions_menu.id != id {
 6306            return f(None);
 6307        }
 6308        f(Some(completions_menu))
 6309    }
 6310
 6311    pub fn confirm_completion(
 6312        &mut self,
 6313        action: &ConfirmCompletion,
 6314        window: &mut Window,
 6315        cx: &mut Context<Self>,
 6316    ) -> Option<Task<Result<()>>> {
 6317        self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
 6318        self.do_completion(action.item_ix, CompletionIntent::Complete, window, cx)
 6319    }
 6320
 6321    pub fn confirm_completion_insert(
 6322        &mut self,
 6323        _: &ConfirmCompletionInsert,
 6324        window: &mut Window,
 6325        cx: &mut Context<Self>,
 6326    ) -> Option<Task<Result<()>>> {
 6327        self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
 6328        self.do_completion(None, CompletionIntent::CompleteWithInsert, window, cx)
 6329    }
 6330
 6331    pub fn confirm_completion_replace(
 6332        &mut self,
 6333        _: &ConfirmCompletionReplace,
 6334        window: &mut Window,
 6335        cx: &mut Context<Self>,
 6336    ) -> Option<Task<Result<()>>> {
 6337        self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
 6338        self.do_completion(None, CompletionIntent::CompleteWithReplace, window, cx)
 6339    }
 6340
 6341    pub fn compose_completion(
 6342        &mut self,
 6343        action: &ComposeCompletion,
 6344        window: &mut Window,
 6345        cx: &mut Context<Self>,
 6346    ) -> Option<Task<Result<()>>> {
 6347        self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
 6348        self.do_completion(action.item_ix, CompletionIntent::Compose, window, cx)
 6349    }
 6350
 6351    fn do_completion(
 6352        &mut self,
 6353        item_ix: Option<usize>,
 6354        intent: CompletionIntent,
 6355        window: &mut Window,
 6356        cx: &mut Context<Editor>,
 6357    ) -> Option<Task<Result<()>>> {
 6358        use language::ToOffset as _;
 6359
 6360        let CodeContextMenu::Completions(completions_menu) = self.hide_context_menu(window, cx)?
 6361        else {
 6362            return None;
 6363        };
 6364
 6365        let candidate_id = {
 6366            let entries = completions_menu.entries.borrow();
 6367            let mat = entries.get(item_ix.unwrap_or(completions_menu.selected_item))?;
 6368            if self.show_edit_predictions_in_menu() {
 6369                self.discard_edit_prediction(EditPredictionDiscardReason::Rejected, cx);
 6370            }
 6371            mat.candidate_id
 6372        };
 6373
 6374        let completion = completions_menu
 6375            .completions
 6376            .borrow()
 6377            .get(candidate_id)?
 6378            .clone();
 6379        cx.stop_propagation();
 6380
 6381        let buffer_handle = completions_menu.buffer.clone();
 6382
 6383        let CompletionEdit {
 6384            new_text,
 6385            snippet,
 6386            replace_range,
 6387        } = process_completion_for_edit(
 6388            &completion,
 6389            intent,
 6390            &buffer_handle,
 6391            &completions_menu.initial_position.text_anchor,
 6392            cx,
 6393        );
 6394
 6395        let buffer = buffer_handle.read(cx);
 6396        let snapshot = self.buffer.read(cx).snapshot(cx);
 6397        let newest_anchor = self.selections.newest_anchor();
 6398        let replace_range_multibuffer = {
 6399            let mut excerpt = snapshot.excerpt_containing(newest_anchor.range()).unwrap();
 6400            excerpt.map_range_from_buffer(replace_range.clone())
 6401        };
 6402        if snapshot.buffer_id_for_anchor(newest_anchor.head()) != Some(buffer.remote_id()) {
 6403            return None;
 6404        }
 6405
 6406        let old_text = buffer
 6407            .text_for_range(replace_range.clone())
 6408            .collect::<String>();
 6409        let lookbehind = newest_anchor
 6410            .start
 6411            .text_anchor
 6412            .to_offset(buffer)
 6413            .saturating_sub(replace_range.start.0);
 6414        let lookahead = replace_range
 6415            .end
 6416            .0
 6417            .saturating_sub(newest_anchor.end.text_anchor.to_offset(buffer));
 6418        let prefix = &old_text[..old_text.len().saturating_sub(lookahead)];
 6419        let suffix = &old_text[lookbehind.min(old_text.len())..];
 6420
 6421        let selections = self
 6422            .selections
 6423            .all::<MultiBufferOffset>(&self.display_snapshot(cx));
 6424        let mut ranges = Vec::new();
 6425        let mut linked_edits = HashMap::<_, Vec<_>>::default();
 6426
 6427        for selection in &selections {
 6428            let range = if selection.id == newest_anchor.id {
 6429                replace_range_multibuffer.clone()
 6430            } else {
 6431                let mut range = selection.range();
 6432
 6433                // if prefix is present, don't duplicate it
 6434                if snapshot.contains_str_at(range.start.saturating_sub_usize(lookbehind), prefix) {
 6435                    range.start = range.start.saturating_sub_usize(lookbehind);
 6436
 6437                    // if suffix is also present, mimic the newest cursor and replace it
 6438                    if selection.id != newest_anchor.id
 6439                        && snapshot.contains_str_at(range.end, suffix)
 6440                    {
 6441                        range.end += lookahead;
 6442                    }
 6443                }
 6444                range
 6445            };
 6446
 6447            ranges.push(range.clone());
 6448
 6449            if !self.linked_edit_ranges.is_empty() {
 6450                let start_anchor = snapshot.anchor_before(range.start);
 6451                let end_anchor = snapshot.anchor_after(range.end);
 6452                if let Some(ranges) = self
 6453                    .linked_editing_ranges_for(start_anchor.text_anchor..end_anchor.text_anchor, cx)
 6454                {
 6455                    for (buffer, edits) in ranges {
 6456                        linked_edits
 6457                            .entry(buffer.clone())
 6458                            .or_default()
 6459                            .extend(edits.into_iter().map(|range| (range, new_text.to_owned())));
 6460                    }
 6461                }
 6462            }
 6463        }
 6464
 6465        let common_prefix_len = old_text
 6466            .chars()
 6467            .zip(new_text.chars())
 6468            .take_while(|(a, b)| a == b)
 6469            .map(|(a, _)| a.len_utf8())
 6470            .sum::<usize>();
 6471
 6472        cx.emit(EditorEvent::InputHandled {
 6473            utf16_range_to_replace: None,
 6474            text: new_text[common_prefix_len..].into(),
 6475        });
 6476
 6477        self.transact(window, cx, |editor, window, cx| {
 6478            if let Some(mut snippet) = snippet {
 6479                snippet.text = new_text.to_string();
 6480                editor
 6481                    .insert_snippet(&ranges, snippet, window, cx)
 6482                    .log_err();
 6483            } else {
 6484                editor.buffer.update(cx, |multi_buffer, cx| {
 6485                    let auto_indent = match completion.insert_text_mode {
 6486                        Some(InsertTextMode::AS_IS) => None,
 6487                        _ => editor.autoindent_mode.clone(),
 6488                    };
 6489                    let edits = ranges.into_iter().map(|range| (range, new_text.as_str()));
 6490                    multi_buffer.edit(edits, auto_indent, cx);
 6491                });
 6492            }
 6493            for (buffer, edits) in linked_edits {
 6494                buffer.update(cx, |buffer, cx| {
 6495                    let snapshot = buffer.snapshot();
 6496                    let edits = edits
 6497                        .into_iter()
 6498                        .map(|(range, text)| {
 6499                            use text::ToPoint as TP;
 6500                            let end_point = TP::to_point(&range.end, &snapshot);
 6501                            let start_point = TP::to_point(&range.start, &snapshot);
 6502                            (start_point..end_point, text)
 6503                        })
 6504                        .sorted_by_key(|(range, _)| range.start);
 6505                    buffer.edit(edits, None, cx);
 6506                })
 6507            }
 6508
 6509            editor.refresh_edit_prediction(true, false, window, cx);
 6510        });
 6511        self.invalidate_autoclose_regions(&self.selections.disjoint_anchors_arc(), &snapshot);
 6512
 6513        let show_new_completions_on_confirm = completion
 6514            .confirm
 6515            .as_ref()
 6516            .is_some_and(|confirm| confirm(intent, window, cx));
 6517        if show_new_completions_on_confirm {
 6518            self.open_or_update_completions_menu(None, None, false, window, cx);
 6519        }
 6520
 6521        let provider = self.completion_provider.as_ref()?;
 6522
 6523        let lsp_store = self.project().map(|project| project.read(cx).lsp_store());
 6524        let command = lsp_store.as_ref().and_then(|lsp_store| {
 6525            let CompletionSource::Lsp {
 6526                lsp_completion,
 6527                server_id,
 6528                ..
 6529            } = &completion.source
 6530            else {
 6531                return None;
 6532            };
 6533            let lsp_command = lsp_completion.command.as_ref()?;
 6534            let available_commands = lsp_store
 6535                .read(cx)
 6536                .lsp_server_capabilities
 6537                .get(server_id)
 6538                .and_then(|server_capabilities| {
 6539                    server_capabilities
 6540                        .execute_command_provider
 6541                        .as_ref()
 6542                        .map(|options| options.commands.as_slice())
 6543                })?;
 6544            if available_commands.contains(&lsp_command.command) {
 6545                Some(CodeAction {
 6546                    server_id: *server_id,
 6547                    range: language::Anchor::MIN..language::Anchor::MIN,
 6548                    lsp_action: LspAction::Command(lsp_command.clone()),
 6549                    resolved: false,
 6550                })
 6551            } else {
 6552                None
 6553            }
 6554        });
 6555
 6556        drop(completion);
 6557        let apply_edits = provider.apply_additional_edits_for_completion(
 6558            buffer_handle.clone(),
 6559            completions_menu.completions.clone(),
 6560            candidate_id,
 6561            true,
 6562            cx,
 6563        );
 6564
 6565        let editor_settings = EditorSettings::get_global(cx);
 6566        if editor_settings.show_signature_help_after_edits || editor_settings.auto_signature_help {
 6567            // After the code completion is finished, users often want to know what signatures are needed.
 6568            // so we should automatically call signature_help
 6569            self.show_signature_help(&ShowSignatureHelp, window, cx);
 6570        }
 6571
 6572        Some(cx.spawn_in(window, async move |editor, cx| {
 6573            apply_edits.await?;
 6574
 6575            if let Some((lsp_store, command)) = lsp_store.zip(command) {
 6576                let title = command.lsp_action.title().to_owned();
 6577                let project_transaction = lsp_store
 6578                    .update(cx, |lsp_store, cx| {
 6579                        lsp_store.apply_code_action(buffer_handle, command, false, cx)
 6580                    })
 6581                    .await
 6582                    .context("applying post-completion command")?;
 6583                if let Some(workspace) = editor.read_with(cx, |editor, _| editor.workspace())? {
 6584                    Self::open_project_transaction(
 6585                        &editor,
 6586                        workspace.downgrade(),
 6587                        project_transaction,
 6588                        title,
 6589                        cx,
 6590                    )
 6591                    .await?;
 6592                }
 6593            }
 6594
 6595            Ok(())
 6596        }))
 6597    }
 6598
 6599    pub fn toggle_code_actions(
 6600        &mut self,
 6601        action: &ToggleCodeActions,
 6602        window: &mut Window,
 6603        cx: &mut Context<Self>,
 6604    ) {
 6605        let quick_launch = action.quick_launch;
 6606        let mut context_menu = self.context_menu.borrow_mut();
 6607        if let Some(CodeContextMenu::CodeActions(code_actions)) = context_menu.as_ref() {
 6608            if code_actions.deployed_from == action.deployed_from {
 6609                // Toggle if we're selecting the same one
 6610                *context_menu = None;
 6611                cx.notify();
 6612                return;
 6613            } else {
 6614                // Otherwise, clear it and start a new one
 6615                *context_menu = None;
 6616                cx.notify();
 6617            }
 6618        }
 6619        drop(context_menu);
 6620        let snapshot = self.snapshot(window, cx);
 6621        let deployed_from = action.deployed_from.clone();
 6622        let action = action.clone();
 6623        self.completion_tasks.clear();
 6624        self.discard_edit_prediction(EditPredictionDiscardReason::Ignored, cx);
 6625
 6626        let multibuffer_point = match &action.deployed_from {
 6627            Some(CodeActionSource::Indicator(row)) | Some(CodeActionSource::RunMenu(row)) => {
 6628                DisplayPoint::new(*row, 0).to_point(&snapshot)
 6629            }
 6630            _ => self
 6631                .selections
 6632                .newest::<Point>(&snapshot.display_snapshot)
 6633                .head(),
 6634        };
 6635        let Some((buffer, buffer_row)) = snapshot
 6636            .buffer_snapshot()
 6637            .buffer_line_for_row(MultiBufferRow(multibuffer_point.row))
 6638            .and_then(|(buffer_snapshot, range)| {
 6639                self.buffer()
 6640                    .read(cx)
 6641                    .buffer(buffer_snapshot.remote_id())
 6642                    .map(|buffer| (buffer, range.start.row))
 6643            })
 6644        else {
 6645            return;
 6646        };
 6647        let buffer_id = buffer.read(cx).remote_id();
 6648        let tasks = self
 6649            .tasks
 6650            .get(&(buffer_id, buffer_row))
 6651            .map(|t| Arc::new(t.to_owned()));
 6652
 6653        if !self.focus_handle.is_focused(window) {
 6654            return;
 6655        }
 6656        let project = self.project.clone();
 6657
 6658        let code_actions_task = match deployed_from {
 6659            Some(CodeActionSource::RunMenu(_)) => Task::ready(None),
 6660            _ => self.code_actions(buffer_row, window, cx),
 6661        };
 6662
 6663        let runnable_task = match deployed_from {
 6664            Some(CodeActionSource::Indicator(_)) => Task::ready(Ok(Default::default())),
 6665            _ => {
 6666                let mut task_context_task = Task::ready(None);
 6667                if let Some(tasks) = &tasks
 6668                    && let Some(project) = project
 6669                {
 6670                    task_context_task =
 6671                        Self::build_tasks_context(&project, &buffer, buffer_row, tasks, cx);
 6672                }
 6673
 6674                cx.spawn_in(window, {
 6675                    let buffer = buffer.clone();
 6676                    async move |editor, cx| {
 6677                        let task_context = task_context_task.await;
 6678
 6679                        let resolved_tasks =
 6680                            tasks
 6681                                .zip(task_context.clone())
 6682                                .map(|(tasks, task_context)| ResolvedTasks {
 6683                                    templates: tasks.resolve(&task_context).collect(),
 6684                                    position: snapshot.buffer_snapshot().anchor_before(Point::new(
 6685                                        multibuffer_point.row,
 6686                                        tasks.column,
 6687                                    )),
 6688                                });
 6689                        let debug_scenarios = editor
 6690                            .update(cx, |editor, cx| {
 6691                                editor.debug_scenarios(&resolved_tasks, &buffer, cx)
 6692                            })?
 6693                            .await;
 6694                        anyhow::Ok((resolved_tasks, debug_scenarios, task_context))
 6695                    }
 6696                })
 6697            }
 6698        };
 6699
 6700        cx.spawn_in(window, async move |editor, cx| {
 6701            let (resolved_tasks, debug_scenarios, task_context) = runnable_task.await?;
 6702            let code_actions = code_actions_task.await;
 6703            let spawn_straight_away = quick_launch
 6704                && resolved_tasks
 6705                    .as_ref()
 6706                    .is_some_and(|tasks| tasks.templates.len() == 1)
 6707                && code_actions
 6708                    .as_ref()
 6709                    .is_none_or(|actions| actions.is_empty())
 6710                && debug_scenarios.is_empty();
 6711
 6712            editor.update_in(cx, |editor, window, cx| {
 6713                crate::hover_popover::hide_hover(editor, cx);
 6714                let actions = CodeActionContents::new(
 6715                    resolved_tasks,
 6716                    code_actions,
 6717                    debug_scenarios,
 6718                    task_context.unwrap_or_default(),
 6719                );
 6720
 6721                // Don't show the menu if there are no actions available
 6722                if actions.is_empty() {
 6723                    cx.notify();
 6724                    return Task::ready(Ok(()));
 6725                }
 6726
 6727                *editor.context_menu.borrow_mut() =
 6728                    Some(CodeContextMenu::CodeActions(CodeActionsMenu {
 6729                        buffer,
 6730                        actions,
 6731                        selected_item: Default::default(),
 6732                        scroll_handle: UniformListScrollHandle::default(),
 6733                        deployed_from,
 6734                    }));
 6735                cx.notify();
 6736                if spawn_straight_away
 6737                    && let Some(task) = editor.confirm_code_action(
 6738                        &ConfirmCodeAction { item_ix: Some(0) },
 6739                        window,
 6740                        cx,
 6741                    )
 6742                {
 6743                    return task;
 6744                }
 6745
 6746                Task::ready(Ok(()))
 6747            })
 6748        })
 6749        .detach_and_log_err(cx);
 6750    }
 6751
 6752    fn debug_scenarios(
 6753        &mut self,
 6754        resolved_tasks: &Option<ResolvedTasks>,
 6755        buffer: &Entity<Buffer>,
 6756        cx: &mut App,
 6757    ) -> Task<Vec<task::DebugScenario>> {
 6758        maybe!({
 6759            let project = self.project()?;
 6760            let dap_store = project.read(cx).dap_store();
 6761            let mut scenarios = vec![];
 6762            let resolved_tasks = resolved_tasks.as_ref()?;
 6763            let buffer = buffer.read(cx);
 6764            let language = buffer.language()?;
 6765            let file = buffer.file();
 6766            let debug_adapter = language_settings(language.name().into(), file, cx)
 6767                .debuggers
 6768                .first()
 6769                .map(SharedString::from)
 6770                .or_else(|| language.config().debuggers.first().map(SharedString::from))?;
 6771
 6772            dap_store.update(cx, |dap_store, cx| {
 6773                for (_, task) in &resolved_tasks.templates {
 6774                    let maybe_scenario = dap_store.debug_scenario_for_build_task(
 6775                        task.original_task().clone(),
 6776                        debug_adapter.clone().into(),
 6777                        task.display_label().to_owned().into(),
 6778                        cx,
 6779                    );
 6780                    scenarios.push(maybe_scenario);
 6781                }
 6782            });
 6783            Some(cx.background_spawn(async move {
 6784                futures::future::join_all(scenarios)
 6785                    .await
 6786                    .into_iter()
 6787                    .flatten()
 6788                    .collect::<Vec<_>>()
 6789            }))
 6790        })
 6791        .unwrap_or_else(|| Task::ready(vec![]))
 6792    }
 6793
 6794    fn code_actions(
 6795        &mut self,
 6796        buffer_row: u32,
 6797        window: &mut Window,
 6798        cx: &mut Context<Self>,
 6799    ) -> Task<Option<Rc<[AvailableCodeAction]>>> {
 6800        let mut task = self.code_actions_task.take();
 6801        cx.spawn_in(window, async move |editor, cx| {
 6802            while let Some(prev_task) = task {
 6803                prev_task.await.log_err();
 6804                task = editor
 6805                    .update(cx, |this, _| this.code_actions_task.take())
 6806                    .ok()?;
 6807            }
 6808
 6809            editor
 6810                .update(cx, |editor, cx| {
 6811                    editor
 6812                        .available_code_actions
 6813                        .clone()
 6814                        .and_then(|(location, code_actions)| {
 6815                            let snapshot = location.buffer.read(cx).snapshot();
 6816                            let point_range = location.range.to_point(&snapshot);
 6817                            let point_range = point_range.start.row..=point_range.end.row;
 6818                            if point_range.contains(&buffer_row) {
 6819                                Some(code_actions)
 6820                            } else {
 6821                                None
 6822                            }
 6823                        })
 6824                })
 6825                .ok()
 6826                .flatten()
 6827        })
 6828    }
 6829
 6830    pub fn confirm_code_action(
 6831        &mut self,
 6832        action: &ConfirmCodeAction,
 6833        window: &mut Window,
 6834        cx: &mut Context<Self>,
 6835    ) -> Option<Task<Result<()>>> {
 6836        self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
 6837
 6838        let actions_menu =
 6839            if let CodeContextMenu::CodeActions(menu) = self.hide_context_menu(window, cx)? {
 6840                menu
 6841            } else {
 6842                return None;
 6843            };
 6844
 6845        let action_ix = action.item_ix.unwrap_or(actions_menu.selected_item);
 6846        let action = actions_menu.actions.get(action_ix)?;
 6847        let title = action.label();
 6848        let buffer = actions_menu.buffer;
 6849        let workspace = self.workspace()?;
 6850
 6851        match action {
 6852            CodeActionsItem::Task(task_source_kind, resolved_task) => {
 6853                workspace.update(cx, |workspace, cx| {
 6854                    workspace.schedule_resolved_task(
 6855                        task_source_kind,
 6856                        resolved_task,
 6857                        false,
 6858                        window,
 6859                        cx,
 6860                    );
 6861
 6862                    Some(Task::ready(Ok(())))
 6863                })
 6864            }
 6865            CodeActionsItem::CodeAction {
 6866                excerpt_id,
 6867                action,
 6868                provider,
 6869            } => {
 6870                let apply_code_action =
 6871                    provider.apply_code_action(buffer, action, excerpt_id, true, window, cx);
 6872                let workspace = workspace.downgrade();
 6873                Some(cx.spawn_in(window, async move |editor, cx| {
 6874                    let project_transaction = apply_code_action.await?;
 6875                    Self::open_project_transaction(
 6876                        &editor,
 6877                        workspace,
 6878                        project_transaction,
 6879                        title,
 6880                        cx,
 6881                    )
 6882                    .await
 6883                }))
 6884            }
 6885            CodeActionsItem::DebugScenario(scenario) => {
 6886                let context = actions_menu.actions.context.into();
 6887
 6888                workspace.update(cx, |workspace, cx| {
 6889                    dap::send_telemetry(&scenario, TelemetrySpawnLocation::Gutter, cx);
 6890                    workspace.start_debug_session(
 6891                        scenario,
 6892                        context,
 6893                        Some(buffer),
 6894                        None,
 6895                        window,
 6896                        cx,
 6897                    );
 6898                });
 6899                Some(Task::ready(Ok(())))
 6900            }
 6901        }
 6902    }
 6903
 6904    fn open_transaction_for_hidden_buffers(
 6905        workspace: Entity<Workspace>,
 6906        transaction: ProjectTransaction,
 6907        title: String,
 6908        window: &mut Window,
 6909        cx: &mut Context<Self>,
 6910    ) {
 6911        if transaction.0.is_empty() {
 6912            return;
 6913        }
 6914
 6915        let edited_buffers_already_open = {
 6916            let other_editors: Vec<Entity<Editor>> = workspace
 6917                .read(cx)
 6918                .panes()
 6919                .iter()
 6920                .flat_map(|pane| pane.read(cx).items_of_type::<Editor>())
 6921                .filter(|editor| editor.entity_id() != cx.entity_id())
 6922                .collect();
 6923
 6924            transaction.0.keys().all(|buffer| {
 6925                other_editors.iter().any(|editor| {
 6926                    let multi_buffer = editor.read(cx).buffer();
 6927                    multi_buffer.read(cx).is_singleton()
 6928                        && multi_buffer
 6929                            .read(cx)
 6930                            .as_singleton()
 6931                            .map_or(false, |singleton| {
 6932                                singleton.entity_id() == buffer.entity_id()
 6933                            })
 6934                })
 6935            })
 6936        };
 6937        if !edited_buffers_already_open {
 6938            let workspace = workspace.downgrade();
 6939            cx.defer_in(window, move |_, window, cx| {
 6940                cx.spawn_in(window, async move |editor, cx| {
 6941                    Self::open_project_transaction(&editor, workspace, transaction, title, cx)
 6942                        .await
 6943                        .ok()
 6944                })
 6945                .detach();
 6946            });
 6947        }
 6948    }
 6949
 6950    pub async fn open_project_transaction(
 6951        editor: &WeakEntity<Editor>,
 6952        workspace: WeakEntity<Workspace>,
 6953        transaction: ProjectTransaction,
 6954        title: String,
 6955        cx: &mut AsyncWindowContext,
 6956    ) -> Result<()> {
 6957        let mut entries = transaction.0.into_iter().collect::<Vec<_>>();
 6958        cx.update(|_, cx| {
 6959            entries.sort_unstable_by_key(|(buffer, _)| {
 6960                buffer.read(cx).file().map(|f| f.path().clone())
 6961            });
 6962        })?;
 6963        if entries.is_empty() {
 6964            return Ok(());
 6965        }
 6966
 6967        // If the project transaction's edits are all contained within this editor, then
 6968        // avoid opening a new editor to display them.
 6969
 6970        if let [(buffer, transaction)] = &*entries {
 6971            let excerpt = editor.update(cx, |editor, cx| {
 6972                editor
 6973                    .buffer()
 6974                    .read(cx)
 6975                    .excerpt_containing(editor.selections.newest_anchor().head(), cx)
 6976            })?;
 6977            if let Some((_, excerpted_buffer, excerpt_range)) = excerpt
 6978                && excerpted_buffer == *buffer
 6979            {
 6980                let all_edits_within_excerpt = buffer.read_with(cx, |buffer, _| {
 6981                    let excerpt_range = excerpt_range.to_offset(buffer);
 6982                    buffer
 6983                        .edited_ranges_for_transaction::<usize>(transaction)
 6984                        .all(|range| {
 6985                            excerpt_range.start <= range.start && excerpt_range.end >= range.end
 6986                        })
 6987                });
 6988
 6989                if all_edits_within_excerpt {
 6990                    return Ok(());
 6991                }
 6992            }
 6993        }
 6994
 6995        let mut ranges_to_highlight = Vec::new();
 6996        let excerpt_buffer = cx.new(|cx| {
 6997            let mut multibuffer = MultiBuffer::new(Capability::ReadWrite).with_title(title);
 6998            for (buffer_handle, transaction) in &entries {
 6999                let edited_ranges = buffer_handle
 7000                    .read(cx)
 7001                    .edited_ranges_for_transaction::<Point>(transaction)
 7002                    .collect::<Vec<_>>();
 7003                let (ranges, _) = multibuffer.set_excerpts_for_path(
 7004                    PathKey::for_buffer(buffer_handle, cx),
 7005                    buffer_handle.clone(),
 7006                    edited_ranges,
 7007                    multibuffer_context_lines(cx),
 7008                    cx,
 7009                );
 7010
 7011                ranges_to_highlight.extend(ranges);
 7012            }
 7013            multibuffer.push_transaction(entries.iter().map(|(b, t)| (b, t)), cx);
 7014            multibuffer
 7015        });
 7016
 7017        workspace.update_in(cx, |workspace, window, cx| {
 7018            let project = workspace.project().clone();
 7019            let editor =
 7020                cx.new(|cx| Editor::for_multibuffer(excerpt_buffer, Some(project), window, cx));
 7021            workspace.add_item_to_active_pane(Box::new(editor.clone()), None, true, window, cx);
 7022            editor.update(cx, |editor, cx| {
 7023                editor.highlight_background(
 7024                    HighlightKey::Editor,
 7025                    &ranges_to_highlight,
 7026                    |_, theme| theme.colors().editor_highlighted_line_background,
 7027                    cx,
 7028                );
 7029            });
 7030        })?;
 7031
 7032        Ok(())
 7033    }
 7034
 7035    pub fn clear_code_action_providers(&mut self) {
 7036        self.code_action_providers.clear();
 7037        self.available_code_actions.take();
 7038    }
 7039
 7040    pub fn add_code_action_provider(
 7041        &mut self,
 7042        provider: Rc<dyn CodeActionProvider>,
 7043        window: &mut Window,
 7044        cx: &mut Context<Self>,
 7045    ) {
 7046        if self
 7047            .code_action_providers
 7048            .iter()
 7049            .any(|existing_provider| existing_provider.id() == provider.id())
 7050        {
 7051            return;
 7052        }
 7053
 7054        self.code_action_providers.push(provider);
 7055        self.refresh_code_actions(window, cx);
 7056    }
 7057
 7058    pub fn remove_code_action_provider(
 7059        &mut self,
 7060        id: Arc<str>,
 7061        window: &mut Window,
 7062        cx: &mut Context<Self>,
 7063    ) {
 7064        self.code_action_providers
 7065            .retain(|provider| provider.id() != id);
 7066        self.refresh_code_actions(window, cx);
 7067    }
 7068
 7069    pub fn code_actions_enabled_for_toolbar(&self, cx: &App) -> bool {
 7070        !self.code_action_providers.is_empty()
 7071            && EditorSettings::get_global(cx).toolbar.code_actions
 7072    }
 7073
 7074    pub fn has_available_code_actions(&self) -> bool {
 7075        self.available_code_actions
 7076            .as_ref()
 7077            .is_some_and(|(_, actions)| !actions.is_empty())
 7078    }
 7079
 7080    fn render_inline_code_actions(
 7081        &self,
 7082        icon_size: ui::IconSize,
 7083        display_row: DisplayRow,
 7084        is_active: bool,
 7085        cx: &mut Context<Self>,
 7086    ) -> AnyElement {
 7087        let show_tooltip = !self.context_menu_visible();
 7088        IconButton::new("inline_code_actions", ui::IconName::BoltFilled)
 7089            .icon_size(icon_size)
 7090            .shape(ui::IconButtonShape::Square)
 7091            .icon_color(ui::Color::Hidden)
 7092            .toggle_state(is_active)
 7093            .when(show_tooltip, |this| {
 7094                this.tooltip({
 7095                    let focus_handle = self.focus_handle.clone();
 7096                    move |_window, cx| {
 7097                        Tooltip::for_action_in(
 7098                            "Toggle Code Actions",
 7099                            &ToggleCodeActions {
 7100                                deployed_from: None,
 7101                                quick_launch: false,
 7102                            },
 7103                            &focus_handle,
 7104                            cx,
 7105                        )
 7106                    }
 7107                })
 7108            })
 7109            .on_click(cx.listener(move |editor, _: &ClickEvent, window, cx| {
 7110                window.focus(&editor.focus_handle(cx), cx);
 7111                editor.toggle_code_actions(
 7112                    &crate::actions::ToggleCodeActions {
 7113                        deployed_from: Some(crate::actions::CodeActionSource::Indicator(
 7114                            display_row,
 7115                        )),
 7116                        quick_launch: false,
 7117                    },
 7118                    window,
 7119                    cx,
 7120                );
 7121            }))
 7122            .into_any_element()
 7123    }
 7124
 7125    pub fn context_menu(&self) -> &RefCell<Option<CodeContextMenu>> {
 7126        &self.context_menu
 7127    }
 7128
 7129    fn refresh_code_actions(&mut self, window: &mut Window, cx: &mut Context<Self>) {
 7130        self.code_actions_task = Some(cx.spawn_in(window, async move |this, cx| {
 7131            cx.background_executor()
 7132                .timer(CODE_ACTIONS_DEBOUNCE_TIMEOUT)
 7133                .await;
 7134
 7135            let (start_buffer, start, _, end, newest_selection) = this
 7136                .update(cx, |this, cx| {
 7137                    let newest_selection = this.selections.newest_anchor().clone();
 7138                    if newest_selection.head().diff_base_anchor.is_some() {
 7139                        return None;
 7140                    }
 7141                    let display_snapshot = this.display_snapshot(cx);
 7142                    let newest_selection_adjusted =
 7143                        this.selections.newest_adjusted(&display_snapshot);
 7144                    let buffer = this.buffer.read(cx);
 7145
 7146                    let (start_buffer, start) =
 7147                        buffer.text_anchor_for_position(newest_selection_adjusted.start, cx)?;
 7148                    let (end_buffer, end) =
 7149                        buffer.text_anchor_for_position(newest_selection_adjusted.end, cx)?;
 7150
 7151                    Some((start_buffer, start, end_buffer, end, newest_selection))
 7152                })?
 7153                .filter(|(start_buffer, _, end_buffer, _, _)| start_buffer == end_buffer)
 7154                .context(
 7155                    "Expected selection to lie in a single buffer when refreshing code actions",
 7156                )?;
 7157            let (providers, tasks) = this.update_in(cx, |this, window, cx| {
 7158                let providers = this.code_action_providers.clone();
 7159                let tasks = this
 7160                    .code_action_providers
 7161                    .iter()
 7162                    .map(|provider| provider.code_actions(&start_buffer, start..end, window, cx))
 7163                    .collect::<Vec<_>>();
 7164                (providers, tasks)
 7165            })?;
 7166
 7167            let mut actions = Vec::new();
 7168            for (provider, provider_actions) in
 7169                providers.into_iter().zip(future::join_all(tasks).await)
 7170            {
 7171                if let Some(provider_actions) = provider_actions.log_err() {
 7172                    actions.extend(provider_actions.into_iter().map(|action| {
 7173                        AvailableCodeAction {
 7174                            excerpt_id: newest_selection.start.excerpt_id,
 7175                            action,
 7176                            provider: provider.clone(),
 7177                        }
 7178                    }));
 7179                }
 7180            }
 7181
 7182            this.update(cx, |this, cx| {
 7183                this.available_code_actions = if actions.is_empty() {
 7184                    None
 7185                } else {
 7186                    Some((
 7187                        Location {
 7188                            buffer: start_buffer,
 7189                            range: start..end,
 7190                        },
 7191                        actions.into(),
 7192                    ))
 7193                };
 7194                cx.notify();
 7195            })
 7196        }));
 7197    }
 7198
 7199    fn start_inline_blame_timer(&mut self, window: &mut Window, cx: &mut Context<Self>) {
 7200        if let Some(delay) = ProjectSettings::get_global(cx).git.inline_blame_delay() {
 7201            self.show_git_blame_inline = false;
 7202
 7203            self.show_git_blame_inline_delay_task =
 7204                Some(cx.spawn_in(window, async move |this, cx| {
 7205                    cx.background_executor().timer(delay).await;
 7206
 7207                    this.update(cx, |this, cx| {
 7208                        this.show_git_blame_inline = true;
 7209                        cx.notify();
 7210                    })
 7211                    .log_err();
 7212                }));
 7213        }
 7214    }
 7215
 7216    pub fn blame_hover(&mut self, _: &BlameHover, window: &mut Window, cx: &mut Context<Self>) {
 7217        let snapshot = self.snapshot(window, cx);
 7218        let cursor = self
 7219            .selections
 7220            .newest::<Point>(&snapshot.display_snapshot)
 7221            .head();
 7222        let Some((buffer, point, _)) = snapshot.buffer_snapshot().point_to_buffer_point(cursor)
 7223        else {
 7224            return;
 7225        };
 7226
 7227        if self.blame.is_none() {
 7228            self.start_git_blame(true, window, cx);
 7229        }
 7230        let Some(blame) = self.blame.as_ref() else {
 7231            return;
 7232        };
 7233
 7234        let row_info = RowInfo {
 7235            buffer_id: Some(buffer.remote_id()),
 7236            buffer_row: Some(point.row),
 7237            ..Default::default()
 7238        };
 7239        let Some((buffer, blame_entry)) = blame
 7240            .update(cx, |blame, cx| blame.blame_for_rows(&[row_info], cx).next())
 7241            .flatten()
 7242        else {
 7243            return;
 7244        };
 7245
 7246        let anchor = self.selections.newest_anchor().head();
 7247        let position = self.to_pixel_point(anchor, &snapshot, window, cx);
 7248        if let (Some(position), Some(last_bounds)) = (position, self.last_bounds) {
 7249            self.show_blame_popover(
 7250                buffer,
 7251                &blame_entry,
 7252                position + last_bounds.origin,
 7253                true,
 7254                cx,
 7255            );
 7256        };
 7257    }
 7258
 7259    fn show_blame_popover(
 7260        &mut self,
 7261        buffer: BufferId,
 7262        blame_entry: &BlameEntry,
 7263        position: gpui::Point<Pixels>,
 7264        ignore_timeout: bool,
 7265        cx: &mut Context<Self>,
 7266    ) {
 7267        if let Some(state) = &mut self.inline_blame_popover {
 7268            state.hide_task.take();
 7269        } else {
 7270            let blame_popover_delay = EditorSettings::get_global(cx).hover_popover_delay.0;
 7271            let blame_entry = blame_entry.clone();
 7272            let show_task = cx.spawn(async move |editor, cx| {
 7273                if !ignore_timeout {
 7274                    cx.background_executor()
 7275                        .timer(std::time::Duration::from_millis(blame_popover_delay))
 7276                        .await;
 7277                }
 7278                editor
 7279                    .update(cx, |editor, cx| {
 7280                        editor.inline_blame_popover_show_task.take();
 7281                        let Some(blame) = editor.blame.as_ref() else {
 7282                            return;
 7283                        };
 7284                        let blame = blame.read(cx);
 7285                        let details = blame.details_for_entry(buffer, &blame_entry);
 7286                        let markdown = cx.new(|cx| {
 7287                            Markdown::new(
 7288                                details
 7289                                    .as_ref()
 7290                                    .map(|message| message.message.clone())
 7291                                    .unwrap_or_default(),
 7292                                None,
 7293                                None,
 7294                                cx,
 7295                            )
 7296                        });
 7297                        editor.inline_blame_popover = Some(InlineBlamePopover {
 7298                            position,
 7299                            hide_task: None,
 7300                            popover_bounds: None,
 7301                            popover_state: InlineBlamePopoverState {
 7302                                scroll_handle: ScrollHandle::new(),
 7303                                commit_message: details,
 7304                                markdown,
 7305                            },
 7306                            keyboard_grace: ignore_timeout,
 7307                        });
 7308                        cx.notify();
 7309                    })
 7310                    .ok();
 7311            });
 7312            self.inline_blame_popover_show_task = Some(show_task);
 7313        }
 7314    }
 7315
 7316    pub fn has_mouse_context_menu(&self) -> bool {
 7317        self.mouse_context_menu.is_some()
 7318    }
 7319
 7320    pub fn hide_blame_popover(&mut self, ignore_timeout: bool, cx: &mut Context<Self>) -> bool {
 7321        self.inline_blame_popover_show_task.take();
 7322        if let Some(state) = &mut self.inline_blame_popover {
 7323            let hide_task = cx.spawn(async move |editor, cx| {
 7324                if !ignore_timeout {
 7325                    cx.background_executor()
 7326                        .timer(std::time::Duration::from_millis(100))
 7327                        .await;
 7328                }
 7329                editor
 7330                    .update(cx, |editor, cx| {
 7331                        editor.inline_blame_popover.take();
 7332                        cx.notify();
 7333                    })
 7334                    .ok();
 7335            });
 7336            state.hide_task = Some(hide_task);
 7337            true
 7338        } else {
 7339            false
 7340        }
 7341    }
 7342
 7343    fn refresh_document_highlights(&mut self, cx: &mut Context<Self>) -> Option<()> {
 7344        if self.pending_rename.is_some() {
 7345            return None;
 7346        }
 7347
 7348        let provider = self.semantics_provider.clone()?;
 7349        let buffer = self.buffer.read(cx);
 7350        let newest_selection = self.selections.newest_anchor().clone();
 7351        let cursor_position = newest_selection.head();
 7352        let (cursor_buffer, cursor_buffer_position) =
 7353            buffer.text_anchor_for_position(cursor_position, cx)?;
 7354        let (tail_buffer, tail_buffer_position) =
 7355            buffer.text_anchor_for_position(newest_selection.tail(), cx)?;
 7356        if cursor_buffer != tail_buffer {
 7357            return None;
 7358        }
 7359
 7360        let snapshot = cursor_buffer.read(cx).snapshot();
 7361        let word_ranges = cx.background_spawn(async move {
 7362            // this might look odd to put on the background thread, but
 7363            // `surrounding_word` can be quite expensive as it calls into
 7364            // tree-sitter language scopes
 7365            let (start_word_range, _) = snapshot.surrounding_word(cursor_buffer_position, None);
 7366            let (end_word_range, _) = snapshot.surrounding_word(tail_buffer_position, None);
 7367            (start_word_range, end_word_range)
 7368        });
 7369
 7370        let debounce = EditorSettings::get_global(cx).lsp_highlight_debounce.0;
 7371        self.document_highlights_task = Some(cx.spawn(async move |this, cx| {
 7372            let (start_word_range, end_word_range) = word_ranges.await;
 7373            if start_word_range != end_word_range {
 7374                this.update(cx, |this, cx| {
 7375                    this.document_highlights_task.take();
 7376                    this.clear_background_highlights(HighlightKey::DocumentHighlightRead, cx);
 7377                    this.clear_background_highlights(HighlightKey::DocumentHighlightWrite, cx);
 7378                })
 7379                .ok();
 7380                return;
 7381            }
 7382            cx.background_executor()
 7383                .timer(Duration::from_millis(debounce))
 7384                .await;
 7385
 7386            let highlights = if let Some(highlights) = cx.update(|cx| {
 7387                provider.document_highlights(&cursor_buffer, cursor_buffer_position, cx)
 7388            }) {
 7389                highlights.await.log_err()
 7390            } else {
 7391                None
 7392            };
 7393
 7394            if let Some(highlights) = highlights {
 7395                this.update(cx, |this, cx| {
 7396                    if this.pending_rename.is_some() {
 7397                        return;
 7398                    }
 7399
 7400                    let buffer = this.buffer.read(cx);
 7401                    if buffer
 7402                        .text_anchor_for_position(cursor_position, cx)
 7403                        .is_none_or(|(buffer, _)| buffer != cursor_buffer)
 7404                    {
 7405                        return;
 7406                    }
 7407
 7408                    let cursor_buffer_snapshot = cursor_buffer.read(cx);
 7409                    let mut write_ranges = Vec::new();
 7410                    let mut read_ranges = Vec::new();
 7411                    for highlight in highlights {
 7412                        let buffer_id = cursor_buffer.read(cx).remote_id();
 7413                        for (excerpt_id, excerpt_range) in buffer.excerpts_for_buffer(buffer_id, cx)
 7414                        {
 7415                            let start = highlight
 7416                                .range
 7417                                .start
 7418                                .max(&excerpt_range.context.start, cursor_buffer_snapshot);
 7419                            let end = highlight
 7420                                .range
 7421                                .end
 7422                                .min(&excerpt_range.context.end, cursor_buffer_snapshot);
 7423                            if start.cmp(&end, cursor_buffer_snapshot).is_ge() {
 7424                                continue;
 7425                            }
 7426
 7427                            let range = Anchor::range_in_buffer(excerpt_id, *start..*end);
 7428                            if highlight.kind == lsp::DocumentHighlightKind::WRITE {
 7429                                write_ranges.push(range);
 7430                            } else {
 7431                                read_ranges.push(range);
 7432                            }
 7433                        }
 7434                    }
 7435
 7436                    this.highlight_background(
 7437                        HighlightKey::DocumentHighlightRead,
 7438                        &read_ranges,
 7439                        |_, theme| theme.colors().editor_document_highlight_read_background,
 7440                        cx,
 7441                    );
 7442                    this.highlight_background(
 7443                        HighlightKey::DocumentHighlightWrite,
 7444                        &write_ranges,
 7445                        |_, theme| theme.colors().editor_document_highlight_write_background,
 7446                        cx,
 7447                    );
 7448                    cx.notify();
 7449                })
 7450                .log_err();
 7451            }
 7452        }));
 7453        None
 7454    }
 7455
 7456    fn prepare_highlight_query_from_selection(
 7457        &mut self,
 7458        window: &Window,
 7459        cx: &mut Context<Editor>,
 7460    ) -> Option<(String, Range<Anchor>)> {
 7461        if matches!(self.mode, EditorMode::SingleLine) {
 7462            return None;
 7463        }
 7464        if !EditorSettings::get_global(cx).selection_highlight {
 7465            return None;
 7466        }
 7467        if self.selections.count() != 1 || self.selections.line_mode() {
 7468            return None;
 7469        }
 7470        let snapshot = self.snapshot(window, cx);
 7471        let selection = self.selections.newest::<Point>(&snapshot);
 7472        // If the selection spans multiple rows OR it is empty
 7473        if selection.start.row != selection.end.row
 7474            || selection.start.column == selection.end.column
 7475        {
 7476            return None;
 7477        }
 7478        let selection_anchor_range = selection.range().to_anchors(snapshot.buffer_snapshot());
 7479        let query = snapshot
 7480            .buffer_snapshot()
 7481            .text_for_range(selection_anchor_range.clone())
 7482            .collect::<String>();
 7483        if query.trim().is_empty() {
 7484            return None;
 7485        }
 7486        Some((query, selection_anchor_range))
 7487    }
 7488
 7489    #[ztracing::instrument(skip_all)]
 7490    fn update_selection_occurrence_highlights(
 7491        &mut self,
 7492        query_text: String,
 7493        query_range: Range<Anchor>,
 7494        multi_buffer_range_to_query: Range<Point>,
 7495        use_debounce: bool,
 7496        window: &mut Window,
 7497        cx: &mut Context<Editor>,
 7498    ) -> Task<()> {
 7499        let multi_buffer_snapshot = self.buffer().read(cx).snapshot(cx);
 7500        cx.spawn_in(window, async move |editor, cx| {
 7501            if use_debounce {
 7502                cx.background_executor()
 7503                    .timer(SELECTION_HIGHLIGHT_DEBOUNCE_TIMEOUT)
 7504                    .await;
 7505            }
 7506            let match_task = cx.background_spawn(async move {
 7507                let buffer_ranges = multi_buffer_snapshot
 7508                    .range_to_buffer_ranges(
 7509                        multi_buffer_range_to_query.start..=multi_buffer_range_to_query.end,
 7510                    )
 7511                    .into_iter()
 7512                    .filter(|(_, excerpt_visible_range, _)| !excerpt_visible_range.is_empty());
 7513                let mut match_ranges = Vec::new();
 7514                let Ok(regex) = project::search::SearchQuery::text(
 7515                    query_text.clone(),
 7516                    false,
 7517                    false,
 7518                    false,
 7519                    Default::default(),
 7520                    Default::default(),
 7521                    false,
 7522                    None,
 7523                ) else {
 7524                    return Vec::default();
 7525                };
 7526                let query_range = query_range.to_anchors(&multi_buffer_snapshot);
 7527                for (buffer_snapshot, search_range, excerpt_id) in buffer_ranges {
 7528                    match_ranges.extend(
 7529                        regex
 7530                            .search(
 7531                                buffer_snapshot,
 7532                                Some(search_range.start.0..search_range.end.0),
 7533                            )
 7534                            .await
 7535                            .into_iter()
 7536                            .filter_map(|match_range| {
 7537                                let match_start = buffer_snapshot
 7538                                    .anchor_after(search_range.start + match_range.start);
 7539                                let match_end = buffer_snapshot
 7540                                    .anchor_before(search_range.start + match_range.end);
 7541                                let match_anchor_range =
 7542                                    Anchor::range_in_buffer(excerpt_id, match_start..match_end);
 7543                                (match_anchor_range != query_range).then_some(match_anchor_range)
 7544                            }),
 7545                    );
 7546                }
 7547                match_ranges
 7548            });
 7549            let match_ranges = match_task.await;
 7550            editor
 7551                .update_in(cx, |editor, _, cx| {
 7552                    if use_debounce {
 7553                        editor.clear_background_highlights(HighlightKey::SelectedTextHighlight, cx);
 7554                        editor.debounced_selection_highlight_complete = true;
 7555                    } else if editor.debounced_selection_highlight_complete {
 7556                        return;
 7557                    }
 7558                    if !match_ranges.is_empty() {
 7559                        editor.highlight_background(
 7560                            HighlightKey::SelectedTextHighlight,
 7561                            &match_ranges,
 7562                            |_, theme| theme.colors().editor_document_highlight_bracket_background,
 7563                            cx,
 7564                        )
 7565                    }
 7566                })
 7567                .log_err();
 7568        })
 7569    }
 7570
 7571    fn refresh_single_line_folds(&mut self, window: &mut Window, cx: &mut Context<Editor>) {
 7572        struct NewlineFold;
 7573        let type_id = std::any::TypeId::of::<NewlineFold>();
 7574        if !self.mode.is_single_line() {
 7575            return;
 7576        }
 7577        let snapshot = self.snapshot(window, cx);
 7578        if snapshot.buffer_snapshot().max_point().row == 0 {
 7579            return;
 7580        }
 7581        let task = cx.background_spawn(async move {
 7582            let new_newlines = snapshot
 7583                .buffer_chars_at(MultiBufferOffset(0))
 7584                .filter_map(|(c, i)| {
 7585                    if c == '\n' {
 7586                        Some(
 7587                            snapshot.buffer_snapshot().anchor_after(i)
 7588                                ..snapshot.buffer_snapshot().anchor_before(i + 1usize),
 7589                        )
 7590                    } else {
 7591                        None
 7592                    }
 7593                })
 7594                .collect::<Vec<_>>();
 7595            let existing_newlines = snapshot
 7596                .folds_in_range(MultiBufferOffset(0)..snapshot.buffer_snapshot().len())
 7597                .filter_map(|fold| {
 7598                    if fold.placeholder.type_tag == Some(type_id) {
 7599                        Some(fold.range.start..fold.range.end)
 7600                    } else {
 7601                        None
 7602                    }
 7603                })
 7604                .collect::<Vec<_>>();
 7605
 7606            (new_newlines, existing_newlines)
 7607        });
 7608        self.folding_newlines = cx.spawn(async move |this, cx| {
 7609            let (new_newlines, existing_newlines) = task.await;
 7610            if new_newlines == existing_newlines {
 7611                return;
 7612            }
 7613            let placeholder = FoldPlaceholder {
 7614                render: Arc::new(move |_, _, cx| {
 7615                    div()
 7616                        .bg(cx.theme().status().hint_background)
 7617                        .border_b_1()
 7618                        .size_full()
 7619                        .font(ThemeSettings::get_global(cx).buffer_font.clone())
 7620                        .border_color(cx.theme().status().hint)
 7621                        .child("\\n")
 7622                        .into_any()
 7623                }),
 7624                constrain_width: false,
 7625                merge_adjacent: false,
 7626                type_tag: Some(type_id),
 7627                collapsed_text: None,
 7628            };
 7629            let creases = new_newlines
 7630                .into_iter()
 7631                .map(|range| Crease::simple(range, placeholder.clone()))
 7632                .collect();
 7633            this.update(cx, |this, cx| {
 7634                this.display_map.update(cx, |display_map, cx| {
 7635                    display_map.remove_folds_with_type(existing_newlines, type_id, cx);
 7636                    display_map.fold(creases, cx);
 7637                });
 7638            })
 7639            .ok();
 7640        });
 7641    }
 7642
 7643    #[ztracing::instrument(skip_all)]
 7644    fn refresh_outline_symbols_at_cursor(&mut self, cx: &mut Context<Editor>) {
 7645        if !self.mode.is_full() {
 7646            return;
 7647        }
 7648        let cursor = self.selections.newest_anchor().head();
 7649        let multi_buffer_snapshot = self.buffer().read(cx).snapshot(cx);
 7650
 7651        if self.uses_lsp_document_symbols(cursor, &multi_buffer_snapshot, cx) {
 7652            self.outline_symbols_at_cursor =
 7653                self.lsp_symbols_at_cursor(cursor, &multi_buffer_snapshot, cx);
 7654            cx.emit(EditorEvent::OutlineSymbolsChanged);
 7655            cx.notify();
 7656        } else {
 7657            let syntax = cx.theme().syntax().clone();
 7658            let background_task = cx.background_spawn(async move {
 7659                multi_buffer_snapshot.symbols_containing(cursor, Some(&syntax))
 7660            });
 7661            self.refresh_outline_symbols_at_cursor_at_cursor_task =
 7662                cx.spawn(async move |this, cx| {
 7663                    let symbols = background_task.await;
 7664                    this.update(cx, |this, cx| {
 7665                        this.outline_symbols_at_cursor = symbols;
 7666                        cx.emit(EditorEvent::OutlineSymbolsChanged);
 7667                        cx.notify();
 7668                    })
 7669                    .ok();
 7670                });
 7671        }
 7672    }
 7673
 7674    #[ztracing::instrument(skip_all)]
 7675    fn refresh_selected_text_highlights(
 7676        &mut self,
 7677        on_buffer_edit: bool,
 7678        window: &mut Window,
 7679        cx: &mut Context<Editor>,
 7680    ) {
 7681        let Some((query_text, query_range)) =
 7682            self.prepare_highlight_query_from_selection(window, cx)
 7683        else {
 7684            self.clear_background_highlights(HighlightKey::SelectedTextHighlight, cx);
 7685            self.quick_selection_highlight_task.take();
 7686            self.debounced_selection_highlight_task.take();
 7687            self.debounced_selection_highlight_complete = false;
 7688            return;
 7689        };
 7690        let display_snapshot = self.display_map.update(cx, |map, cx| map.snapshot(cx));
 7691        let multi_buffer_snapshot = self.buffer().read(cx).snapshot(cx);
 7692        let query_changed = self
 7693            .quick_selection_highlight_task
 7694            .as_ref()
 7695            .is_none_or(|(prev_anchor_range, _)| prev_anchor_range != &query_range);
 7696        if query_changed {
 7697            self.debounced_selection_highlight_complete = false;
 7698        }
 7699        if on_buffer_edit || query_changed {
 7700            let multi_buffer_visible_start = self
 7701                .scroll_manager
 7702                .native_anchor(&display_snapshot, cx)
 7703                .anchor
 7704                .to_point(&multi_buffer_snapshot);
 7705            let multi_buffer_visible_end = multi_buffer_snapshot.clip_point(
 7706                multi_buffer_visible_start
 7707                    + Point::new(self.visible_line_count().unwrap_or(0.).ceil() as u32, 0),
 7708                Bias::Left,
 7709            );
 7710            let multi_buffer_visible_range = multi_buffer_visible_start..multi_buffer_visible_end;
 7711            self.quick_selection_highlight_task = Some((
 7712                query_range.clone(),
 7713                self.update_selection_occurrence_highlights(
 7714                    query_text.clone(),
 7715                    query_range.clone(),
 7716                    multi_buffer_visible_range,
 7717                    false,
 7718                    window,
 7719                    cx,
 7720                ),
 7721            ));
 7722        }
 7723        if on_buffer_edit
 7724            || self
 7725                .debounced_selection_highlight_task
 7726                .as_ref()
 7727                .is_none_or(|(prev_anchor_range, _)| prev_anchor_range != &query_range)
 7728        {
 7729            let multi_buffer_start = multi_buffer_snapshot
 7730                .anchor_before(MultiBufferOffset(0))
 7731                .to_point(&multi_buffer_snapshot);
 7732            let multi_buffer_end = multi_buffer_snapshot
 7733                .anchor_after(multi_buffer_snapshot.len())
 7734                .to_point(&multi_buffer_snapshot);
 7735            let multi_buffer_full_range = multi_buffer_start..multi_buffer_end;
 7736            self.debounced_selection_highlight_task = Some((
 7737                query_range.clone(),
 7738                self.update_selection_occurrence_highlights(
 7739                    query_text,
 7740                    query_range,
 7741                    multi_buffer_full_range,
 7742                    true,
 7743                    window,
 7744                    cx,
 7745                ),
 7746            ));
 7747        }
 7748    }
 7749
 7750    pub fn refresh_edit_prediction(
 7751        &mut self,
 7752        debounce: bool,
 7753        user_requested: bool,
 7754        window: &mut Window,
 7755        cx: &mut Context<Self>,
 7756    ) -> Option<()> {
 7757        let provider = self.edit_prediction_provider()?;
 7758        let cursor = self.selections.newest_anchor().head();
 7759        let (buffer, cursor_buffer_position) =
 7760            self.buffer.read(cx).text_anchor_for_position(cursor, cx)?;
 7761
 7762        if DisableAiSettings::is_ai_disabled_for_buffer(Some(&buffer), cx) {
 7763            return None;
 7764        }
 7765
 7766        if !self.edit_predictions_enabled_in_buffer(&buffer, cursor_buffer_position, cx) {
 7767            self.discard_edit_prediction(EditPredictionDiscardReason::Ignored, cx);
 7768            return None;
 7769        }
 7770
 7771        self.update_visible_edit_prediction(window, cx);
 7772
 7773        if !user_requested
 7774            && (!self.should_show_edit_predictions()
 7775                || !self.is_focused(window)
 7776                || buffer.read(cx).is_empty())
 7777        {
 7778            self.discard_edit_prediction(EditPredictionDiscardReason::Ignored, cx);
 7779            return None;
 7780        }
 7781
 7782        provider.refresh(buffer, cursor_buffer_position, debounce, cx);
 7783        Some(())
 7784    }
 7785
 7786    fn show_edit_predictions_in_menu(&self) -> bool {
 7787        match self.edit_prediction_settings {
 7788            EditPredictionSettings::Disabled => false,
 7789            EditPredictionSettings::Enabled { show_in_menu, .. } => show_in_menu,
 7790        }
 7791    }
 7792
 7793    pub fn edit_predictions_enabled(&self) -> bool {
 7794        match self.edit_prediction_settings {
 7795            EditPredictionSettings::Disabled => false,
 7796            EditPredictionSettings::Enabled { .. } => true,
 7797        }
 7798    }
 7799
 7800    fn edit_prediction_requires_modifier(&self) -> bool {
 7801        match self.edit_prediction_settings {
 7802            EditPredictionSettings::Disabled => false,
 7803            EditPredictionSettings::Enabled {
 7804                preview_requires_modifier,
 7805                ..
 7806            } => preview_requires_modifier,
 7807        }
 7808    }
 7809
 7810    pub fn update_edit_prediction_settings(&mut self, cx: &mut Context<Self>) {
 7811        if self.edit_prediction_provider.is_none() {
 7812            self.edit_prediction_settings = EditPredictionSettings::Disabled;
 7813            self.discard_edit_prediction(EditPredictionDiscardReason::Ignored, cx);
 7814            return;
 7815        }
 7816
 7817        let selection = self.selections.newest_anchor();
 7818        let cursor = selection.head();
 7819
 7820        if let Some((buffer, cursor_buffer_position)) =
 7821            self.buffer.read(cx).text_anchor_for_position(cursor, cx)
 7822        {
 7823            if DisableAiSettings::is_ai_disabled_for_buffer(Some(&buffer), cx) {
 7824                self.edit_prediction_settings = EditPredictionSettings::Disabled;
 7825                self.discard_edit_prediction(EditPredictionDiscardReason::Ignored, cx);
 7826                return;
 7827            }
 7828            self.edit_prediction_settings =
 7829                self.edit_prediction_settings_at_position(&buffer, cursor_buffer_position, cx);
 7830        }
 7831    }
 7832
 7833    fn edit_prediction_settings_at_position(
 7834        &self,
 7835        buffer: &Entity<Buffer>,
 7836        buffer_position: language::Anchor,
 7837        cx: &App,
 7838    ) -> EditPredictionSettings {
 7839        if !self.mode.is_full()
 7840            || !self.show_edit_predictions_override.unwrap_or(true)
 7841            || self.edit_predictions_disabled_in_scope(buffer, buffer_position, cx)
 7842        {
 7843            return EditPredictionSettings::Disabled;
 7844        }
 7845
 7846        let buffer = buffer.read(cx);
 7847
 7848        let file = buffer.file();
 7849
 7850        if !language_settings(buffer.language().map(|l| l.name()), file, cx).show_edit_predictions {
 7851            return EditPredictionSettings::Disabled;
 7852        };
 7853
 7854        let by_provider = matches!(
 7855            self.menu_edit_predictions_policy,
 7856            MenuEditPredictionsPolicy::ByProvider
 7857        );
 7858
 7859        let show_in_menu = by_provider
 7860            && self
 7861                .edit_prediction_provider
 7862                .as_ref()
 7863                .is_some_and(|provider| provider.provider.show_predictions_in_menu());
 7864
 7865        let preview_requires_modifier =
 7866            all_language_settings(file, cx).edit_predictions_mode() == EditPredictionsMode::Subtle;
 7867
 7868        EditPredictionSettings::Enabled {
 7869            show_in_menu,
 7870            preview_requires_modifier,
 7871        }
 7872    }
 7873
 7874    fn should_show_edit_predictions(&self) -> bool {
 7875        self.snippet_stack.is_empty() && self.edit_predictions_enabled()
 7876    }
 7877
 7878    pub fn edit_prediction_preview_is_active(&self) -> bool {
 7879        matches!(
 7880            self.edit_prediction_preview,
 7881            EditPredictionPreview::Active { .. }
 7882        )
 7883    }
 7884
 7885    pub fn edit_predictions_enabled_at_cursor(&self, cx: &App) -> bool {
 7886        let cursor = self.selections.newest_anchor().head();
 7887        if let Some((buffer, cursor_position)) =
 7888            self.buffer.read(cx).text_anchor_for_position(cursor, cx)
 7889        {
 7890            self.edit_predictions_enabled_in_buffer(&buffer, cursor_position, cx)
 7891        } else {
 7892            false
 7893        }
 7894    }
 7895
 7896    pub fn supports_minimap(&self, cx: &App) -> bool {
 7897        !self.minimap_visibility.disabled() && self.buffer_kind(cx) == ItemBufferKind::Singleton
 7898    }
 7899
 7900    fn edit_predictions_enabled_in_buffer(
 7901        &self,
 7902        buffer: &Entity<Buffer>,
 7903        buffer_position: language::Anchor,
 7904        cx: &App,
 7905    ) -> bool {
 7906        maybe!({
 7907            if self.read_only(cx) {
 7908                return Some(false);
 7909            }
 7910            let provider = self.edit_prediction_provider()?;
 7911            if !provider.is_enabled(buffer, buffer_position, cx) {
 7912                return Some(false);
 7913            }
 7914            let buffer = buffer.read(cx);
 7915            let Some(file) = buffer.file() else {
 7916                return Some(true);
 7917            };
 7918            let settings = all_language_settings(Some(file), cx);
 7919            Some(settings.edit_predictions_enabled_for_file(file, cx))
 7920        })
 7921        .unwrap_or(false)
 7922    }
 7923
 7924    pub fn show_edit_prediction(
 7925        &mut self,
 7926        _: &ShowEditPrediction,
 7927        window: &mut Window,
 7928        cx: &mut Context<Self>,
 7929    ) {
 7930        if !self.has_active_edit_prediction() {
 7931            self.refresh_edit_prediction(false, true, window, cx);
 7932            return;
 7933        }
 7934
 7935        self.update_visible_edit_prediction(window, cx);
 7936    }
 7937
 7938    pub fn display_cursor_names(
 7939        &mut self,
 7940        _: &DisplayCursorNames,
 7941        window: &mut Window,
 7942        cx: &mut Context<Self>,
 7943    ) {
 7944        self.show_cursor_names(window, cx);
 7945    }
 7946
 7947    fn show_cursor_names(&mut self, window: &mut Window, cx: &mut Context<Self>) {
 7948        self.show_cursor_names = true;
 7949        cx.notify();
 7950        cx.spawn_in(window, async move |this, cx| {
 7951            cx.background_executor().timer(CURSORS_VISIBLE_FOR).await;
 7952            this.update(cx, |this, cx| {
 7953                this.show_cursor_names = false;
 7954                cx.notify()
 7955            })
 7956            .ok()
 7957        })
 7958        .detach();
 7959    }
 7960
 7961    pub fn accept_partial_edit_prediction(
 7962        &mut self,
 7963        granularity: EditPredictionGranularity,
 7964        window: &mut Window,
 7965        cx: &mut Context<Self>,
 7966    ) {
 7967        if self.show_edit_predictions_in_menu() {
 7968            self.hide_context_menu(window, cx);
 7969        }
 7970
 7971        let Some(active_edit_prediction) = self.active_edit_prediction.as_ref() else {
 7972            return;
 7973        };
 7974
 7975        if !matches!(granularity, EditPredictionGranularity::Full) && self.selections.count() != 1 {
 7976            return;
 7977        }
 7978
 7979        match &active_edit_prediction.completion {
 7980            EditPrediction::MoveWithin { target, .. } => {
 7981                let target = *target;
 7982
 7983                if matches!(granularity, EditPredictionGranularity::Full) {
 7984                    if let Some(position_map) = &self.last_position_map {
 7985                        let target_row = target.to_display_point(&position_map.snapshot).row();
 7986                        let is_visible = position_map.visible_row_range.contains(&target_row);
 7987
 7988                        if is_visible || !self.edit_prediction_requires_modifier() {
 7989                            self.unfold_ranges(&[target..target], true, false, cx);
 7990                            self.change_selections(
 7991                                SelectionEffects::scroll(Autoscroll::newest()),
 7992                                window,
 7993                                cx,
 7994                                |selections| {
 7995                                    selections.select_anchor_ranges([target..target]);
 7996                                },
 7997                            );
 7998                            self.clear_row_highlights::<EditPredictionPreview>();
 7999                            self.edit_prediction_preview
 8000                                .set_previous_scroll_position(None);
 8001                        } else {
 8002                            // Highlight and request scroll
 8003                            self.edit_prediction_preview
 8004                                .set_previous_scroll_position(Some(
 8005                                    position_map.snapshot.scroll_anchor,
 8006                                ));
 8007                            self.highlight_rows::<EditPredictionPreview>(
 8008                                target..target,
 8009                                cx.theme().colors().editor_highlighted_line_background,
 8010                                RowHighlightOptions {
 8011                                    autoscroll: true,
 8012                                    ..Default::default()
 8013                                },
 8014                                cx,
 8015                            );
 8016                            self.request_autoscroll(Autoscroll::fit(), cx);
 8017                        }
 8018                    }
 8019                } else {
 8020                    self.change_selections(
 8021                        SelectionEffects::scroll(Autoscroll::newest()),
 8022                        window,
 8023                        cx,
 8024                        |selections| {
 8025                            selections.select_anchor_ranges([target..target]);
 8026                        },
 8027                    );
 8028                }
 8029            }
 8030            EditPrediction::MoveOutside { snapshot, target } => {
 8031                if let Some(workspace) = self.workspace() {
 8032                    Self::open_editor_at_anchor(snapshot, *target, &workspace, window, cx)
 8033                        .detach_and_log_err(cx);
 8034                }
 8035            }
 8036            EditPrediction::Edit {
 8037                edits,
 8038                cursor_position,
 8039                ..
 8040            } => {
 8041                self.report_edit_prediction_event(
 8042                    active_edit_prediction.completion_id.clone(),
 8043                    true,
 8044                    cx,
 8045                );
 8046
 8047                match granularity {
 8048                    EditPredictionGranularity::Full => {
 8049                        let transaction_id_prev = self.buffer.read(cx).last_transaction_id(cx);
 8050
 8051                        // Compute fallback cursor position BEFORE applying the edit,
 8052                        // so the anchor tracks through the edit correctly
 8053                        let fallback_cursor_target = {
 8054                            let snapshot = self.buffer.read(cx).snapshot(cx);
 8055                            edits.last().unwrap().0.end.bias_right(&snapshot)
 8056                        };
 8057
 8058                        self.buffer.update(cx, |buffer, cx| {
 8059                            buffer.edit(edits.iter().cloned(), None, cx)
 8060                        });
 8061
 8062                        if let Some(provider) = self.edit_prediction_provider() {
 8063                            provider.accept(cx);
 8064                        }
 8065
 8066                        // Resolve cursor position after the edit is applied
 8067                        let cursor_target = if let Some((anchor, offset)) = cursor_position {
 8068                            // The anchor tracks through the edit, then we add the offset
 8069                            let snapshot = self.buffer.read(cx).snapshot(cx);
 8070                            let base_offset = anchor.to_offset(&snapshot).0;
 8071                            let target_offset =
 8072                                MultiBufferOffset((base_offset + offset).min(snapshot.len().0));
 8073                            snapshot.anchor_after(target_offset)
 8074                        } else {
 8075                            fallback_cursor_target
 8076                        };
 8077
 8078                        self.change_selections(SelectionEffects::no_scroll(), window, cx, |s| {
 8079                            s.select_anchor_ranges([cursor_target..cursor_target]);
 8080                        });
 8081
 8082                        let selections = self.selections.disjoint_anchors_arc();
 8083                        if let Some(transaction_id_now) =
 8084                            self.buffer.read(cx).last_transaction_id(cx)
 8085                        {
 8086                            if transaction_id_prev != Some(transaction_id_now) {
 8087                                self.selection_history
 8088                                    .insert_transaction(transaction_id_now, selections);
 8089                            }
 8090                        }
 8091
 8092                        self.update_visible_edit_prediction(window, cx);
 8093                        if self.active_edit_prediction.is_none() {
 8094                            self.refresh_edit_prediction(true, true, window, cx);
 8095                        }
 8096                        cx.notify();
 8097                    }
 8098                    _ => {
 8099                        let snapshot = self.buffer.read(cx).snapshot(cx);
 8100                        let cursor_offset = self
 8101                            .selections
 8102                            .newest::<MultiBufferOffset>(&self.display_snapshot(cx))
 8103                            .head();
 8104
 8105                        let insertion = edits.iter().find_map(|(range, text)| {
 8106                            let range = range.to_offset(&snapshot);
 8107                            if range.is_empty() && range.start == cursor_offset {
 8108                                Some(text)
 8109                            } else {
 8110                                None
 8111                            }
 8112                        });
 8113
 8114                        if let Some(text) = insertion {
 8115                            let text_to_insert = match granularity {
 8116                                EditPredictionGranularity::Word => {
 8117                                    let mut partial = text
 8118                                        .chars()
 8119                                        .by_ref()
 8120                                        .take_while(|c| c.is_alphabetic())
 8121                                        .collect::<String>();
 8122                                    if partial.is_empty() {
 8123                                        partial = text
 8124                                            .chars()
 8125                                            .by_ref()
 8126                                            .take_while(|c| c.is_whitespace() || !c.is_alphabetic())
 8127                                            .collect::<String>();
 8128                                    }
 8129                                    partial
 8130                                }
 8131                                EditPredictionGranularity::Line => {
 8132                                    if let Some(line) = text.split_inclusive('\n').next() {
 8133                                        line.to_string()
 8134                                    } else {
 8135                                        text.to_string()
 8136                                    }
 8137                                }
 8138                                EditPredictionGranularity::Full => unreachable!(),
 8139                            };
 8140
 8141                            cx.emit(EditorEvent::InputHandled {
 8142                                utf16_range_to_replace: None,
 8143                                text: text_to_insert.clone().into(),
 8144                            });
 8145
 8146                            self.insert_with_autoindent_mode(&text_to_insert, None, window, cx);
 8147                            self.refresh_edit_prediction(true, true, window, cx);
 8148                            cx.notify();
 8149                        } else {
 8150                            self.accept_partial_edit_prediction(
 8151                                EditPredictionGranularity::Full,
 8152                                window,
 8153                                cx,
 8154                            );
 8155                        }
 8156                    }
 8157                }
 8158            }
 8159        }
 8160
 8161        self.edit_prediction_requires_modifier_in_indent_conflict = false;
 8162    }
 8163
 8164    pub fn accept_next_word_edit_prediction(
 8165        &mut self,
 8166        _: &AcceptNextWordEditPrediction,
 8167        window: &mut Window,
 8168        cx: &mut Context<Self>,
 8169    ) {
 8170        self.accept_partial_edit_prediction(EditPredictionGranularity::Word, window, cx);
 8171    }
 8172
 8173    pub fn accept_next_line_edit_prediction(
 8174        &mut self,
 8175        _: &AcceptNextLineEditPrediction,
 8176        window: &mut Window,
 8177        cx: &mut Context<Self>,
 8178    ) {
 8179        self.accept_partial_edit_prediction(EditPredictionGranularity::Line, window, cx);
 8180    }
 8181
 8182    pub fn accept_edit_prediction(
 8183        &mut self,
 8184        _: &AcceptEditPrediction,
 8185        window: &mut Window,
 8186        cx: &mut Context<Self>,
 8187    ) {
 8188        self.accept_partial_edit_prediction(EditPredictionGranularity::Full, window, cx);
 8189    }
 8190
 8191    fn discard_edit_prediction(
 8192        &mut self,
 8193        reason: EditPredictionDiscardReason,
 8194        cx: &mut Context<Self>,
 8195    ) -> bool {
 8196        if reason == EditPredictionDiscardReason::Rejected {
 8197            let completion_id = self
 8198                .active_edit_prediction
 8199                .as_ref()
 8200                .and_then(|active_completion| active_completion.completion_id.clone());
 8201
 8202            self.report_edit_prediction_event(completion_id, false, cx);
 8203        }
 8204
 8205        if let Some(provider) = self.edit_prediction_provider() {
 8206            provider.discard(reason, cx);
 8207        }
 8208
 8209        self.take_active_edit_prediction(cx)
 8210    }
 8211
 8212    fn report_edit_prediction_event(&self, id: Option<SharedString>, accepted: bool, cx: &App) {
 8213        let Some(provider) = self.edit_prediction_provider() else {
 8214            return;
 8215        };
 8216
 8217        let Some((_, buffer, _)) = self
 8218            .buffer
 8219            .read(cx)
 8220            .excerpt_containing(self.selections.newest_anchor().head(), cx)
 8221        else {
 8222            return;
 8223        };
 8224
 8225        let extension = buffer
 8226            .read(cx)
 8227            .file()
 8228            .and_then(|file| Some(file.path().extension()?.to_string()));
 8229
 8230        let event_type = match accepted {
 8231            true => "Edit Prediction Accepted",
 8232            false => "Edit Prediction Discarded",
 8233        };
 8234        telemetry::event!(
 8235            event_type,
 8236            provider = provider.name(),
 8237            prediction_id = id,
 8238            suggestion_accepted = accepted,
 8239            file_extension = extension,
 8240        );
 8241    }
 8242
 8243    fn open_editor_at_anchor(
 8244        snapshot: &language::BufferSnapshot,
 8245        target: language::Anchor,
 8246        workspace: &Entity<Workspace>,
 8247        window: &mut Window,
 8248        cx: &mut App,
 8249    ) -> Task<Result<()>> {
 8250        workspace.update(cx, |workspace, cx| {
 8251            let path = snapshot.file().map(|file| file.full_path(cx));
 8252            let Some(path) =
 8253                path.and_then(|path| workspace.project().read(cx).find_project_path(path, cx))
 8254            else {
 8255                return Task::ready(Err(anyhow::anyhow!("Project path not found")));
 8256            };
 8257            let target = text::ToPoint::to_point(&target, snapshot);
 8258            let item = workspace.open_path(path, None, true, window, cx);
 8259            window.spawn(cx, async move |cx| {
 8260                let Some(editor) = item.await?.downcast::<Editor>() else {
 8261                    return Ok(());
 8262                };
 8263                editor
 8264                    .update_in(cx, |editor, window, cx| {
 8265                        editor.go_to_singleton_buffer_point(target, window, cx);
 8266                    })
 8267                    .ok();
 8268                anyhow::Ok(())
 8269            })
 8270        })
 8271    }
 8272
 8273    pub fn has_active_edit_prediction(&self) -> bool {
 8274        self.active_edit_prediction.is_some()
 8275    }
 8276
 8277    fn take_active_edit_prediction(&mut self, cx: &mut Context<Self>) -> bool {
 8278        let Some(active_edit_prediction) = self.active_edit_prediction.take() else {
 8279            return false;
 8280        };
 8281
 8282        self.splice_inlays(&active_edit_prediction.inlay_ids, Default::default(), cx);
 8283        self.clear_highlights(HighlightKey::EditPredictionHighlight, cx);
 8284        self.stale_edit_prediction_in_menu = Some(active_edit_prediction);
 8285        true
 8286    }
 8287
 8288    /// Returns true when we're displaying the edit prediction popover below the cursor
 8289    /// like we are not previewing and the LSP autocomplete menu is visible
 8290    /// or we are in `when_holding_modifier` mode.
 8291    pub fn edit_prediction_visible_in_cursor_popover(&self, has_completion: bool) -> bool {
 8292        if self.edit_prediction_preview_is_active()
 8293            || !self.show_edit_predictions_in_menu()
 8294            || !self.edit_predictions_enabled()
 8295        {
 8296            return false;
 8297        }
 8298
 8299        if self.has_visible_completions_menu() {
 8300            return true;
 8301        }
 8302
 8303        has_completion && self.edit_prediction_requires_modifier()
 8304    }
 8305
 8306    fn handle_modifiers_changed(
 8307        &mut self,
 8308        modifiers: Modifiers,
 8309        position_map: &PositionMap,
 8310        window: &mut Window,
 8311        cx: &mut Context<Self>,
 8312    ) {
 8313        // Ensure that the edit prediction preview is updated, even when not
 8314        // enabled, if there's an active edit prediction preview.
 8315        if self.show_edit_predictions_in_menu()
 8316            || matches!(
 8317                self.edit_prediction_preview,
 8318                EditPredictionPreview::Active { .. }
 8319            )
 8320        {
 8321            self.update_edit_prediction_preview(&modifiers, window, cx);
 8322        }
 8323
 8324        self.update_selection_mode(&modifiers, position_map, window, cx);
 8325
 8326        let mouse_position = window.mouse_position();
 8327        if !position_map.text_hitbox.is_hovered(window) {
 8328            return;
 8329        }
 8330
 8331        self.update_hovered_link(
 8332            position_map.point_for_position(mouse_position),
 8333            &position_map.snapshot,
 8334            modifiers,
 8335            window,
 8336            cx,
 8337        )
 8338    }
 8339
 8340    fn is_cmd_or_ctrl_pressed(modifiers: &Modifiers, cx: &mut Context<Self>) -> bool {
 8341        match EditorSettings::get_global(cx).multi_cursor_modifier {
 8342            MultiCursorModifier::Alt => modifiers.secondary(),
 8343            MultiCursorModifier::CmdOrCtrl => modifiers.alt,
 8344        }
 8345    }
 8346
 8347    fn is_alt_pressed(modifiers: &Modifiers, cx: &mut Context<Self>) -> bool {
 8348        match EditorSettings::get_global(cx).multi_cursor_modifier {
 8349            MultiCursorModifier::Alt => modifiers.alt,
 8350            MultiCursorModifier::CmdOrCtrl => modifiers.secondary(),
 8351        }
 8352    }
 8353
 8354    fn columnar_selection_mode(
 8355        modifiers: &Modifiers,
 8356        cx: &mut Context<Self>,
 8357    ) -> Option<ColumnarMode> {
 8358        if modifiers.shift && modifiers.number_of_modifiers() == 2 {
 8359            if Self::is_cmd_or_ctrl_pressed(modifiers, cx) {
 8360                Some(ColumnarMode::FromMouse)
 8361            } else if Self::is_alt_pressed(modifiers, cx) {
 8362                Some(ColumnarMode::FromSelection)
 8363            } else {
 8364                None
 8365            }
 8366        } else {
 8367            None
 8368        }
 8369    }
 8370
 8371    fn update_selection_mode(
 8372        &mut self,
 8373        modifiers: &Modifiers,
 8374        position_map: &PositionMap,
 8375        window: &mut Window,
 8376        cx: &mut Context<Self>,
 8377    ) {
 8378        let Some(mode) = Self::columnar_selection_mode(modifiers, cx) else {
 8379            return;
 8380        };
 8381        if self.selections.pending_anchor().is_none() {
 8382            return;
 8383        }
 8384
 8385        let mouse_position = window.mouse_position();
 8386        let point_for_position = position_map.point_for_position(mouse_position);
 8387        let position = point_for_position.previous_valid;
 8388
 8389        self.select(
 8390            SelectPhase::BeginColumnar {
 8391                position,
 8392                reset: false,
 8393                mode,
 8394                goal_column: point_for_position.exact_unclipped.column(),
 8395            },
 8396            window,
 8397            cx,
 8398        );
 8399    }
 8400
 8401    fn update_edit_prediction_preview(
 8402        &mut self,
 8403        modifiers: &Modifiers,
 8404        window: &mut Window,
 8405        cx: &mut Context<Self>,
 8406    ) {
 8407        let mut modifiers_held = false;
 8408
 8409        // Check bindings for all granularities.
 8410        // If the user holds the key for Word, Line, or Full, we want to show the preview.
 8411        let granularities = [
 8412            EditPredictionGranularity::Full,
 8413            EditPredictionGranularity::Line,
 8414            EditPredictionGranularity::Word,
 8415        ];
 8416
 8417        for granularity in granularities {
 8418            if let Some(keystroke) = self
 8419                .accept_edit_prediction_keybind(granularity, window, cx)
 8420                .keystroke()
 8421            {
 8422                modifiers_held = modifiers_held
 8423                    || (keystroke.modifiers() == modifiers && keystroke.modifiers().modified());
 8424            }
 8425        }
 8426
 8427        if modifiers_held {
 8428            if matches!(
 8429                self.edit_prediction_preview,
 8430                EditPredictionPreview::Inactive { .. }
 8431            ) {
 8432                self.edit_prediction_preview = EditPredictionPreview::Active {
 8433                    previous_scroll_position: None,
 8434                    since: Instant::now(),
 8435                };
 8436
 8437                self.update_visible_edit_prediction(window, cx);
 8438                cx.notify();
 8439            }
 8440        } else if let EditPredictionPreview::Active {
 8441            previous_scroll_position,
 8442            since,
 8443        } = self.edit_prediction_preview
 8444        {
 8445            if let (Some(previous_scroll_position), Some(position_map)) =
 8446                (previous_scroll_position, self.last_position_map.as_ref())
 8447            {
 8448                self.set_scroll_position(
 8449                    previous_scroll_position
 8450                        .scroll_position(&position_map.snapshot.display_snapshot),
 8451                    window,
 8452                    cx,
 8453                );
 8454            }
 8455
 8456            self.edit_prediction_preview = EditPredictionPreview::Inactive {
 8457                released_too_fast: since.elapsed() < Duration::from_millis(200),
 8458            };
 8459            self.clear_row_highlights::<EditPredictionPreview>();
 8460            self.update_visible_edit_prediction(window, cx);
 8461            cx.notify();
 8462        }
 8463    }
 8464
 8465    fn update_visible_edit_prediction(
 8466        &mut self,
 8467        _window: &mut Window,
 8468        cx: &mut Context<Self>,
 8469    ) -> Option<()> {
 8470        if self.ime_transaction.is_some() {
 8471            self.discard_edit_prediction(EditPredictionDiscardReason::Ignored, cx);
 8472            return None;
 8473        }
 8474
 8475        let selection = self.selections.newest_anchor();
 8476        let cursor = selection.head();
 8477        let multibuffer = self.buffer.read(cx).snapshot(cx);
 8478
 8479        // Check project-level disable_ai setting for the current buffer
 8480        if let Some((buffer, _)) = self.buffer.read(cx).text_anchor_for_position(cursor, cx) {
 8481            if DisableAiSettings::is_ai_disabled_for_buffer(Some(&buffer), cx) {
 8482                return None;
 8483            }
 8484        }
 8485        let offset_selection = selection.map(|endpoint| endpoint.to_offset(&multibuffer));
 8486        let excerpt_id = cursor.excerpt_id;
 8487
 8488        let show_in_menu = self.show_edit_predictions_in_menu();
 8489        let completions_menu_has_precedence = !show_in_menu
 8490            && (self.context_menu.borrow().is_some()
 8491                || (!self.completion_tasks.is_empty() && !self.has_active_edit_prediction()));
 8492
 8493        if completions_menu_has_precedence
 8494            || !offset_selection.is_empty()
 8495            || self
 8496                .active_edit_prediction
 8497                .as_ref()
 8498                .is_some_and(|completion| {
 8499                    let Some(invalidation_range) = completion.invalidation_range.as_ref() else {
 8500                        return false;
 8501                    };
 8502                    let invalidation_range = invalidation_range.to_offset(&multibuffer);
 8503                    let invalidation_range = invalidation_range.start..=invalidation_range.end;
 8504                    !invalidation_range.contains(&offset_selection.head())
 8505                })
 8506        {
 8507            self.discard_edit_prediction(EditPredictionDiscardReason::Ignored, cx);
 8508            return None;
 8509        }
 8510
 8511        self.take_active_edit_prediction(cx);
 8512        let Some(provider) = self.edit_prediction_provider() else {
 8513            self.edit_prediction_settings = EditPredictionSettings::Disabled;
 8514            return None;
 8515        };
 8516
 8517        let (buffer, cursor_buffer_position) =
 8518            self.buffer.read(cx).text_anchor_for_position(cursor, cx)?;
 8519
 8520        self.edit_prediction_settings =
 8521            self.edit_prediction_settings_at_position(&buffer, cursor_buffer_position, cx);
 8522
 8523        self.edit_prediction_indent_conflict = multibuffer.is_line_whitespace_upto(cursor);
 8524
 8525        if self.edit_prediction_indent_conflict {
 8526            let cursor_point = cursor.to_point(&multibuffer);
 8527            let mut suggested_indent = None;
 8528            multibuffer.suggested_indents_callback(
 8529                cursor_point.row..cursor_point.row + 1,
 8530                &mut |_, indent| {
 8531                    suggested_indent = Some(indent);
 8532                    ControlFlow::Break(())
 8533                },
 8534                cx,
 8535            );
 8536
 8537            if let Some(indent) = suggested_indent
 8538                && indent.len == cursor_point.column
 8539            {
 8540                self.edit_prediction_indent_conflict = false;
 8541            }
 8542        }
 8543
 8544        let edit_prediction = provider.suggest(&buffer, cursor_buffer_position, cx)?;
 8545
 8546        let (completion_id, edits, predicted_cursor_position, edit_preview) = match edit_prediction
 8547        {
 8548            edit_prediction_types::EditPrediction::Local {
 8549                id,
 8550                edits,
 8551                cursor_position,
 8552                edit_preview,
 8553            } => (id, edits, cursor_position, edit_preview),
 8554            edit_prediction_types::EditPrediction::Jump {
 8555                id,
 8556                snapshot,
 8557                target,
 8558            } => {
 8559                if let Some(provider) = &self.edit_prediction_provider {
 8560                    provider.provider.did_show(SuggestionDisplayType::Jump, cx);
 8561                }
 8562                self.stale_edit_prediction_in_menu = None;
 8563                self.active_edit_prediction = Some(EditPredictionState {
 8564                    inlay_ids: vec![],
 8565                    completion: EditPrediction::MoveOutside { snapshot, target },
 8566                    completion_id: id,
 8567                    invalidation_range: None,
 8568                });
 8569                cx.notify();
 8570                return Some(());
 8571            }
 8572        };
 8573
 8574        let edits = edits
 8575            .into_iter()
 8576            .flat_map(|(range, new_text)| {
 8577                Some((
 8578                    multibuffer.anchor_range_in_excerpt(excerpt_id, range)?,
 8579                    new_text,
 8580                ))
 8581            })
 8582            .collect::<Vec<_>>();
 8583        if edits.is_empty() {
 8584            return None;
 8585        }
 8586
 8587        let cursor_position = predicted_cursor_position.and_then(|predicted| {
 8588            let anchor = multibuffer.anchor_in_excerpt(excerpt_id, predicted.anchor)?;
 8589            Some((anchor, predicted.offset))
 8590        });
 8591
 8592        let first_edit_start = edits.first().unwrap().0.start;
 8593        let first_edit_start_point = first_edit_start.to_point(&multibuffer);
 8594        let edit_start_row = first_edit_start_point.row.saturating_sub(2);
 8595
 8596        let last_edit_end = edits.last().unwrap().0.end;
 8597        let last_edit_end_point = last_edit_end.to_point(&multibuffer);
 8598        let edit_end_row = cmp::min(multibuffer.max_point().row, last_edit_end_point.row + 2);
 8599
 8600        let cursor_row = cursor.to_point(&multibuffer).row;
 8601
 8602        let snapshot = multibuffer.buffer_for_excerpt(excerpt_id).cloned()?;
 8603
 8604        let mut inlay_ids = Vec::new();
 8605        let invalidation_row_range;
 8606        let move_invalidation_row_range = if cursor_row < edit_start_row {
 8607            Some(cursor_row..edit_end_row)
 8608        } else if cursor_row > edit_end_row {
 8609            Some(edit_start_row..cursor_row)
 8610        } else {
 8611            None
 8612        };
 8613        let supports_jump = self
 8614            .edit_prediction_provider
 8615            .as_ref()
 8616            .map(|provider| provider.provider.supports_jump_to_edit())
 8617            .unwrap_or(true);
 8618
 8619        let is_move = supports_jump
 8620            && (move_invalidation_row_range.is_some() || self.edit_predictions_hidden_for_vim_mode);
 8621        let completion = if is_move {
 8622            if let Some(provider) = &self.edit_prediction_provider {
 8623                provider.provider.did_show(SuggestionDisplayType::Jump, cx);
 8624            }
 8625            invalidation_row_range =
 8626                move_invalidation_row_range.unwrap_or(edit_start_row..edit_end_row);
 8627            let target = first_edit_start;
 8628            EditPrediction::MoveWithin { target, snapshot }
 8629        } else {
 8630            let show_completions_in_buffer = !self.edit_prediction_visible_in_cursor_popover(true)
 8631                && !self.edit_predictions_hidden_for_vim_mode;
 8632
 8633            let display_mode = if all_edits_insertions_or_deletions(&edits, &multibuffer) {
 8634                if provider.show_tab_accept_marker() {
 8635                    EditDisplayMode::TabAccept
 8636                } else {
 8637                    EditDisplayMode::Inline
 8638                }
 8639            } else {
 8640                EditDisplayMode::DiffPopover
 8641            };
 8642
 8643            if show_completions_in_buffer {
 8644                if let Some(provider) = &self.edit_prediction_provider {
 8645                    let suggestion_display_type = match display_mode {
 8646                        EditDisplayMode::DiffPopover => SuggestionDisplayType::DiffPopover,
 8647                        EditDisplayMode::Inline | EditDisplayMode::TabAccept => {
 8648                            SuggestionDisplayType::GhostText
 8649                        }
 8650                    };
 8651                    provider.provider.did_show(suggestion_display_type, cx);
 8652                }
 8653                if edits
 8654                    .iter()
 8655                    .all(|(range, _)| range.to_offset(&multibuffer).is_empty())
 8656                {
 8657                    let mut inlays = Vec::new();
 8658                    for (range, new_text) in &edits {
 8659                        let inlay = Inlay::edit_prediction(
 8660                            post_inc(&mut self.next_inlay_id),
 8661                            range.start,
 8662                            new_text.as_ref(),
 8663                        );
 8664                        inlay_ids.push(inlay.id);
 8665                        inlays.push(inlay);
 8666                    }
 8667
 8668                    self.splice_inlays(&[], inlays, cx);
 8669                } else {
 8670                    let background_color = cx.theme().status().deleted_background;
 8671                    self.highlight_text(
 8672                        HighlightKey::EditPredictionHighlight,
 8673                        edits.iter().map(|(range, _)| range.clone()).collect(),
 8674                        HighlightStyle {
 8675                            background_color: Some(background_color),
 8676                            ..Default::default()
 8677                        },
 8678                        cx,
 8679                    );
 8680                }
 8681            }
 8682
 8683            invalidation_row_range = edit_start_row..edit_end_row;
 8684
 8685            EditPrediction::Edit {
 8686                edits,
 8687                cursor_position,
 8688                edit_preview,
 8689                display_mode,
 8690                snapshot,
 8691            }
 8692        };
 8693
 8694        let invalidation_range = multibuffer
 8695            .anchor_before(Point::new(invalidation_row_range.start, 0))
 8696            ..multibuffer.anchor_after(Point::new(
 8697                invalidation_row_range.end,
 8698                multibuffer.line_len(MultiBufferRow(invalidation_row_range.end)),
 8699            ));
 8700
 8701        self.stale_edit_prediction_in_menu = None;
 8702        self.active_edit_prediction = Some(EditPredictionState {
 8703            inlay_ids,
 8704            completion,
 8705            completion_id,
 8706            invalidation_range: Some(invalidation_range),
 8707        });
 8708
 8709        cx.notify();
 8710
 8711        Some(())
 8712    }
 8713
 8714    pub fn edit_prediction_provider(&self) -> Option<Arc<dyn EditPredictionDelegateHandle>> {
 8715        Some(self.edit_prediction_provider.as_ref()?.provider.clone())
 8716    }
 8717
 8718    fn clear_tasks(&mut self) {
 8719        self.tasks.clear()
 8720    }
 8721
 8722    fn insert_tasks(&mut self, key: (BufferId, BufferRow), value: RunnableTasks) {
 8723        if self.tasks.insert(key, value).is_some() {
 8724            // This case should hopefully be rare, but just in case...
 8725            log::error!(
 8726                "multiple different run targets found on a single line, only the last target will be rendered"
 8727            )
 8728        }
 8729    }
 8730
 8731    /// Get all display points of breakpoints that will be rendered within editor
 8732    ///
 8733    /// This function is used to handle overlaps between breakpoints and Code action/runner symbol.
 8734    /// It's also used to set the color of line numbers with breakpoints to the breakpoint color.
 8735    /// TODO debugger: Use this function to color toggle symbols that house nested breakpoints
 8736    fn active_breakpoints(
 8737        &self,
 8738        range: Range<DisplayRow>,
 8739        window: &mut Window,
 8740        cx: &mut Context<Self>,
 8741    ) -> HashMap<DisplayRow, (Anchor, Breakpoint, Option<BreakpointSessionState>)> {
 8742        let mut breakpoint_display_points = HashMap::default();
 8743
 8744        let Some(breakpoint_store) = self.breakpoint_store.clone() else {
 8745            return breakpoint_display_points;
 8746        };
 8747
 8748        let snapshot = self.snapshot(window, cx);
 8749
 8750        let multi_buffer_snapshot = snapshot.buffer_snapshot();
 8751        let Some(project) = self.project() else {
 8752            return breakpoint_display_points;
 8753        };
 8754
 8755        let range = snapshot.display_point_to_point(DisplayPoint::new(range.start, 0), Bias::Left)
 8756            ..snapshot.display_point_to_point(DisplayPoint::new(range.end, 0), Bias::Right);
 8757
 8758        for (buffer_snapshot, range, excerpt_id) in
 8759            multi_buffer_snapshot.range_to_buffer_ranges(range.start..=range.end)
 8760        {
 8761            let Some(buffer) = project
 8762                .read(cx)
 8763                .buffer_for_id(buffer_snapshot.remote_id(), cx)
 8764            else {
 8765                continue;
 8766            };
 8767            let breakpoints = breakpoint_store.read(cx).breakpoints(
 8768                &buffer,
 8769                Some(
 8770                    buffer_snapshot.anchor_before(range.start)
 8771                        ..buffer_snapshot.anchor_after(range.end),
 8772                ),
 8773                buffer_snapshot,
 8774                cx,
 8775            );
 8776            for (breakpoint, state) in breakpoints {
 8777                let multi_buffer_anchor = Anchor::in_buffer(excerpt_id, breakpoint.position);
 8778                let position = multi_buffer_anchor
 8779                    .to_point(&multi_buffer_snapshot)
 8780                    .to_display_point(&snapshot);
 8781
 8782                breakpoint_display_points.insert(
 8783                    position.row(),
 8784                    (multi_buffer_anchor, breakpoint.bp.clone(), state),
 8785                );
 8786            }
 8787        }
 8788
 8789        breakpoint_display_points
 8790    }
 8791
 8792    fn breakpoint_context_menu(
 8793        &self,
 8794        anchor: Anchor,
 8795        window: &mut Window,
 8796        cx: &mut Context<Self>,
 8797    ) -> Entity<ui::ContextMenu> {
 8798        let weak_editor = cx.weak_entity();
 8799        let focus_handle = self.focus_handle(cx);
 8800
 8801        let row = self
 8802            .buffer
 8803            .read(cx)
 8804            .snapshot(cx)
 8805            .summary_for_anchor::<Point>(&anchor)
 8806            .row;
 8807
 8808        let breakpoint = self
 8809            .breakpoint_at_row(row, window, cx)
 8810            .map(|(anchor, bp)| (anchor, Arc::from(bp)));
 8811
 8812        let log_breakpoint_msg = if breakpoint.as_ref().is_some_and(|bp| bp.1.message.is_some()) {
 8813            "Edit Log Breakpoint"
 8814        } else {
 8815            "Set Log Breakpoint"
 8816        };
 8817
 8818        let condition_breakpoint_msg = if breakpoint
 8819            .as_ref()
 8820            .is_some_and(|bp| bp.1.condition.is_some())
 8821        {
 8822            "Edit Condition Breakpoint"
 8823        } else {
 8824            "Set Condition Breakpoint"
 8825        };
 8826
 8827        let hit_condition_breakpoint_msg = if breakpoint
 8828            .as_ref()
 8829            .is_some_and(|bp| bp.1.hit_condition.is_some())
 8830        {
 8831            "Edit Hit Condition Breakpoint"
 8832        } else {
 8833            "Set Hit Condition Breakpoint"
 8834        };
 8835
 8836        let set_breakpoint_msg = if breakpoint.as_ref().is_some() {
 8837            "Unset Breakpoint"
 8838        } else {
 8839            "Set Breakpoint"
 8840        };
 8841
 8842        let run_to_cursor = window.is_action_available(&RunToCursor, cx);
 8843
 8844        let toggle_state_msg = breakpoint.as_ref().map_or(None, |bp| match bp.1.state {
 8845            BreakpointState::Enabled => Some("Disable"),
 8846            BreakpointState::Disabled => Some("Enable"),
 8847        });
 8848
 8849        let (anchor, breakpoint) =
 8850            breakpoint.unwrap_or_else(|| (anchor, Arc::new(Breakpoint::new_standard())));
 8851
 8852        ui::ContextMenu::build(window, cx, |menu, _, _cx| {
 8853            menu.on_blur_subscription(Subscription::new(|| {}))
 8854                .context(focus_handle)
 8855                .when(run_to_cursor, |this| {
 8856                    let weak_editor = weak_editor.clone();
 8857                    this.entry("Run to cursor", None, move |window, cx| {
 8858                        weak_editor
 8859                            .update(cx, |editor, cx| {
 8860                                editor.change_selections(
 8861                                    SelectionEffects::no_scroll(),
 8862                                    window,
 8863                                    cx,
 8864                                    |s| s.select_ranges([Point::new(row, 0)..Point::new(row, 0)]),
 8865                                );
 8866                            })
 8867                            .ok();
 8868
 8869                        window.dispatch_action(Box::new(RunToCursor), cx);
 8870                    })
 8871                    .separator()
 8872                })
 8873                .when_some(toggle_state_msg, |this, msg| {
 8874                    this.entry(msg, None, {
 8875                        let weak_editor = weak_editor.clone();
 8876                        let breakpoint = breakpoint.clone();
 8877                        move |_window, cx| {
 8878                            weak_editor
 8879                                .update(cx, |this, cx| {
 8880                                    this.edit_breakpoint_at_anchor(
 8881                                        anchor,
 8882                                        breakpoint.as_ref().clone(),
 8883                                        BreakpointEditAction::InvertState,
 8884                                        cx,
 8885                                    );
 8886                                })
 8887                                .log_err();
 8888                        }
 8889                    })
 8890                })
 8891                .entry(set_breakpoint_msg, None, {
 8892                    let weak_editor = weak_editor.clone();
 8893                    let breakpoint = breakpoint.clone();
 8894                    move |_window, cx| {
 8895                        weak_editor
 8896                            .update(cx, |this, cx| {
 8897                                this.edit_breakpoint_at_anchor(
 8898                                    anchor,
 8899                                    breakpoint.as_ref().clone(),
 8900                                    BreakpointEditAction::Toggle,
 8901                                    cx,
 8902                                );
 8903                            })
 8904                            .log_err();
 8905                    }
 8906                })
 8907                .entry(log_breakpoint_msg, None, {
 8908                    let breakpoint = breakpoint.clone();
 8909                    let weak_editor = weak_editor.clone();
 8910                    move |window, cx| {
 8911                        weak_editor
 8912                            .update(cx, |this, cx| {
 8913                                this.add_edit_breakpoint_block(
 8914                                    anchor,
 8915                                    breakpoint.as_ref(),
 8916                                    BreakpointPromptEditAction::Log,
 8917                                    window,
 8918                                    cx,
 8919                                );
 8920                            })
 8921                            .log_err();
 8922                    }
 8923                })
 8924                .entry(condition_breakpoint_msg, None, {
 8925                    let breakpoint = breakpoint.clone();
 8926                    let weak_editor = weak_editor.clone();
 8927                    move |window, cx| {
 8928                        weak_editor
 8929                            .update(cx, |this, cx| {
 8930                                this.add_edit_breakpoint_block(
 8931                                    anchor,
 8932                                    breakpoint.as_ref(),
 8933                                    BreakpointPromptEditAction::Condition,
 8934                                    window,
 8935                                    cx,
 8936                                );
 8937                            })
 8938                            .log_err();
 8939                    }
 8940                })
 8941                .entry(hit_condition_breakpoint_msg, None, move |window, cx| {
 8942                    weak_editor
 8943                        .update(cx, |this, cx| {
 8944                            this.add_edit_breakpoint_block(
 8945                                anchor,
 8946                                breakpoint.as_ref(),
 8947                                BreakpointPromptEditAction::HitCondition,
 8948                                window,
 8949                                cx,
 8950                            );
 8951                        })
 8952                        .log_err();
 8953                })
 8954        })
 8955    }
 8956
 8957    fn render_breakpoint(
 8958        &self,
 8959        position: Anchor,
 8960        row: DisplayRow,
 8961        breakpoint: &Breakpoint,
 8962        state: Option<BreakpointSessionState>,
 8963        cx: &mut Context<Self>,
 8964    ) -> IconButton {
 8965        let is_rejected = state.is_some_and(|s| !s.verified);
 8966        // Is it a breakpoint that shows up when hovering over gutter?
 8967        let (is_phantom, collides_with_existing) = self.gutter_breakpoint_indicator.0.map_or(
 8968            (false, false),
 8969            |PhantomBreakpointIndicator {
 8970                 is_active,
 8971                 display_row,
 8972                 collides_with_existing_breakpoint,
 8973             }| {
 8974                (
 8975                    is_active && display_row == row,
 8976                    collides_with_existing_breakpoint,
 8977                )
 8978            },
 8979        );
 8980
 8981        let (color, icon) = {
 8982            let icon = match (&breakpoint.message.is_some(), breakpoint.is_disabled()) {
 8983                (false, false) => ui::IconName::DebugBreakpoint,
 8984                (true, false) => ui::IconName::DebugLogBreakpoint,
 8985                (false, true) => ui::IconName::DebugDisabledBreakpoint,
 8986                (true, true) => ui::IconName::DebugDisabledLogBreakpoint,
 8987            };
 8988
 8989            let theme_colors = cx.theme().colors();
 8990
 8991            let color = if is_phantom {
 8992                if collides_with_existing {
 8993                    Color::Custom(
 8994                        theme_colors
 8995                            .debugger_accent
 8996                            .blend(theme_colors.text.opacity(0.6)),
 8997                    )
 8998                } else {
 8999                    Color::Hint
 9000                }
 9001            } else if is_rejected {
 9002                Color::Disabled
 9003            } else {
 9004                Color::Debugger
 9005            };
 9006
 9007            (color, icon)
 9008        };
 9009
 9010        let breakpoint = Arc::from(breakpoint.clone());
 9011
 9012        let alt_as_text = gpui::Keystroke {
 9013            modifiers: Modifiers::secondary_key(),
 9014            ..Default::default()
 9015        };
 9016        let primary_action_text = if breakpoint.is_disabled() {
 9017            "Enable breakpoint"
 9018        } else if is_phantom && !collides_with_existing {
 9019            "Set breakpoint"
 9020        } else {
 9021            "Unset breakpoint"
 9022        };
 9023        let focus_handle = self.focus_handle.clone();
 9024
 9025        let meta = if is_rejected {
 9026            SharedString::from("No executable code is associated with this line.")
 9027        } else if collides_with_existing && !breakpoint.is_disabled() {
 9028            SharedString::from(format!(
 9029                "{alt_as_text}-click to disable,\nright-click for more options."
 9030            ))
 9031        } else {
 9032            SharedString::from("Right-click for more options.")
 9033        };
 9034        IconButton::new(("breakpoint_indicator", row.0 as usize), icon)
 9035            .icon_size(IconSize::XSmall)
 9036            .size(ui::ButtonSize::None)
 9037            .when(is_rejected, |this| {
 9038                this.indicator(Indicator::icon(Icon::new(IconName::Warning)).color(Color::Warning))
 9039            })
 9040            .icon_color(color)
 9041            .style(ButtonStyle::Transparent)
 9042            .on_click(cx.listener({
 9043                move |editor, event: &ClickEvent, window, cx| {
 9044                    let edit_action = if event.modifiers().platform || breakpoint.is_disabled() {
 9045                        BreakpointEditAction::InvertState
 9046                    } else {
 9047                        BreakpointEditAction::Toggle
 9048                    };
 9049
 9050                    window.focus(&editor.focus_handle(cx), cx);
 9051                    editor.update_breakpoint_collision_on_toggle(row, &edit_action);
 9052                    editor.edit_breakpoint_at_anchor(
 9053                        position,
 9054                        breakpoint.as_ref().clone(),
 9055                        edit_action,
 9056                        cx,
 9057                    );
 9058                }
 9059            }))
 9060            .on_right_click(cx.listener(move |editor, event: &ClickEvent, window, cx| {
 9061                editor.set_breakpoint_context_menu(
 9062                    row,
 9063                    Some(position),
 9064                    event.position(),
 9065                    window,
 9066                    cx,
 9067                );
 9068            }))
 9069            .tooltip(move |_window, cx| {
 9070                Tooltip::with_meta_in(
 9071                    primary_action_text,
 9072                    Some(&ToggleBreakpoint),
 9073                    meta.clone(),
 9074                    &focus_handle,
 9075                    cx,
 9076                )
 9077            })
 9078    }
 9079
 9080    fn build_tasks_context(
 9081        project: &Entity<Project>,
 9082        buffer: &Entity<Buffer>,
 9083        buffer_row: u32,
 9084        tasks: &Arc<RunnableTasks>,
 9085        cx: &mut Context<Self>,
 9086    ) -> Task<Option<task::TaskContext>> {
 9087        let position = Point::new(buffer_row, tasks.column);
 9088        let range_start = buffer.read(cx).anchor_at(position, Bias::Right);
 9089        let location = Location {
 9090            buffer: buffer.clone(),
 9091            range: range_start..range_start,
 9092        };
 9093        // Fill in the environmental variables from the tree-sitter captures
 9094        let mut captured_task_variables = TaskVariables::default();
 9095        for (capture_name, value) in tasks.extra_variables.clone() {
 9096            captured_task_variables.insert(
 9097                task::VariableName::Custom(capture_name.into()),
 9098                value.clone(),
 9099            );
 9100        }
 9101        project.update(cx, |project, cx| {
 9102            project.task_store().update(cx, |task_store, cx| {
 9103                task_store.task_context_for_location(captured_task_variables, location, cx)
 9104            })
 9105        })
 9106    }
 9107
 9108    pub fn spawn_nearest_task(
 9109        &mut self,
 9110        action: &SpawnNearestTask,
 9111        window: &mut Window,
 9112        cx: &mut Context<Self>,
 9113    ) {
 9114        let Some((workspace, _)) = self.workspace.clone() else {
 9115            return;
 9116        };
 9117        let Some(project) = self.project.clone() else {
 9118            return;
 9119        };
 9120
 9121        // Try to find a closest, enclosing node using tree-sitter that has a task
 9122        let Some((buffer, buffer_row, tasks)) = self
 9123            .find_enclosing_node_task(cx)
 9124            // Or find the task that's closest in row-distance.
 9125            .or_else(|| self.find_closest_task(cx))
 9126        else {
 9127            return;
 9128        };
 9129
 9130        let reveal_strategy = action.reveal;
 9131        let task_context = Self::build_tasks_context(&project, &buffer, buffer_row, &tasks, cx);
 9132        cx.spawn_in(window, async move |_, cx| {
 9133            let context = task_context.await?;
 9134            let (task_source_kind, mut resolved_task) = tasks.resolve(&context).next()?;
 9135
 9136            let resolved = &mut resolved_task.resolved;
 9137            resolved.reveal = reveal_strategy;
 9138
 9139            workspace
 9140                .update_in(cx, |workspace, window, cx| {
 9141                    workspace.schedule_resolved_task(
 9142                        task_source_kind,
 9143                        resolved_task,
 9144                        false,
 9145                        window,
 9146                        cx,
 9147                    );
 9148                })
 9149                .ok()
 9150        })
 9151        .detach();
 9152    }
 9153
 9154    fn find_closest_task(
 9155        &mut self,
 9156        cx: &mut Context<Self>,
 9157    ) -> Option<(Entity<Buffer>, u32, Arc<RunnableTasks>)> {
 9158        let cursor_row = self
 9159            .selections
 9160            .newest_adjusted(&self.display_snapshot(cx))
 9161            .head()
 9162            .row;
 9163
 9164        let ((buffer_id, row), tasks) = self
 9165            .tasks
 9166            .iter()
 9167            .min_by_key(|((_, row), _)| cursor_row.abs_diff(*row))?;
 9168
 9169        let buffer = self.buffer.read(cx).buffer(*buffer_id)?;
 9170        let tasks = Arc::new(tasks.to_owned());
 9171        Some((buffer, *row, tasks))
 9172    }
 9173
 9174    fn find_enclosing_node_task(
 9175        &mut self,
 9176        cx: &mut Context<Self>,
 9177    ) -> Option<(Entity<Buffer>, u32, Arc<RunnableTasks>)> {
 9178        let snapshot = self.buffer.read(cx).snapshot(cx);
 9179        let offset = self
 9180            .selections
 9181            .newest::<MultiBufferOffset>(&self.display_snapshot(cx))
 9182            .head();
 9183        let mut excerpt = snapshot.excerpt_containing(offset..offset)?;
 9184        let offset = excerpt.map_offset_to_buffer(offset);
 9185        let buffer_id = excerpt.buffer().remote_id();
 9186
 9187        let layer = excerpt.buffer().syntax_layer_at(offset)?;
 9188        let mut cursor = layer.node().walk();
 9189
 9190        while cursor.goto_first_child_for_byte(offset.0).is_some() {
 9191            if cursor.node().end_byte() == offset.0 {
 9192                cursor.goto_next_sibling();
 9193            }
 9194        }
 9195
 9196        // Ascend to the smallest ancestor that contains the range and has a task.
 9197        loop {
 9198            let node = cursor.node();
 9199            let node_range = node.byte_range();
 9200            let symbol_start_row = excerpt.buffer().offset_to_point(node.start_byte()).row;
 9201
 9202            // Check if this node contains our offset
 9203            if node_range.start <= offset.0 && node_range.end >= offset.0 {
 9204                // If it contains offset, check for task
 9205                if let Some(tasks) = self.tasks.get(&(buffer_id, symbol_start_row)) {
 9206                    let buffer = self.buffer.read(cx).buffer(buffer_id)?;
 9207                    return Some((buffer, symbol_start_row, Arc::new(tasks.to_owned())));
 9208                }
 9209            }
 9210
 9211            if !cursor.goto_parent() {
 9212                break;
 9213            }
 9214        }
 9215        None
 9216    }
 9217
 9218    fn render_run_indicator(
 9219        &self,
 9220        _style: &EditorStyle,
 9221        is_active: bool,
 9222        row: DisplayRow,
 9223        breakpoint: Option<(Anchor, Breakpoint, Option<BreakpointSessionState>)>,
 9224        cx: &mut Context<Self>,
 9225    ) -> IconButton {
 9226        let color = Color::Muted;
 9227        let position = breakpoint.as_ref().map(|(anchor, _, _)| *anchor);
 9228
 9229        IconButton::new(
 9230            ("run_indicator", row.0 as usize),
 9231            ui::IconName::PlayOutlined,
 9232        )
 9233        .shape(ui::IconButtonShape::Square)
 9234        .icon_size(IconSize::XSmall)
 9235        .icon_color(color)
 9236        .toggle_state(is_active)
 9237        .on_click(cx.listener(move |editor, e: &ClickEvent, window, cx| {
 9238            let quick_launch = match e {
 9239                ClickEvent::Keyboard(_) => true,
 9240                ClickEvent::Mouse(e) => e.down.button == MouseButton::Left,
 9241            };
 9242
 9243            window.focus(&editor.focus_handle(cx), cx);
 9244            editor.toggle_code_actions(
 9245                &ToggleCodeActions {
 9246                    deployed_from: Some(CodeActionSource::RunMenu(row)),
 9247                    quick_launch,
 9248                },
 9249                window,
 9250                cx,
 9251            );
 9252        }))
 9253        .on_right_click(cx.listener(move |editor, event: &ClickEvent, window, cx| {
 9254            editor.set_breakpoint_context_menu(row, position, event.position(), window, cx);
 9255        }))
 9256    }
 9257
 9258    pub fn context_menu_visible(&self) -> bool {
 9259        !self.edit_prediction_preview_is_active()
 9260            && self
 9261                .context_menu
 9262                .borrow()
 9263                .as_ref()
 9264                .is_some_and(|menu| menu.visible())
 9265    }
 9266
 9267    pub fn context_menu_origin(&self) -> Option<ContextMenuOrigin> {
 9268        self.context_menu
 9269            .borrow()
 9270            .as_ref()
 9271            .map(|menu| menu.origin())
 9272    }
 9273
 9274    pub fn set_context_menu_options(&mut self, options: ContextMenuOptions) {
 9275        self.context_menu_options = Some(options);
 9276    }
 9277
 9278    const EDIT_PREDICTION_POPOVER_PADDING_X: Pixels = px(24.);
 9279    const EDIT_PREDICTION_POPOVER_PADDING_Y: Pixels = px(2.);
 9280
 9281    fn render_edit_prediction_popover(
 9282        &mut self,
 9283        text_bounds: &Bounds<Pixels>,
 9284        content_origin: gpui::Point<Pixels>,
 9285        right_margin: Pixels,
 9286        editor_snapshot: &EditorSnapshot,
 9287        visible_row_range: Range<DisplayRow>,
 9288        scroll_top: ScrollOffset,
 9289        scroll_bottom: ScrollOffset,
 9290        line_layouts: &[LineWithInvisibles],
 9291        line_height: Pixels,
 9292        scroll_position: gpui::Point<ScrollOffset>,
 9293        scroll_pixel_position: gpui::Point<ScrollPixelOffset>,
 9294        newest_selection_head: Option<DisplayPoint>,
 9295        editor_width: Pixels,
 9296        style: &EditorStyle,
 9297        window: &mut Window,
 9298        cx: &mut App,
 9299    ) -> Option<(AnyElement, gpui::Point<Pixels>)> {
 9300        if self.mode().is_minimap() {
 9301            return None;
 9302        }
 9303        let active_edit_prediction = self.active_edit_prediction.as_ref()?;
 9304
 9305        if self.edit_prediction_visible_in_cursor_popover(true) {
 9306            return None;
 9307        }
 9308
 9309        match &active_edit_prediction.completion {
 9310            EditPrediction::MoveWithin { target, .. } => {
 9311                let target_display_point = target.to_display_point(editor_snapshot);
 9312
 9313                if self.edit_prediction_requires_modifier() {
 9314                    if !self.edit_prediction_preview_is_active() {
 9315                        return None;
 9316                    }
 9317
 9318                    self.render_edit_prediction_modifier_jump_popover(
 9319                        text_bounds,
 9320                        content_origin,
 9321                        visible_row_range,
 9322                        line_layouts,
 9323                        line_height,
 9324                        scroll_pixel_position,
 9325                        newest_selection_head,
 9326                        target_display_point,
 9327                        window,
 9328                        cx,
 9329                    )
 9330                } else {
 9331                    self.render_edit_prediction_eager_jump_popover(
 9332                        text_bounds,
 9333                        content_origin,
 9334                        editor_snapshot,
 9335                        visible_row_range,
 9336                        scroll_top,
 9337                        scroll_bottom,
 9338                        line_height,
 9339                        scroll_pixel_position,
 9340                        target_display_point,
 9341                        editor_width,
 9342                        window,
 9343                        cx,
 9344                    )
 9345                }
 9346            }
 9347            EditPrediction::Edit {
 9348                display_mode: EditDisplayMode::Inline,
 9349                ..
 9350            } => None,
 9351            EditPrediction::Edit {
 9352                display_mode: EditDisplayMode::TabAccept,
 9353                edits,
 9354                ..
 9355            } => {
 9356                let range = &edits.first()?.0;
 9357                let target_display_point = range.end.to_display_point(editor_snapshot);
 9358
 9359                self.render_edit_prediction_end_of_line_popover(
 9360                    "Accept",
 9361                    editor_snapshot,
 9362                    visible_row_range,
 9363                    target_display_point,
 9364                    line_height,
 9365                    scroll_pixel_position,
 9366                    content_origin,
 9367                    editor_width,
 9368                    window,
 9369                    cx,
 9370                )
 9371            }
 9372            EditPrediction::Edit {
 9373                edits,
 9374                edit_preview,
 9375                display_mode: EditDisplayMode::DiffPopover,
 9376                snapshot,
 9377                ..
 9378            } => self.render_edit_prediction_diff_popover(
 9379                text_bounds,
 9380                content_origin,
 9381                right_margin,
 9382                editor_snapshot,
 9383                visible_row_range,
 9384                line_layouts,
 9385                line_height,
 9386                scroll_position,
 9387                scroll_pixel_position,
 9388                newest_selection_head,
 9389                editor_width,
 9390                style,
 9391                edits,
 9392                edit_preview,
 9393                snapshot,
 9394                window,
 9395                cx,
 9396            ),
 9397            EditPrediction::MoveOutside { snapshot, .. } => {
 9398                let mut element = self
 9399                    .render_edit_prediction_jump_outside_popover(snapshot, window, cx)
 9400                    .into_any();
 9401
 9402                let size = element.layout_as_root(AvailableSpace::min_size(), window, cx);
 9403                let origin_x = text_bounds.size.width - size.width - px(30.);
 9404                let origin = text_bounds.origin + gpui::Point::new(origin_x, px(16.));
 9405                element.prepaint_at(origin, window, cx);
 9406
 9407                Some((element, origin))
 9408            }
 9409        }
 9410    }
 9411
 9412    fn render_edit_prediction_modifier_jump_popover(
 9413        &mut self,
 9414        text_bounds: &Bounds<Pixels>,
 9415        content_origin: gpui::Point<Pixels>,
 9416        visible_row_range: Range<DisplayRow>,
 9417        line_layouts: &[LineWithInvisibles],
 9418        line_height: Pixels,
 9419        scroll_pixel_position: gpui::Point<ScrollPixelOffset>,
 9420        newest_selection_head: Option<DisplayPoint>,
 9421        target_display_point: DisplayPoint,
 9422        window: &mut Window,
 9423        cx: &mut App,
 9424    ) -> Option<(AnyElement, gpui::Point<Pixels>)> {
 9425        let scrolled_content_origin =
 9426            content_origin - gpui::Point::new(scroll_pixel_position.x.into(), Pixels::ZERO);
 9427
 9428        const SCROLL_PADDING_Y: Pixels = px(12.);
 9429
 9430        if target_display_point.row() < visible_row_range.start {
 9431            return self.render_edit_prediction_scroll_popover(
 9432                |_| SCROLL_PADDING_Y,
 9433                IconName::ArrowUp,
 9434                visible_row_range,
 9435                line_layouts,
 9436                newest_selection_head,
 9437                scrolled_content_origin,
 9438                window,
 9439                cx,
 9440            );
 9441        } else if target_display_point.row() >= visible_row_range.end {
 9442            return self.render_edit_prediction_scroll_popover(
 9443                |size| text_bounds.size.height - size.height - SCROLL_PADDING_Y,
 9444                IconName::ArrowDown,
 9445                visible_row_range,
 9446                line_layouts,
 9447                newest_selection_head,
 9448                scrolled_content_origin,
 9449                window,
 9450                cx,
 9451            );
 9452        }
 9453
 9454        const POLE_WIDTH: Pixels = px(2.);
 9455
 9456        let line_layout =
 9457            line_layouts.get(target_display_point.row().minus(visible_row_range.start) as usize)?;
 9458        let target_column = target_display_point.column() as usize;
 9459
 9460        let target_x = line_layout.x_for_index(target_column);
 9461        let target_y = (target_display_point.row().as_f64() * f64::from(line_height))
 9462            - scroll_pixel_position.y;
 9463
 9464        let flag_on_right = target_x < text_bounds.size.width / 2.;
 9465
 9466        let mut border_color = Self::edit_prediction_callout_popover_border_color(cx);
 9467        border_color.l += 0.001;
 9468
 9469        let mut element = v_flex()
 9470            .items_end()
 9471            .when(flag_on_right, |el| el.items_start())
 9472            .child(if flag_on_right {
 9473                self.render_edit_prediction_line_popover("Jump", None, window, cx)
 9474                    .rounded_bl(px(0.))
 9475                    .rounded_tl(px(0.))
 9476                    .border_l_2()
 9477                    .border_color(border_color)
 9478            } else {
 9479                self.render_edit_prediction_line_popover("Jump", None, window, cx)
 9480                    .rounded_br(px(0.))
 9481                    .rounded_tr(px(0.))
 9482                    .border_r_2()
 9483                    .border_color(border_color)
 9484            })
 9485            .child(div().w(POLE_WIDTH).bg(border_color).h(line_height))
 9486            .into_any();
 9487
 9488        let size = element.layout_as_root(AvailableSpace::min_size(), window, cx);
 9489
 9490        let mut origin = scrolled_content_origin + point(target_x, target_y.into())
 9491            - point(
 9492                if flag_on_right {
 9493                    POLE_WIDTH
 9494                } else {
 9495                    size.width - POLE_WIDTH
 9496                },
 9497                size.height - line_height,
 9498            );
 9499
 9500        origin.x = origin.x.max(content_origin.x);
 9501
 9502        element.prepaint_at(origin, window, cx);
 9503
 9504        Some((element, origin))
 9505    }
 9506
 9507    fn render_edit_prediction_scroll_popover(
 9508        &mut self,
 9509        to_y: impl Fn(Size<Pixels>) -> Pixels,
 9510        scroll_icon: IconName,
 9511        visible_row_range: Range<DisplayRow>,
 9512        line_layouts: &[LineWithInvisibles],
 9513        newest_selection_head: Option<DisplayPoint>,
 9514        scrolled_content_origin: gpui::Point<Pixels>,
 9515        window: &mut Window,
 9516        cx: &mut App,
 9517    ) -> Option<(AnyElement, gpui::Point<Pixels>)> {
 9518        let mut element = self
 9519            .render_edit_prediction_line_popover("Scroll", Some(scroll_icon), window, cx)
 9520            .into_any();
 9521
 9522        let size = element.layout_as_root(AvailableSpace::min_size(), window, cx);
 9523
 9524        let cursor = newest_selection_head?;
 9525        let cursor_row_layout =
 9526            line_layouts.get(cursor.row().minus(visible_row_range.start) as usize)?;
 9527        let cursor_column = cursor.column() as usize;
 9528
 9529        let cursor_character_x = cursor_row_layout.x_for_index(cursor_column);
 9530
 9531        let origin = scrolled_content_origin + point(cursor_character_x, to_y(size));
 9532
 9533        element.prepaint_at(origin, window, cx);
 9534        Some((element, origin))
 9535    }
 9536
 9537    fn render_edit_prediction_eager_jump_popover(
 9538        &mut self,
 9539        text_bounds: &Bounds<Pixels>,
 9540        content_origin: gpui::Point<Pixels>,
 9541        editor_snapshot: &EditorSnapshot,
 9542        visible_row_range: Range<DisplayRow>,
 9543        scroll_top: ScrollOffset,
 9544        scroll_bottom: ScrollOffset,
 9545        line_height: Pixels,
 9546        scroll_pixel_position: gpui::Point<ScrollPixelOffset>,
 9547        target_display_point: DisplayPoint,
 9548        editor_width: Pixels,
 9549        window: &mut Window,
 9550        cx: &mut App,
 9551    ) -> Option<(AnyElement, gpui::Point<Pixels>)> {
 9552        if target_display_point.row().as_f64() < scroll_top {
 9553            let mut element = self
 9554                .render_edit_prediction_line_popover(
 9555                    "Jump to Edit",
 9556                    Some(IconName::ArrowUp),
 9557                    window,
 9558                    cx,
 9559                )
 9560                .into_any();
 9561
 9562            let size = element.layout_as_root(AvailableSpace::min_size(), window, cx);
 9563            let offset = point(
 9564                (text_bounds.size.width - size.width) / 2.,
 9565                Self::EDIT_PREDICTION_POPOVER_PADDING_Y,
 9566            );
 9567
 9568            let origin = text_bounds.origin + offset;
 9569            element.prepaint_at(origin, window, cx);
 9570            Some((element, origin))
 9571        } else if (target_display_point.row().as_f64() + 1.) > scroll_bottom {
 9572            let mut element = self
 9573                .render_edit_prediction_line_popover(
 9574                    "Jump to Edit",
 9575                    Some(IconName::ArrowDown),
 9576                    window,
 9577                    cx,
 9578                )
 9579                .into_any();
 9580
 9581            let size = element.layout_as_root(AvailableSpace::min_size(), window, cx);
 9582            let offset = point(
 9583                (text_bounds.size.width - size.width) / 2.,
 9584                text_bounds.size.height - size.height - Self::EDIT_PREDICTION_POPOVER_PADDING_Y,
 9585            );
 9586
 9587            let origin = text_bounds.origin + offset;
 9588            element.prepaint_at(origin, window, cx);
 9589            Some((element, origin))
 9590        } else {
 9591            self.render_edit_prediction_end_of_line_popover(
 9592                "Jump to Edit",
 9593                editor_snapshot,
 9594                visible_row_range,
 9595                target_display_point,
 9596                line_height,
 9597                scroll_pixel_position,
 9598                content_origin,
 9599                editor_width,
 9600                window,
 9601                cx,
 9602            )
 9603        }
 9604    }
 9605
 9606    fn render_edit_prediction_end_of_line_popover(
 9607        self: &mut Editor,
 9608        label: &'static str,
 9609        editor_snapshot: &EditorSnapshot,
 9610        visible_row_range: Range<DisplayRow>,
 9611        target_display_point: DisplayPoint,
 9612        line_height: Pixels,
 9613        scroll_pixel_position: gpui::Point<ScrollPixelOffset>,
 9614        content_origin: gpui::Point<Pixels>,
 9615        editor_width: Pixels,
 9616        window: &mut Window,
 9617        cx: &mut App,
 9618    ) -> Option<(AnyElement, gpui::Point<Pixels>)> {
 9619        let target_line_end = DisplayPoint::new(
 9620            target_display_point.row(),
 9621            editor_snapshot.line_len(target_display_point.row()),
 9622        );
 9623
 9624        let mut element = self
 9625            .render_edit_prediction_line_popover(label, None, window, cx)
 9626            .into_any();
 9627
 9628        let size = element.layout_as_root(AvailableSpace::min_size(), window, cx);
 9629
 9630        let line_origin =
 9631            self.display_to_pixel_point(target_line_end, editor_snapshot, window, cx)?;
 9632
 9633        let start_point = content_origin - point(scroll_pixel_position.x.into(), Pixels::ZERO);
 9634        let mut origin = start_point
 9635            + line_origin
 9636            + point(Self::EDIT_PREDICTION_POPOVER_PADDING_X, Pixels::ZERO);
 9637        origin.x = origin.x.max(content_origin.x);
 9638
 9639        let max_x = content_origin.x + editor_width - size.width;
 9640
 9641        if origin.x > max_x {
 9642            let offset = line_height + Self::EDIT_PREDICTION_POPOVER_PADDING_Y;
 9643
 9644            let icon = if visible_row_range.contains(&(target_display_point.row() + 2)) {
 9645                origin.y += offset;
 9646                IconName::ArrowUp
 9647            } else {
 9648                origin.y -= offset;
 9649                IconName::ArrowDown
 9650            };
 9651
 9652            element = self
 9653                .render_edit_prediction_line_popover(label, Some(icon), window, cx)
 9654                .into_any();
 9655
 9656            let size = element.layout_as_root(AvailableSpace::min_size(), window, cx);
 9657
 9658            origin.x = content_origin.x + editor_width - size.width - px(2.);
 9659        }
 9660
 9661        element.prepaint_at(origin, window, cx);
 9662        Some((element, origin))
 9663    }
 9664
 9665    fn render_edit_prediction_diff_popover(
 9666        self: &Editor,
 9667        text_bounds: &Bounds<Pixels>,
 9668        content_origin: gpui::Point<Pixels>,
 9669        right_margin: Pixels,
 9670        editor_snapshot: &EditorSnapshot,
 9671        visible_row_range: Range<DisplayRow>,
 9672        line_layouts: &[LineWithInvisibles],
 9673        line_height: Pixels,
 9674        scroll_position: gpui::Point<ScrollOffset>,
 9675        scroll_pixel_position: gpui::Point<ScrollPixelOffset>,
 9676        newest_selection_head: Option<DisplayPoint>,
 9677        editor_width: Pixels,
 9678        style: &EditorStyle,
 9679        edits: &Vec<(Range<Anchor>, Arc<str>)>,
 9680        edit_preview: &Option<language::EditPreview>,
 9681        snapshot: &language::BufferSnapshot,
 9682        window: &mut Window,
 9683        cx: &mut App,
 9684    ) -> Option<(AnyElement, gpui::Point<Pixels>)> {
 9685        let edit_start = edits
 9686            .first()
 9687            .unwrap()
 9688            .0
 9689            .start
 9690            .to_display_point(editor_snapshot);
 9691        let edit_end = edits
 9692            .last()
 9693            .unwrap()
 9694            .0
 9695            .end
 9696            .to_display_point(editor_snapshot);
 9697
 9698        let is_visible = visible_row_range.contains(&edit_start.row())
 9699            || visible_row_range.contains(&edit_end.row());
 9700        if !is_visible {
 9701            return None;
 9702        }
 9703
 9704        let highlighted_edits = if let Some(edit_preview) = edit_preview.as_ref() {
 9705            crate::edit_prediction_edit_text(snapshot, edits, edit_preview, false, cx)
 9706        } else {
 9707            // Fallback for providers without edit_preview
 9708            crate::edit_prediction_fallback_text(edits, cx)
 9709        };
 9710
 9711        let styled_text = highlighted_edits.to_styled_text(&style.text);
 9712        let line_count = highlighted_edits.text.lines().count();
 9713
 9714        const BORDER_WIDTH: Pixels = px(1.);
 9715
 9716        let keybind = self.render_edit_prediction_accept_keybind(window, cx);
 9717        let has_keybind = keybind.is_some();
 9718
 9719        let mut element = h_flex()
 9720            .items_start()
 9721            .child(
 9722                h_flex()
 9723                    .bg(cx.theme().colors().editor_background)
 9724                    .border(BORDER_WIDTH)
 9725                    .shadow_xs()
 9726                    .border_color(cx.theme().colors().border)
 9727                    .rounded_l_lg()
 9728                    .when(line_count > 1, |el| el.rounded_br_lg())
 9729                    .pr_1()
 9730                    .child(styled_text),
 9731            )
 9732            .child(
 9733                h_flex()
 9734                    .h(line_height + BORDER_WIDTH * 2.)
 9735                    .px_1p5()
 9736                    .gap_1()
 9737                    // Workaround: For some reason, there's a gap if we don't do this
 9738                    .ml(-BORDER_WIDTH)
 9739                    .shadow(vec![gpui::BoxShadow {
 9740                        color: gpui::black().opacity(0.05),
 9741                        offset: point(px(1.), px(1.)),
 9742                        blur_radius: px(2.),
 9743                        spread_radius: px(0.),
 9744                    }])
 9745                    .bg(Editor::edit_prediction_line_popover_bg_color(cx))
 9746                    .border(BORDER_WIDTH)
 9747                    .border_color(cx.theme().colors().border)
 9748                    .rounded_r_lg()
 9749                    .id("edit_prediction_diff_popover_keybind")
 9750                    .when(!has_keybind, |el| {
 9751                        let status_colors = cx.theme().status();
 9752
 9753                        el.bg(status_colors.error_background)
 9754                            .border_color(status_colors.error.opacity(0.6))
 9755                            .child(Icon::new(IconName::Info).color(Color::Error))
 9756                            .cursor_default()
 9757                            .hoverable_tooltip(move |_window, cx| {
 9758                                cx.new(|_| MissingEditPredictionKeybindingTooltip).into()
 9759                            })
 9760                    })
 9761                    .children(keybind),
 9762            )
 9763            .into_any();
 9764
 9765        let longest_row =
 9766            editor_snapshot.longest_row_in_range(edit_start.row()..edit_end.row() + 1);
 9767        let longest_line_width = if visible_row_range.contains(&longest_row) {
 9768            line_layouts[(longest_row.0 - visible_row_range.start.0) as usize].width
 9769        } else {
 9770            layout_line(
 9771                longest_row,
 9772                editor_snapshot,
 9773                style,
 9774                editor_width,
 9775                |_| false,
 9776                window,
 9777                cx,
 9778            )
 9779            .width
 9780        };
 9781
 9782        let viewport_bounds =
 9783            Bounds::new(Default::default(), window.viewport_size()).extend(Edges {
 9784                right: -right_margin,
 9785                ..Default::default()
 9786            });
 9787
 9788        let x_after_longest = Pixels::from(
 9789            ScrollPixelOffset::from(
 9790                text_bounds.origin.x + longest_line_width + Self::EDIT_PREDICTION_POPOVER_PADDING_X,
 9791            ) - scroll_pixel_position.x,
 9792        );
 9793
 9794        let element_bounds = element.layout_as_root(AvailableSpace::min_size(), window, cx);
 9795
 9796        // Fully visible if it can be displayed within the window (allow overlapping other
 9797        // panes). However, this is only allowed if the popover starts within text_bounds.
 9798        let can_position_to_the_right = x_after_longest < text_bounds.right()
 9799            && x_after_longest + element_bounds.width < viewport_bounds.right();
 9800
 9801        let mut origin = if can_position_to_the_right {
 9802            point(
 9803                x_after_longest,
 9804                text_bounds.origin.y
 9805                    + Pixels::from(
 9806                        edit_start.row().as_f64() * ScrollPixelOffset::from(line_height)
 9807                            - scroll_pixel_position.y,
 9808                    ),
 9809            )
 9810        } else {
 9811            let cursor_row = newest_selection_head.map(|head| head.row());
 9812            let above_edit = edit_start
 9813                .row()
 9814                .0
 9815                .checked_sub(line_count as u32)
 9816                .map(DisplayRow);
 9817            let below_edit = Some(edit_end.row() + 1);
 9818            let above_cursor =
 9819                cursor_row.and_then(|row| row.0.checked_sub(line_count as u32).map(DisplayRow));
 9820            let below_cursor = cursor_row.map(|cursor_row| cursor_row + 1);
 9821
 9822            // Place the edit popover adjacent to the edit if there is a location
 9823            // available that is onscreen and does not obscure the cursor. Otherwise,
 9824            // place it adjacent to the cursor.
 9825            let row_target = [above_edit, below_edit, above_cursor, below_cursor]
 9826                .into_iter()
 9827                .flatten()
 9828                .find(|&start_row| {
 9829                    let end_row = start_row + line_count as u32;
 9830                    visible_row_range.contains(&start_row)
 9831                        && visible_row_range.contains(&end_row)
 9832                        && cursor_row
 9833                            .is_none_or(|cursor_row| !((start_row..end_row).contains(&cursor_row)))
 9834                })?;
 9835
 9836            content_origin
 9837                + point(
 9838                    Pixels::from(-scroll_pixel_position.x),
 9839                    Pixels::from(
 9840                        (row_target.as_f64() - scroll_position.y) * f64::from(line_height),
 9841                    ),
 9842                )
 9843        };
 9844
 9845        origin.x -= BORDER_WIDTH;
 9846
 9847        window.defer_draw(element, origin, 1);
 9848
 9849        // Do not return an element, since it will already be drawn due to defer_draw.
 9850        None
 9851    }
 9852
 9853    fn edit_prediction_cursor_popover_height(&self) -> Pixels {
 9854        px(30.)
 9855    }
 9856
 9857    fn current_user_player_color(&self, cx: &mut App) -> PlayerColor {
 9858        if self.read_only(cx) {
 9859            cx.theme().players().read_only()
 9860        } else {
 9861            self.style.as_ref().unwrap().local_player
 9862        }
 9863    }
 9864
 9865    fn render_edit_prediction_accept_keybind(
 9866        &self,
 9867        window: &mut Window,
 9868        cx: &mut App,
 9869    ) -> Option<AnyElement> {
 9870        let accept_binding =
 9871            self.accept_edit_prediction_keybind(EditPredictionGranularity::Full, window, cx);
 9872        let accept_keystroke = accept_binding.keystroke()?;
 9873
 9874        let is_platform_style_mac = PlatformStyle::platform() == PlatformStyle::Mac;
 9875
 9876        let modifiers_color = if *accept_keystroke.modifiers() == window.modifiers() {
 9877            Color::Accent
 9878        } else {
 9879            Color::Muted
 9880        };
 9881
 9882        h_flex()
 9883            .px_0p5()
 9884            .when(is_platform_style_mac, |parent| parent.gap_0p5())
 9885            .font(theme::ThemeSettings::get_global(cx).buffer_font.clone())
 9886            .text_size(TextSize::XSmall.rems(cx))
 9887            .child(h_flex().children(ui::render_modifiers(
 9888                accept_keystroke.modifiers(),
 9889                PlatformStyle::platform(),
 9890                Some(modifiers_color),
 9891                Some(IconSize::XSmall.rems().into()),
 9892                true,
 9893            )))
 9894            .when(is_platform_style_mac, |parent| {
 9895                parent.child(accept_keystroke.key().to_string())
 9896            })
 9897            .when(!is_platform_style_mac, |parent| {
 9898                parent.child(
 9899                    Key::new(
 9900                        util::capitalize(accept_keystroke.key()),
 9901                        Some(Color::Default),
 9902                    )
 9903                    .size(Some(IconSize::XSmall.rems().into())),
 9904                )
 9905            })
 9906            .into_any()
 9907            .into()
 9908    }
 9909
 9910    fn render_edit_prediction_line_popover(
 9911        &self,
 9912        label: impl Into<SharedString>,
 9913        icon: Option<IconName>,
 9914        window: &mut Window,
 9915        cx: &mut App,
 9916    ) -> Stateful<Div> {
 9917        let padding_right = if icon.is_some() { px(4.) } else { px(8.) };
 9918
 9919        let keybind = self.render_edit_prediction_accept_keybind(window, cx);
 9920        let has_keybind = keybind.is_some();
 9921        let icons = Self::get_prediction_provider_icons(&self.edit_prediction_provider, cx);
 9922
 9923        h_flex()
 9924            .id("ep-line-popover")
 9925            .py_0p5()
 9926            .pl_1()
 9927            .pr(padding_right)
 9928            .gap_1()
 9929            .rounded_md()
 9930            .border_1()
 9931            .bg(Self::edit_prediction_line_popover_bg_color(cx))
 9932            .border_color(Self::edit_prediction_callout_popover_border_color(cx))
 9933            .shadow_xs()
 9934            .when(!has_keybind, |el| {
 9935                let status_colors = cx.theme().status();
 9936
 9937                el.bg(status_colors.error_background)
 9938                    .border_color(status_colors.error.opacity(0.6))
 9939                    .pl_2()
 9940                    .child(Icon::new(icons.error).color(Color::Error))
 9941                    .cursor_default()
 9942                    .hoverable_tooltip(move |_window, cx| {
 9943                        cx.new(|_| MissingEditPredictionKeybindingTooltip).into()
 9944                    })
 9945            })
 9946            .children(keybind)
 9947            .child(
 9948                Label::new(label)
 9949                    .size(LabelSize::Small)
 9950                    .when(!has_keybind, |el| {
 9951                        el.color(cx.theme().status().error.into()).strikethrough()
 9952                    }),
 9953            )
 9954            .when(!has_keybind, |el| {
 9955                el.child(
 9956                    h_flex().ml_1().child(
 9957                        Icon::new(IconName::Info)
 9958                            .size(IconSize::Small)
 9959                            .color(cx.theme().status().error.into()),
 9960                    ),
 9961                )
 9962            })
 9963            .when_some(icon, |element, icon| {
 9964                element.child(
 9965                    div()
 9966                        .mt(px(1.5))
 9967                        .child(Icon::new(icon).size(IconSize::Small)),
 9968                )
 9969            })
 9970    }
 9971
 9972    fn render_edit_prediction_jump_outside_popover(
 9973        &self,
 9974        snapshot: &BufferSnapshot,
 9975        window: &mut Window,
 9976        cx: &mut App,
 9977    ) -> Stateful<Div> {
 9978        let keybind = self.render_edit_prediction_accept_keybind(window, cx);
 9979        let has_keybind = keybind.is_some();
 9980        let icons = Self::get_prediction_provider_icons(&self.edit_prediction_provider, cx);
 9981
 9982        let file_name = snapshot
 9983            .file()
 9984            .map(|file| SharedString::new(file.file_name(cx)))
 9985            .unwrap_or(SharedString::new_static("untitled"));
 9986
 9987        h_flex()
 9988            .id("ep-jump-outside-popover")
 9989            .py_1()
 9990            .px_2()
 9991            .gap_1()
 9992            .rounded_md()
 9993            .border_1()
 9994            .bg(Self::edit_prediction_line_popover_bg_color(cx))
 9995            .border_color(Self::edit_prediction_callout_popover_border_color(cx))
 9996            .shadow_xs()
 9997            .when(!has_keybind, |el| {
 9998                let status_colors = cx.theme().status();
 9999
10000                el.bg(status_colors.error_background)
10001                    .border_color(status_colors.error.opacity(0.6))
10002                    .pl_2()
10003                    .child(Icon::new(icons.error).color(Color::Error))
10004                    .cursor_default()
10005                    .hoverable_tooltip(move |_window, cx| {
10006                        cx.new(|_| MissingEditPredictionKeybindingTooltip).into()
10007                    })
10008            })
10009            .children(keybind)
10010            .child(
10011                Label::new(file_name)
10012                    .size(LabelSize::Small)
10013                    .buffer_font(cx)
10014                    .when(!has_keybind, |el| {
10015                        el.color(cx.theme().status().error.into()).strikethrough()
10016                    }),
10017            )
10018            .when(!has_keybind, |el| {
10019                el.child(
10020                    h_flex().ml_1().child(
10021                        Icon::new(IconName::Info)
10022                            .size(IconSize::Small)
10023                            .color(cx.theme().status().error.into()),
10024                    ),
10025                )
10026            })
10027            .child(
10028                div()
10029                    .mt(px(1.5))
10030                    .child(Icon::new(IconName::ArrowUpRight).size(IconSize::Small)),
10031            )
10032    }
10033
10034    fn edit_prediction_line_popover_bg_color(cx: &App) -> Hsla {
10035        let accent_color = cx.theme().colors().text_accent;
10036        let editor_bg_color = cx.theme().colors().editor_background;
10037        editor_bg_color.blend(accent_color.opacity(0.1))
10038    }
10039
10040    fn edit_prediction_callout_popover_border_color(cx: &App) -> Hsla {
10041        let accent_color = cx.theme().colors().text_accent;
10042        let editor_bg_color = cx.theme().colors().editor_background;
10043        editor_bg_color.blend(accent_color.opacity(0.6))
10044    }
10045    fn get_prediction_provider_icons(
10046        provider: &Option<RegisteredEditPredictionDelegate>,
10047        cx: &App,
10048    ) -> edit_prediction_types::EditPredictionIconSet {
10049        match provider {
10050            Some(provider) => provider.provider.icons(cx),
10051            None => edit_prediction_types::EditPredictionIconSet::new(IconName::ZedPredict),
10052        }
10053    }
10054
10055    fn render_edit_prediction_cursor_popover(
10056        &self,
10057        min_width: Pixels,
10058        max_width: Pixels,
10059        cursor_point: Point,
10060        style: &EditorStyle,
10061        accept_keystroke: Option<&gpui::KeybindingKeystroke>,
10062        _window: &Window,
10063        cx: &mut Context<Editor>,
10064    ) -> Option<AnyElement> {
10065        let provider = self.edit_prediction_provider.as_ref()?;
10066        let icons = Self::get_prediction_provider_icons(&self.edit_prediction_provider, cx);
10067
10068        let is_refreshing = provider.provider.is_refreshing(cx);
10069
10070        fn pending_completion_container(icon: IconName) -> Div {
10071            h_flex().h_full().flex_1().gap_2().child(Icon::new(icon))
10072        }
10073
10074        let completion = match &self.active_edit_prediction {
10075            Some(prediction) => {
10076                if !self.has_visible_completions_menu() {
10077                    const RADIUS: Pixels = px(6.);
10078                    const BORDER_WIDTH: Pixels = px(1.);
10079
10080                    return Some(
10081                        h_flex()
10082                            .elevation_2(cx)
10083                            .border(BORDER_WIDTH)
10084                            .border_color(cx.theme().colors().border)
10085                            .when(accept_keystroke.is_none(), |el| {
10086                                el.border_color(cx.theme().status().error)
10087                            })
10088                            .rounded(RADIUS)
10089                            .rounded_tl(px(0.))
10090                            .overflow_hidden()
10091                            .child(div().px_1p5().child(match &prediction.completion {
10092                                EditPrediction::MoveWithin { target, snapshot } => {
10093                                    use text::ToPoint as _;
10094                                    if target.text_anchor.to_point(snapshot).row > cursor_point.row
10095                                    {
10096                                        Icon::new(icons.down)
10097                                    } else {
10098                                        Icon::new(icons.up)
10099                                    }
10100                                }
10101                                EditPrediction::MoveOutside { .. } => {
10102                                    // TODO [zeta2] custom icon for external jump?
10103                                    Icon::new(icons.base)
10104                                }
10105                                EditPrediction::Edit { .. } => Icon::new(icons.base),
10106                            }))
10107                            .child(
10108                                h_flex()
10109                                    .gap_1()
10110                                    .py_1()
10111                                    .px_2()
10112                                    .rounded_r(RADIUS - BORDER_WIDTH)
10113                                    .border_l_1()
10114                                    .border_color(cx.theme().colors().border)
10115                                    .bg(Self::edit_prediction_line_popover_bg_color(cx))
10116                                    .when(self.edit_prediction_preview.released_too_fast(), |el| {
10117                                        el.child(
10118                                            Label::new("Hold")
10119                                                .size(LabelSize::Small)
10120                                                .when(accept_keystroke.is_none(), |el| {
10121                                                    el.strikethrough()
10122                                                })
10123                                                .line_height_style(LineHeightStyle::UiLabel),
10124                                        )
10125                                    })
10126                                    .id("edit_prediction_cursor_popover_keybind")
10127                                    .when(accept_keystroke.is_none(), |el| {
10128                                        let status_colors = cx.theme().status();
10129
10130                                        el.bg(status_colors.error_background)
10131                                            .border_color(status_colors.error.opacity(0.6))
10132                                            .child(Icon::new(IconName::Info).color(Color::Error))
10133                                            .cursor_default()
10134                                            .hoverable_tooltip(move |_window, cx| {
10135                                                cx.new(|_| MissingEditPredictionKeybindingTooltip)
10136                                                    .into()
10137                                            })
10138                                    })
10139                                    .when_some(
10140                                        accept_keystroke.as_ref(),
10141                                        |el, accept_keystroke| {
10142                                            el.child(h_flex().children(ui::render_modifiers(
10143                                                accept_keystroke.modifiers(),
10144                                                PlatformStyle::platform(),
10145                                                Some(Color::Default),
10146                                                Some(IconSize::XSmall.rems().into()),
10147                                                false,
10148                                            )))
10149                                        },
10150                                    ),
10151                            )
10152                            .into_any(),
10153                    );
10154                }
10155
10156                self.render_edit_prediction_cursor_popover_preview(
10157                    prediction,
10158                    cursor_point,
10159                    style,
10160                    cx,
10161                )?
10162            }
10163
10164            None if is_refreshing => match &self.stale_edit_prediction_in_menu {
10165                Some(stale_completion) => self.render_edit_prediction_cursor_popover_preview(
10166                    stale_completion,
10167                    cursor_point,
10168                    style,
10169                    cx,
10170                )?,
10171
10172                None => pending_completion_container(icons.base)
10173                    .child(Label::new("...").size(LabelSize::Small)),
10174            },
10175
10176            None => pending_completion_container(icons.base)
10177                .child(Label::new("...").size(LabelSize::Small)),
10178        };
10179
10180        let completion = if is_refreshing || self.active_edit_prediction.is_none() {
10181            completion
10182                .with_animation(
10183                    "loading-completion",
10184                    Animation::new(Duration::from_secs(2))
10185                        .repeat()
10186                        .with_easing(pulsating_between(0.4, 0.8)),
10187                    |label, delta| label.opacity(delta),
10188                )
10189                .into_any_element()
10190        } else {
10191            completion.into_any_element()
10192        };
10193
10194        let has_completion = self.active_edit_prediction.is_some();
10195
10196        let is_platform_style_mac = PlatformStyle::platform() == PlatformStyle::Mac;
10197        Some(
10198            h_flex()
10199                .min_w(min_width)
10200                .max_w(max_width)
10201                .flex_1()
10202                .elevation_2(cx)
10203                .border_color(cx.theme().colors().border)
10204                .child(
10205                    div()
10206                        .flex_1()
10207                        .py_1()
10208                        .px_2()
10209                        .overflow_hidden()
10210                        .child(completion),
10211                )
10212                .when_some(accept_keystroke, |el, accept_keystroke| {
10213                    if !accept_keystroke.modifiers().modified() {
10214                        return el;
10215                    }
10216
10217                    el.child(
10218                        h_flex()
10219                            .h_full()
10220                            .border_l_1()
10221                            .rounded_r_lg()
10222                            .border_color(cx.theme().colors().border)
10223                            .bg(Self::edit_prediction_line_popover_bg_color(cx))
10224                            .gap_1()
10225                            .py_1()
10226                            .px_2()
10227                            .child(
10228                                h_flex()
10229                                    .font(theme::ThemeSettings::get_global(cx).buffer_font.clone())
10230                                    .when(is_platform_style_mac, |parent| parent.gap_1())
10231                                    .child(h_flex().children(ui::render_modifiers(
10232                                        accept_keystroke.modifiers(),
10233                                        PlatformStyle::platform(),
10234                                        Some(if !has_completion {
10235                                            Color::Muted
10236                                        } else {
10237                                            Color::Default
10238                                        }),
10239                                        None,
10240                                        false,
10241                                    ))),
10242                            )
10243                            .child(Label::new("Preview").into_any_element())
10244                            .opacity(if has_completion { 1.0 } else { 0.4 }),
10245                    )
10246                })
10247                .into_any(),
10248        )
10249    }
10250
10251    fn render_edit_prediction_cursor_popover_preview(
10252        &self,
10253        completion: &EditPredictionState,
10254        cursor_point: Point,
10255        style: &EditorStyle,
10256        cx: &mut Context<Editor>,
10257    ) -> Option<Div> {
10258        use text::ToPoint as _;
10259
10260        fn render_relative_row_jump(
10261            prefix: impl Into<String>,
10262            current_row: u32,
10263            target_row: u32,
10264        ) -> Div {
10265            let (row_diff, arrow) = if target_row < current_row {
10266                (current_row - target_row, IconName::ArrowUp)
10267            } else {
10268                (target_row - current_row, IconName::ArrowDown)
10269            };
10270
10271            h_flex()
10272                .child(
10273                    Label::new(format!("{}{}", prefix.into(), row_diff))
10274                        .color(Color::Muted)
10275                        .size(LabelSize::Small),
10276                )
10277                .child(Icon::new(arrow).color(Color::Muted).size(IconSize::Small))
10278        }
10279
10280        let supports_jump = self
10281            .edit_prediction_provider
10282            .as_ref()
10283            .map(|provider| provider.provider.supports_jump_to_edit())
10284            .unwrap_or(true);
10285
10286        let icons = Self::get_prediction_provider_icons(&self.edit_prediction_provider, cx);
10287
10288        match &completion.completion {
10289            EditPrediction::MoveWithin {
10290                target, snapshot, ..
10291            } => {
10292                if !supports_jump {
10293                    return None;
10294                }
10295
10296                Some(
10297                    h_flex()
10298                        .px_2()
10299                        .gap_2()
10300                        .flex_1()
10301                        .child(
10302                            if target.text_anchor.to_point(snapshot).row > cursor_point.row {
10303                                Icon::new(icons.down)
10304                            } else {
10305                                Icon::new(icons.up)
10306                            },
10307                        )
10308                        .child(Label::new("Jump to Edit")),
10309                )
10310            }
10311            EditPrediction::MoveOutside { snapshot, .. } => {
10312                let file_name = snapshot
10313                    .file()
10314                    .map(|file| file.file_name(cx))
10315                    .unwrap_or("untitled");
10316                Some(
10317                    h_flex()
10318                        .px_2()
10319                        .gap_2()
10320                        .flex_1()
10321                        .child(Icon::new(icons.base))
10322                        .child(Label::new(format!("Jump to {file_name}"))),
10323                )
10324            }
10325            EditPrediction::Edit {
10326                edits,
10327                edit_preview,
10328                snapshot,
10329                ..
10330            } => {
10331                let first_edit_row = edits.first()?.0.start.text_anchor.to_point(snapshot).row;
10332
10333                let (highlighted_edits, has_more_lines) =
10334                    if let Some(edit_preview) = edit_preview.as_ref() {
10335                        crate::edit_prediction_edit_text(snapshot, edits, edit_preview, true, cx)
10336                            .first_line_preview()
10337                    } else {
10338                        crate::edit_prediction_fallback_text(edits, cx).first_line_preview()
10339                    };
10340
10341                let styled_text = gpui::StyledText::new(highlighted_edits.text)
10342                    .with_default_highlights(&style.text, highlighted_edits.highlights);
10343
10344                let preview = h_flex()
10345                    .gap_1()
10346                    .min_w_16()
10347                    .child(styled_text)
10348                    .when(has_more_lines, |parent| parent.child(""));
10349
10350                let left = if supports_jump && first_edit_row != cursor_point.row {
10351                    render_relative_row_jump("", cursor_point.row, first_edit_row)
10352                        .into_any_element()
10353                } else {
10354                    Icon::new(icons.base).into_any_element()
10355                };
10356
10357                Some(
10358                    h_flex()
10359                        .h_full()
10360                        .flex_1()
10361                        .gap_2()
10362                        .pr_1()
10363                        .overflow_x_hidden()
10364                        .font(theme::ThemeSettings::get_global(cx).buffer_font.clone())
10365                        .child(left)
10366                        .child(preview),
10367                )
10368            }
10369        }
10370    }
10371
10372    pub fn render_context_menu(
10373        &mut self,
10374        max_height_in_lines: u32,
10375        window: &mut Window,
10376        cx: &mut Context<Editor>,
10377    ) -> Option<AnyElement> {
10378        let menu = self.context_menu.borrow();
10379        let menu = menu.as_ref()?;
10380        if !menu.visible() {
10381            return None;
10382        };
10383        self.style
10384            .as_ref()
10385            .map(|style| menu.render(style, max_height_in_lines, window, cx))
10386    }
10387
10388    fn render_context_menu_aside(
10389        &mut self,
10390        max_size: Size<Pixels>,
10391        window: &mut Window,
10392        cx: &mut Context<Editor>,
10393    ) -> Option<AnyElement> {
10394        self.context_menu.borrow_mut().as_mut().and_then(|menu| {
10395            if menu.visible() {
10396                menu.render_aside(max_size, window, cx)
10397            } else {
10398                None
10399            }
10400        })
10401    }
10402
10403    fn hide_context_menu(
10404        &mut self,
10405        window: &mut Window,
10406        cx: &mut Context<Self>,
10407    ) -> Option<CodeContextMenu> {
10408        cx.notify();
10409        self.completion_tasks.clear();
10410        let context_menu = self.context_menu.borrow_mut().take();
10411        self.stale_edit_prediction_in_menu.take();
10412        self.update_visible_edit_prediction(window, cx);
10413        if let Some(CodeContextMenu::Completions(_)) = &context_menu
10414            && let Some(completion_provider) = &self.completion_provider
10415        {
10416            completion_provider.selection_changed(None, window, cx);
10417        }
10418        context_menu
10419    }
10420
10421    fn show_snippet_choices(
10422        &mut self,
10423        choices: &Vec<String>,
10424        selection: Range<Anchor>,
10425        cx: &mut Context<Self>,
10426    ) {
10427        let Some((_, buffer, _)) = self
10428            .buffer()
10429            .read(cx)
10430            .excerpt_containing(selection.start, cx)
10431        else {
10432            return;
10433        };
10434        let Some((_, end_buffer, _)) = self.buffer().read(cx).excerpt_containing(selection.end, cx)
10435        else {
10436            return;
10437        };
10438        if buffer != end_buffer {
10439            log::error!("expected anchor range to have matching buffer IDs");
10440            return;
10441        }
10442
10443        let id = post_inc(&mut self.next_completion_id);
10444        let snippet_sort_order = EditorSettings::get_global(cx).snippet_sort_order;
10445        let mut context_menu = self.context_menu.borrow_mut();
10446        let old_menu = context_menu.take();
10447        *context_menu = Some(CodeContextMenu::Completions(
10448            CompletionsMenu::new_snippet_choices(
10449                id,
10450                true,
10451                choices,
10452                selection,
10453                buffer,
10454                old_menu.map(|menu| menu.primary_scroll_handle()),
10455                snippet_sort_order,
10456            ),
10457        ));
10458    }
10459
10460    pub fn insert_snippet(
10461        &mut self,
10462        insertion_ranges: &[Range<MultiBufferOffset>],
10463        snippet: Snippet,
10464        window: &mut Window,
10465        cx: &mut Context<Self>,
10466    ) -> Result<()> {
10467        struct Tabstop<T> {
10468            is_end_tabstop: bool,
10469            ranges: Vec<Range<T>>,
10470            choices: Option<Vec<String>>,
10471        }
10472
10473        let tabstops = self.buffer.update(cx, |buffer, cx| {
10474            let snippet_text: Arc<str> = snippet.text.clone().into();
10475            let edits = insertion_ranges
10476                .iter()
10477                .cloned()
10478                .map(|range| (range, snippet_text.clone()));
10479            let autoindent_mode = AutoindentMode::Block {
10480                original_indent_columns: Vec::new(),
10481            };
10482            buffer.edit(edits, Some(autoindent_mode), cx);
10483
10484            let snapshot = &*buffer.read(cx);
10485            let snippet = &snippet;
10486            snippet
10487                .tabstops
10488                .iter()
10489                .map(|tabstop| {
10490                    let is_end_tabstop = tabstop.ranges.first().is_some_and(|tabstop| {
10491                        tabstop.is_empty() && tabstop.start == snippet.text.len() as isize
10492                    });
10493                    let mut tabstop_ranges = tabstop
10494                        .ranges
10495                        .iter()
10496                        .flat_map(|tabstop_range| {
10497                            let mut delta = 0_isize;
10498                            insertion_ranges.iter().map(move |insertion_range| {
10499                                let insertion_start = insertion_range.start + delta;
10500                                delta += snippet.text.len() as isize
10501                                    - (insertion_range.end - insertion_range.start) as isize;
10502
10503                                let start =
10504                                    (insertion_start + tabstop_range.start).min(snapshot.len());
10505                                let end = (insertion_start + tabstop_range.end).min(snapshot.len());
10506                                snapshot.anchor_before(start)..snapshot.anchor_after(end)
10507                            })
10508                        })
10509                        .collect::<Vec<_>>();
10510                    tabstop_ranges.sort_unstable_by(|a, b| a.start.cmp(&b.start, snapshot));
10511
10512                    Tabstop {
10513                        is_end_tabstop,
10514                        ranges: tabstop_ranges,
10515                        choices: tabstop.choices.clone(),
10516                    }
10517                })
10518                .collect::<Vec<_>>()
10519        });
10520        if let Some(tabstop) = tabstops.first() {
10521            self.change_selections(Default::default(), window, cx, |s| {
10522                // Reverse order so that the first range is the newest created selection.
10523                // Completions will use it and autoscroll will prioritize it.
10524                s.select_ranges(tabstop.ranges.iter().rev().cloned());
10525            });
10526
10527            if let Some(choices) = &tabstop.choices
10528                && let Some(selection) = tabstop.ranges.first()
10529            {
10530                self.show_snippet_choices(choices, selection.clone(), cx)
10531            }
10532
10533            // If we're already at the last tabstop and it's at the end of the snippet,
10534            // we're done, we don't need to keep the state around.
10535            if !tabstop.is_end_tabstop {
10536                let choices = tabstops
10537                    .iter()
10538                    .map(|tabstop| tabstop.choices.clone())
10539                    .collect();
10540
10541                let ranges = tabstops
10542                    .into_iter()
10543                    .map(|tabstop| tabstop.ranges)
10544                    .collect::<Vec<_>>();
10545
10546                self.snippet_stack.push(SnippetState {
10547                    active_index: 0,
10548                    ranges,
10549                    choices,
10550                });
10551            }
10552
10553            // Check whether the just-entered snippet ends with an auto-closable bracket.
10554            if self.autoclose_regions.is_empty() {
10555                let snapshot = self.buffer.read(cx).snapshot(cx);
10556                for selection in &mut self.selections.all::<Point>(&self.display_snapshot(cx)) {
10557                    let selection_head = selection.head();
10558                    let Some(scope) = snapshot.language_scope_at(selection_head) else {
10559                        continue;
10560                    };
10561
10562                    let mut bracket_pair = None;
10563                    let max_lookup_length = scope
10564                        .brackets()
10565                        .map(|(pair, _)| {
10566                            pair.start
10567                                .as_str()
10568                                .chars()
10569                                .count()
10570                                .max(pair.end.as_str().chars().count())
10571                        })
10572                        .max();
10573                    if let Some(max_lookup_length) = max_lookup_length {
10574                        let next_text = snapshot
10575                            .chars_at(selection_head)
10576                            .take(max_lookup_length)
10577                            .collect::<String>();
10578                        let prev_text = snapshot
10579                            .reversed_chars_at(selection_head)
10580                            .take(max_lookup_length)
10581                            .collect::<String>();
10582
10583                        for (pair, enabled) in scope.brackets() {
10584                            if enabled
10585                                && pair.close
10586                                && prev_text.starts_with(pair.start.as_str())
10587                                && next_text.starts_with(pair.end.as_str())
10588                            {
10589                                bracket_pair = Some(pair.clone());
10590                                break;
10591                            }
10592                        }
10593                    }
10594
10595                    if let Some(pair) = bracket_pair {
10596                        let snapshot_settings = snapshot.language_settings_at(selection_head, cx);
10597                        let autoclose_enabled =
10598                            self.use_autoclose && snapshot_settings.use_autoclose;
10599                        if autoclose_enabled {
10600                            let start = snapshot.anchor_after(selection_head);
10601                            let end = snapshot.anchor_after(selection_head);
10602                            self.autoclose_regions.push(AutocloseRegion {
10603                                selection_id: selection.id,
10604                                range: start..end,
10605                                pair,
10606                            });
10607                        }
10608                    }
10609                }
10610            }
10611        }
10612        Ok(())
10613    }
10614
10615    pub fn move_to_next_snippet_tabstop(
10616        &mut self,
10617        window: &mut Window,
10618        cx: &mut Context<Self>,
10619    ) -> bool {
10620        self.move_to_snippet_tabstop(Bias::Right, window, cx)
10621    }
10622
10623    pub fn move_to_prev_snippet_tabstop(
10624        &mut self,
10625        window: &mut Window,
10626        cx: &mut Context<Self>,
10627    ) -> bool {
10628        self.move_to_snippet_tabstop(Bias::Left, window, cx)
10629    }
10630
10631    pub fn move_to_snippet_tabstop(
10632        &mut self,
10633        bias: Bias,
10634        window: &mut Window,
10635        cx: &mut Context<Self>,
10636    ) -> bool {
10637        if let Some(mut snippet) = self.snippet_stack.pop() {
10638            match bias {
10639                Bias::Left => {
10640                    if snippet.active_index > 0 {
10641                        snippet.active_index -= 1;
10642                    } else {
10643                        self.snippet_stack.push(snippet);
10644                        return false;
10645                    }
10646                }
10647                Bias::Right => {
10648                    if snippet.active_index + 1 < snippet.ranges.len() {
10649                        snippet.active_index += 1;
10650                    } else {
10651                        self.snippet_stack.push(snippet);
10652                        return false;
10653                    }
10654                }
10655            }
10656            if let Some(current_ranges) = snippet.ranges.get(snippet.active_index) {
10657                self.change_selections(Default::default(), window, cx, |s| {
10658                    // Reverse order so that the first range is the newest created selection.
10659                    // Completions will use it and autoscroll will prioritize it.
10660                    s.select_ranges(current_ranges.iter().rev().cloned())
10661                });
10662
10663                if let Some(choices) = &snippet.choices[snippet.active_index]
10664                    && let Some(selection) = current_ranges.first()
10665                {
10666                    self.show_snippet_choices(choices, selection.clone(), cx);
10667                }
10668
10669                // If snippet state is not at the last tabstop, push it back on the stack
10670                if snippet.active_index + 1 < snippet.ranges.len() {
10671                    self.snippet_stack.push(snippet);
10672                }
10673                return true;
10674            }
10675        }
10676
10677        false
10678    }
10679
10680    pub fn clear(&mut self, window: &mut Window, cx: &mut Context<Self>) {
10681        self.transact(window, cx, |this, window, cx| {
10682            this.select_all(&SelectAll, window, cx);
10683            this.insert("", window, cx);
10684        });
10685    }
10686
10687    pub fn backspace(&mut self, _: &Backspace, window: &mut Window, cx: &mut Context<Self>) {
10688        if self.read_only(cx) {
10689            return;
10690        }
10691        self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
10692        self.transact(window, cx, |this, window, cx| {
10693            this.select_autoclose_pair(window, cx);
10694
10695            let display_map = this.display_map.update(cx, |map, cx| map.snapshot(cx));
10696
10697            let mut linked_ranges = HashMap::<_, Vec<_>>::default();
10698            if !this.linked_edit_ranges.is_empty() {
10699                let selections = this.selections.all::<MultiBufferPoint>(&display_map);
10700                let snapshot = this.buffer.read(cx).snapshot(cx);
10701
10702                for selection in selections.iter() {
10703                    let selection_start = snapshot.anchor_before(selection.start).text_anchor;
10704                    let selection_end = snapshot.anchor_after(selection.end).text_anchor;
10705                    if selection_start.buffer_id != selection_end.buffer_id {
10706                        continue;
10707                    }
10708                    if let Some(ranges) =
10709                        this.linked_editing_ranges_for(selection_start..selection_end, cx)
10710                    {
10711                        for (buffer, entries) in ranges {
10712                            linked_ranges.entry(buffer).or_default().extend(entries);
10713                        }
10714                    }
10715                }
10716            }
10717
10718            let mut selections = this.selections.all::<MultiBufferPoint>(&display_map);
10719            for selection in &mut selections {
10720                if selection.is_empty() {
10721                    let old_head = selection.head();
10722                    let mut new_head =
10723                        movement::left(&display_map, old_head.to_display_point(&display_map))
10724                            .to_point(&display_map);
10725                    if let Some((buffer, line_buffer_range)) = display_map
10726                        .buffer_snapshot()
10727                        .buffer_line_for_row(MultiBufferRow(old_head.row))
10728                    {
10729                        let indent_size = buffer.indent_size_for_line(line_buffer_range.start.row);
10730                        let indent_len = match indent_size.kind {
10731                            IndentKind::Space => {
10732                                buffer.settings_at(line_buffer_range.start, cx).tab_size
10733                            }
10734                            IndentKind::Tab => NonZeroU32::new(1).unwrap(),
10735                        };
10736                        if old_head.column <= indent_size.len && old_head.column > 0 {
10737                            let indent_len = indent_len.get();
10738                            new_head = cmp::min(
10739                                new_head,
10740                                MultiBufferPoint::new(
10741                                    old_head.row,
10742                                    ((old_head.column - 1) / indent_len) * indent_len,
10743                                ),
10744                            );
10745                        }
10746                    }
10747
10748                    selection.set_head(new_head, SelectionGoal::None);
10749                }
10750            }
10751
10752            this.change_selections(Default::default(), window, cx, |s| s.select(selections));
10753            this.insert("", window, cx);
10754            let empty_str: Arc<str> = Arc::from("");
10755            for (buffer, edits) in linked_ranges {
10756                let snapshot = buffer.read(cx).snapshot();
10757                use text::ToPoint as TP;
10758
10759                let edits = edits
10760                    .into_iter()
10761                    .map(|range| {
10762                        let end_point = TP::to_point(&range.end, &snapshot);
10763                        let mut start_point = TP::to_point(&range.start, &snapshot);
10764
10765                        if end_point == start_point {
10766                            let offset = text::ToOffset::to_offset(&range.start, &snapshot)
10767                                .saturating_sub(1);
10768                            start_point =
10769                                snapshot.clip_point(TP::to_point(&offset, &snapshot), Bias::Left);
10770                        };
10771
10772                        (start_point..end_point, empty_str.clone())
10773                    })
10774                    .sorted_by_key(|(range, _)| range.start)
10775                    .collect::<Vec<_>>();
10776                buffer.update(cx, |this, cx| {
10777                    this.edit(edits, None, cx);
10778                })
10779            }
10780            this.refresh_edit_prediction(true, false, window, cx);
10781            refresh_linked_ranges(this, window, cx);
10782        });
10783    }
10784
10785    pub fn delete(&mut self, _: &Delete, window: &mut Window, cx: &mut Context<Self>) {
10786        if self.read_only(cx) {
10787            return;
10788        }
10789        self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
10790        self.transact(window, cx, |this, window, cx| {
10791            this.change_selections(Default::default(), window, cx, |s| {
10792                s.move_with(&mut |map, selection| {
10793                    if selection.is_empty() {
10794                        let cursor = movement::right(map, selection.head());
10795                        selection.end = cursor;
10796                        selection.reversed = true;
10797                        selection.goal = SelectionGoal::None;
10798                    }
10799                })
10800            });
10801            this.insert("", window, cx);
10802            this.refresh_edit_prediction(true, false, window, cx);
10803        });
10804    }
10805
10806    pub fn backtab(&mut self, _: &Backtab, window: &mut Window, cx: &mut Context<Self>) {
10807        if self.mode.is_single_line() {
10808            cx.propagate();
10809            return;
10810        }
10811
10812        self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
10813        if self.move_to_prev_snippet_tabstop(window, cx) {
10814            return;
10815        }
10816        self.outdent(&Outdent, window, cx);
10817    }
10818
10819    pub fn next_snippet_tabstop(
10820        &mut self,
10821        _: &NextSnippetTabstop,
10822        window: &mut Window,
10823        cx: &mut Context<Self>,
10824    ) {
10825        if self.mode.is_single_line() || self.snippet_stack.is_empty() {
10826            cx.propagate();
10827            return;
10828        }
10829
10830        if self.move_to_next_snippet_tabstop(window, cx) {
10831            self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
10832            return;
10833        }
10834        cx.propagate();
10835    }
10836
10837    pub fn previous_snippet_tabstop(
10838        &mut self,
10839        _: &PreviousSnippetTabstop,
10840        window: &mut Window,
10841        cx: &mut Context<Self>,
10842    ) {
10843        if self.mode.is_single_line() || self.snippet_stack.is_empty() {
10844            cx.propagate();
10845            return;
10846        }
10847
10848        if self.move_to_prev_snippet_tabstop(window, cx) {
10849            self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
10850            return;
10851        }
10852        cx.propagate();
10853    }
10854
10855    pub fn tab(&mut self, _: &Tab, window: &mut Window, cx: &mut Context<Self>) {
10856        if self.mode.is_single_line() {
10857            cx.propagate();
10858            return;
10859        }
10860
10861        if self.move_to_next_snippet_tabstop(window, cx) {
10862            self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
10863            return;
10864        }
10865        if self.read_only(cx) {
10866            return;
10867        }
10868        self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
10869        let mut selections = self.selections.all_adjusted(&self.display_snapshot(cx));
10870        let buffer = self.buffer.read(cx);
10871        let snapshot = buffer.snapshot(cx);
10872        let rows_iter = selections.iter().map(|s| s.head().row);
10873        let suggested_indents = snapshot.suggested_indents(rows_iter, cx);
10874
10875        let has_some_cursor_in_whitespace = selections
10876            .iter()
10877            .filter(|selection| selection.is_empty())
10878            .any(|selection| {
10879                let cursor = selection.head();
10880                let current_indent = snapshot.indent_size_for_line(MultiBufferRow(cursor.row));
10881                cursor.column < current_indent.len
10882            });
10883
10884        let mut edits = Vec::new();
10885        let mut prev_edited_row = 0;
10886        let mut row_delta = 0;
10887        for selection in &mut selections {
10888            if selection.start.row != prev_edited_row {
10889                row_delta = 0;
10890            }
10891            prev_edited_row = selection.end.row;
10892
10893            // If cursor is after a list prefix, make selection non-empty to trigger line indent
10894            if selection.is_empty() {
10895                let cursor = selection.head();
10896                let settings = buffer.language_settings_at(cursor, cx);
10897                if settings.indent_list_on_tab {
10898                    if let Some(language) = snapshot.language_scope_at(Point::new(cursor.row, 0)) {
10899                        if is_list_prefix_row(MultiBufferRow(cursor.row), &snapshot, &language) {
10900                            row_delta = Self::indent_selection(
10901                                buffer, &snapshot, selection, &mut edits, row_delta, cx,
10902                            );
10903                            continue;
10904                        }
10905                    }
10906                }
10907            }
10908
10909            // If the selection is non-empty, then increase the indentation of the selected lines.
10910            if !selection.is_empty() {
10911                row_delta =
10912                    Self::indent_selection(buffer, &snapshot, selection, &mut edits, row_delta, cx);
10913                continue;
10914            }
10915
10916            let cursor = selection.head();
10917            let current_indent = snapshot.indent_size_for_line(MultiBufferRow(cursor.row));
10918            if let Some(suggested_indent) =
10919                suggested_indents.get(&MultiBufferRow(cursor.row)).copied()
10920            {
10921                // Don't do anything if already at suggested indent
10922                // and there is any other cursor which is not
10923                if has_some_cursor_in_whitespace
10924                    && cursor.column == current_indent.len
10925                    && current_indent.len == suggested_indent.len
10926                {
10927                    continue;
10928                }
10929
10930                // Adjust line and move cursor to suggested indent
10931                // if cursor is not at suggested indent
10932                if cursor.column < suggested_indent.len
10933                    && cursor.column <= current_indent.len
10934                    && current_indent.len <= suggested_indent.len
10935                {
10936                    selection.start = Point::new(cursor.row, suggested_indent.len);
10937                    selection.end = selection.start;
10938                    if row_delta == 0 {
10939                        edits.extend(Buffer::edit_for_indent_size_adjustment(
10940                            cursor.row,
10941                            current_indent,
10942                            suggested_indent,
10943                        ));
10944                        row_delta = suggested_indent.len - current_indent.len;
10945                    }
10946                    continue;
10947                }
10948
10949                // If current indent is more than suggested indent
10950                // only move cursor to current indent and skip indent
10951                if cursor.column < current_indent.len && current_indent.len > suggested_indent.len {
10952                    selection.start = Point::new(cursor.row, current_indent.len);
10953                    selection.end = selection.start;
10954                    continue;
10955                }
10956            }
10957
10958            // Otherwise, insert a hard or soft tab.
10959            let settings = buffer.language_settings_at(cursor, cx);
10960            let tab_size = if settings.hard_tabs {
10961                IndentSize::tab()
10962            } else {
10963                let tab_size = settings.tab_size.get();
10964                let indent_remainder = snapshot
10965                    .text_for_range(Point::new(cursor.row, 0)..cursor)
10966                    .flat_map(str::chars)
10967                    .fold(row_delta % tab_size, |counter: u32, c| {
10968                        if c == '\t' {
10969                            0
10970                        } else {
10971                            (counter + 1) % tab_size
10972                        }
10973                    });
10974
10975                let chars_to_next_tab_stop = tab_size - indent_remainder;
10976                IndentSize::spaces(chars_to_next_tab_stop)
10977            };
10978            selection.start = Point::new(cursor.row, cursor.column + row_delta + tab_size.len);
10979            selection.end = selection.start;
10980            edits.push((cursor..cursor, tab_size.chars().collect::<String>()));
10981            row_delta += tab_size.len;
10982        }
10983
10984        self.transact(window, cx, |this, window, cx| {
10985            this.buffer.update(cx, |b, cx| b.edit(edits, None, cx));
10986            this.change_selections(Default::default(), window, cx, |s| s.select(selections));
10987            this.refresh_edit_prediction(true, false, window, cx);
10988        });
10989    }
10990
10991    pub fn indent(&mut self, _: &Indent, window: &mut Window, cx: &mut Context<Self>) {
10992        if self.read_only(cx) {
10993            return;
10994        }
10995        if self.mode.is_single_line() {
10996            cx.propagate();
10997            return;
10998        }
10999
11000        self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
11001        let mut selections = self.selections.all::<Point>(&self.display_snapshot(cx));
11002        let mut prev_edited_row = 0;
11003        let mut row_delta = 0;
11004        let mut edits = Vec::new();
11005        let buffer = self.buffer.read(cx);
11006        let snapshot = buffer.snapshot(cx);
11007        for selection in &mut selections {
11008            if selection.start.row != prev_edited_row {
11009                row_delta = 0;
11010            }
11011            prev_edited_row = selection.end.row;
11012
11013            row_delta =
11014                Self::indent_selection(buffer, &snapshot, selection, &mut edits, row_delta, cx);
11015        }
11016
11017        self.transact(window, cx, |this, window, cx| {
11018            this.buffer.update(cx, |b, cx| b.edit(edits, None, cx));
11019            this.change_selections(Default::default(), window, cx, |s| s.select(selections));
11020        });
11021    }
11022
11023    fn indent_selection(
11024        buffer: &MultiBuffer,
11025        snapshot: &MultiBufferSnapshot,
11026        selection: &mut Selection<Point>,
11027        edits: &mut Vec<(Range<Point>, String)>,
11028        delta_for_start_row: u32,
11029        cx: &App,
11030    ) -> u32 {
11031        let settings = buffer.language_settings_at(selection.start, cx);
11032        let tab_size = settings.tab_size.get();
11033        let indent_kind = if settings.hard_tabs {
11034            IndentKind::Tab
11035        } else {
11036            IndentKind::Space
11037        };
11038        let mut start_row = selection.start.row;
11039        let mut end_row = selection.end.row + 1;
11040
11041        // If a selection ends at the beginning of a line, don't indent
11042        // that last line.
11043        if selection.end.column == 0 && selection.end.row > selection.start.row {
11044            end_row -= 1;
11045        }
11046
11047        // Avoid re-indenting a row that has already been indented by a
11048        // previous selection, but still update this selection's column
11049        // to reflect that indentation.
11050        if delta_for_start_row > 0 {
11051            start_row += 1;
11052            selection.start.column += delta_for_start_row;
11053            if selection.end.row == selection.start.row {
11054                selection.end.column += delta_for_start_row;
11055            }
11056        }
11057
11058        let mut delta_for_end_row = 0;
11059        let has_multiple_rows = start_row + 1 != end_row;
11060        for row in start_row..end_row {
11061            let current_indent = snapshot.indent_size_for_line(MultiBufferRow(row));
11062            let indent_delta = match (current_indent.kind, indent_kind) {
11063                (IndentKind::Space, IndentKind::Space) => {
11064                    let columns_to_next_tab_stop = tab_size - (current_indent.len % tab_size);
11065                    IndentSize::spaces(columns_to_next_tab_stop)
11066                }
11067                (IndentKind::Tab, IndentKind::Space) => IndentSize::spaces(tab_size),
11068                (_, IndentKind::Tab) => IndentSize::tab(),
11069            };
11070
11071            let start = if has_multiple_rows || current_indent.len < selection.start.column {
11072                0
11073            } else {
11074                selection.start.column
11075            };
11076            let row_start = Point::new(row, start);
11077            edits.push((
11078                row_start..row_start,
11079                indent_delta.chars().collect::<String>(),
11080            ));
11081
11082            // Update this selection's endpoints to reflect the indentation.
11083            if row == selection.start.row {
11084                selection.start.column += indent_delta.len;
11085            }
11086            if row == selection.end.row {
11087                selection.end.column += indent_delta.len;
11088                delta_for_end_row = indent_delta.len;
11089            }
11090        }
11091
11092        if selection.start.row == selection.end.row {
11093            delta_for_start_row + delta_for_end_row
11094        } else {
11095            delta_for_end_row
11096        }
11097    }
11098
11099    pub fn outdent(&mut self, _: &Outdent, window: &mut Window, cx: &mut Context<Self>) {
11100        if self.read_only(cx) {
11101            return;
11102        }
11103        if self.mode.is_single_line() {
11104            cx.propagate();
11105            return;
11106        }
11107
11108        self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
11109        let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
11110        let selections = self.selections.all::<Point>(&display_map);
11111        let mut deletion_ranges = Vec::new();
11112        let mut last_outdent = None;
11113        {
11114            let buffer = self.buffer.read(cx);
11115            let snapshot = buffer.snapshot(cx);
11116            for selection in &selections {
11117                let settings = buffer.language_settings_at(selection.start, cx);
11118                let tab_size = settings.tab_size.get();
11119                let mut rows = selection.spanned_rows(false, &display_map);
11120
11121                // Avoid re-outdenting a row that has already been outdented by a
11122                // previous selection.
11123                if let Some(last_row) = last_outdent
11124                    && last_row == rows.start
11125                {
11126                    rows.start = rows.start.next_row();
11127                }
11128                let has_multiple_rows = rows.len() > 1;
11129                for row in rows.iter_rows() {
11130                    let indent_size = snapshot.indent_size_for_line(row);
11131                    if indent_size.len > 0 {
11132                        let deletion_len = match indent_size.kind {
11133                            IndentKind::Space => {
11134                                let columns_to_prev_tab_stop = indent_size.len % tab_size;
11135                                if columns_to_prev_tab_stop == 0 {
11136                                    tab_size
11137                                } else {
11138                                    columns_to_prev_tab_stop
11139                                }
11140                            }
11141                            IndentKind::Tab => 1,
11142                        };
11143                        let start = if has_multiple_rows
11144                            || deletion_len > selection.start.column
11145                            || indent_size.len < selection.start.column
11146                        {
11147                            0
11148                        } else {
11149                            selection.start.column - deletion_len
11150                        };
11151                        deletion_ranges.push(
11152                            Point::new(row.0, start)..Point::new(row.0, start + deletion_len),
11153                        );
11154                        last_outdent = Some(row);
11155                    }
11156                }
11157            }
11158        }
11159
11160        self.transact(window, cx, |this, window, cx| {
11161            this.buffer.update(cx, |buffer, cx| {
11162                let empty_str: Arc<str> = Arc::default();
11163                buffer.edit(
11164                    deletion_ranges
11165                        .into_iter()
11166                        .map(|range| (range, empty_str.clone())),
11167                    None,
11168                    cx,
11169                );
11170            });
11171            let selections = this
11172                .selections
11173                .all::<MultiBufferOffset>(&this.display_snapshot(cx));
11174            this.change_selections(Default::default(), window, cx, |s| s.select(selections));
11175        });
11176    }
11177
11178    pub fn autoindent(&mut self, _: &AutoIndent, window: &mut Window, cx: &mut Context<Self>) {
11179        if self.read_only(cx) {
11180            return;
11181        }
11182        if self.mode.is_single_line() {
11183            cx.propagate();
11184            return;
11185        }
11186
11187        self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
11188        let selections = self
11189            .selections
11190            .all::<MultiBufferOffset>(&self.display_snapshot(cx))
11191            .into_iter()
11192            .map(|s| s.range());
11193
11194        self.transact(window, cx, |this, window, cx| {
11195            this.buffer.update(cx, |buffer, cx| {
11196                buffer.autoindent_ranges(selections, cx);
11197            });
11198            let selections = this
11199                .selections
11200                .all::<MultiBufferOffset>(&this.display_snapshot(cx));
11201            this.change_selections(Default::default(), window, cx, |s| s.select(selections));
11202        });
11203    }
11204
11205    pub fn delete_line(&mut self, _: &DeleteLine, window: &mut Window, cx: &mut Context<Self>) {
11206        self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
11207        let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
11208        let selections = self.selections.all::<Point>(&display_map);
11209
11210        let mut new_cursors = Vec::new();
11211        let mut edit_ranges = Vec::new();
11212        let mut selections = selections.iter().peekable();
11213        while let Some(selection) = selections.next() {
11214            let mut rows = selection.spanned_rows(false, &display_map);
11215
11216            // Accumulate contiguous regions of rows that we want to delete.
11217            while let Some(next_selection) = selections.peek() {
11218                let next_rows = next_selection.spanned_rows(false, &display_map);
11219                if next_rows.start <= rows.end {
11220                    rows.end = next_rows.end;
11221                    selections.next().unwrap();
11222                } else {
11223                    break;
11224                }
11225            }
11226
11227            let buffer = display_map.buffer_snapshot();
11228            let mut edit_start = ToOffset::to_offset(&Point::new(rows.start.0, 0), buffer);
11229            let (edit_end, target_row) = if buffer.max_point().row >= rows.end.0 {
11230                // If there's a line after the range, delete the \n from the end of the row range
11231                (
11232                    ToOffset::to_offset(&Point::new(rows.end.0, 0), buffer),
11233                    rows.end,
11234                )
11235            } else {
11236                // If there isn't a line after the range, delete the \n from the line before the
11237                // start of the row range
11238                edit_start = edit_start.saturating_sub_usize(1);
11239                (buffer.len(), rows.start.previous_row())
11240            };
11241
11242            let text_layout_details = self.text_layout_details(window, cx);
11243            let x = display_map.x_for_display_point(
11244                selection.head().to_display_point(&display_map),
11245                &text_layout_details,
11246            );
11247            let row = Point::new(target_row.0, 0)
11248                .to_display_point(&display_map)
11249                .row();
11250            let column = display_map.display_column_for_x(row, x, &text_layout_details);
11251
11252            new_cursors.push((
11253                selection.id,
11254                buffer.anchor_after(DisplayPoint::new(row, column).to_point(&display_map)),
11255                SelectionGoal::None,
11256            ));
11257            edit_ranges.push(edit_start..edit_end);
11258        }
11259
11260        self.transact(window, cx, |this, window, cx| {
11261            let buffer = this.buffer.update(cx, |buffer, cx| {
11262                let empty_str: Arc<str> = Arc::default();
11263                buffer.edit(
11264                    edit_ranges
11265                        .into_iter()
11266                        .map(|range| (range, empty_str.clone())),
11267                    None,
11268                    cx,
11269                );
11270                buffer.snapshot(cx)
11271            });
11272            let new_selections = new_cursors
11273                .into_iter()
11274                .map(|(id, cursor, goal)| {
11275                    let cursor = cursor.to_point(&buffer);
11276                    Selection {
11277                        id,
11278                        start: cursor,
11279                        end: cursor,
11280                        reversed: false,
11281                        goal,
11282                    }
11283                })
11284                .collect();
11285
11286            this.change_selections(Default::default(), window, cx, |s| {
11287                s.select(new_selections);
11288            });
11289        });
11290    }
11291
11292    pub fn join_lines_impl(
11293        &mut self,
11294        insert_whitespace: bool,
11295        window: &mut Window,
11296        cx: &mut Context<Self>,
11297    ) {
11298        if self.read_only(cx) {
11299            return;
11300        }
11301        let mut row_ranges = Vec::<Range<MultiBufferRow>>::new();
11302        for selection in self.selections.all::<Point>(&self.display_snapshot(cx)) {
11303            let start = MultiBufferRow(selection.start.row);
11304            // Treat single line selections as if they include the next line. Otherwise this action
11305            // would do nothing for single line selections individual cursors.
11306            let end = if selection.start.row == selection.end.row {
11307                MultiBufferRow(selection.start.row + 1)
11308            } else {
11309                MultiBufferRow(selection.end.row)
11310            };
11311
11312            if let Some(last_row_range) = row_ranges.last_mut()
11313                && start <= last_row_range.end
11314            {
11315                last_row_range.end = end;
11316                continue;
11317            }
11318            row_ranges.push(start..end);
11319        }
11320
11321        let snapshot = self.buffer.read(cx).snapshot(cx);
11322        let mut cursor_positions = Vec::new();
11323        for row_range in &row_ranges {
11324            let anchor = snapshot.anchor_before(Point::new(
11325                row_range.end.previous_row().0,
11326                snapshot.line_len(row_range.end.previous_row()),
11327            ));
11328            cursor_positions.push(anchor..anchor);
11329        }
11330
11331        self.transact(window, cx, |this, window, cx| {
11332            for row_range in row_ranges.into_iter().rev() {
11333                for row in row_range.iter_rows().rev() {
11334                    let end_of_line = Point::new(row.0, snapshot.line_len(row));
11335                    let next_line_row = row.next_row();
11336                    let indent = snapshot.indent_size_for_line(next_line_row);
11337                    let mut join_start_column = indent.len;
11338
11339                    if let Some(language_scope) =
11340                        snapshot.language_scope_at(Point::new(next_line_row.0, indent.len))
11341                    {
11342                        let line_end =
11343                            Point::new(next_line_row.0, snapshot.line_len(next_line_row));
11344                        let line_text_after_indent = snapshot
11345                            .text_for_range(Point::new(next_line_row.0, indent.len)..line_end)
11346                            .collect::<String>();
11347
11348                        if !line_text_after_indent.is_empty() {
11349                            let block_prefix = language_scope
11350                                .block_comment()
11351                                .map(|c| c.prefix.as_ref())
11352                                .filter(|p| !p.is_empty());
11353                            let doc_prefix = language_scope
11354                                .documentation_comment()
11355                                .map(|c| c.prefix.as_ref())
11356                                .filter(|p| !p.is_empty());
11357                            let all_prefixes = language_scope
11358                                .line_comment_prefixes()
11359                                .iter()
11360                                .map(|p| p.as_ref())
11361                                .chain(block_prefix)
11362                                .chain(doc_prefix)
11363                                .chain(language_scope.unordered_list().iter().map(|p| p.as_ref()));
11364
11365                            let mut longest_prefix_len = None;
11366                            for prefix in all_prefixes {
11367                                let trimmed = prefix.trim_end();
11368                                if line_text_after_indent.starts_with(trimmed) {
11369                                    let candidate_len =
11370                                        if line_text_after_indent.starts_with(prefix) {
11371                                            prefix.len()
11372                                        } else {
11373                                            trimmed.len()
11374                                        };
11375                                    if longest_prefix_len.map_or(true, |len| candidate_len > len) {
11376                                        longest_prefix_len = Some(candidate_len);
11377                                    }
11378                                }
11379                            }
11380
11381                            if let Some(prefix_len) = longest_prefix_len {
11382                                join_start_column =
11383                                    join_start_column.saturating_add(prefix_len as u32);
11384                            }
11385                        }
11386                    }
11387
11388                    let start_of_next_line = Point::new(next_line_row.0, join_start_column);
11389
11390                    let replace = if snapshot.line_len(next_line_row) > join_start_column
11391                        && insert_whitespace
11392                    {
11393                        " "
11394                    } else {
11395                        ""
11396                    };
11397
11398                    this.buffer.update(cx, |buffer, cx| {
11399                        buffer.edit([(end_of_line..start_of_next_line, replace)], None, cx)
11400                    });
11401                }
11402            }
11403
11404            this.change_selections(Default::default(), window, cx, |s| {
11405                s.select_anchor_ranges(cursor_positions)
11406            });
11407        });
11408    }
11409
11410    pub fn join_lines(&mut self, _: &JoinLines, window: &mut Window, cx: &mut Context<Self>) {
11411        self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
11412        self.join_lines_impl(true, window, cx);
11413    }
11414
11415    pub fn sort_lines_case_sensitive(
11416        &mut self,
11417        _: &SortLinesCaseSensitive,
11418        window: &mut Window,
11419        cx: &mut Context<Self>,
11420    ) {
11421        self.manipulate_immutable_lines(window, cx, |lines| lines.sort())
11422    }
11423
11424    pub fn sort_lines_by_length(
11425        &mut self,
11426        _: &SortLinesByLength,
11427        window: &mut Window,
11428        cx: &mut Context<Self>,
11429    ) {
11430        self.manipulate_immutable_lines(window, cx, |lines| {
11431            lines.sort_by_key(|&line| line.chars().count())
11432        })
11433    }
11434
11435    pub fn sort_lines_case_insensitive(
11436        &mut self,
11437        _: &SortLinesCaseInsensitive,
11438        window: &mut Window,
11439        cx: &mut Context<Self>,
11440    ) {
11441        self.manipulate_immutable_lines(window, cx, |lines| {
11442            lines.sort_by_key(|line| line.to_lowercase())
11443        })
11444    }
11445
11446    pub fn unique_lines_case_insensitive(
11447        &mut self,
11448        _: &UniqueLinesCaseInsensitive,
11449        window: &mut Window,
11450        cx: &mut Context<Self>,
11451    ) {
11452        self.manipulate_immutable_lines(window, cx, |lines| {
11453            let mut seen = HashSet::default();
11454            lines.retain(|line| seen.insert(line.to_lowercase()));
11455        })
11456    }
11457
11458    pub fn unique_lines_case_sensitive(
11459        &mut self,
11460        _: &UniqueLinesCaseSensitive,
11461        window: &mut Window,
11462        cx: &mut Context<Self>,
11463    ) {
11464        self.manipulate_immutable_lines(window, cx, |lines| {
11465            let mut seen = HashSet::default();
11466            lines.retain(|line| seen.insert(*line));
11467        })
11468    }
11469
11470    fn enable_wrap_selections_in_tag(&self, cx: &App) -> bool {
11471        let snapshot = self.buffer.read(cx).snapshot(cx);
11472        for selection in self.selections.disjoint_anchors_arc().iter() {
11473            if snapshot
11474                .language_at(selection.start)
11475                .and_then(|lang| lang.config().wrap_characters.as_ref())
11476                .is_some()
11477            {
11478                return true;
11479            }
11480        }
11481        false
11482    }
11483
11484    fn wrap_selections_in_tag(
11485        &mut self,
11486        _: &WrapSelectionsInTag,
11487        window: &mut Window,
11488        cx: &mut Context<Self>,
11489    ) {
11490        self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
11491
11492        let snapshot = self.buffer.read(cx).snapshot(cx);
11493
11494        let mut edits = Vec::new();
11495        let mut boundaries = Vec::new();
11496
11497        for selection in self
11498            .selections
11499            .all_adjusted(&self.display_snapshot(cx))
11500            .iter()
11501        {
11502            let Some(wrap_config) = snapshot
11503                .language_at(selection.start)
11504                .and_then(|lang| lang.config().wrap_characters.clone())
11505            else {
11506                continue;
11507            };
11508
11509            let open_tag = format!("{}{}", wrap_config.start_prefix, wrap_config.start_suffix);
11510            let close_tag = format!("{}{}", wrap_config.end_prefix, wrap_config.end_suffix);
11511
11512            let start_before = snapshot.anchor_before(selection.start);
11513            let end_after = snapshot.anchor_after(selection.end);
11514
11515            edits.push((start_before..start_before, open_tag));
11516            edits.push((end_after..end_after, close_tag));
11517
11518            boundaries.push((
11519                start_before,
11520                end_after,
11521                wrap_config.start_prefix.len(),
11522                wrap_config.end_suffix.len(),
11523            ));
11524        }
11525
11526        if edits.is_empty() {
11527            return;
11528        }
11529
11530        self.transact(window, cx, |this, window, cx| {
11531            let buffer = this.buffer.update(cx, |buffer, cx| {
11532                buffer.edit(edits, None, cx);
11533                buffer.snapshot(cx)
11534            });
11535
11536            let mut new_selections = Vec::with_capacity(boundaries.len() * 2);
11537            for (start_before, end_after, start_prefix_len, end_suffix_len) in
11538                boundaries.into_iter()
11539            {
11540                let open_offset = start_before.to_offset(&buffer) + start_prefix_len;
11541                let close_offset = end_after
11542                    .to_offset(&buffer)
11543                    .saturating_sub_usize(end_suffix_len);
11544                new_selections.push(open_offset..open_offset);
11545                new_selections.push(close_offset..close_offset);
11546            }
11547
11548            this.change_selections(Default::default(), window, cx, |s| {
11549                s.select_ranges(new_selections);
11550            });
11551
11552            this.request_autoscroll(Autoscroll::fit(), cx);
11553        });
11554    }
11555
11556    pub fn toggle_read_only(
11557        &mut self,
11558        _: &workspace::ToggleReadOnlyFile,
11559        _: &mut Window,
11560        cx: &mut Context<Self>,
11561    ) {
11562        if let Some(buffer) = self.buffer.read(cx).as_singleton() {
11563            buffer.update(cx, |buffer, cx| {
11564                buffer.set_capability(
11565                    match buffer.capability() {
11566                        Capability::ReadWrite => Capability::Read,
11567                        Capability::Read => Capability::ReadWrite,
11568                        Capability::ReadOnly => Capability::ReadOnly,
11569                    },
11570                    cx,
11571                );
11572            })
11573        }
11574    }
11575
11576    pub fn reload_file(&mut self, _: &ReloadFile, window: &mut Window, cx: &mut Context<Self>) {
11577        let Some(project) = self.project.clone() else {
11578            return;
11579        };
11580        let task = self.reload(project, window, cx);
11581        self.detach_and_notify_err(task, window, cx);
11582    }
11583
11584    pub fn restore_file(
11585        &mut self,
11586        _: &::git::RestoreFile,
11587        window: &mut Window,
11588        cx: &mut Context<Self>,
11589    ) {
11590        self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
11591        let mut buffer_ids = HashSet::default();
11592        let snapshot = self.buffer().read(cx).snapshot(cx);
11593        for selection in self
11594            .selections
11595            .all::<MultiBufferOffset>(&self.display_snapshot(cx))
11596        {
11597            buffer_ids.extend(snapshot.buffer_ids_for_range(selection.range()))
11598        }
11599
11600        let buffer = self.buffer().read(cx);
11601        let ranges = buffer_ids
11602            .into_iter()
11603            .flat_map(|buffer_id| buffer.excerpt_ranges_for_buffer(buffer_id, cx))
11604            .collect::<Vec<_>>();
11605
11606        self.restore_hunks_in_ranges(ranges, window, cx);
11607    }
11608
11609    pub fn git_restore(&mut self, _: &Restore, window: &mut Window, cx: &mut Context<Self>) {
11610        self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
11611        let selections = self
11612            .selections
11613            .all(&self.display_snapshot(cx))
11614            .into_iter()
11615            .map(|s| s.range())
11616            .collect();
11617        self.restore_hunks_in_ranges(selections, window, cx);
11618    }
11619
11620    pub fn restore_hunks_in_ranges(
11621        &mut self,
11622        ranges: Vec<Range<Point>>,
11623        window: &mut Window,
11624        cx: &mut Context<Editor>,
11625    ) {
11626        if self.delegate_stage_and_restore {
11627            let hunks = self.snapshot(window, cx).hunks_for_ranges(ranges);
11628            if !hunks.is_empty() {
11629                cx.emit(EditorEvent::RestoreRequested { hunks });
11630            }
11631            return;
11632        }
11633        let hunks = self.snapshot(window, cx).hunks_for_ranges(ranges);
11634        self.transact(window, cx, |editor, window, cx| {
11635            editor.restore_diff_hunks(hunks, cx);
11636            editor.change_selections(SelectionEffects::no_scroll(), window, cx, |selections| {
11637                selections.refresh()
11638            });
11639        });
11640    }
11641
11642    pub(crate) fn restore_diff_hunks(&self, hunks: Vec<MultiBufferDiffHunk>, cx: &mut App) {
11643        let mut revert_changes = HashMap::default();
11644        let chunk_by = hunks.into_iter().chunk_by(|hunk| hunk.buffer_id);
11645        for (buffer_id, hunks) in &chunk_by {
11646            let hunks = hunks.collect::<Vec<_>>();
11647            for hunk in &hunks {
11648                self.prepare_restore_change(&mut revert_changes, hunk, cx);
11649            }
11650            self.do_stage_or_unstage(false, buffer_id, hunks.into_iter(), cx);
11651        }
11652        if !revert_changes.is_empty() {
11653            self.buffer().update(cx, |multi_buffer, cx| {
11654                for (buffer_id, changes) in revert_changes {
11655                    if let Some(buffer) = multi_buffer.buffer(buffer_id) {
11656                        buffer.update(cx, |buffer, cx| {
11657                            buffer.edit(
11658                                changes
11659                                    .into_iter()
11660                                    .map(|(range, text)| (range, text.to_string())),
11661                                None,
11662                                cx,
11663                            );
11664                        });
11665                    }
11666                }
11667            });
11668        }
11669    }
11670
11671    pub fn status_for_buffer_id(&self, buffer_id: BufferId, cx: &App) -> Option<FileStatus> {
11672        if let Some(status) = self
11673            .addons
11674            .iter()
11675            .find_map(|(_, addon)| addon.override_status_for_buffer_id(buffer_id, cx))
11676        {
11677            return Some(status);
11678        }
11679        self.project
11680            .as_ref()?
11681            .read(cx)
11682            .status_for_buffer_id(buffer_id, cx)
11683    }
11684
11685    pub fn open_active_item_in_terminal(
11686        &mut self,
11687        _: &OpenInTerminal,
11688        window: &mut Window,
11689        cx: &mut Context<Self>,
11690    ) {
11691        if let Some(working_directory) = self.active_excerpt(cx).and_then(|(_, buffer, _)| {
11692            let project_path = buffer.read(cx).project_path(cx)?;
11693            let project = self.project()?.read(cx);
11694            let entry = project.entry_for_path(&project_path, cx)?;
11695            let parent = match &entry.canonical_path {
11696                Some(canonical_path) => canonical_path.to_path_buf(),
11697                None => project.absolute_path(&project_path, cx)?,
11698            }
11699            .parent()?
11700            .to_path_buf();
11701            Some(parent)
11702        }) {
11703            window.dispatch_action(
11704                OpenTerminal {
11705                    working_directory,
11706                    local: false,
11707                }
11708                .boxed_clone(),
11709                cx,
11710            );
11711        }
11712    }
11713
11714    fn set_breakpoint_context_menu(
11715        &mut self,
11716        display_row: DisplayRow,
11717        position: Option<Anchor>,
11718        clicked_point: gpui::Point<Pixels>,
11719        window: &mut Window,
11720        cx: &mut Context<Self>,
11721    ) {
11722        let source = self
11723            .buffer
11724            .read(cx)
11725            .snapshot(cx)
11726            .anchor_before(Point::new(display_row.0, 0u32));
11727
11728        let context_menu = self.breakpoint_context_menu(position.unwrap_or(source), window, cx);
11729
11730        self.mouse_context_menu = MouseContextMenu::pinned_to_editor(
11731            self,
11732            source,
11733            clicked_point,
11734            context_menu,
11735            window,
11736            cx,
11737        );
11738    }
11739
11740    fn add_edit_breakpoint_block(
11741        &mut self,
11742        anchor: Anchor,
11743        breakpoint: &Breakpoint,
11744        edit_action: BreakpointPromptEditAction,
11745        window: &mut Window,
11746        cx: &mut Context<Self>,
11747    ) {
11748        let weak_editor = cx.weak_entity();
11749        let bp_prompt = cx.new(|cx| {
11750            BreakpointPromptEditor::new(
11751                weak_editor,
11752                anchor,
11753                breakpoint.clone(),
11754                edit_action,
11755                window,
11756                cx,
11757            )
11758        });
11759
11760        let height = bp_prompt.update(cx, |this, cx| {
11761            this.prompt
11762                .update(cx, |prompt, cx| prompt.max_point(cx).row().0 + 1 + 2)
11763        });
11764        let cloned_prompt = bp_prompt.clone();
11765        let blocks = vec![BlockProperties {
11766            style: BlockStyle::Sticky,
11767            placement: BlockPlacement::Above(anchor),
11768            height: Some(height),
11769            render: Arc::new(move |cx| {
11770                *cloned_prompt.read(cx).editor_margins.lock() = *cx.margins;
11771                cloned_prompt.clone().into_any_element()
11772            }),
11773            priority: 0,
11774        }];
11775
11776        let focus_handle = bp_prompt.focus_handle(cx);
11777        window.focus(&focus_handle, cx);
11778
11779        let block_ids = self.insert_blocks(blocks, None, cx);
11780        bp_prompt.update(cx, |prompt, _| {
11781            prompt.add_block_ids(block_ids);
11782        });
11783    }
11784
11785    pub(crate) fn breakpoint_at_row(
11786        &self,
11787        row: u32,
11788        window: &mut Window,
11789        cx: &mut Context<Self>,
11790    ) -> Option<(Anchor, Breakpoint)> {
11791        let snapshot = self.snapshot(window, cx);
11792        let breakpoint_position = snapshot.buffer_snapshot().anchor_before(Point::new(row, 0));
11793
11794        self.breakpoint_at_anchor(breakpoint_position, &snapshot, cx)
11795    }
11796
11797    pub(crate) fn breakpoint_at_anchor(
11798        &self,
11799        breakpoint_position: Anchor,
11800        snapshot: &EditorSnapshot,
11801        cx: &mut Context<Self>,
11802    ) -> Option<(Anchor, Breakpoint)> {
11803        let buffer = self
11804            .buffer
11805            .read(cx)
11806            .buffer_for_anchor(breakpoint_position, cx)?;
11807
11808        let enclosing_excerpt = breakpoint_position.excerpt_id;
11809        let buffer_snapshot = buffer.read(cx).snapshot();
11810
11811        let row = buffer_snapshot
11812            .summary_for_anchor::<text::PointUtf16>(&breakpoint_position.text_anchor)
11813            .row;
11814
11815        let line_len = snapshot.buffer_snapshot().line_len(MultiBufferRow(row));
11816        let anchor_end = snapshot
11817            .buffer_snapshot()
11818            .anchor_after(Point::new(row, line_len));
11819
11820        self.breakpoint_store
11821            .as_ref()?
11822            .read_with(cx, |breakpoint_store, cx| {
11823                breakpoint_store
11824                    .breakpoints(
11825                        &buffer,
11826                        Some(breakpoint_position.text_anchor..anchor_end.text_anchor),
11827                        &buffer_snapshot,
11828                        cx,
11829                    )
11830                    .next()
11831                    .and_then(|(bp, _)| {
11832                        let breakpoint_row = buffer_snapshot
11833                            .summary_for_anchor::<text::PointUtf16>(&bp.position)
11834                            .row;
11835
11836                        if breakpoint_row == row {
11837                            snapshot
11838                                .buffer_snapshot()
11839                                .anchor_in_excerpt(enclosing_excerpt, bp.position)
11840                                .map(|position| (position, bp.bp.clone()))
11841                        } else {
11842                            None
11843                        }
11844                    })
11845            })
11846    }
11847
11848    pub fn edit_log_breakpoint(
11849        &mut self,
11850        _: &EditLogBreakpoint,
11851        window: &mut Window,
11852        cx: &mut Context<Self>,
11853    ) {
11854        if self.breakpoint_store.is_none() {
11855            return;
11856        }
11857
11858        for (anchor, breakpoint) in self.breakpoints_at_cursors(window, cx) {
11859            let breakpoint = breakpoint.unwrap_or_else(|| Breakpoint {
11860                message: None,
11861                state: BreakpointState::Enabled,
11862                condition: None,
11863                hit_condition: None,
11864            });
11865
11866            self.add_edit_breakpoint_block(
11867                anchor,
11868                &breakpoint,
11869                BreakpointPromptEditAction::Log,
11870                window,
11871                cx,
11872            );
11873        }
11874    }
11875
11876    fn breakpoints_at_cursors(
11877        &self,
11878        window: &mut Window,
11879        cx: &mut Context<Self>,
11880    ) -> Vec<(Anchor, Option<Breakpoint>)> {
11881        let snapshot = self.snapshot(window, cx);
11882        let cursors = self
11883            .selections
11884            .disjoint_anchors_arc()
11885            .iter()
11886            .map(|selection| {
11887                let cursor_position: Point = selection.head().to_point(&snapshot.buffer_snapshot());
11888
11889                let breakpoint_position = self
11890                    .breakpoint_at_row(cursor_position.row, window, cx)
11891                    .map(|bp| bp.0)
11892                    .unwrap_or_else(|| {
11893                        snapshot
11894                            .display_snapshot
11895                            .buffer_snapshot()
11896                            .anchor_after(Point::new(cursor_position.row, 0))
11897                    });
11898
11899                let breakpoint = self
11900                    .breakpoint_at_anchor(breakpoint_position, &snapshot, cx)
11901                    .map(|(anchor, breakpoint)| (anchor, Some(breakpoint)));
11902
11903                breakpoint.unwrap_or_else(|| (breakpoint_position, None))
11904            })
11905            // 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.
11906            .collect::<HashMap<Anchor, _>>();
11907
11908        cursors.into_iter().collect()
11909    }
11910
11911    pub fn enable_breakpoint(
11912        &mut self,
11913        _: &crate::actions::EnableBreakpoint,
11914        window: &mut Window,
11915        cx: &mut Context<Self>,
11916    ) {
11917        if self.breakpoint_store.is_none() {
11918            return;
11919        }
11920
11921        for (anchor, breakpoint) in self.breakpoints_at_cursors(window, cx) {
11922            let Some(breakpoint) = breakpoint.filter(|breakpoint| breakpoint.is_disabled()) else {
11923                continue;
11924            };
11925            self.edit_breakpoint_at_anchor(
11926                anchor,
11927                breakpoint,
11928                BreakpointEditAction::InvertState,
11929                cx,
11930            );
11931        }
11932    }
11933
11934    pub fn disable_breakpoint(
11935        &mut self,
11936        _: &crate::actions::DisableBreakpoint,
11937        window: &mut Window,
11938        cx: &mut Context<Self>,
11939    ) {
11940        if self.breakpoint_store.is_none() {
11941            return;
11942        }
11943
11944        for (anchor, breakpoint) in self.breakpoints_at_cursors(window, cx) {
11945            let Some(breakpoint) = breakpoint.filter(|breakpoint| breakpoint.is_enabled()) else {
11946                continue;
11947            };
11948            self.edit_breakpoint_at_anchor(
11949                anchor,
11950                breakpoint,
11951                BreakpointEditAction::InvertState,
11952                cx,
11953            );
11954        }
11955    }
11956
11957    pub fn toggle_breakpoint(
11958        &mut self,
11959        _: &crate::actions::ToggleBreakpoint,
11960        window: &mut Window,
11961        cx: &mut Context<Self>,
11962    ) {
11963        if self.breakpoint_store.is_none() {
11964            return;
11965        }
11966
11967        let snapshot = self.snapshot(window, cx);
11968        for (anchor, breakpoint) in self.breakpoints_at_cursors(window, cx) {
11969            if self.gutter_breakpoint_indicator.0.is_some() {
11970                let display_row = anchor
11971                    .to_point(snapshot.buffer_snapshot())
11972                    .to_display_point(&snapshot.display_snapshot)
11973                    .row();
11974                self.update_breakpoint_collision_on_toggle(
11975                    display_row,
11976                    &BreakpointEditAction::Toggle,
11977                );
11978            }
11979
11980            if let Some(breakpoint) = breakpoint {
11981                self.edit_breakpoint_at_anchor(
11982                    anchor,
11983                    breakpoint,
11984                    BreakpointEditAction::Toggle,
11985                    cx,
11986                );
11987            } else {
11988                self.edit_breakpoint_at_anchor(
11989                    anchor,
11990                    Breakpoint::new_standard(),
11991                    BreakpointEditAction::Toggle,
11992                    cx,
11993                );
11994            }
11995        }
11996    }
11997
11998    fn update_breakpoint_collision_on_toggle(
11999        &mut self,
12000        display_row: DisplayRow,
12001        edit_action: &BreakpointEditAction,
12002    ) {
12003        if let Some(ref mut breakpoint_indicator) = self.gutter_breakpoint_indicator.0 {
12004            if breakpoint_indicator.display_row == display_row
12005                && matches!(edit_action, BreakpointEditAction::Toggle)
12006            {
12007                breakpoint_indicator.collides_with_existing_breakpoint =
12008                    !breakpoint_indicator.collides_with_existing_breakpoint;
12009            }
12010        }
12011    }
12012
12013    pub fn edit_breakpoint_at_anchor(
12014        &mut self,
12015        breakpoint_position: Anchor,
12016        breakpoint: Breakpoint,
12017        edit_action: BreakpointEditAction,
12018        cx: &mut Context<Self>,
12019    ) {
12020        let Some(breakpoint_store) = &self.breakpoint_store else {
12021            return;
12022        };
12023
12024        let Some(buffer) = self
12025            .buffer
12026            .read(cx)
12027            .buffer_for_anchor(breakpoint_position, cx)
12028        else {
12029            return;
12030        };
12031
12032        breakpoint_store.update(cx, |breakpoint_store, cx| {
12033            breakpoint_store.toggle_breakpoint(
12034                buffer,
12035                BreakpointWithPosition {
12036                    position: breakpoint_position.text_anchor,
12037                    bp: breakpoint,
12038                },
12039                edit_action,
12040                cx,
12041            );
12042        });
12043
12044        cx.notify();
12045    }
12046
12047    #[cfg(any(test, feature = "test-support"))]
12048    pub fn breakpoint_store(&self) -> Option<Entity<BreakpointStore>> {
12049        self.breakpoint_store.clone()
12050    }
12051
12052    pub fn prepare_restore_change(
12053        &self,
12054        revert_changes: &mut HashMap<BufferId, Vec<(Range<text::Anchor>, Rope)>>,
12055        hunk: &MultiBufferDiffHunk,
12056        cx: &mut App,
12057    ) -> Option<()> {
12058        if hunk.is_created_file() {
12059            return None;
12060        }
12061        let buffer = self.buffer.read(cx);
12062        let diff = buffer.diff_for(hunk.buffer_id)?;
12063        let buffer = buffer.buffer(hunk.buffer_id)?;
12064        let buffer = buffer.read(cx);
12065        let original_text = diff
12066            .read(cx)
12067            .base_text(cx)
12068            .as_rope()
12069            .slice(hunk.diff_base_byte_range.start.0..hunk.diff_base_byte_range.end.0);
12070        let buffer_snapshot = buffer.snapshot();
12071        let buffer_revert_changes = revert_changes.entry(buffer.remote_id()).or_default();
12072        if let Err(i) = buffer_revert_changes.binary_search_by(|probe| {
12073            probe
12074                .0
12075                .start
12076                .cmp(&hunk.buffer_range.start, &buffer_snapshot)
12077                .then(probe.0.end.cmp(&hunk.buffer_range.end, &buffer_snapshot))
12078        }) {
12079            buffer_revert_changes.insert(i, (hunk.buffer_range.clone(), original_text));
12080            Some(())
12081        } else {
12082            None
12083        }
12084    }
12085
12086    pub fn reverse_lines(&mut self, _: &ReverseLines, window: &mut Window, cx: &mut Context<Self>) {
12087        self.manipulate_immutable_lines(window, cx, |lines| lines.reverse())
12088    }
12089
12090    pub fn shuffle_lines(&mut self, _: &ShuffleLines, window: &mut Window, cx: &mut Context<Self>) {
12091        self.manipulate_immutable_lines(window, cx, |lines| lines.shuffle(&mut rand::rng()))
12092    }
12093
12094    pub fn rotate_selections_forward(
12095        &mut self,
12096        _: &RotateSelectionsForward,
12097        window: &mut Window,
12098        cx: &mut Context<Self>,
12099    ) {
12100        self.rotate_selections(window, cx, false)
12101    }
12102
12103    pub fn rotate_selections_backward(
12104        &mut self,
12105        _: &RotateSelectionsBackward,
12106        window: &mut Window,
12107        cx: &mut Context<Self>,
12108    ) {
12109        self.rotate_selections(window, cx, true)
12110    }
12111
12112    fn rotate_selections(&mut self, window: &mut Window, cx: &mut Context<Self>, reverse: bool) {
12113        self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
12114        let display_snapshot = self.display_snapshot(cx);
12115        let selections = self.selections.all::<MultiBufferOffset>(&display_snapshot);
12116
12117        if selections.len() < 2 {
12118            return;
12119        }
12120
12121        let (edits, new_selections) = {
12122            let buffer = self.buffer.read(cx).read(cx);
12123            let has_selections = selections.iter().any(|s| !s.is_empty());
12124            if has_selections {
12125                let mut selected_texts: Vec<String> = selections
12126                    .iter()
12127                    .map(|selection| {
12128                        buffer
12129                            .text_for_range(selection.start..selection.end)
12130                            .collect()
12131                    })
12132                    .collect();
12133
12134                if reverse {
12135                    selected_texts.rotate_left(1);
12136                } else {
12137                    selected_texts.rotate_right(1);
12138                }
12139
12140                let mut offset_delta: i64 = 0;
12141                let mut new_selections = Vec::new();
12142                let edits: Vec<_> = selections
12143                    .iter()
12144                    .zip(selected_texts.iter())
12145                    .map(|(selection, new_text)| {
12146                        let old_len = (selection.end.0 - selection.start.0) as i64;
12147                        let new_len = new_text.len() as i64;
12148                        let adjusted_start =
12149                            MultiBufferOffset((selection.start.0 as i64 + offset_delta) as usize);
12150                        let adjusted_end =
12151                            MultiBufferOffset((adjusted_start.0 as i64 + new_len) as usize);
12152
12153                        new_selections.push(Selection {
12154                            id: selection.id,
12155                            start: adjusted_start,
12156                            end: adjusted_end,
12157                            reversed: selection.reversed,
12158                            goal: selection.goal,
12159                        });
12160
12161                        offset_delta += new_len - old_len;
12162                        (selection.start..selection.end, new_text.clone())
12163                    })
12164                    .collect();
12165                (edits, new_selections)
12166            } else {
12167                let mut all_rows: Vec<u32> = selections
12168                    .iter()
12169                    .map(|selection| buffer.offset_to_point(selection.start).row)
12170                    .collect();
12171                all_rows.sort_unstable();
12172                all_rows.dedup();
12173
12174                if all_rows.len() < 2 {
12175                    return;
12176                }
12177
12178                let line_ranges: Vec<Range<MultiBufferOffset>> = all_rows
12179                    .iter()
12180                    .map(|&row| {
12181                        let start = Point::new(row, 0);
12182                        let end = Point::new(row, buffer.line_len(MultiBufferRow(row)));
12183                        buffer.point_to_offset(start)..buffer.point_to_offset(end)
12184                    })
12185                    .collect();
12186
12187                let mut line_texts: Vec<String> = line_ranges
12188                    .iter()
12189                    .map(|range| buffer.text_for_range(range.clone()).collect())
12190                    .collect();
12191
12192                if reverse {
12193                    line_texts.rotate_left(1);
12194                } else {
12195                    line_texts.rotate_right(1);
12196                }
12197
12198                let edits = line_ranges
12199                    .iter()
12200                    .zip(line_texts.iter())
12201                    .map(|(range, new_text)| (range.clone(), new_text.clone()))
12202                    .collect();
12203
12204                let num_rows = all_rows.len();
12205                let row_to_index: std::collections::HashMap<u32, usize> = all_rows
12206                    .iter()
12207                    .enumerate()
12208                    .map(|(i, &row)| (row, i))
12209                    .collect();
12210
12211                // Compute new line start offsets after rotation (handles CRLF)
12212                let newline_len = line_ranges[1].start.0 - line_ranges[0].end.0;
12213                let first_line_start = line_ranges[0].start.0;
12214                let mut new_line_starts: Vec<usize> = vec![first_line_start];
12215                for text in line_texts.iter().take(num_rows - 1) {
12216                    let prev_start = *new_line_starts.last().unwrap();
12217                    new_line_starts.push(prev_start + text.len() + newline_len);
12218                }
12219
12220                let new_selections = selections
12221                    .iter()
12222                    .map(|selection| {
12223                        let point = buffer.offset_to_point(selection.start);
12224                        let old_index = row_to_index[&point.row];
12225                        let new_index = if reverse {
12226                            (old_index + num_rows - 1) % num_rows
12227                        } else {
12228                            (old_index + 1) % num_rows
12229                        };
12230                        let new_offset =
12231                            MultiBufferOffset(new_line_starts[new_index] + point.column as usize);
12232                        Selection {
12233                            id: selection.id,
12234                            start: new_offset,
12235                            end: new_offset,
12236                            reversed: selection.reversed,
12237                            goal: selection.goal,
12238                        }
12239                    })
12240                    .collect();
12241
12242                (edits, new_selections)
12243            }
12244        };
12245
12246        self.transact(window, cx, |this, window, cx| {
12247            this.buffer.update(cx, |buffer, cx| {
12248                buffer.edit(edits, None, cx);
12249            });
12250            this.change_selections(Default::default(), window, cx, |s| {
12251                s.select(new_selections);
12252            });
12253        });
12254    }
12255
12256    fn manipulate_lines<M>(
12257        &mut self,
12258        window: &mut Window,
12259        cx: &mut Context<Self>,
12260        mut manipulate: M,
12261    ) where
12262        M: FnMut(&str) -> LineManipulationResult,
12263    {
12264        self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
12265
12266        let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
12267        let buffer = self.buffer.read(cx).snapshot(cx);
12268
12269        let mut edits = Vec::new();
12270
12271        let selections = self.selections.all::<Point>(&display_map);
12272        let mut selections = selections.iter().peekable();
12273        let mut contiguous_row_selections = Vec::new();
12274        let mut new_selections = Vec::new();
12275        let mut added_lines = 0;
12276        let mut removed_lines = 0;
12277
12278        while let Some(selection) = selections.next() {
12279            let (start_row, end_row) = consume_contiguous_rows(
12280                &mut contiguous_row_selections,
12281                selection,
12282                &display_map,
12283                &mut selections,
12284            );
12285
12286            let start_point = Point::new(start_row.0, 0);
12287            let end_point = Point::new(
12288                end_row.previous_row().0,
12289                buffer.line_len(end_row.previous_row()),
12290            );
12291            let text = buffer
12292                .text_for_range(start_point..end_point)
12293                .collect::<String>();
12294
12295            let LineManipulationResult {
12296                new_text,
12297                line_count_before,
12298                line_count_after,
12299            } = manipulate(&text);
12300
12301            edits.push((start_point..end_point, new_text));
12302
12303            // Selections must change based on added and removed line count
12304            let start_row =
12305                MultiBufferRow(start_point.row + added_lines as u32 - removed_lines as u32);
12306            let end_row = MultiBufferRow(start_row.0 + line_count_after.saturating_sub(1) as u32);
12307            new_selections.push(Selection {
12308                id: selection.id,
12309                start: start_row,
12310                end: end_row,
12311                goal: SelectionGoal::None,
12312                reversed: selection.reversed,
12313            });
12314
12315            if line_count_after > line_count_before {
12316                added_lines += line_count_after - line_count_before;
12317            } else if line_count_before > line_count_after {
12318                removed_lines += line_count_before - line_count_after;
12319            }
12320        }
12321
12322        self.transact(window, cx, |this, window, cx| {
12323            let buffer = this.buffer.update(cx, |buffer, cx| {
12324                buffer.edit(edits, None, cx);
12325                buffer.snapshot(cx)
12326            });
12327
12328            // Recalculate offsets on newly edited buffer
12329            let new_selections = new_selections
12330                .iter()
12331                .map(|s| {
12332                    let start_point = Point::new(s.start.0, 0);
12333                    let end_point = Point::new(s.end.0, buffer.line_len(s.end));
12334                    Selection {
12335                        id: s.id,
12336                        start: buffer.point_to_offset(start_point),
12337                        end: buffer.point_to_offset(end_point),
12338                        goal: s.goal,
12339                        reversed: s.reversed,
12340                    }
12341                })
12342                .collect();
12343
12344            this.change_selections(Default::default(), window, cx, |s| {
12345                s.select(new_selections);
12346            });
12347
12348            this.request_autoscroll(Autoscroll::fit(), cx);
12349        });
12350    }
12351
12352    fn manipulate_immutable_lines<Fn>(
12353        &mut self,
12354        window: &mut Window,
12355        cx: &mut Context<Self>,
12356        mut callback: Fn,
12357    ) where
12358        Fn: FnMut(&mut Vec<&str>),
12359    {
12360        self.manipulate_lines(window, cx, |text| {
12361            let mut lines: Vec<&str> = text.split('\n').collect();
12362            let line_count_before = lines.len();
12363
12364            callback(&mut lines);
12365
12366            LineManipulationResult {
12367                new_text: lines.join("\n"),
12368                line_count_before,
12369                line_count_after: lines.len(),
12370            }
12371        });
12372    }
12373
12374    fn manipulate_mutable_lines<Fn>(
12375        &mut self,
12376        window: &mut Window,
12377        cx: &mut Context<Self>,
12378        mut callback: Fn,
12379    ) where
12380        Fn: FnMut(&mut Vec<Cow<'_, str>>),
12381    {
12382        self.manipulate_lines(window, cx, |text| {
12383            let mut lines: Vec<Cow<str>> = text.split('\n').map(Cow::from).collect();
12384            let line_count_before = lines.len();
12385
12386            callback(&mut lines);
12387
12388            LineManipulationResult {
12389                new_text: lines.join("\n"),
12390                line_count_before,
12391                line_count_after: lines.len(),
12392            }
12393        });
12394    }
12395
12396    pub fn convert_indentation_to_spaces(
12397        &mut self,
12398        _: &ConvertIndentationToSpaces,
12399        window: &mut Window,
12400        cx: &mut Context<Self>,
12401    ) {
12402        let settings = self.buffer.read(cx).language_settings(cx);
12403        let tab_size = settings.tab_size.get() as usize;
12404
12405        self.manipulate_mutable_lines(window, cx, |lines| {
12406            // Allocates a reasonably sized scratch buffer once for the whole loop
12407            let mut reindented_line = String::with_capacity(MAX_LINE_LEN);
12408            // Avoids recomputing spaces that could be inserted many times
12409            let space_cache: Vec<Vec<char>> = (1..=tab_size)
12410                .map(|n| IndentSize::spaces(n as u32).chars().collect())
12411                .collect();
12412
12413            for line in lines.iter_mut().filter(|line| !line.is_empty()) {
12414                let mut chars = line.as_ref().chars();
12415                let mut col = 0;
12416                let mut changed = false;
12417
12418                for ch in chars.by_ref() {
12419                    match ch {
12420                        ' ' => {
12421                            reindented_line.push(' ');
12422                            col += 1;
12423                        }
12424                        '\t' => {
12425                            // \t are converted to spaces depending on the current column
12426                            let spaces_len = tab_size - (col % tab_size);
12427                            reindented_line.extend(&space_cache[spaces_len - 1]);
12428                            col += spaces_len;
12429                            changed = true;
12430                        }
12431                        _ => {
12432                            // If we dont append before break, the character is consumed
12433                            reindented_line.push(ch);
12434                            break;
12435                        }
12436                    }
12437                }
12438
12439                if !changed {
12440                    reindented_line.clear();
12441                    continue;
12442                }
12443                // Append the rest of the line and replace old reference with new one
12444                reindented_line.extend(chars);
12445                *line = Cow::Owned(reindented_line.clone());
12446                reindented_line.clear();
12447            }
12448        });
12449    }
12450
12451    pub fn convert_indentation_to_tabs(
12452        &mut self,
12453        _: &ConvertIndentationToTabs,
12454        window: &mut Window,
12455        cx: &mut Context<Self>,
12456    ) {
12457        let settings = self.buffer.read(cx).language_settings(cx);
12458        let tab_size = settings.tab_size.get() as usize;
12459
12460        self.manipulate_mutable_lines(window, cx, |lines| {
12461            // Allocates a reasonably sized buffer once for the whole loop
12462            let mut reindented_line = String::with_capacity(MAX_LINE_LEN);
12463            // Avoids recomputing spaces that could be inserted many times
12464            let space_cache: Vec<Vec<char>> = (1..=tab_size)
12465                .map(|n| IndentSize::spaces(n as u32).chars().collect())
12466                .collect();
12467
12468            for line in lines.iter_mut().filter(|line| !line.is_empty()) {
12469                let mut chars = line.chars();
12470                let mut spaces_count = 0;
12471                let mut first_non_indent_char = None;
12472                let mut changed = false;
12473
12474                for ch in chars.by_ref() {
12475                    match ch {
12476                        ' ' => {
12477                            // Keep track of spaces. Append \t when we reach tab_size
12478                            spaces_count += 1;
12479                            changed = true;
12480                            if spaces_count == tab_size {
12481                                reindented_line.push('\t');
12482                                spaces_count = 0;
12483                            }
12484                        }
12485                        '\t' => {
12486                            reindented_line.push('\t');
12487                            spaces_count = 0;
12488                        }
12489                        _ => {
12490                            // Dont append it yet, we might have remaining spaces
12491                            first_non_indent_char = Some(ch);
12492                            break;
12493                        }
12494                    }
12495                }
12496
12497                if !changed {
12498                    reindented_line.clear();
12499                    continue;
12500                }
12501                // Remaining spaces that didn't make a full tab stop
12502                if spaces_count > 0 {
12503                    reindented_line.extend(&space_cache[spaces_count - 1]);
12504                }
12505                // If we consume an extra character that was not indentation, add it back
12506                if let Some(extra_char) = first_non_indent_char {
12507                    reindented_line.push(extra_char);
12508                }
12509                // Append the rest of the line and replace old reference with new one
12510                reindented_line.extend(chars);
12511                *line = Cow::Owned(reindented_line.clone());
12512                reindented_line.clear();
12513            }
12514        });
12515    }
12516
12517    pub fn convert_to_upper_case(
12518        &mut self,
12519        _: &ConvertToUpperCase,
12520        window: &mut Window,
12521        cx: &mut Context<Self>,
12522    ) {
12523        self.manipulate_text(window, cx, |text| text.to_uppercase())
12524    }
12525
12526    pub fn convert_to_lower_case(
12527        &mut self,
12528        _: &ConvertToLowerCase,
12529        window: &mut Window,
12530        cx: &mut Context<Self>,
12531    ) {
12532        self.manipulate_text(window, cx, |text| text.to_lowercase())
12533    }
12534
12535    pub fn convert_to_title_case(
12536        &mut self,
12537        _: &ConvertToTitleCase,
12538        window: &mut Window,
12539        cx: &mut Context<Self>,
12540    ) {
12541        self.manipulate_text(window, cx, |text| {
12542            text.split('\n')
12543                .map(|line| line.to_case(Case::Title))
12544                .join("\n")
12545        })
12546    }
12547
12548    pub fn convert_to_snake_case(
12549        &mut self,
12550        _: &ConvertToSnakeCase,
12551        window: &mut Window,
12552        cx: &mut Context<Self>,
12553    ) {
12554        self.manipulate_text(window, cx, |text| text.to_case(Case::Snake))
12555    }
12556
12557    pub fn convert_to_kebab_case(
12558        &mut self,
12559        _: &ConvertToKebabCase,
12560        window: &mut Window,
12561        cx: &mut Context<Self>,
12562    ) {
12563        self.manipulate_text(window, cx, |text| text.to_case(Case::Kebab))
12564    }
12565
12566    pub fn convert_to_upper_camel_case(
12567        &mut self,
12568        _: &ConvertToUpperCamelCase,
12569        window: &mut Window,
12570        cx: &mut Context<Self>,
12571    ) {
12572        self.manipulate_text(window, cx, |text| {
12573            text.split('\n')
12574                .map(|line| line.to_case(Case::UpperCamel))
12575                .join("\n")
12576        })
12577    }
12578
12579    pub fn convert_to_lower_camel_case(
12580        &mut self,
12581        _: &ConvertToLowerCamelCase,
12582        window: &mut Window,
12583        cx: &mut Context<Self>,
12584    ) {
12585        self.manipulate_text(window, cx, |text| text.to_case(Case::Camel))
12586    }
12587
12588    pub fn convert_to_opposite_case(
12589        &mut self,
12590        _: &ConvertToOppositeCase,
12591        window: &mut Window,
12592        cx: &mut Context<Self>,
12593    ) {
12594        self.manipulate_text(window, cx, |text| {
12595            text.chars()
12596                .fold(String::with_capacity(text.len()), |mut t, c| {
12597                    if c.is_uppercase() {
12598                        t.extend(c.to_lowercase());
12599                    } else {
12600                        t.extend(c.to_uppercase());
12601                    }
12602                    t
12603                })
12604        })
12605    }
12606
12607    pub fn convert_to_sentence_case(
12608        &mut self,
12609        _: &ConvertToSentenceCase,
12610        window: &mut Window,
12611        cx: &mut Context<Self>,
12612    ) {
12613        self.manipulate_text(window, cx, |text| text.to_case(Case::Sentence))
12614    }
12615
12616    pub fn toggle_case(&mut self, _: &ToggleCase, window: &mut Window, cx: &mut Context<Self>) {
12617        self.manipulate_text(window, cx, |text| {
12618            let has_upper_case_characters = text.chars().any(|c| c.is_uppercase());
12619            if has_upper_case_characters {
12620                text.to_lowercase()
12621            } else {
12622                text.to_uppercase()
12623            }
12624        })
12625    }
12626
12627    pub fn convert_to_rot13(
12628        &mut self,
12629        _: &ConvertToRot13,
12630        window: &mut Window,
12631        cx: &mut Context<Self>,
12632    ) {
12633        self.manipulate_text(window, cx, |text| {
12634            text.chars()
12635                .map(|c| match c {
12636                    'A'..='M' | 'a'..='m' => ((c as u8) + 13) as char,
12637                    'N'..='Z' | 'n'..='z' => ((c as u8) - 13) as char,
12638                    _ => c,
12639                })
12640                .collect()
12641        })
12642    }
12643
12644    pub fn convert_to_rot47(
12645        &mut self,
12646        _: &ConvertToRot47,
12647        window: &mut Window,
12648        cx: &mut Context<Self>,
12649    ) {
12650        self.manipulate_text(window, cx, |text| {
12651            text.chars()
12652                .map(|c| {
12653                    let code_point = c as u32;
12654                    if code_point >= 33 && code_point <= 126 {
12655                        return char::from_u32(33 + ((code_point + 14) % 94)).unwrap();
12656                    }
12657                    c
12658                })
12659                .collect()
12660        })
12661    }
12662
12663    fn manipulate_text<Fn>(&mut self, window: &mut Window, cx: &mut Context<Self>, mut callback: Fn)
12664    where
12665        Fn: FnMut(&str) -> String,
12666    {
12667        let buffer = self.buffer.read(cx).snapshot(cx);
12668
12669        let mut new_selections = Vec::new();
12670        let mut edits = Vec::new();
12671        let mut selection_adjustment = 0isize;
12672
12673        for selection in self.selections.all_adjusted(&self.display_snapshot(cx)) {
12674            let selection_is_empty = selection.is_empty();
12675
12676            let (start, end) = if selection_is_empty {
12677                let (word_range, _) = buffer.surrounding_word(selection.start, None);
12678                (word_range.start, word_range.end)
12679            } else {
12680                (
12681                    buffer.point_to_offset(selection.start),
12682                    buffer.point_to_offset(selection.end),
12683                )
12684            };
12685
12686            let text = buffer.text_for_range(start..end).collect::<String>();
12687            let old_length = text.len() as isize;
12688            let text = callback(&text);
12689
12690            new_selections.push(Selection {
12691                start: MultiBufferOffset((start.0 as isize - selection_adjustment) as usize),
12692                end: MultiBufferOffset(
12693                    ((start.0 + text.len()) as isize - selection_adjustment) as usize,
12694                ),
12695                goal: SelectionGoal::None,
12696                id: selection.id,
12697                reversed: selection.reversed,
12698            });
12699
12700            selection_adjustment += old_length - text.len() as isize;
12701
12702            edits.push((start..end, text));
12703        }
12704
12705        self.transact(window, cx, |this, window, cx| {
12706            this.buffer.update(cx, |buffer, cx| {
12707                buffer.edit(edits, None, cx);
12708            });
12709
12710            this.change_selections(Default::default(), window, cx, |s| {
12711                s.select(new_selections);
12712            });
12713
12714            this.request_autoscroll(Autoscroll::fit(), cx);
12715        });
12716    }
12717
12718    pub fn move_selection_on_drop(
12719        &mut self,
12720        selection: &Selection<Anchor>,
12721        target: DisplayPoint,
12722        is_cut: bool,
12723        window: &mut Window,
12724        cx: &mut Context<Self>,
12725    ) {
12726        let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
12727        let buffer = display_map.buffer_snapshot();
12728        let mut edits = Vec::new();
12729        let insert_point = display_map
12730            .clip_point(target, Bias::Left)
12731            .to_point(&display_map);
12732        let text = buffer
12733            .text_for_range(selection.start..selection.end)
12734            .collect::<String>();
12735        if is_cut {
12736            edits.push(((selection.start..selection.end), String::new()));
12737        }
12738        let insert_anchor = buffer.anchor_before(insert_point);
12739        edits.push(((insert_anchor..insert_anchor), text));
12740        let last_edit_start = insert_anchor.bias_left(buffer);
12741        let last_edit_end = insert_anchor.bias_right(buffer);
12742        self.transact(window, cx, |this, window, cx| {
12743            this.buffer.update(cx, |buffer, cx| {
12744                buffer.edit(edits, None, cx);
12745            });
12746            this.change_selections(Default::default(), window, cx, |s| {
12747                s.select_anchor_ranges([last_edit_start..last_edit_end]);
12748            });
12749        });
12750    }
12751
12752    pub fn clear_selection_drag_state(&mut self) {
12753        self.selection_drag_state = SelectionDragState::None;
12754    }
12755
12756    pub fn duplicate(
12757        &mut self,
12758        upwards: bool,
12759        whole_lines: bool,
12760        window: &mut Window,
12761        cx: &mut Context<Self>,
12762    ) {
12763        self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
12764
12765        let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
12766        let buffer = display_map.buffer_snapshot();
12767        let selections = self.selections.all::<Point>(&display_map);
12768
12769        let mut edits = Vec::new();
12770        let mut selections_iter = selections.iter().peekable();
12771        while let Some(selection) = selections_iter.next() {
12772            let mut rows = selection.spanned_rows(false, &display_map);
12773            // duplicate line-wise
12774            if whole_lines || selection.start == selection.end {
12775                // Avoid duplicating the same lines twice.
12776                while let Some(next_selection) = selections_iter.peek() {
12777                    let next_rows = next_selection.spanned_rows(false, &display_map);
12778                    if next_rows.start < rows.end {
12779                        rows.end = next_rows.end;
12780                        selections_iter.next().unwrap();
12781                    } else {
12782                        break;
12783                    }
12784                }
12785
12786                // Copy the text from the selected row region and splice it either at the start
12787                // or end of the region.
12788                let start = Point::new(rows.start.0, 0);
12789                let end = Point::new(
12790                    rows.end.previous_row().0,
12791                    buffer.line_len(rows.end.previous_row()),
12792                );
12793
12794                let mut text = buffer.text_for_range(start..end).collect::<String>();
12795
12796                let insert_location = if upwards {
12797                    // When duplicating upward, we need to insert before the current line.
12798                    // If we're on the last line and it doesn't end with a newline,
12799                    // we need to add a newline before the duplicated content.
12800                    let needs_leading_newline = rows.end.0 >= buffer.max_point().row
12801                        && buffer.max_point().column > 0
12802                        && !text.ends_with('\n');
12803
12804                    if needs_leading_newline {
12805                        text.insert(0, '\n');
12806                        end
12807                    } else {
12808                        text.push('\n');
12809                        Point::new(rows.start.0, 0)
12810                    }
12811                } else {
12812                    text.push('\n');
12813                    start
12814                };
12815                edits.push((insert_location..insert_location, text));
12816            } else {
12817                // duplicate character-wise
12818                let start = selection.start;
12819                let end = selection.end;
12820                let text = buffer.text_for_range(start..end).collect::<String>();
12821                edits.push((selection.end..selection.end, text));
12822            }
12823        }
12824
12825        self.transact(window, cx, |this, window, cx| {
12826            this.buffer.update(cx, |buffer, cx| {
12827                buffer.edit(edits, None, cx);
12828            });
12829
12830            // When duplicating upward with whole lines, move the cursor to the duplicated line
12831            if upwards && whole_lines {
12832                let display_map = this.display_map.update(cx, |map, cx| map.snapshot(cx));
12833
12834                this.change_selections(SelectionEffects::no_scroll(), window, cx, |s| {
12835                    let mut new_ranges = Vec::new();
12836                    let selections = s.all::<Point>(&display_map);
12837                    let mut selections_iter = selections.iter().peekable();
12838
12839                    while let Some(first_selection) = selections_iter.next() {
12840                        // Group contiguous selections together to find the total row span
12841                        let mut group_selections = vec![first_selection];
12842                        let mut rows = first_selection.spanned_rows(false, &display_map);
12843
12844                        while let Some(next_selection) = selections_iter.peek() {
12845                            let next_rows = next_selection.spanned_rows(false, &display_map);
12846                            if next_rows.start < rows.end {
12847                                rows.end = next_rows.end;
12848                                group_selections.push(selections_iter.next().unwrap());
12849                            } else {
12850                                break;
12851                            }
12852                        }
12853
12854                        let row_count = rows.end.0 - rows.start.0;
12855
12856                        // Move all selections in this group up by the total number of duplicated rows
12857                        for selection in group_selections {
12858                            let new_start = Point::new(
12859                                selection.start.row.saturating_sub(row_count),
12860                                selection.start.column,
12861                            );
12862
12863                            let new_end = Point::new(
12864                                selection.end.row.saturating_sub(row_count),
12865                                selection.end.column,
12866                            );
12867
12868                            new_ranges.push(new_start..new_end);
12869                        }
12870                    }
12871
12872                    s.select_ranges(new_ranges);
12873                });
12874            }
12875
12876            this.request_autoscroll(Autoscroll::fit(), cx);
12877        });
12878    }
12879
12880    pub fn duplicate_line_up(
12881        &mut self,
12882        _: &DuplicateLineUp,
12883        window: &mut Window,
12884        cx: &mut Context<Self>,
12885    ) {
12886        self.duplicate(true, true, window, cx);
12887    }
12888
12889    pub fn duplicate_line_down(
12890        &mut self,
12891        _: &DuplicateLineDown,
12892        window: &mut Window,
12893        cx: &mut Context<Self>,
12894    ) {
12895        self.duplicate(false, true, window, cx);
12896    }
12897
12898    pub fn duplicate_selection(
12899        &mut self,
12900        _: &DuplicateSelection,
12901        window: &mut Window,
12902        cx: &mut Context<Self>,
12903    ) {
12904        self.duplicate(false, false, window, cx);
12905    }
12906
12907    pub fn move_line_up(&mut self, _: &MoveLineUp, window: &mut Window, cx: &mut Context<Self>) {
12908        self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
12909        if self.mode.is_single_line() {
12910            cx.propagate();
12911            return;
12912        }
12913
12914        let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
12915        let buffer = self.buffer.read(cx).snapshot(cx);
12916
12917        let mut edits = Vec::new();
12918        let mut unfold_ranges = Vec::new();
12919        let mut refold_creases = Vec::new();
12920
12921        let selections = self.selections.all::<Point>(&display_map);
12922        let mut selections = selections.iter().peekable();
12923        let mut contiguous_row_selections = Vec::new();
12924        let mut new_selections = Vec::new();
12925
12926        while let Some(selection) = selections.next() {
12927            // Find all the selections that span a contiguous row range
12928            let (start_row, end_row) = consume_contiguous_rows(
12929                &mut contiguous_row_selections,
12930                selection,
12931                &display_map,
12932                &mut selections,
12933            );
12934
12935            // Move the text spanned by the row range to be before the line preceding the row range
12936            if start_row.0 > 0 {
12937                let range_to_move = Point::new(
12938                    start_row.previous_row().0,
12939                    buffer.line_len(start_row.previous_row()),
12940                )
12941                    ..Point::new(
12942                        end_row.previous_row().0,
12943                        buffer.line_len(end_row.previous_row()),
12944                    );
12945                let insertion_point = display_map
12946                    .prev_line_boundary(Point::new(start_row.previous_row().0, 0))
12947                    .0;
12948
12949                // Don't move lines across excerpts
12950                if buffer
12951                    .excerpt_containing(insertion_point..range_to_move.end)
12952                    .is_some()
12953                {
12954                    let text = buffer
12955                        .text_for_range(range_to_move.clone())
12956                        .flat_map(|s| s.chars())
12957                        .skip(1)
12958                        .chain(['\n'])
12959                        .collect::<String>();
12960
12961                    edits.push((
12962                        buffer.anchor_after(range_to_move.start)
12963                            ..buffer.anchor_before(range_to_move.end),
12964                        String::new(),
12965                    ));
12966                    let insertion_anchor = buffer.anchor_after(insertion_point);
12967                    edits.push((insertion_anchor..insertion_anchor, text));
12968
12969                    let row_delta = range_to_move.start.row - insertion_point.row + 1;
12970
12971                    // Move selections up
12972                    new_selections.extend(contiguous_row_selections.drain(..).map(
12973                        |mut selection| {
12974                            selection.start.row -= row_delta;
12975                            selection.end.row -= row_delta;
12976                            selection
12977                        },
12978                    ));
12979
12980                    // Move folds up
12981                    unfold_ranges.push(range_to_move.clone());
12982                    for fold in display_map.folds_in_range(
12983                        buffer.anchor_before(range_to_move.start)
12984                            ..buffer.anchor_after(range_to_move.end),
12985                    ) {
12986                        let mut start = fold.range.start.to_point(&buffer);
12987                        let mut end = fold.range.end.to_point(&buffer);
12988                        start.row -= row_delta;
12989                        end.row -= row_delta;
12990                        refold_creases.push(Crease::simple(start..end, fold.placeholder.clone()));
12991                    }
12992                }
12993            }
12994
12995            // If we didn't move line(s), preserve the existing selections
12996            new_selections.append(&mut contiguous_row_selections);
12997        }
12998
12999        self.transact(window, cx, |this, window, cx| {
13000            this.unfold_ranges(&unfold_ranges, true, true, cx);
13001            this.buffer.update(cx, |buffer, cx| {
13002                for (range, text) in edits {
13003                    buffer.edit([(range, text)], None, cx);
13004                }
13005            });
13006            this.fold_creases(refold_creases, true, window, cx);
13007            this.change_selections(Default::default(), window, cx, |s| {
13008                s.select(new_selections);
13009            })
13010        });
13011    }
13012
13013    pub fn move_line_down(
13014        &mut self,
13015        _: &MoveLineDown,
13016        window: &mut Window,
13017        cx: &mut Context<Self>,
13018    ) {
13019        self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
13020        if self.mode.is_single_line() {
13021            cx.propagate();
13022            return;
13023        }
13024
13025        let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
13026        let buffer = self.buffer.read(cx).snapshot(cx);
13027
13028        let mut edits = Vec::new();
13029        let mut unfold_ranges = Vec::new();
13030        let mut refold_creases = Vec::new();
13031
13032        let selections = self.selections.all::<Point>(&display_map);
13033        let mut selections = selections.iter().peekable();
13034        let mut contiguous_row_selections = Vec::new();
13035        let mut new_selections = Vec::new();
13036
13037        while let Some(selection) = selections.next() {
13038            // Find all the selections that span a contiguous row range
13039            let (start_row, end_row) = consume_contiguous_rows(
13040                &mut contiguous_row_selections,
13041                selection,
13042                &display_map,
13043                &mut selections,
13044            );
13045
13046            // Move the text spanned by the row range to be after the last line of the row range
13047            if end_row.0 <= buffer.max_point().row {
13048                let range_to_move =
13049                    MultiBufferPoint::new(start_row.0, 0)..MultiBufferPoint::new(end_row.0, 0);
13050                let insertion_point = display_map
13051                    .next_line_boundary(MultiBufferPoint::new(end_row.0, 0))
13052                    .0;
13053
13054                // Don't move lines across excerpt boundaries
13055                if buffer
13056                    .excerpt_containing(range_to_move.start..insertion_point)
13057                    .is_some()
13058                {
13059                    let mut text = String::from("\n");
13060                    text.extend(buffer.text_for_range(range_to_move.clone()));
13061                    text.pop(); // Drop trailing newline
13062                    edits.push((
13063                        buffer.anchor_after(range_to_move.start)
13064                            ..buffer.anchor_before(range_to_move.end),
13065                        String::new(),
13066                    ));
13067                    let insertion_anchor = buffer.anchor_after(insertion_point);
13068                    edits.push((insertion_anchor..insertion_anchor, text));
13069
13070                    let row_delta = insertion_point.row - range_to_move.end.row + 1;
13071
13072                    // Move selections down
13073                    new_selections.extend(contiguous_row_selections.drain(..).map(
13074                        |mut selection| {
13075                            selection.start.row += row_delta;
13076                            selection.end.row += row_delta;
13077                            selection
13078                        },
13079                    ));
13080
13081                    // Move folds down
13082                    unfold_ranges.push(range_to_move.clone());
13083                    for fold in display_map.folds_in_range(
13084                        buffer.anchor_before(range_to_move.start)
13085                            ..buffer.anchor_after(range_to_move.end),
13086                    ) {
13087                        let mut start = fold.range.start.to_point(&buffer);
13088                        let mut end = fold.range.end.to_point(&buffer);
13089                        start.row += row_delta;
13090                        end.row += row_delta;
13091                        refold_creases.push(Crease::simple(start..end, fold.placeholder.clone()));
13092                    }
13093                }
13094            }
13095
13096            // If we didn't move line(s), preserve the existing selections
13097            new_selections.append(&mut contiguous_row_selections);
13098        }
13099
13100        self.transact(window, cx, |this, window, cx| {
13101            this.unfold_ranges(&unfold_ranges, true, true, cx);
13102            this.buffer.update(cx, |buffer, cx| {
13103                for (range, text) in edits {
13104                    buffer.edit([(range, text)], None, cx);
13105                }
13106            });
13107            this.fold_creases(refold_creases, true, window, cx);
13108            this.change_selections(Default::default(), window, cx, |s| s.select(new_selections));
13109        });
13110    }
13111
13112    pub fn transpose(&mut self, _: &Transpose, window: &mut Window, cx: &mut Context<Self>) {
13113        self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
13114        let text_layout_details = &self.text_layout_details(window, cx);
13115        self.transact(window, cx, |this, window, cx| {
13116            let edits = this.change_selections(Default::default(), window, cx, |s| {
13117                let mut edits: Vec<(Range<MultiBufferOffset>, String)> = Default::default();
13118                s.move_with(&mut |display_map, selection| {
13119                    if !selection.is_empty() {
13120                        return;
13121                    }
13122
13123                    let mut head = selection.head();
13124                    let mut transpose_offset = head.to_offset(display_map, Bias::Right);
13125                    if head.column() == display_map.line_len(head.row()) {
13126                        transpose_offset = display_map
13127                            .buffer_snapshot()
13128                            .clip_offset(transpose_offset.saturating_sub_usize(1), Bias::Left);
13129                    }
13130
13131                    if transpose_offset == MultiBufferOffset(0) {
13132                        return;
13133                    }
13134
13135                    *head.column_mut() += 1;
13136                    head = display_map.clip_point(head, Bias::Right);
13137                    let goal = SelectionGoal::HorizontalPosition(
13138                        display_map
13139                            .x_for_display_point(head, text_layout_details)
13140                            .into(),
13141                    );
13142                    selection.collapse_to(head, goal);
13143
13144                    let transpose_start = display_map
13145                        .buffer_snapshot()
13146                        .clip_offset(transpose_offset.saturating_sub_usize(1), Bias::Left);
13147                    if edits.last().is_none_or(|e| e.0.end <= transpose_start) {
13148                        let transpose_end = display_map
13149                            .buffer_snapshot()
13150                            .clip_offset(transpose_offset + 1usize, Bias::Right);
13151                        if let Some(ch) = display_map
13152                            .buffer_snapshot()
13153                            .chars_at(transpose_start)
13154                            .next()
13155                        {
13156                            edits.push((transpose_start..transpose_offset, String::new()));
13157                            edits.push((transpose_end..transpose_end, ch.to_string()));
13158                        }
13159                    }
13160                });
13161                edits
13162            });
13163            this.buffer
13164                .update(cx, |buffer, cx| buffer.edit(edits, None, cx));
13165            let selections = this
13166                .selections
13167                .all::<MultiBufferOffset>(&this.display_snapshot(cx));
13168            this.change_selections(Default::default(), window, cx, |s| {
13169                s.select(selections);
13170            });
13171        });
13172    }
13173
13174    pub fn rewrap(&mut self, _: &Rewrap, _: &mut Window, cx: &mut Context<Self>) {
13175        self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
13176        if self.mode.is_single_line() {
13177            cx.propagate();
13178            return;
13179        }
13180
13181        self.rewrap_impl(RewrapOptions::default(), cx)
13182    }
13183
13184    pub fn rewrap_impl(&mut self, options: RewrapOptions, cx: &mut Context<Self>) {
13185        let buffer = self.buffer.read(cx).snapshot(cx);
13186        let selections = self.selections.all::<Point>(&self.display_snapshot(cx));
13187
13188        #[derive(Clone, Debug, PartialEq)]
13189        enum CommentFormat {
13190            /// single line comment, with prefix for line
13191            Line(String),
13192            /// single line within a block comment, with prefix for line
13193            BlockLine(String),
13194            /// a single line of a block comment that includes the initial delimiter
13195            BlockCommentWithStart(BlockCommentConfig),
13196            /// a single line of a block comment that includes the ending delimiter
13197            BlockCommentWithEnd(BlockCommentConfig),
13198        }
13199
13200        // Split selections to respect paragraph, indent, and comment prefix boundaries.
13201        let wrap_ranges = selections.into_iter().flat_map(|selection| {
13202            let mut non_blank_rows_iter = (selection.start.row..=selection.end.row)
13203                .filter(|row| !buffer.is_line_blank(MultiBufferRow(*row)))
13204                .peekable();
13205
13206            let first_row = if let Some(&row) = non_blank_rows_iter.peek() {
13207                row
13208            } else {
13209                return Vec::new();
13210            };
13211
13212            let language_settings = buffer.language_settings_at(selection.head(), cx);
13213            let language_scope = buffer.language_scope_at(selection.head());
13214
13215            let indent_and_prefix_for_row =
13216                |row: u32| -> (IndentSize, Option<CommentFormat>, Option<String>) {
13217                    let indent = buffer.indent_size_for_line(MultiBufferRow(row));
13218                    let (comment_prefix, rewrap_prefix) = if let Some(language_scope) =
13219                        &language_scope
13220                    {
13221                        let indent_end = Point::new(row, indent.len);
13222                        let line_end = Point::new(row, buffer.line_len(MultiBufferRow(row)));
13223                        let line_text_after_indent = buffer
13224                            .text_for_range(indent_end..line_end)
13225                            .collect::<String>();
13226
13227                        let is_within_comment_override = buffer
13228                            .language_scope_at(indent_end)
13229                            .is_some_and(|scope| scope.override_name() == Some("comment"));
13230                        let comment_delimiters = if is_within_comment_override {
13231                            // we are within a comment syntax node, but we don't
13232                            // yet know what kind of comment: block, doc or line
13233                            match (
13234                                language_scope.documentation_comment(),
13235                                language_scope.block_comment(),
13236                            ) {
13237                                (Some(config), _) | (_, Some(config))
13238                                    if buffer.contains_str_at(indent_end, &config.start) =>
13239                                {
13240                                    Some(CommentFormat::BlockCommentWithStart(config.clone()))
13241                                }
13242                                (Some(config), _) | (_, Some(config))
13243                                    if line_text_after_indent.ends_with(config.end.as_ref()) =>
13244                                {
13245                                    Some(CommentFormat::BlockCommentWithEnd(config.clone()))
13246                                }
13247                                (Some(config), _) | (_, Some(config))
13248                                    if buffer.contains_str_at(indent_end, &config.prefix) =>
13249                                {
13250                                    Some(CommentFormat::BlockLine(config.prefix.to_string()))
13251                                }
13252                                (_, _) => language_scope
13253                                    .line_comment_prefixes()
13254                                    .iter()
13255                                    .find(|prefix| buffer.contains_str_at(indent_end, prefix))
13256                                    .map(|prefix| CommentFormat::Line(prefix.to_string())),
13257                            }
13258                        } else {
13259                            // we not in an overridden comment node, but we may
13260                            // be within a non-overridden line comment node
13261                            language_scope
13262                                .line_comment_prefixes()
13263                                .iter()
13264                                .find(|prefix| buffer.contains_str_at(indent_end, prefix))
13265                                .map(|prefix| CommentFormat::Line(prefix.to_string()))
13266                        };
13267
13268                        let rewrap_prefix = language_scope
13269                            .rewrap_prefixes()
13270                            .iter()
13271                            .find_map(|prefix_regex| {
13272                                prefix_regex.find(&line_text_after_indent).map(|mat| {
13273                                    if mat.start() == 0 {
13274                                        Some(mat.as_str().to_string())
13275                                    } else {
13276                                        None
13277                                    }
13278                                })
13279                            })
13280                            .flatten();
13281                        (comment_delimiters, rewrap_prefix)
13282                    } else {
13283                        (None, None)
13284                    };
13285                    (indent, comment_prefix, rewrap_prefix)
13286                };
13287
13288            let mut ranges = Vec::new();
13289            let from_empty_selection = selection.is_empty();
13290
13291            let mut current_range_start = first_row;
13292            let mut prev_row = first_row;
13293            let (
13294                mut current_range_indent,
13295                mut current_range_comment_delimiters,
13296                mut current_range_rewrap_prefix,
13297            ) = indent_and_prefix_for_row(first_row);
13298
13299            for row in non_blank_rows_iter.skip(1) {
13300                let has_paragraph_break = row > prev_row + 1;
13301
13302                let (row_indent, row_comment_delimiters, row_rewrap_prefix) =
13303                    indent_and_prefix_for_row(row);
13304
13305                let has_indent_change = row_indent != current_range_indent;
13306                let has_comment_change = row_comment_delimiters != current_range_comment_delimiters;
13307
13308                let has_boundary_change = has_comment_change
13309                    || row_rewrap_prefix.is_some()
13310                    || (has_indent_change && current_range_comment_delimiters.is_some());
13311
13312                if has_paragraph_break || has_boundary_change {
13313                    ranges.push((
13314                        language_settings.clone(),
13315                        Point::new(current_range_start, 0)
13316                            ..Point::new(prev_row, buffer.line_len(MultiBufferRow(prev_row))),
13317                        current_range_indent,
13318                        current_range_comment_delimiters.clone(),
13319                        current_range_rewrap_prefix.clone(),
13320                        from_empty_selection,
13321                    ));
13322                    current_range_start = row;
13323                    current_range_indent = row_indent;
13324                    current_range_comment_delimiters = row_comment_delimiters;
13325                    current_range_rewrap_prefix = row_rewrap_prefix;
13326                }
13327                prev_row = row;
13328            }
13329
13330            ranges.push((
13331                language_settings.clone(),
13332                Point::new(current_range_start, 0)
13333                    ..Point::new(prev_row, buffer.line_len(MultiBufferRow(prev_row))),
13334                current_range_indent,
13335                current_range_comment_delimiters,
13336                current_range_rewrap_prefix,
13337                from_empty_selection,
13338            ));
13339
13340            ranges
13341        });
13342
13343        let mut edits = Vec::new();
13344        let mut rewrapped_row_ranges = Vec::<RangeInclusive<u32>>::new();
13345
13346        for (
13347            language_settings,
13348            wrap_range,
13349            mut indent_size,
13350            comment_prefix,
13351            rewrap_prefix,
13352            from_empty_selection,
13353        ) in wrap_ranges
13354        {
13355            let mut start_row = wrap_range.start.row;
13356            let mut end_row = wrap_range.end.row;
13357
13358            // Skip selections that overlap with a range that has already been rewrapped.
13359            let selection_range = start_row..end_row;
13360            if rewrapped_row_ranges
13361                .iter()
13362                .any(|range| range.overlaps(&selection_range))
13363            {
13364                continue;
13365            }
13366
13367            let tab_size = language_settings.tab_size;
13368
13369            let (line_prefix, inside_comment) = match &comment_prefix {
13370                Some(CommentFormat::Line(prefix) | CommentFormat::BlockLine(prefix)) => {
13371                    (Some(prefix.as_str()), true)
13372                }
13373                Some(CommentFormat::BlockCommentWithEnd(BlockCommentConfig { prefix, .. })) => {
13374                    (Some(prefix.as_ref()), true)
13375                }
13376                Some(CommentFormat::BlockCommentWithStart(BlockCommentConfig {
13377                    start: _,
13378                    end: _,
13379                    prefix,
13380                    tab_size,
13381                })) => {
13382                    indent_size.len += tab_size;
13383                    (Some(prefix.as_ref()), true)
13384                }
13385                None => (None, false),
13386            };
13387            let indent_prefix = indent_size.chars().collect::<String>();
13388            let line_prefix = format!("{indent_prefix}{}", line_prefix.unwrap_or(""));
13389
13390            let allow_rewrap_based_on_language = match language_settings.allow_rewrap {
13391                RewrapBehavior::InComments => inside_comment,
13392                RewrapBehavior::InSelections => !wrap_range.is_empty(),
13393                RewrapBehavior::Anywhere => true,
13394            };
13395
13396            let should_rewrap = options.override_language_settings
13397                || allow_rewrap_based_on_language
13398                || self.hard_wrap.is_some();
13399            if !should_rewrap {
13400                continue;
13401            }
13402
13403            if from_empty_selection {
13404                'expand_upwards: while start_row > 0 {
13405                    let prev_row = start_row - 1;
13406                    if buffer.contains_str_at(Point::new(prev_row, 0), &line_prefix)
13407                        && buffer.line_len(MultiBufferRow(prev_row)) as usize > line_prefix.len()
13408                        && !buffer.is_line_blank(MultiBufferRow(prev_row))
13409                    {
13410                        start_row = prev_row;
13411                    } else {
13412                        break 'expand_upwards;
13413                    }
13414                }
13415
13416                'expand_downwards: while end_row < buffer.max_point().row {
13417                    let next_row = end_row + 1;
13418                    if buffer.contains_str_at(Point::new(next_row, 0), &line_prefix)
13419                        && buffer.line_len(MultiBufferRow(next_row)) as usize > line_prefix.len()
13420                        && !buffer.is_line_blank(MultiBufferRow(next_row))
13421                    {
13422                        end_row = next_row;
13423                    } else {
13424                        break 'expand_downwards;
13425                    }
13426                }
13427            }
13428
13429            let start = Point::new(start_row, 0);
13430            let start_offset = ToOffset::to_offset(&start, &buffer);
13431            let end = Point::new(end_row, buffer.line_len(MultiBufferRow(end_row)));
13432            let selection_text = buffer.text_for_range(start..end).collect::<String>();
13433            let mut first_line_delimiter = None;
13434            let mut last_line_delimiter = None;
13435            let Some(lines_without_prefixes) = selection_text
13436                .lines()
13437                .enumerate()
13438                .map(|(ix, line)| {
13439                    let line_trimmed = line.trim_start();
13440                    if rewrap_prefix.is_some() && ix > 0 {
13441                        Ok(line_trimmed)
13442                    } else if let Some(
13443                        CommentFormat::BlockCommentWithStart(BlockCommentConfig {
13444                            start,
13445                            prefix,
13446                            end,
13447                            tab_size,
13448                        })
13449                        | CommentFormat::BlockCommentWithEnd(BlockCommentConfig {
13450                            start,
13451                            prefix,
13452                            end,
13453                            tab_size,
13454                        }),
13455                    ) = &comment_prefix
13456                    {
13457                        let line_trimmed = line_trimmed
13458                            .strip_prefix(start.as_ref())
13459                            .map(|s| {
13460                                let mut indent_size = indent_size;
13461                                indent_size.len -= tab_size;
13462                                let indent_prefix: String = indent_size.chars().collect();
13463                                first_line_delimiter = Some((indent_prefix, start));
13464                                s.trim_start()
13465                            })
13466                            .unwrap_or(line_trimmed);
13467                        let line_trimmed = line_trimmed
13468                            .strip_suffix(end.as_ref())
13469                            .map(|s| {
13470                                last_line_delimiter = Some(end);
13471                                s.trim_end()
13472                            })
13473                            .unwrap_or(line_trimmed);
13474                        let line_trimmed = line_trimmed
13475                            .strip_prefix(prefix.as_ref())
13476                            .unwrap_or(line_trimmed);
13477                        Ok(line_trimmed)
13478                    } else if let Some(CommentFormat::BlockLine(prefix)) = &comment_prefix {
13479                        line_trimmed.strip_prefix(prefix).with_context(|| {
13480                            format!("line did not start with prefix {prefix:?}: {line:?}")
13481                        })
13482                    } else {
13483                        line_trimmed
13484                            .strip_prefix(&line_prefix.trim_start())
13485                            .with_context(|| {
13486                                format!("line did not start with prefix {line_prefix:?}: {line:?}")
13487                            })
13488                    }
13489                })
13490                .collect::<Result<Vec<_>, _>>()
13491                .log_err()
13492            else {
13493                continue;
13494            };
13495
13496            let wrap_column = self.hard_wrap.unwrap_or_else(|| {
13497                buffer
13498                    .language_settings_at(Point::new(start_row, 0), cx)
13499                    .preferred_line_length as usize
13500            });
13501
13502            let subsequent_lines_prefix = if let Some(rewrap_prefix_str) = &rewrap_prefix {
13503                format!("{}{}", indent_prefix, " ".repeat(rewrap_prefix_str.len()))
13504            } else {
13505                line_prefix.clone()
13506            };
13507
13508            let wrapped_text = {
13509                let mut wrapped_text = wrap_with_prefix(
13510                    line_prefix,
13511                    subsequent_lines_prefix,
13512                    lines_without_prefixes.join("\n"),
13513                    wrap_column,
13514                    tab_size,
13515                    options.preserve_existing_whitespace,
13516                );
13517
13518                if let Some((indent, delimiter)) = first_line_delimiter {
13519                    wrapped_text = format!("{indent}{delimiter}\n{wrapped_text}");
13520                }
13521                if let Some(last_line) = last_line_delimiter {
13522                    wrapped_text = format!("{wrapped_text}\n{indent_prefix}{last_line}");
13523                }
13524
13525                wrapped_text
13526            };
13527
13528            // TODO: should always use char-based diff while still supporting cursor behavior that
13529            // matches vim.
13530            let mut diff_options = DiffOptions::default();
13531            if options.override_language_settings {
13532                diff_options.max_word_diff_len = 0;
13533                diff_options.max_word_diff_line_count = 0;
13534            } else {
13535                diff_options.max_word_diff_len = usize::MAX;
13536                diff_options.max_word_diff_line_count = usize::MAX;
13537            }
13538
13539            for (old_range, new_text) in
13540                text_diff_with_options(&selection_text, &wrapped_text, diff_options)
13541            {
13542                let edit_start = buffer.anchor_after(start_offset + old_range.start);
13543                let edit_end = buffer.anchor_after(start_offset + old_range.end);
13544                edits.push((edit_start..edit_end, new_text));
13545            }
13546
13547            rewrapped_row_ranges.push(start_row..=end_row);
13548        }
13549
13550        self.buffer
13551            .update(cx, |buffer, cx| buffer.edit(edits, None, cx));
13552    }
13553
13554    pub fn cut_common(
13555        &mut self,
13556        cut_no_selection_line: bool,
13557        window: &mut Window,
13558        cx: &mut Context<Self>,
13559    ) -> ClipboardItem {
13560        let mut text = String::new();
13561        let buffer = self.buffer.read(cx).snapshot(cx);
13562        let mut selections = self.selections.all::<Point>(&self.display_snapshot(cx));
13563        let mut clipboard_selections = Vec::with_capacity(selections.len());
13564        {
13565            let max_point = buffer.max_point();
13566            let mut is_first = true;
13567            let mut prev_selection_was_entire_line = false;
13568            for selection in &mut selections {
13569                let is_entire_line =
13570                    (selection.is_empty() && cut_no_selection_line) || self.selections.line_mode();
13571                if is_entire_line {
13572                    selection.start = Point::new(selection.start.row, 0);
13573                    if !selection.is_empty() && selection.end.column == 0 {
13574                        selection.end = cmp::min(max_point, selection.end);
13575                    } else {
13576                        selection.end = cmp::min(max_point, Point::new(selection.end.row + 1, 0));
13577                    }
13578                    selection.goal = SelectionGoal::None;
13579                }
13580                if is_first {
13581                    is_first = false;
13582                } else if !prev_selection_was_entire_line {
13583                    text += "\n";
13584                }
13585                prev_selection_was_entire_line = is_entire_line;
13586                let mut len = 0;
13587                for chunk in buffer.text_for_range(selection.start..selection.end) {
13588                    text.push_str(chunk);
13589                    len += chunk.len();
13590                }
13591
13592                clipboard_selections.push(ClipboardSelection::for_buffer(
13593                    len,
13594                    is_entire_line,
13595                    selection.range(),
13596                    &buffer,
13597                    self.project.as_ref(),
13598                    cx,
13599                ));
13600            }
13601        }
13602
13603        self.transact(window, cx, |this, window, cx| {
13604            this.change_selections(Default::default(), window, cx, |s| {
13605                s.select(selections);
13606            });
13607            this.insert("", window, cx);
13608        });
13609        ClipboardItem::new_string_with_json_metadata(text, clipboard_selections)
13610    }
13611
13612    pub fn cut(&mut self, _: &Cut, window: &mut Window, cx: &mut Context<Self>) {
13613        self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
13614        let item = self.cut_common(true, window, cx);
13615        cx.write_to_clipboard(item);
13616    }
13617
13618    pub fn kill_ring_cut(&mut self, _: &KillRingCut, window: &mut Window, cx: &mut Context<Self>) {
13619        self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
13620        self.change_selections(SelectionEffects::no_scroll(), window, cx, |s| {
13621            s.move_with(&mut |snapshot, sel| {
13622                if sel.is_empty() {
13623                    sel.end = DisplayPoint::new(sel.end.row(), snapshot.line_len(sel.end.row()));
13624                }
13625                if sel.is_empty() {
13626                    sel.end = DisplayPoint::new(sel.end.row() + 1_u32, 0);
13627                }
13628            });
13629        });
13630        let item = self.cut_common(false, window, cx);
13631        cx.set_global(KillRing(item))
13632    }
13633
13634    pub fn kill_ring_yank(
13635        &mut self,
13636        _: &KillRingYank,
13637        window: &mut Window,
13638        cx: &mut Context<Self>,
13639    ) {
13640        self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
13641        let (text, metadata) = if let Some(KillRing(item)) = cx.try_global() {
13642            if let Some(ClipboardEntry::String(kill_ring)) = item.entries().first() {
13643                (kill_ring.text().to_string(), kill_ring.metadata_json())
13644            } else {
13645                return;
13646            }
13647        } else {
13648            return;
13649        };
13650        self.do_paste(&text, metadata, false, window, cx);
13651    }
13652
13653    pub fn copy_and_trim(&mut self, _: &CopyAndTrim, _: &mut Window, cx: &mut Context<Self>) {
13654        self.do_copy(true, cx);
13655    }
13656
13657    pub fn copy(&mut self, _: &Copy, _: &mut Window, cx: &mut Context<Self>) {
13658        self.do_copy(false, cx);
13659    }
13660
13661    fn do_copy(&self, strip_leading_indents: bool, cx: &mut Context<Self>) {
13662        let selections = self.selections.all::<Point>(&self.display_snapshot(cx));
13663        let buffer = self.buffer.read(cx).read(cx);
13664        let mut text = String::new();
13665
13666        let mut clipboard_selections = Vec::with_capacity(selections.len());
13667        {
13668            let max_point = buffer.max_point();
13669            let mut is_first = true;
13670            let mut prev_selection_was_entire_line = false;
13671            for selection in &selections {
13672                let mut start = selection.start;
13673                let mut end = selection.end;
13674                let is_entire_line = selection.is_empty() || self.selections.line_mode();
13675                let mut add_trailing_newline = false;
13676                if is_entire_line {
13677                    start = Point::new(start.row, 0);
13678                    let next_line_start = Point::new(end.row + 1, 0);
13679                    if next_line_start <= max_point {
13680                        end = next_line_start;
13681                    } else {
13682                        // We're on the last line without a trailing newline.
13683                        // Copy to the end of the line and add a newline afterwards.
13684                        end = Point::new(end.row, buffer.line_len(MultiBufferRow(end.row)));
13685                        add_trailing_newline = true;
13686                    }
13687                }
13688
13689                let mut trimmed_selections = Vec::new();
13690                if strip_leading_indents && end.row.saturating_sub(start.row) > 0 {
13691                    let row = MultiBufferRow(start.row);
13692                    let first_indent = buffer.indent_size_for_line(row);
13693                    if first_indent.len == 0 || start.column > first_indent.len {
13694                        trimmed_selections.push(start..end);
13695                    } else {
13696                        trimmed_selections.push(
13697                            Point::new(row.0, first_indent.len)
13698                                ..Point::new(row.0, buffer.line_len(row)),
13699                        );
13700                        for row in start.row + 1..=end.row {
13701                            let mut line_len = buffer.line_len(MultiBufferRow(row));
13702                            if row == end.row {
13703                                line_len = end.column;
13704                            }
13705                            if line_len == 0 {
13706                                trimmed_selections
13707                                    .push(Point::new(row, 0)..Point::new(row, line_len));
13708                                continue;
13709                            }
13710                            let row_indent_size = buffer.indent_size_for_line(MultiBufferRow(row));
13711                            if row_indent_size.len >= first_indent.len {
13712                                trimmed_selections.push(
13713                                    Point::new(row, first_indent.len)..Point::new(row, line_len),
13714                                );
13715                            } else {
13716                                trimmed_selections.clear();
13717                                trimmed_selections.push(start..end);
13718                                break;
13719                            }
13720                        }
13721                    }
13722                } else {
13723                    trimmed_selections.push(start..end);
13724                }
13725
13726                let is_multiline_trim = trimmed_selections.len() > 1;
13727                for trimmed_range in trimmed_selections {
13728                    if is_first {
13729                        is_first = false;
13730                    } else if is_multiline_trim || !prev_selection_was_entire_line {
13731                        text += "\n";
13732                    }
13733                    prev_selection_was_entire_line = is_entire_line && !is_multiline_trim;
13734                    let mut len = 0;
13735                    for chunk in buffer.text_for_range(trimmed_range.start..trimmed_range.end) {
13736                        text.push_str(chunk);
13737                        len += chunk.len();
13738                    }
13739                    if add_trailing_newline {
13740                        text.push('\n');
13741                        len += 1;
13742                    }
13743                    clipboard_selections.push(ClipboardSelection::for_buffer(
13744                        len,
13745                        is_entire_line,
13746                        trimmed_range,
13747                        &buffer,
13748                        self.project.as_ref(),
13749                        cx,
13750                    ));
13751                }
13752            }
13753        }
13754
13755        cx.write_to_clipboard(ClipboardItem::new_string_with_json_metadata(
13756            text,
13757            clipboard_selections,
13758        ));
13759    }
13760
13761    pub fn do_paste(
13762        &mut self,
13763        text: &String,
13764        clipboard_selections: Option<Vec<ClipboardSelection>>,
13765        handle_entire_lines: bool,
13766        window: &mut Window,
13767        cx: &mut Context<Self>,
13768    ) {
13769        if self.read_only(cx) {
13770            return;
13771        }
13772
13773        let clipboard_text = Cow::Borrowed(text.as_str());
13774
13775        self.transact(window, cx, |this, window, cx| {
13776            let had_active_edit_prediction = this.has_active_edit_prediction();
13777            let display_map = this.display_snapshot(cx);
13778            let old_selections = this.selections.all::<MultiBufferOffset>(&display_map);
13779            let cursor_offset = this
13780                .selections
13781                .last::<MultiBufferOffset>(&display_map)
13782                .head();
13783
13784            if let Some(mut clipboard_selections) = clipboard_selections {
13785                let all_selections_were_entire_line =
13786                    clipboard_selections.iter().all(|s| s.is_entire_line);
13787                let first_selection_indent_column =
13788                    clipboard_selections.first().map(|s| s.first_line_indent);
13789                if clipboard_selections.len() != old_selections.len() {
13790                    clipboard_selections.drain(..);
13791                }
13792                let mut auto_indent_on_paste = true;
13793
13794                this.buffer.update(cx, |buffer, cx| {
13795                    let snapshot = buffer.read(cx);
13796                    auto_indent_on_paste = snapshot
13797                        .language_settings_at(cursor_offset, cx)
13798                        .auto_indent_on_paste;
13799
13800                    let mut start_offset = 0;
13801                    let mut edits = Vec::new();
13802                    let mut original_indent_columns = Vec::new();
13803                    for (ix, selection) in old_selections.iter().enumerate() {
13804                        let to_insert;
13805                        let entire_line;
13806                        let original_indent_column;
13807                        if let Some(clipboard_selection) = clipboard_selections.get(ix) {
13808                            let end_offset = start_offset + clipboard_selection.len;
13809                            to_insert = &clipboard_text[start_offset..end_offset];
13810                            entire_line = clipboard_selection.is_entire_line;
13811                            start_offset = if entire_line {
13812                                end_offset
13813                            } else {
13814                                end_offset + 1
13815                            };
13816                            original_indent_column = Some(clipboard_selection.first_line_indent);
13817                        } else {
13818                            to_insert = &*clipboard_text;
13819                            entire_line = all_selections_were_entire_line;
13820                            original_indent_column = first_selection_indent_column
13821                        }
13822
13823                        let (range, to_insert) =
13824                            if selection.is_empty() && handle_entire_lines && entire_line {
13825                                // If the corresponding selection was empty when this slice of the
13826                                // clipboard text was written, then the entire line containing the
13827                                // selection was copied. If this selection is also currently empty,
13828                                // then paste the line before the current line of the buffer.
13829                                let column = selection.start.to_point(&snapshot).column as usize;
13830                                let line_start = selection.start - column;
13831                                (line_start..line_start, Cow::Borrowed(to_insert))
13832                            } else {
13833                                let language = snapshot.language_at(selection.head());
13834                                let range = selection.range();
13835                                if let Some(language) = language
13836                                    && language.name() == "Markdown".into()
13837                                {
13838                                    edit_for_markdown_paste(
13839                                        &snapshot,
13840                                        range,
13841                                        to_insert,
13842                                        url::Url::parse(to_insert).ok(),
13843                                    )
13844                                } else {
13845                                    (range, Cow::Borrowed(to_insert))
13846                                }
13847                            };
13848
13849                        edits.push((range, to_insert));
13850                        original_indent_columns.push(original_indent_column);
13851                    }
13852                    drop(snapshot);
13853
13854                    buffer.edit(
13855                        edits,
13856                        if auto_indent_on_paste {
13857                            Some(AutoindentMode::Block {
13858                                original_indent_columns,
13859                            })
13860                        } else {
13861                            None
13862                        },
13863                        cx,
13864                    );
13865                });
13866
13867                let selections = this
13868                    .selections
13869                    .all::<MultiBufferOffset>(&this.display_snapshot(cx));
13870                this.change_selections(Default::default(), window, cx, |s| s.select(selections));
13871            } else {
13872                let url = url::Url::parse(&clipboard_text).ok();
13873
13874                let auto_indent_mode = if !clipboard_text.is_empty() {
13875                    Some(AutoindentMode::Block {
13876                        original_indent_columns: Vec::new(),
13877                    })
13878                } else {
13879                    None
13880                };
13881
13882                let selection_anchors = this.buffer.update(cx, |buffer, cx| {
13883                    let snapshot = buffer.snapshot(cx);
13884
13885                    let anchors = old_selections
13886                        .iter()
13887                        .map(|s| {
13888                            let anchor = snapshot.anchor_after(s.head());
13889                            s.map(|_| anchor)
13890                        })
13891                        .collect::<Vec<_>>();
13892
13893                    let mut edits = Vec::new();
13894
13895                    for selection in old_selections.iter() {
13896                        let language = snapshot.language_at(selection.head());
13897                        let range = selection.range();
13898
13899                        let (edit_range, edit_text) = if let Some(language) = language
13900                            && language.name() == "Markdown".into()
13901                        {
13902                            edit_for_markdown_paste(&snapshot, range, &clipboard_text, url.clone())
13903                        } else {
13904                            (range, clipboard_text.clone())
13905                        };
13906
13907                        edits.push((edit_range, edit_text));
13908                    }
13909
13910                    drop(snapshot);
13911                    buffer.edit(edits, auto_indent_mode, cx);
13912
13913                    anchors
13914                });
13915
13916                this.change_selections(Default::default(), window, cx, |s| {
13917                    s.select_anchors(selection_anchors);
13918                });
13919            }
13920
13921            //   🤔                 |    ..     | show_in_menu |
13922            // | ..                  |   true        true
13923            // | had_edit_prediction |   false       true
13924
13925            let trigger_in_words =
13926                this.show_edit_predictions_in_menu() || !had_active_edit_prediction;
13927
13928            this.trigger_completion_on_input(text, trigger_in_words, window, cx);
13929        });
13930    }
13931
13932    pub fn diff_clipboard_with_selection(
13933        &mut self,
13934        _: &DiffClipboardWithSelection,
13935        window: &mut Window,
13936        cx: &mut Context<Self>,
13937    ) {
13938        let selections = self
13939            .selections
13940            .all::<MultiBufferOffset>(&self.display_snapshot(cx));
13941
13942        if selections.is_empty() {
13943            log::warn!("There should always be at least one selection in Zed. This is a bug.");
13944            return;
13945        };
13946
13947        let clipboard_text = match cx.read_from_clipboard() {
13948            Some(item) => match item.entries().first() {
13949                Some(ClipboardEntry::String(text)) => Some(text.text().to_string()),
13950                _ => None,
13951            },
13952            None => None,
13953        };
13954
13955        let Some(clipboard_text) = clipboard_text else {
13956            log::warn!("Clipboard doesn't contain text.");
13957            return;
13958        };
13959
13960        window.dispatch_action(
13961            Box::new(DiffClipboardWithSelectionData {
13962                clipboard_text,
13963                editor: cx.entity(),
13964            }),
13965            cx,
13966        );
13967    }
13968
13969    pub fn paste(&mut self, _: &Paste, window: &mut Window, cx: &mut Context<Self>) {
13970        self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
13971        if let Some(item) = cx.read_from_clipboard() {
13972            let entries = item.entries();
13973
13974            match entries.first() {
13975                // For now, we only support applying metadata if there's one string. In the future, we can incorporate all the selections
13976                // of all the pasted entries.
13977                Some(ClipboardEntry::String(clipboard_string)) if entries.len() == 1 => self
13978                    .do_paste(
13979                        clipboard_string.text(),
13980                        clipboard_string.metadata_json::<Vec<ClipboardSelection>>(),
13981                        true,
13982                        window,
13983                        cx,
13984                    ),
13985                _ => self.do_paste(&item.text().unwrap_or_default(), None, true, window, cx),
13986            }
13987        }
13988    }
13989
13990    pub fn undo(&mut self, _: &Undo, window: &mut Window, cx: &mut Context<Self>) {
13991        if self.read_only(cx) {
13992            return;
13993        }
13994
13995        self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
13996
13997        if let Some(transaction_id) = self.buffer.update(cx, |buffer, cx| buffer.undo(cx)) {
13998            if let Some((selections, _)) =
13999                self.selection_history.transaction(transaction_id).cloned()
14000            {
14001                self.change_selections(SelectionEffects::no_scroll(), window, cx, |s| {
14002                    s.select_anchors(selections.to_vec());
14003                });
14004            } else {
14005                log::error!(
14006                    "No entry in selection_history found for undo. \
14007                     This may correspond to a bug where undo does not update the selection. \
14008                     If this is occurring, please add details to \
14009                     https://github.com/zed-industries/zed/issues/22692"
14010                );
14011            }
14012            self.request_autoscroll(Autoscroll::fit(), cx);
14013            self.unmark_text(window, cx);
14014            self.refresh_edit_prediction(true, false, window, cx);
14015            cx.emit(EditorEvent::Edited { transaction_id });
14016            cx.emit(EditorEvent::TransactionUndone { transaction_id });
14017        }
14018    }
14019
14020    pub fn redo(&mut self, _: &Redo, window: &mut Window, cx: &mut Context<Self>) {
14021        if self.read_only(cx) {
14022            return;
14023        }
14024
14025        self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
14026
14027        if let Some(transaction_id) = self.buffer.update(cx, |buffer, cx| buffer.redo(cx)) {
14028            if let Some((_, Some(selections))) =
14029                self.selection_history.transaction(transaction_id).cloned()
14030            {
14031                self.change_selections(SelectionEffects::no_scroll(), window, cx, |s| {
14032                    s.select_anchors(selections.to_vec());
14033                });
14034            } else {
14035                log::error!(
14036                    "No entry in selection_history found for redo. \
14037                     This may correspond to a bug where undo does not update the selection. \
14038                     If this is occurring, please add details to \
14039                     https://github.com/zed-industries/zed/issues/22692"
14040                );
14041            }
14042            self.request_autoscroll(Autoscroll::fit(), cx);
14043            self.unmark_text(window, cx);
14044            self.refresh_edit_prediction(true, false, window, cx);
14045            cx.emit(EditorEvent::Edited { transaction_id });
14046        }
14047    }
14048
14049    pub fn finalize_last_transaction(&mut self, cx: &mut Context<Self>) {
14050        self.buffer
14051            .update(cx, |buffer, cx| buffer.finalize_last_transaction(cx));
14052    }
14053
14054    pub fn group_until_transaction(&mut self, tx_id: TransactionId, cx: &mut Context<Self>) {
14055        self.buffer
14056            .update(cx, |buffer, cx| buffer.group_until_transaction(tx_id, cx));
14057    }
14058
14059    pub fn move_left(&mut self, _: &MoveLeft, window: &mut Window, cx: &mut Context<Self>) {
14060        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
14061        self.change_selections(Default::default(), window, cx, |s| {
14062            s.move_with(&mut |map, selection| {
14063                let cursor = if selection.is_empty() {
14064                    movement::left(map, selection.start)
14065                } else {
14066                    selection.start
14067                };
14068                selection.collapse_to(cursor, SelectionGoal::None);
14069            });
14070        })
14071    }
14072
14073    pub fn select_left(&mut self, _: &SelectLeft, window: &mut Window, cx: &mut Context<Self>) {
14074        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
14075        self.change_selections(Default::default(), window, cx, |s| {
14076            s.move_heads_with(&mut |map, head, _| (movement::left(map, head), SelectionGoal::None));
14077        })
14078    }
14079
14080    pub fn move_right(&mut self, _: &MoveRight, window: &mut Window, cx: &mut Context<Self>) {
14081        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
14082        self.change_selections(Default::default(), window, cx, |s| {
14083            s.move_with(&mut |map, selection| {
14084                let cursor = if selection.is_empty() {
14085                    movement::right(map, selection.end)
14086                } else {
14087                    selection.end
14088                };
14089                selection.collapse_to(cursor, SelectionGoal::None)
14090            });
14091        })
14092    }
14093
14094    pub fn select_right(&mut self, _: &SelectRight, window: &mut Window, cx: &mut Context<Self>) {
14095        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
14096        self.change_selections(Default::default(), window, cx, |s| {
14097            s.move_heads_with(&mut |map, head, _| {
14098                (movement::right(map, head), SelectionGoal::None)
14099            });
14100        });
14101    }
14102
14103    pub fn move_up(&mut self, _: &MoveUp, window: &mut Window, cx: &mut Context<Self>) {
14104        if self.take_rename(true, window, cx).is_some() {
14105            return;
14106        }
14107
14108        if self.mode.is_single_line() {
14109            cx.propagate();
14110            return;
14111        }
14112
14113        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
14114
14115        let text_layout_details = &self.text_layout_details(window, cx);
14116        let selection_count = self.selections.count();
14117        let first_selection = self.selections.first_anchor();
14118
14119        self.change_selections(Default::default(), window, cx, |s| {
14120            s.move_with(&mut |map, selection| {
14121                if !selection.is_empty() {
14122                    selection.goal = SelectionGoal::None;
14123                }
14124                let (cursor, goal) = movement::up(
14125                    map,
14126                    selection.start,
14127                    selection.goal,
14128                    false,
14129                    text_layout_details,
14130                );
14131                selection.collapse_to(cursor, goal);
14132            });
14133        });
14134
14135        if selection_count == 1 && first_selection.range() == self.selections.first_anchor().range()
14136        {
14137            cx.propagate();
14138        }
14139    }
14140
14141    pub fn move_up_by_lines(
14142        &mut self,
14143        action: &MoveUpByLines,
14144        window: &mut Window,
14145        cx: &mut Context<Self>,
14146    ) {
14147        if self.take_rename(true, window, cx).is_some() {
14148            return;
14149        }
14150
14151        if self.mode.is_single_line() {
14152            cx.propagate();
14153            return;
14154        }
14155
14156        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
14157
14158        let text_layout_details = &self.text_layout_details(window, cx);
14159
14160        self.change_selections(Default::default(), window, cx, |s| {
14161            s.move_with(&mut |map, selection| {
14162                if !selection.is_empty() {
14163                    selection.goal = SelectionGoal::None;
14164                }
14165                let (cursor, goal) = movement::up_by_rows(
14166                    map,
14167                    selection.start,
14168                    action.lines,
14169                    selection.goal,
14170                    false,
14171                    text_layout_details,
14172                );
14173                selection.collapse_to(cursor, goal);
14174            });
14175        })
14176    }
14177
14178    pub fn move_down_by_lines(
14179        &mut self,
14180        action: &MoveDownByLines,
14181        window: &mut Window,
14182        cx: &mut Context<Self>,
14183    ) {
14184        if self.take_rename(true, window, cx).is_some() {
14185            return;
14186        }
14187
14188        if self.mode.is_single_line() {
14189            cx.propagate();
14190            return;
14191        }
14192
14193        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
14194
14195        let text_layout_details = &self.text_layout_details(window, cx);
14196
14197        self.change_selections(Default::default(), window, cx, |s| {
14198            s.move_with(&mut |map, selection| {
14199                if !selection.is_empty() {
14200                    selection.goal = SelectionGoal::None;
14201                }
14202                let (cursor, goal) = movement::down_by_rows(
14203                    map,
14204                    selection.start,
14205                    action.lines,
14206                    selection.goal,
14207                    false,
14208                    text_layout_details,
14209                );
14210                selection.collapse_to(cursor, goal);
14211            });
14212        })
14213    }
14214
14215    pub fn select_down_by_lines(
14216        &mut self,
14217        action: &SelectDownByLines,
14218        window: &mut Window,
14219        cx: &mut Context<Self>,
14220    ) {
14221        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
14222        let text_layout_details = &self.text_layout_details(window, cx);
14223        self.change_selections(Default::default(), window, cx, |s| {
14224            s.move_heads_with(&mut |map, head, goal| {
14225                movement::down_by_rows(map, head, action.lines, goal, false, text_layout_details)
14226            })
14227        })
14228    }
14229
14230    pub fn select_up_by_lines(
14231        &mut self,
14232        action: &SelectUpByLines,
14233        window: &mut Window,
14234        cx: &mut Context<Self>,
14235    ) {
14236        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
14237        let text_layout_details = &self.text_layout_details(window, cx);
14238        self.change_selections(Default::default(), window, cx, |s| {
14239            s.move_heads_with(&mut |map, head, goal| {
14240                movement::up_by_rows(map, head, action.lines, goal, false, text_layout_details)
14241            })
14242        })
14243    }
14244
14245    pub fn select_page_up(
14246        &mut self,
14247        _: &SelectPageUp,
14248        window: &mut Window,
14249        cx: &mut Context<Self>,
14250    ) {
14251        let Some(row_count) = self.visible_row_count() else {
14252            return;
14253        };
14254
14255        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
14256
14257        let text_layout_details = &self.text_layout_details(window, cx);
14258
14259        self.change_selections(Default::default(), window, cx, |s| {
14260            s.move_heads_with(&mut |map, head, goal| {
14261                movement::up_by_rows(map, head, row_count, goal, false, text_layout_details)
14262            })
14263        })
14264    }
14265
14266    pub fn move_page_up(
14267        &mut self,
14268        action: &MovePageUp,
14269        window: &mut Window,
14270        cx: &mut Context<Self>,
14271    ) {
14272        if self.take_rename(true, window, cx).is_some() {
14273            return;
14274        }
14275
14276        if self
14277            .context_menu
14278            .borrow_mut()
14279            .as_mut()
14280            .map(|menu| menu.select_first(self.completion_provider.as_deref(), window, cx))
14281            .unwrap_or(false)
14282        {
14283            return;
14284        }
14285
14286        if matches!(self.mode, EditorMode::SingleLine) {
14287            cx.propagate();
14288            return;
14289        }
14290
14291        let Some(row_count) = self.visible_row_count() else {
14292            return;
14293        };
14294
14295        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
14296
14297        let effects = if action.center_cursor {
14298            SelectionEffects::scroll(Autoscroll::center())
14299        } else {
14300            SelectionEffects::default()
14301        };
14302
14303        let text_layout_details = &self.text_layout_details(window, cx);
14304
14305        self.change_selections(effects, window, cx, |s| {
14306            s.move_with(&mut |map, selection| {
14307                if !selection.is_empty() {
14308                    selection.goal = SelectionGoal::None;
14309                }
14310                let (cursor, goal) = movement::up_by_rows(
14311                    map,
14312                    selection.end,
14313                    row_count,
14314                    selection.goal,
14315                    false,
14316                    text_layout_details,
14317                );
14318                selection.collapse_to(cursor, goal);
14319            });
14320        });
14321    }
14322
14323    pub fn select_up(&mut self, _: &SelectUp, window: &mut Window, cx: &mut Context<Self>) {
14324        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
14325        let text_layout_details = &self.text_layout_details(window, cx);
14326        self.change_selections(Default::default(), window, cx, |s| {
14327            s.move_heads_with(&mut |map, head, goal| {
14328                movement::up(map, head, goal, false, text_layout_details)
14329            })
14330        })
14331    }
14332
14333    pub fn move_down(&mut self, _: &MoveDown, window: &mut Window, cx: &mut Context<Self>) {
14334        self.take_rename(true, window, cx);
14335
14336        if self.mode.is_single_line() {
14337            cx.propagate();
14338            return;
14339        }
14340
14341        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
14342
14343        let text_layout_details = &self.text_layout_details(window, cx);
14344        let selection_count = self.selections.count();
14345        let first_selection = self.selections.first_anchor();
14346
14347        self.change_selections(Default::default(), window, cx, |s| {
14348            s.move_with(&mut |map, selection| {
14349                if !selection.is_empty() {
14350                    selection.goal = SelectionGoal::None;
14351                }
14352                let (cursor, goal) = movement::down(
14353                    map,
14354                    selection.end,
14355                    selection.goal,
14356                    false,
14357                    text_layout_details,
14358                );
14359                selection.collapse_to(cursor, goal);
14360            });
14361        });
14362
14363        if selection_count == 1 && first_selection.range() == self.selections.first_anchor().range()
14364        {
14365            cx.propagate();
14366        }
14367    }
14368
14369    pub fn select_page_down(
14370        &mut self,
14371        _: &SelectPageDown,
14372        window: &mut Window,
14373        cx: &mut Context<Self>,
14374    ) {
14375        let Some(row_count) = self.visible_row_count() else {
14376            return;
14377        };
14378
14379        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
14380
14381        let text_layout_details = &self.text_layout_details(window, cx);
14382
14383        self.change_selections(Default::default(), window, cx, |s| {
14384            s.move_heads_with(&mut |map, head, goal| {
14385                movement::down_by_rows(map, head, row_count, goal, false, text_layout_details)
14386            })
14387        })
14388    }
14389
14390    pub fn move_page_down(
14391        &mut self,
14392        action: &MovePageDown,
14393        window: &mut Window,
14394        cx: &mut Context<Self>,
14395    ) {
14396        if self.take_rename(true, window, cx).is_some() {
14397            return;
14398        }
14399
14400        if self
14401            .context_menu
14402            .borrow_mut()
14403            .as_mut()
14404            .map(|menu| menu.select_last(self.completion_provider.as_deref(), window, cx))
14405            .unwrap_or(false)
14406        {
14407            return;
14408        }
14409
14410        if matches!(self.mode, EditorMode::SingleLine) {
14411            cx.propagate();
14412            return;
14413        }
14414
14415        let Some(row_count) = self.visible_row_count() else {
14416            return;
14417        };
14418
14419        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
14420
14421        let effects = if action.center_cursor {
14422            SelectionEffects::scroll(Autoscroll::center())
14423        } else {
14424            SelectionEffects::default()
14425        };
14426
14427        let text_layout_details = &self.text_layout_details(window, cx);
14428        self.change_selections(effects, window, cx, |s| {
14429            s.move_with(&mut |map, selection| {
14430                if !selection.is_empty() {
14431                    selection.goal = SelectionGoal::None;
14432                }
14433                let (cursor, goal) = movement::down_by_rows(
14434                    map,
14435                    selection.end,
14436                    row_count,
14437                    selection.goal,
14438                    false,
14439                    text_layout_details,
14440                );
14441                selection.collapse_to(cursor, goal);
14442            });
14443        });
14444    }
14445
14446    pub fn select_down(&mut self, _: &SelectDown, window: &mut Window, cx: &mut Context<Self>) {
14447        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
14448        let text_layout_details = &self.text_layout_details(window, cx);
14449        self.change_selections(Default::default(), window, cx, |s| {
14450            s.move_heads_with(&mut |map, head, goal| {
14451                movement::down(map, head, goal, false, text_layout_details)
14452            })
14453        });
14454    }
14455
14456    pub fn context_menu_first(
14457        &mut self,
14458        _: &ContextMenuFirst,
14459        window: &mut Window,
14460        cx: &mut Context<Self>,
14461    ) {
14462        if let Some(context_menu) = self.context_menu.borrow_mut().as_mut() {
14463            context_menu.select_first(self.completion_provider.as_deref(), window, cx);
14464        }
14465    }
14466
14467    pub fn context_menu_prev(
14468        &mut self,
14469        _: &ContextMenuPrevious,
14470        window: &mut Window,
14471        cx: &mut Context<Self>,
14472    ) {
14473        if let Some(context_menu) = self.context_menu.borrow_mut().as_mut() {
14474            context_menu.select_prev(self.completion_provider.as_deref(), window, cx);
14475        }
14476    }
14477
14478    pub fn context_menu_next(
14479        &mut self,
14480        _: &ContextMenuNext,
14481        window: &mut Window,
14482        cx: &mut Context<Self>,
14483    ) {
14484        if let Some(context_menu) = self.context_menu.borrow_mut().as_mut() {
14485            context_menu.select_next(self.completion_provider.as_deref(), window, cx);
14486        }
14487    }
14488
14489    pub fn context_menu_last(
14490        &mut self,
14491        _: &ContextMenuLast,
14492        window: &mut Window,
14493        cx: &mut Context<Self>,
14494    ) {
14495        if let Some(context_menu) = self.context_menu.borrow_mut().as_mut() {
14496            context_menu.select_last(self.completion_provider.as_deref(), window, cx);
14497        }
14498    }
14499
14500    pub fn signature_help_prev(
14501        &mut self,
14502        _: &SignatureHelpPrevious,
14503        _: &mut Window,
14504        cx: &mut Context<Self>,
14505    ) {
14506        if let Some(popover) = self.signature_help_state.popover_mut() {
14507            if popover.current_signature == 0 {
14508                popover.current_signature = popover.signatures.len() - 1;
14509            } else {
14510                popover.current_signature -= 1;
14511            }
14512            cx.notify();
14513        }
14514    }
14515
14516    pub fn signature_help_next(
14517        &mut self,
14518        _: &SignatureHelpNext,
14519        _: &mut Window,
14520        cx: &mut Context<Self>,
14521    ) {
14522        if let Some(popover) = self.signature_help_state.popover_mut() {
14523            if popover.current_signature + 1 == popover.signatures.len() {
14524                popover.current_signature = 0;
14525            } else {
14526                popover.current_signature += 1;
14527            }
14528            cx.notify();
14529        }
14530    }
14531
14532    pub fn move_to_previous_word_start(
14533        &mut self,
14534        _: &MoveToPreviousWordStart,
14535        window: &mut Window,
14536        cx: &mut Context<Self>,
14537    ) {
14538        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
14539        self.change_selections(Default::default(), window, cx, |s| {
14540            s.move_cursors_with(&mut |map, head, _| {
14541                (
14542                    movement::previous_word_start(map, head),
14543                    SelectionGoal::None,
14544                )
14545            });
14546        })
14547    }
14548
14549    pub fn move_to_previous_subword_start(
14550        &mut self,
14551        _: &MoveToPreviousSubwordStart,
14552        window: &mut Window,
14553        cx: &mut Context<Self>,
14554    ) {
14555        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
14556        self.change_selections(Default::default(), window, cx, |s| {
14557            s.move_cursors_with(&mut |map, head, _| {
14558                (
14559                    movement::previous_subword_start(map, head),
14560                    SelectionGoal::None,
14561                )
14562            });
14563        })
14564    }
14565
14566    pub fn select_to_previous_word_start(
14567        &mut self,
14568        _: &SelectToPreviousWordStart,
14569        window: &mut Window,
14570        cx: &mut Context<Self>,
14571    ) {
14572        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
14573        self.change_selections(Default::default(), window, cx, |s| {
14574            s.move_heads_with(&mut |map, head, _| {
14575                (
14576                    movement::previous_word_start(map, head),
14577                    SelectionGoal::None,
14578                )
14579            });
14580        })
14581    }
14582
14583    pub fn select_to_previous_subword_start(
14584        &mut self,
14585        _: &SelectToPreviousSubwordStart,
14586        window: &mut Window,
14587        cx: &mut Context<Self>,
14588    ) {
14589        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
14590        self.change_selections(Default::default(), window, cx, |s| {
14591            s.move_heads_with(&mut |map, head, _| {
14592                (
14593                    movement::previous_subword_start(map, head),
14594                    SelectionGoal::None,
14595                )
14596            });
14597        })
14598    }
14599
14600    pub fn delete_to_previous_word_start(
14601        &mut self,
14602        action: &DeleteToPreviousWordStart,
14603        window: &mut Window,
14604        cx: &mut Context<Self>,
14605    ) {
14606        self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
14607        self.transact(window, cx, |this, window, cx| {
14608            this.select_autoclose_pair(window, cx);
14609            this.change_selections(Default::default(), window, cx, |s| {
14610                s.move_with(&mut |map, selection| {
14611                    if selection.is_empty() {
14612                        let mut cursor = if action.ignore_newlines {
14613                            movement::previous_word_start(map, selection.head())
14614                        } else {
14615                            movement::previous_word_start_or_newline(map, selection.head())
14616                        };
14617                        cursor = movement::adjust_greedy_deletion(
14618                            map,
14619                            selection.head(),
14620                            cursor,
14621                            action.ignore_brackets,
14622                        );
14623                        selection.set_head(cursor, SelectionGoal::None);
14624                    }
14625                });
14626            });
14627            this.insert("", window, cx);
14628        });
14629    }
14630
14631    pub fn delete_to_previous_subword_start(
14632        &mut self,
14633        action: &DeleteToPreviousSubwordStart,
14634        window: &mut Window,
14635        cx: &mut Context<Self>,
14636    ) {
14637        self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
14638        self.transact(window, cx, |this, window, cx| {
14639            this.select_autoclose_pair(window, cx);
14640            this.change_selections(Default::default(), window, cx, |s| {
14641                s.move_with(&mut |map, selection| {
14642                    if selection.is_empty() {
14643                        let mut cursor = if action.ignore_newlines {
14644                            movement::previous_subword_start(map, selection.head())
14645                        } else {
14646                            movement::previous_subword_start_or_newline(map, selection.head())
14647                        };
14648                        cursor = movement::adjust_greedy_deletion(
14649                            map,
14650                            selection.head(),
14651                            cursor,
14652                            action.ignore_brackets,
14653                        );
14654                        selection.set_head(cursor, SelectionGoal::None);
14655                    }
14656                });
14657            });
14658            this.insert("", window, cx);
14659        });
14660    }
14661
14662    pub fn move_to_next_word_end(
14663        &mut self,
14664        _: &MoveToNextWordEnd,
14665        window: &mut Window,
14666        cx: &mut Context<Self>,
14667    ) {
14668        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
14669        self.change_selections(Default::default(), window, cx, |s| {
14670            s.move_cursors_with(&mut |map, head, _| {
14671                (movement::next_word_end(map, head), SelectionGoal::None)
14672            });
14673        })
14674    }
14675
14676    pub fn move_to_next_subword_end(
14677        &mut self,
14678        _: &MoveToNextSubwordEnd,
14679        window: &mut Window,
14680        cx: &mut Context<Self>,
14681    ) {
14682        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
14683        self.change_selections(Default::default(), window, cx, |s| {
14684            s.move_cursors_with(&mut |map, head, _| {
14685                (movement::next_subword_end(map, head), SelectionGoal::None)
14686            });
14687        })
14688    }
14689
14690    pub fn select_to_next_word_end(
14691        &mut self,
14692        _: &SelectToNextWordEnd,
14693        window: &mut Window,
14694        cx: &mut Context<Self>,
14695    ) {
14696        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
14697        self.change_selections(Default::default(), window, cx, |s| {
14698            s.move_heads_with(&mut |map, head, _| {
14699                (movement::next_word_end(map, head), SelectionGoal::None)
14700            });
14701        })
14702    }
14703
14704    pub fn select_to_next_subword_end(
14705        &mut self,
14706        _: &SelectToNextSubwordEnd,
14707        window: &mut Window,
14708        cx: &mut Context<Self>,
14709    ) {
14710        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
14711        self.change_selections(Default::default(), window, cx, |s| {
14712            s.move_heads_with(&mut |map, head, _| {
14713                (movement::next_subword_end(map, head), SelectionGoal::None)
14714            });
14715        })
14716    }
14717
14718    pub fn delete_to_next_word_end(
14719        &mut self,
14720        action: &DeleteToNextWordEnd,
14721        window: &mut Window,
14722        cx: &mut Context<Self>,
14723    ) {
14724        self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
14725        self.transact(window, cx, |this, window, cx| {
14726            this.change_selections(Default::default(), window, cx, |s| {
14727                s.move_with(&mut |map, selection| {
14728                    if selection.is_empty() {
14729                        let mut cursor = if action.ignore_newlines {
14730                            movement::next_word_end(map, selection.head())
14731                        } else {
14732                            movement::next_word_end_or_newline(map, selection.head())
14733                        };
14734                        cursor = movement::adjust_greedy_deletion(
14735                            map,
14736                            selection.head(),
14737                            cursor,
14738                            action.ignore_brackets,
14739                        );
14740                        selection.set_head(cursor, SelectionGoal::None);
14741                    }
14742                });
14743            });
14744            this.insert("", window, cx);
14745        });
14746    }
14747
14748    pub fn delete_to_next_subword_end(
14749        &mut self,
14750        action: &DeleteToNextSubwordEnd,
14751        window: &mut Window,
14752        cx: &mut Context<Self>,
14753    ) {
14754        self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
14755        self.transact(window, cx, |this, window, cx| {
14756            this.change_selections(Default::default(), window, cx, |s| {
14757                s.move_with(&mut |map, selection| {
14758                    if selection.is_empty() {
14759                        let mut cursor = if action.ignore_newlines {
14760                            movement::next_subword_end(map, selection.head())
14761                        } else {
14762                            movement::next_subword_end_or_newline(map, selection.head())
14763                        };
14764                        cursor = movement::adjust_greedy_deletion(
14765                            map,
14766                            selection.head(),
14767                            cursor,
14768                            action.ignore_brackets,
14769                        );
14770                        selection.set_head(cursor, SelectionGoal::None);
14771                    }
14772                });
14773            });
14774            this.insert("", window, cx);
14775        });
14776    }
14777
14778    pub fn move_to_beginning_of_line(
14779        &mut self,
14780        action: &MoveToBeginningOfLine,
14781        window: &mut Window,
14782        cx: &mut Context<Self>,
14783    ) {
14784        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
14785        self.change_selections(Default::default(), window, cx, |s| {
14786            s.move_cursors_with(&mut |map, head, _| {
14787                (
14788                    movement::indented_line_beginning(
14789                        map,
14790                        head,
14791                        action.stop_at_soft_wraps,
14792                        action.stop_at_indent,
14793                    ),
14794                    SelectionGoal::None,
14795                )
14796            });
14797        })
14798    }
14799
14800    pub fn select_to_beginning_of_line(
14801        &mut self,
14802        action: &SelectToBeginningOfLine,
14803        window: &mut Window,
14804        cx: &mut Context<Self>,
14805    ) {
14806        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
14807        self.change_selections(Default::default(), window, cx, |s| {
14808            s.move_heads_with(&mut |map, head, _| {
14809                (
14810                    movement::indented_line_beginning(
14811                        map,
14812                        head,
14813                        action.stop_at_soft_wraps,
14814                        action.stop_at_indent,
14815                    ),
14816                    SelectionGoal::None,
14817                )
14818            });
14819        });
14820    }
14821
14822    pub fn delete_to_beginning_of_line(
14823        &mut self,
14824        action: &DeleteToBeginningOfLine,
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.change_selections(Default::default(), window, cx, |s| {
14831                s.move_with(&mut |_, selection| {
14832                    selection.reversed = true;
14833                });
14834            });
14835
14836            this.select_to_beginning_of_line(
14837                &SelectToBeginningOfLine {
14838                    stop_at_soft_wraps: false,
14839                    stop_at_indent: action.stop_at_indent,
14840                },
14841                window,
14842                cx,
14843            );
14844            this.backspace(&Backspace, window, cx);
14845        });
14846    }
14847
14848    pub fn move_to_end_of_line(
14849        &mut self,
14850        action: &MoveToEndOfLine,
14851        window: &mut Window,
14852        cx: &mut Context<Self>,
14853    ) {
14854        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
14855        self.change_selections(Default::default(), window, cx, |s| {
14856            s.move_cursors_with(&mut |map, head, _| {
14857                (
14858                    movement::line_end(map, head, action.stop_at_soft_wraps),
14859                    SelectionGoal::None,
14860                )
14861            });
14862        })
14863    }
14864
14865    pub fn select_to_end_of_line(
14866        &mut self,
14867        action: &SelectToEndOfLine,
14868        window: &mut Window,
14869        cx: &mut Context<Self>,
14870    ) {
14871        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
14872        self.change_selections(Default::default(), window, cx, |s| {
14873            s.move_heads_with(&mut |map, head, _| {
14874                (
14875                    movement::line_end(map, head, action.stop_at_soft_wraps),
14876                    SelectionGoal::None,
14877                )
14878            });
14879        })
14880    }
14881
14882    pub fn delete_to_end_of_line(
14883        &mut self,
14884        _: &DeleteToEndOfLine,
14885        window: &mut Window,
14886        cx: &mut Context<Self>,
14887    ) {
14888        self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
14889        self.transact(window, cx, |this, window, cx| {
14890            this.select_to_end_of_line(
14891                &SelectToEndOfLine {
14892                    stop_at_soft_wraps: false,
14893                },
14894                window,
14895                cx,
14896            );
14897            this.delete(&Delete, window, cx);
14898        });
14899    }
14900
14901    pub fn cut_to_end_of_line(
14902        &mut self,
14903        action: &CutToEndOfLine,
14904        window: &mut Window,
14905        cx: &mut Context<Self>,
14906    ) {
14907        self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
14908        self.transact(window, cx, |this, window, cx| {
14909            this.select_to_end_of_line(
14910                &SelectToEndOfLine {
14911                    stop_at_soft_wraps: false,
14912                },
14913                window,
14914                cx,
14915            );
14916            if !action.stop_at_newlines {
14917                this.change_selections(Default::default(), window, cx, |s| {
14918                    s.move_with(&mut |_, sel| {
14919                        if sel.is_empty() {
14920                            sel.end = DisplayPoint::new(sel.end.row() + 1_u32, 0);
14921                        }
14922                    });
14923                });
14924            }
14925            this.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
14926            let item = this.cut_common(false, window, cx);
14927            cx.write_to_clipboard(item);
14928        });
14929    }
14930
14931    pub fn move_to_start_of_paragraph(
14932        &mut self,
14933        _: &MoveToStartOfParagraph,
14934        window: &mut Window,
14935        cx: &mut Context<Self>,
14936    ) {
14937        if matches!(self.mode, EditorMode::SingleLine) {
14938            cx.propagate();
14939            return;
14940        }
14941        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
14942        self.change_selections(Default::default(), window, cx, |s| {
14943            s.move_with(&mut |map, selection| {
14944                selection.collapse_to(
14945                    movement::start_of_paragraph(map, selection.head(), 1),
14946                    SelectionGoal::None,
14947                )
14948            });
14949        })
14950    }
14951
14952    pub fn move_to_end_of_paragraph(
14953        &mut self,
14954        _: &MoveToEndOfParagraph,
14955        window: &mut Window,
14956        cx: &mut Context<Self>,
14957    ) {
14958        if matches!(self.mode, EditorMode::SingleLine) {
14959            cx.propagate();
14960            return;
14961        }
14962        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
14963        self.change_selections(Default::default(), window, cx, |s| {
14964            s.move_with(&mut |map, selection| {
14965                selection.collapse_to(
14966                    movement::end_of_paragraph(map, selection.head(), 1),
14967                    SelectionGoal::None,
14968                )
14969            });
14970        })
14971    }
14972
14973    pub fn select_to_start_of_paragraph(
14974        &mut self,
14975        _: &SelectToStartOfParagraph,
14976        window: &mut Window,
14977        cx: &mut Context<Self>,
14978    ) {
14979        if matches!(self.mode, EditorMode::SingleLine) {
14980            cx.propagate();
14981            return;
14982        }
14983        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
14984        self.change_selections(Default::default(), window, cx, |s| {
14985            s.move_heads_with(&mut |map, head, _| {
14986                (
14987                    movement::start_of_paragraph(map, head, 1),
14988                    SelectionGoal::None,
14989                )
14990            });
14991        })
14992    }
14993
14994    pub fn select_to_end_of_paragraph(
14995        &mut self,
14996        _: &SelectToEndOfParagraph,
14997        window: &mut Window,
14998        cx: &mut Context<Self>,
14999    ) {
15000        if matches!(self.mode, EditorMode::SingleLine) {
15001            cx.propagate();
15002            return;
15003        }
15004        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
15005        self.change_selections(Default::default(), window, cx, |s| {
15006            s.move_heads_with(&mut |map, head, _| {
15007                (
15008                    movement::end_of_paragraph(map, head, 1),
15009                    SelectionGoal::None,
15010                )
15011            });
15012        })
15013    }
15014
15015    pub fn move_to_start_of_excerpt(
15016        &mut self,
15017        _: &MoveToStartOfExcerpt,
15018        window: &mut Window,
15019        cx: &mut Context<Self>,
15020    ) {
15021        if matches!(self.mode, EditorMode::SingleLine) {
15022            cx.propagate();
15023            return;
15024        }
15025        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
15026        self.change_selections(Default::default(), window, cx, |s| {
15027            s.move_with(&mut |map, selection| {
15028                selection.collapse_to(
15029                    movement::start_of_excerpt(
15030                        map,
15031                        selection.head(),
15032                        workspace::searchable::Direction::Prev,
15033                    ),
15034                    SelectionGoal::None,
15035                )
15036            });
15037        })
15038    }
15039
15040    pub fn move_to_start_of_next_excerpt(
15041        &mut self,
15042        _: &MoveToStartOfNextExcerpt,
15043        window: &mut Window,
15044        cx: &mut Context<Self>,
15045    ) {
15046        if matches!(self.mode, EditorMode::SingleLine) {
15047            cx.propagate();
15048            return;
15049        }
15050
15051        self.change_selections(Default::default(), window, cx, |s| {
15052            s.move_with(&mut |map, selection| {
15053                selection.collapse_to(
15054                    movement::start_of_excerpt(
15055                        map,
15056                        selection.head(),
15057                        workspace::searchable::Direction::Next,
15058                    ),
15059                    SelectionGoal::None,
15060                )
15061            });
15062        })
15063    }
15064
15065    pub fn move_to_end_of_excerpt(
15066        &mut self,
15067        _: &MoveToEndOfExcerpt,
15068        window: &mut Window,
15069        cx: &mut Context<Self>,
15070    ) {
15071        if matches!(self.mode, EditorMode::SingleLine) {
15072            cx.propagate();
15073            return;
15074        }
15075        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
15076        self.change_selections(Default::default(), window, cx, |s| {
15077            s.move_with(&mut |map, selection| {
15078                selection.collapse_to(
15079                    movement::end_of_excerpt(
15080                        map,
15081                        selection.head(),
15082                        workspace::searchable::Direction::Next,
15083                    ),
15084                    SelectionGoal::None,
15085                )
15086            });
15087        })
15088    }
15089
15090    pub fn move_to_end_of_previous_excerpt(
15091        &mut self,
15092        _: &MoveToEndOfPreviousExcerpt,
15093        window: &mut Window,
15094        cx: &mut Context<Self>,
15095    ) {
15096        if matches!(self.mode, EditorMode::SingleLine) {
15097            cx.propagate();
15098            return;
15099        }
15100        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
15101        self.change_selections(Default::default(), window, cx, |s| {
15102            s.move_with(&mut |map, selection| {
15103                selection.collapse_to(
15104                    movement::end_of_excerpt(
15105                        map,
15106                        selection.head(),
15107                        workspace::searchable::Direction::Prev,
15108                    ),
15109                    SelectionGoal::None,
15110                )
15111            });
15112        })
15113    }
15114
15115    pub fn select_to_start_of_excerpt(
15116        &mut self,
15117        _: &SelectToStartOfExcerpt,
15118        window: &mut Window,
15119        cx: &mut Context<Self>,
15120    ) {
15121        if matches!(self.mode, EditorMode::SingleLine) {
15122            cx.propagate();
15123            return;
15124        }
15125        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
15126        self.change_selections(Default::default(), window, cx, |s| {
15127            s.move_heads_with(&mut |map, head, _| {
15128                (
15129                    movement::start_of_excerpt(map, head, workspace::searchable::Direction::Prev),
15130                    SelectionGoal::None,
15131                )
15132            });
15133        })
15134    }
15135
15136    pub fn select_to_start_of_next_excerpt(
15137        &mut self,
15138        _: &SelectToStartOfNextExcerpt,
15139        window: &mut Window,
15140        cx: &mut Context<Self>,
15141    ) {
15142        if matches!(self.mode, EditorMode::SingleLine) {
15143            cx.propagate();
15144            return;
15145        }
15146        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
15147        self.change_selections(Default::default(), window, cx, |s| {
15148            s.move_heads_with(&mut |map, head, _| {
15149                (
15150                    movement::start_of_excerpt(map, head, workspace::searchable::Direction::Next),
15151                    SelectionGoal::None,
15152                )
15153            });
15154        })
15155    }
15156
15157    pub fn select_to_end_of_excerpt(
15158        &mut self,
15159        _: &SelectToEndOfExcerpt,
15160        window: &mut Window,
15161        cx: &mut Context<Self>,
15162    ) {
15163        if matches!(self.mode, EditorMode::SingleLine) {
15164            cx.propagate();
15165            return;
15166        }
15167        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
15168        self.change_selections(Default::default(), window, cx, |s| {
15169            s.move_heads_with(&mut |map, head, _| {
15170                (
15171                    movement::end_of_excerpt(map, head, workspace::searchable::Direction::Next),
15172                    SelectionGoal::None,
15173                )
15174            });
15175        })
15176    }
15177
15178    pub fn select_to_end_of_previous_excerpt(
15179        &mut self,
15180        _: &SelectToEndOfPreviousExcerpt,
15181        window: &mut Window,
15182        cx: &mut Context<Self>,
15183    ) {
15184        if matches!(self.mode, EditorMode::SingleLine) {
15185            cx.propagate();
15186            return;
15187        }
15188        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
15189        self.change_selections(Default::default(), window, cx, |s| {
15190            s.move_heads_with(&mut |map, head, _| {
15191                (
15192                    movement::end_of_excerpt(map, head, workspace::searchable::Direction::Prev),
15193                    SelectionGoal::None,
15194                )
15195            });
15196        })
15197    }
15198
15199    pub fn move_to_beginning(
15200        &mut self,
15201        _: &MoveToBeginning,
15202        window: &mut Window,
15203        cx: &mut Context<Self>,
15204    ) {
15205        if matches!(self.mode, EditorMode::SingleLine) {
15206            cx.propagate();
15207            return;
15208        }
15209        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
15210        self.change_selections(Default::default(), window, cx, |s| {
15211            s.select_ranges(vec![Anchor::min()..Anchor::min()]);
15212        });
15213    }
15214
15215    pub fn select_to_beginning(
15216        &mut self,
15217        _: &SelectToBeginning,
15218        window: &mut Window,
15219        cx: &mut Context<Self>,
15220    ) {
15221        let mut selection = self.selections.last::<Point>(&self.display_snapshot(cx));
15222        selection.set_head(Point::zero(), SelectionGoal::None);
15223        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
15224        self.change_selections(Default::default(), window, cx, |s| {
15225            s.select(vec![selection]);
15226        });
15227    }
15228
15229    pub fn move_to_end(&mut self, _: &MoveToEnd, window: &mut Window, cx: &mut Context<Self>) {
15230        if matches!(self.mode, EditorMode::SingleLine) {
15231            cx.propagate();
15232            return;
15233        }
15234        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
15235        let cursor = self.buffer.read(cx).read(cx).len();
15236        self.change_selections(Default::default(), window, cx, |s| {
15237            s.select_ranges(vec![cursor..cursor])
15238        });
15239    }
15240
15241    pub fn set_nav_history(&mut self, nav_history: Option<ItemNavHistory>) {
15242        self.nav_history = nav_history;
15243    }
15244
15245    pub fn nav_history(&self) -> Option<&ItemNavHistory> {
15246        self.nav_history.as_ref()
15247    }
15248
15249    pub fn create_nav_history_entry(&mut self, cx: &mut Context<Self>) {
15250        self.push_to_nav_history(
15251            self.selections.newest_anchor().head(),
15252            None,
15253            false,
15254            true,
15255            cx,
15256        );
15257    }
15258
15259    fn navigation_data(&self, cursor_anchor: Anchor, cx: &mut Context<Self>) -> NavigationData {
15260        let display_snapshot = self.display_map.update(cx, |map, cx| map.snapshot(cx));
15261        let buffer = self.buffer.read(cx).read(cx);
15262        let cursor_position = cursor_anchor.to_point(&buffer);
15263        let scroll_anchor = self.scroll_manager.native_anchor(&display_snapshot, cx);
15264        let scroll_top_row = scroll_anchor.top_row(&buffer);
15265        drop(buffer);
15266
15267        NavigationData {
15268            cursor_anchor,
15269            cursor_position,
15270            scroll_anchor,
15271            scroll_top_row,
15272        }
15273    }
15274
15275    fn navigation_entry(
15276        &self,
15277        cursor_anchor: Anchor,
15278        cx: &mut Context<Self>,
15279    ) -> Option<NavigationEntry> {
15280        let Some(history) = self.nav_history.clone() else {
15281            return None;
15282        };
15283        let data = self.navigation_data(cursor_anchor, cx);
15284        Some(history.navigation_entry(Some(Arc::new(data) as Arc<dyn Any + Send + Sync>)))
15285    }
15286
15287    fn push_to_nav_history(
15288        &mut self,
15289        cursor_anchor: Anchor,
15290        new_position: Option<Point>,
15291        is_deactivate: bool,
15292        always: bool,
15293        cx: &mut Context<Self>,
15294    ) {
15295        let data = self.navigation_data(cursor_anchor, cx);
15296        if let Some(nav_history) = self.nav_history.as_mut() {
15297            if let Some(new_position) = new_position {
15298                let row_delta = (new_position.row as i64 - data.cursor_position.row as i64).abs();
15299                if row_delta == 0 || (row_delta < MIN_NAVIGATION_HISTORY_ROW_DELTA && !always) {
15300                    return;
15301                }
15302            }
15303
15304            nav_history.push(Some(data), cx);
15305            cx.emit(EditorEvent::PushedToNavHistory {
15306                anchor: cursor_anchor,
15307                is_deactivate,
15308            })
15309        }
15310    }
15311
15312    pub fn select_to_end(&mut self, _: &SelectToEnd, window: &mut Window, cx: &mut Context<Self>) {
15313        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
15314        let buffer = self.buffer.read(cx).snapshot(cx);
15315        let mut selection = self
15316            .selections
15317            .first::<MultiBufferOffset>(&self.display_snapshot(cx));
15318        selection.set_head(buffer.len(), SelectionGoal::None);
15319        self.change_selections(Default::default(), window, cx, |s| {
15320            s.select(vec![selection]);
15321        });
15322    }
15323
15324    pub fn select_all(&mut self, _: &SelectAll, window: &mut Window, cx: &mut Context<Self>) {
15325        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
15326        self.change_selections(SelectionEffects::no_scroll(), window, cx, |s| {
15327            s.select_ranges(vec![Anchor::min()..Anchor::max()]);
15328        });
15329    }
15330
15331    pub fn select_line(&mut self, _: &SelectLine, window: &mut Window, cx: &mut Context<Self>) {
15332        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
15333        let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
15334        let mut selections = self.selections.all::<Point>(&display_map);
15335        let max_point = display_map.buffer_snapshot().max_point();
15336        for selection in &mut selections {
15337            let rows = selection.spanned_rows(true, &display_map);
15338            selection.start = Point::new(rows.start.0, 0);
15339            selection.end = cmp::min(max_point, Point::new(rows.end.0, 0));
15340            selection.reversed = false;
15341        }
15342        self.change_selections(Default::default(), window, cx, |s| {
15343            s.select(selections);
15344        });
15345    }
15346
15347    pub fn split_selection_into_lines(
15348        &mut self,
15349        action: &SplitSelectionIntoLines,
15350        window: &mut Window,
15351        cx: &mut Context<Self>,
15352    ) {
15353        let selections = self
15354            .selections
15355            .all::<Point>(&self.display_snapshot(cx))
15356            .into_iter()
15357            .map(|selection| selection.start..selection.end)
15358            .collect::<Vec<_>>();
15359        self.unfold_ranges(&selections, true, true, cx);
15360
15361        let mut new_selection_ranges = Vec::new();
15362        {
15363            let buffer = self.buffer.read(cx).read(cx);
15364            for selection in selections {
15365                for row in selection.start.row..selection.end.row {
15366                    let line_start = Point::new(row, 0);
15367                    let line_end = Point::new(row, buffer.line_len(MultiBufferRow(row)));
15368
15369                    if action.keep_selections {
15370                        // Keep the selection range for each line
15371                        let selection_start = if row == selection.start.row {
15372                            selection.start
15373                        } else {
15374                            line_start
15375                        };
15376                        new_selection_ranges.push(selection_start..line_end);
15377                    } else {
15378                        // Collapse to cursor at end of line
15379                        new_selection_ranges.push(line_end..line_end);
15380                    }
15381                }
15382
15383                let is_multiline_selection = selection.start.row != selection.end.row;
15384                // Don't insert last one if it's a multi-line selection ending at the start of a line,
15385                // so this action feels more ergonomic when paired with other selection operations
15386                let should_skip_last = is_multiline_selection && selection.end.column == 0;
15387                if !should_skip_last {
15388                    if action.keep_selections {
15389                        if is_multiline_selection {
15390                            let line_start = Point::new(selection.end.row, 0);
15391                            new_selection_ranges.push(line_start..selection.end);
15392                        } else {
15393                            new_selection_ranges.push(selection.start..selection.end);
15394                        }
15395                    } else {
15396                        new_selection_ranges.push(selection.end..selection.end);
15397                    }
15398                }
15399            }
15400        }
15401        self.change_selections(Default::default(), window, cx, |s| {
15402            s.select_ranges(new_selection_ranges);
15403        });
15404    }
15405
15406    pub fn add_selection_above(
15407        &mut self,
15408        action: &AddSelectionAbove,
15409        window: &mut Window,
15410        cx: &mut Context<Self>,
15411    ) {
15412        self.add_selection(true, action.skip_soft_wrap, window, cx);
15413    }
15414
15415    pub fn add_selection_below(
15416        &mut self,
15417        action: &AddSelectionBelow,
15418        window: &mut Window,
15419        cx: &mut Context<Self>,
15420    ) {
15421        self.add_selection(false, action.skip_soft_wrap, window, cx);
15422    }
15423
15424    fn add_selection(
15425        &mut self,
15426        above: bool,
15427        skip_soft_wrap: bool,
15428        window: &mut Window,
15429        cx: &mut Context<Self>,
15430    ) {
15431        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
15432
15433        let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
15434        let all_selections = self.selections.all::<Point>(&display_map);
15435        let text_layout_details = self.text_layout_details(window, cx);
15436
15437        let (mut columnar_selections, new_selections_to_columnarize) = {
15438            if let Some(state) = self.add_selections_state.as_ref() {
15439                let columnar_selection_ids: HashSet<_> = state
15440                    .groups
15441                    .iter()
15442                    .flat_map(|group| group.stack.iter())
15443                    .copied()
15444                    .collect();
15445
15446                all_selections
15447                    .into_iter()
15448                    .partition(|s| columnar_selection_ids.contains(&s.id))
15449            } else {
15450                (Vec::new(), all_selections)
15451            }
15452        };
15453
15454        let mut state = self
15455            .add_selections_state
15456            .take()
15457            .unwrap_or_else(|| AddSelectionsState { groups: Vec::new() });
15458
15459        for selection in new_selections_to_columnarize {
15460            let range = selection.display_range(&display_map).sorted();
15461            let start_x = display_map.x_for_display_point(range.start, &text_layout_details);
15462            let end_x = display_map.x_for_display_point(range.end, &text_layout_details);
15463            let positions = start_x.min(end_x)..start_x.max(end_x);
15464            let mut stack = Vec::new();
15465            for row in range.start.row().0..=range.end.row().0 {
15466                if let Some(selection) = self.selections.build_columnar_selection(
15467                    &display_map,
15468                    DisplayRow(row),
15469                    &positions,
15470                    selection.reversed,
15471                    &text_layout_details,
15472                ) {
15473                    stack.push(selection.id);
15474                    columnar_selections.push(selection);
15475                }
15476            }
15477            if !stack.is_empty() {
15478                if above {
15479                    stack.reverse();
15480                }
15481                state.groups.push(AddSelectionsGroup { above, stack });
15482            }
15483        }
15484
15485        let mut final_selections = Vec::new();
15486        let end_row = if above {
15487            DisplayRow(0)
15488        } else {
15489            display_map.max_point().row()
15490        };
15491
15492        // When `skip_soft_wrap` is true, we use buffer columns instead of pixel
15493        // positions to place new selections, so we need to keep track of the
15494        // column range of the oldest selection in each group, because
15495        // intermediate selections may have been clamped to shorter lines.
15496        // selections may have been clamped to shorter lines.
15497        let mut goal_columns_by_selection_id = if skip_soft_wrap {
15498            let mut map = HashMap::default();
15499            for group in state.groups.iter() {
15500                if let Some(oldest_id) = group.stack.first() {
15501                    if let Some(oldest_selection) =
15502                        columnar_selections.iter().find(|s| s.id == *oldest_id)
15503                    {
15504                        let start_col = oldest_selection.start.column;
15505                        let end_col = oldest_selection.end.column;
15506                        let goal_columns = start_col.min(end_col)..start_col.max(end_col);
15507                        for id in &group.stack {
15508                            map.insert(*id, goal_columns.clone());
15509                        }
15510                    }
15511                }
15512            }
15513            map
15514        } else {
15515            HashMap::default()
15516        };
15517
15518        let mut last_added_item_per_group = HashMap::default();
15519        for group in state.groups.iter_mut() {
15520            if let Some(last_id) = group.stack.last() {
15521                last_added_item_per_group.insert(*last_id, group);
15522            }
15523        }
15524
15525        for selection in columnar_selections {
15526            if let Some(group) = last_added_item_per_group.get_mut(&selection.id) {
15527                if above == group.above {
15528                    let range = selection.display_range(&display_map).sorted();
15529                    debug_assert_eq!(range.start.row(), range.end.row());
15530                    let row = range.start.row();
15531                    let positions =
15532                        if let SelectionGoal::HorizontalRange { start, end } = selection.goal {
15533                            Pixels::from(start)..Pixels::from(end)
15534                        } else {
15535                            let start_x =
15536                                display_map.x_for_display_point(range.start, &text_layout_details);
15537                            let end_x =
15538                                display_map.x_for_display_point(range.end, &text_layout_details);
15539                            start_x.min(end_x)..start_x.max(end_x)
15540                        };
15541
15542                    let maybe_new_selection = if skip_soft_wrap {
15543                        let goal_columns = goal_columns_by_selection_id
15544                            .remove(&selection.id)
15545                            .unwrap_or_else(|| {
15546                                let start_col = selection.start.column;
15547                                let end_col = selection.end.column;
15548                                start_col.min(end_col)..start_col.max(end_col)
15549                            });
15550                        self.selections.find_next_columnar_selection_by_buffer_row(
15551                            &display_map,
15552                            row,
15553                            end_row,
15554                            above,
15555                            &goal_columns,
15556                            selection.reversed,
15557                            &text_layout_details,
15558                        )
15559                    } else {
15560                        self.selections.find_next_columnar_selection_by_display_row(
15561                            &display_map,
15562                            row,
15563                            end_row,
15564                            above,
15565                            &positions,
15566                            selection.reversed,
15567                            &text_layout_details,
15568                        )
15569                    };
15570
15571                    if let Some(new_selection) = maybe_new_selection {
15572                        group.stack.push(new_selection.id);
15573                        if above {
15574                            final_selections.push(new_selection);
15575                            final_selections.push(selection);
15576                        } else {
15577                            final_selections.push(selection);
15578                            final_selections.push(new_selection);
15579                        }
15580                    } else {
15581                        final_selections.push(selection);
15582                    }
15583                } else {
15584                    group.stack.pop();
15585                }
15586            } else {
15587                final_selections.push(selection);
15588            }
15589        }
15590
15591        self.change_selections(Default::default(), window, cx, |s| {
15592            s.select(final_selections);
15593        });
15594
15595        let final_selection_ids: HashSet<_> = self
15596            .selections
15597            .all::<Point>(&display_map)
15598            .iter()
15599            .map(|s| s.id)
15600            .collect();
15601        state.groups.retain_mut(|group| {
15602            // selections might get merged above so we remove invalid items from stacks
15603            group.stack.retain(|id| final_selection_ids.contains(id));
15604
15605            // single selection in stack can be treated as initial state
15606            group.stack.len() > 1
15607        });
15608
15609        if !state.groups.is_empty() {
15610            self.add_selections_state = Some(state);
15611        }
15612    }
15613
15614    pub fn insert_snippet_at_selections(
15615        &mut self,
15616        action: &InsertSnippet,
15617        window: &mut Window,
15618        cx: &mut Context<Self>,
15619    ) {
15620        self.try_insert_snippet_at_selections(action, window, cx)
15621            .log_err();
15622    }
15623
15624    fn try_insert_snippet_at_selections(
15625        &mut self,
15626        action: &InsertSnippet,
15627        window: &mut Window,
15628        cx: &mut Context<Self>,
15629    ) -> Result<()> {
15630        let insertion_ranges = self
15631            .selections
15632            .all::<MultiBufferOffset>(&self.display_snapshot(cx))
15633            .into_iter()
15634            .map(|selection| selection.range())
15635            .collect_vec();
15636
15637        let snippet = if let Some(snippet_body) = &action.snippet {
15638            if action.language.is_none() && action.name.is_none() {
15639                Snippet::parse(snippet_body)?
15640            } else {
15641                bail!("`snippet` is mutually exclusive with `language` and `name`")
15642            }
15643        } else if let Some(name) = &action.name {
15644            let project = self.project().context("no project")?;
15645            let snippet_store = project.read(cx).snippets().read(cx);
15646            let snippet = snippet_store
15647                .snippets_for(action.language.clone(), cx)
15648                .into_iter()
15649                .find(|snippet| snippet.name == *name)
15650                .context("snippet not found")?;
15651            Snippet::parse(&snippet.body)?
15652        } else {
15653            // todo(andrew): open modal to select snippet
15654            bail!("`name` or `snippet` is required")
15655        };
15656
15657        self.insert_snippet(&insertion_ranges, snippet, window, cx)
15658    }
15659
15660    fn select_match_ranges(
15661        &mut self,
15662        range: Range<MultiBufferOffset>,
15663        reversed: bool,
15664        replace_newest: bool,
15665        auto_scroll: Option<Autoscroll>,
15666        window: &mut Window,
15667        cx: &mut Context<Editor>,
15668    ) {
15669        self.unfold_ranges(
15670            std::slice::from_ref(&range),
15671            false,
15672            auto_scroll.is_some(),
15673            cx,
15674        );
15675        let effects = if let Some(scroll) = auto_scroll {
15676            SelectionEffects::scroll(scroll)
15677        } else {
15678            SelectionEffects::no_scroll()
15679        };
15680        self.change_selections(effects, window, cx, |s| {
15681            if replace_newest {
15682                s.delete(s.newest_anchor().id);
15683            }
15684            if reversed {
15685                s.insert_range(range.end..range.start);
15686            } else {
15687                s.insert_range(range);
15688            }
15689        });
15690    }
15691
15692    pub fn select_next_match_internal(
15693        &mut self,
15694        display_map: &DisplaySnapshot,
15695        replace_newest: bool,
15696        autoscroll: Option<Autoscroll>,
15697        window: &mut Window,
15698        cx: &mut Context<Self>,
15699    ) -> Result<()> {
15700        let buffer = display_map.buffer_snapshot();
15701        let mut selections = self.selections.all::<MultiBufferOffset>(&display_map);
15702        if let Some(mut select_next_state) = self.select_next_state.take() {
15703            let query = &select_next_state.query;
15704            if !select_next_state.done {
15705                let first_selection = selections.iter().min_by_key(|s| s.id).unwrap();
15706                let last_selection = selections.iter().max_by_key(|s| s.id).unwrap();
15707                let mut next_selected_range = None;
15708
15709                let bytes_after_last_selection =
15710                    buffer.bytes_in_range(last_selection.end..buffer.len());
15711                let bytes_before_first_selection =
15712                    buffer.bytes_in_range(MultiBufferOffset(0)..first_selection.start);
15713                let query_matches = query
15714                    .stream_find_iter(bytes_after_last_selection)
15715                    .map(|result| (last_selection.end, result))
15716                    .chain(
15717                        query
15718                            .stream_find_iter(bytes_before_first_selection)
15719                            .map(|result| (MultiBufferOffset(0), result)),
15720                    );
15721
15722                for (start_offset, query_match) in query_matches {
15723                    let query_match = query_match.unwrap(); // can only fail due to I/O
15724                    let offset_range =
15725                        start_offset + query_match.start()..start_offset + query_match.end();
15726
15727                    if !select_next_state.wordwise
15728                        || (!buffer.is_inside_word(offset_range.start, None)
15729                            && !buffer.is_inside_word(offset_range.end, None))
15730                    {
15731                        let idx = selections
15732                            .partition_point(|selection| selection.end <= offset_range.start);
15733                        let overlaps = selections
15734                            .get(idx)
15735                            .map_or(false, |selection| selection.start < offset_range.end);
15736
15737                        if !overlaps {
15738                            next_selected_range = Some(offset_range);
15739                            break;
15740                        }
15741                    }
15742                }
15743
15744                if let Some(next_selected_range) = next_selected_range {
15745                    self.select_match_ranges(
15746                        next_selected_range,
15747                        last_selection.reversed,
15748                        replace_newest,
15749                        autoscroll,
15750                        window,
15751                        cx,
15752                    );
15753                } else {
15754                    select_next_state.done = true;
15755                }
15756            }
15757
15758            self.select_next_state = Some(select_next_state);
15759        } else {
15760            let mut only_carets = true;
15761            let mut same_text_selected = true;
15762            let mut selected_text = None;
15763
15764            let mut selections_iter = selections.iter().peekable();
15765            while let Some(selection) = selections_iter.next() {
15766                if selection.start != selection.end {
15767                    only_carets = false;
15768                }
15769
15770                if same_text_selected {
15771                    if selected_text.is_none() {
15772                        selected_text =
15773                            Some(buffer.text_for_range(selection.range()).collect::<String>());
15774                    }
15775
15776                    if let Some(next_selection) = selections_iter.peek() {
15777                        if next_selection.len() == selection.len() {
15778                            let next_selected_text = buffer
15779                                .text_for_range(next_selection.range())
15780                                .collect::<String>();
15781                            if Some(next_selected_text) != selected_text {
15782                                same_text_selected = false;
15783                                selected_text = None;
15784                            }
15785                        } else {
15786                            same_text_selected = false;
15787                            selected_text = None;
15788                        }
15789                    }
15790                }
15791            }
15792
15793            if only_carets {
15794                for selection in &mut selections {
15795                    let (word_range, _) = buffer.surrounding_word(selection.start, None);
15796                    selection.start = word_range.start;
15797                    selection.end = word_range.end;
15798                    selection.goal = SelectionGoal::None;
15799                    selection.reversed = false;
15800                    self.select_match_ranges(
15801                        selection.start..selection.end,
15802                        selection.reversed,
15803                        replace_newest,
15804                        autoscroll,
15805                        window,
15806                        cx,
15807                    );
15808                }
15809
15810                if selections.len() == 1 {
15811                    let selection = selections
15812                        .last()
15813                        .expect("ensured that there's only one selection");
15814                    let query = buffer
15815                        .text_for_range(selection.start..selection.end)
15816                        .collect::<String>();
15817                    let is_empty = query.is_empty();
15818                    let select_state = SelectNextState {
15819                        query: self.build_query(&[query], cx)?,
15820                        wordwise: true,
15821                        done: is_empty,
15822                    };
15823                    self.select_next_state = Some(select_state);
15824                } else {
15825                    self.select_next_state = None;
15826                }
15827            } else if let Some(selected_text) = selected_text {
15828                self.select_next_state = Some(SelectNextState {
15829                    query: self.build_query(&[selected_text], cx)?,
15830                    wordwise: false,
15831                    done: false,
15832                });
15833                self.select_next_match_internal(
15834                    display_map,
15835                    replace_newest,
15836                    autoscroll,
15837                    window,
15838                    cx,
15839                )?;
15840            }
15841        }
15842        Ok(())
15843    }
15844
15845    pub fn select_all_matches(
15846        &mut self,
15847        _action: &SelectAllMatches,
15848        window: &mut Window,
15849        cx: &mut Context<Self>,
15850    ) -> Result<()> {
15851        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
15852
15853        let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
15854
15855        self.select_next_match_internal(&display_map, false, None, window, cx)?;
15856        let Some(select_next_state) = self.select_next_state.as_mut().filter(|state| !state.done)
15857        else {
15858            return Ok(());
15859        };
15860
15861        let mut new_selections = Vec::new();
15862
15863        let reversed = self
15864            .selections
15865            .oldest::<MultiBufferOffset>(&display_map)
15866            .reversed;
15867        let buffer = display_map.buffer_snapshot();
15868        let query_matches = select_next_state
15869            .query
15870            .stream_find_iter(buffer.bytes_in_range(MultiBufferOffset(0)..buffer.len()));
15871
15872        for query_match in query_matches.into_iter() {
15873            let query_match = query_match.context("query match for select all action")?; // can only fail due to I/O
15874            let offset_range = if reversed {
15875                MultiBufferOffset(query_match.end())..MultiBufferOffset(query_match.start())
15876            } else {
15877                MultiBufferOffset(query_match.start())..MultiBufferOffset(query_match.end())
15878            };
15879
15880            if !select_next_state.wordwise
15881                || (!buffer.is_inside_word(offset_range.start, None)
15882                    && !buffer.is_inside_word(offset_range.end, None))
15883            {
15884                new_selections.push(offset_range.start..offset_range.end);
15885            }
15886        }
15887
15888        select_next_state.done = true;
15889
15890        if new_selections.is_empty() {
15891            log::error!("bug: new_selections is empty in select_all_matches");
15892            return Ok(());
15893        }
15894
15895        self.unfold_ranges(&new_selections, false, false, cx);
15896        self.change_selections(SelectionEffects::no_scroll(), window, cx, |selections| {
15897            selections.select_ranges(new_selections)
15898        });
15899
15900        Ok(())
15901    }
15902
15903    pub fn select_next(
15904        &mut self,
15905        action: &SelectNext,
15906        window: &mut Window,
15907        cx: &mut Context<Self>,
15908    ) -> Result<()> {
15909        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
15910        let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
15911        self.select_next_match_internal(
15912            &display_map,
15913            action.replace_newest,
15914            Some(Autoscroll::newest()),
15915            window,
15916            cx,
15917        )
15918    }
15919
15920    pub fn select_previous(
15921        &mut self,
15922        action: &SelectPrevious,
15923        window: &mut Window,
15924        cx: &mut Context<Self>,
15925    ) -> Result<()> {
15926        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
15927        let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
15928        let buffer = display_map.buffer_snapshot();
15929        let mut selections = self.selections.all::<MultiBufferOffset>(&display_map);
15930        if let Some(mut select_prev_state) = self.select_prev_state.take() {
15931            let query = &select_prev_state.query;
15932            if !select_prev_state.done {
15933                let first_selection = selections.iter().min_by_key(|s| s.id).unwrap();
15934                let last_selection = selections.iter().max_by_key(|s| s.id).unwrap();
15935                let mut next_selected_range = None;
15936                // When we're iterating matches backwards, the oldest match will actually be the furthest one in the buffer.
15937                let bytes_before_last_selection =
15938                    buffer.reversed_bytes_in_range(MultiBufferOffset(0)..last_selection.start);
15939                let bytes_after_first_selection =
15940                    buffer.reversed_bytes_in_range(first_selection.end..buffer.len());
15941                let query_matches = query
15942                    .stream_find_iter(bytes_before_last_selection)
15943                    .map(|result| (last_selection.start, result))
15944                    .chain(
15945                        query
15946                            .stream_find_iter(bytes_after_first_selection)
15947                            .map(|result| (buffer.len(), result)),
15948                    );
15949                for (end_offset, query_match) in query_matches {
15950                    let query_match = query_match.unwrap(); // can only fail due to I/O
15951                    let offset_range =
15952                        end_offset - query_match.end()..end_offset - query_match.start();
15953
15954                    if !select_prev_state.wordwise
15955                        || (!buffer.is_inside_word(offset_range.start, None)
15956                            && !buffer.is_inside_word(offset_range.end, None))
15957                    {
15958                        next_selected_range = Some(offset_range);
15959                        break;
15960                    }
15961                }
15962
15963                if let Some(next_selected_range) = next_selected_range {
15964                    self.select_match_ranges(
15965                        next_selected_range,
15966                        last_selection.reversed,
15967                        action.replace_newest,
15968                        Some(Autoscroll::newest()),
15969                        window,
15970                        cx,
15971                    );
15972                } else {
15973                    select_prev_state.done = true;
15974                }
15975            }
15976
15977            self.select_prev_state = Some(select_prev_state);
15978        } else {
15979            let mut only_carets = true;
15980            let mut same_text_selected = true;
15981            let mut selected_text = None;
15982
15983            let mut selections_iter = selections.iter().peekable();
15984            while let Some(selection) = selections_iter.next() {
15985                if selection.start != selection.end {
15986                    only_carets = false;
15987                }
15988
15989                if same_text_selected {
15990                    if selected_text.is_none() {
15991                        selected_text =
15992                            Some(buffer.text_for_range(selection.range()).collect::<String>());
15993                    }
15994
15995                    if let Some(next_selection) = selections_iter.peek() {
15996                        if next_selection.len() == selection.len() {
15997                            let next_selected_text = buffer
15998                                .text_for_range(next_selection.range())
15999                                .collect::<String>();
16000                            if Some(next_selected_text) != selected_text {
16001                                same_text_selected = false;
16002                                selected_text = None;
16003                            }
16004                        } else {
16005                            same_text_selected = false;
16006                            selected_text = None;
16007                        }
16008                    }
16009                }
16010            }
16011
16012            if only_carets {
16013                for selection in &mut selections {
16014                    let (word_range, _) = buffer.surrounding_word(selection.start, None);
16015                    selection.start = word_range.start;
16016                    selection.end = word_range.end;
16017                    selection.goal = SelectionGoal::None;
16018                    selection.reversed = false;
16019                    self.select_match_ranges(
16020                        selection.start..selection.end,
16021                        selection.reversed,
16022                        action.replace_newest,
16023                        Some(Autoscroll::newest()),
16024                        window,
16025                        cx,
16026                    );
16027                }
16028                if selections.len() == 1 {
16029                    let selection = selections
16030                        .last()
16031                        .expect("ensured that there's only one selection");
16032                    let query = buffer
16033                        .text_for_range(selection.start..selection.end)
16034                        .collect::<String>();
16035                    let is_empty = query.is_empty();
16036                    let select_state = SelectNextState {
16037                        query: self.build_query(&[query.chars().rev().collect::<String>()], cx)?,
16038                        wordwise: true,
16039                        done: is_empty,
16040                    };
16041                    self.select_prev_state = Some(select_state);
16042                } else {
16043                    self.select_prev_state = None;
16044                }
16045            } else if let Some(selected_text) = selected_text {
16046                self.select_prev_state = Some(SelectNextState {
16047                    query: self
16048                        .build_query(&[selected_text.chars().rev().collect::<String>()], cx)?,
16049                    wordwise: false,
16050                    done: false,
16051                });
16052                self.select_previous(action, window, cx)?;
16053            }
16054        }
16055        Ok(())
16056    }
16057
16058    /// Builds an `AhoCorasick` automaton from the provided patterns, while
16059    /// setting the case sensitivity based on the global
16060    /// `SelectNextCaseSensitive` setting, if set, otherwise based on the
16061    /// editor's settings.
16062    fn build_query<I, P>(&self, patterns: I, cx: &Context<Self>) -> Result<AhoCorasick, BuildError>
16063    where
16064        I: IntoIterator<Item = P>,
16065        P: AsRef<[u8]>,
16066    {
16067        let case_sensitive = self
16068            .select_next_is_case_sensitive
16069            .unwrap_or_else(|| EditorSettings::get_global(cx).search.case_sensitive);
16070
16071        let mut builder = AhoCorasickBuilder::new();
16072        builder.ascii_case_insensitive(!case_sensitive);
16073        builder.build(patterns)
16074    }
16075
16076    pub fn find_next_match(
16077        &mut self,
16078        _: &FindNextMatch,
16079        window: &mut Window,
16080        cx: &mut Context<Self>,
16081    ) -> Result<()> {
16082        let selections = self.selections.disjoint_anchors_arc();
16083        match selections.first() {
16084            Some(first) if selections.len() >= 2 => {
16085                self.change_selections(Default::default(), window, cx, |s| {
16086                    s.select_ranges([first.range()]);
16087                });
16088            }
16089            _ => self.select_next(
16090                &SelectNext {
16091                    replace_newest: true,
16092                },
16093                window,
16094                cx,
16095            )?,
16096        }
16097        Ok(())
16098    }
16099
16100    pub fn find_previous_match(
16101        &mut self,
16102        _: &FindPreviousMatch,
16103        window: &mut Window,
16104        cx: &mut Context<Self>,
16105    ) -> Result<()> {
16106        let selections = self.selections.disjoint_anchors_arc();
16107        match selections.last() {
16108            Some(last) if selections.len() >= 2 => {
16109                self.change_selections(Default::default(), window, cx, |s| {
16110                    s.select_ranges([last.range()]);
16111                });
16112            }
16113            _ => self.select_previous(
16114                &SelectPrevious {
16115                    replace_newest: true,
16116                },
16117                window,
16118                cx,
16119            )?,
16120        }
16121        Ok(())
16122    }
16123
16124    pub fn toggle_comments(
16125        &mut self,
16126        action: &ToggleComments,
16127        window: &mut Window,
16128        cx: &mut Context<Self>,
16129    ) {
16130        if self.read_only(cx) {
16131            return;
16132        }
16133        self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
16134        let text_layout_details = &self.text_layout_details(window, cx);
16135        self.transact(window, cx, |this, window, cx| {
16136            let mut selections = this
16137                .selections
16138                .all::<MultiBufferPoint>(&this.display_snapshot(cx));
16139            let mut edits = Vec::new();
16140            let mut selection_edit_ranges = Vec::new();
16141            let mut last_toggled_row = None;
16142            let snapshot = this.buffer.read(cx).read(cx);
16143            let empty_str: Arc<str> = Arc::default();
16144            let mut suffixes_inserted = Vec::new();
16145            let ignore_indent = action.ignore_indent;
16146
16147            fn comment_prefix_range(
16148                snapshot: &MultiBufferSnapshot,
16149                row: MultiBufferRow,
16150                comment_prefix: &str,
16151                comment_prefix_whitespace: &str,
16152                ignore_indent: bool,
16153            ) -> Range<Point> {
16154                let indent_size = if ignore_indent {
16155                    0
16156                } else {
16157                    snapshot.indent_size_for_line(row).len
16158                };
16159
16160                let start = Point::new(row.0, indent_size);
16161
16162                let mut line_bytes = snapshot
16163                    .bytes_in_range(start..snapshot.max_point())
16164                    .flatten()
16165                    .copied();
16166
16167                // If this line currently begins with the line comment prefix, then record
16168                // the range containing the prefix.
16169                if line_bytes
16170                    .by_ref()
16171                    .take(comment_prefix.len())
16172                    .eq(comment_prefix.bytes())
16173                {
16174                    // Include any whitespace that matches the comment prefix.
16175                    let matching_whitespace_len = line_bytes
16176                        .zip(comment_prefix_whitespace.bytes())
16177                        .take_while(|(a, b)| a == b)
16178                        .count() as u32;
16179                    let end = Point::new(
16180                        start.row,
16181                        start.column + comment_prefix.len() as u32 + matching_whitespace_len,
16182                    );
16183                    start..end
16184                } else {
16185                    start..start
16186                }
16187            }
16188
16189            fn comment_suffix_range(
16190                snapshot: &MultiBufferSnapshot,
16191                row: MultiBufferRow,
16192                comment_suffix: &str,
16193                comment_suffix_has_leading_space: bool,
16194            ) -> Range<Point> {
16195                let end = Point::new(row.0, snapshot.line_len(row));
16196                let suffix_start_column = end.column.saturating_sub(comment_suffix.len() as u32);
16197
16198                let mut line_end_bytes = snapshot
16199                    .bytes_in_range(Point::new(end.row, suffix_start_column.saturating_sub(1))..end)
16200                    .flatten()
16201                    .copied();
16202
16203                let leading_space_len = if suffix_start_column > 0
16204                    && line_end_bytes.next() == Some(b' ')
16205                    && comment_suffix_has_leading_space
16206                {
16207                    1
16208                } else {
16209                    0
16210                };
16211
16212                // If this line currently begins with the line comment prefix, then record
16213                // the range containing the prefix.
16214                if line_end_bytes.by_ref().eq(comment_suffix.bytes()) {
16215                    let start = Point::new(end.row, suffix_start_column - leading_space_len);
16216                    start..end
16217                } else {
16218                    end..end
16219                }
16220            }
16221
16222            // TODO: Handle selections that cross excerpts
16223            for selection in &mut selections {
16224                let start_column = snapshot
16225                    .indent_size_for_line(MultiBufferRow(selection.start.row))
16226                    .len;
16227                let language = if let Some(language) =
16228                    snapshot.language_scope_at(Point::new(selection.start.row, start_column))
16229                {
16230                    language
16231                } else {
16232                    continue;
16233                };
16234
16235                selection_edit_ranges.clear();
16236
16237                // If multiple selections contain a given row, avoid processing that
16238                // row more than once.
16239                let mut start_row = MultiBufferRow(selection.start.row);
16240                if last_toggled_row == Some(start_row) {
16241                    start_row = start_row.next_row();
16242                }
16243                let end_row =
16244                    if selection.end.row > selection.start.row && selection.end.column == 0 {
16245                        MultiBufferRow(selection.end.row - 1)
16246                    } else {
16247                        MultiBufferRow(selection.end.row)
16248                    };
16249                last_toggled_row = Some(end_row);
16250
16251                if start_row > end_row {
16252                    continue;
16253                }
16254
16255                // If the language has line comments, toggle those.
16256                let mut full_comment_prefixes = language.line_comment_prefixes().to_vec();
16257
16258                // If ignore_indent is set, trim spaces from the right side of all full_comment_prefixes
16259                if ignore_indent {
16260                    full_comment_prefixes = full_comment_prefixes
16261                        .into_iter()
16262                        .map(|s| Arc::from(s.trim_end()))
16263                        .collect();
16264                }
16265
16266                if !full_comment_prefixes.is_empty() {
16267                    let first_prefix = full_comment_prefixes
16268                        .first()
16269                        .expect("prefixes is non-empty");
16270                    let prefix_trimmed_lengths = full_comment_prefixes
16271                        .iter()
16272                        .map(|p| p.trim_end_matches(' ').len())
16273                        .collect::<SmallVec<[usize; 4]>>();
16274
16275                    let mut all_selection_lines_are_comments = true;
16276
16277                    for row in start_row.0..=end_row.0 {
16278                        let row = MultiBufferRow(row);
16279                        if start_row < end_row && snapshot.is_line_blank(row) {
16280                            continue;
16281                        }
16282
16283                        let prefix_range = full_comment_prefixes
16284                            .iter()
16285                            .zip(prefix_trimmed_lengths.iter().copied())
16286                            .map(|(prefix, trimmed_prefix_len)| {
16287                                comment_prefix_range(
16288                                    snapshot.deref(),
16289                                    row,
16290                                    &prefix[..trimmed_prefix_len],
16291                                    &prefix[trimmed_prefix_len..],
16292                                    ignore_indent,
16293                                )
16294                            })
16295                            .max_by_key(|range| range.end.column - range.start.column)
16296                            .expect("prefixes is non-empty");
16297
16298                        if prefix_range.is_empty() {
16299                            all_selection_lines_are_comments = false;
16300                        }
16301
16302                        selection_edit_ranges.push(prefix_range);
16303                    }
16304
16305                    if all_selection_lines_are_comments {
16306                        edits.extend(
16307                            selection_edit_ranges
16308                                .iter()
16309                                .cloned()
16310                                .map(|range| (range, empty_str.clone())),
16311                        );
16312                    } else {
16313                        let min_column = selection_edit_ranges
16314                            .iter()
16315                            .map(|range| range.start.column)
16316                            .min()
16317                            .unwrap_or(0);
16318                        edits.extend(selection_edit_ranges.iter().map(|range| {
16319                            let position = Point::new(range.start.row, min_column);
16320                            (position..position, first_prefix.clone())
16321                        }));
16322                    }
16323                } else if let Some(BlockCommentConfig {
16324                    start: full_comment_prefix,
16325                    end: comment_suffix,
16326                    ..
16327                }) = language.block_comment()
16328                {
16329                    let comment_prefix = full_comment_prefix.trim_end_matches(' ');
16330                    let comment_prefix_whitespace = &full_comment_prefix[comment_prefix.len()..];
16331                    let prefix_range = comment_prefix_range(
16332                        snapshot.deref(),
16333                        start_row,
16334                        comment_prefix,
16335                        comment_prefix_whitespace,
16336                        ignore_indent,
16337                    );
16338                    let suffix_range = comment_suffix_range(
16339                        snapshot.deref(),
16340                        end_row,
16341                        comment_suffix.trim_start_matches(' '),
16342                        comment_suffix.starts_with(' '),
16343                    );
16344
16345                    if prefix_range.is_empty() || suffix_range.is_empty() {
16346                        edits.push((
16347                            prefix_range.start..prefix_range.start,
16348                            full_comment_prefix.clone(),
16349                        ));
16350                        edits.push((suffix_range.end..suffix_range.end, comment_suffix.clone()));
16351                        suffixes_inserted.push((end_row, comment_suffix.len()));
16352                    } else {
16353                        edits.push((prefix_range, empty_str.clone()));
16354                        edits.push((suffix_range, empty_str.clone()));
16355                    }
16356                } else {
16357                    continue;
16358                }
16359            }
16360
16361            drop(snapshot);
16362            this.buffer.update(cx, |buffer, cx| {
16363                buffer.edit(edits, None, cx);
16364            });
16365
16366            // Adjust selections so that they end before any comment suffixes that
16367            // were inserted.
16368            let mut suffixes_inserted = suffixes_inserted.into_iter().peekable();
16369            let mut selections = this.selections.all::<Point>(&this.display_snapshot(cx));
16370            let snapshot = this.buffer.read(cx).read(cx);
16371            for selection in &mut selections {
16372                while let Some((row, suffix_len)) = suffixes_inserted.peek().copied() {
16373                    match row.cmp(&MultiBufferRow(selection.end.row)) {
16374                        Ordering::Less => {
16375                            suffixes_inserted.next();
16376                            continue;
16377                        }
16378                        Ordering::Greater => break,
16379                        Ordering::Equal => {
16380                            if selection.end.column == snapshot.line_len(row) {
16381                                if selection.is_empty() {
16382                                    selection.start.column -= suffix_len as u32;
16383                                }
16384                                selection.end.column -= suffix_len as u32;
16385                            }
16386                            break;
16387                        }
16388                    }
16389                }
16390            }
16391
16392            drop(snapshot);
16393            this.change_selections(Default::default(), window, cx, |s| s.select(selections));
16394
16395            let selections = this.selections.all::<Point>(&this.display_snapshot(cx));
16396            let selections_on_single_row = selections.windows(2).all(|selections| {
16397                selections[0].start.row == selections[1].start.row
16398                    && selections[0].end.row == selections[1].end.row
16399                    && selections[0].start.row == selections[0].end.row
16400            });
16401            let selections_selecting = selections
16402                .iter()
16403                .any(|selection| selection.start != selection.end);
16404            let advance_downwards = action.advance_downwards
16405                && selections_on_single_row
16406                && !selections_selecting
16407                && !matches!(this.mode, EditorMode::SingleLine);
16408
16409            if advance_downwards {
16410                let snapshot = this.buffer.read(cx).snapshot(cx);
16411
16412                this.change_selections(Default::default(), window, cx, |s| {
16413                    s.move_cursors_with(&mut |display_snapshot, display_point, _| {
16414                        let mut point = display_point.to_point(display_snapshot);
16415                        point.row += 1;
16416                        point = snapshot.clip_point(point, Bias::Left);
16417                        let display_point = point.to_display_point(display_snapshot);
16418                        let goal = SelectionGoal::HorizontalPosition(
16419                            display_snapshot
16420                                .x_for_display_point(display_point, text_layout_details)
16421                                .into(),
16422                        );
16423                        (display_point, goal)
16424                    })
16425                });
16426            }
16427        });
16428    }
16429
16430    pub fn select_enclosing_symbol(
16431        &mut self,
16432        _: &SelectEnclosingSymbol,
16433        window: &mut Window,
16434        cx: &mut Context<Self>,
16435    ) {
16436        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
16437
16438        let buffer = self.buffer.read(cx).snapshot(cx);
16439        let old_selections = self
16440            .selections
16441            .all::<MultiBufferOffset>(&self.display_snapshot(cx))
16442            .into_boxed_slice();
16443
16444        fn update_selection(
16445            selection: &Selection<MultiBufferOffset>,
16446            buffer_snap: &MultiBufferSnapshot,
16447        ) -> Option<Selection<MultiBufferOffset>> {
16448            let cursor = selection.head();
16449            let (_buffer_id, symbols) = buffer_snap.symbols_containing(cursor, None)?;
16450            for symbol in symbols.iter().rev() {
16451                let start = symbol.range.start.to_offset(buffer_snap);
16452                let end = symbol.range.end.to_offset(buffer_snap);
16453                let new_range = start..end;
16454                if start < selection.start || end > selection.end {
16455                    return Some(Selection {
16456                        id: selection.id,
16457                        start: new_range.start,
16458                        end: new_range.end,
16459                        goal: SelectionGoal::None,
16460                        reversed: selection.reversed,
16461                    });
16462                }
16463            }
16464            None
16465        }
16466
16467        let mut selected_larger_symbol = false;
16468        let new_selections = old_selections
16469            .iter()
16470            .map(|selection| match update_selection(selection, &buffer) {
16471                Some(new_selection) => {
16472                    if new_selection.range() != selection.range() {
16473                        selected_larger_symbol = true;
16474                    }
16475                    new_selection
16476                }
16477                None => selection.clone(),
16478            })
16479            .collect::<Vec<_>>();
16480
16481        if selected_larger_symbol {
16482            self.change_selections(Default::default(), window, cx, |s| {
16483                s.select(new_selections);
16484            });
16485        }
16486    }
16487
16488    pub fn select_larger_syntax_node(
16489        &mut self,
16490        _: &SelectLargerSyntaxNode,
16491        window: &mut Window,
16492        cx: &mut Context<Self>,
16493    ) {
16494        let Some(visible_row_count) = self.visible_row_count() else {
16495            return;
16496        };
16497        let old_selections: Box<[_]> = self
16498            .selections
16499            .all::<MultiBufferOffset>(&self.display_snapshot(cx))
16500            .into();
16501        if old_selections.is_empty() {
16502            return;
16503        }
16504
16505        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
16506
16507        let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
16508        let buffer = self.buffer.read(cx).snapshot(cx);
16509
16510        let mut selected_larger_node = false;
16511        let mut new_selections = old_selections
16512            .iter()
16513            .map(|selection| {
16514                let old_range = selection.start..selection.end;
16515
16516                if let Some((node, _)) = buffer.syntax_ancestor(old_range.clone()) {
16517                    // manually select word at selection
16518                    if ["string_content", "inline"].contains(&node.kind()) {
16519                        let (word_range, _) = buffer.surrounding_word(old_range.start, None);
16520                        // ignore if word is already selected
16521                        if !word_range.is_empty() && old_range != word_range {
16522                            let (last_word_range, _) = buffer.surrounding_word(old_range.end, None);
16523                            // only select word if start and end point belongs to same word
16524                            if word_range == last_word_range {
16525                                selected_larger_node = true;
16526                                return Selection {
16527                                    id: selection.id,
16528                                    start: word_range.start,
16529                                    end: word_range.end,
16530                                    goal: SelectionGoal::None,
16531                                    reversed: selection.reversed,
16532                                };
16533                            }
16534                        }
16535                    }
16536                }
16537
16538                let mut new_range = old_range.clone();
16539                while let Some((node, range)) = buffer.syntax_ancestor(new_range.clone()) {
16540                    new_range = range;
16541                    if !node.is_named() {
16542                        continue;
16543                    }
16544                    if !display_map.intersects_fold(new_range.start)
16545                        && !display_map.intersects_fold(new_range.end)
16546                    {
16547                        break;
16548                    }
16549                }
16550
16551                selected_larger_node |= new_range != old_range;
16552                Selection {
16553                    id: selection.id,
16554                    start: new_range.start,
16555                    end: new_range.end,
16556                    goal: SelectionGoal::None,
16557                    reversed: selection.reversed,
16558                }
16559            })
16560            .collect::<Vec<_>>();
16561
16562        if !selected_larger_node {
16563            return; // don't put this call in the history
16564        }
16565
16566        // scroll based on transformation done to the last selection created by the user
16567        let (last_old, last_new) = old_selections
16568            .last()
16569            .zip(new_selections.last().cloned())
16570            .expect("old_selections isn't empty");
16571
16572        // revert selection
16573        let is_selection_reversed = {
16574            let should_newest_selection_be_reversed = last_old.start != last_new.start;
16575            new_selections.last_mut().expect("checked above").reversed =
16576                should_newest_selection_be_reversed;
16577            should_newest_selection_be_reversed
16578        };
16579
16580        if selected_larger_node {
16581            self.select_syntax_node_history.disable_clearing = true;
16582            self.change_selections(SelectionEffects::no_scroll(), window, cx, |s| {
16583                s.select(new_selections.clone());
16584            });
16585            self.select_syntax_node_history.disable_clearing = false;
16586        }
16587
16588        let start_row = last_new.start.to_display_point(&display_map).row().0;
16589        let end_row = last_new.end.to_display_point(&display_map).row().0;
16590        let selection_height = end_row - start_row + 1;
16591        let scroll_margin_rows = self.vertical_scroll_margin() as u32;
16592
16593        let fits_on_the_screen = visible_row_count >= selection_height + scroll_margin_rows * 2;
16594        let scroll_behavior = if fits_on_the_screen {
16595            self.request_autoscroll(Autoscroll::fit(), cx);
16596            SelectSyntaxNodeScrollBehavior::FitSelection
16597        } else if is_selection_reversed {
16598            self.scroll_cursor_top(&ScrollCursorTop, window, cx);
16599            SelectSyntaxNodeScrollBehavior::CursorTop
16600        } else {
16601            self.scroll_cursor_bottom(&ScrollCursorBottom, window, cx);
16602            SelectSyntaxNodeScrollBehavior::CursorBottom
16603        };
16604
16605        self.select_syntax_node_history.push((
16606            old_selections,
16607            scroll_behavior,
16608            is_selection_reversed,
16609        ));
16610    }
16611
16612    pub fn select_smaller_syntax_node(
16613        &mut self,
16614        _: &SelectSmallerSyntaxNode,
16615        window: &mut Window,
16616        cx: &mut Context<Self>,
16617    ) {
16618        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
16619
16620        if let Some((mut selections, scroll_behavior, is_selection_reversed)) =
16621            self.select_syntax_node_history.pop()
16622        {
16623            if let Some(selection) = selections.last_mut() {
16624                selection.reversed = is_selection_reversed;
16625            }
16626
16627            self.select_syntax_node_history.disable_clearing = true;
16628            self.change_selections(SelectionEffects::no_scroll(), window, cx, |s| {
16629                s.select(selections.to_vec());
16630            });
16631            self.select_syntax_node_history.disable_clearing = false;
16632
16633            match scroll_behavior {
16634                SelectSyntaxNodeScrollBehavior::CursorTop => {
16635                    self.scroll_cursor_top(&ScrollCursorTop, window, cx);
16636                }
16637                SelectSyntaxNodeScrollBehavior::FitSelection => {
16638                    self.request_autoscroll(Autoscroll::fit(), cx);
16639                }
16640                SelectSyntaxNodeScrollBehavior::CursorBottom => {
16641                    self.scroll_cursor_bottom(&ScrollCursorBottom, window, cx);
16642                }
16643            }
16644        }
16645    }
16646
16647    pub fn unwrap_syntax_node(
16648        &mut self,
16649        _: &UnwrapSyntaxNode,
16650        window: &mut Window,
16651        cx: &mut Context<Self>,
16652    ) {
16653        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
16654
16655        let buffer = self.buffer.read(cx).snapshot(cx);
16656        let selections = self
16657            .selections
16658            .all::<MultiBufferOffset>(&self.display_snapshot(cx))
16659            .into_iter()
16660            // subtracting the offset requires sorting
16661            .sorted_by_key(|i| i.start);
16662
16663        let full_edits = selections
16664            .into_iter()
16665            .filter_map(|selection| {
16666                let child = if selection.is_empty()
16667                    && let Some((_, ancestor_range)) =
16668                        buffer.syntax_ancestor(selection.start..selection.end)
16669                {
16670                    ancestor_range
16671                } else {
16672                    selection.range()
16673                };
16674
16675                let mut parent = child.clone();
16676                while let Some((_, ancestor_range)) = buffer.syntax_ancestor(parent.clone()) {
16677                    parent = ancestor_range;
16678                    if parent.start < child.start || parent.end > child.end {
16679                        break;
16680                    }
16681                }
16682
16683                if parent == child {
16684                    return None;
16685                }
16686                let text = buffer.text_for_range(child).collect::<String>();
16687                Some((selection.id, parent, text))
16688            })
16689            .collect::<Vec<_>>();
16690        if full_edits.is_empty() {
16691            return;
16692        }
16693
16694        self.transact(window, cx, |this, window, cx| {
16695            this.buffer.update(cx, |buffer, cx| {
16696                buffer.edit(
16697                    full_edits
16698                        .iter()
16699                        .map(|(_, p, t)| (p.clone(), t.clone()))
16700                        .collect::<Vec<_>>(),
16701                    None,
16702                    cx,
16703                );
16704            });
16705            this.change_selections(Default::default(), window, cx, |s| {
16706                let mut offset = 0;
16707                let mut selections = vec![];
16708                for (id, parent, text) in full_edits {
16709                    let start = parent.start - offset;
16710                    offset += (parent.end - parent.start) - text.len();
16711                    selections.push(Selection {
16712                        id,
16713                        start,
16714                        end: start + text.len(),
16715                        reversed: false,
16716                        goal: Default::default(),
16717                    });
16718                }
16719                s.select(selections);
16720            });
16721        });
16722    }
16723
16724    pub fn select_next_syntax_node(
16725        &mut self,
16726        _: &SelectNextSyntaxNode,
16727        window: &mut Window,
16728        cx: &mut Context<Self>,
16729    ) {
16730        let old_selections: Box<[_]> = self
16731            .selections
16732            .all::<MultiBufferOffset>(&self.display_snapshot(cx))
16733            .into();
16734        if old_selections.is_empty() {
16735            return;
16736        }
16737
16738        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
16739
16740        let buffer = self.buffer.read(cx).snapshot(cx);
16741        let mut selected_sibling = false;
16742
16743        let new_selections = old_selections
16744            .iter()
16745            .map(|selection| {
16746                let old_range = selection.start..selection.end;
16747
16748                let old_range =
16749                    old_range.start.to_offset(&buffer)..old_range.end.to_offset(&buffer);
16750                let excerpt = buffer.excerpt_containing(old_range.clone());
16751
16752                if let Some(mut excerpt) = excerpt
16753                    && let Some(node) = excerpt
16754                        .buffer()
16755                        .syntax_next_sibling(excerpt.map_range_to_buffer(old_range))
16756                {
16757                    let new_range = excerpt.map_range_from_buffer(
16758                        BufferOffset(node.byte_range().start)..BufferOffset(node.byte_range().end),
16759                    );
16760                    selected_sibling = true;
16761                    Selection {
16762                        id: selection.id,
16763                        start: new_range.start,
16764                        end: new_range.end,
16765                        goal: SelectionGoal::None,
16766                        reversed: selection.reversed,
16767                    }
16768                } else {
16769                    selection.clone()
16770                }
16771            })
16772            .collect::<Vec<_>>();
16773
16774        if selected_sibling {
16775            self.change_selections(
16776                SelectionEffects::scroll(Autoscroll::fit()),
16777                window,
16778                cx,
16779                |s| {
16780                    s.select(new_selections);
16781                },
16782            );
16783        }
16784    }
16785
16786    pub fn select_prev_syntax_node(
16787        &mut self,
16788        _: &SelectPreviousSyntaxNode,
16789        window: &mut Window,
16790        cx: &mut Context<Self>,
16791    ) {
16792        let old_selections: Box<[_]> = self
16793            .selections
16794            .all::<MultiBufferOffset>(&self.display_snapshot(cx))
16795            .into();
16796        if old_selections.is_empty() {
16797            return;
16798        }
16799
16800        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
16801
16802        let buffer = self.buffer.read(cx).snapshot(cx);
16803        let mut selected_sibling = false;
16804
16805        let new_selections = old_selections
16806            .iter()
16807            .map(|selection| {
16808                let old_range = selection.start..selection.end;
16809                let old_range =
16810                    old_range.start.to_offset(&buffer)..old_range.end.to_offset(&buffer);
16811                let excerpt = buffer.excerpt_containing(old_range.clone());
16812
16813                if let Some(mut excerpt) = excerpt
16814                    && let Some(node) = excerpt
16815                        .buffer()
16816                        .syntax_prev_sibling(excerpt.map_range_to_buffer(old_range))
16817                {
16818                    let new_range = excerpt.map_range_from_buffer(
16819                        BufferOffset(node.byte_range().start)..BufferOffset(node.byte_range().end),
16820                    );
16821                    selected_sibling = true;
16822                    Selection {
16823                        id: selection.id,
16824                        start: new_range.start,
16825                        end: new_range.end,
16826                        goal: SelectionGoal::None,
16827                        reversed: selection.reversed,
16828                    }
16829                } else {
16830                    selection.clone()
16831                }
16832            })
16833            .collect::<Vec<_>>();
16834
16835        if selected_sibling {
16836            self.change_selections(
16837                SelectionEffects::scroll(Autoscroll::fit()),
16838                window,
16839                cx,
16840                |s| {
16841                    s.select(new_selections);
16842                },
16843            );
16844        }
16845    }
16846
16847    pub fn move_to_start_of_larger_syntax_node(
16848        &mut self,
16849        _: &MoveToStartOfLargerSyntaxNode,
16850        window: &mut Window,
16851        cx: &mut Context<Self>,
16852    ) {
16853        self.move_cursors_to_syntax_nodes(window, cx, false);
16854    }
16855
16856    pub fn move_to_end_of_larger_syntax_node(
16857        &mut self,
16858        _: &MoveToEndOfLargerSyntaxNode,
16859        window: &mut Window,
16860        cx: &mut Context<Self>,
16861    ) {
16862        self.move_cursors_to_syntax_nodes(window, cx, true);
16863    }
16864
16865    fn find_syntax_node_boundary(
16866        &self,
16867        selection_pos: MultiBufferOffset,
16868        move_to_end: bool,
16869        display_map: &DisplaySnapshot,
16870        buffer: &MultiBufferSnapshot,
16871    ) -> MultiBufferOffset {
16872        let old_range = selection_pos..selection_pos;
16873        let mut new_pos = selection_pos;
16874        let mut search_range = old_range;
16875        while let Some((node, range)) = buffer.syntax_ancestor(search_range.clone()) {
16876            search_range = range.clone();
16877            if !node.is_named()
16878                || display_map.intersects_fold(range.start)
16879                || display_map.intersects_fold(range.end)
16880                // If cursor is already at the end of the syntax node, continue searching
16881                || (move_to_end && range.end == selection_pos)
16882                // If cursor is already at the start of the syntax node, continue searching
16883                || (!move_to_end && range.start == selection_pos)
16884            {
16885                continue;
16886            }
16887
16888            // If we found a string_content node, find the largest parent that is still string_content
16889            // Enables us to skip to the end of strings without taking multiple steps inside the string
16890            let (_, final_range) = if node.kind() == "string_content" {
16891                let mut current_node = node;
16892                let mut current_range = range;
16893                while let Some((parent, parent_range)) =
16894                    buffer.syntax_ancestor(current_range.clone())
16895                {
16896                    if parent.kind() == "string_content" {
16897                        current_node = parent;
16898                        current_range = parent_range;
16899                    } else {
16900                        break;
16901                    }
16902                }
16903
16904                (current_node, current_range)
16905            } else {
16906                (node, range)
16907            };
16908
16909            new_pos = if move_to_end {
16910                final_range.end
16911            } else {
16912                final_range.start
16913            };
16914
16915            break;
16916        }
16917
16918        new_pos
16919    }
16920
16921    fn move_cursors_to_syntax_nodes(
16922        &mut self,
16923        window: &mut Window,
16924        cx: &mut Context<Self>,
16925        move_to_end: bool,
16926    ) -> bool {
16927        let old_selections: Box<[_]> = self
16928            .selections
16929            .all::<MultiBufferOffset>(&self.display_snapshot(cx))
16930            .into();
16931        if old_selections.is_empty() {
16932            return false;
16933        }
16934
16935        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
16936
16937        let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
16938        let buffer = self.buffer.read(cx).snapshot(cx);
16939
16940        let mut any_cursor_moved = false;
16941        let new_selections = old_selections
16942            .iter()
16943            .map(|selection| {
16944                if !selection.is_empty() {
16945                    return selection.clone();
16946                }
16947
16948                let selection_pos = selection.head();
16949                let new_pos = self.find_syntax_node_boundary(
16950                    selection_pos,
16951                    move_to_end,
16952                    &display_map,
16953                    &buffer,
16954                );
16955
16956                any_cursor_moved |= new_pos != selection_pos;
16957
16958                Selection {
16959                    id: selection.id,
16960                    start: new_pos,
16961                    end: new_pos,
16962                    goal: SelectionGoal::None,
16963                    reversed: false,
16964                }
16965            })
16966            .collect::<Vec<_>>();
16967
16968        self.change_selections(Default::default(), window, cx, |s| {
16969            s.select(new_selections);
16970        });
16971        self.request_autoscroll(Autoscroll::newest(), cx);
16972
16973        any_cursor_moved
16974    }
16975
16976    pub fn select_to_start_of_larger_syntax_node(
16977        &mut self,
16978        _: &SelectToStartOfLargerSyntaxNode,
16979        window: &mut Window,
16980        cx: &mut Context<Self>,
16981    ) {
16982        self.select_to_syntax_nodes(window, cx, false);
16983    }
16984
16985    pub fn select_to_end_of_larger_syntax_node(
16986        &mut self,
16987        _: &SelectToEndOfLargerSyntaxNode,
16988        window: &mut Window,
16989        cx: &mut Context<Self>,
16990    ) {
16991        self.select_to_syntax_nodes(window, cx, true);
16992    }
16993
16994    fn select_to_syntax_nodes(
16995        &mut self,
16996        window: &mut Window,
16997        cx: &mut Context<Self>,
16998        move_to_end: bool,
16999    ) {
17000        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
17001
17002        let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
17003        let buffer = self.buffer.read(cx).snapshot(cx);
17004        let old_selections = self.selections.all::<MultiBufferOffset>(&display_map);
17005
17006        let new_selections = old_selections
17007            .iter()
17008            .map(|selection| {
17009                let new_pos = self.find_syntax_node_boundary(
17010                    selection.head(),
17011                    move_to_end,
17012                    &display_map,
17013                    &buffer,
17014                );
17015
17016                let mut new_selection = selection.clone();
17017                new_selection.set_head(new_pos, SelectionGoal::None);
17018                new_selection
17019            })
17020            .collect::<Vec<_>>();
17021
17022        self.change_selections(Default::default(), window, cx, |s| {
17023            s.select(new_selections);
17024        });
17025    }
17026
17027    fn refresh_runnables(&mut self, window: &mut Window, cx: &mut Context<Self>) -> Task<()> {
17028        if !EditorSettings::get_global(cx).gutter.runnables || !self.enable_runnables {
17029            self.clear_tasks();
17030            return Task::ready(());
17031        }
17032        let project = self.project().map(Entity::downgrade);
17033        let task_sources = self.lsp_task_sources(cx);
17034        let multi_buffer = self.buffer.downgrade();
17035        cx.spawn_in(window, async move |editor, cx| {
17036            cx.background_executor().timer(UPDATE_DEBOUNCE).await;
17037            let Some(project) = project.and_then(|p| p.upgrade()) else {
17038                return;
17039            };
17040            let Ok(display_snapshot) = editor.update(cx, |this, cx| {
17041                this.display_map.update(cx, |map, cx| map.snapshot(cx))
17042            }) else {
17043                return;
17044            };
17045
17046            let hide_runnables = project.update(cx, |project, _| project.is_via_collab());
17047            if hide_runnables {
17048                return;
17049            }
17050            let new_rows =
17051                cx.background_spawn({
17052                    let snapshot = display_snapshot.clone();
17053                    async move {
17054                        Self::fetch_runnable_ranges(&snapshot, Anchor::min()..Anchor::max())
17055                    }
17056                })
17057                    .await;
17058            let Ok(lsp_tasks) =
17059                cx.update(|_, cx| crate::lsp_tasks(project.clone(), &task_sources, None, cx))
17060            else {
17061                return;
17062            };
17063            let lsp_tasks = lsp_tasks.await;
17064
17065            let Ok(mut lsp_tasks_by_rows) = cx.update(|_, cx| {
17066                lsp_tasks
17067                    .into_iter()
17068                    .flat_map(|(kind, tasks)| {
17069                        tasks.into_iter().filter_map(move |(location, task)| {
17070                            Some((kind.clone(), location?, task))
17071                        })
17072                    })
17073                    .fold(HashMap::default(), |mut acc, (kind, location, task)| {
17074                        let buffer = location.target.buffer;
17075                        let buffer_snapshot = buffer.read(cx).snapshot();
17076                        let offset = display_snapshot.buffer_snapshot().excerpts().find_map(
17077                            |(excerpt_id, snapshot, _)| {
17078                                if snapshot.remote_id() == buffer_snapshot.remote_id() {
17079                                    display_snapshot
17080                                        .buffer_snapshot()
17081                                        .anchor_in_excerpt(excerpt_id, location.target.range.start)
17082                                } else {
17083                                    None
17084                                }
17085                            },
17086                        );
17087                        if let Some(offset) = offset {
17088                            let task_buffer_range =
17089                                location.target.range.to_point(&buffer_snapshot);
17090                            let context_buffer_range =
17091                                task_buffer_range.to_offset(&buffer_snapshot);
17092                            let context_range = BufferOffset(context_buffer_range.start)
17093                                ..BufferOffset(context_buffer_range.end);
17094
17095                            acc.entry((buffer_snapshot.remote_id(), task_buffer_range.start.row))
17096                                .or_insert_with(|| RunnableTasks {
17097                                    templates: Vec::new(),
17098                                    offset,
17099                                    column: task_buffer_range.start.column,
17100                                    extra_variables: HashMap::default(),
17101                                    context_range,
17102                                })
17103                                .templates
17104                                .push((kind, task.original_task().clone()));
17105                        }
17106
17107                        acc
17108                    })
17109            }) else {
17110                return;
17111            };
17112
17113            let Ok(prefer_lsp) = multi_buffer.update(cx, |buffer, cx| {
17114                buffer.language_settings(cx).tasks.prefer_lsp
17115            }) else {
17116                return;
17117            };
17118
17119            let rows = Self::runnable_rows(
17120                project,
17121                display_snapshot,
17122                prefer_lsp && !lsp_tasks_by_rows.is_empty(),
17123                new_rows,
17124                cx.clone(),
17125            )
17126            .await;
17127            editor
17128                .update(cx, |editor, _| {
17129                    editor.clear_tasks();
17130                    for (key, mut value) in rows {
17131                        if let Some(lsp_tasks) = lsp_tasks_by_rows.remove(&key) {
17132                            value.templates.extend(lsp_tasks.templates);
17133                        }
17134
17135                        editor.insert_tasks(key, value);
17136                    }
17137                    for (key, value) in lsp_tasks_by_rows {
17138                        editor.insert_tasks(key, value);
17139                    }
17140                })
17141                .ok();
17142        })
17143    }
17144    fn fetch_runnable_ranges(
17145        snapshot: &DisplaySnapshot,
17146        range: Range<Anchor>,
17147    ) -> Vec<(Range<MultiBufferOffset>, language::RunnableRange)> {
17148        snapshot.buffer_snapshot().runnable_ranges(range).collect()
17149    }
17150
17151    fn runnable_rows(
17152        project: Entity<Project>,
17153        snapshot: DisplaySnapshot,
17154        prefer_lsp: bool,
17155        runnable_ranges: Vec<(Range<MultiBufferOffset>, language::RunnableRange)>,
17156        cx: AsyncWindowContext,
17157    ) -> Task<Vec<((BufferId, BufferRow), RunnableTasks)>> {
17158        cx.spawn(async move |cx| {
17159            let mut runnable_rows = Vec::with_capacity(runnable_ranges.len());
17160            for (run_range, mut runnable) in runnable_ranges {
17161                let Some(tasks) = cx
17162                    .update(|_, cx| Self::templates_with_tags(&project, &mut runnable.runnable, cx))
17163                    .ok()
17164                else {
17165                    continue;
17166                };
17167                let mut tasks = tasks.await;
17168
17169                if prefer_lsp {
17170                    tasks.retain(|(task_kind, _)| {
17171                        !matches!(task_kind, TaskSourceKind::Language { .. })
17172                    });
17173                }
17174                if tasks.is_empty() {
17175                    continue;
17176                }
17177
17178                let point = run_range.start.to_point(&snapshot.buffer_snapshot());
17179                let Some(row) = snapshot
17180                    .buffer_snapshot()
17181                    .buffer_line_for_row(MultiBufferRow(point.row))
17182                    .map(|(_, range)| range.start.row)
17183                else {
17184                    continue;
17185                };
17186
17187                let context_range =
17188                    BufferOffset(runnable.full_range.start)..BufferOffset(runnable.full_range.end);
17189                runnable_rows.push((
17190                    (runnable.buffer_id, row),
17191                    RunnableTasks {
17192                        templates: tasks,
17193                        offset: snapshot.buffer_snapshot().anchor_before(run_range.start),
17194                        context_range,
17195                        column: point.column,
17196                        extra_variables: runnable.extra_captures,
17197                    },
17198                ));
17199            }
17200            runnable_rows
17201        })
17202    }
17203
17204    fn templates_with_tags(
17205        project: &Entity<Project>,
17206        runnable: &mut Runnable,
17207        cx: &mut App,
17208    ) -> Task<Vec<(TaskSourceKind, TaskTemplate)>> {
17209        let (inventory, worktree_id, file) = project.read_with(cx, |project, cx| {
17210            let (worktree_id, file) = project
17211                .buffer_for_id(runnable.buffer, cx)
17212                .and_then(|buffer| buffer.read(cx).file())
17213                .map(|file| (file.worktree_id(cx), file.clone()))
17214                .unzip();
17215
17216            (
17217                project.task_store().read(cx).task_inventory().cloned(),
17218                worktree_id,
17219                file,
17220            )
17221        });
17222
17223        let tags = mem::take(&mut runnable.tags);
17224        let language = runnable.language.clone();
17225        cx.spawn(async move |cx| {
17226            let mut templates_with_tags = Vec::new();
17227            if let Some(inventory) = inventory {
17228                for RunnableTag(tag) in tags {
17229                    let new_tasks = inventory.update(cx, |inventory, cx| {
17230                        inventory.list_tasks(file.clone(), Some(language.clone()), worktree_id, cx)
17231                    });
17232                    templates_with_tags.extend(new_tasks.await.into_iter().filter(
17233                        move |(_, template)| {
17234                            template.tags.iter().any(|source_tag| source_tag == &tag)
17235                        },
17236                    ));
17237                }
17238            }
17239            templates_with_tags.sort_by_key(|(kind, _)| kind.to_owned());
17240
17241            if let Some((leading_tag_source, _)) = templates_with_tags.first() {
17242                // Strongest source wins; if we have worktree tag binding, prefer that to
17243                // global and language bindings;
17244                // if we have a global binding, prefer that to language binding.
17245                let first_mismatch = templates_with_tags
17246                    .iter()
17247                    .position(|(tag_source, _)| tag_source != leading_tag_source);
17248                if let Some(index) = first_mismatch {
17249                    templates_with_tags.truncate(index);
17250                }
17251            }
17252
17253            templates_with_tags
17254        })
17255    }
17256
17257    pub fn move_to_enclosing_bracket(
17258        &mut self,
17259        _: &MoveToEnclosingBracket,
17260        window: &mut Window,
17261        cx: &mut Context<Self>,
17262    ) {
17263        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
17264        self.change_selections(Default::default(), window, cx, |s| {
17265            s.move_offsets_with(&mut |snapshot, selection| {
17266                let Some(enclosing_bracket_ranges) =
17267                    snapshot.enclosing_bracket_ranges(selection.start..selection.end)
17268                else {
17269                    return;
17270                };
17271
17272                let mut best_length = usize::MAX;
17273                let mut best_inside = false;
17274                let mut best_in_bracket_range = false;
17275                let mut best_destination = None;
17276                for (open, close) in enclosing_bracket_ranges {
17277                    let close = close.to_inclusive();
17278                    let length = *close.end() - open.start;
17279                    let inside = selection.start >= open.end && selection.end <= *close.start();
17280                    let in_bracket_range = open.to_inclusive().contains(&selection.head())
17281                        || close.contains(&selection.head());
17282
17283                    // If best is next to a bracket and current isn't, skip
17284                    if !in_bracket_range && best_in_bracket_range {
17285                        continue;
17286                    }
17287
17288                    // Prefer smaller lengths unless best is inside and current isn't
17289                    if length > best_length && (best_inside || !inside) {
17290                        continue;
17291                    }
17292
17293                    best_length = length;
17294                    best_inside = inside;
17295                    best_in_bracket_range = in_bracket_range;
17296                    best_destination = Some(
17297                        if close.contains(&selection.start) && close.contains(&selection.end) {
17298                            if inside { open.end } else { open.start }
17299                        } else if inside {
17300                            *close.start()
17301                        } else {
17302                            *close.end()
17303                        },
17304                    );
17305                }
17306
17307                if let Some(destination) = best_destination {
17308                    selection.collapse_to(destination, SelectionGoal::None);
17309                }
17310            })
17311        });
17312    }
17313
17314    pub fn undo_selection(
17315        &mut self,
17316        _: &UndoSelection,
17317        window: &mut Window,
17318        cx: &mut Context<Self>,
17319    ) {
17320        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
17321        if let Some(entry) = self.selection_history.undo_stack.pop_back() {
17322            self.selection_history.mode = SelectionHistoryMode::Undoing;
17323            self.with_selection_effects_deferred(window, cx, |this, window, cx| {
17324                this.end_selection(window, cx);
17325                this.change_selections(
17326                    SelectionEffects::scroll(Autoscroll::newest()),
17327                    window,
17328                    cx,
17329                    |s| s.select_anchors(entry.selections.to_vec()),
17330                );
17331            });
17332            self.selection_history.mode = SelectionHistoryMode::Normal;
17333
17334            self.select_next_state = entry.select_next_state;
17335            self.select_prev_state = entry.select_prev_state;
17336            self.add_selections_state = entry.add_selections_state;
17337        }
17338    }
17339
17340    pub fn redo_selection(
17341        &mut self,
17342        _: &RedoSelection,
17343        window: &mut Window,
17344        cx: &mut Context<Self>,
17345    ) {
17346        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
17347        if let Some(entry) = self.selection_history.redo_stack.pop_back() {
17348            self.selection_history.mode = SelectionHistoryMode::Redoing;
17349            self.with_selection_effects_deferred(window, cx, |this, window, cx| {
17350                this.end_selection(window, cx);
17351                this.change_selections(
17352                    SelectionEffects::scroll(Autoscroll::newest()),
17353                    window,
17354                    cx,
17355                    |s| s.select_anchors(entry.selections.to_vec()),
17356                );
17357            });
17358            self.selection_history.mode = SelectionHistoryMode::Normal;
17359
17360            self.select_next_state = entry.select_next_state;
17361            self.select_prev_state = entry.select_prev_state;
17362            self.add_selections_state = entry.add_selections_state;
17363        }
17364    }
17365
17366    pub fn expand_excerpts(
17367        &mut self,
17368        action: &ExpandExcerpts,
17369        _: &mut Window,
17370        cx: &mut Context<Self>,
17371    ) {
17372        self.expand_excerpts_for_direction(action.lines, ExpandExcerptDirection::UpAndDown, cx)
17373    }
17374
17375    pub fn expand_excerpts_down(
17376        &mut self,
17377        action: &ExpandExcerptsDown,
17378        _: &mut Window,
17379        cx: &mut Context<Self>,
17380    ) {
17381        self.expand_excerpts_for_direction(action.lines, ExpandExcerptDirection::Down, cx)
17382    }
17383
17384    pub fn expand_excerpts_up(
17385        &mut self,
17386        action: &ExpandExcerptsUp,
17387        _: &mut Window,
17388        cx: &mut Context<Self>,
17389    ) {
17390        self.expand_excerpts_for_direction(action.lines, ExpandExcerptDirection::Up, cx)
17391    }
17392
17393    pub fn expand_excerpts_for_direction(
17394        &mut self,
17395        lines: u32,
17396        direction: ExpandExcerptDirection,
17397        cx: &mut Context<Self>,
17398    ) {
17399        let selections = self.selections.disjoint_anchors_arc();
17400
17401        let lines = if lines == 0 {
17402            EditorSettings::get_global(cx).expand_excerpt_lines
17403        } else {
17404            lines
17405        };
17406
17407        let snapshot = self.buffer.read(cx).snapshot(cx);
17408        let excerpt_ids = selections
17409            .iter()
17410            .flat_map(|selection| snapshot.excerpt_ids_for_range(selection.range()))
17411            .unique()
17412            .sorted()
17413            .collect::<Vec<_>>();
17414
17415        if self.delegate_expand_excerpts {
17416            cx.emit(EditorEvent::ExpandExcerptsRequested {
17417                excerpt_ids,
17418                lines,
17419                direction,
17420            });
17421            return;
17422        }
17423
17424        self.buffer.update(cx, |buffer, cx| {
17425            buffer.expand_excerpts(excerpt_ids, lines, direction, cx)
17426        })
17427    }
17428
17429    pub fn expand_excerpt(
17430        &mut self,
17431        excerpt: ExcerptId,
17432        direction: ExpandExcerptDirection,
17433        window: &mut Window,
17434        cx: &mut Context<Self>,
17435    ) {
17436        let lines_to_expand = EditorSettings::get_global(cx).expand_excerpt_lines;
17437
17438        if self.delegate_expand_excerpts {
17439            cx.emit(EditorEvent::ExpandExcerptsRequested {
17440                excerpt_ids: vec![excerpt],
17441                lines: lines_to_expand,
17442                direction,
17443            });
17444            return;
17445        }
17446
17447        let current_scroll_position = self.scroll_position(cx);
17448        let mut scroll = None;
17449
17450        if direction == ExpandExcerptDirection::Down {
17451            let multi_buffer = self.buffer.read(cx);
17452            let snapshot = multi_buffer.snapshot(cx);
17453            if let Some(buffer_id) = snapshot.buffer_id_for_excerpt(excerpt)
17454                && let Some(buffer) = multi_buffer.buffer(buffer_id)
17455                && let Some(excerpt_range) = snapshot.context_range_for_excerpt(excerpt)
17456            {
17457                let buffer_snapshot = buffer.read(cx).snapshot();
17458                let excerpt_end_row = Point::from_anchor(&excerpt_range.end, &buffer_snapshot).row;
17459                let last_row = buffer_snapshot.max_point().row;
17460                let lines_below = last_row.saturating_sub(excerpt_end_row);
17461                if lines_below >= lines_to_expand {
17462                    scroll = Some(
17463                        current_scroll_position
17464                            + gpui::Point::new(0.0, lines_to_expand as ScrollOffset),
17465                    );
17466                }
17467            }
17468        }
17469        if direction == ExpandExcerptDirection::Up
17470            && self
17471                .buffer
17472                .read(cx)
17473                .snapshot(cx)
17474                .excerpt_before(excerpt)
17475                .is_none()
17476        {
17477            scroll = Some(current_scroll_position);
17478        }
17479
17480        self.buffer.update(cx, |buffer, cx| {
17481            buffer.expand_excerpts([excerpt], lines_to_expand, direction, cx)
17482        });
17483
17484        if let Some(new_scroll_position) = scroll {
17485            self.set_scroll_position(new_scroll_position, window, cx);
17486        }
17487    }
17488
17489    pub fn go_to_singleton_buffer_point(
17490        &mut self,
17491        point: Point,
17492        window: &mut Window,
17493        cx: &mut Context<Self>,
17494    ) {
17495        self.go_to_singleton_buffer_range(point..point, window, cx);
17496    }
17497
17498    pub fn go_to_singleton_buffer_range(
17499        &mut self,
17500        range: Range<Point>,
17501        window: &mut Window,
17502        cx: &mut Context<Self>,
17503    ) {
17504        let multibuffer = self.buffer().read(cx);
17505        let Some(buffer) = multibuffer.as_singleton() else {
17506            return;
17507        };
17508        let Some(start) = multibuffer.buffer_point_to_anchor(&buffer, range.start, cx) else {
17509            return;
17510        };
17511        let Some(end) = multibuffer.buffer_point_to_anchor(&buffer, range.end, cx) else {
17512            return;
17513        };
17514        self.change_selections(
17515            SelectionEffects::default().nav_history(true),
17516            window,
17517            cx,
17518            |s| s.select_anchor_ranges([start..end]),
17519        );
17520    }
17521
17522    pub fn go_to_diagnostic(
17523        &mut self,
17524        action: &GoToDiagnostic,
17525        window: &mut Window,
17526        cx: &mut Context<Self>,
17527    ) {
17528        if !self.diagnostics_enabled() {
17529            return;
17530        }
17531        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
17532        self.go_to_diagnostic_impl(Direction::Next, action.severity, window, cx)
17533    }
17534
17535    pub fn go_to_prev_diagnostic(
17536        &mut self,
17537        action: &GoToPreviousDiagnostic,
17538        window: &mut Window,
17539        cx: &mut Context<Self>,
17540    ) {
17541        if !self.diagnostics_enabled() {
17542            return;
17543        }
17544        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
17545        self.go_to_diagnostic_impl(Direction::Prev, action.severity, window, cx)
17546    }
17547
17548    pub fn go_to_diagnostic_impl(
17549        &mut self,
17550        direction: Direction,
17551        severity: GoToDiagnosticSeverityFilter,
17552        window: &mut Window,
17553        cx: &mut Context<Self>,
17554    ) {
17555        let buffer = self.buffer.read(cx).snapshot(cx);
17556        let selection = self
17557            .selections
17558            .newest::<MultiBufferOffset>(&self.display_snapshot(cx));
17559
17560        let mut active_group_id = None;
17561        if let ActiveDiagnostic::Group(active_group) = &self.active_diagnostics
17562            && active_group.active_range.start.to_offset(&buffer) == selection.start
17563        {
17564            active_group_id = Some(active_group.group_id);
17565        }
17566
17567        fn filtered<'a>(
17568            severity: GoToDiagnosticSeverityFilter,
17569            diagnostics: impl Iterator<Item = DiagnosticEntryRef<'a, MultiBufferOffset>>,
17570        ) -> impl Iterator<Item = DiagnosticEntryRef<'a, MultiBufferOffset>> {
17571            diagnostics
17572                .filter(move |entry| severity.matches(entry.diagnostic.severity))
17573                .filter(|entry| entry.range.start != entry.range.end)
17574                .filter(|entry| !entry.diagnostic.is_unnecessary)
17575        }
17576
17577        let before = filtered(
17578            severity,
17579            buffer
17580                .diagnostics_in_range(MultiBufferOffset(0)..selection.start)
17581                .filter(|entry| entry.range.start <= selection.start),
17582        );
17583        let after = filtered(
17584            severity,
17585            buffer
17586                .diagnostics_in_range(selection.start..buffer.len())
17587                .filter(|entry| entry.range.start >= selection.start),
17588        );
17589
17590        let mut found: Option<DiagnosticEntryRef<MultiBufferOffset>> = None;
17591        if direction == Direction::Prev {
17592            'outer: for prev_diagnostics in [before.collect::<Vec<_>>(), after.collect::<Vec<_>>()]
17593            {
17594                for diagnostic in prev_diagnostics.into_iter().rev() {
17595                    if diagnostic.range.start != selection.start
17596                        || active_group_id
17597                            .is_some_and(|active| diagnostic.diagnostic.group_id < active)
17598                    {
17599                        found = Some(diagnostic);
17600                        break 'outer;
17601                    }
17602                }
17603            }
17604        } else {
17605            for diagnostic in after.chain(before) {
17606                if diagnostic.range.start != selection.start
17607                    || active_group_id.is_some_and(|active| diagnostic.diagnostic.group_id > active)
17608                {
17609                    found = Some(diagnostic);
17610                    break;
17611                }
17612            }
17613        }
17614        let Some(next_diagnostic) = found else {
17615            return;
17616        };
17617
17618        let next_diagnostic_start = buffer.anchor_after(next_diagnostic.range.start);
17619        let Some(buffer_id) = buffer.buffer_id_for_anchor(next_diagnostic_start) else {
17620            return;
17621        };
17622        let snapshot = self.snapshot(window, cx);
17623        if snapshot.intersects_fold(next_diagnostic.range.start) {
17624            self.unfold_ranges(
17625                std::slice::from_ref(&next_diagnostic.range),
17626                true,
17627                false,
17628                cx,
17629            );
17630        }
17631        self.change_selections(Default::default(), window, cx, |s| {
17632            s.select_ranges(vec![
17633                next_diagnostic.range.start..next_diagnostic.range.start,
17634            ])
17635        });
17636        self.activate_diagnostics(buffer_id, next_diagnostic, window, cx);
17637        self.refresh_edit_prediction(false, true, window, cx);
17638    }
17639
17640    pub fn go_to_next_hunk(&mut self, _: &GoToHunk, window: &mut Window, cx: &mut Context<Self>) {
17641        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
17642        let snapshot = self.snapshot(window, cx);
17643        let selection = self.selections.newest::<Point>(&self.display_snapshot(cx));
17644        self.go_to_hunk_before_or_after_position(
17645            &snapshot,
17646            selection.head(),
17647            Direction::Next,
17648            window,
17649            cx,
17650        );
17651    }
17652
17653    pub fn go_to_hunk_before_or_after_position(
17654        &mut self,
17655        snapshot: &EditorSnapshot,
17656        position: Point,
17657        direction: Direction,
17658        window: &mut Window,
17659        cx: &mut Context<Editor>,
17660    ) {
17661        let row = if direction == Direction::Next {
17662            self.hunk_after_position(snapshot, position)
17663                .map(|hunk| hunk.row_range.start)
17664        } else {
17665            self.hunk_before_position(snapshot, position)
17666        };
17667
17668        if let Some(row) = row {
17669            let destination = Point::new(row.0, 0);
17670            let autoscroll = Autoscroll::center();
17671
17672            self.unfold_ranges(&[destination..destination], false, false, cx);
17673            self.change_selections(SelectionEffects::scroll(autoscroll), window, cx, |s| {
17674                s.select_ranges([destination..destination]);
17675            });
17676        }
17677    }
17678
17679    fn hunk_after_position(
17680        &mut self,
17681        snapshot: &EditorSnapshot,
17682        position: Point,
17683    ) -> Option<MultiBufferDiffHunk> {
17684        snapshot
17685            .buffer_snapshot()
17686            .diff_hunks_in_range(position..snapshot.buffer_snapshot().max_point())
17687            .find(|hunk| hunk.row_range.start.0 > position.row)
17688            .or_else(|| {
17689                snapshot
17690                    .buffer_snapshot()
17691                    .diff_hunks_in_range(Point::zero()..position)
17692                    .find(|hunk| hunk.row_range.end.0 < position.row)
17693            })
17694    }
17695
17696    fn go_to_prev_hunk(
17697        &mut self,
17698        _: &GoToPreviousHunk,
17699        window: &mut Window,
17700        cx: &mut Context<Self>,
17701    ) {
17702        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
17703        let snapshot = self.snapshot(window, cx);
17704        let selection = self.selections.newest::<Point>(&snapshot.display_snapshot);
17705        self.go_to_hunk_before_or_after_position(
17706            &snapshot,
17707            selection.head(),
17708            Direction::Prev,
17709            window,
17710            cx,
17711        );
17712    }
17713
17714    fn hunk_before_position(
17715        &mut self,
17716        snapshot: &EditorSnapshot,
17717        position: Point,
17718    ) -> Option<MultiBufferRow> {
17719        snapshot
17720            .buffer_snapshot()
17721            .diff_hunk_before(position)
17722            .or_else(|| snapshot.buffer_snapshot().diff_hunk_before(Point::MAX))
17723    }
17724
17725    fn go_to_next_change(
17726        &mut self,
17727        _: &GoToNextChange,
17728        window: &mut Window,
17729        cx: &mut Context<Self>,
17730    ) {
17731        if let Some(selections) = self
17732            .change_list
17733            .next_change(1, Direction::Next)
17734            .map(|s| s.to_vec())
17735        {
17736            self.change_selections(Default::default(), window, cx, |s| {
17737                let map = s.display_snapshot();
17738                s.select_display_ranges(selections.iter().map(|a| {
17739                    let point = a.to_display_point(&map);
17740                    point..point
17741                }))
17742            })
17743        }
17744    }
17745
17746    fn go_to_previous_change(
17747        &mut self,
17748        _: &GoToPreviousChange,
17749        window: &mut Window,
17750        cx: &mut Context<Self>,
17751    ) {
17752        if let Some(selections) = self
17753            .change_list
17754            .next_change(1, Direction::Prev)
17755            .map(|s| s.to_vec())
17756        {
17757            self.change_selections(Default::default(), window, cx, |s| {
17758                let map = s.display_snapshot();
17759                s.select_display_ranges(selections.iter().map(|a| {
17760                    let point = a.to_display_point(&map);
17761                    point..point
17762                }))
17763            })
17764        }
17765    }
17766
17767    pub fn go_to_next_document_highlight(
17768        &mut self,
17769        _: &GoToNextDocumentHighlight,
17770        window: &mut Window,
17771        cx: &mut Context<Self>,
17772    ) {
17773        self.go_to_document_highlight_before_or_after_position(Direction::Next, window, cx);
17774    }
17775
17776    pub fn go_to_prev_document_highlight(
17777        &mut self,
17778        _: &GoToPreviousDocumentHighlight,
17779        window: &mut Window,
17780        cx: &mut Context<Self>,
17781    ) {
17782        self.go_to_document_highlight_before_or_after_position(Direction::Prev, window, cx);
17783    }
17784
17785    pub fn go_to_document_highlight_before_or_after_position(
17786        &mut self,
17787        direction: Direction,
17788        window: &mut Window,
17789        cx: &mut Context<Editor>,
17790    ) {
17791        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
17792        let snapshot = self.snapshot(window, cx);
17793        let buffer = &snapshot.buffer_snapshot();
17794        let position = self
17795            .selections
17796            .newest::<Point>(&snapshot.display_snapshot)
17797            .head();
17798        let anchor_position = buffer.anchor_after(position);
17799
17800        // Get all document highlights (both read and write)
17801        let mut all_highlights = Vec::new();
17802
17803        if let Some((_, read_highlights)) = self
17804            .background_highlights
17805            .get(&HighlightKey::DocumentHighlightRead)
17806        {
17807            all_highlights.extend(read_highlights.iter());
17808        }
17809
17810        if let Some((_, write_highlights)) = self
17811            .background_highlights
17812            .get(&HighlightKey::DocumentHighlightWrite)
17813        {
17814            all_highlights.extend(write_highlights.iter());
17815        }
17816
17817        if all_highlights.is_empty() {
17818            return;
17819        }
17820
17821        // Sort highlights by position
17822        all_highlights.sort_by(|a, b| a.start.cmp(&b.start, buffer));
17823
17824        let target_highlight = match direction {
17825            Direction::Next => {
17826                // Find the first highlight after the current position
17827                all_highlights
17828                    .iter()
17829                    .find(|highlight| highlight.start.cmp(&anchor_position, buffer).is_gt())
17830            }
17831            Direction::Prev => {
17832                // Find the last highlight before the current position
17833                all_highlights
17834                    .iter()
17835                    .rev()
17836                    .find(|highlight| highlight.end.cmp(&anchor_position, buffer).is_lt())
17837            }
17838        };
17839
17840        if let Some(highlight) = target_highlight {
17841            let destination = highlight.start.to_point(buffer);
17842            let autoscroll = Autoscroll::center();
17843
17844            self.unfold_ranges(&[destination..destination], false, false, cx);
17845            self.change_selections(SelectionEffects::scroll(autoscroll), window, cx, |s| {
17846                s.select_ranges([destination..destination]);
17847            });
17848        }
17849    }
17850
17851    fn go_to_line<T: 'static>(
17852        &mut self,
17853        position: Anchor,
17854        highlight_color: Option<Hsla>,
17855        window: &mut Window,
17856        cx: &mut Context<Self>,
17857    ) {
17858        let snapshot = self.snapshot(window, cx).display_snapshot;
17859        let position = position.to_point(&snapshot.buffer_snapshot());
17860        let start = snapshot
17861            .buffer_snapshot()
17862            .clip_point(Point::new(position.row, 0), Bias::Left);
17863        let end = start + Point::new(1, 0);
17864        let start = snapshot.buffer_snapshot().anchor_before(start);
17865        let end = snapshot.buffer_snapshot().anchor_before(end);
17866
17867        self.highlight_rows::<T>(
17868            start..end,
17869            highlight_color
17870                .unwrap_or_else(|| cx.theme().colors().editor_highlighted_line_background),
17871            Default::default(),
17872            cx,
17873        );
17874
17875        if self.buffer.read(cx).is_singleton() {
17876            self.request_autoscroll(Autoscroll::center().for_anchor(start), cx);
17877        }
17878    }
17879
17880    pub fn go_to_definition(
17881        &mut self,
17882        _: &GoToDefinition,
17883        window: &mut Window,
17884        cx: &mut Context<Self>,
17885    ) -> Task<Result<Navigated>> {
17886        let definition =
17887            self.go_to_definition_of_kind(GotoDefinitionKind::Symbol, false, window, cx);
17888        let fallback_strategy = EditorSettings::get_global(cx).go_to_definition_fallback;
17889        cx.spawn_in(window, async move |editor, cx| {
17890            if definition.await? == Navigated::Yes {
17891                return Ok(Navigated::Yes);
17892            }
17893            match fallback_strategy {
17894                GoToDefinitionFallback::None => Ok(Navigated::No),
17895                GoToDefinitionFallback::FindAllReferences => {
17896                    match editor.update_in(cx, |editor, window, cx| {
17897                        editor.find_all_references(&FindAllReferences::default(), window, cx)
17898                    })? {
17899                        Some(references) => references.await,
17900                        None => Ok(Navigated::No),
17901                    }
17902                }
17903            }
17904        })
17905    }
17906
17907    pub fn go_to_declaration(
17908        &mut self,
17909        _: &GoToDeclaration,
17910        window: &mut Window,
17911        cx: &mut Context<Self>,
17912    ) -> Task<Result<Navigated>> {
17913        self.go_to_definition_of_kind(GotoDefinitionKind::Declaration, false, window, cx)
17914    }
17915
17916    pub fn go_to_declaration_split(
17917        &mut self,
17918        _: &GoToDeclaration,
17919        window: &mut Window,
17920        cx: &mut Context<Self>,
17921    ) -> Task<Result<Navigated>> {
17922        self.go_to_definition_of_kind(GotoDefinitionKind::Declaration, true, window, cx)
17923    }
17924
17925    pub fn go_to_implementation(
17926        &mut self,
17927        _: &GoToImplementation,
17928        window: &mut Window,
17929        cx: &mut Context<Self>,
17930    ) -> Task<Result<Navigated>> {
17931        self.go_to_definition_of_kind(GotoDefinitionKind::Implementation, false, window, cx)
17932    }
17933
17934    pub fn go_to_implementation_split(
17935        &mut self,
17936        _: &GoToImplementationSplit,
17937        window: &mut Window,
17938        cx: &mut Context<Self>,
17939    ) -> Task<Result<Navigated>> {
17940        self.go_to_definition_of_kind(GotoDefinitionKind::Implementation, true, window, cx)
17941    }
17942
17943    pub fn go_to_type_definition(
17944        &mut self,
17945        _: &GoToTypeDefinition,
17946        window: &mut Window,
17947        cx: &mut Context<Self>,
17948    ) -> Task<Result<Navigated>> {
17949        self.go_to_definition_of_kind(GotoDefinitionKind::Type, false, window, cx)
17950    }
17951
17952    pub fn go_to_definition_split(
17953        &mut self,
17954        _: &GoToDefinitionSplit,
17955        window: &mut Window,
17956        cx: &mut Context<Self>,
17957    ) -> Task<Result<Navigated>> {
17958        self.go_to_definition_of_kind(GotoDefinitionKind::Symbol, true, window, cx)
17959    }
17960
17961    pub fn go_to_type_definition_split(
17962        &mut self,
17963        _: &GoToTypeDefinitionSplit,
17964        window: &mut Window,
17965        cx: &mut Context<Self>,
17966    ) -> Task<Result<Navigated>> {
17967        self.go_to_definition_of_kind(GotoDefinitionKind::Type, true, window, cx)
17968    }
17969
17970    fn go_to_definition_of_kind(
17971        &mut self,
17972        kind: GotoDefinitionKind,
17973        split: bool,
17974        window: &mut Window,
17975        cx: &mut Context<Self>,
17976    ) -> Task<Result<Navigated>> {
17977        let Some(provider) = self.semantics_provider.clone() else {
17978            return Task::ready(Ok(Navigated::No));
17979        };
17980        let head = self
17981            .selections
17982            .newest::<MultiBufferOffset>(&self.display_snapshot(cx))
17983            .head();
17984        let buffer = self.buffer.read(cx);
17985        let Some((buffer, head)) = buffer.text_anchor_for_position(head, cx) else {
17986            return Task::ready(Ok(Navigated::No));
17987        };
17988        let Some(definitions) = provider.definitions(&buffer, head, kind, cx) else {
17989            return Task::ready(Ok(Navigated::No));
17990        };
17991
17992        let nav_entry = self.navigation_entry(self.selections.newest_anchor().head(), cx);
17993
17994        cx.spawn_in(window, async move |editor, cx| {
17995            let Some(definitions) = definitions.await? else {
17996                return Ok(Navigated::No);
17997            };
17998            let navigated = editor
17999                .update_in(cx, |editor, window, cx| {
18000                    editor.navigate_to_hover_links(
18001                        Some(kind),
18002                        definitions
18003                            .into_iter()
18004                            .filter(|location| {
18005                                hover_links::exclude_link_to_position(&buffer, &head, location, cx)
18006                            })
18007                            .map(HoverLink::Text)
18008                            .collect::<Vec<_>>(),
18009                        nav_entry,
18010                        split,
18011                        window,
18012                        cx,
18013                    )
18014                })?
18015                .await?;
18016            anyhow::Ok(navigated)
18017        })
18018    }
18019
18020    pub fn open_url(&mut self, _: &OpenUrl, window: &mut Window, cx: &mut Context<Self>) {
18021        let selection = self.selections.newest_anchor();
18022        let head = selection.head();
18023        let tail = selection.tail();
18024
18025        let Some((buffer, start_position)) =
18026            self.buffer.read(cx).text_anchor_for_position(head, cx)
18027        else {
18028            return;
18029        };
18030
18031        let end_position = if head != tail {
18032            let Some((_, pos)) = self.buffer.read(cx).text_anchor_for_position(tail, cx) else {
18033                return;
18034            };
18035            Some(pos)
18036        } else {
18037            None
18038        };
18039
18040        let url_finder = cx.spawn_in(window, async move |_editor, cx| {
18041            let url = if let Some(end_pos) = end_position {
18042                find_url_from_range(&buffer, start_position..end_pos, cx.clone())
18043            } else {
18044                find_url(&buffer, start_position, cx.clone()).map(|(_, url)| url)
18045            };
18046
18047            if let Some(url) = url {
18048                cx.update(|window, cx| {
18049                    if parse_zed_link(&url, cx).is_some() {
18050                        window.dispatch_action(Box::new(zed_actions::OpenZedUrl { url }), cx);
18051                    } else {
18052                        cx.open_url(&url);
18053                    }
18054                })?;
18055            }
18056
18057            anyhow::Ok(())
18058        });
18059
18060        url_finder.detach();
18061    }
18062
18063    pub fn open_selected_filename(
18064        &mut self,
18065        _: &OpenSelectedFilename,
18066        window: &mut Window,
18067        cx: &mut Context<Self>,
18068    ) {
18069        let Some(workspace) = self.workspace() else {
18070            return;
18071        };
18072
18073        let position = self.selections.newest_anchor().head();
18074
18075        let Some((buffer, buffer_position)) =
18076            self.buffer.read(cx).text_anchor_for_position(position, cx)
18077        else {
18078            return;
18079        };
18080
18081        let project = self.project.clone();
18082
18083        cx.spawn_in(window, async move |_, cx| {
18084            let result = find_file(&buffer, project, buffer_position, cx).await;
18085
18086            if let Some((_, path)) = result {
18087                workspace
18088                    .update_in(cx, |workspace, window, cx| {
18089                        workspace.open_resolved_path(path, window, cx)
18090                    })?
18091                    .await?;
18092            }
18093            anyhow::Ok(())
18094        })
18095        .detach();
18096    }
18097
18098    pub(crate) fn navigate_to_hover_links(
18099        &mut self,
18100        kind: Option<GotoDefinitionKind>,
18101        definitions: Vec<HoverLink>,
18102        origin: Option<NavigationEntry>,
18103        split: bool,
18104        window: &mut Window,
18105        cx: &mut Context<Editor>,
18106    ) -> Task<Result<Navigated>> {
18107        // Separate out url and file links, we can only handle one of them at most or an arbitrary number of locations
18108        let mut first_url_or_file = None;
18109        let definitions: Vec<_> = definitions
18110            .into_iter()
18111            .filter_map(|def| match def {
18112                HoverLink::Text(link) => Some(Task::ready(anyhow::Ok(Some(link.target)))),
18113                HoverLink::InlayHint(lsp_location, server_id) => {
18114                    let computation =
18115                        self.compute_target_location(lsp_location, server_id, window, cx);
18116                    Some(cx.background_spawn(computation))
18117                }
18118                HoverLink::Url(url) => {
18119                    first_url_or_file = Some(Either::Left(url));
18120                    None
18121                }
18122                HoverLink::File(path) => {
18123                    first_url_or_file = Some(Either::Right(path));
18124                    None
18125                }
18126            })
18127            .collect();
18128
18129        let workspace = self.workspace();
18130
18131        cx.spawn_in(window, async move |editor, cx| {
18132            let locations: Vec<Location> = future::join_all(definitions)
18133                .await
18134                .into_iter()
18135                .filter_map(|location| location.transpose())
18136                .collect::<Result<_>>()
18137                .context("location tasks")?;
18138            let mut locations = cx.update(|_, cx| {
18139                locations
18140                    .into_iter()
18141                    .map(|location| {
18142                        let buffer = location.buffer.read(cx);
18143                        (location.buffer, location.range.to_point(buffer))
18144                    })
18145                    .into_group_map()
18146            })?;
18147            let mut num_locations = 0;
18148            for ranges in locations.values_mut() {
18149                ranges.sort_by_key(|range| (range.start, Reverse(range.end)));
18150                ranges.dedup();
18151                num_locations += ranges.len();
18152            }
18153
18154            if num_locations > 1 {
18155                let tab_kind = match kind {
18156                    Some(GotoDefinitionKind::Implementation) => "Implementations",
18157                    Some(GotoDefinitionKind::Symbol) | None => "Definitions",
18158                    Some(GotoDefinitionKind::Declaration) => "Declarations",
18159                    Some(GotoDefinitionKind::Type) => "Types",
18160                };
18161                let title = editor
18162                    .update_in(cx, |_, _, cx| {
18163                        let target = locations
18164                            .iter()
18165                            .flat_map(|(k, v)| iter::repeat(k.clone()).zip(v))
18166                            .map(|(buffer, location)| {
18167                                buffer
18168                                    .read(cx)
18169                                    .text_for_range(location.clone())
18170                                    .collect::<String>()
18171                            })
18172                            .filter(|text| !text.contains('\n'))
18173                            .unique()
18174                            .take(3)
18175                            .join(", ");
18176                        if target.is_empty() {
18177                            tab_kind.to_owned()
18178                        } else {
18179                            format!("{tab_kind} for {target}")
18180                        }
18181                    })
18182                    .context("buffer title")?;
18183
18184                let Some(workspace) = workspace else {
18185                    return Ok(Navigated::No);
18186                };
18187
18188                let opened = workspace
18189                    .update_in(cx, |workspace, window, cx| {
18190                        let allow_preview = PreviewTabsSettings::get_global(cx)
18191                            .enable_preview_multibuffer_from_code_navigation;
18192                        if let Some((target_editor, target_pane)) =
18193                            Self::open_locations_in_multibuffer(
18194                                workspace,
18195                                locations,
18196                                title,
18197                                split,
18198                                allow_preview,
18199                                MultibufferSelectionMode::First,
18200                                window,
18201                                cx,
18202                            )
18203                        {
18204                            // We create our own nav history instead of using
18205                            // `target_editor.nav_history` because `nav_history`
18206                            // seems to be populated asynchronously when an item
18207                            // is added to a pane
18208                            let mut nav_history = target_pane
18209                                .update(cx, |pane, _| pane.nav_history_for_item(&target_editor));
18210                            target_editor.update(cx, |editor, cx| {
18211                                let nav_data = editor
18212                                    .navigation_data(editor.selections.newest_anchor().head(), cx);
18213                                let target =
18214                                    Some(nav_history.navigation_entry(Some(
18215                                        Arc::new(nav_data) as Arc<dyn Any + Send + Sync>
18216                                    )));
18217                                nav_history.push_tag(origin, target);
18218                            })
18219                        }
18220                    })
18221                    .is_ok();
18222
18223                anyhow::Ok(Navigated::from_bool(opened))
18224            } else if num_locations == 0 {
18225                // If there is one url or file, open it directly
18226                match first_url_or_file {
18227                    Some(Either::Left(url)) => {
18228                        cx.update(|window, cx| {
18229                            if parse_zed_link(&url, cx).is_some() {
18230                                window
18231                                    .dispatch_action(Box::new(zed_actions::OpenZedUrl { url }), cx);
18232                            } else {
18233                                cx.open_url(&url);
18234                            }
18235                        })?;
18236                        Ok(Navigated::Yes)
18237                    }
18238                    Some(Either::Right(path)) => {
18239                        // TODO(andrew): respect preview tab settings
18240                        //               `enable_keep_preview_on_code_navigation` and
18241                        //               `enable_preview_file_from_code_navigation`
18242                        let Some(workspace) = workspace else {
18243                            return Ok(Navigated::No);
18244                        };
18245                        workspace
18246                            .update_in(cx, |workspace, window, cx| {
18247                                workspace.open_resolved_path(path, window, cx)
18248                            })?
18249                            .await?;
18250                        Ok(Navigated::Yes)
18251                    }
18252                    None => Ok(Navigated::No),
18253                }
18254            } else {
18255                let (target_buffer, target_ranges) = locations.into_iter().next().unwrap();
18256                let target_range = target_ranges.first().unwrap().clone();
18257
18258                editor.update_in(cx, |editor, window, cx| {
18259                    let range = editor.range_for_match(&target_range);
18260                    let range = collapse_multiline_range(range);
18261
18262                    if !split
18263                        && Some(&target_buffer) == editor.buffer.read(cx).as_singleton().as_ref()
18264                    {
18265                        editor.go_to_singleton_buffer_range(range, window, cx);
18266
18267                        let target =
18268                            editor.navigation_entry(editor.selections.newest_anchor().head(), cx);
18269                        if let Some(mut nav_history) = editor.nav_history.clone() {
18270                            nav_history.push_tag(origin, target);
18271                        }
18272                    } else {
18273                        let Some(workspace) = workspace else {
18274                            return Navigated::No;
18275                        };
18276                        let pane = workspace.read(cx).active_pane().clone();
18277                        window.defer(cx, move |window, cx| {
18278                            let (target_editor, target_pane): (Entity<Self>, Entity<Pane>) =
18279                                workspace.update(cx, |workspace, cx| {
18280                                    let pane = if split {
18281                                        workspace.adjacent_pane(window, cx)
18282                                    } else {
18283                                        workspace.active_pane().clone()
18284                                    };
18285
18286                                    let preview_tabs_settings = PreviewTabsSettings::get_global(cx);
18287                                    let keep_old_preview = preview_tabs_settings
18288                                        .enable_keep_preview_on_code_navigation;
18289                                    let allow_new_preview = preview_tabs_settings
18290                                        .enable_preview_file_from_code_navigation;
18291
18292                                    let editor = workspace.open_project_item(
18293                                        pane.clone(),
18294                                        target_buffer.clone(),
18295                                        true,
18296                                        true,
18297                                        keep_old_preview,
18298                                        allow_new_preview,
18299                                        window,
18300                                        cx,
18301                                    );
18302                                    (editor, pane)
18303                                });
18304                            // We create our own nav history instead of using
18305                            // `target_editor.nav_history` because `nav_history`
18306                            // seems to be populated asynchronously when an item
18307                            // is added to a pane
18308                            let mut nav_history = target_pane
18309                                .update(cx, |pane, _| pane.nav_history_for_item(&target_editor));
18310                            target_editor.update(cx, |target_editor, cx| {
18311                                // When selecting a definition in a different buffer, disable the nav history
18312                                // to avoid creating a history entry at the previous cursor location.
18313                                pane.update(cx, |pane, _| pane.disable_history());
18314                                target_editor.go_to_singleton_buffer_range(range, window, cx);
18315
18316                                let nav_data = target_editor.navigation_data(
18317                                    target_editor.selections.newest_anchor().head(),
18318                                    cx,
18319                                );
18320                                let target =
18321                                    Some(nav_history.navigation_entry(Some(
18322                                        Arc::new(nav_data) as Arc<dyn Any + Send + Sync>
18323                                    )));
18324                                nav_history.push_tag(origin, target);
18325                                pane.update(cx, |pane, _| pane.enable_history());
18326                            });
18327                        });
18328                    }
18329                    Navigated::Yes
18330                })
18331            }
18332        })
18333    }
18334
18335    fn compute_target_location(
18336        &self,
18337        lsp_location: lsp::Location,
18338        server_id: LanguageServerId,
18339        window: &mut Window,
18340        cx: &mut Context<Self>,
18341    ) -> Task<anyhow::Result<Option<Location>>> {
18342        let Some(project) = self.project.clone() else {
18343            return Task::ready(Ok(None));
18344        };
18345
18346        cx.spawn_in(window, async move |editor, cx| {
18347            let location_task = editor.update(cx, |_, cx| {
18348                project.update(cx, |project, cx| {
18349                    project.open_local_buffer_via_lsp(lsp_location.uri.clone(), server_id, cx)
18350                })
18351            })?;
18352            let location = Some({
18353                let target_buffer_handle = location_task.await.context("open local buffer")?;
18354                let range = target_buffer_handle.read_with(cx, |target_buffer, _| {
18355                    let target_start = target_buffer
18356                        .clip_point_utf16(point_from_lsp(lsp_location.range.start), Bias::Left);
18357                    let target_end = target_buffer
18358                        .clip_point_utf16(point_from_lsp(lsp_location.range.end), Bias::Left);
18359                    target_buffer.anchor_after(target_start)
18360                        ..target_buffer.anchor_before(target_end)
18361                });
18362                Location {
18363                    buffer: target_buffer_handle,
18364                    range,
18365                }
18366            });
18367            Ok(location)
18368        })
18369    }
18370
18371    fn go_to_next_reference(
18372        &mut self,
18373        _: &GoToNextReference,
18374        window: &mut Window,
18375        cx: &mut Context<Self>,
18376    ) {
18377        let task = self.go_to_reference_before_or_after_position(Direction::Next, 1, window, cx);
18378        if let Some(task) = task {
18379            task.detach();
18380        };
18381    }
18382
18383    fn go_to_prev_reference(
18384        &mut self,
18385        _: &GoToPreviousReference,
18386        window: &mut Window,
18387        cx: &mut Context<Self>,
18388    ) {
18389        let task = self.go_to_reference_before_or_after_position(Direction::Prev, 1, window, cx);
18390        if let Some(task) = task {
18391            task.detach();
18392        };
18393    }
18394
18395    pub fn go_to_reference_before_or_after_position(
18396        &mut self,
18397        direction: Direction,
18398        count: usize,
18399        window: &mut Window,
18400        cx: &mut Context<Self>,
18401    ) -> Option<Task<Result<()>>> {
18402        let selection = self.selections.newest_anchor();
18403        let head = selection.head();
18404
18405        let multi_buffer = self.buffer.read(cx);
18406
18407        let (buffer, text_head) = multi_buffer.text_anchor_for_position(head, cx)?;
18408        let workspace = self.workspace()?;
18409        let project = workspace.read(cx).project().clone();
18410        let references =
18411            project.update(cx, |project, cx| project.references(&buffer, text_head, cx));
18412        Some(cx.spawn_in(window, async move |editor, cx| -> Result<()> {
18413            let Some(locations) = references.await? else {
18414                return Ok(());
18415            };
18416
18417            if locations.is_empty() {
18418                // totally normal - the cursor may be on something which is not
18419                // a symbol (e.g. a keyword)
18420                log::info!("no references found under cursor");
18421                return Ok(());
18422            }
18423
18424            let multi_buffer = editor.read_with(cx, |editor, _| editor.buffer().clone())?;
18425
18426            let (locations, current_location_index) =
18427                multi_buffer.update(cx, |multi_buffer, cx| {
18428                    let mut locations = locations
18429                        .into_iter()
18430                        .filter_map(|loc| {
18431                            let start = multi_buffer.buffer_anchor_to_anchor(
18432                                &loc.buffer,
18433                                loc.range.start,
18434                                cx,
18435                            )?;
18436                            let end = multi_buffer.buffer_anchor_to_anchor(
18437                                &loc.buffer,
18438                                loc.range.end,
18439                                cx,
18440                            )?;
18441                            Some(start..end)
18442                        })
18443                        .collect::<Vec<_>>();
18444
18445                    let multi_buffer_snapshot = multi_buffer.snapshot(cx);
18446                    // There is an O(n) implementation, but given this list will be
18447                    // small (usually <100 items), the extra O(log(n)) factor isn't
18448                    // worth the (surprisingly large amount of) extra complexity.
18449                    locations
18450                        .sort_unstable_by(|l, r| l.start.cmp(&r.start, &multi_buffer_snapshot));
18451
18452                    let head_offset = head.to_offset(&multi_buffer_snapshot);
18453
18454                    let current_location_index = locations.iter().position(|loc| {
18455                        loc.start.to_offset(&multi_buffer_snapshot) <= head_offset
18456                            && loc.end.to_offset(&multi_buffer_snapshot) >= head_offset
18457                    });
18458
18459                    (locations, current_location_index)
18460                });
18461
18462            let Some(current_location_index) = current_location_index else {
18463                // This indicates something has gone wrong, because we already
18464                // handle the "no references" case above
18465                log::error!(
18466                    "failed to find current reference under cursor. Total references: {}",
18467                    locations.len()
18468                );
18469                return Ok(());
18470            };
18471
18472            let destination_location_index = match direction {
18473                Direction::Next => (current_location_index + count) % locations.len(),
18474                Direction::Prev => {
18475                    (current_location_index + locations.len() - count % locations.len())
18476                        % locations.len()
18477                }
18478            };
18479
18480            // TODO(cameron): is this needed?
18481            // the thinking is to avoid "jumping to the current location" (avoid
18482            // polluting "jumplist" in vim terms)
18483            if current_location_index == destination_location_index {
18484                return Ok(());
18485            }
18486
18487            let Range { start, end } = locations[destination_location_index];
18488
18489            editor.update_in(cx, |editor, window, cx| {
18490                let effects = SelectionEffects::default();
18491
18492                editor.unfold_ranges(&[start..end], false, false, cx);
18493                editor.change_selections(effects, window, cx, |s| {
18494                    s.select_ranges([start..start]);
18495                });
18496            })?;
18497
18498            Ok(())
18499        }))
18500    }
18501
18502    pub fn find_all_references(
18503        &mut self,
18504        action: &FindAllReferences,
18505        window: &mut Window,
18506        cx: &mut Context<Self>,
18507    ) -> Option<Task<Result<Navigated>>> {
18508        let always_open_multibuffer = action.always_open_multibuffer;
18509        let selection = self.selections.newest_anchor();
18510        let multi_buffer = self.buffer.read(cx);
18511        let multi_buffer_snapshot = multi_buffer.snapshot(cx);
18512        let selection_offset = selection.map(|anchor| anchor.to_offset(&multi_buffer_snapshot));
18513        let selection_point = selection.map(|anchor| anchor.to_point(&multi_buffer_snapshot));
18514        let head = selection_offset.head();
18515
18516        let head_anchor = multi_buffer_snapshot.anchor_at(
18517            head,
18518            if head < selection_offset.tail() {
18519                Bias::Right
18520            } else {
18521                Bias::Left
18522            },
18523        );
18524
18525        match self
18526            .find_all_references_task_sources
18527            .binary_search_by(|anchor| anchor.cmp(&head_anchor, &multi_buffer_snapshot))
18528        {
18529            Ok(_) => {
18530                log::info!(
18531                    "Ignoring repeated FindAllReferences invocation with the position of already running task"
18532                );
18533                return None;
18534            }
18535            Err(i) => {
18536                self.find_all_references_task_sources.insert(i, head_anchor);
18537            }
18538        }
18539
18540        let (buffer, head) = multi_buffer.text_anchor_for_position(head, cx)?;
18541        let workspace = self.workspace()?;
18542        let project = workspace.read(cx).project().clone();
18543        let references = project.update(cx, |project, cx| project.references(&buffer, head, cx));
18544        Some(cx.spawn_in(window, async move |editor, cx| {
18545            let _cleanup = cx.on_drop(&editor, move |editor, _| {
18546                if let Ok(i) = editor
18547                    .find_all_references_task_sources
18548                    .binary_search_by(|anchor| anchor.cmp(&head_anchor, &multi_buffer_snapshot))
18549                {
18550                    editor.find_all_references_task_sources.remove(i);
18551                }
18552            });
18553
18554            let Some(locations) = references.await? else {
18555                return anyhow::Ok(Navigated::No);
18556            };
18557            let mut locations = cx.update(|_, cx| {
18558                locations
18559                    .into_iter()
18560                    .map(|location| {
18561                        let buffer = location.buffer.read(cx);
18562                        (location.buffer, location.range.to_point(buffer))
18563                    })
18564                    // if special-casing the single-match case, remove ranges
18565                    // that intersect current selection
18566                    .filter(|(location_buffer, location)| {
18567                        if always_open_multibuffer || &buffer != location_buffer {
18568                            return true;
18569                        }
18570
18571                        !location.contains_inclusive(&selection_point.range())
18572                    })
18573                    .into_group_map()
18574            })?;
18575            if locations.is_empty() {
18576                return anyhow::Ok(Navigated::No);
18577            }
18578            for ranges in locations.values_mut() {
18579                ranges.sort_by_key(|range| (range.start, Reverse(range.end)));
18580                ranges.dedup();
18581            }
18582            let mut num_locations = 0;
18583            for ranges in locations.values_mut() {
18584                ranges.sort_by_key(|range| (range.start, Reverse(range.end)));
18585                ranges.dedup();
18586                num_locations += ranges.len();
18587            }
18588
18589            if num_locations == 1 && !always_open_multibuffer {
18590                let (target_buffer, target_ranges) = locations.into_iter().next().unwrap();
18591                let target_range = target_ranges.first().unwrap().clone();
18592
18593                return editor.update_in(cx, |editor, window, cx| {
18594                    let range = target_range.to_point(target_buffer.read(cx));
18595                    let range = editor.range_for_match(&range);
18596                    let range = range.start..range.start;
18597
18598                    if Some(&target_buffer) == editor.buffer.read(cx).as_singleton().as_ref() {
18599                        editor.go_to_singleton_buffer_range(range, window, cx);
18600                    } else {
18601                        let pane = workspace.read(cx).active_pane().clone();
18602                        window.defer(cx, move |window, cx| {
18603                            let target_editor: Entity<Self> =
18604                                workspace.update(cx, |workspace, cx| {
18605                                    let pane = workspace.active_pane().clone();
18606
18607                                    let preview_tabs_settings = PreviewTabsSettings::get_global(cx);
18608                                    let keep_old_preview = preview_tabs_settings
18609                                        .enable_keep_preview_on_code_navigation;
18610                                    let allow_new_preview = preview_tabs_settings
18611                                        .enable_preview_file_from_code_navigation;
18612
18613                                    workspace.open_project_item(
18614                                        pane,
18615                                        target_buffer.clone(),
18616                                        true,
18617                                        true,
18618                                        keep_old_preview,
18619                                        allow_new_preview,
18620                                        window,
18621                                        cx,
18622                                    )
18623                                });
18624                            target_editor.update(cx, |target_editor, cx| {
18625                                // When selecting a definition in a different buffer, disable the nav history
18626                                // to avoid creating a history entry at the previous cursor location.
18627                                pane.update(cx, |pane, _| pane.disable_history());
18628                                target_editor.go_to_singleton_buffer_range(range, window, cx);
18629                                pane.update(cx, |pane, _| pane.enable_history());
18630                            });
18631                        });
18632                    }
18633                    Navigated::No
18634                });
18635            }
18636
18637            workspace.update_in(cx, |workspace, window, cx| {
18638                let target = locations
18639                    .iter()
18640                    .flat_map(|(k, v)| iter::repeat(k.clone()).zip(v))
18641                    .map(|(buffer, location)| {
18642                        buffer
18643                            .read(cx)
18644                            .text_for_range(location.clone())
18645                            .collect::<String>()
18646                    })
18647                    .filter(|text| !text.contains('\n'))
18648                    .unique()
18649                    .take(3)
18650                    .join(", ");
18651                let title = if target.is_empty() {
18652                    "References".to_owned()
18653                } else {
18654                    format!("References to {target}")
18655                };
18656                let allow_preview = PreviewTabsSettings::get_global(cx)
18657                    .enable_preview_multibuffer_from_code_navigation;
18658                Self::open_locations_in_multibuffer(
18659                    workspace,
18660                    locations,
18661                    title,
18662                    false,
18663                    allow_preview,
18664                    MultibufferSelectionMode::First,
18665                    window,
18666                    cx,
18667                );
18668                Navigated::Yes
18669            })
18670        }))
18671    }
18672
18673    /// Opens a multibuffer with the given project locations in it.
18674    pub fn open_locations_in_multibuffer(
18675        workspace: &mut Workspace,
18676        locations: std::collections::HashMap<Entity<Buffer>, Vec<Range<Point>>>,
18677        title: String,
18678        split: bool,
18679        allow_preview: bool,
18680        multibuffer_selection_mode: MultibufferSelectionMode,
18681        window: &mut Window,
18682        cx: &mut Context<Workspace>,
18683    ) -> Option<(Entity<Editor>, Entity<Pane>)> {
18684        if locations.is_empty() {
18685            log::error!("bug: open_locations_in_multibuffer called with empty list of locations");
18686            return None;
18687        }
18688
18689        let capability = workspace.project().read(cx).capability();
18690        let mut ranges = <Vec<Range<Anchor>>>::new();
18691
18692        // a key to find existing multibuffer editors with the same set of locations
18693        // to prevent us from opening more and more multibuffer tabs for searches and the like
18694        let mut key = (title.clone(), vec![]);
18695        let excerpt_buffer = cx.new(|cx| {
18696            let key = &mut key.1;
18697            let mut multibuffer = MultiBuffer::new(capability);
18698            for (buffer, mut ranges_for_buffer) in locations {
18699                ranges_for_buffer.sort_by_key(|range| (range.start, Reverse(range.end)));
18700                key.push((buffer.read(cx).remote_id(), ranges_for_buffer.clone()));
18701                let (new_ranges, _) = multibuffer.set_excerpts_for_path(
18702                    PathKey::for_buffer(&buffer, cx),
18703                    buffer.clone(),
18704                    ranges_for_buffer,
18705                    multibuffer_context_lines(cx),
18706                    cx,
18707                );
18708                ranges.extend(new_ranges)
18709            }
18710
18711            multibuffer.with_title(title)
18712        });
18713        let existing = workspace.active_pane().update(cx, |pane, cx| {
18714            pane.items()
18715                .filter_map(|item| item.downcast::<Editor>())
18716                .find(|editor| {
18717                    editor
18718                        .read(cx)
18719                        .lookup_key
18720                        .as_ref()
18721                        .and_then(|it| {
18722                            it.downcast_ref::<(String, Vec<(BufferId, Vec<Range<Point>>)>)>()
18723                        })
18724                        .is_some_and(|it| *it == key)
18725                })
18726        });
18727        let was_existing = existing.is_some();
18728        let editor = existing.unwrap_or_else(|| {
18729            cx.new(|cx| {
18730                let mut editor = Editor::for_multibuffer(
18731                    excerpt_buffer,
18732                    Some(workspace.project().clone()),
18733                    window,
18734                    cx,
18735                );
18736                editor.lookup_key = Some(Box::new(key));
18737                editor
18738            })
18739        });
18740        editor.update(cx, |editor, cx| match multibuffer_selection_mode {
18741            MultibufferSelectionMode::First => {
18742                if let Some(first_range) = ranges.first() {
18743                    editor.change_selections(
18744                        SelectionEffects::no_scroll(),
18745                        window,
18746                        cx,
18747                        |selections| {
18748                            selections.clear_disjoint();
18749                            selections.select_anchor_ranges(std::iter::once(first_range.clone()));
18750                        },
18751                    );
18752                }
18753                editor.highlight_background(
18754                    HighlightKey::Editor,
18755                    &ranges,
18756                    |_, theme| theme.colors().editor_highlighted_line_background,
18757                    cx,
18758                );
18759            }
18760            MultibufferSelectionMode::All => {
18761                editor.change_selections(SelectionEffects::no_scroll(), window, cx, |selections| {
18762                    selections.clear_disjoint();
18763                    selections.select_anchor_ranges(ranges);
18764                });
18765            }
18766        });
18767
18768        let item = Box::new(editor.clone());
18769
18770        let pane = if split {
18771            workspace.adjacent_pane(window, cx)
18772        } else {
18773            workspace.active_pane().clone()
18774        };
18775        let activate_pane = split;
18776
18777        let mut destination_index = None;
18778        pane.update(cx, |pane, cx| {
18779            if allow_preview && !was_existing {
18780                destination_index = pane.replace_preview_item_id(item.item_id(), window, cx);
18781            }
18782            if was_existing && !allow_preview {
18783                pane.unpreview_item_if_preview(item.item_id());
18784            }
18785            pane.add_item(item, activate_pane, true, destination_index, window, cx);
18786        });
18787
18788        Some((editor, pane))
18789    }
18790
18791    pub fn rename(
18792        &mut self,
18793        _: &Rename,
18794        window: &mut Window,
18795        cx: &mut Context<Self>,
18796    ) -> Option<Task<Result<()>>> {
18797        use language::ToOffset as _;
18798
18799        let provider = self.semantics_provider.clone()?;
18800        let selection = self.selections.newest_anchor().clone();
18801        let (cursor_buffer, cursor_buffer_position) = self
18802            .buffer
18803            .read(cx)
18804            .text_anchor_for_position(selection.head(), cx)?;
18805        let (tail_buffer, cursor_buffer_position_end) = self
18806            .buffer
18807            .read(cx)
18808            .text_anchor_for_position(selection.tail(), cx)?;
18809        if tail_buffer != cursor_buffer {
18810            return None;
18811        }
18812
18813        let snapshot = cursor_buffer.read(cx).snapshot();
18814        let cursor_buffer_offset = cursor_buffer_position.to_offset(&snapshot);
18815        let cursor_buffer_offset_end = cursor_buffer_position_end.to_offset(&snapshot);
18816        let prepare_rename = provider
18817            .range_for_rename(&cursor_buffer, cursor_buffer_position, cx)
18818            .unwrap_or_else(|| Task::ready(Ok(None)));
18819        drop(snapshot);
18820
18821        Some(cx.spawn_in(window, async move |this, cx| {
18822            let rename_range = if let Some(range) = prepare_rename.await? {
18823                Some(range)
18824            } else {
18825                this.update(cx, |this, cx| {
18826                    let buffer = this.buffer.read(cx).snapshot(cx);
18827                    let mut buffer_highlights = this
18828                        .document_highlights_for_position(selection.head(), &buffer)
18829                        .filter(|highlight| {
18830                            highlight.start.excerpt_id == selection.head().excerpt_id
18831                                && highlight.end.excerpt_id == selection.head().excerpt_id
18832                        });
18833                    buffer_highlights
18834                        .next()
18835                        .map(|highlight| highlight.start.text_anchor..highlight.end.text_anchor)
18836                })?
18837            };
18838            if let Some(rename_range) = rename_range {
18839                this.update_in(cx, |this, window, cx| {
18840                    let snapshot = cursor_buffer.read(cx).snapshot();
18841                    let rename_buffer_range = rename_range.to_offset(&snapshot);
18842                    let cursor_offset_in_rename_range =
18843                        cursor_buffer_offset.saturating_sub(rename_buffer_range.start);
18844                    let cursor_offset_in_rename_range_end =
18845                        cursor_buffer_offset_end.saturating_sub(rename_buffer_range.start);
18846
18847                    this.take_rename(false, window, cx);
18848                    let buffer = this.buffer.read(cx).read(cx);
18849                    let cursor_offset = selection.head().to_offset(&buffer);
18850                    let rename_start =
18851                        cursor_offset.saturating_sub_usize(cursor_offset_in_rename_range);
18852                    let rename_end = rename_start + rename_buffer_range.len();
18853                    let range = buffer.anchor_before(rename_start)..buffer.anchor_after(rename_end);
18854                    let mut old_highlight_id = None;
18855                    let old_name: Arc<str> = buffer
18856                        .chunks(rename_start..rename_end, true)
18857                        .map(|chunk| {
18858                            if old_highlight_id.is_none() {
18859                                old_highlight_id = chunk.syntax_highlight_id;
18860                            }
18861                            chunk.text
18862                        })
18863                        .collect::<String>()
18864                        .into();
18865
18866                    drop(buffer);
18867
18868                    // Position the selection in the rename editor so that it matches the current selection.
18869                    this.show_local_selections = false;
18870                    let rename_editor = cx.new(|cx| {
18871                        let mut editor = Editor::single_line(window, cx);
18872                        editor.buffer.update(cx, |buffer, cx| {
18873                            buffer.edit(
18874                                [(MultiBufferOffset(0)..MultiBufferOffset(0), old_name.clone())],
18875                                None,
18876                                cx,
18877                            )
18878                        });
18879                        let cursor_offset_in_rename_range =
18880                            MultiBufferOffset(cursor_offset_in_rename_range);
18881                        let cursor_offset_in_rename_range_end =
18882                            MultiBufferOffset(cursor_offset_in_rename_range_end);
18883                        let rename_selection_range = match cursor_offset_in_rename_range
18884                            .cmp(&cursor_offset_in_rename_range_end)
18885                        {
18886                            Ordering::Equal => {
18887                                editor.select_all(&SelectAll, window, cx);
18888                                return editor;
18889                            }
18890                            Ordering::Less => {
18891                                cursor_offset_in_rename_range..cursor_offset_in_rename_range_end
18892                            }
18893                            Ordering::Greater => {
18894                                cursor_offset_in_rename_range_end..cursor_offset_in_rename_range
18895                            }
18896                        };
18897                        if rename_selection_range.end.0 > old_name.len() {
18898                            editor.select_all(&SelectAll, window, cx);
18899                        } else {
18900                            editor.change_selections(Default::default(), window, cx, |s| {
18901                                s.select_ranges([rename_selection_range]);
18902                            });
18903                        }
18904                        editor
18905                    });
18906                    cx.subscribe(&rename_editor, |_, _, e: &EditorEvent, cx| {
18907                        if e == &EditorEvent::Focused {
18908                            cx.emit(EditorEvent::FocusedIn)
18909                        }
18910                    })
18911                    .detach();
18912
18913                    let write_highlights =
18914                        this.clear_background_highlights(HighlightKey::DocumentHighlightWrite, cx);
18915                    let read_highlights =
18916                        this.clear_background_highlights(HighlightKey::DocumentHighlightRead, cx);
18917                    let ranges = write_highlights
18918                        .iter()
18919                        .flat_map(|(_, ranges)| ranges.iter())
18920                        .chain(read_highlights.iter().flat_map(|(_, ranges)| ranges.iter()))
18921                        .cloned()
18922                        .collect();
18923
18924                    this.highlight_text(
18925                        HighlightKey::Rename,
18926                        ranges,
18927                        HighlightStyle {
18928                            fade_out: Some(0.6),
18929                            ..Default::default()
18930                        },
18931                        cx,
18932                    );
18933                    let rename_focus_handle = rename_editor.focus_handle(cx);
18934                    window.focus(&rename_focus_handle, cx);
18935                    let block_id = this.insert_blocks(
18936                        [BlockProperties {
18937                            style: BlockStyle::Flex,
18938                            placement: BlockPlacement::Below(range.start),
18939                            height: Some(1),
18940                            render: Arc::new({
18941                                let rename_editor = rename_editor.clone();
18942                                move |cx: &mut BlockContext| {
18943                                    let mut text_style = cx.editor_style.text.clone();
18944                                    if let Some(highlight_style) = old_highlight_id
18945                                        .and_then(|h| h.style(&cx.editor_style.syntax))
18946                                    {
18947                                        text_style = text_style.highlight(highlight_style);
18948                                    }
18949                                    div()
18950                                        .block_mouse_except_scroll()
18951                                        .pl(cx.anchor_x)
18952                                        .child(EditorElement::new(
18953                                            &rename_editor,
18954                                            EditorStyle {
18955                                                background: cx.theme().system().transparent,
18956                                                local_player: cx.editor_style.local_player,
18957                                                text: text_style,
18958                                                scrollbar_width: cx.editor_style.scrollbar_width,
18959                                                syntax: cx.editor_style.syntax.clone(),
18960                                                status: cx.editor_style.status.clone(),
18961                                                inlay_hints_style: HighlightStyle {
18962                                                    font_weight: Some(FontWeight::BOLD),
18963                                                    ..make_inlay_hints_style(cx.app)
18964                                                },
18965                                                edit_prediction_styles: make_suggestion_styles(
18966                                                    cx.app,
18967                                                ),
18968                                                ..EditorStyle::default()
18969                                            },
18970                                        ))
18971                                        .into_any_element()
18972                                }
18973                            }),
18974                            priority: 0,
18975                        }],
18976                        Some(Autoscroll::fit()),
18977                        cx,
18978                    )[0];
18979                    this.pending_rename = Some(RenameState {
18980                        range,
18981                        old_name,
18982                        editor: rename_editor,
18983                        block_id,
18984                    });
18985                })?;
18986            }
18987
18988            Ok(())
18989        }))
18990    }
18991
18992    pub fn confirm_rename(
18993        &mut self,
18994        _: &ConfirmRename,
18995        window: &mut Window,
18996        cx: &mut Context<Self>,
18997    ) -> Option<Task<Result<()>>> {
18998        let rename = self.take_rename(false, window, cx)?;
18999        let workspace = self.workspace()?.downgrade();
19000        let (buffer, start) = self
19001            .buffer
19002            .read(cx)
19003            .text_anchor_for_position(rename.range.start, cx)?;
19004        let (end_buffer, _) = self
19005            .buffer
19006            .read(cx)
19007            .text_anchor_for_position(rename.range.end, cx)?;
19008        if buffer != end_buffer {
19009            return None;
19010        }
19011
19012        let old_name = rename.old_name;
19013        let new_name = rename.editor.read(cx).text(cx);
19014
19015        let rename = self.semantics_provider.as_ref()?.perform_rename(
19016            &buffer,
19017            start,
19018            new_name.clone(),
19019            cx,
19020        )?;
19021
19022        Some(cx.spawn_in(window, async move |editor, cx| {
19023            let project_transaction = rename.await?;
19024            Self::open_project_transaction(
19025                &editor,
19026                workspace,
19027                project_transaction,
19028                format!("Rename: {}{}", old_name, new_name),
19029                cx,
19030            )
19031            .await?;
19032
19033            editor.update(cx, |editor, cx| {
19034                editor.refresh_document_highlights(cx);
19035            })?;
19036            Ok(())
19037        }))
19038    }
19039
19040    fn take_rename(
19041        &mut self,
19042        moving_cursor: bool,
19043        window: &mut Window,
19044        cx: &mut Context<Self>,
19045    ) -> Option<RenameState> {
19046        let rename = self.pending_rename.take()?;
19047        if rename.editor.focus_handle(cx).is_focused(window) {
19048            window.focus(&self.focus_handle, cx);
19049        }
19050
19051        self.remove_blocks(
19052            [rename.block_id].into_iter().collect(),
19053            Some(Autoscroll::fit()),
19054            cx,
19055        );
19056        self.clear_highlights(HighlightKey::Rename, cx);
19057        self.show_local_selections = true;
19058
19059        if moving_cursor {
19060            let cursor_in_rename_editor = rename.editor.update(cx, |editor, cx| {
19061                editor
19062                    .selections
19063                    .newest::<MultiBufferOffset>(&editor.display_snapshot(cx))
19064                    .head()
19065            });
19066
19067            // Update the selection to match the position of the selection inside
19068            // the rename editor.
19069            let snapshot = self.buffer.read(cx).read(cx);
19070            let rename_range = rename.range.to_offset(&snapshot);
19071            let cursor_in_editor = snapshot
19072                .clip_offset(rename_range.start + cursor_in_rename_editor, Bias::Left)
19073                .min(rename_range.end);
19074            drop(snapshot);
19075
19076            self.change_selections(SelectionEffects::no_scroll(), window, cx, |s| {
19077                s.select_ranges(vec![cursor_in_editor..cursor_in_editor])
19078            });
19079        } else {
19080            self.refresh_document_highlights(cx);
19081        }
19082
19083        Some(rename)
19084    }
19085
19086    pub fn pending_rename(&self) -> Option<&RenameState> {
19087        self.pending_rename.as_ref()
19088    }
19089
19090    fn format(
19091        &mut self,
19092        _: &Format,
19093        window: &mut Window,
19094        cx: &mut Context<Self>,
19095    ) -> Option<Task<Result<()>>> {
19096        self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
19097
19098        let project = match &self.project {
19099            Some(project) => project.clone(),
19100            None => return None,
19101        };
19102
19103        Some(self.perform_format(
19104            project,
19105            FormatTrigger::Manual,
19106            FormatTarget::Buffers(self.buffer.read(cx).all_buffers()),
19107            window,
19108            cx,
19109        ))
19110    }
19111
19112    fn format_selections(
19113        &mut self,
19114        _: &FormatSelections,
19115        window: &mut Window,
19116        cx: &mut Context<Self>,
19117    ) -> Option<Task<Result<()>>> {
19118        self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
19119
19120        let project = match &self.project {
19121            Some(project) => project.clone(),
19122            None => return None,
19123        };
19124
19125        let ranges = self
19126            .selections
19127            .all_adjusted(&self.display_snapshot(cx))
19128            .into_iter()
19129            .map(|selection| selection.range())
19130            .collect_vec();
19131
19132        Some(self.perform_format(
19133            project,
19134            FormatTrigger::Manual,
19135            FormatTarget::Ranges(ranges),
19136            window,
19137            cx,
19138        ))
19139    }
19140
19141    fn perform_format(
19142        &mut self,
19143        project: Entity<Project>,
19144        trigger: FormatTrigger,
19145        target: FormatTarget,
19146        window: &mut Window,
19147        cx: &mut Context<Self>,
19148    ) -> Task<Result<()>> {
19149        let buffer = self.buffer.clone();
19150        let (buffers, target) = match target {
19151            FormatTarget::Buffers(buffers) => (buffers, LspFormatTarget::Buffers),
19152            FormatTarget::Ranges(selection_ranges) => {
19153                let multi_buffer = buffer.read(cx);
19154                let snapshot = multi_buffer.read(cx);
19155                let mut buffers = HashSet::default();
19156                let mut buffer_id_to_ranges: BTreeMap<BufferId, Vec<Range<text::Anchor>>> =
19157                    BTreeMap::new();
19158                for selection_range in selection_ranges {
19159                    for (buffer, buffer_range, _) in
19160                        snapshot.range_to_buffer_ranges(selection_range.start..=selection_range.end)
19161                    {
19162                        let buffer_id = buffer.remote_id();
19163                        let start = buffer.anchor_before(buffer_range.start);
19164                        let end = buffer.anchor_after(buffer_range.end);
19165                        buffers.insert(multi_buffer.buffer(buffer_id).unwrap());
19166                        buffer_id_to_ranges
19167                            .entry(buffer_id)
19168                            .and_modify(|buffer_ranges| buffer_ranges.push(start..end))
19169                            .or_insert_with(|| vec![start..end]);
19170                    }
19171                }
19172                (buffers, LspFormatTarget::Ranges(buffer_id_to_ranges))
19173            }
19174        };
19175
19176        let transaction_id_prev = buffer.read(cx).last_transaction_id(cx);
19177        let selections_prev = transaction_id_prev
19178            .and_then(|transaction_id_prev| {
19179                // default to selections as they were after the last edit, if we have them,
19180                // instead of how they are now.
19181                // This will make it so that editing, moving somewhere else, formatting, then undoing the format
19182                // will take you back to where you made the last edit, instead of staying where you scrolled
19183                self.selection_history
19184                    .transaction(transaction_id_prev)
19185                    .map(|t| t.0.clone())
19186            })
19187            .unwrap_or_else(|| self.selections.disjoint_anchors_arc());
19188
19189        let mut timeout = cx.background_executor().timer(FORMAT_TIMEOUT).fuse();
19190        let format = project.update(cx, |project, cx| {
19191            project.format(buffers, target, true, trigger, cx)
19192        });
19193
19194        cx.spawn_in(window, async move |editor, cx| {
19195            let transaction = futures::select_biased! {
19196                transaction = format.log_err().fuse() => transaction,
19197                () = timeout => {
19198                    log::warn!("timed out waiting for formatting");
19199                    None
19200                }
19201            };
19202
19203            buffer.update(cx, |buffer, cx| {
19204                if let Some(transaction) = transaction
19205                    && !buffer.is_singleton()
19206                {
19207                    buffer.push_transaction(&transaction.0, cx);
19208                }
19209                cx.notify();
19210            });
19211
19212            if let Some(transaction_id_now) =
19213                buffer.read_with(cx, |b, cx| b.last_transaction_id(cx))
19214            {
19215                let has_new_transaction = transaction_id_prev != Some(transaction_id_now);
19216                if has_new_transaction {
19217                    editor
19218                        .update(cx, |editor, _| {
19219                            editor
19220                                .selection_history
19221                                .insert_transaction(transaction_id_now, selections_prev);
19222                        })
19223                        .ok();
19224                }
19225            }
19226
19227            Ok(())
19228        })
19229    }
19230
19231    fn organize_imports(
19232        &mut self,
19233        _: &OrganizeImports,
19234        window: &mut Window,
19235        cx: &mut Context<Self>,
19236    ) -> Option<Task<Result<()>>> {
19237        self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
19238        let project = match &self.project {
19239            Some(project) => project.clone(),
19240            None => return None,
19241        };
19242        Some(self.perform_code_action_kind(
19243            project,
19244            CodeActionKind::SOURCE_ORGANIZE_IMPORTS,
19245            window,
19246            cx,
19247        ))
19248    }
19249
19250    fn perform_code_action_kind(
19251        &mut self,
19252        project: Entity<Project>,
19253        kind: CodeActionKind,
19254        window: &mut Window,
19255        cx: &mut Context<Self>,
19256    ) -> Task<Result<()>> {
19257        let buffer = self.buffer.clone();
19258        let buffers = buffer.read(cx).all_buffers();
19259        let mut timeout = cx.background_executor().timer(CODE_ACTION_TIMEOUT).fuse();
19260        let apply_action = project.update(cx, |project, cx| {
19261            project.apply_code_action_kind(buffers, kind, true, cx)
19262        });
19263        cx.spawn_in(window, async move |_, cx| {
19264            let transaction = futures::select_biased! {
19265                () = timeout => {
19266                    log::warn!("timed out waiting for executing code action");
19267                    None
19268                }
19269                transaction = apply_action.log_err().fuse() => transaction,
19270            };
19271            buffer.update(cx, |buffer, cx| {
19272                // check if we need this
19273                if let Some(transaction) = transaction
19274                    && !buffer.is_singleton()
19275                {
19276                    buffer.push_transaction(&transaction.0, cx);
19277                }
19278                cx.notify();
19279            });
19280            Ok(())
19281        })
19282    }
19283
19284    pub fn restart_language_server(
19285        &mut self,
19286        _: &RestartLanguageServer,
19287        _: &mut Window,
19288        cx: &mut Context<Self>,
19289    ) {
19290        if let Some(project) = self.project.clone() {
19291            self.buffer.update(cx, |multi_buffer, cx| {
19292                project.update(cx, |project, cx| {
19293                    project.restart_language_servers_for_buffers(
19294                        multi_buffer.all_buffers().into_iter().collect(),
19295                        HashSet::default(),
19296                        cx,
19297                    );
19298                });
19299            })
19300        }
19301    }
19302
19303    pub fn stop_language_server(
19304        &mut self,
19305        _: &StopLanguageServer,
19306        _: &mut Window,
19307        cx: &mut Context<Self>,
19308    ) {
19309        if let Some(project) = self.project.clone() {
19310            self.buffer.update(cx, |multi_buffer, cx| {
19311                project.update(cx, |project, cx| {
19312                    project.stop_language_servers_for_buffers(
19313                        multi_buffer.all_buffers().into_iter().collect(),
19314                        HashSet::default(),
19315                        cx,
19316                    );
19317                });
19318            });
19319        }
19320    }
19321
19322    fn cancel_language_server_work(
19323        workspace: &mut Workspace,
19324        _: &actions::CancelLanguageServerWork,
19325        _: &mut Window,
19326        cx: &mut Context<Workspace>,
19327    ) {
19328        let project = workspace.project();
19329        let buffers = workspace
19330            .active_item(cx)
19331            .and_then(|item| item.act_as::<Editor>(cx))
19332            .map_or(HashSet::default(), |editor| {
19333                editor.read(cx).buffer.read(cx).all_buffers()
19334            });
19335        project.update(cx, |project, cx| {
19336            project.cancel_language_server_work_for_buffers(buffers, cx);
19337        });
19338    }
19339
19340    fn show_character_palette(
19341        &mut self,
19342        _: &ShowCharacterPalette,
19343        window: &mut Window,
19344        _: &mut Context<Self>,
19345    ) {
19346        window.show_character_palette();
19347    }
19348
19349    fn refresh_active_diagnostics(&mut self, cx: &mut Context<Editor>) {
19350        if !self.diagnostics_enabled() {
19351            return;
19352        }
19353
19354        if let ActiveDiagnostic::Group(active_diagnostics) = &mut self.active_diagnostics {
19355            let buffer = self.buffer.read(cx).snapshot(cx);
19356            let primary_range_start = active_diagnostics.active_range.start.to_offset(&buffer);
19357            let primary_range_end = active_diagnostics.active_range.end.to_offset(&buffer);
19358            let is_valid = buffer
19359                .diagnostics_in_range::<MultiBufferOffset>(primary_range_start..primary_range_end)
19360                .any(|entry| {
19361                    entry.diagnostic.is_primary
19362                        && !entry.range.is_empty()
19363                        && entry.range.start == primary_range_start
19364                        && entry.diagnostic.message == active_diagnostics.active_message
19365                });
19366
19367            if !is_valid {
19368                self.dismiss_diagnostics(cx);
19369            }
19370        }
19371    }
19372
19373    pub fn active_diagnostic_group(&self) -> Option<&ActiveDiagnosticGroup> {
19374        match &self.active_diagnostics {
19375            ActiveDiagnostic::Group(group) => Some(group),
19376            _ => None,
19377        }
19378    }
19379
19380    pub fn set_all_diagnostics_active(&mut self, cx: &mut Context<Self>) {
19381        if !self.diagnostics_enabled() {
19382            return;
19383        }
19384        self.dismiss_diagnostics(cx);
19385        self.active_diagnostics = ActiveDiagnostic::All;
19386    }
19387
19388    fn activate_diagnostics(
19389        &mut self,
19390        buffer_id: BufferId,
19391        diagnostic: DiagnosticEntryRef<'_, MultiBufferOffset>,
19392        window: &mut Window,
19393        cx: &mut Context<Self>,
19394    ) {
19395        if !self.diagnostics_enabled() || matches!(self.active_diagnostics, ActiveDiagnostic::All) {
19396            return;
19397        }
19398        self.dismiss_diagnostics(cx);
19399        let snapshot = self.snapshot(window, cx);
19400        let buffer = self.buffer.read(cx).snapshot(cx);
19401        let Some(renderer) = GlobalDiagnosticRenderer::global(cx) else {
19402            return;
19403        };
19404
19405        let diagnostic_group = buffer
19406            .diagnostic_group(buffer_id, diagnostic.diagnostic.group_id)
19407            .collect::<Vec<_>>();
19408
19409        let language_registry = self
19410            .project()
19411            .map(|project| project.read(cx).languages().clone());
19412
19413        let blocks = renderer.render_group(
19414            diagnostic_group,
19415            buffer_id,
19416            snapshot,
19417            cx.weak_entity(),
19418            language_registry,
19419            cx,
19420        );
19421
19422        let blocks = self.display_map.update(cx, |display_map, cx| {
19423            display_map.insert_blocks(blocks, cx).into_iter().collect()
19424        });
19425        self.active_diagnostics = ActiveDiagnostic::Group(ActiveDiagnosticGroup {
19426            active_range: buffer.anchor_before(diagnostic.range.start)
19427                ..buffer.anchor_after(diagnostic.range.end),
19428            active_message: diagnostic.diagnostic.message.clone(),
19429            group_id: diagnostic.diagnostic.group_id,
19430            blocks,
19431        });
19432        cx.notify();
19433    }
19434
19435    fn dismiss_diagnostics(&mut self, cx: &mut Context<Self>) {
19436        if matches!(self.active_diagnostics, ActiveDiagnostic::All) {
19437            return;
19438        };
19439
19440        let prev = mem::replace(&mut self.active_diagnostics, ActiveDiagnostic::None);
19441        if let ActiveDiagnostic::Group(group) = prev {
19442            self.display_map.update(cx, |display_map, cx| {
19443                display_map.remove_blocks(group.blocks, cx);
19444            });
19445            cx.notify();
19446        }
19447    }
19448
19449    /// Disable inline diagnostics rendering for this editor.
19450    pub fn disable_inline_diagnostics(&mut self) {
19451        self.inline_diagnostics_enabled = false;
19452        self.inline_diagnostics_update = Task::ready(());
19453        self.inline_diagnostics.clear();
19454    }
19455
19456    pub fn disable_diagnostics(&mut self, cx: &mut Context<Self>) {
19457        self.diagnostics_enabled = false;
19458        self.dismiss_diagnostics(cx);
19459        self.inline_diagnostics_update = Task::ready(());
19460        self.inline_diagnostics.clear();
19461    }
19462
19463    pub fn disable_word_completions(&mut self) {
19464        self.word_completions_enabled = false;
19465    }
19466
19467    pub fn diagnostics_enabled(&self) -> bool {
19468        self.diagnostics_enabled && self.mode.is_full()
19469    }
19470
19471    pub fn inline_diagnostics_enabled(&self) -> bool {
19472        self.inline_diagnostics_enabled && self.diagnostics_enabled()
19473    }
19474
19475    pub fn show_inline_diagnostics(&self) -> bool {
19476        self.show_inline_diagnostics
19477    }
19478
19479    pub fn toggle_inline_diagnostics(
19480        &mut self,
19481        _: &ToggleInlineDiagnostics,
19482        window: &mut Window,
19483        cx: &mut Context<Editor>,
19484    ) {
19485        self.show_inline_diagnostics = !self.show_inline_diagnostics;
19486        self.refresh_inline_diagnostics(false, window, cx);
19487    }
19488
19489    pub fn set_max_diagnostics_severity(&mut self, severity: DiagnosticSeverity, cx: &mut App) {
19490        self.diagnostics_max_severity = severity;
19491        self.display_map.update(cx, |display_map, _| {
19492            display_map.diagnostics_max_severity = self.diagnostics_max_severity;
19493        });
19494    }
19495
19496    pub fn toggle_diagnostics(
19497        &mut self,
19498        _: &ToggleDiagnostics,
19499        window: &mut Window,
19500        cx: &mut Context<Editor>,
19501    ) {
19502        if !self.diagnostics_enabled() {
19503            return;
19504        }
19505
19506        let new_severity = if self.diagnostics_max_severity == DiagnosticSeverity::Off {
19507            EditorSettings::get_global(cx)
19508                .diagnostics_max_severity
19509                .filter(|severity| severity != &DiagnosticSeverity::Off)
19510                .unwrap_or(DiagnosticSeverity::Hint)
19511        } else {
19512            DiagnosticSeverity::Off
19513        };
19514        self.set_max_diagnostics_severity(new_severity, cx);
19515        if self.diagnostics_max_severity == DiagnosticSeverity::Off {
19516            self.active_diagnostics = ActiveDiagnostic::None;
19517            self.inline_diagnostics_update = Task::ready(());
19518            self.inline_diagnostics.clear();
19519        } else {
19520            self.refresh_inline_diagnostics(false, window, cx);
19521        }
19522
19523        cx.notify();
19524    }
19525
19526    pub fn toggle_minimap(
19527        &mut self,
19528        _: &ToggleMinimap,
19529        window: &mut Window,
19530        cx: &mut Context<Editor>,
19531    ) {
19532        if self.supports_minimap(cx) {
19533            self.set_minimap_visibility(self.minimap_visibility.toggle_visibility(), window, cx);
19534        }
19535    }
19536
19537    fn refresh_inline_diagnostics(
19538        &mut self,
19539        debounce: bool,
19540        window: &mut Window,
19541        cx: &mut Context<Self>,
19542    ) {
19543        let max_severity = ProjectSettings::get_global(cx)
19544            .diagnostics
19545            .inline
19546            .max_severity
19547            .unwrap_or(self.diagnostics_max_severity);
19548
19549        if !self.inline_diagnostics_enabled()
19550            || !self.diagnostics_enabled()
19551            || !self.show_inline_diagnostics
19552            || max_severity == DiagnosticSeverity::Off
19553        {
19554            self.inline_diagnostics_update = Task::ready(());
19555            self.inline_diagnostics.clear();
19556            return;
19557        }
19558
19559        let debounce_ms = ProjectSettings::get_global(cx)
19560            .diagnostics
19561            .inline
19562            .update_debounce_ms;
19563        let debounce = if debounce && debounce_ms > 0 {
19564            Some(Duration::from_millis(debounce_ms))
19565        } else {
19566            None
19567        };
19568        self.inline_diagnostics_update = cx.spawn_in(window, async move |editor, cx| {
19569            if let Some(debounce) = debounce {
19570                cx.background_executor().timer(debounce).await;
19571            }
19572            let Some(snapshot) = editor.upgrade().map(|editor| {
19573                editor.update(cx, |editor, cx| editor.buffer().read(cx).snapshot(cx))
19574            }) else {
19575                return;
19576            };
19577
19578            let new_inline_diagnostics = cx
19579                .background_spawn(async move {
19580                    let mut inline_diagnostics = Vec::<(Anchor, InlineDiagnostic)>::new();
19581                    for diagnostic_entry in
19582                        snapshot.diagnostics_in_range(MultiBufferOffset(0)..snapshot.len())
19583                    {
19584                        let message = diagnostic_entry
19585                            .diagnostic
19586                            .message
19587                            .split_once('\n')
19588                            .map(|(line, _)| line)
19589                            .map(SharedString::new)
19590                            .unwrap_or_else(|| {
19591                                SharedString::new(&*diagnostic_entry.diagnostic.message)
19592                            });
19593                        let start_anchor = snapshot.anchor_before(diagnostic_entry.range.start);
19594                        let (Ok(i) | Err(i)) = inline_diagnostics
19595                            .binary_search_by(|(probe, _)| probe.cmp(&start_anchor, &snapshot));
19596                        inline_diagnostics.insert(
19597                            i,
19598                            (
19599                                start_anchor,
19600                                InlineDiagnostic {
19601                                    message,
19602                                    group_id: diagnostic_entry.diagnostic.group_id,
19603                                    start: diagnostic_entry.range.start.to_point(&snapshot),
19604                                    is_primary: diagnostic_entry.diagnostic.is_primary,
19605                                    severity: diagnostic_entry.diagnostic.severity,
19606                                },
19607                            ),
19608                        );
19609                    }
19610                    inline_diagnostics
19611                })
19612                .await;
19613
19614            editor
19615                .update(cx, |editor, cx| {
19616                    editor.inline_diagnostics = new_inline_diagnostics;
19617                    cx.notify();
19618                })
19619                .ok();
19620        });
19621    }
19622
19623    fn pull_diagnostics(
19624        &mut self,
19625        buffer_id: BufferId,
19626        _window: &Window,
19627        cx: &mut Context<Self>,
19628    ) -> Option<()> {
19629        // `ActiveDiagnostic::All` is a special mode where editor's diagnostics are managed by the external view,
19630        // skip any LSP updates for it.
19631
19632        if self.active_diagnostics == ActiveDiagnostic::All
19633            || !self.mode().is_full()
19634            || !self.diagnostics_enabled()
19635        {
19636            return None;
19637        }
19638        let pull_diagnostics_settings = ProjectSettings::get_global(cx)
19639            .diagnostics
19640            .lsp_pull_diagnostics;
19641        if !pull_diagnostics_settings.enabled {
19642            return None;
19643        }
19644        let debounce = Duration::from_millis(pull_diagnostics_settings.debounce_ms);
19645        let project = self.project()?.downgrade();
19646        let buffer = self.buffer().read(cx).buffer(buffer_id)?;
19647
19648        self.pull_diagnostics_task = cx.spawn(async move |_, cx| {
19649            cx.background_executor().timer(debounce).await;
19650            if let Ok(task) = project.update(cx, |project, cx| {
19651                project.lsp_store().update(cx, |lsp_store, cx| {
19652                    lsp_store.pull_diagnostics_for_buffer(buffer, cx)
19653                })
19654            }) {
19655                task.await.log_err();
19656            }
19657            project
19658                .update(cx, |project, cx| {
19659                    project.lsp_store().update(cx, |lsp_store, cx| {
19660                        lsp_store.pull_document_diagnostics_for_buffer_edit(buffer_id, cx);
19661                    })
19662                })
19663                .log_err();
19664        });
19665
19666        Some(())
19667    }
19668
19669    pub fn set_selections_from_remote(
19670        &mut self,
19671        selections: Vec<Selection<Anchor>>,
19672        pending_selection: Option<Selection<Anchor>>,
19673        window: &mut Window,
19674        cx: &mut Context<Self>,
19675    ) {
19676        let old_cursor_position = self.selections.newest_anchor().head();
19677        self.selections
19678            .change_with(&self.display_snapshot(cx), |s| {
19679                s.select_anchors(selections);
19680                if let Some(pending_selection) = pending_selection {
19681                    s.set_pending(pending_selection, SelectMode::Character);
19682                } else {
19683                    s.clear_pending();
19684                }
19685            });
19686        self.selections_did_change(
19687            false,
19688            &old_cursor_position,
19689            SelectionEffects::default(),
19690            window,
19691            cx,
19692        );
19693    }
19694
19695    pub fn transact(
19696        &mut self,
19697        window: &mut Window,
19698        cx: &mut Context<Self>,
19699        update: impl FnOnce(&mut Self, &mut Window, &mut Context<Self>),
19700    ) -> Option<TransactionId> {
19701        self.with_selection_effects_deferred(window, cx, |this, window, cx| {
19702            this.start_transaction_at(Instant::now(), window, cx);
19703            update(this, window, cx);
19704            this.end_transaction_at(Instant::now(), cx)
19705        })
19706    }
19707
19708    pub fn start_transaction_at(
19709        &mut self,
19710        now: Instant,
19711        window: &mut Window,
19712        cx: &mut Context<Self>,
19713    ) -> Option<TransactionId> {
19714        self.end_selection(window, cx);
19715        if let Some(tx_id) = self
19716            .buffer
19717            .update(cx, |buffer, cx| buffer.start_transaction_at(now, cx))
19718        {
19719            self.selection_history
19720                .insert_transaction(tx_id, self.selections.disjoint_anchors_arc());
19721            cx.emit(EditorEvent::TransactionBegun {
19722                transaction_id: tx_id,
19723            });
19724            Some(tx_id)
19725        } else {
19726            None
19727        }
19728    }
19729
19730    pub fn end_transaction_at(
19731        &mut self,
19732        now: Instant,
19733        cx: &mut Context<Self>,
19734    ) -> Option<TransactionId> {
19735        if let Some(transaction_id) = self
19736            .buffer
19737            .update(cx, |buffer, cx| buffer.end_transaction_at(now, cx))
19738        {
19739            if let Some((_, end_selections)) =
19740                self.selection_history.transaction_mut(transaction_id)
19741            {
19742                *end_selections = Some(self.selections.disjoint_anchors_arc());
19743            } else {
19744                log::error!("unexpectedly ended a transaction that wasn't started by this editor");
19745            }
19746
19747            cx.emit(EditorEvent::Edited { transaction_id });
19748            Some(transaction_id)
19749        } else {
19750            None
19751        }
19752    }
19753
19754    pub fn modify_transaction_selection_history(
19755        &mut self,
19756        transaction_id: TransactionId,
19757        modify: impl FnOnce(&mut (Arc<[Selection<Anchor>]>, Option<Arc<[Selection<Anchor>]>>)),
19758    ) -> bool {
19759        self.selection_history
19760            .transaction_mut(transaction_id)
19761            .map(modify)
19762            .is_some()
19763    }
19764
19765    pub fn set_mark(&mut self, _: &actions::SetMark, window: &mut Window, cx: &mut Context<Self>) {
19766        if self.selection_mark_mode {
19767            self.change_selections(SelectionEffects::no_scroll(), window, cx, |s| {
19768                s.move_with(&mut |_, sel| {
19769                    sel.collapse_to(sel.head(), SelectionGoal::None);
19770                });
19771            })
19772        }
19773        self.selection_mark_mode = true;
19774        cx.notify();
19775    }
19776
19777    pub fn swap_selection_ends(
19778        &mut self,
19779        _: &actions::SwapSelectionEnds,
19780        window: &mut Window,
19781        cx: &mut Context<Self>,
19782    ) {
19783        self.change_selections(SelectionEffects::no_scroll(), window, cx, |s| {
19784            s.move_with(&mut |_, sel| {
19785                if sel.start != sel.end {
19786                    sel.reversed = !sel.reversed
19787                }
19788            });
19789        });
19790        self.request_autoscroll(Autoscroll::newest(), cx);
19791        cx.notify();
19792    }
19793
19794    pub fn toggle_focus(
19795        workspace: &mut Workspace,
19796        _: &actions::ToggleFocus,
19797        window: &mut Window,
19798        cx: &mut Context<Workspace>,
19799    ) {
19800        let Some(item) = workspace.recent_active_item_by_type::<Self>(cx) else {
19801            return;
19802        };
19803        workspace.activate_item(&item, true, true, window, cx);
19804    }
19805
19806    pub fn toggle_fold(
19807        &mut self,
19808        _: &actions::ToggleFold,
19809        window: &mut Window,
19810        cx: &mut Context<Self>,
19811    ) {
19812        if self.buffer_kind(cx) == ItemBufferKind::Singleton {
19813            let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
19814            let selection = self.selections.newest::<Point>(&display_map);
19815
19816            let range = if selection.is_empty() {
19817                let point = selection.head().to_display_point(&display_map);
19818                let start = DisplayPoint::new(point.row(), 0).to_point(&display_map);
19819                let end = DisplayPoint::new(point.row(), display_map.line_len(point.row()))
19820                    .to_point(&display_map);
19821                start..end
19822            } else {
19823                selection.range()
19824            };
19825            if display_map.folds_in_range(range).next().is_some() {
19826                self.unfold_lines(&Default::default(), window, cx)
19827            } else {
19828                self.fold(&Default::default(), window, cx)
19829            }
19830        } else {
19831            let multi_buffer_snapshot = self.buffer.read(cx).snapshot(cx);
19832            let buffer_ids: HashSet<_> = self
19833                .selections
19834                .disjoint_anchor_ranges()
19835                .flat_map(|range| multi_buffer_snapshot.buffer_ids_for_range(range))
19836                .collect();
19837
19838            let should_unfold = buffer_ids
19839                .iter()
19840                .any(|buffer_id| self.is_buffer_folded(*buffer_id, cx));
19841
19842            for buffer_id in buffer_ids {
19843                if should_unfold {
19844                    self.unfold_buffer(buffer_id, cx);
19845                } else {
19846                    self.fold_buffer(buffer_id, cx);
19847                }
19848            }
19849        }
19850    }
19851
19852    pub fn toggle_fold_recursive(
19853        &mut self,
19854        _: &actions::ToggleFoldRecursive,
19855        window: &mut Window,
19856        cx: &mut Context<Self>,
19857    ) {
19858        let selection = self.selections.newest::<Point>(&self.display_snapshot(cx));
19859
19860        let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
19861        let range = if selection.is_empty() {
19862            let point = selection.head().to_display_point(&display_map);
19863            let start = DisplayPoint::new(point.row(), 0).to_point(&display_map);
19864            let end = DisplayPoint::new(point.row(), display_map.line_len(point.row()))
19865                .to_point(&display_map);
19866            start..end
19867        } else {
19868            selection.range()
19869        };
19870        if display_map.folds_in_range(range).next().is_some() {
19871            self.unfold_recursive(&Default::default(), window, cx)
19872        } else {
19873            self.fold_recursive(&Default::default(), window, cx)
19874        }
19875    }
19876
19877    pub fn fold(&mut self, _: &actions::Fold, window: &mut Window, cx: &mut Context<Self>) {
19878        if self.buffer_kind(cx) == ItemBufferKind::Singleton {
19879            let mut to_fold = Vec::new();
19880            let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
19881            let selections = self.selections.all_adjusted(&display_map);
19882
19883            for selection in selections {
19884                let range = selection.range().sorted();
19885                let buffer_start_row = range.start.row;
19886
19887                if range.start.row != range.end.row {
19888                    let mut found = false;
19889                    let mut row = range.start.row;
19890                    while row <= range.end.row {
19891                        if let Some(crease) = display_map.crease_for_buffer_row(MultiBufferRow(row))
19892                        {
19893                            found = true;
19894                            row = crease.range().end.row + 1;
19895                            to_fold.push(crease);
19896                        } else {
19897                            row += 1
19898                        }
19899                    }
19900                    if found {
19901                        continue;
19902                    }
19903                }
19904
19905                for row in (0..=range.start.row).rev() {
19906                    if let Some(crease) = display_map.crease_for_buffer_row(MultiBufferRow(row))
19907                        && crease.range().end.row >= buffer_start_row
19908                    {
19909                        to_fold.push(crease);
19910                        if row <= range.start.row {
19911                            break;
19912                        }
19913                    }
19914                }
19915            }
19916
19917            self.fold_creases(to_fold, true, window, cx);
19918        } else {
19919            let multi_buffer_snapshot = self.buffer.read(cx).snapshot(cx);
19920            let buffer_ids = self
19921                .selections
19922                .disjoint_anchor_ranges()
19923                .flat_map(|range| multi_buffer_snapshot.buffer_ids_for_range(range))
19924                .collect::<HashSet<_>>();
19925            for buffer_id in buffer_ids {
19926                self.fold_buffer(buffer_id, cx);
19927            }
19928        }
19929    }
19930
19931    pub fn toggle_fold_all(
19932        &mut self,
19933        _: &actions::ToggleFoldAll,
19934        window: &mut Window,
19935        cx: &mut Context<Self>,
19936    ) {
19937        let has_folds = if self.buffer.read(cx).is_singleton() {
19938            let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
19939            let has_folds = display_map
19940                .folds_in_range(MultiBufferOffset(0)..display_map.buffer_snapshot().len())
19941                .next()
19942                .is_some();
19943            has_folds
19944        } else {
19945            let buffer_ids = self.buffer.read(cx).excerpt_buffer_ids();
19946            let has_folds = buffer_ids
19947                .iter()
19948                .any(|buffer_id| self.is_buffer_folded(*buffer_id, cx));
19949            has_folds
19950        };
19951
19952        if has_folds {
19953            self.unfold_all(&actions::UnfoldAll, window, cx);
19954        } else {
19955            self.fold_all(&actions::FoldAll, window, cx);
19956        }
19957    }
19958
19959    fn fold_at_level(
19960        &mut self,
19961        fold_at: &FoldAtLevel,
19962        window: &mut Window,
19963        cx: &mut Context<Self>,
19964    ) {
19965        if !self.buffer.read(cx).is_singleton() {
19966            return;
19967        }
19968
19969        let fold_at_level = fold_at.0;
19970        let snapshot = self.buffer.read(cx).snapshot(cx);
19971        let mut to_fold = Vec::new();
19972        let mut stack = vec![(0, snapshot.max_row().0, 1)];
19973
19974        let row_ranges_to_keep: Vec<Range<u32>> = self
19975            .selections
19976            .all::<Point>(&self.display_snapshot(cx))
19977            .into_iter()
19978            .map(|sel| sel.start.row..sel.end.row)
19979            .collect();
19980
19981        while let Some((mut start_row, end_row, current_level)) = stack.pop() {
19982            while start_row < end_row {
19983                match self
19984                    .snapshot(window, cx)
19985                    .crease_for_buffer_row(MultiBufferRow(start_row))
19986                {
19987                    Some(crease) => {
19988                        let nested_start_row = crease.range().start.row + 1;
19989                        let nested_end_row = crease.range().end.row;
19990
19991                        if current_level < fold_at_level {
19992                            stack.push((nested_start_row, nested_end_row, current_level + 1));
19993                        } else if current_level == fold_at_level {
19994                            // Fold iff there is no selection completely contained within the fold region
19995                            if !row_ranges_to_keep.iter().any(|selection| {
19996                                selection.end >= nested_start_row
19997                                    && selection.start <= nested_end_row
19998                            }) {
19999                                to_fold.push(crease);
20000                            }
20001                        }
20002
20003                        start_row = nested_end_row + 1;
20004                    }
20005                    None => start_row += 1,
20006                }
20007            }
20008        }
20009
20010        self.fold_creases(to_fold, true, window, cx);
20011    }
20012
20013    pub fn fold_at_level_1(
20014        &mut self,
20015        _: &actions::FoldAtLevel1,
20016        window: &mut Window,
20017        cx: &mut Context<Self>,
20018    ) {
20019        self.fold_at_level(&actions::FoldAtLevel(1), window, cx);
20020    }
20021
20022    pub fn fold_at_level_2(
20023        &mut self,
20024        _: &actions::FoldAtLevel2,
20025        window: &mut Window,
20026        cx: &mut Context<Self>,
20027    ) {
20028        self.fold_at_level(&actions::FoldAtLevel(2), window, cx);
20029    }
20030
20031    pub fn fold_at_level_3(
20032        &mut self,
20033        _: &actions::FoldAtLevel3,
20034        window: &mut Window,
20035        cx: &mut Context<Self>,
20036    ) {
20037        self.fold_at_level(&actions::FoldAtLevel(3), window, cx);
20038    }
20039
20040    pub fn fold_at_level_4(
20041        &mut self,
20042        _: &actions::FoldAtLevel4,
20043        window: &mut Window,
20044        cx: &mut Context<Self>,
20045    ) {
20046        self.fold_at_level(&actions::FoldAtLevel(4), window, cx);
20047    }
20048
20049    pub fn fold_at_level_5(
20050        &mut self,
20051        _: &actions::FoldAtLevel5,
20052        window: &mut Window,
20053        cx: &mut Context<Self>,
20054    ) {
20055        self.fold_at_level(&actions::FoldAtLevel(5), window, cx);
20056    }
20057
20058    pub fn fold_at_level_6(
20059        &mut self,
20060        _: &actions::FoldAtLevel6,
20061        window: &mut Window,
20062        cx: &mut Context<Self>,
20063    ) {
20064        self.fold_at_level(&actions::FoldAtLevel(6), window, cx);
20065    }
20066
20067    pub fn fold_at_level_7(
20068        &mut self,
20069        _: &actions::FoldAtLevel7,
20070        window: &mut Window,
20071        cx: &mut Context<Self>,
20072    ) {
20073        self.fold_at_level(&actions::FoldAtLevel(7), window, cx);
20074    }
20075
20076    pub fn fold_at_level_8(
20077        &mut self,
20078        _: &actions::FoldAtLevel8,
20079        window: &mut Window,
20080        cx: &mut Context<Self>,
20081    ) {
20082        self.fold_at_level(&actions::FoldAtLevel(8), window, cx);
20083    }
20084
20085    pub fn fold_at_level_9(
20086        &mut self,
20087        _: &actions::FoldAtLevel9,
20088        window: &mut Window,
20089        cx: &mut Context<Self>,
20090    ) {
20091        self.fold_at_level(&actions::FoldAtLevel(9), window, cx);
20092    }
20093
20094    pub fn fold_all(&mut self, _: &actions::FoldAll, window: &mut Window, cx: &mut Context<Self>) {
20095        if self.buffer.read(cx).is_singleton() {
20096            let mut fold_ranges = Vec::new();
20097            let snapshot = self.buffer.read(cx).snapshot(cx);
20098
20099            for row in 0..snapshot.max_row().0 {
20100                if let Some(foldable_range) = self
20101                    .snapshot(window, cx)
20102                    .crease_for_buffer_row(MultiBufferRow(row))
20103                {
20104                    fold_ranges.push(foldable_range);
20105                }
20106            }
20107
20108            self.fold_creases(fold_ranges, true, window, cx);
20109        } else {
20110            self.toggle_fold_multiple_buffers = cx.spawn_in(window, async move |editor, cx| {
20111                editor
20112                    .update_in(cx, |editor, _, cx| {
20113                        for buffer_id in editor.buffer.read(cx).excerpt_buffer_ids() {
20114                            editor.fold_buffer(buffer_id, cx);
20115                        }
20116                    })
20117                    .ok();
20118            });
20119        }
20120    }
20121
20122    pub fn fold_function_bodies(
20123        &mut self,
20124        _: &actions::FoldFunctionBodies,
20125        window: &mut Window,
20126        cx: &mut Context<Self>,
20127    ) {
20128        let snapshot = self.buffer.read(cx).snapshot(cx);
20129
20130        let ranges = snapshot
20131            .text_object_ranges(
20132                MultiBufferOffset(0)..snapshot.len(),
20133                TreeSitterOptions::default(),
20134            )
20135            .filter_map(|(range, obj)| (obj == TextObject::InsideFunction).then_some(range))
20136            .collect::<Vec<_>>();
20137
20138        let creases = ranges
20139            .into_iter()
20140            .map(|range| Crease::simple(range, self.display_map.read(cx).fold_placeholder.clone()))
20141            .collect();
20142
20143        self.fold_creases(creases, true, window, cx);
20144    }
20145
20146    pub fn fold_recursive(
20147        &mut self,
20148        _: &actions::FoldRecursive,
20149        window: &mut Window,
20150        cx: &mut Context<Self>,
20151    ) {
20152        let mut to_fold = Vec::new();
20153        let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
20154        let selections = self.selections.all_adjusted(&display_map);
20155
20156        for selection in selections {
20157            let range = selection.range().sorted();
20158            let buffer_start_row = range.start.row;
20159
20160            if range.start.row != range.end.row {
20161                let mut found = false;
20162                for row in range.start.row..=range.end.row {
20163                    if let Some(crease) = display_map.crease_for_buffer_row(MultiBufferRow(row)) {
20164                        found = true;
20165                        to_fold.push(crease);
20166                    }
20167                }
20168                if found {
20169                    continue;
20170                }
20171            }
20172
20173            for row in (0..=range.start.row).rev() {
20174                if let Some(crease) = display_map.crease_for_buffer_row(MultiBufferRow(row)) {
20175                    if crease.range().end.row >= buffer_start_row {
20176                        to_fold.push(crease);
20177                    } else {
20178                        break;
20179                    }
20180                }
20181            }
20182        }
20183
20184        self.fold_creases(to_fold, true, window, cx);
20185    }
20186
20187    pub fn fold_at(
20188        &mut self,
20189        buffer_row: MultiBufferRow,
20190        window: &mut Window,
20191        cx: &mut Context<Self>,
20192    ) {
20193        let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
20194
20195        if let Some(crease) = display_map.crease_for_buffer_row(buffer_row) {
20196            let autoscroll = self
20197                .selections
20198                .all::<Point>(&display_map)
20199                .iter()
20200                .any(|selection| crease.range().overlaps(&selection.range()));
20201
20202            self.fold_creases(vec![crease], autoscroll, window, cx);
20203        }
20204    }
20205
20206    pub fn unfold_lines(&mut self, _: &UnfoldLines, _window: &mut Window, cx: &mut Context<Self>) {
20207        if self.buffer_kind(cx) == ItemBufferKind::Singleton {
20208            let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
20209            let buffer = display_map.buffer_snapshot();
20210            let selections = self.selections.all::<Point>(&display_map);
20211            let ranges = selections
20212                .iter()
20213                .map(|s| {
20214                    let range = s.display_range(&display_map).sorted();
20215                    let mut start = range.start.to_point(&display_map);
20216                    let mut end = range.end.to_point(&display_map);
20217                    start.column = 0;
20218                    end.column = buffer.line_len(MultiBufferRow(end.row));
20219                    start..end
20220                })
20221                .collect::<Vec<_>>();
20222
20223            self.unfold_ranges(&ranges, true, true, cx);
20224        } else {
20225            let multi_buffer_snapshot = self.buffer.read(cx).snapshot(cx);
20226            let buffer_ids = self
20227                .selections
20228                .disjoint_anchor_ranges()
20229                .flat_map(|range| multi_buffer_snapshot.buffer_ids_for_range(range))
20230                .collect::<HashSet<_>>();
20231            for buffer_id in buffer_ids {
20232                self.unfold_buffer(buffer_id, cx);
20233            }
20234        }
20235    }
20236
20237    pub fn unfold_recursive(
20238        &mut self,
20239        _: &UnfoldRecursive,
20240        _window: &mut Window,
20241        cx: &mut Context<Self>,
20242    ) {
20243        let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
20244        let selections = self.selections.all::<Point>(&display_map);
20245        let ranges = selections
20246            .iter()
20247            .map(|s| {
20248                let mut range = s.display_range(&display_map).sorted();
20249                *range.start.column_mut() = 0;
20250                *range.end.column_mut() = display_map.line_len(range.end.row());
20251                let start = range.start.to_point(&display_map);
20252                let end = range.end.to_point(&display_map);
20253                start..end
20254            })
20255            .collect::<Vec<_>>();
20256
20257        self.unfold_ranges(&ranges, true, true, cx);
20258    }
20259
20260    pub fn unfold_at(
20261        &mut self,
20262        buffer_row: MultiBufferRow,
20263        _window: &mut Window,
20264        cx: &mut Context<Self>,
20265    ) {
20266        let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
20267
20268        let intersection_range = Point::new(buffer_row.0, 0)
20269            ..Point::new(
20270                buffer_row.0,
20271                display_map.buffer_snapshot().line_len(buffer_row),
20272            );
20273
20274        let autoscroll = self
20275            .selections
20276            .all::<Point>(&display_map)
20277            .iter()
20278            .any(|selection| RangeExt::overlaps(&selection.range(), &intersection_range));
20279
20280        self.unfold_ranges(&[intersection_range], true, autoscroll, cx);
20281    }
20282
20283    pub fn unfold_all(
20284        &mut self,
20285        _: &actions::UnfoldAll,
20286        _window: &mut Window,
20287        cx: &mut Context<Self>,
20288    ) {
20289        if self.buffer.read(cx).is_singleton() {
20290            let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
20291            self.unfold_ranges(
20292                &[MultiBufferOffset(0)..display_map.buffer_snapshot().len()],
20293                true,
20294                true,
20295                cx,
20296            );
20297        } else {
20298            self.toggle_fold_multiple_buffers = cx.spawn(async move |editor, cx| {
20299                editor
20300                    .update(cx, |editor, cx| {
20301                        for buffer_id in editor.buffer.read(cx).excerpt_buffer_ids() {
20302                            editor.unfold_buffer(buffer_id, cx);
20303                        }
20304                    })
20305                    .ok();
20306            });
20307        }
20308    }
20309
20310    pub fn fold_selected_ranges(
20311        &mut self,
20312        _: &FoldSelectedRanges,
20313        window: &mut Window,
20314        cx: &mut Context<Self>,
20315    ) {
20316        let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
20317        let selections = self.selections.all_adjusted(&display_map);
20318        let ranges = selections
20319            .into_iter()
20320            .map(|s| Crease::simple(s.range(), display_map.fold_placeholder.clone()))
20321            .collect::<Vec<_>>();
20322        self.fold_creases(ranges, true, window, cx);
20323    }
20324
20325    pub fn fold_ranges<T: ToOffset + Clone>(
20326        &mut self,
20327        ranges: Vec<Range<T>>,
20328        auto_scroll: bool,
20329        window: &mut Window,
20330        cx: &mut Context<Self>,
20331    ) {
20332        let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
20333        let ranges = ranges
20334            .into_iter()
20335            .map(|r| Crease::simple(r, display_map.fold_placeholder.clone()))
20336            .collect::<Vec<_>>();
20337        self.fold_creases(ranges, auto_scroll, window, cx);
20338    }
20339
20340    pub fn fold_creases<T: ToOffset + Clone>(
20341        &mut self,
20342        creases: Vec<Crease<T>>,
20343        auto_scroll: bool,
20344        _window: &mut Window,
20345        cx: &mut Context<Self>,
20346    ) {
20347        if creases.is_empty() {
20348            return;
20349        }
20350
20351        self.display_map.update(cx, |map, cx| map.fold(creases, cx));
20352
20353        if auto_scroll {
20354            self.request_autoscroll(Autoscroll::fit(), cx);
20355        }
20356
20357        cx.notify();
20358
20359        self.scrollbar_marker_state.dirty = true;
20360        self.folds_did_change(cx);
20361    }
20362
20363    /// Removes any folds whose ranges intersect any of the given ranges.
20364    pub fn unfold_ranges<T: ToOffset + Clone>(
20365        &mut self,
20366        ranges: &[Range<T>],
20367        inclusive: bool,
20368        auto_scroll: bool,
20369        cx: &mut Context<Self>,
20370    ) {
20371        self.remove_folds_with(ranges, auto_scroll, cx, |map, cx| {
20372            map.unfold_intersecting(ranges.iter().cloned(), inclusive, cx);
20373        });
20374        self.folds_did_change(cx);
20375    }
20376
20377    pub fn fold_buffer(&mut self, buffer_id: BufferId, cx: &mut Context<Self>) {
20378        self.fold_buffers([buffer_id], cx);
20379    }
20380
20381    pub fn fold_buffers(
20382        &mut self,
20383        buffer_ids: impl IntoIterator<Item = BufferId>,
20384        cx: &mut Context<Self>,
20385    ) {
20386        if self.buffer().read(cx).is_singleton() {
20387            return;
20388        }
20389
20390        let ids_to_fold: Vec<BufferId> = buffer_ids
20391            .into_iter()
20392            .filter(|id| !self.is_buffer_folded(*id, cx))
20393            .collect();
20394
20395        if ids_to_fold.is_empty() {
20396            return;
20397        }
20398
20399        let mut all_folded_excerpt_ids = Vec::new();
20400        for buffer_id in &ids_to_fold {
20401            let folded_excerpts = self.buffer().read(cx).excerpts_for_buffer(*buffer_id, cx);
20402            all_folded_excerpt_ids.extend(folded_excerpts.into_iter().map(|(id, _)| id));
20403        }
20404
20405        self.display_map.update(cx, |display_map, cx| {
20406            display_map.fold_buffers(ids_to_fold.clone(), cx)
20407        });
20408
20409        let snapshot = self.display_snapshot(cx);
20410        self.selections.change_with(&snapshot, |selections| {
20411            for buffer_id in ids_to_fold {
20412                selections.remove_selections_from_buffer(buffer_id);
20413            }
20414        });
20415
20416        cx.emit(EditorEvent::BufferFoldToggled {
20417            ids: all_folded_excerpt_ids,
20418            folded: true,
20419        });
20420        cx.notify();
20421    }
20422
20423    pub fn unfold_buffer(&mut self, buffer_id: BufferId, cx: &mut Context<Self>) {
20424        if self.buffer().read(cx).is_singleton() || !self.is_buffer_folded(buffer_id, cx) {
20425            return;
20426        }
20427        let unfolded_excerpts = self.buffer().read(cx).excerpts_for_buffer(buffer_id, cx);
20428        self.display_map.update(cx, |display_map, cx| {
20429            display_map.unfold_buffers([buffer_id], cx);
20430        });
20431        cx.emit(EditorEvent::BufferFoldToggled {
20432            ids: unfolded_excerpts.iter().map(|&(id, _)| id).collect(),
20433            folded: false,
20434        });
20435        cx.notify();
20436    }
20437
20438    pub fn is_buffer_folded(&self, buffer: BufferId, cx: &App) -> bool {
20439        self.display_map.read(cx).is_buffer_folded(buffer)
20440    }
20441
20442    pub fn has_any_buffer_folded(&self, cx: &App) -> bool {
20443        if self.buffer().read(cx).is_singleton() {
20444            return false;
20445        }
20446        !self.folded_buffers(cx).is_empty()
20447    }
20448
20449    pub fn folded_buffers<'a>(&self, cx: &'a App) -> &'a HashSet<BufferId> {
20450        self.display_map.read(cx).folded_buffers()
20451    }
20452
20453    pub fn disable_header_for_buffer(&mut self, buffer_id: BufferId, cx: &mut Context<Self>) {
20454        self.display_map.update(cx, |display_map, cx| {
20455            display_map.disable_header_for_buffer(buffer_id, cx);
20456        });
20457        cx.notify();
20458    }
20459
20460    /// Removes any folds with the given ranges.
20461    pub fn remove_folds_with_type<T: ToOffset + Clone>(
20462        &mut self,
20463        ranges: &[Range<T>],
20464        type_id: TypeId,
20465        auto_scroll: bool,
20466        cx: &mut Context<Self>,
20467    ) {
20468        self.remove_folds_with(ranges, auto_scroll, cx, |map, cx| {
20469            map.remove_folds_with_type(ranges.iter().cloned(), type_id, cx)
20470        });
20471        self.folds_did_change(cx);
20472    }
20473
20474    fn remove_folds_with<T: ToOffset + Clone>(
20475        &mut self,
20476        ranges: &[Range<T>],
20477        auto_scroll: bool,
20478        cx: &mut Context<Self>,
20479        update: impl FnOnce(&mut DisplayMap, &mut Context<DisplayMap>),
20480    ) {
20481        if ranges.is_empty() {
20482            return;
20483        }
20484
20485        let mut buffers_affected = HashSet::default();
20486        let multi_buffer = self.buffer().read(cx);
20487        for range in ranges {
20488            if let Some((_, buffer, _)) = multi_buffer.excerpt_containing(range.start.clone(), cx) {
20489                buffers_affected.insert(buffer.read(cx).remote_id());
20490            };
20491        }
20492
20493        self.display_map.update(cx, update);
20494
20495        if auto_scroll {
20496            self.request_autoscroll(Autoscroll::fit(), cx);
20497        }
20498
20499        cx.notify();
20500        self.scrollbar_marker_state.dirty = true;
20501        self.active_indent_guides_state.dirty = true;
20502    }
20503
20504    pub fn update_renderer_widths(
20505        &mut self,
20506        widths: impl IntoIterator<Item = (ChunkRendererId, Pixels)>,
20507        cx: &mut Context<Self>,
20508    ) -> bool {
20509        self.display_map
20510            .update(cx, |map, cx| map.update_fold_widths(widths, cx))
20511    }
20512
20513    pub fn default_fold_placeholder(&self, cx: &App) -> FoldPlaceholder {
20514        self.display_map.read(cx).fold_placeholder.clone()
20515    }
20516
20517    pub fn set_expand_all_diff_hunks(&mut self, cx: &mut App) {
20518        self.buffer.update(cx, |buffer, cx| {
20519            buffer.set_all_diff_hunks_expanded(cx);
20520        });
20521    }
20522
20523    pub fn expand_all_diff_hunks(
20524        &mut self,
20525        _: &ExpandAllDiffHunks,
20526        _window: &mut Window,
20527        cx: &mut Context<Self>,
20528    ) {
20529        self.buffer.update(cx, |buffer, cx| {
20530            buffer.expand_diff_hunks(vec![Anchor::min()..Anchor::max()], cx)
20531        });
20532    }
20533
20534    pub fn collapse_all_diff_hunks(
20535        &mut self,
20536        _: &CollapseAllDiffHunks,
20537        _window: &mut Window,
20538        cx: &mut Context<Self>,
20539    ) {
20540        self.buffer.update(cx, |buffer, cx| {
20541            buffer.collapse_diff_hunks(vec![Anchor::min()..Anchor::max()], cx)
20542        });
20543    }
20544
20545    pub fn toggle_selected_diff_hunks(
20546        &mut self,
20547        _: &ToggleSelectedDiffHunks,
20548        _window: &mut Window,
20549        cx: &mut Context<Self>,
20550    ) {
20551        let ranges: Vec<_> = self
20552            .selections
20553            .disjoint_anchors()
20554            .iter()
20555            .map(|s| s.range())
20556            .collect();
20557        self.toggle_diff_hunks_in_ranges(ranges, cx);
20558    }
20559
20560    pub fn diff_hunks_in_ranges<'a>(
20561        &'a self,
20562        ranges: &'a [Range<Anchor>],
20563        buffer: &'a MultiBufferSnapshot,
20564    ) -> impl 'a + Iterator<Item = MultiBufferDiffHunk> {
20565        ranges.iter().flat_map(move |range| {
20566            let end_excerpt_id = range.end.excerpt_id;
20567            let range = range.to_point(buffer);
20568            let mut peek_end = range.end;
20569            if range.end.row < buffer.max_row().0 {
20570                peek_end = Point::new(range.end.row + 1, 0);
20571            }
20572            buffer
20573                .diff_hunks_in_range(range.start..peek_end)
20574                .filter(move |hunk| hunk.excerpt_id.cmp(&end_excerpt_id, buffer).is_le())
20575        })
20576    }
20577
20578    pub fn has_stageable_diff_hunks_in_ranges(
20579        &self,
20580        ranges: &[Range<Anchor>],
20581        snapshot: &MultiBufferSnapshot,
20582    ) -> bool {
20583        let mut hunks = self.diff_hunks_in_ranges(ranges, snapshot);
20584        hunks.any(|hunk| hunk.status().has_secondary_hunk())
20585    }
20586
20587    pub fn toggle_staged_selected_diff_hunks(
20588        &mut self,
20589        _: &::git::ToggleStaged,
20590        _: &mut Window,
20591        cx: &mut Context<Self>,
20592    ) {
20593        let snapshot = self.buffer.read(cx).snapshot(cx);
20594        let ranges: Vec<_> = self
20595            .selections
20596            .disjoint_anchors()
20597            .iter()
20598            .map(|s| s.range())
20599            .collect();
20600        let stage = self.has_stageable_diff_hunks_in_ranges(&ranges, &snapshot);
20601        self.stage_or_unstage_diff_hunks(stage, ranges, cx);
20602    }
20603
20604    pub fn set_render_diff_hunk_controls(
20605        &mut self,
20606        render_diff_hunk_controls: RenderDiffHunkControlsFn,
20607        cx: &mut Context<Self>,
20608    ) {
20609        self.render_diff_hunk_controls = render_diff_hunk_controls;
20610        cx.notify();
20611    }
20612
20613    pub fn stage_and_next(
20614        &mut self,
20615        _: &::git::StageAndNext,
20616        window: &mut Window,
20617        cx: &mut Context<Self>,
20618    ) {
20619        self.do_stage_or_unstage_and_next(true, window, cx);
20620    }
20621
20622    pub fn unstage_and_next(
20623        &mut self,
20624        _: &::git::UnstageAndNext,
20625        window: &mut Window,
20626        cx: &mut Context<Self>,
20627    ) {
20628        self.do_stage_or_unstage_and_next(false, window, cx);
20629    }
20630
20631    pub fn stage_or_unstage_diff_hunks(
20632        &mut self,
20633        stage: bool,
20634        ranges: Vec<Range<Anchor>>,
20635        cx: &mut Context<Self>,
20636    ) {
20637        if self.delegate_stage_and_restore {
20638            let snapshot = self.buffer.read(cx).snapshot(cx);
20639            let hunks: Vec<_> = self.diff_hunks_in_ranges(&ranges, &snapshot).collect();
20640            if !hunks.is_empty() {
20641                cx.emit(EditorEvent::StageOrUnstageRequested { stage, hunks });
20642            }
20643            return;
20644        }
20645        let task = self.save_buffers_for_ranges_if_needed(&ranges, cx);
20646        cx.spawn(async move |this, cx| {
20647            task.await?;
20648            this.update(cx, |this, cx| {
20649                let snapshot = this.buffer.read(cx).snapshot(cx);
20650                let chunk_by = this
20651                    .diff_hunks_in_ranges(&ranges, &snapshot)
20652                    .chunk_by(|hunk| hunk.buffer_id);
20653                for (buffer_id, hunks) in &chunk_by {
20654                    this.do_stage_or_unstage(stage, buffer_id, hunks, cx);
20655                }
20656            })
20657        })
20658        .detach_and_log_err(cx);
20659    }
20660
20661    fn save_buffers_for_ranges_if_needed(
20662        &mut self,
20663        ranges: &[Range<Anchor>],
20664        cx: &mut Context<Editor>,
20665    ) -> Task<Result<()>> {
20666        let multibuffer = self.buffer.read(cx);
20667        let snapshot = multibuffer.read(cx);
20668        let buffer_ids: HashSet<_> = ranges
20669            .iter()
20670            .flat_map(|range| snapshot.buffer_ids_for_range(range.clone()))
20671            .collect();
20672        drop(snapshot);
20673
20674        let mut buffers = HashSet::default();
20675        for buffer_id in buffer_ids {
20676            if let Some(buffer_entity) = multibuffer.buffer(buffer_id) {
20677                let buffer = buffer_entity.read(cx);
20678                if buffer.file().is_some_and(|file| file.disk_state().exists()) && buffer.is_dirty()
20679                {
20680                    buffers.insert(buffer_entity);
20681                }
20682            }
20683        }
20684
20685        if let Some(project) = &self.project {
20686            project.update(cx, |project, cx| project.save_buffers(buffers, cx))
20687        } else {
20688            Task::ready(Ok(()))
20689        }
20690    }
20691
20692    fn do_stage_or_unstage_and_next(
20693        &mut self,
20694        stage: bool,
20695        window: &mut Window,
20696        cx: &mut Context<Self>,
20697    ) {
20698        let ranges = self.selections.disjoint_anchor_ranges().collect::<Vec<_>>();
20699
20700        if ranges.iter().any(|range| range.start != range.end) {
20701            self.stage_or_unstage_diff_hunks(stage, ranges, cx);
20702            return;
20703        }
20704
20705        self.stage_or_unstage_diff_hunks(stage, ranges, cx);
20706        let snapshot = self.snapshot(window, cx);
20707        let position = self
20708            .selections
20709            .newest::<Point>(&snapshot.display_snapshot)
20710            .head();
20711        let mut row = snapshot
20712            .buffer_snapshot()
20713            .diff_hunks_in_range(position..snapshot.buffer_snapshot().max_point())
20714            .find(|hunk| hunk.row_range.start.0 > position.row)
20715            .map(|hunk| hunk.row_range.start);
20716
20717        let all_diff_hunks_expanded = self.buffer().read(cx).all_diff_hunks_expanded();
20718        // Outside of the project diff editor, wrap around to the beginning.
20719        if !all_diff_hunks_expanded {
20720            row = row.or_else(|| {
20721                snapshot
20722                    .buffer_snapshot()
20723                    .diff_hunks_in_range(Point::zero()..position)
20724                    .find(|hunk| hunk.row_range.end.0 < position.row)
20725                    .map(|hunk| hunk.row_range.start)
20726            });
20727        }
20728
20729        if let Some(row) = row {
20730            let destination = Point::new(row.0, 0);
20731            let autoscroll = Autoscroll::center();
20732
20733            self.unfold_ranges(&[destination..destination], false, false, cx);
20734            self.change_selections(SelectionEffects::scroll(autoscroll), window, cx, |s| {
20735                s.select_ranges([destination..destination]);
20736            });
20737        }
20738    }
20739
20740    pub(crate) fn do_stage_or_unstage(
20741        &self,
20742        stage: bool,
20743        buffer_id: BufferId,
20744        hunks: impl Iterator<Item = MultiBufferDiffHunk>,
20745        cx: &mut App,
20746    ) -> Option<()> {
20747        let project = self.project()?;
20748        let buffer = project.read(cx).buffer_for_id(buffer_id, cx)?;
20749        let diff = self.buffer.read(cx).diff_for(buffer_id)?;
20750        let buffer_snapshot = buffer.read(cx).snapshot();
20751        let file_exists = buffer_snapshot
20752            .file()
20753            .is_some_and(|file| file.disk_state().exists());
20754        diff.update(cx, |diff, cx| {
20755            diff.stage_or_unstage_hunks(
20756                stage,
20757                &hunks
20758                    .map(|hunk| buffer_diff::DiffHunk {
20759                        buffer_range: hunk.buffer_range,
20760                        // We don't need to pass in word diffs here because they're only used for rendering and
20761                        // this function changes internal state
20762                        base_word_diffs: Vec::default(),
20763                        buffer_word_diffs: Vec::default(),
20764                        diff_base_byte_range: hunk.diff_base_byte_range.start.0
20765                            ..hunk.diff_base_byte_range.end.0,
20766                        secondary_status: hunk.status.secondary,
20767                        range: Point::zero()..Point::zero(), // unused
20768                    })
20769                    .collect::<Vec<_>>(),
20770                &buffer_snapshot,
20771                file_exists,
20772                cx,
20773            )
20774        });
20775        None
20776    }
20777
20778    pub fn expand_selected_diff_hunks(&mut self, cx: &mut Context<Self>) {
20779        let ranges: Vec<_> = self
20780            .selections
20781            .disjoint_anchors()
20782            .iter()
20783            .map(|s| s.range())
20784            .collect();
20785        self.buffer
20786            .update(cx, |buffer, cx| buffer.expand_diff_hunks(ranges, cx))
20787    }
20788
20789    pub fn clear_expanded_diff_hunks(&mut self, cx: &mut Context<Self>) -> bool {
20790        self.buffer.update(cx, |buffer, cx| {
20791            let ranges = vec![Anchor::min()..Anchor::max()];
20792            if !buffer.all_diff_hunks_expanded()
20793                && buffer.has_expanded_diff_hunks_in_ranges(&ranges, cx)
20794            {
20795                buffer.collapse_diff_hunks(ranges, cx);
20796                true
20797            } else {
20798                false
20799            }
20800        })
20801    }
20802
20803    fn has_any_expanded_diff_hunks(&self, cx: &App) -> bool {
20804        if self.buffer.read(cx).all_diff_hunks_expanded() {
20805            return true;
20806        }
20807        let ranges = vec![Anchor::min()..Anchor::max()];
20808        self.buffer
20809            .read(cx)
20810            .has_expanded_diff_hunks_in_ranges(&ranges, cx)
20811    }
20812
20813    fn toggle_diff_hunks_in_ranges(
20814        &mut self,
20815        ranges: Vec<Range<Anchor>>,
20816        cx: &mut Context<Editor>,
20817    ) {
20818        self.buffer.update(cx, |buffer, cx| {
20819            let expand = !buffer.has_expanded_diff_hunks_in_ranges(&ranges, cx);
20820            buffer.expand_or_collapse_diff_hunks(ranges, expand, cx);
20821        })
20822    }
20823
20824    fn toggle_single_diff_hunk(&mut self, range: Range<Anchor>, cx: &mut Context<Self>) {
20825        self.buffer.update(cx, |buffer, cx| {
20826            buffer.toggle_single_diff_hunk(range, cx);
20827        })
20828    }
20829
20830    pub(crate) fn apply_all_diff_hunks(
20831        &mut self,
20832        _: &ApplyAllDiffHunks,
20833        window: &mut Window,
20834        cx: &mut Context<Self>,
20835    ) {
20836        self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
20837
20838        let buffers = self.buffer.read(cx).all_buffers();
20839        for branch_buffer in buffers {
20840            branch_buffer.update(cx, |branch_buffer, cx| {
20841                branch_buffer.merge_into_base(Vec::new(), cx);
20842            });
20843        }
20844
20845        if let Some(project) = self.project.clone() {
20846            self.save(
20847                SaveOptions {
20848                    format: true,
20849                    autosave: false,
20850                },
20851                project,
20852                window,
20853                cx,
20854            )
20855            .detach_and_log_err(cx);
20856        }
20857    }
20858
20859    pub(crate) fn apply_selected_diff_hunks(
20860        &mut self,
20861        _: &ApplyDiffHunk,
20862        window: &mut Window,
20863        cx: &mut Context<Self>,
20864    ) {
20865        self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
20866        let snapshot = self.snapshot(window, cx);
20867        let hunks = snapshot.hunks_for_ranges(
20868            self.selections
20869                .all(&snapshot.display_snapshot)
20870                .into_iter()
20871                .map(|selection| selection.range()),
20872        );
20873        let mut ranges_by_buffer = HashMap::default();
20874        self.transact(window, cx, |editor, _window, cx| {
20875            for hunk in hunks {
20876                if let Some(buffer) = editor.buffer.read(cx).buffer(hunk.buffer_id) {
20877                    ranges_by_buffer
20878                        .entry(buffer.clone())
20879                        .or_insert_with(Vec::new)
20880                        .push(hunk.buffer_range.to_offset(buffer.read(cx)));
20881                }
20882            }
20883
20884            for (buffer, ranges) in ranges_by_buffer {
20885                buffer.update(cx, |buffer, cx| {
20886                    buffer.merge_into_base(ranges, cx);
20887                });
20888            }
20889        });
20890
20891        if let Some(project) = self.project.clone() {
20892            self.save(
20893                SaveOptions {
20894                    format: true,
20895                    autosave: false,
20896                },
20897                project,
20898                window,
20899                cx,
20900            )
20901            .detach_and_log_err(cx);
20902        }
20903    }
20904
20905    pub fn set_gutter_hovered(&mut self, hovered: bool, cx: &mut Context<Self>) {
20906        if hovered != self.gutter_hovered {
20907            self.gutter_hovered = hovered;
20908            cx.notify();
20909        }
20910    }
20911
20912    pub fn insert_blocks(
20913        &mut self,
20914        blocks: impl IntoIterator<Item = BlockProperties<Anchor>>,
20915        autoscroll: Option<Autoscroll>,
20916        cx: &mut Context<Self>,
20917    ) -> Vec<CustomBlockId> {
20918        let blocks = self
20919            .display_map
20920            .update(cx, |display_map, cx| display_map.insert_blocks(blocks, cx));
20921        if let Some(autoscroll) = autoscroll {
20922            self.request_autoscroll(autoscroll, cx);
20923        }
20924        cx.notify();
20925        blocks
20926    }
20927
20928    pub fn resize_blocks(
20929        &mut self,
20930        heights: HashMap<CustomBlockId, u32>,
20931        autoscroll: Option<Autoscroll>,
20932        cx: &mut Context<Self>,
20933    ) {
20934        self.display_map
20935            .update(cx, |display_map, cx| display_map.resize_blocks(heights, cx));
20936        if let Some(autoscroll) = autoscroll {
20937            self.request_autoscroll(autoscroll, cx);
20938        }
20939        cx.notify();
20940    }
20941
20942    pub fn replace_blocks(
20943        &mut self,
20944        renderers: HashMap<CustomBlockId, RenderBlock>,
20945        autoscroll: Option<Autoscroll>,
20946        cx: &mut Context<Self>,
20947    ) {
20948        self.display_map
20949            .update(cx, |display_map, _cx| display_map.replace_blocks(renderers));
20950        if let Some(autoscroll) = autoscroll {
20951            self.request_autoscroll(autoscroll, cx);
20952        }
20953        cx.notify();
20954    }
20955
20956    pub fn remove_blocks(
20957        &mut self,
20958        block_ids: HashSet<CustomBlockId>,
20959        autoscroll: Option<Autoscroll>,
20960        cx: &mut Context<Self>,
20961    ) {
20962        self.display_map.update(cx, |display_map, cx| {
20963            display_map.remove_blocks(block_ids, cx)
20964        });
20965        if let Some(autoscroll) = autoscroll {
20966            self.request_autoscroll(autoscroll, cx);
20967        }
20968        cx.notify();
20969    }
20970
20971    pub fn row_for_block(
20972        &self,
20973        block_id: CustomBlockId,
20974        cx: &mut Context<Self>,
20975    ) -> Option<DisplayRow> {
20976        self.display_map
20977            .update(cx, |map, cx| map.row_for_block(block_id, cx))
20978    }
20979
20980    pub(crate) fn set_focused_block(&mut self, focused_block: FocusedBlock) {
20981        self.focused_block = Some(focused_block);
20982    }
20983
20984    pub(crate) fn take_focused_block(&mut self) -> Option<FocusedBlock> {
20985        self.focused_block.take()
20986    }
20987
20988    pub fn insert_creases(
20989        &mut self,
20990        creases: impl IntoIterator<Item = Crease<Anchor>>,
20991        cx: &mut Context<Self>,
20992    ) -> Vec<CreaseId> {
20993        self.display_map
20994            .update(cx, |map, cx| map.insert_creases(creases, cx))
20995    }
20996
20997    pub fn remove_creases(
20998        &mut self,
20999        ids: impl IntoIterator<Item = CreaseId>,
21000        cx: &mut Context<Self>,
21001    ) -> Vec<(CreaseId, Range<Anchor>)> {
21002        self.display_map
21003            .update(cx, |map, cx| map.remove_creases(ids, cx))
21004    }
21005
21006    pub fn longest_row(&self, cx: &mut App) -> DisplayRow {
21007        self.display_map
21008            .update(cx, |map, cx| map.snapshot(cx))
21009            .longest_row()
21010    }
21011
21012    pub fn max_point(&self, cx: &mut App) -> DisplayPoint {
21013        self.display_map
21014            .update(cx, |map, cx| map.snapshot(cx))
21015            .max_point()
21016    }
21017
21018    pub fn text(&self, cx: &App) -> String {
21019        self.buffer.read(cx).read(cx).text()
21020    }
21021
21022    pub fn is_empty(&self, cx: &App) -> bool {
21023        self.buffer.read(cx).read(cx).is_empty()
21024    }
21025
21026    pub fn text_option(&self, cx: &App) -> Option<String> {
21027        let text = self.text(cx);
21028        let text = text.trim();
21029
21030        if text.is_empty() {
21031            return None;
21032        }
21033
21034        Some(text.to_string())
21035    }
21036
21037    pub fn set_text(
21038        &mut self,
21039        text: impl Into<Arc<str>>,
21040        window: &mut Window,
21041        cx: &mut Context<Self>,
21042    ) {
21043        self.transact(window, cx, |this, _, cx| {
21044            this.buffer
21045                .read(cx)
21046                .as_singleton()
21047                .expect("you can only call set_text on editors for singleton buffers")
21048                .update(cx, |buffer, cx| buffer.set_text(text, cx));
21049        });
21050    }
21051
21052    pub fn display_text(&self, cx: &mut App) -> String {
21053        self.display_map
21054            .update(cx, |map, cx| map.snapshot(cx))
21055            .text()
21056    }
21057
21058    fn create_minimap(
21059        &self,
21060        minimap_settings: MinimapSettings,
21061        window: &mut Window,
21062        cx: &mut Context<Self>,
21063    ) -> Option<Entity<Self>> {
21064        (minimap_settings.minimap_enabled() && self.buffer_kind(cx) == ItemBufferKind::Singleton)
21065            .then(|| self.initialize_new_minimap(minimap_settings, window, cx))
21066    }
21067
21068    fn initialize_new_minimap(
21069        &self,
21070        minimap_settings: MinimapSettings,
21071        window: &mut Window,
21072        cx: &mut Context<Self>,
21073    ) -> Entity<Self> {
21074        const MINIMAP_FONT_WEIGHT: gpui::FontWeight = gpui::FontWeight::BLACK;
21075        const MINIMAP_FONT_FAMILY: SharedString = SharedString::new_static(".ZedMono");
21076
21077        let mut minimap = Editor::new_internal(
21078            EditorMode::Minimap {
21079                parent: cx.weak_entity(),
21080            },
21081            self.buffer.clone(),
21082            None,
21083            Some(self.display_map.clone()),
21084            window,
21085            cx,
21086        );
21087        let my_snapshot = self.display_map.update(cx, |map, cx| map.snapshot(cx));
21088        let minimap_snapshot = minimap.display_map.update(cx, |map, cx| map.snapshot(cx));
21089        minimap.scroll_manager.clone_state(
21090            &self.scroll_manager,
21091            &my_snapshot,
21092            &minimap_snapshot,
21093            cx,
21094        );
21095        minimap.set_text_style_refinement(TextStyleRefinement {
21096            font_size: Some(MINIMAP_FONT_SIZE),
21097            font_weight: Some(MINIMAP_FONT_WEIGHT),
21098            font_family: Some(MINIMAP_FONT_FAMILY),
21099            ..Default::default()
21100        });
21101        minimap.update_minimap_configuration(minimap_settings, cx);
21102        cx.new(|_| minimap)
21103    }
21104
21105    fn update_minimap_configuration(&mut self, minimap_settings: MinimapSettings, cx: &App) {
21106        let current_line_highlight = minimap_settings
21107            .current_line_highlight
21108            .unwrap_or_else(|| EditorSettings::get_global(cx).current_line_highlight);
21109        self.set_current_line_highlight(Some(current_line_highlight));
21110    }
21111
21112    pub fn minimap(&self) -> Option<&Entity<Self>> {
21113        self.minimap
21114            .as_ref()
21115            .filter(|_| self.minimap_visibility.visible())
21116    }
21117
21118    pub fn wrap_guides(&self, cx: &App) -> SmallVec<[(usize, bool); 2]> {
21119        let mut wrap_guides = smallvec![];
21120
21121        if self.show_wrap_guides == Some(false) {
21122            return wrap_guides;
21123        }
21124
21125        let settings = self.buffer.read(cx).language_settings(cx);
21126        if settings.show_wrap_guides {
21127            match self.soft_wrap_mode(cx) {
21128                SoftWrap::Column(soft_wrap) => {
21129                    wrap_guides.push((soft_wrap as usize, true));
21130                }
21131                SoftWrap::Bounded(soft_wrap) => {
21132                    wrap_guides.push((soft_wrap as usize, true));
21133                }
21134                SoftWrap::GitDiff | SoftWrap::None | SoftWrap::EditorWidth => {}
21135            }
21136            wrap_guides.extend(settings.wrap_guides.iter().map(|guide| (*guide, false)))
21137        }
21138
21139        wrap_guides
21140    }
21141
21142    pub fn soft_wrap_mode(&self, cx: &App) -> SoftWrap {
21143        let settings = self.buffer.read(cx).language_settings(cx);
21144        let mode = self.soft_wrap_mode_override.unwrap_or(settings.soft_wrap);
21145        match mode {
21146            language_settings::SoftWrap::PreferLine | language_settings::SoftWrap::None => {
21147                SoftWrap::None
21148            }
21149            language_settings::SoftWrap::EditorWidth => SoftWrap::EditorWidth,
21150            language_settings::SoftWrap::PreferredLineLength => {
21151                SoftWrap::Column(settings.preferred_line_length)
21152            }
21153            language_settings::SoftWrap::Bounded => {
21154                SoftWrap::Bounded(settings.preferred_line_length)
21155            }
21156        }
21157    }
21158
21159    pub fn set_soft_wrap_mode(
21160        &mut self,
21161        mode: language_settings::SoftWrap,
21162        cx: &mut Context<Self>,
21163    ) {
21164        self.soft_wrap_mode_override = Some(mode);
21165        cx.notify();
21166    }
21167
21168    pub fn set_hard_wrap(&mut self, hard_wrap: Option<usize>, cx: &mut Context<Self>) {
21169        self.hard_wrap = hard_wrap;
21170        cx.notify();
21171    }
21172
21173    pub fn set_text_style_refinement(&mut self, style: TextStyleRefinement) {
21174        self.text_style_refinement = Some(style);
21175    }
21176
21177    /// called by the Element so we know what style we were most recently rendered with.
21178    pub fn set_style(&mut self, style: EditorStyle, window: &mut Window, cx: &mut Context<Self>) {
21179        // We intentionally do not inform the display map about the minimap style
21180        // so that wrapping is not recalculated and stays consistent for the editor
21181        // and its linked minimap.
21182        if !self.mode.is_minimap() {
21183            let font = style.text.font();
21184            let font_size = style.text.font_size.to_pixels(window.rem_size());
21185            let display_map = self
21186                .placeholder_display_map
21187                .as_ref()
21188                .filter(|_| self.is_empty(cx))
21189                .unwrap_or(&self.display_map);
21190
21191            display_map.update(cx, |map, cx| map.set_font(font, font_size, cx));
21192        }
21193        self.style = Some(style);
21194    }
21195
21196    pub fn style(&mut self, cx: &App) -> &EditorStyle {
21197        if self.style.is_none() {
21198            self.style = Some(self.create_style(cx));
21199        }
21200        self.style.as_ref().unwrap()
21201    }
21202
21203    // Called by the element. This method is not designed to be called outside of the editor
21204    // element's layout code because it does not notify when rewrapping is computed synchronously.
21205    pub(crate) fn set_wrap_width(&self, width: Option<Pixels>, cx: &mut App) -> bool {
21206        if self.is_empty(cx) {
21207            self.placeholder_display_map
21208                .as_ref()
21209                .map_or(false, |display_map| {
21210                    display_map.update(cx, |map, cx| map.set_wrap_width(width, cx))
21211                })
21212        } else {
21213            self.display_map
21214                .update(cx, |map, cx| map.set_wrap_width(width, cx))
21215        }
21216    }
21217
21218    pub fn set_soft_wrap(&mut self) {
21219        self.soft_wrap_mode_override = Some(language_settings::SoftWrap::EditorWidth)
21220    }
21221
21222    pub fn toggle_soft_wrap(&mut self, _: &ToggleSoftWrap, _: &mut Window, cx: &mut Context<Self>) {
21223        if self.soft_wrap_mode_override.is_some() {
21224            self.soft_wrap_mode_override.take();
21225        } else {
21226            let soft_wrap = match self.soft_wrap_mode(cx) {
21227                SoftWrap::GitDiff => return,
21228                SoftWrap::None => language_settings::SoftWrap::EditorWidth,
21229                SoftWrap::EditorWidth | SoftWrap::Column(_) | SoftWrap::Bounded(_) => {
21230                    language_settings::SoftWrap::None
21231                }
21232            };
21233            self.soft_wrap_mode_override = Some(soft_wrap);
21234        }
21235        cx.notify();
21236    }
21237
21238    pub fn toggle_tab_bar(&mut self, _: &ToggleTabBar, _: &mut Window, cx: &mut Context<Self>) {
21239        let Some(workspace) = self.workspace() else {
21240            return;
21241        };
21242        let fs = workspace.read(cx).app_state().fs.clone();
21243        let current_show = TabBarSettings::get_global(cx).show;
21244        update_settings_file(fs, cx, move |setting, _| {
21245            setting.tab_bar.get_or_insert_default().show = Some(!current_show);
21246        });
21247    }
21248
21249    pub fn toggle_indent_guides(
21250        &mut self,
21251        _: &ToggleIndentGuides,
21252        _: &mut Window,
21253        cx: &mut Context<Self>,
21254    ) {
21255        let currently_enabled = self.should_show_indent_guides().unwrap_or_else(|| {
21256            self.buffer
21257                .read(cx)
21258                .language_settings(cx)
21259                .indent_guides
21260                .enabled
21261        });
21262        self.show_indent_guides = Some(!currently_enabled);
21263        cx.notify();
21264    }
21265
21266    fn should_show_indent_guides(&self) -> Option<bool> {
21267        self.show_indent_guides
21268    }
21269
21270    pub fn disable_indent_guides_for_buffer(
21271        &mut self,
21272        buffer_id: BufferId,
21273        cx: &mut Context<Self>,
21274    ) {
21275        self.buffers_with_disabled_indent_guides.insert(buffer_id);
21276        cx.notify();
21277    }
21278
21279    pub fn has_indent_guides_disabled_for_buffer(&self, buffer_id: BufferId) -> bool {
21280        self.buffers_with_disabled_indent_guides
21281            .contains(&buffer_id)
21282    }
21283
21284    pub fn toggle_line_numbers(
21285        &mut self,
21286        _: &ToggleLineNumbers,
21287        _: &mut Window,
21288        cx: &mut Context<Self>,
21289    ) {
21290        let mut editor_settings = EditorSettings::get_global(cx).clone();
21291        editor_settings.gutter.line_numbers = !editor_settings.gutter.line_numbers;
21292        EditorSettings::override_global(editor_settings, cx);
21293    }
21294
21295    pub fn line_numbers_enabled(&self, cx: &App) -> bool {
21296        if let Some(show_line_numbers) = self.show_line_numbers {
21297            return show_line_numbers;
21298        }
21299        EditorSettings::get_global(cx).gutter.line_numbers
21300    }
21301
21302    pub fn relative_line_numbers(&self, cx: &App) -> RelativeLineNumbers {
21303        match (
21304            self.use_relative_line_numbers,
21305            EditorSettings::get_global(cx).relative_line_numbers,
21306        ) {
21307            (None, setting) => setting,
21308            (Some(false), _) => RelativeLineNumbers::Disabled,
21309            (Some(true), RelativeLineNumbers::Wrapped) => RelativeLineNumbers::Wrapped,
21310            (Some(true), _) => RelativeLineNumbers::Enabled,
21311        }
21312    }
21313
21314    pub fn toggle_relative_line_numbers(
21315        &mut self,
21316        _: &ToggleRelativeLineNumbers,
21317        _: &mut Window,
21318        cx: &mut Context<Self>,
21319    ) {
21320        let is_relative = self.relative_line_numbers(cx);
21321        self.set_relative_line_number(Some(!is_relative.enabled()), cx)
21322    }
21323
21324    pub fn set_relative_line_number(&mut self, is_relative: Option<bool>, cx: &mut Context<Self>) {
21325        self.use_relative_line_numbers = is_relative;
21326        cx.notify();
21327    }
21328
21329    pub fn set_show_gutter(&mut self, show_gutter: bool, cx: &mut Context<Self>) {
21330        self.show_gutter = show_gutter;
21331        cx.notify();
21332    }
21333
21334    pub fn set_show_scrollbars(&mut self, show: bool, cx: &mut Context<Self>) {
21335        self.show_scrollbars = ScrollbarAxes {
21336            horizontal: show,
21337            vertical: show,
21338        };
21339        cx.notify();
21340    }
21341
21342    pub fn set_show_vertical_scrollbar(&mut self, show: bool, cx: &mut Context<Self>) {
21343        self.show_scrollbars.vertical = show;
21344        cx.notify();
21345    }
21346
21347    pub fn set_show_horizontal_scrollbar(&mut self, show: bool, cx: &mut Context<Self>) {
21348        self.show_scrollbars.horizontal = show;
21349        cx.notify();
21350    }
21351
21352    pub fn set_minimap_visibility(
21353        &mut self,
21354        minimap_visibility: MinimapVisibility,
21355        window: &mut Window,
21356        cx: &mut Context<Self>,
21357    ) {
21358        if self.minimap_visibility != minimap_visibility {
21359            if minimap_visibility.visible() && self.minimap.is_none() {
21360                let minimap_settings = EditorSettings::get_global(cx).minimap;
21361                self.minimap =
21362                    self.create_minimap(minimap_settings.with_show_override(), window, cx);
21363            }
21364            self.minimap_visibility = minimap_visibility;
21365            cx.notify();
21366        }
21367    }
21368
21369    pub fn disable_scrollbars_and_minimap(&mut self, window: &mut Window, cx: &mut Context<Self>) {
21370        self.set_show_scrollbars(false, cx);
21371        self.set_minimap_visibility(MinimapVisibility::Disabled, window, cx);
21372    }
21373
21374    pub fn hide_minimap_by_default(&mut self, window: &mut Window, cx: &mut Context<Self>) {
21375        self.set_minimap_visibility(self.minimap_visibility.hidden(), window, cx);
21376    }
21377
21378    /// Normally the text in full mode and auto height editors is padded on the
21379    /// left side by roughly half a character width for improved hit testing.
21380    ///
21381    /// Use this method to disable this for cases where this is not wanted (e.g.
21382    /// if you want to align the editor text with some other text above or below)
21383    /// or if you want to add this padding to single-line editors.
21384    pub fn set_offset_content(&mut self, offset_content: bool, cx: &mut Context<Self>) {
21385        self.offset_content = offset_content;
21386        cx.notify();
21387    }
21388
21389    pub fn set_show_line_numbers(&mut self, show_line_numbers: bool, cx: &mut Context<Self>) {
21390        self.show_line_numbers = Some(show_line_numbers);
21391        cx.notify();
21392    }
21393
21394    pub fn disable_expand_excerpt_buttons(&mut self, cx: &mut Context<Self>) {
21395        self.disable_expand_excerpt_buttons = true;
21396        cx.notify();
21397    }
21398
21399    pub fn set_number_deleted_lines(&mut self, number: bool, cx: &mut Context<Self>) {
21400        self.number_deleted_lines = number;
21401        cx.notify();
21402    }
21403
21404    pub fn set_delegate_expand_excerpts(&mut self, delegate: bool) {
21405        self.delegate_expand_excerpts = delegate;
21406    }
21407
21408    pub fn set_delegate_stage_and_restore(&mut self, delegate: bool) {
21409        self.delegate_stage_and_restore = delegate;
21410    }
21411
21412    pub fn set_delegate_open_excerpts(&mut self, delegate: bool) {
21413        self.delegate_open_excerpts = delegate;
21414    }
21415
21416    pub fn set_on_local_selections_changed(
21417        &mut self,
21418        callback: Option<Box<dyn Fn(Point, &mut Window, &mut Context<Self>) + 'static>>,
21419    ) {
21420        self.on_local_selections_changed = callback;
21421    }
21422
21423    pub fn set_suppress_selection_callback(&mut self, suppress: bool) {
21424        self.suppress_selection_callback = suppress;
21425    }
21426
21427    pub fn set_show_git_diff_gutter(&mut self, show_git_diff_gutter: bool, cx: &mut Context<Self>) {
21428        self.show_git_diff_gutter = Some(show_git_diff_gutter);
21429        cx.notify();
21430    }
21431
21432    pub fn set_show_code_actions(&mut self, show_code_actions: bool, cx: &mut Context<Self>) {
21433        self.show_code_actions = Some(show_code_actions);
21434        cx.notify();
21435    }
21436
21437    pub fn set_show_runnables(&mut self, show_runnables: bool, cx: &mut Context<Self>) {
21438        self.show_runnables = Some(show_runnables);
21439        cx.notify();
21440    }
21441
21442    pub fn set_show_breakpoints(&mut self, show_breakpoints: bool, cx: &mut Context<Self>) {
21443        self.show_breakpoints = Some(show_breakpoints);
21444        cx.notify();
21445    }
21446
21447    pub fn set_show_diff_review_button(&mut self, show: bool, cx: &mut Context<Self>) {
21448        self.show_diff_review_button = show;
21449        cx.notify();
21450    }
21451
21452    pub fn show_diff_review_button(&self) -> bool {
21453        self.show_diff_review_button
21454    }
21455
21456    pub fn render_diff_review_button(
21457        &self,
21458        display_row: DisplayRow,
21459        width: Pixels,
21460        cx: &mut Context<Self>,
21461    ) -> impl IntoElement {
21462        let text_color = cx.theme().colors().text;
21463        let icon_color = cx.theme().colors().icon_accent;
21464
21465        h_flex()
21466            .id("diff_review_button")
21467            .cursor_pointer()
21468            .w(width - px(1.))
21469            .h(relative(0.9))
21470            .justify_center()
21471            .rounded_sm()
21472            .border_1()
21473            .border_color(text_color.opacity(0.1))
21474            .bg(text_color.opacity(0.15))
21475            .hover(|s| {
21476                s.bg(icon_color.opacity(0.4))
21477                    .border_color(icon_color.opacity(0.5))
21478            })
21479            .child(Icon::new(IconName::Plus).size(IconSize::Small))
21480            .tooltip(Tooltip::text("Add Review (drag to select multiple lines)"))
21481            .on_mouse_down(
21482                gpui::MouseButton::Left,
21483                cx.listener(move |editor, _event: &gpui::MouseDownEvent, window, cx| {
21484                    editor.start_diff_review_drag(display_row, window, cx);
21485                }),
21486            )
21487    }
21488
21489    pub fn start_diff_review_drag(
21490        &mut self,
21491        display_row: DisplayRow,
21492        window: &mut Window,
21493        cx: &mut Context<Self>,
21494    ) {
21495        let snapshot = self.snapshot(window, cx);
21496        let point = snapshot
21497            .display_snapshot
21498            .display_point_to_point(DisplayPoint::new(display_row, 0), Bias::Left);
21499        let anchor = snapshot.buffer_snapshot().anchor_before(point);
21500        self.diff_review_drag_state = Some(DiffReviewDragState {
21501            start_anchor: anchor,
21502            current_anchor: anchor,
21503        });
21504        cx.notify();
21505    }
21506
21507    pub fn update_diff_review_drag(
21508        &mut self,
21509        display_row: DisplayRow,
21510        window: &mut Window,
21511        cx: &mut Context<Self>,
21512    ) {
21513        if self.diff_review_drag_state.is_none() {
21514            return;
21515        }
21516        let snapshot = self.snapshot(window, cx);
21517        let point = snapshot
21518            .display_snapshot
21519            .display_point_to_point(display_row.as_display_point(), Bias::Left);
21520        let anchor = snapshot.buffer_snapshot().anchor_before(point);
21521        if let Some(drag_state) = &mut self.diff_review_drag_state {
21522            drag_state.current_anchor = anchor;
21523            cx.notify();
21524        }
21525    }
21526
21527    pub fn end_diff_review_drag(&mut self, window: &mut Window, cx: &mut Context<Self>) {
21528        if let Some(drag_state) = self.diff_review_drag_state.take() {
21529            let snapshot = self.snapshot(window, cx);
21530            let range = drag_state.row_range(&snapshot.display_snapshot);
21531            self.show_diff_review_overlay(*range.start()..*range.end(), window, cx);
21532        }
21533        cx.notify();
21534    }
21535
21536    pub fn cancel_diff_review_drag(&mut self, cx: &mut Context<Self>) {
21537        self.diff_review_drag_state = None;
21538        cx.notify();
21539    }
21540
21541    /// Calculates the appropriate block height for the diff review overlay.
21542    /// Height is in lines: 2 for input row, 1 for header when comments exist,
21543    /// and 2 lines per comment when expanded.
21544    fn calculate_overlay_height(
21545        &self,
21546        hunk_key: &DiffHunkKey,
21547        comments_expanded: bool,
21548        snapshot: &MultiBufferSnapshot,
21549    ) -> u32 {
21550        let comment_count = self.hunk_comment_count(hunk_key, snapshot);
21551        let base_height: u32 = 2; // Input row with avatar and buttons
21552
21553        if comment_count == 0 {
21554            base_height
21555        } else if comments_expanded {
21556            // Header (1 line) + 2 lines per comment
21557            base_height + 1 + (comment_count as u32 * 2)
21558        } else {
21559            // Just header when collapsed
21560            base_height + 1
21561        }
21562    }
21563
21564    pub fn show_diff_review_overlay(
21565        &mut self,
21566        display_range: Range<DisplayRow>,
21567        window: &mut Window,
21568        cx: &mut Context<Self>,
21569    ) {
21570        let Range { start, end } = display_range.sorted();
21571
21572        let buffer_snapshot = self.buffer.read(cx).snapshot(cx);
21573        let editor_snapshot = self.snapshot(window, cx);
21574
21575        // Convert display rows to multibuffer points
21576        let start_point = editor_snapshot
21577            .display_snapshot
21578            .display_point_to_point(start.as_display_point(), Bias::Left);
21579        let end_point = editor_snapshot
21580            .display_snapshot
21581            .display_point_to_point(end.as_display_point(), Bias::Left);
21582        let end_multi_buffer_row = MultiBufferRow(end_point.row);
21583
21584        // Create anchor range for the selected lines (start of first line to end of last line)
21585        let line_end = Point::new(
21586            end_point.row,
21587            buffer_snapshot.line_len(end_multi_buffer_row),
21588        );
21589        let anchor_range =
21590            buffer_snapshot.anchor_after(start_point)..buffer_snapshot.anchor_before(line_end);
21591
21592        // Compute the hunk key for this display row
21593        let file_path = buffer_snapshot
21594            .file_at(start_point)
21595            .map(|file: &Arc<dyn language::File>| file.path().clone())
21596            .unwrap_or_else(|| Arc::from(util::rel_path::RelPath::empty()));
21597        let hunk_start_anchor = buffer_snapshot.anchor_before(start_point);
21598        let new_hunk_key = DiffHunkKey {
21599            file_path,
21600            hunk_start_anchor,
21601        };
21602
21603        // Check if we already have an overlay for this hunk
21604        if let Some(existing_overlay) = self.diff_review_overlays.iter().find(|overlay| {
21605            Self::hunk_keys_match(&overlay.hunk_key, &new_hunk_key, &buffer_snapshot)
21606        }) {
21607            // Just focus the existing overlay's prompt editor
21608            let focus_handle = existing_overlay.prompt_editor.focus_handle(cx);
21609            window.focus(&focus_handle, cx);
21610            return;
21611        }
21612
21613        // Dismiss overlays that have no comments for their hunks
21614        self.dismiss_overlays_without_comments(cx);
21615
21616        // Get the current user's avatar URI from the project's user_store
21617        let user_avatar_uri = self.project.as_ref().and_then(|project| {
21618            let user_store = project.read(cx).user_store();
21619            user_store
21620                .read(cx)
21621                .current_user()
21622                .map(|user| user.avatar_uri.clone())
21623        });
21624
21625        // Create anchor at the end of the last row so the block appears immediately below it
21626        // Use multibuffer coordinates for anchor creation
21627        let line_len = buffer_snapshot.line_len(end_multi_buffer_row);
21628        let anchor = buffer_snapshot.anchor_after(Point::new(end_multi_buffer_row.0, line_len));
21629
21630        // Use the hunk key we already computed
21631        let hunk_key = new_hunk_key;
21632
21633        // Create the prompt editor for the review input
21634        let prompt_editor = cx.new(|cx| {
21635            let mut editor = Editor::single_line(window, cx);
21636            editor.set_placeholder_text("Add a review comment...", window, cx);
21637            editor
21638        });
21639
21640        // Register the Newline action on the prompt editor to submit the review
21641        let parent_editor = cx.entity().downgrade();
21642        let subscription = prompt_editor.update(cx, |prompt_editor, _cx| {
21643            prompt_editor.register_action({
21644                let parent_editor = parent_editor.clone();
21645                move |_: &crate::actions::Newline, window, cx| {
21646                    if let Some(editor) = parent_editor.upgrade() {
21647                        editor.update(cx, |editor, cx| {
21648                            editor.submit_diff_review_comment(window, cx);
21649                        });
21650                    }
21651                }
21652            })
21653        });
21654
21655        // Calculate initial height based on existing comments for this hunk
21656        let initial_height = self.calculate_overlay_height(&hunk_key, true, &buffer_snapshot);
21657
21658        // Create the overlay block
21659        let prompt_editor_for_render = prompt_editor.clone();
21660        let hunk_key_for_render = hunk_key.clone();
21661        let editor_handle = cx.entity().downgrade();
21662        let block = BlockProperties {
21663            style: BlockStyle::Sticky,
21664            placement: BlockPlacement::Below(anchor),
21665            height: Some(initial_height),
21666            render: Arc::new(move |cx| {
21667                Self::render_diff_review_overlay(
21668                    &prompt_editor_for_render,
21669                    &hunk_key_for_render,
21670                    &editor_handle,
21671                    cx,
21672                )
21673            }),
21674            priority: 0,
21675        };
21676
21677        let block_ids = self.insert_blocks([block], None, cx);
21678        let Some(block_id) = block_ids.into_iter().next() else {
21679            log::error!("Failed to insert diff review overlay block");
21680            return;
21681        };
21682
21683        self.diff_review_overlays.push(DiffReviewOverlay {
21684            anchor_range,
21685            block_id,
21686            prompt_editor: prompt_editor.clone(),
21687            hunk_key,
21688            comments_expanded: true,
21689            inline_edit_editors: HashMap::default(),
21690            inline_edit_subscriptions: HashMap::default(),
21691            user_avatar_uri,
21692            _subscription: subscription,
21693        });
21694
21695        // Focus the prompt editor
21696        let focus_handle = prompt_editor.focus_handle(cx);
21697        window.focus(&focus_handle, cx);
21698
21699        cx.notify();
21700    }
21701
21702    /// Dismisses all diff review overlays.
21703    pub fn dismiss_all_diff_review_overlays(&mut self, cx: &mut Context<Self>) {
21704        if self.diff_review_overlays.is_empty() {
21705            return;
21706        }
21707        let block_ids: HashSet<_> = self
21708            .diff_review_overlays
21709            .drain(..)
21710            .map(|overlay| overlay.block_id)
21711            .collect();
21712        self.remove_blocks(block_ids, None, cx);
21713        cx.notify();
21714    }
21715
21716    /// Dismisses overlays that have no comments stored for their hunks.
21717    /// Keeps overlays that have at least one comment.
21718    fn dismiss_overlays_without_comments(&mut self, cx: &mut Context<Self>) {
21719        let snapshot = self.buffer.read(cx).snapshot(cx);
21720
21721        // First, compute which overlays have comments (to avoid borrow issues with retain)
21722        let overlays_with_comments: Vec<bool> = self
21723            .diff_review_overlays
21724            .iter()
21725            .map(|overlay| self.hunk_comment_count(&overlay.hunk_key, &snapshot) > 0)
21726            .collect();
21727
21728        // Now collect block IDs to remove and retain overlays
21729        let mut block_ids_to_remove = HashSet::default();
21730        let mut index = 0;
21731        self.diff_review_overlays.retain(|overlay| {
21732            let has_comments = overlays_with_comments[index];
21733            index += 1;
21734            if !has_comments {
21735                block_ids_to_remove.insert(overlay.block_id);
21736            }
21737            has_comments
21738        });
21739
21740        if !block_ids_to_remove.is_empty() {
21741            self.remove_blocks(block_ids_to_remove, None, cx);
21742            cx.notify();
21743        }
21744    }
21745
21746    /// Refreshes the diff review overlay block to update its height and render function.
21747    /// Uses resize_blocks and replace_blocks to avoid visual flicker from remove+insert.
21748    fn refresh_diff_review_overlay_height(
21749        &mut self,
21750        hunk_key: &DiffHunkKey,
21751        _window: &mut Window,
21752        cx: &mut Context<Self>,
21753    ) {
21754        // Extract all needed data from overlay first to avoid borrow conflicts
21755        let snapshot = self.buffer.read(cx).snapshot(cx);
21756        let (comments_expanded, block_id, prompt_editor) = {
21757            let Some(overlay) = self
21758                .diff_review_overlays
21759                .iter()
21760                .find(|overlay| Self::hunk_keys_match(&overlay.hunk_key, hunk_key, &snapshot))
21761            else {
21762                return;
21763            };
21764
21765            (
21766                overlay.comments_expanded,
21767                overlay.block_id,
21768                overlay.prompt_editor.clone(),
21769            )
21770        };
21771
21772        // Calculate new height
21773        let snapshot = self.buffer.read(cx).snapshot(cx);
21774        let new_height = self.calculate_overlay_height(hunk_key, comments_expanded, &snapshot);
21775
21776        // Update the block height using resize_blocks (avoids flicker)
21777        let mut heights = HashMap::default();
21778        heights.insert(block_id, new_height);
21779        self.resize_blocks(heights, None, cx);
21780
21781        // Update the render function using replace_blocks (avoids flicker)
21782        let hunk_key_for_render = hunk_key.clone();
21783        let editor_handle = cx.entity().downgrade();
21784        let render: Arc<dyn Fn(&mut BlockContext) -> AnyElement + Send + Sync> =
21785            Arc::new(move |cx| {
21786                Self::render_diff_review_overlay(
21787                    &prompt_editor,
21788                    &hunk_key_for_render,
21789                    &editor_handle,
21790                    cx,
21791                )
21792            });
21793
21794        let mut renderers = HashMap::default();
21795        renderers.insert(block_id, render);
21796        self.replace_blocks(renderers, None, cx);
21797    }
21798
21799    /// Action handler for SubmitDiffReviewComment.
21800    pub fn submit_diff_review_comment_action(
21801        &mut self,
21802        _: &SubmitDiffReviewComment,
21803        window: &mut Window,
21804        cx: &mut Context<Self>,
21805    ) {
21806        self.submit_diff_review_comment(window, cx);
21807    }
21808
21809    /// Stores the diff review comment locally.
21810    /// Comments are stored per-hunk and can later be batch-submitted to the Agent panel.
21811    pub fn submit_diff_review_comment(&mut self, window: &mut Window, cx: &mut Context<Self>) {
21812        // Find the overlay that currently has focus
21813        let overlay_index = self
21814            .diff_review_overlays
21815            .iter()
21816            .position(|overlay| overlay.prompt_editor.focus_handle(cx).is_focused(window));
21817        let Some(overlay_index) = overlay_index else {
21818            return;
21819        };
21820        let overlay = &self.diff_review_overlays[overlay_index];
21821
21822        let comment_text = overlay.prompt_editor.read(cx).text(cx).trim().to_string();
21823        if comment_text.is_empty() {
21824            return;
21825        }
21826
21827        let anchor_range = overlay.anchor_range.clone();
21828        let hunk_key = overlay.hunk_key.clone();
21829
21830        self.add_review_comment(hunk_key.clone(), comment_text, anchor_range, cx);
21831
21832        // Clear the prompt editor but keep the overlay open
21833        if let Some(overlay) = self.diff_review_overlays.get(overlay_index) {
21834            overlay.prompt_editor.update(cx, |editor, cx| {
21835                editor.clear(window, cx);
21836            });
21837        }
21838
21839        // Refresh the overlay to update the block height for the new comment
21840        self.refresh_diff_review_overlay_height(&hunk_key, window, cx);
21841
21842        cx.notify();
21843    }
21844
21845    /// Returns the prompt editor for the diff review overlay, if one is active.
21846    /// This is primarily used for testing.
21847    pub fn diff_review_prompt_editor(&self) -> Option<&Entity<Editor>> {
21848        self.diff_review_overlays
21849            .first()
21850            .map(|overlay| &overlay.prompt_editor)
21851    }
21852
21853    /// Returns the line range for the first diff review overlay, if one is active.
21854    /// Returns (start_row, end_row) as physical line numbers in the underlying file.
21855    pub fn diff_review_line_range(&self, cx: &App) -> Option<(u32, u32)> {
21856        let overlay = self.diff_review_overlays.first()?;
21857        let snapshot = self.buffer.read(cx).snapshot(cx);
21858        let start_point = overlay.anchor_range.start.to_point(&snapshot);
21859        let end_point = overlay.anchor_range.end.to_point(&snapshot);
21860        let start_row = snapshot
21861            .point_to_buffer_point(start_point)
21862            .map(|(_, p, _)| p.row)
21863            .unwrap_or(start_point.row);
21864        let end_row = snapshot
21865            .point_to_buffer_point(end_point)
21866            .map(|(_, p, _)| p.row)
21867            .unwrap_or(end_point.row);
21868        Some((start_row, end_row))
21869    }
21870
21871    /// Sets whether the comments section is expanded in the diff review overlay.
21872    /// This is primarily used for testing.
21873    pub fn set_diff_review_comments_expanded(&mut self, expanded: bool, cx: &mut Context<Self>) {
21874        for overlay in &mut self.diff_review_overlays {
21875            overlay.comments_expanded = expanded;
21876        }
21877        cx.notify();
21878    }
21879
21880    /// Compares two DiffHunkKeys for equality by resolving their anchors.
21881    fn hunk_keys_match(a: &DiffHunkKey, b: &DiffHunkKey, snapshot: &MultiBufferSnapshot) -> bool {
21882        a.file_path == b.file_path
21883            && a.hunk_start_anchor.to_point(snapshot) == b.hunk_start_anchor.to_point(snapshot)
21884    }
21885
21886    /// Returns comments for a specific hunk, ordered by creation time.
21887    pub fn comments_for_hunk<'a>(
21888        &'a self,
21889        key: &DiffHunkKey,
21890        snapshot: &MultiBufferSnapshot,
21891    ) -> &'a [StoredReviewComment] {
21892        let key_point = key.hunk_start_anchor.to_point(snapshot);
21893        self.stored_review_comments
21894            .iter()
21895            .find(|(k, _)| {
21896                k.file_path == key.file_path && k.hunk_start_anchor.to_point(snapshot) == key_point
21897            })
21898            .map(|(_, comments)| comments.as_slice())
21899            .unwrap_or(&[])
21900    }
21901
21902    /// Returns the total count of stored review comments across all hunks.
21903    pub fn total_review_comment_count(&self) -> usize {
21904        self.stored_review_comments
21905            .iter()
21906            .map(|(_, v)| v.len())
21907            .sum()
21908    }
21909
21910    /// Returns the count of comments for a specific hunk.
21911    pub fn hunk_comment_count(&self, key: &DiffHunkKey, snapshot: &MultiBufferSnapshot) -> usize {
21912        let key_point = key.hunk_start_anchor.to_point(snapshot);
21913        self.stored_review_comments
21914            .iter()
21915            .find(|(k, _)| {
21916                k.file_path == key.file_path && k.hunk_start_anchor.to_point(snapshot) == key_point
21917            })
21918            .map(|(_, v)| v.len())
21919            .unwrap_or(0)
21920    }
21921
21922    /// Adds a new review comment to a specific hunk.
21923    pub fn add_review_comment(
21924        &mut self,
21925        hunk_key: DiffHunkKey,
21926        comment: String,
21927        anchor_range: Range<Anchor>,
21928        cx: &mut Context<Self>,
21929    ) -> usize {
21930        let id = self.next_review_comment_id;
21931        self.next_review_comment_id += 1;
21932
21933        let stored_comment = StoredReviewComment::new(id, comment, anchor_range);
21934
21935        let snapshot = self.buffer.read(cx).snapshot(cx);
21936        let key_point = hunk_key.hunk_start_anchor.to_point(&snapshot);
21937
21938        // Find existing entry for this hunk or add a new one
21939        if let Some((_, comments)) = self.stored_review_comments.iter_mut().find(|(k, _)| {
21940            k.file_path == hunk_key.file_path
21941                && k.hunk_start_anchor.to_point(&snapshot) == key_point
21942        }) {
21943            comments.push(stored_comment);
21944        } else {
21945            self.stored_review_comments
21946                .push((hunk_key, vec![stored_comment]));
21947        }
21948
21949        cx.emit(EditorEvent::ReviewCommentsChanged {
21950            total_count: self.total_review_comment_count(),
21951        });
21952        cx.notify();
21953        id
21954    }
21955
21956    /// Removes a review comment by ID from any hunk.
21957    pub fn remove_review_comment(&mut self, id: usize, cx: &mut Context<Self>) -> bool {
21958        for (_, comments) in self.stored_review_comments.iter_mut() {
21959            if let Some(index) = comments.iter().position(|c| c.id == id) {
21960                comments.remove(index);
21961                cx.emit(EditorEvent::ReviewCommentsChanged {
21962                    total_count: self.total_review_comment_count(),
21963                });
21964                cx.notify();
21965                return true;
21966            }
21967        }
21968        false
21969    }
21970
21971    /// Updates a review comment's text by ID.
21972    pub fn update_review_comment(
21973        &mut self,
21974        id: usize,
21975        new_comment: String,
21976        cx: &mut Context<Self>,
21977    ) -> bool {
21978        for (_, comments) in self.stored_review_comments.iter_mut() {
21979            if let Some(comment) = comments.iter_mut().find(|c| c.id == id) {
21980                comment.comment = new_comment;
21981                comment.is_editing = false;
21982                cx.emit(EditorEvent::ReviewCommentsChanged {
21983                    total_count: self.total_review_comment_count(),
21984                });
21985                cx.notify();
21986                return true;
21987            }
21988        }
21989        false
21990    }
21991
21992    /// Sets a comment's editing state.
21993    pub fn set_comment_editing(&mut self, id: usize, is_editing: bool, cx: &mut Context<Self>) {
21994        for (_, comments) in self.stored_review_comments.iter_mut() {
21995            if let Some(comment) = comments.iter_mut().find(|c| c.id == id) {
21996                comment.is_editing = is_editing;
21997                cx.notify();
21998                return;
21999            }
22000        }
22001    }
22002
22003    /// Takes all stored comments from all hunks, clearing the storage.
22004    /// Returns a Vec of (hunk_key, comments) pairs.
22005    pub fn take_all_review_comments(
22006        &mut self,
22007        cx: &mut Context<Self>,
22008    ) -> Vec<(DiffHunkKey, Vec<StoredReviewComment>)> {
22009        // Dismiss all overlays when taking comments (e.g., when sending to agent)
22010        self.dismiss_all_diff_review_overlays(cx);
22011        let comments = std::mem::take(&mut self.stored_review_comments);
22012        // Reset the ID counter since all comments have been taken
22013        self.next_review_comment_id = 0;
22014        cx.emit(EditorEvent::ReviewCommentsChanged { total_count: 0 });
22015        cx.notify();
22016        comments
22017    }
22018
22019    /// Removes review comments whose anchors are no longer valid or whose
22020    /// associated diff hunks no longer exist.
22021    ///
22022    /// This should be called when the buffer changes to prevent orphaned comments
22023    /// from accumulating.
22024    pub fn cleanup_orphaned_review_comments(&mut self, cx: &mut Context<Self>) {
22025        let snapshot = self.buffer.read(cx).snapshot(cx);
22026        let original_count = self.total_review_comment_count();
22027
22028        // Remove comments with invalid hunk anchors
22029        self.stored_review_comments
22030            .retain(|(hunk_key, _)| hunk_key.hunk_start_anchor.is_valid(&snapshot));
22031
22032        // Also clean up individual comments with invalid anchor ranges
22033        for (_, comments) in &mut self.stored_review_comments {
22034            comments.retain(|comment| {
22035                comment.range.start.is_valid(&snapshot) && comment.range.end.is_valid(&snapshot)
22036            });
22037        }
22038
22039        // Remove empty hunk entries
22040        self.stored_review_comments
22041            .retain(|(_, comments)| !comments.is_empty());
22042
22043        let new_count = self.total_review_comment_count();
22044        if new_count != original_count {
22045            cx.emit(EditorEvent::ReviewCommentsChanged {
22046                total_count: new_count,
22047            });
22048            cx.notify();
22049        }
22050    }
22051
22052    /// Toggles the expanded state of the comments section in the overlay.
22053    pub fn toggle_review_comments_expanded(
22054        &mut self,
22055        _: &ToggleReviewCommentsExpanded,
22056        window: &mut Window,
22057        cx: &mut Context<Self>,
22058    ) {
22059        // Find the overlay that currently has focus, or use the first one
22060        let overlay_info = self.diff_review_overlays.iter_mut().find_map(|overlay| {
22061            if overlay.prompt_editor.focus_handle(cx).is_focused(window) {
22062                overlay.comments_expanded = !overlay.comments_expanded;
22063                Some(overlay.hunk_key.clone())
22064            } else {
22065                None
22066            }
22067        });
22068
22069        // If no focused overlay found, toggle the first one
22070        let hunk_key = overlay_info.or_else(|| {
22071            self.diff_review_overlays.first_mut().map(|overlay| {
22072                overlay.comments_expanded = !overlay.comments_expanded;
22073                overlay.hunk_key.clone()
22074            })
22075        });
22076
22077        if let Some(hunk_key) = hunk_key {
22078            self.refresh_diff_review_overlay_height(&hunk_key, window, cx);
22079            cx.notify();
22080        }
22081    }
22082
22083    /// Handles the EditReviewComment action - sets a comment into editing mode.
22084    pub fn edit_review_comment(
22085        &mut self,
22086        action: &EditReviewComment,
22087        window: &mut Window,
22088        cx: &mut Context<Self>,
22089    ) {
22090        let comment_id = action.id;
22091
22092        // Set the comment to editing mode
22093        self.set_comment_editing(comment_id, true, cx);
22094
22095        // Find the overlay that contains this comment and create an inline editor if needed
22096        // First, find which hunk this comment belongs to
22097        let hunk_key = self
22098            .stored_review_comments
22099            .iter()
22100            .find_map(|(key, comments)| {
22101                if comments.iter().any(|c| c.id == comment_id) {
22102                    Some(key.clone())
22103                } else {
22104                    None
22105                }
22106            });
22107
22108        let snapshot = self.buffer.read(cx).snapshot(cx);
22109        if let Some(hunk_key) = hunk_key {
22110            if let Some(overlay) = self
22111                .diff_review_overlays
22112                .iter_mut()
22113                .find(|overlay| Self::hunk_keys_match(&overlay.hunk_key, &hunk_key, &snapshot))
22114            {
22115                if let std::collections::hash_map::Entry::Vacant(entry) =
22116                    overlay.inline_edit_editors.entry(comment_id)
22117                {
22118                    // Find the comment text
22119                    let comment_text = self
22120                        .stored_review_comments
22121                        .iter()
22122                        .flat_map(|(_, comments)| comments)
22123                        .find(|c| c.id == comment_id)
22124                        .map(|c| c.comment.clone())
22125                        .unwrap_or_default();
22126
22127                    // Create inline editor
22128                    let parent_editor = cx.entity().downgrade();
22129                    let inline_editor = cx.new(|cx| {
22130                        let mut editor = Editor::single_line(window, cx);
22131                        editor.set_text(&*comment_text, window, cx);
22132                        // Select all text for easy replacement
22133                        editor.select_all(&crate::actions::SelectAll, window, cx);
22134                        editor
22135                    });
22136
22137                    // Register the Newline action to confirm the edit
22138                    let subscription = inline_editor.update(cx, |inline_editor, _cx| {
22139                        inline_editor.register_action({
22140                            let parent_editor = parent_editor.clone();
22141                            move |_: &crate::actions::Newline, window, cx| {
22142                                if let Some(editor) = parent_editor.upgrade() {
22143                                    editor.update(cx, |editor, cx| {
22144                                        editor.confirm_edit_review_comment(comment_id, window, cx);
22145                                    });
22146                                }
22147                            }
22148                        })
22149                    });
22150
22151                    // Store the subscription to keep the action handler alive
22152                    overlay
22153                        .inline_edit_subscriptions
22154                        .insert(comment_id, subscription);
22155
22156                    // Focus the inline editor
22157                    let focus_handle = inline_editor.focus_handle(cx);
22158                    window.focus(&focus_handle, cx);
22159
22160                    entry.insert(inline_editor);
22161                }
22162            }
22163        }
22164
22165        cx.notify();
22166    }
22167
22168    /// Confirms an inline edit of a review comment.
22169    pub fn confirm_edit_review_comment(
22170        &mut self,
22171        comment_id: usize,
22172        _window: &mut Window,
22173        cx: &mut Context<Self>,
22174    ) {
22175        // Get the new text from the inline editor
22176        // Find the overlay containing this comment's inline editor
22177        let snapshot = self.buffer.read(cx).snapshot(cx);
22178        let hunk_key = self
22179            .stored_review_comments
22180            .iter()
22181            .find_map(|(key, comments)| {
22182                if comments.iter().any(|c| c.id == comment_id) {
22183                    Some(key.clone())
22184                } else {
22185                    None
22186                }
22187            });
22188
22189        let new_text = hunk_key
22190            .as_ref()
22191            .and_then(|hunk_key| {
22192                self.diff_review_overlays
22193                    .iter()
22194                    .find(|overlay| Self::hunk_keys_match(&overlay.hunk_key, hunk_key, &snapshot))
22195            })
22196            .as_ref()
22197            .and_then(|overlay| overlay.inline_edit_editors.get(&comment_id))
22198            .map(|editor| editor.read(cx).text(cx).trim().to_string());
22199
22200        if let Some(new_text) = new_text {
22201            if !new_text.is_empty() {
22202                self.update_review_comment(comment_id, new_text, cx);
22203            }
22204        }
22205
22206        // Remove the inline editor and its subscription
22207        if let Some(hunk_key) = hunk_key {
22208            if let Some(overlay) = self
22209                .diff_review_overlays
22210                .iter_mut()
22211                .find(|overlay| Self::hunk_keys_match(&overlay.hunk_key, &hunk_key, &snapshot))
22212            {
22213                overlay.inline_edit_editors.remove(&comment_id);
22214                overlay.inline_edit_subscriptions.remove(&comment_id);
22215            }
22216        }
22217
22218        // Clear editing state
22219        self.set_comment_editing(comment_id, false, cx);
22220    }
22221
22222    /// Cancels an inline edit of a review comment.
22223    pub fn cancel_edit_review_comment(
22224        &mut self,
22225        comment_id: usize,
22226        _window: &mut Window,
22227        cx: &mut Context<Self>,
22228    ) {
22229        // Find which hunk this comment belongs to
22230        let hunk_key = self
22231            .stored_review_comments
22232            .iter()
22233            .find_map(|(key, comments)| {
22234                if comments.iter().any(|c| c.id == comment_id) {
22235                    Some(key.clone())
22236                } else {
22237                    None
22238                }
22239            });
22240
22241        // Remove the inline editor and its subscription
22242        if let Some(hunk_key) = hunk_key {
22243            let snapshot = self.buffer.read(cx).snapshot(cx);
22244            if let Some(overlay) = self
22245                .diff_review_overlays
22246                .iter_mut()
22247                .find(|overlay| Self::hunk_keys_match(&overlay.hunk_key, &hunk_key, &snapshot))
22248            {
22249                overlay.inline_edit_editors.remove(&comment_id);
22250                overlay.inline_edit_subscriptions.remove(&comment_id);
22251            }
22252        }
22253
22254        // Clear editing state
22255        self.set_comment_editing(comment_id, false, cx);
22256    }
22257
22258    /// Action handler for ConfirmEditReviewComment.
22259    pub fn confirm_edit_review_comment_action(
22260        &mut self,
22261        action: &ConfirmEditReviewComment,
22262        window: &mut Window,
22263        cx: &mut Context<Self>,
22264    ) {
22265        self.confirm_edit_review_comment(action.id, window, cx);
22266    }
22267
22268    /// Action handler for CancelEditReviewComment.
22269    pub fn cancel_edit_review_comment_action(
22270        &mut self,
22271        action: &CancelEditReviewComment,
22272        window: &mut Window,
22273        cx: &mut Context<Self>,
22274    ) {
22275        self.cancel_edit_review_comment(action.id, window, cx);
22276    }
22277
22278    /// Handles the DeleteReviewComment action - removes a comment.
22279    pub fn delete_review_comment(
22280        &mut self,
22281        action: &DeleteReviewComment,
22282        window: &mut Window,
22283        cx: &mut Context<Self>,
22284    ) {
22285        // Get the hunk key before removing the comment
22286        // Find the hunk key from the comment itself
22287        let comment_id = action.id;
22288        let hunk_key = self
22289            .stored_review_comments
22290            .iter()
22291            .find_map(|(key, comments)| {
22292                if comments.iter().any(|c| c.id == comment_id) {
22293                    Some(key.clone())
22294                } else {
22295                    None
22296                }
22297            });
22298
22299        // Also get it from the overlay for refresh purposes
22300        let overlay_hunk_key = self
22301            .diff_review_overlays
22302            .first()
22303            .map(|o| o.hunk_key.clone());
22304
22305        self.remove_review_comment(action.id, cx);
22306
22307        // Refresh the overlay height after removing a comment
22308        if let Some(hunk_key) = hunk_key.or(overlay_hunk_key) {
22309            self.refresh_diff_review_overlay_height(&hunk_key, window, cx);
22310        }
22311    }
22312
22313    fn render_diff_review_overlay(
22314        prompt_editor: &Entity<Editor>,
22315        hunk_key: &DiffHunkKey,
22316        editor_handle: &WeakEntity<Editor>,
22317        cx: &mut BlockContext,
22318    ) -> AnyElement {
22319        fn format_line_ranges(ranges: &[(u32, u32)]) -> Option<String> {
22320            if ranges.is_empty() {
22321                return None;
22322            }
22323            let formatted: Vec<String> = ranges
22324                .iter()
22325                .map(|(start, end)| {
22326                    let start_line = start + 1;
22327                    let end_line = end + 1;
22328                    if start_line == end_line {
22329                        format!("Line {start_line}")
22330                    } else {
22331                        format!("Lines {start_line}-{end_line}")
22332                    }
22333                })
22334                .collect();
22335            // Don't show label for single line in single excerpt
22336            if ranges.len() == 1 && ranges[0].0 == ranges[0].1 {
22337                return None;
22338            }
22339            Some(formatted.join(""))
22340        }
22341
22342        let theme = cx.theme();
22343        let colors = theme.colors();
22344
22345        let (comments, comments_expanded, inline_editors, user_avatar_uri, line_ranges) =
22346            editor_handle
22347                .upgrade()
22348                .map(|editor| {
22349                    let editor = editor.read(cx);
22350                    let snapshot = editor.buffer().read(cx).snapshot(cx);
22351                    let comments = editor.comments_for_hunk(hunk_key, &snapshot).to_vec();
22352                    let (expanded, editors, avatar_uri, line_ranges) = editor
22353                        .diff_review_overlays
22354                        .iter()
22355                        .find(|overlay| {
22356                            Editor::hunk_keys_match(&overlay.hunk_key, hunk_key, &snapshot)
22357                        })
22358                        .map(|o| {
22359                            let start_point = o.anchor_range.start.to_point(&snapshot);
22360                            let end_point = o.anchor_range.end.to_point(&snapshot);
22361                            // Get line ranges per excerpt to detect discontinuities
22362                            let buffer_ranges =
22363                                snapshot.range_to_buffer_ranges(start_point..end_point);
22364                            let ranges: Vec<(u32, u32)> = buffer_ranges
22365                                .iter()
22366                                .map(|(buffer, range, _)| {
22367                                    let start = buffer.offset_to_point(range.start.0).row;
22368                                    let end = buffer.offset_to_point(range.end.0).row;
22369                                    (start, end)
22370                                })
22371                                .collect();
22372                            (
22373                                o.comments_expanded,
22374                                o.inline_edit_editors.clone(),
22375                                o.user_avatar_uri.clone(),
22376                                if ranges.is_empty() {
22377                                    None
22378                                } else {
22379                                    Some(ranges)
22380                                },
22381                            )
22382                        })
22383                        .unwrap_or((true, HashMap::default(), None, None));
22384                    (comments, expanded, editors, avatar_uri, line_ranges)
22385                })
22386                .unwrap_or((Vec::new(), true, HashMap::default(), None, None));
22387
22388        let comment_count = comments.len();
22389        let avatar_size = px(20.);
22390        let action_icon_size = IconSize::XSmall;
22391
22392        v_flex()
22393            .w_full()
22394            .bg(colors.editor_background)
22395            .border_b_1()
22396            .border_color(colors.border)
22397            .px_2()
22398            .pb_2()
22399            .gap_2()
22400            // Line range indicator (only shown for multi-line selections or multiple excerpts)
22401            .when_some(line_ranges, |el, ranges| {
22402                let label = format_line_ranges(&ranges);
22403                if let Some(label) = label {
22404                    el.child(
22405                        h_flex()
22406                            .w_full()
22407                            .px_2()
22408                            .child(Label::new(label).size(LabelSize::Small).color(Color::Muted)),
22409                    )
22410                } else {
22411                    el
22412                }
22413            })
22414            // Top row: editable input with user's avatar
22415            .child(
22416                h_flex()
22417                    .w_full()
22418                    .items_center()
22419                    .gap_2()
22420                    .px_2()
22421                    .py_1p5()
22422                    .rounded_md()
22423                    .bg(colors.surface_background)
22424                    .child(
22425                        div()
22426                            .size(avatar_size)
22427                            .flex_shrink_0()
22428                            .rounded_full()
22429                            .overflow_hidden()
22430                            .child(if let Some(ref avatar_uri) = user_avatar_uri {
22431                                Avatar::new(avatar_uri.clone())
22432                                    .size(avatar_size)
22433                                    .into_any_element()
22434                            } else {
22435                                Icon::new(IconName::Person)
22436                                    .size(IconSize::Small)
22437                                    .color(ui::Color::Muted)
22438                                    .into_any_element()
22439                            }),
22440                    )
22441                    .child(
22442                        div()
22443                            .flex_1()
22444                            .border_1()
22445                            .border_color(colors.border)
22446                            .rounded_md()
22447                            .bg(colors.editor_background)
22448                            .px_2()
22449                            .py_1()
22450                            .child(prompt_editor.clone()),
22451                    )
22452                    .child(
22453                        h_flex()
22454                            .flex_shrink_0()
22455                            .gap_1()
22456                            .child(
22457                                IconButton::new("diff-review-close", IconName::Close)
22458                                    .icon_color(ui::Color::Muted)
22459                                    .icon_size(action_icon_size)
22460                                    .tooltip(Tooltip::text("Close"))
22461                                    .on_click(|_, window, cx| {
22462                                        window
22463                                            .dispatch_action(Box::new(crate::actions::Cancel), cx);
22464                                    }),
22465                            )
22466                            .child(
22467                                IconButton::new("diff-review-add", IconName::Return)
22468                                    .icon_color(ui::Color::Muted)
22469                                    .icon_size(action_icon_size)
22470                                    .tooltip(Tooltip::text("Add comment"))
22471                                    .on_click(|_, window, cx| {
22472                                        window.dispatch_action(
22473                                            Box::new(crate::actions::SubmitDiffReviewComment),
22474                                            cx,
22475                                        );
22476                                    }),
22477                            ),
22478                    ),
22479            )
22480            // Expandable comments section (only shown when there are comments)
22481            .when(comment_count > 0, |el| {
22482                el.child(Self::render_comments_section(
22483                    comments,
22484                    comments_expanded,
22485                    inline_editors,
22486                    user_avatar_uri,
22487                    avatar_size,
22488                    action_icon_size,
22489                    colors,
22490                ))
22491            })
22492            .into_any_element()
22493    }
22494
22495    fn render_comments_section(
22496        comments: Vec<StoredReviewComment>,
22497        expanded: bool,
22498        inline_editors: HashMap<usize, Entity<Editor>>,
22499        user_avatar_uri: Option<SharedUri>,
22500        avatar_size: Pixels,
22501        action_icon_size: IconSize,
22502        colors: &theme::ThemeColors,
22503    ) -> impl IntoElement {
22504        let comment_count = comments.len();
22505
22506        v_flex()
22507            .w_full()
22508            .gap_1()
22509            // Header with expand/collapse toggle
22510            .child(
22511                h_flex()
22512                    .id("review-comments-header")
22513                    .w_full()
22514                    .items_center()
22515                    .gap_1()
22516                    .px_2()
22517                    .py_1()
22518                    .cursor_pointer()
22519                    .rounded_md()
22520                    .hover(|style| style.bg(colors.ghost_element_hover))
22521                    .on_click(|_, window: &mut Window, cx| {
22522                        window.dispatch_action(
22523                            Box::new(crate::actions::ToggleReviewCommentsExpanded),
22524                            cx,
22525                        );
22526                    })
22527                    .child(
22528                        Icon::new(if expanded {
22529                            IconName::ChevronDown
22530                        } else {
22531                            IconName::ChevronRight
22532                        })
22533                        .size(IconSize::Small)
22534                        .color(ui::Color::Muted),
22535                    )
22536                    .child(
22537                        Label::new(format!(
22538                            "{} Comment{}",
22539                            comment_count,
22540                            if comment_count == 1 { "" } else { "s" }
22541                        ))
22542                        .size(LabelSize::Small)
22543                        .color(Color::Muted),
22544                    ),
22545            )
22546            // Comments list (when expanded)
22547            .when(expanded, |el| {
22548                el.children(comments.into_iter().map(|comment| {
22549                    let inline_editor = inline_editors.get(&comment.id).cloned();
22550                    Self::render_comment_row(
22551                        comment,
22552                        inline_editor,
22553                        user_avatar_uri.clone(),
22554                        avatar_size,
22555                        action_icon_size,
22556                        colors,
22557                    )
22558                }))
22559            })
22560    }
22561
22562    fn render_comment_row(
22563        comment: StoredReviewComment,
22564        inline_editor: Option<Entity<Editor>>,
22565        user_avatar_uri: Option<SharedUri>,
22566        avatar_size: Pixels,
22567        action_icon_size: IconSize,
22568        colors: &theme::ThemeColors,
22569    ) -> impl IntoElement {
22570        let comment_id = comment.id;
22571        let is_editing = inline_editor.is_some();
22572
22573        h_flex()
22574            .w_full()
22575            .items_center()
22576            .gap_2()
22577            .px_2()
22578            .py_1p5()
22579            .rounded_md()
22580            .bg(colors.surface_background)
22581            .child(
22582                div()
22583                    .size(avatar_size)
22584                    .flex_shrink_0()
22585                    .rounded_full()
22586                    .overflow_hidden()
22587                    .child(if let Some(ref avatar_uri) = user_avatar_uri {
22588                        Avatar::new(avatar_uri.clone())
22589                            .size(avatar_size)
22590                            .into_any_element()
22591                    } else {
22592                        Icon::new(IconName::Person)
22593                            .size(IconSize::Small)
22594                            .color(ui::Color::Muted)
22595                            .into_any_element()
22596                    }),
22597            )
22598            .child(if let Some(editor) = inline_editor {
22599                // Inline edit mode: show an editable text field
22600                div()
22601                    .flex_1()
22602                    .border_1()
22603                    .border_color(colors.border)
22604                    .rounded_md()
22605                    .bg(colors.editor_background)
22606                    .px_2()
22607                    .py_1()
22608                    .child(editor)
22609                    .into_any_element()
22610            } else {
22611                // Display mode: show the comment text
22612                div()
22613                    .flex_1()
22614                    .text_sm()
22615                    .text_color(colors.text)
22616                    .child(comment.comment)
22617                    .into_any_element()
22618            })
22619            .child(if is_editing {
22620                // Editing mode: show close and confirm buttons
22621                h_flex()
22622                    .gap_1()
22623                    .child(
22624                        IconButton::new(
22625                            format!("diff-review-cancel-edit-{comment_id}"),
22626                            IconName::Close,
22627                        )
22628                        .icon_color(ui::Color::Muted)
22629                        .icon_size(action_icon_size)
22630                        .tooltip(Tooltip::text("Cancel"))
22631                        .on_click(move |_, window, cx| {
22632                            window.dispatch_action(
22633                                Box::new(crate::actions::CancelEditReviewComment {
22634                                    id: comment_id,
22635                                }),
22636                                cx,
22637                            );
22638                        }),
22639                    )
22640                    .child(
22641                        IconButton::new(
22642                            format!("diff-review-confirm-edit-{comment_id}"),
22643                            IconName::Return,
22644                        )
22645                        .icon_color(ui::Color::Muted)
22646                        .icon_size(action_icon_size)
22647                        .tooltip(Tooltip::text("Confirm"))
22648                        .on_click(move |_, window, cx| {
22649                            window.dispatch_action(
22650                                Box::new(crate::actions::ConfirmEditReviewComment {
22651                                    id: comment_id,
22652                                }),
22653                                cx,
22654                            );
22655                        }),
22656                    )
22657                    .into_any_element()
22658            } else {
22659                // Display mode: no action buttons for now (edit/delete not yet implemented)
22660                gpui::Empty.into_any_element()
22661            })
22662    }
22663
22664    pub fn set_masked(&mut self, masked: bool, cx: &mut Context<Self>) {
22665        if self.display_map.read(cx).masked != masked {
22666            self.display_map.update(cx, |map, _| map.masked = masked);
22667        }
22668        cx.notify()
22669    }
22670
22671    pub fn set_show_wrap_guides(&mut self, show_wrap_guides: bool, cx: &mut Context<Self>) {
22672        self.show_wrap_guides = Some(show_wrap_guides);
22673        cx.notify();
22674    }
22675
22676    pub fn set_show_indent_guides(&mut self, show_indent_guides: bool, cx: &mut Context<Self>) {
22677        self.show_indent_guides = Some(show_indent_guides);
22678        cx.notify();
22679    }
22680
22681    pub fn working_directory(&self, cx: &App) -> Option<PathBuf> {
22682        if let Some(buffer) = self.buffer().read(cx).as_singleton() {
22683            if let Some(file) = buffer.read(cx).file().and_then(|f| f.as_local())
22684                && let Some(dir) = file.abs_path(cx).parent()
22685            {
22686                return Some(dir.to_owned());
22687            }
22688        }
22689
22690        None
22691    }
22692
22693    fn target_file<'a>(&self, cx: &'a App) -> Option<&'a dyn language::LocalFile> {
22694        self.active_excerpt(cx)?
22695            .1
22696            .read(cx)
22697            .file()
22698            .and_then(|f| f.as_local())
22699    }
22700
22701    pub fn target_file_abs_path(&self, cx: &mut Context<Self>) -> Option<PathBuf> {
22702        self.active_excerpt(cx).and_then(|(_, buffer, _)| {
22703            let buffer = buffer.read(cx);
22704            if let Some(project_path) = buffer.project_path(cx) {
22705                let project = self.project()?.read(cx);
22706                project.absolute_path(&project_path, cx)
22707            } else {
22708                buffer
22709                    .file()
22710                    .and_then(|file| file.as_local().map(|file| file.abs_path(cx)))
22711            }
22712        })
22713    }
22714
22715    pub fn reveal_in_finder(
22716        &mut self,
22717        _: &RevealInFileManager,
22718        _window: &mut Window,
22719        cx: &mut Context<Self>,
22720    ) {
22721        if let Some(path) = self.target_file_abs_path(cx) {
22722            if let Some(project) = self.project() {
22723                project.update(cx, |project, cx| project.reveal_path(&path, cx));
22724            } else {
22725                cx.reveal_path(&path);
22726            }
22727        }
22728    }
22729
22730    pub fn copy_path(
22731        &mut self,
22732        _: &zed_actions::workspace::CopyPath,
22733        _window: &mut Window,
22734        cx: &mut Context<Self>,
22735    ) {
22736        if let Some(path) = self.target_file_abs_path(cx)
22737            && let Some(path) = path.to_str()
22738        {
22739            cx.write_to_clipboard(ClipboardItem::new_string(path.to_string()));
22740        } else {
22741            cx.propagate();
22742        }
22743    }
22744
22745    pub fn copy_relative_path(
22746        &mut self,
22747        _: &zed_actions::workspace::CopyRelativePath,
22748        _window: &mut Window,
22749        cx: &mut Context<Self>,
22750    ) {
22751        if let Some(path) = self.active_excerpt(cx).and_then(|(_, buffer, _)| {
22752            let project = self.project()?.read(cx);
22753            let path = buffer.read(cx).file()?.path();
22754            let path = path.display(project.path_style(cx));
22755            Some(path)
22756        }) {
22757            cx.write_to_clipboard(ClipboardItem::new_string(path.to_string()));
22758        } else {
22759            cx.propagate();
22760        }
22761    }
22762
22763    /// Returns the project path for the editor's buffer, if any buffer is
22764    /// opened in the editor.
22765    pub fn project_path(&self, cx: &App) -> Option<ProjectPath> {
22766        if let Some(buffer) = self.buffer.read(cx).as_singleton() {
22767            buffer.read(cx).project_path(cx)
22768        } else {
22769            None
22770        }
22771    }
22772
22773    // Returns true if the editor handled a go-to-line request
22774    pub fn go_to_active_debug_line(&mut self, window: &mut Window, cx: &mut Context<Self>) -> bool {
22775        maybe!({
22776            let breakpoint_store = self.breakpoint_store.as_ref()?;
22777
22778            let Some(active_stack_frame) = breakpoint_store.read(cx).active_position().cloned()
22779            else {
22780                self.clear_row_highlights::<ActiveDebugLine>();
22781                return None;
22782            };
22783
22784            let position = active_stack_frame.position;
22785            let buffer_id = position.buffer_id?;
22786            let snapshot = self
22787                .project
22788                .as_ref()?
22789                .read(cx)
22790                .buffer_for_id(buffer_id, cx)?
22791                .read(cx)
22792                .snapshot();
22793
22794            let mut handled = false;
22795            for (id, ExcerptRange { context, .. }) in
22796                self.buffer.read(cx).excerpts_for_buffer(buffer_id, cx)
22797            {
22798                if context.start.cmp(&position, &snapshot).is_ge()
22799                    || context.end.cmp(&position, &snapshot).is_lt()
22800                {
22801                    continue;
22802                }
22803                let snapshot = self.buffer.read(cx).snapshot(cx);
22804                let multibuffer_anchor = snapshot.anchor_in_excerpt(id, position)?;
22805
22806                handled = true;
22807                self.clear_row_highlights::<ActiveDebugLine>();
22808
22809                self.go_to_line::<ActiveDebugLine>(
22810                    multibuffer_anchor,
22811                    Some(cx.theme().colors().editor_debugger_active_line_background),
22812                    window,
22813                    cx,
22814                );
22815
22816                cx.notify();
22817            }
22818
22819            handled.then_some(())
22820        })
22821        .is_some()
22822    }
22823
22824    pub fn copy_file_name_without_extension(
22825        &mut self,
22826        _: &CopyFileNameWithoutExtension,
22827        _: &mut Window,
22828        cx: &mut Context<Self>,
22829    ) {
22830        if let Some(file_stem) = self.active_excerpt(cx).and_then(|(_, buffer, _)| {
22831            let file = buffer.read(cx).file()?;
22832            file.path().file_stem()
22833        }) {
22834            cx.write_to_clipboard(ClipboardItem::new_string(file_stem.to_string()));
22835        }
22836    }
22837
22838    pub fn copy_file_name(&mut self, _: &CopyFileName, _: &mut Window, cx: &mut Context<Self>) {
22839        if let Some(file_name) = self.active_excerpt(cx).and_then(|(_, buffer, _)| {
22840            let file = buffer.read(cx).file()?;
22841            Some(file.file_name(cx))
22842        }) {
22843            cx.write_to_clipboard(ClipboardItem::new_string(file_name.to_string()));
22844        }
22845    }
22846
22847    pub fn toggle_git_blame(
22848        &mut self,
22849        _: &::git::Blame,
22850        window: &mut Window,
22851        cx: &mut Context<Self>,
22852    ) {
22853        self.show_git_blame_gutter = !self.show_git_blame_gutter;
22854
22855        if self.show_git_blame_gutter && !self.has_blame_entries(cx) {
22856            self.start_git_blame(true, window, cx);
22857        }
22858
22859        cx.notify();
22860    }
22861
22862    pub fn toggle_git_blame_inline(
22863        &mut self,
22864        _: &ToggleGitBlameInline,
22865        window: &mut Window,
22866        cx: &mut Context<Self>,
22867    ) {
22868        self.toggle_git_blame_inline_internal(true, window, cx);
22869        cx.notify();
22870    }
22871
22872    pub fn open_git_blame_commit(
22873        &mut self,
22874        _: &OpenGitBlameCommit,
22875        window: &mut Window,
22876        cx: &mut Context<Self>,
22877    ) {
22878        self.open_git_blame_commit_internal(window, cx);
22879    }
22880
22881    fn open_git_blame_commit_internal(
22882        &mut self,
22883        window: &mut Window,
22884        cx: &mut Context<Self>,
22885    ) -> Option<()> {
22886        let blame = self.blame.as_ref()?;
22887        let snapshot = self.snapshot(window, cx);
22888        let cursor = self
22889            .selections
22890            .newest::<Point>(&snapshot.display_snapshot)
22891            .head();
22892        let (buffer, point, _) = snapshot.buffer_snapshot().point_to_buffer_point(cursor)?;
22893        let (_, blame_entry) = blame
22894            .update(cx, |blame, cx| {
22895                blame
22896                    .blame_for_rows(
22897                        &[RowInfo {
22898                            buffer_id: Some(buffer.remote_id()),
22899                            buffer_row: Some(point.row),
22900                            ..Default::default()
22901                        }],
22902                        cx,
22903                    )
22904                    .next()
22905            })
22906            .flatten()?;
22907        let renderer = cx.global::<GlobalBlameRenderer>().0.clone();
22908        let repo = blame.read(cx).repository(cx, buffer.remote_id())?;
22909        let workspace = self.workspace()?.downgrade();
22910        renderer.open_blame_commit(blame_entry, repo, workspace, window, cx);
22911        None
22912    }
22913
22914    pub fn git_blame_inline_enabled(&self) -> bool {
22915        self.git_blame_inline_enabled
22916    }
22917
22918    pub fn toggle_selection_menu(
22919        &mut self,
22920        _: &ToggleSelectionMenu,
22921        _: &mut Window,
22922        cx: &mut Context<Self>,
22923    ) {
22924        self.show_selection_menu = self
22925            .show_selection_menu
22926            .map(|show_selections_menu| !show_selections_menu)
22927            .or_else(|| Some(!EditorSettings::get_global(cx).toolbar.selections_menu));
22928
22929        cx.notify();
22930    }
22931
22932    pub fn selection_menu_enabled(&self, cx: &App) -> bool {
22933        self.show_selection_menu
22934            .unwrap_or_else(|| EditorSettings::get_global(cx).toolbar.selections_menu)
22935    }
22936
22937    fn start_git_blame(
22938        &mut self,
22939        user_triggered: bool,
22940        window: &mut Window,
22941        cx: &mut Context<Self>,
22942    ) {
22943        if let Some(project) = self.project() {
22944            if let Some(buffer) = self.buffer().read(cx).as_singleton()
22945                && buffer.read(cx).file().is_none()
22946            {
22947                return;
22948            }
22949
22950            let focused = self.focus_handle(cx).contains_focused(window, cx);
22951
22952            let project = project.clone();
22953            let blame = cx
22954                .new(|cx| GitBlame::new(self.buffer.clone(), project, user_triggered, focused, cx));
22955            self.blame_subscription =
22956                Some(cx.observe_in(&blame, window, |_, _, _, cx| cx.notify()));
22957            self.blame = Some(blame);
22958        }
22959    }
22960
22961    fn toggle_git_blame_inline_internal(
22962        &mut self,
22963        user_triggered: bool,
22964        window: &mut Window,
22965        cx: &mut Context<Self>,
22966    ) {
22967        if self.git_blame_inline_enabled {
22968            self.git_blame_inline_enabled = false;
22969            self.show_git_blame_inline = false;
22970            self.show_git_blame_inline_delay_task.take();
22971        } else {
22972            self.git_blame_inline_enabled = true;
22973            self.start_git_blame_inline(user_triggered, window, cx);
22974        }
22975
22976        cx.notify();
22977    }
22978
22979    fn start_git_blame_inline(
22980        &mut self,
22981        user_triggered: bool,
22982        window: &mut Window,
22983        cx: &mut Context<Self>,
22984    ) {
22985        self.start_git_blame(user_triggered, window, cx);
22986
22987        if ProjectSettings::get_global(cx)
22988            .git
22989            .inline_blame_delay()
22990            .is_some()
22991        {
22992            self.start_inline_blame_timer(window, cx);
22993        } else {
22994            self.show_git_blame_inline = true
22995        }
22996    }
22997
22998    pub fn blame(&self) -> Option<&Entity<GitBlame>> {
22999        self.blame.as_ref()
23000    }
23001
23002    pub fn show_git_blame_gutter(&self) -> bool {
23003        self.show_git_blame_gutter
23004    }
23005
23006    pub fn render_git_blame_gutter(&self, cx: &App) -> bool {
23007        !self.mode().is_minimap() && self.show_git_blame_gutter && self.has_blame_entries(cx)
23008    }
23009
23010    pub fn render_git_blame_inline(&self, window: &Window, cx: &App) -> bool {
23011        self.show_git_blame_inline
23012            && (self.focus_handle.is_focused(window) || self.inline_blame_popover.is_some())
23013            && !self.newest_selection_head_on_empty_line(cx)
23014            && self.has_blame_entries(cx)
23015    }
23016
23017    fn has_blame_entries(&self, cx: &App) -> bool {
23018        self.blame()
23019            .is_some_and(|blame| blame.read(cx).has_generated_entries())
23020    }
23021
23022    fn newest_selection_head_on_empty_line(&self, cx: &App) -> bool {
23023        let cursor_anchor = self.selections.newest_anchor().head();
23024
23025        let snapshot = self.buffer.read(cx).snapshot(cx);
23026        let buffer_row = MultiBufferRow(cursor_anchor.to_point(&snapshot).row);
23027
23028        snapshot.line_len(buffer_row) == 0
23029    }
23030
23031    fn get_permalink_to_line(&self, cx: &mut Context<Self>) -> Task<Result<url::Url>> {
23032        let buffer_and_selection = maybe!({
23033            let selection = self.selections.newest::<Point>(&self.display_snapshot(cx));
23034            let selection_range = selection.range();
23035
23036            let multi_buffer = self.buffer().read(cx);
23037            let multi_buffer_snapshot = multi_buffer.snapshot(cx);
23038            let buffer_ranges = multi_buffer_snapshot
23039                .range_to_buffer_ranges(selection_range.start..=selection_range.end);
23040
23041            let (buffer, range, _) = if selection.reversed {
23042                buffer_ranges.first()
23043            } else {
23044                buffer_ranges.last()
23045            }?;
23046
23047            let buffer_range = range.to_point(buffer);
23048
23049            let Some(buffer_diff) = multi_buffer.diff_for(buffer.remote_id()) else {
23050                return Some((
23051                    multi_buffer.buffer(buffer.remote_id()).unwrap(),
23052                    buffer_range.start.row..buffer_range.end.row,
23053                ));
23054            };
23055
23056            let buffer_diff_snapshot = buffer_diff.read(cx).snapshot(cx);
23057            let start =
23058                buffer_diff_snapshot.buffer_point_to_base_text_point(buffer_range.start, buffer);
23059            let end =
23060                buffer_diff_snapshot.buffer_point_to_base_text_point(buffer_range.end, buffer);
23061
23062            Some((
23063                multi_buffer.buffer(buffer.remote_id()).unwrap(),
23064                start.row..end.row,
23065            ))
23066        });
23067
23068        let Some((buffer, selection)) = buffer_and_selection else {
23069            return Task::ready(Err(anyhow!("failed to determine buffer and selection")));
23070        };
23071
23072        let Some(project) = self.project() else {
23073            return Task::ready(Err(anyhow!("editor does not have project")));
23074        };
23075
23076        project.update(cx, |project, cx| {
23077            project.get_permalink_to_line(&buffer, selection, cx)
23078        })
23079    }
23080
23081    pub fn copy_permalink_to_line(
23082        &mut self,
23083        _: &CopyPermalinkToLine,
23084        window: &mut Window,
23085        cx: &mut Context<Self>,
23086    ) {
23087        let permalink_task = self.get_permalink_to_line(cx);
23088        let workspace = self.workspace();
23089
23090        cx.spawn_in(window, async move |_, cx| match permalink_task.await {
23091            Ok(permalink) => {
23092                cx.update(|_, cx| {
23093                    cx.write_to_clipboard(ClipboardItem::new_string(permalink.to_string()));
23094                })
23095                .ok();
23096            }
23097            Err(err) => {
23098                let message = format!("Failed to copy permalink: {err}");
23099
23100                anyhow::Result::<()>::Err(err).log_err();
23101
23102                if let Some(workspace) = workspace {
23103                    workspace
23104                        .update_in(cx, |workspace, _, cx| {
23105                            struct CopyPermalinkToLine;
23106
23107                            workspace.show_toast(
23108                                Toast::new(
23109                                    NotificationId::unique::<CopyPermalinkToLine>(),
23110                                    message,
23111                                ),
23112                                cx,
23113                            )
23114                        })
23115                        .ok();
23116                }
23117            }
23118        })
23119        .detach();
23120    }
23121
23122    pub fn copy_file_location(
23123        &mut self,
23124        _: &CopyFileLocation,
23125        _: &mut Window,
23126        cx: &mut Context<Self>,
23127    ) {
23128        let selection = self
23129            .selections
23130            .newest::<Point>(&self.display_snapshot(cx))
23131            .start
23132            .row
23133            + 1;
23134        if let Some(file_location) = self.active_excerpt(cx).and_then(|(_, buffer, _)| {
23135            let project = self.project()?.read(cx);
23136            let file = buffer.read(cx).file()?;
23137            let path = file.path().display(project.path_style(cx));
23138
23139            Some(format!("{path}:{selection}"))
23140        }) {
23141            cx.write_to_clipboard(ClipboardItem::new_string(file_location));
23142        }
23143    }
23144
23145    pub fn open_permalink_to_line(
23146        &mut self,
23147        _: &OpenPermalinkToLine,
23148        window: &mut Window,
23149        cx: &mut Context<Self>,
23150    ) {
23151        let permalink_task = self.get_permalink_to_line(cx);
23152        let workspace = self.workspace();
23153
23154        cx.spawn_in(window, async move |_, cx| match permalink_task.await {
23155            Ok(permalink) => {
23156                cx.update(|_, cx| {
23157                    cx.open_url(permalink.as_ref());
23158                })
23159                .ok();
23160            }
23161            Err(err) => {
23162                let message = format!("Failed to open permalink: {err}");
23163
23164                anyhow::Result::<()>::Err(err).log_err();
23165
23166                if let Some(workspace) = workspace {
23167                    workspace.update(cx, |workspace, cx| {
23168                        struct OpenPermalinkToLine;
23169
23170                        workspace.show_toast(
23171                            Toast::new(NotificationId::unique::<OpenPermalinkToLine>(), message),
23172                            cx,
23173                        )
23174                    });
23175                }
23176            }
23177        })
23178        .detach();
23179    }
23180
23181    pub fn insert_uuid_v4(
23182        &mut self,
23183        _: &InsertUuidV4,
23184        window: &mut Window,
23185        cx: &mut Context<Self>,
23186    ) {
23187        self.insert_uuid(UuidVersion::V4, window, cx);
23188    }
23189
23190    pub fn insert_uuid_v7(
23191        &mut self,
23192        _: &InsertUuidV7,
23193        window: &mut Window,
23194        cx: &mut Context<Self>,
23195    ) {
23196        self.insert_uuid(UuidVersion::V7, window, cx);
23197    }
23198
23199    fn insert_uuid(&mut self, version: UuidVersion, window: &mut Window, cx: &mut Context<Self>) {
23200        self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
23201        self.transact(window, cx, |this, window, cx| {
23202            let edits = this
23203                .selections
23204                .all::<Point>(&this.display_snapshot(cx))
23205                .into_iter()
23206                .map(|selection| {
23207                    let uuid = match version {
23208                        UuidVersion::V4 => uuid::Uuid::new_v4(),
23209                        UuidVersion::V7 => uuid::Uuid::now_v7(),
23210                    };
23211
23212                    (selection.range(), uuid.to_string())
23213                });
23214            this.edit(edits, cx);
23215            this.refresh_edit_prediction(true, false, window, cx);
23216        });
23217    }
23218
23219    pub fn open_selections_in_multibuffer(
23220        &mut self,
23221        _: &OpenSelectionsInMultibuffer,
23222        window: &mut Window,
23223        cx: &mut Context<Self>,
23224    ) {
23225        let multibuffer = self.buffer.read(cx);
23226
23227        let Some(buffer) = multibuffer.as_singleton() else {
23228            return;
23229        };
23230
23231        let Some(workspace) = self.workspace() else {
23232            return;
23233        };
23234
23235        let title = multibuffer.title(cx).to_string();
23236
23237        let locations = self
23238            .selections
23239            .all_anchors(&self.display_snapshot(cx))
23240            .iter()
23241            .map(|selection| {
23242                (
23243                    buffer.clone(),
23244                    (selection.start.text_anchor..selection.end.text_anchor)
23245                        .to_point(buffer.read(cx)),
23246                )
23247            })
23248            .into_group_map();
23249
23250        cx.spawn_in(window, async move |_, cx| {
23251            workspace.update_in(cx, |workspace, window, cx| {
23252                Self::open_locations_in_multibuffer(
23253                    workspace,
23254                    locations,
23255                    format!("Selections for '{title}'"),
23256                    false,
23257                    false,
23258                    MultibufferSelectionMode::All,
23259                    window,
23260                    cx,
23261                );
23262            })
23263        })
23264        .detach();
23265    }
23266
23267    /// Adds a row highlight for the given range. If a row has multiple highlights, the
23268    /// last highlight added will be used.
23269    ///
23270    /// If the range ends at the beginning of a line, then that line will not be highlighted.
23271    pub fn highlight_rows<T: 'static>(
23272        &mut self,
23273        range: Range<Anchor>,
23274        color: Hsla,
23275        options: RowHighlightOptions,
23276        cx: &mut Context<Self>,
23277    ) {
23278        let snapshot = self.buffer().read(cx).snapshot(cx);
23279        let row_highlights = self.highlighted_rows.entry(TypeId::of::<T>()).or_default();
23280        let ix = row_highlights.binary_search_by(|highlight| {
23281            Ordering::Equal
23282                .then_with(|| highlight.range.start.cmp(&range.start, &snapshot))
23283                .then_with(|| highlight.range.end.cmp(&range.end, &snapshot))
23284        });
23285
23286        if let Err(mut ix) = ix {
23287            let index = post_inc(&mut self.highlight_order);
23288
23289            // If this range intersects with the preceding highlight, then merge it with
23290            // the preceding highlight. Otherwise insert a new highlight.
23291            let mut merged = false;
23292            if ix > 0 {
23293                let prev_highlight = &mut row_highlights[ix - 1];
23294                if prev_highlight
23295                    .range
23296                    .end
23297                    .cmp(&range.start, &snapshot)
23298                    .is_ge()
23299                {
23300                    ix -= 1;
23301                    if prev_highlight.range.end.cmp(&range.end, &snapshot).is_lt() {
23302                        prev_highlight.range.end = range.end;
23303                    }
23304                    merged = true;
23305                    prev_highlight.index = index;
23306                    prev_highlight.color = color;
23307                    prev_highlight.options = options;
23308                }
23309            }
23310
23311            if !merged {
23312                row_highlights.insert(
23313                    ix,
23314                    RowHighlight {
23315                        range,
23316                        index,
23317                        color,
23318                        options,
23319                        type_id: TypeId::of::<T>(),
23320                    },
23321                );
23322            }
23323
23324            // If any of the following highlights intersect with this one, merge them.
23325            while let Some(next_highlight) = row_highlights.get(ix + 1) {
23326                let highlight = &row_highlights[ix];
23327                if next_highlight
23328                    .range
23329                    .start
23330                    .cmp(&highlight.range.end, &snapshot)
23331                    .is_le()
23332                {
23333                    if next_highlight
23334                        .range
23335                        .end
23336                        .cmp(&highlight.range.end, &snapshot)
23337                        .is_gt()
23338                    {
23339                        row_highlights[ix].range.end = next_highlight.range.end;
23340                    }
23341                    row_highlights.remove(ix + 1);
23342                } else {
23343                    break;
23344                }
23345            }
23346        }
23347    }
23348
23349    /// Remove any highlighted row ranges of the given type that intersect the
23350    /// given ranges.
23351    pub fn remove_highlighted_rows<T: 'static>(
23352        &mut self,
23353        ranges_to_remove: Vec<Range<Anchor>>,
23354        cx: &mut Context<Self>,
23355    ) {
23356        let snapshot = self.buffer().read(cx).snapshot(cx);
23357        let row_highlights = self.highlighted_rows.entry(TypeId::of::<T>()).or_default();
23358        let mut ranges_to_remove = ranges_to_remove.iter().peekable();
23359        row_highlights.retain(|highlight| {
23360            while let Some(range_to_remove) = ranges_to_remove.peek() {
23361                match range_to_remove.end.cmp(&highlight.range.start, &snapshot) {
23362                    Ordering::Less | Ordering::Equal => {
23363                        ranges_to_remove.next();
23364                    }
23365                    Ordering::Greater => {
23366                        match range_to_remove.start.cmp(&highlight.range.end, &snapshot) {
23367                            Ordering::Less | Ordering::Equal => {
23368                                return false;
23369                            }
23370                            Ordering::Greater => break,
23371                        }
23372                    }
23373                }
23374            }
23375
23376            true
23377        })
23378    }
23379
23380    /// Clear all anchor ranges for a certain highlight context type, so no corresponding rows will be highlighted.
23381    pub fn clear_row_highlights<T: 'static>(&mut self) {
23382        self.highlighted_rows.remove(&TypeId::of::<T>());
23383    }
23384
23385    /// For a highlight given context type, gets all anchor ranges that will be used for row highlighting.
23386    pub fn highlighted_rows<T: 'static>(&self) -> impl '_ + Iterator<Item = (Range<Anchor>, Hsla)> {
23387        self.highlighted_rows
23388            .get(&TypeId::of::<T>())
23389            .map_or(&[] as &[_], |vec| vec.as_slice())
23390            .iter()
23391            .map(|highlight| (highlight.range.clone(), highlight.color))
23392    }
23393
23394    /// Merges all anchor ranges for all context types ever set, picking the last highlight added in case of a row conflict.
23395    /// Returns a map of display rows that are highlighted and their corresponding highlight color.
23396    /// Allows to ignore certain kinds of highlights.
23397    pub fn highlighted_display_rows(
23398        &self,
23399        window: &mut Window,
23400        cx: &mut App,
23401    ) -> BTreeMap<DisplayRow, LineHighlight> {
23402        let snapshot = self.snapshot(window, cx);
23403        let mut used_highlight_orders = HashMap::default();
23404        self.highlighted_rows
23405            .iter()
23406            .flat_map(|(_, highlighted_rows)| highlighted_rows.iter())
23407            .fold(
23408                BTreeMap::<DisplayRow, LineHighlight>::new(),
23409                |mut unique_rows, highlight| {
23410                    let start = highlight.range.start.to_display_point(&snapshot);
23411                    let end = highlight.range.end.to_display_point(&snapshot);
23412                    let start_row = start.row().0;
23413                    let end_row = if !highlight.range.end.text_anchor.is_max() && end.column() == 0
23414                    {
23415                        end.row().0.saturating_sub(1)
23416                    } else {
23417                        end.row().0
23418                    };
23419                    for row in start_row..=end_row {
23420                        let used_index =
23421                            used_highlight_orders.entry(row).or_insert(highlight.index);
23422                        if highlight.index >= *used_index {
23423                            *used_index = highlight.index;
23424                            unique_rows.insert(
23425                                DisplayRow(row),
23426                                LineHighlight {
23427                                    include_gutter: highlight.options.include_gutter,
23428                                    border: None,
23429                                    background: highlight.color.into(),
23430                                    type_id: Some(highlight.type_id),
23431                                },
23432                            );
23433                        }
23434                    }
23435                    unique_rows
23436                },
23437            )
23438    }
23439
23440    pub fn highlighted_display_row_for_autoscroll(
23441        &self,
23442        snapshot: &DisplaySnapshot,
23443    ) -> Option<DisplayRow> {
23444        self.highlighted_rows
23445            .values()
23446            .flat_map(|highlighted_rows| highlighted_rows.iter())
23447            .filter_map(|highlight| {
23448                if highlight.options.autoscroll {
23449                    Some(highlight.range.start.to_display_point(snapshot).row())
23450                } else {
23451                    None
23452                }
23453            })
23454            .min()
23455    }
23456
23457    pub fn set_search_within_ranges(&mut self, ranges: &[Range<Anchor>], cx: &mut Context<Self>) {
23458        self.highlight_background(
23459            HighlightKey::SearchWithinRange,
23460            ranges,
23461            |_, colors| colors.colors().editor_document_highlight_read_background,
23462            cx,
23463        )
23464    }
23465
23466    pub fn set_breadcrumb_header(&mut self, new_header: String) {
23467        self.breadcrumb_header = Some(new_header);
23468    }
23469
23470    pub fn clear_search_within_ranges(&mut self, cx: &mut Context<Self>) {
23471        self.clear_background_highlights(HighlightKey::SearchWithinRange, cx);
23472    }
23473
23474    pub fn highlight_background(
23475        &mut self,
23476        key: HighlightKey,
23477        ranges: &[Range<Anchor>],
23478        color_fetcher: impl Fn(&usize, &Theme) -> Hsla + Send + Sync + 'static,
23479        cx: &mut Context<Self>,
23480    ) {
23481        self.background_highlights
23482            .insert(key, (Arc::new(color_fetcher), Arc::from(ranges)));
23483        self.scrollbar_marker_state.dirty = true;
23484        cx.notify();
23485    }
23486
23487    pub fn highlight_background_key(
23488        &mut self,
23489        key: HighlightKey,
23490        ranges: &[Range<Anchor>],
23491        color_fetcher: impl Fn(&usize, &Theme) -> Hsla + Send + Sync + 'static,
23492        cx: &mut Context<Self>,
23493    ) {
23494        self.background_highlights
23495            .insert(key, (Arc::new(color_fetcher), Arc::from(ranges)));
23496        self.scrollbar_marker_state.dirty = true;
23497        cx.notify();
23498    }
23499
23500    pub fn clear_background_highlights(
23501        &mut self,
23502        key: HighlightKey,
23503        cx: &mut Context<Self>,
23504    ) -> Option<BackgroundHighlight> {
23505        let text_highlights = self.background_highlights.remove(&key)?;
23506        if !text_highlights.1.is_empty() {
23507            self.scrollbar_marker_state.dirty = true;
23508            cx.notify();
23509        }
23510        Some(text_highlights)
23511    }
23512
23513    pub fn highlight_gutter<T: 'static>(
23514        &mut self,
23515        ranges: impl Into<Vec<Range<Anchor>>>,
23516        color_fetcher: fn(&App) -> Hsla,
23517        cx: &mut Context<Self>,
23518    ) {
23519        self.gutter_highlights
23520            .insert(TypeId::of::<T>(), (color_fetcher, ranges.into()));
23521        cx.notify();
23522    }
23523
23524    pub fn clear_gutter_highlights<T: 'static>(
23525        &mut self,
23526        cx: &mut Context<Self>,
23527    ) -> Option<GutterHighlight> {
23528        cx.notify();
23529        self.gutter_highlights.remove(&TypeId::of::<T>())
23530    }
23531
23532    pub fn insert_gutter_highlight<T: 'static>(
23533        &mut self,
23534        range: Range<Anchor>,
23535        color_fetcher: fn(&App) -> Hsla,
23536        cx: &mut Context<Self>,
23537    ) {
23538        let snapshot = self.buffer().read(cx).snapshot(cx);
23539        let mut highlights = self
23540            .gutter_highlights
23541            .remove(&TypeId::of::<T>())
23542            .map(|(_, highlights)| highlights)
23543            .unwrap_or_default();
23544        let ix = highlights.binary_search_by(|highlight| {
23545            Ordering::Equal
23546                .then_with(|| highlight.start.cmp(&range.start, &snapshot))
23547                .then_with(|| highlight.end.cmp(&range.end, &snapshot))
23548        });
23549        if let Err(ix) = ix {
23550            highlights.insert(ix, range);
23551        }
23552        self.gutter_highlights
23553            .insert(TypeId::of::<T>(), (color_fetcher, highlights));
23554    }
23555
23556    pub fn remove_gutter_highlights<T: 'static>(
23557        &mut self,
23558        ranges_to_remove: Vec<Range<Anchor>>,
23559        cx: &mut Context<Self>,
23560    ) {
23561        let snapshot = self.buffer().read(cx).snapshot(cx);
23562        let Some((color_fetcher, mut gutter_highlights)) =
23563            self.gutter_highlights.remove(&TypeId::of::<T>())
23564        else {
23565            return;
23566        };
23567        let mut ranges_to_remove = ranges_to_remove.iter().peekable();
23568        gutter_highlights.retain(|highlight| {
23569            while let Some(range_to_remove) = ranges_to_remove.peek() {
23570                match range_to_remove.end.cmp(&highlight.start, &snapshot) {
23571                    Ordering::Less | Ordering::Equal => {
23572                        ranges_to_remove.next();
23573                    }
23574                    Ordering::Greater => {
23575                        match range_to_remove.start.cmp(&highlight.end, &snapshot) {
23576                            Ordering::Less | Ordering::Equal => {
23577                                return false;
23578                            }
23579                            Ordering::Greater => break,
23580                        }
23581                    }
23582                }
23583            }
23584
23585            true
23586        });
23587        self.gutter_highlights
23588            .insert(TypeId::of::<T>(), (color_fetcher, gutter_highlights));
23589    }
23590
23591    #[cfg(feature = "test-support")]
23592    pub fn all_text_highlights(
23593        &self,
23594        window: &mut Window,
23595        cx: &mut Context<Self>,
23596    ) -> Vec<(HighlightStyle, Vec<Range<DisplayPoint>>)> {
23597        let snapshot = self.snapshot(window, cx);
23598        self.display_map.update(cx, |display_map, _| {
23599            display_map
23600                .all_text_highlights()
23601                .map(|(_, highlight)| {
23602                    let (style, ranges) = highlight.as_ref();
23603                    (
23604                        *style,
23605                        ranges
23606                            .iter()
23607                            .map(|range| range.clone().to_display_points(&snapshot))
23608                            .collect(),
23609                    )
23610                })
23611                .collect()
23612        })
23613    }
23614
23615    #[cfg(feature = "test-support")]
23616    pub fn all_text_background_highlights(
23617        &self,
23618        window: &mut Window,
23619        cx: &mut Context<Self>,
23620    ) -> Vec<(Range<DisplayPoint>, Hsla)> {
23621        let snapshot = self.snapshot(window, cx);
23622        let buffer = &snapshot.buffer_snapshot();
23623        let start = buffer.anchor_before(MultiBufferOffset(0));
23624        let end = buffer.anchor_after(buffer.len());
23625        self.sorted_background_highlights_in_range(start..end, &snapshot, cx.theme())
23626    }
23627
23628    #[cfg(any(test, feature = "test-support"))]
23629    pub fn sorted_background_highlights_in_range(
23630        &self,
23631        search_range: Range<Anchor>,
23632        display_snapshot: &DisplaySnapshot,
23633        theme: &Theme,
23634    ) -> Vec<(Range<DisplayPoint>, Hsla)> {
23635        let mut res = self.background_highlights_in_range(search_range, display_snapshot, theme);
23636        res.sort_by(|a, b| {
23637            a.0.start
23638                .cmp(&b.0.start)
23639                .then_with(|| a.0.end.cmp(&b.0.end))
23640                .then_with(|| a.1.cmp(&b.1))
23641        });
23642        res
23643    }
23644
23645    #[cfg(feature = "test-support")]
23646    pub fn search_background_highlights(&mut self, cx: &mut Context<Self>) -> Vec<Range<Point>> {
23647        let snapshot = self.buffer().read(cx).snapshot(cx);
23648
23649        let highlights = self
23650            .background_highlights
23651            .get(&HighlightKey::BufferSearchHighlights);
23652
23653        if let Some((_color, ranges)) = highlights {
23654            ranges
23655                .iter()
23656                .map(|range| range.start.to_point(&snapshot)..range.end.to_point(&snapshot))
23657                .collect_vec()
23658        } else {
23659            vec![]
23660        }
23661    }
23662
23663    fn document_highlights_for_position<'a>(
23664        &'a self,
23665        position: Anchor,
23666        buffer: &'a MultiBufferSnapshot,
23667    ) -> impl 'a + Iterator<Item = &'a Range<Anchor>> {
23668        let read_highlights = self
23669            .background_highlights
23670            .get(&HighlightKey::DocumentHighlightRead)
23671            .map(|h| &h.1);
23672        let write_highlights = self
23673            .background_highlights
23674            .get(&HighlightKey::DocumentHighlightWrite)
23675            .map(|h| &h.1);
23676        let left_position = position.bias_left(buffer);
23677        let right_position = position.bias_right(buffer);
23678        read_highlights
23679            .into_iter()
23680            .chain(write_highlights)
23681            .flat_map(move |ranges| {
23682                let start_ix = match ranges.binary_search_by(|probe| {
23683                    let cmp = probe.end.cmp(&left_position, buffer);
23684                    if cmp.is_ge() {
23685                        Ordering::Greater
23686                    } else {
23687                        Ordering::Less
23688                    }
23689                }) {
23690                    Ok(i) | Err(i) => i,
23691                };
23692
23693                ranges[start_ix..]
23694                    .iter()
23695                    .take_while(move |range| range.start.cmp(&right_position, buffer).is_le())
23696            })
23697    }
23698
23699    pub fn has_background_highlights(&self, key: HighlightKey) -> bool {
23700        self.background_highlights
23701            .get(&key)
23702            .is_some_and(|(_, highlights)| !highlights.is_empty())
23703    }
23704
23705    /// Returns all background highlights for a given range.
23706    ///
23707    /// The order of highlights is not deterministic, do sort the ranges if needed for the logic.
23708    pub fn background_highlights_in_range(
23709        &self,
23710        search_range: Range<Anchor>,
23711        display_snapshot: &DisplaySnapshot,
23712        theme: &Theme,
23713    ) -> Vec<(Range<DisplayPoint>, Hsla)> {
23714        let mut results = Vec::new();
23715        for (color_fetcher, ranges) in self.background_highlights.values() {
23716            let start_ix = match ranges.binary_search_by(|probe| {
23717                let cmp = probe
23718                    .end
23719                    .cmp(&search_range.start, &display_snapshot.buffer_snapshot());
23720                if cmp.is_gt() {
23721                    Ordering::Greater
23722                } else {
23723                    Ordering::Less
23724                }
23725            }) {
23726                Ok(i) | Err(i) => i,
23727            };
23728            for (index, range) in ranges[start_ix..].iter().enumerate() {
23729                if range
23730                    .start
23731                    .cmp(&search_range.end, &display_snapshot.buffer_snapshot())
23732                    .is_ge()
23733                {
23734                    break;
23735                }
23736
23737                let color = color_fetcher(&(start_ix + index), theme);
23738                let start = range.start.to_display_point(display_snapshot);
23739                let end = range.end.to_display_point(display_snapshot);
23740                results.push((start..end, color))
23741            }
23742        }
23743        results
23744    }
23745
23746    pub fn gutter_highlights_in_range(
23747        &self,
23748        search_range: Range<Anchor>,
23749        display_snapshot: &DisplaySnapshot,
23750        cx: &App,
23751    ) -> Vec<(Range<DisplayPoint>, Hsla)> {
23752        let mut results = Vec::new();
23753        for (color_fetcher, ranges) in self.gutter_highlights.values() {
23754            let color = color_fetcher(cx);
23755            let start_ix = match ranges.binary_search_by(|probe| {
23756                let cmp = probe
23757                    .end
23758                    .cmp(&search_range.start, &display_snapshot.buffer_snapshot());
23759                if cmp.is_gt() {
23760                    Ordering::Greater
23761                } else {
23762                    Ordering::Less
23763                }
23764            }) {
23765                Ok(i) | Err(i) => i,
23766            };
23767            for range in &ranges[start_ix..] {
23768                if range
23769                    .start
23770                    .cmp(&search_range.end, &display_snapshot.buffer_snapshot())
23771                    .is_ge()
23772                {
23773                    break;
23774                }
23775
23776                let start = range.start.to_display_point(display_snapshot);
23777                let end = range.end.to_display_point(display_snapshot);
23778                results.push((start..end, color))
23779            }
23780        }
23781        results
23782    }
23783
23784    /// Get the text ranges corresponding to the redaction query
23785    pub fn redacted_ranges(
23786        &self,
23787        search_range: Range<Anchor>,
23788        display_snapshot: &DisplaySnapshot,
23789        cx: &App,
23790    ) -> Vec<Range<DisplayPoint>> {
23791        display_snapshot
23792            .buffer_snapshot()
23793            .redacted_ranges(search_range, |file| {
23794                if let Some(file) = file {
23795                    file.is_private()
23796                        && EditorSettings::get(
23797                            Some(SettingsLocation {
23798                                worktree_id: file.worktree_id(cx),
23799                                path: file.path().as_ref(),
23800                            }),
23801                            cx,
23802                        )
23803                        .redact_private_values
23804                } else {
23805                    false
23806                }
23807            })
23808            .map(|range| {
23809                range.start.to_display_point(display_snapshot)
23810                    ..range.end.to_display_point(display_snapshot)
23811            })
23812            .collect()
23813    }
23814
23815    pub fn highlight_text_key(
23816        &mut self,
23817        key: HighlightKey,
23818        ranges: Vec<Range<Anchor>>,
23819        style: HighlightStyle,
23820        merge: bool,
23821        cx: &mut Context<Self>,
23822    ) {
23823        self.display_map.update(cx, |map, cx| {
23824            map.highlight_text(key, ranges, style, merge, cx);
23825        });
23826        cx.notify();
23827    }
23828
23829    pub fn highlight_text(
23830        &mut self,
23831        key: HighlightKey,
23832        ranges: Vec<Range<Anchor>>,
23833        style: HighlightStyle,
23834        cx: &mut Context<Self>,
23835    ) {
23836        self.display_map.update(cx, |map, cx| {
23837            map.highlight_text(key, ranges, style, false, cx)
23838        });
23839        cx.notify();
23840    }
23841
23842    pub fn text_highlights<'a>(
23843        &'a self,
23844        key: HighlightKey,
23845        cx: &'a App,
23846    ) -> Option<(HighlightStyle, &'a [Range<Anchor>])> {
23847        self.display_map.read(cx).text_highlights(key)
23848    }
23849
23850    pub fn clear_highlights(&mut self, key: HighlightKey, cx: &mut Context<Self>) {
23851        let cleared = self
23852            .display_map
23853            .update(cx, |map, _| map.clear_highlights(key));
23854        if cleared {
23855            cx.notify();
23856        }
23857    }
23858
23859    pub fn clear_highlights_with(
23860        &mut self,
23861        f: &mut dyn FnMut(&HighlightKey) -> bool,
23862        cx: &mut Context<Self>,
23863    ) {
23864        let cleared = self
23865            .display_map
23866            .update(cx, |map, _| map.clear_highlights_with(f));
23867        if cleared {
23868            cx.notify();
23869        }
23870    }
23871
23872    pub fn show_local_cursors(&self, window: &mut Window, cx: &mut App) -> bool {
23873        (self.read_only(cx) || self.blink_manager.read(cx).visible())
23874            && self.focus_handle.is_focused(window)
23875    }
23876
23877    pub fn set_show_cursor_when_unfocused(&mut self, is_enabled: bool, cx: &mut Context<Self>) {
23878        self.show_cursor_when_unfocused = is_enabled;
23879        cx.notify();
23880    }
23881
23882    fn on_buffer_changed(&mut self, _: Entity<MultiBuffer>, cx: &mut Context<Self>) {
23883        cx.notify();
23884    }
23885
23886    fn on_debug_session_event(
23887        &mut self,
23888        _session: Entity<Session>,
23889        event: &SessionEvent,
23890        cx: &mut Context<Self>,
23891    ) {
23892        if let SessionEvent::InvalidateInlineValue = event {
23893            self.refresh_inline_values(cx);
23894        }
23895    }
23896
23897    pub fn refresh_inline_values(&mut self, cx: &mut Context<Self>) {
23898        let Some(project) = self.project.clone() else {
23899            return;
23900        };
23901
23902        if !self.inline_value_cache.enabled {
23903            let inlays = std::mem::take(&mut self.inline_value_cache.inlays);
23904            self.splice_inlays(&inlays, Vec::new(), cx);
23905            return;
23906        }
23907
23908        let current_execution_position = self
23909            .highlighted_rows
23910            .get(&TypeId::of::<ActiveDebugLine>())
23911            .and_then(|lines| lines.last().map(|line| line.range.end));
23912
23913        self.inline_value_cache.refresh_task = cx.spawn(async move |editor, cx| {
23914            let inline_values = editor
23915                .update(cx, |editor, cx| {
23916                    let Some(current_execution_position) = current_execution_position else {
23917                        return Some(Task::ready(Ok(Vec::new())));
23918                    };
23919
23920                    let buffer = editor.buffer.read_with(cx, |buffer, cx| {
23921                        let snapshot = buffer.snapshot(cx);
23922
23923                        let excerpt = snapshot.excerpt_containing(
23924                            current_execution_position..current_execution_position,
23925                        )?;
23926
23927                        editor.buffer.read(cx).buffer(excerpt.buffer_id())
23928                    })?;
23929
23930                    let range =
23931                        buffer.read(cx).anchor_before(0)..current_execution_position.text_anchor;
23932
23933                    project.inline_values(buffer, range, cx)
23934                })
23935                .ok()
23936                .flatten()?
23937                .await
23938                .context("refreshing debugger inlays")
23939                .log_err()?;
23940
23941            let mut buffer_inline_values: HashMap<BufferId, Vec<InlayHint>> = HashMap::default();
23942
23943            for (buffer_id, inline_value) in inline_values
23944                .into_iter()
23945                .filter_map(|hint| Some((hint.position.buffer_id?, hint)))
23946            {
23947                buffer_inline_values
23948                    .entry(buffer_id)
23949                    .or_default()
23950                    .push(inline_value);
23951            }
23952
23953            editor
23954                .update(cx, |editor, cx| {
23955                    let snapshot = editor.buffer.read(cx).snapshot(cx);
23956                    let mut new_inlays = Vec::default();
23957
23958                    for (excerpt_id, buffer_snapshot, _) in snapshot.excerpts() {
23959                        let buffer_id = buffer_snapshot.remote_id();
23960                        buffer_inline_values
23961                            .get(&buffer_id)
23962                            .into_iter()
23963                            .flatten()
23964                            .for_each(|hint| {
23965                                let inlay = Inlay::debugger(
23966                                    post_inc(&mut editor.next_inlay_id),
23967                                    Anchor::in_buffer(excerpt_id, hint.position),
23968                                    hint.text(),
23969                                );
23970                                if !inlay.text().chars().contains(&'\n') {
23971                                    new_inlays.push(inlay);
23972                                }
23973                            });
23974                    }
23975
23976                    let mut inlay_ids = new_inlays.iter().map(|inlay| inlay.id).collect();
23977                    std::mem::swap(&mut editor.inline_value_cache.inlays, &mut inlay_ids);
23978
23979                    editor.splice_inlays(&inlay_ids, new_inlays, cx);
23980                })
23981                .ok()?;
23982            Some(())
23983        });
23984    }
23985
23986    fn on_buffer_event(
23987        &mut self,
23988        multibuffer: &Entity<MultiBuffer>,
23989        event: &multi_buffer::Event,
23990        window: &mut Window,
23991        cx: &mut Context<Self>,
23992    ) {
23993        match event {
23994            multi_buffer::Event::Edited { edited_buffer } => {
23995                self.scrollbar_marker_state.dirty = true;
23996                self.active_indent_guides_state.dirty = true;
23997                self.refresh_active_diagnostics(cx);
23998                self.refresh_code_actions(window, cx);
23999                self.refresh_single_line_folds(window, cx);
24000                let snapshot = self.snapshot(window, cx);
24001                self.refresh_matching_bracket_highlights(&snapshot, cx);
24002                self.refresh_outline_symbols_at_cursor(cx);
24003                self.refresh_sticky_headers(&snapshot, cx);
24004                if self.has_active_edit_prediction() {
24005                    self.update_visible_edit_prediction(window, cx);
24006                }
24007
24008                // Clean up orphaned review comments after edits
24009                self.cleanup_orphaned_review_comments(cx);
24010
24011                if let Some(buffer) = edited_buffer {
24012                    if buffer.read(cx).file().is_none() {
24013                        cx.emit(EditorEvent::TitleChanged);
24014                    }
24015
24016                    if self.project.is_some() {
24017                        let buffer_id = buffer.read(cx).remote_id();
24018                        self.register_buffer(buffer_id, cx);
24019                        self.update_lsp_data(Some(buffer_id), window, cx);
24020                        self.refresh_inlay_hints(
24021                            InlayHintRefreshReason::BufferEdited(buffer_id),
24022                            cx,
24023                        );
24024                    }
24025                }
24026
24027                cx.emit(EditorEvent::BufferEdited);
24028                cx.emit(SearchEvent::MatchesInvalidated);
24029
24030                let Some(project) = &self.project else { return };
24031                let (telemetry, is_via_ssh) = {
24032                    let project = project.read(cx);
24033                    let telemetry = project.client().telemetry().clone();
24034                    let is_via_ssh = project.is_via_remote_server();
24035                    (telemetry, is_via_ssh)
24036                };
24037                telemetry.log_edit_event("editor", is_via_ssh);
24038            }
24039            multi_buffer::Event::ExcerptsAdded {
24040                buffer,
24041                predecessor,
24042                excerpts,
24043            } => {
24044                self.tasks_update_task = Some(self.refresh_runnables(window, cx));
24045                let buffer_id = buffer.read(cx).remote_id();
24046                if self.buffer.read(cx).diff_for(buffer_id).is_none()
24047                    && let Some(project) = &self.project
24048                {
24049                    update_uncommitted_diff_for_buffer(
24050                        cx.entity(),
24051                        project,
24052                        [buffer.clone()],
24053                        self.buffer.clone(),
24054                        cx,
24055                    )
24056                    .detach();
24057                }
24058                self.semantic_token_state
24059                    .invalidate_buffer(&buffer.read(cx).remote_id());
24060                self.update_lsp_data(Some(buffer_id), window, cx);
24061                self.refresh_inlay_hints(InlayHintRefreshReason::NewLinesShown, cx);
24062                self.colorize_brackets(false, cx);
24063                self.refresh_selected_text_highlights(true, window, cx);
24064                cx.emit(EditorEvent::ExcerptsAdded {
24065                    buffer: buffer.clone(),
24066                    predecessor: *predecessor,
24067                    excerpts: excerpts.clone(),
24068                });
24069            }
24070            multi_buffer::Event::ExcerptsRemoved {
24071                ids,
24072                removed_buffer_ids,
24073            } => {
24074                if let Some(inlay_hints) = &mut self.inlay_hints {
24075                    inlay_hints.remove_inlay_chunk_data(removed_buffer_ids);
24076                }
24077                self.refresh_inlay_hints(InlayHintRefreshReason::ExcerptsRemoved(ids.clone()), cx);
24078                for buffer_id in removed_buffer_ids {
24079                    self.registered_buffers.remove(buffer_id);
24080                    self.tasks
24081                        .retain(|(task_buffer_id, _), _| task_buffer_id != buffer_id);
24082                    self.semantic_token_state.invalidate_buffer(buffer_id);
24083                    self.display_map.update(cx, |display_map, cx| {
24084                        display_map.invalidate_semantic_highlights(*buffer_id);
24085                        display_map.clear_lsp_folding_ranges(*buffer_id, cx);
24086                    });
24087                }
24088                jsx_tag_auto_close::refresh_enabled_in_any_buffer(self, multibuffer, cx);
24089                cx.emit(EditorEvent::ExcerptsRemoved {
24090                    ids: ids.clone(),
24091                    removed_buffer_ids: removed_buffer_ids.clone(),
24092                });
24093            }
24094            multi_buffer::Event::ExcerptsEdited {
24095                excerpt_ids,
24096                buffer_ids,
24097            } => {
24098                self.display_map.update(cx, |map, cx| {
24099                    map.unfold_buffers(buffer_ids.iter().copied(), cx)
24100                });
24101                cx.emit(EditorEvent::ExcerptsEdited {
24102                    ids: excerpt_ids.clone(),
24103                });
24104            }
24105            multi_buffer::Event::ExcerptsExpanded { ids } => {
24106                self.refresh_inlay_hints(InlayHintRefreshReason::NewLinesShown, cx);
24107                self.refresh_document_highlights(cx);
24108                let snapshot = multibuffer.read(cx).snapshot(cx);
24109                for id in ids {
24110                    self.fetched_tree_sitter_chunks.remove(id);
24111                    if let Some(buffer) = snapshot.buffer_for_excerpt(*id) {
24112                        self.semantic_token_state
24113                            .invalidate_buffer(&buffer.remote_id());
24114                    }
24115                }
24116                self.colorize_brackets(false, cx);
24117                self.update_lsp_data(None, window, cx);
24118                cx.emit(EditorEvent::ExcerptsExpanded { ids: ids.clone() })
24119            }
24120            multi_buffer::Event::Reparsed(buffer_id) => {
24121                self.tasks_update_task = Some(self.refresh_runnables(window, cx));
24122                self.refresh_selected_text_highlights(true, window, cx);
24123                self.colorize_brackets(true, cx);
24124                jsx_tag_auto_close::refresh_enabled_in_any_buffer(self, multibuffer, cx);
24125
24126                cx.emit(EditorEvent::Reparsed(*buffer_id));
24127            }
24128            multi_buffer::Event::DiffHunksToggled => {
24129                self.tasks_update_task = Some(self.refresh_runnables(window, cx));
24130            }
24131            multi_buffer::Event::LanguageChanged(buffer_id, is_fresh_language) => {
24132                if !is_fresh_language {
24133                    self.registered_buffers.remove(&buffer_id);
24134                }
24135                jsx_tag_auto_close::refresh_enabled_in_any_buffer(self, multibuffer, cx);
24136                cx.emit(EditorEvent::Reparsed(*buffer_id));
24137                cx.notify();
24138            }
24139            multi_buffer::Event::DirtyChanged => cx.emit(EditorEvent::DirtyChanged),
24140            multi_buffer::Event::Saved => cx.emit(EditorEvent::Saved),
24141            multi_buffer::Event::FileHandleChanged
24142            | multi_buffer::Event::Reloaded
24143            | multi_buffer::Event::BufferDiffChanged => cx.emit(EditorEvent::TitleChanged),
24144            multi_buffer::Event::DiagnosticsUpdated => {
24145                self.update_diagnostics_state(window, cx);
24146            }
24147            _ => {}
24148        };
24149    }
24150
24151    fn update_diagnostics_state(&mut self, window: &mut Window, cx: &mut Context<'_, Editor>) {
24152        if !self.diagnostics_enabled() {
24153            return;
24154        }
24155        self.refresh_active_diagnostics(cx);
24156        self.refresh_inline_diagnostics(true, window, cx);
24157        self.scrollbar_marker_state.dirty = true;
24158        cx.notify();
24159    }
24160
24161    pub fn start_temporary_diff_override(&mut self) {
24162        self.load_diff_task.take();
24163        self.temporary_diff_override = true;
24164    }
24165
24166    pub fn end_temporary_diff_override(&mut self, cx: &mut Context<Self>) {
24167        self.temporary_diff_override = false;
24168        self.set_render_diff_hunk_controls(Arc::new(render_diff_hunk_controls), cx);
24169        self.buffer.update(cx, |buffer, cx| {
24170            buffer.set_all_diff_hunks_collapsed(cx);
24171        });
24172
24173        if let Some(project) = self.project.clone() {
24174            self.load_diff_task = Some(
24175                update_uncommitted_diff_for_buffer(
24176                    cx.entity(),
24177                    &project,
24178                    self.buffer.read(cx).all_buffers(),
24179                    self.buffer.clone(),
24180                    cx,
24181                )
24182                .shared(),
24183            );
24184        }
24185    }
24186
24187    fn on_display_map_changed(
24188        &mut self,
24189        _: Entity<DisplayMap>,
24190        _: &mut Window,
24191        cx: &mut Context<Self>,
24192    ) {
24193        cx.notify();
24194    }
24195
24196    fn fetch_accent_data(&self, cx: &App) -> Option<AccentData> {
24197        if !self.mode.is_full() {
24198            return None;
24199        }
24200
24201        let theme_settings = theme::ThemeSettings::get_global(cx);
24202        let theme = cx.theme();
24203        let accent_colors = theme.accents().clone();
24204
24205        let accent_overrides = theme_settings
24206            .theme_overrides
24207            .get(theme.name.as_ref())
24208            .map(|theme_style| &theme_style.accents)
24209            .into_iter()
24210            .flatten()
24211            .chain(
24212                theme_settings
24213                    .experimental_theme_overrides
24214                    .as_ref()
24215                    .map(|overrides| &overrides.accents)
24216                    .into_iter()
24217                    .flatten(),
24218            )
24219            .flat_map(|accent| accent.0.clone().map(SharedString::from))
24220            .collect();
24221
24222        Some(AccentData {
24223            colors: accent_colors,
24224            overrides: accent_overrides,
24225        })
24226    }
24227
24228    fn fetch_applicable_language_settings(
24229        &self,
24230        cx: &App,
24231    ) -> HashMap<Option<LanguageName>, LanguageSettings> {
24232        if !self.mode.is_full() {
24233            return HashMap::default();
24234        }
24235
24236        self.buffer().read(cx).all_buffers().into_iter().fold(
24237            HashMap::default(),
24238            |mut acc, buffer| {
24239                let buffer = buffer.read(cx);
24240                let language = buffer.language().map(|language| language.name());
24241                if let hash_map::Entry::Vacant(v) = acc.entry(language.clone()) {
24242                    let file = buffer.file();
24243                    v.insert(language_settings(language, file, cx).into_owned());
24244                }
24245                acc
24246            },
24247        )
24248    }
24249
24250    fn settings_changed(&mut self, window: &mut Window, cx: &mut Context<Self>) {
24251        let new_language_settings = self.fetch_applicable_language_settings(cx);
24252        let language_settings_changed = new_language_settings != self.applicable_language_settings;
24253        self.applicable_language_settings = new_language_settings;
24254
24255        let new_accents = self.fetch_accent_data(cx);
24256        let accents_changed = new_accents != self.accent_data;
24257        self.accent_data = new_accents;
24258
24259        if self.diagnostics_enabled() {
24260            let new_severity = EditorSettings::get_global(cx)
24261                .diagnostics_max_severity
24262                .unwrap_or(DiagnosticSeverity::Hint);
24263            self.set_max_diagnostics_severity(new_severity, cx);
24264        }
24265        self.tasks_update_task = Some(self.refresh_runnables(window, cx));
24266        self.update_edit_prediction_settings(cx);
24267        self.refresh_edit_prediction(true, false, window, cx);
24268        self.refresh_inline_values(cx);
24269
24270        let old_cursor_shape = self.cursor_shape;
24271        let old_show_breadcrumbs = self.show_breadcrumbs;
24272
24273        {
24274            let editor_settings = EditorSettings::get_global(cx);
24275            self.scroll_manager.vertical_scroll_margin = editor_settings.vertical_scroll_margin;
24276            self.show_breadcrumbs = editor_settings.toolbar.breadcrumbs;
24277            self.cursor_shape = editor_settings.cursor_shape.unwrap_or_default();
24278            self.hide_mouse_mode = editor_settings.hide_mouse.unwrap_or_default();
24279        }
24280
24281        if old_cursor_shape != self.cursor_shape {
24282            cx.emit(EditorEvent::CursorShapeChanged);
24283        }
24284
24285        if old_show_breadcrumbs != self.show_breadcrumbs {
24286            cx.emit(EditorEvent::BreadcrumbsChanged);
24287        }
24288
24289        let (restore_unsaved_buffers, show_inline_diagnostics, inline_blame_enabled) = {
24290            let project_settings = ProjectSettings::get_global(cx);
24291            (
24292                project_settings.session.restore_unsaved_buffers,
24293                project_settings.diagnostics.inline.enabled,
24294                project_settings.git.inline_blame.enabled,
24295            )
24296        };
24297        self.buffer_serialization = self
24298            .should_serialize_buffer()
24299            .then(|| BufferSerialization::new(restore_unsaved_buffers));
24300
24301        if self.mode.is_full() {
24302            if self.show_inline_diagnostics != show_inline_diagnostics {
24303                self.show_inline_diagnostics = show_inline_diagnostics;
24304                self.refresh_inline_diagnostics(false, window, cx);
24305            }
24306
24307            if self.git_blame_inline_enabled != inline_blame_enabled {
24308                self.toggle_git_blame_inline_internal(false, window, cx);
24309            }
24310
24311            let minimap_settings = EditorSettings::get_global(cx).minimap;
24312            if self.minimap_visibility != MinimapVisibility::Disabled {
24313                if self.minimap_visibility.settings_visibility()
24314                    != minimap_settings.minimap_enabled()
24315                {
24316                    self.set_minimap_visibility(
24317                        MinimapVisibility::for_mode(self.mode(), cx),
24318                        window,
24319                        cx,
24320                    );
24321                } else if let Some(minimap_entity) = self.minimap.as_ref() {
24322                    minimap_entity.update(cx, |minimap_editor, cx| {
24323                        minimap_editor.update_minimap_configuration(minimap_settings, cx)
24324                    })
24325                }
24326            }
24327
24328            if language_settings_changed || accents_changed {
24329                self.colorize_brackets(true, cx);
24330            }
24331
24332            if language_settings_changed {
24333                self.clear_disabled_lsp_folding_ranges(window, cx);
24334                self.refresh_document_symbols(None, cx);
24335            }
24336
24337            if let Some(inlay_splice) = self.colors.as_mut().and_then(|colors| {
24338                colors.render_mode_updated(EditorSettings::get_global(cx).lsp_document_colors)
24339            }) {
24340                if !inlay_splice.is_empty() {
24341                    self.splice_inlays(&inlay_splice.to_remove, inlay_splice.to_insert, cx);
24342                }
24343                self.refresh_document_colors(None, window, cx);
24344            }
24345
24346            self.refresh_inlay_hints(
24347                InlayHintRefreshReason::SettingsChange(inlay_hint_settings(
24348                    self.selections.newest_anchor().head(),
24349                    &self.buffer.read(cx).snapshot(cx),
24350                    cx,
24351                )),
24352                cx,
24353            );
24354
24355            let new_semantic_token_rules = ProjectSettings::get_global(cx)
24356                .global_lsp_settings
24357                .semantic_token_rules
24358                .clone();
24359            let semantic_token_rules_changed = self
24360                .semantic_token_state
24361                .update_rules(new_semantic_token_rules);
24362            if language_settings_changed || semantic_token_rules_changed {
24363                self.invalidate_semantic_tokens(None);
24364                self.refresh_semantic_tokens(None, None, cx);
24365            }
24366        }
24367
24368        cx.notify();
24369    }
24370
24371    fn theme_changed(&mut self, _: &mut Window, cx: &mut Context<Self>) {
24372        if !self.mode.is_full() {
24373            return;
24374        }
24375
24376        let new_accents = self.fetch_accent_data(cx);
24377        if new_accents != self.accent_data {
24378            self.accent_data = new_accents;
24379            self.colorize_brackets(true, cx);
24380        }
24381
24382        self.invalidate_semantic_tokens(None);
24383        self.refresh_semantic_tokens(None, None, cx);
24384    }
24385
24386    pub fn set_searchable(&mut self, searchable: bool) {
24387        self.searchable = searchable;
24388    }
24389
24390    pub fn searchable(&self) -> bool {
24391        self.searchable
24392    }
24393
24394    pub fn open_excerpts_in_split(
24395        &mut self,
24396        _: &OpenExcerptsSplit,
24397        window: &mut Window,
24398        cx: &mut Context<Self>,
24399    ) {
24400        self.open_excerpts_common(None, true, window, cx)
24401    }
24402
24403    pub fn open_excerpts(&mut self, _: &OpenExcerpts, window: &mut Window, cx: &mut Context<Self>) {
24404        self.open_excerpts_common(None, false, window, cx)
24405    }
24406
24407    pub(crate) fn open_excerpts_common(
24408        &mut self,
24409        jump_data: Option<JumpData>,
24410        split: bool,
24411        window: &mut Window,
24412        cx: &mut Context<Self>,
24413    ) {
24414        if self.buffer.read(cx).is_singleton() {
24415            cx.propagate();
24416            return;
24417        }
24418
24419        let mut new_selections_by_buffer = HashMap::default();
24420        match &jump_data {
24421            Some(JumpData::MultiBufferPoint {
24422                excerpt_id,
24423                position,
24424                anchor,
24425                line_offset_from_top,
24426            }) => {
24427                let multi_buffer_snapshot = self.buffer.read(cx).snapshot(cx);
24428                if let Some(buffer) = multi_buffer_snapshot
24429                    .buffer_id_for_excerpt(*excerpt_id)
24430                    .and_then(|buffer_id| self.buffer.read(cx).buffer(buffer_id))
24431                {
24432                    let buffer_snapshot = buffer.read(cx).snapshot();
24433                    let jump_to_point = if buffer_snapshot.can_resolve(anchor) {
24434                        language::ToPoint::to_point(anchor, &buffer_snapshot)
24435                    } else {
24436                        buffer_snapshot.clip_point(*position, Bias::Left)
24437                    };
24438                    let jump_to_offset = buffer_snapshot.point_to_offset(jump_to_point);
24439                    new_selections_by_buffer.insert(
24440                        buffer,
24441                        (
24442                            vec![BufferOffset(jump_to_offset)..BufferOffset(jump_to_offset)],
24443                            Some(*line_offset_from_top),
24444                        ),
24445                    );
24446                }
24447            }
24448            Some(JumpData::MultiBufferRow {
24449                row,
24450                line_offset_from_top,
24451            }) => {
24452                let point = MultiBufferPoint::new(row.0, 0);
24453                if let Some((buffer, buffer_point, _)) =
24454                    self.buffer.read(cx).point_to_buffer_point(point, cx)
24455                {
24456                    let buffer_offset = buffer.read(cx).point_to_offset(buffer_point);
24457                    new_selections_by_buffer
24458                        .entry(buffer)
24459                        .or_insert((Vec::new(), Some(*line_offset_from_top)))
24460                        .0
24461                        .push(BufferOffset(buffer_offset)..BufferOffset(buffer_offset))
24462                }
24463            }
24464            None => {
24465                let selections = self
24466                    .selections
24467                    .all::<MultiBufferOffset>(&self.display_snapshot(cx));
24468                let multi_buffer = self.buffer.read(cx);
24469                for selection in selections {
24470                    for (snapshot, range, _, anchor) in multi_buffer
24471                        .snapshot(cx)
24472                        .range_to_buffer_ranges_with_deleted_hunks(selection.range())
24473                    {
24474                        if let Some(anchor) = anchor {
24475                            let Some(buffer_handle) = multi_buffer.buffer_for_anchor(anchor, cx)
24476                            else {
24477                                continue;
24478                            };
24479                            let offset = text::ToOffset::to_offset(
24480                                &anchor.text_anchor,
24481                                &buffer_handle.read(cx).snapshot(),
24482                            );
24483                            let range = BufferOffset(offset)..BufferOffset(offset);
24484                            new_selections_by_buffer
24485                                .entry(buffer_handle)
24486                                .or_insert((Vec::new(), None))
24487                                .0
24488                                .push(range)
24489                        } else {
24490                            let Some(buffer_handle) = multi_buffer.buffer(snapshot.remote_id())
24491                            else {
24492                                continue;
24493                            };
24494                            new_selections_by_buffer
24495                                .entry(buffer_handle)
24496                                .or_insert((Vec::new(), None))
24497                                .0
24498                                .push(range)
24499                        }
24500                    }
24501                }
24502            }
24503        }
24504
24505        if self.delegate_open_excerpts {
24506            let selections_by_buffer: HashMap<_, _> = new_selections_by_buffer
24507                .into_iter()
24508                .map(|(buffer, value)| (buffer.read(cx).remote_id(), value))
24509                .collect();
24510            if !selections_by_buffer.is_empty() {
24511                cx.emit(EditorEvent::OpenExcerptsRequested {
24512                    selections_by_buffer,
24513                    split,
24514                });
24515            }
24516            return;
24517        }
24518
24519        let Some(workspace) = self.workspace() else {
24520            cx.propagate();
24521            return;
24522        };
24523
24524        new_selections_by_buffer
24525            .retain(|buffer, _| buffer.read(cx).file().is_none_or(|file| file.can_open()));
24526
24527        if new_selections_by_buffer.is_empty() {
24528            return;
24529        }
24530
24531        Self::open_buffers_in_workspace(
24532            workspace.downgrade(),
24533            new_selections_by_buffer,
24534            split,
24535            window,
24536            cx,
24537        );
24538    }
24539
24540    pub(crate) fn open_buffers_in_workspace(
24541        workspace: WeakEntity<Workspace>,
24542        new_selections_by_buffer: HashMap<
24543            Entity<language::Buffer>,
24544            (Vec<Range<BufferOffset>>, Option<u32>),
24545        >,
24546        split: bool,
24547        window: &mut Window,
24548        cx: &mut App,
24549    ) {
24550        // We defer the pane interaction because we ourselves are a workspace item
24551        // and activating a new item causes the pane to call a method on us reentrantly,
24552        // which panics if we're on the stack.
24553        window.defer(cx, move |window, cx| {
24554            workspace
24555                .update(cx, |workspace, cx| {
24556                    let pane = if split {
24557                        workspace.adjacent_pane(window, cx)
24558                    } else {
24559                        workspace.active_pane().clone()
24560                    };
24561
24562                    for (buffer, (ranges, scroll_offset)) in new_selections_by_buffer {
24563                        let buffer_read = buffer.read(cx);
24564                        let (has_file, is_project_file) = if let Some(file) = buffer_read.file() {
24565                            (true, project::File::from_dyn(Some(file)).is_some())
24566                        } else {
24567                            (false, false)
24568                        };
24569
24570                        // If project file is none workspace.open_project_item will fail to open the excerpt
24571                        // in a pre existing workspace item if one exists, because Buffer entity_id will be None
24572                        // so we check if there's a tab match in that case first
24573                        let editor = (!has_file || !is_project_file)
24574                            .then(|| {
24575                                // Handle file-less buffers separately: those are not really the project items, so won't have a project path or entity id,
24576                                // so `workspace.open_project_item` will never find them, always opening a new editor.
24577                                // Instead, we try to activate the existing editor in the pane first.
24578                                let (editor, pane_item_index, pane_item_id) =
24579                                    pane.read(cx).items().enumerate().find_map(|(i, item)| {
24580                                        let editor = item.downcast::<Editor>()?;
24581                                        let singleton_buffer =
24582                                            editor.read(cx).buffer().read(cx).as_singleton()?;
24583                                        if singleton_buffer == buffer {
24584                                            Some((editor, i, item.item_id()))
24585                                        } else {
24586                                            None
24587                                        }
24588                                    })?;
24589                                pane.update(cx, |pane, cx| {
24590                                    pane.activate_item(pane_item_index, true, true, window, cx);
24591                                    if !PreviewTabsSettings::get_global(cx)
24592                                        .enable_preview_from_multibuffer
24593                                    {
24594                                        pane.unpreview_item_if_preview(pane_item_id);
24595                                    }
24596                                });
24597                                Some(editor)
24598                            })
24599                            .flatten()
24600                            .unwrap_or_else(|| {
24601                                let keep_old_preview = PreviewTabsSettings::get_global(cx)
24602                                    .enable_keep_preview_on_code_navigation;
24603                                let allow_new_preview = PreviewTabsSettings::get_global(cx)
24604                                    .enable_preview_from_multibuffer;
24605                                workspace.open_project_item::<Self>(
24606                                    pane.clone(),
24607                                    buffer,
24608                                    true,
24609                                    true,
24610                                    keep_old_preview,
24611                                    allow_new_preview,
24612                                    window,
24613                                    cx,
24614                                )
24615                            });
24616
24617                        editor.update(cx, |editor, cx| {
24618                            if has_file && !is_project_file {
24619                                editor.set_read_only(true);
24620                            }
24621                            let autoscroll = match scroll_offset {
24622                                Some(scroll_offset) => {
24623                                    Autoscroll::top_relative(scroll_offset as usize)
24624                                }
24625                                None => Autoscroll::newest(),
24626                            };
24627                            let nav_history = editor.nav_history.take();
24628                            let multibuffer_snapshot = editor.buffer().read(cx).snapshot(cx);
24629                            let Some((excerpt_id, _, buffer_snapshot)) =
24630                                multibuffer_snapshot.as_singleton()
24631                            else {
24632                                return;
24633                            };
24634                            editor.change_selections(
24635                                SelectionEffects::scroll(autoscroll),
24636                                window,
24637                                cx,
24638                                |s| {
24639                                    s.select_ranges(ranges.into_iter().map(|range| {
24640                                        let range = buffer_snapshot.anchor_before(range.start)
24641                                            ..buffer_snapshot.anchor_after(range.end);
24642                                        multibuffer_snapshot
24643                                            .anchor_range_in_excerpt(excerpt_id, range)
24644                                            .unwrap()
24645                                    }));
24646                                },
24647                            );
24648                            editor.nav_history = nav_history;
24649                        });
24650                    }
24651                })
24652                .ok();
24653        });
24654    }
24655
24656    fn marked_text_ranges(&self, cx: &App) -> Option<Vec<Range<MultiBufferOffsetUtf16>>> {
24657        let snapshot = self.buffer.read(cx).read(cx);
24658        let (_, ranges) = self.text_highlights(HighlightKey::InputComposition, cx)?;
24659        Some(
24660            ranges
24661                .iter()
24662                .map(move |range| {
24663                    range.start.to_offset_utf16(&snapshot)..range.end.to_offset_utf16(&snapshot)
24664                })
24665                .collect(),
24666        )
24667    }
24668
24669    fn selection_replacement_ranges(
24670        &self,
24671        range: Range<MultiBufferOffsetUtf16>,
24672        cx: &mut App,
24673    ) -> Vec<Range<MultiBufferOffsetUtf16>> {
24674        let selections = self
24675            .selections
24676            .all::<MultiBufferOffsetUtf16>(&self.display_snapshot(cx));
24677        let newest_selection = selections
24678            .iter()
24679            .max_by_key(|selection| selection.id)
24680            .unwrap();
24681        let start_delta = range.start.0.0 as isize - newest_selection.start.0.0 as isize;
24682        let end_delta = range.end.0.0 as isize - newest_selection.end.0.0 as isize;
24683        let snapshot = self.buffer.read(cx).read(cx);
24684        selections
24685            .into_iter()
24686            .map(|mut selection| {
24687                selection.start.0.0 =
24688                    (selection.start.0.0 as isize).saturating_add(start_delta) as usize;
24689                selection.end.0.0 = (selection.end.0.0 as isize).saturating_add(end_delta) as usize;
24690                snapshot.clip_offset_utf16(selection.start, Bias::Left)
24691                    ..snapshot.clip_offset_utf16(selection.end, Bias::Right)
24692            })
24693            .collect()
24694    }
24695
24696    fn report_editor_event(
24697        &self,
24698        reported_event: ReportEditorEvent,
24699        file_extension: Option<String>,
24700        cx: &App,
24701    ) {
24702        if cfg!(any(test, feature = "test-support")) {
24703            return;
24704        }
24705
24706        let Some(project) = &self.project else { return };
24707
24708        // If None, we are in a file without an extension
24709        let file = self
24710            .buffer
24711            .read(cx)
24712            .as_singleton()
24713            .and_then(|b| b.read(cx).file());
24714        let file_extension = file_extension.or(file
24715            .as_ref()
24716            .and_then(|file| Path::new(file.file_name(cx)).extension())
24717            .and_then(|e| e.to_str())
24718            .map(|a| a.to_string()));
24719
24720        let vim_mode = vim_mode_setting::VimModeSetting::try_get(cx)
24721            .map(|vim_mode| vim_mode.0)
24722            .unwrap_or(false);
24723
24724        let edit_predictions_provider = all_language_settings(file, cx).edit_predictions.provider;
24725        let copilot_enabled = edit_predictions_provider
24726            == language::language_settings::EditPredictionProvider::Copilot;
24727        let copilot_enabled_for_language = self
24728            .buffer
24729            .read(cx)
24730            .language_settings(cx)
24731            .show_edit_predictions;
24732
24733        let project = project.read(cx);
24734        let event_type = reported_event.event_type();
24735
24736        if let ReportEditorEvent::Saved { auto_saved } = reported_event {
24737            telemetry::event!(
24738                event_type,
24739                type = if auto_saved {"autosave"} else {"manual"},
24740                file_extension,
24741                vim_mode,
24742                copilot_enabled,
24743                copilot_enabled_for_language,
24744                edit_predictions_provider,
24745                is_via_ssh = project.is_via_remote_server(),
24746            );
24747        } else {
24748            telemetry::event!(
24749                event_type,
24750                file_extension,
24751                vim_mode,
24752                copilot_enabled,
24753                copilot_enabled_for_language,
24754                edit_predictions_provider,
24755                is_via_ssh = project.is_via_remote_server(),
24756            );
24757        };
24758    }
24759
24760    /// Copy the highlighted chunks to the clipboard as JSON. The format is an array of lines,
24761    /// with each line being an array of {text, highlight} objects.
24762    fn copy_highlight_json(
24763        &mut self,
24764        _: &CopyHighlightJson,
24765        window: &mut Window,
24766        cx: &mut Context<Self>,
24767    ) {
24768        #[derive(Serialize)]
24769        struct Chunk<'a> {
24770            text: String,
24771            highlight: Option<&'a str>,
24772        }
24773
24774        let snapshot = self.buffer.read(cx).snapshot(cx);
24775        let range = self
24776            .selected_text_range(false, window, cx)
24777            .and_then(|selection| {
24778                if selection.range.is_empty() {
24779                    None
24780                } else {
24781                    Some(
24782                        snapshot.offset_utf16_to_offset(MultiBufferOffsetUtf16(OffsetUtf16(
24783                            selection.range.start,
24784                        )))
24785                            ..snapshot.offset_utf16_to_offset(MultiBufferOffsetUtf16(OffsetUtf16(
24786                                selection.range.end,
24787                            ))),
24788                    )
24789                }
24790            })
24791            .unwrap_or_else(|| MultiBufferOffset(0)..snapshot.len());
24792
24793        let chunks = snapshot.chunks(range, true);
24794        let mut lines = Vec::new();
24795        let mut line: VecDeque<Chunk> = VecDeque::new();
24796
24797        let Some(style) = self.style.as_ref() else {
24798            return;
24799        };
24800
24801        for chunk in chunks {
24802            let highlight = chunk
24803                .syntax_highlight_id
24804                .and_then(|id| id.name(&style.syntax));
24805            let mut chunk_lines = chunk.text.split('\n').peekable();
24806            while let Some(text) = chunk_lines.next() {
24807                let mut merged_with_last_token = false;
24808                if let Some(last_token) = line.back_mut()
24809                    && last_token.highlight == highlight
24810                {
24811                    last_token.text.push_str(text);
24812                    merged_with_last_token = true;
24813                }
24814
24815                if !merged_with_last_token {
24816                    line.push_back(Chunk {
24817                        text: text.into(),
24818                        highlight,
24819                    });
24820                }
24821
24822                if chunk_lines.peek().is_some() {
24823                    if line.len() > 1 && line.front().unwrap().text.is_empty() {
24824                        line.pop_front();
24825                    }
24826                    if line.len() > 1 && line.back().unwrap().text.is_empty() {
24827                        line.pop_back();
24828                    }
24829
24830                    lines.push(mem::take(&mut line));
24831                }
24832            }
24833        }
24834
24835        let Some(lines) = serde_json::to_string_pretty(&lines).log_err() else {
24836            return;
24837        };
24838        cx.write_to_clipboard(ClipboardItem::new_string(lines));
24839    }
24840
24841    pub fn open_context_menu(
24842        &mut self,
24843        _: &OpenContextMenu,
24844        window: &mut Window,
24845        cx: &mut Context<Self>,
24846    ) {
24847        self.request_autoscroll(Autoscroll::newest(), cx);
24848        let position = self
24849            .selections
24850            .newest_display(&self.display_snapshot(cx))
24851            .start;
24852        mouse_context_menu::deploy_context_menu(self, None, position, window, cx);
24853    }
24854
24855    pub fn replay_insert_event(
24856        &mut self,
24857        text: &str,
24858        relative_utf16_range: Option<Range<isize>>,
24859        window: &mut Window,
24860        cx: &mut Context<Self>,
24861    ) {
24862        if !self.input_enabled {
24863            cx.emit(EditorEvent::InputIgnored { text: text.into() });
24864            return;
24865        }
24866        if let Some(relative_utf16_range) = relative_utf16_range {
24867            let selections = self
24868                .selections
24869                .all::<MultiBufferOffsetUtf16>(&self.display_snapshot(cx));
24870            self.change_selections(SelectionEffects::no_scroll(), window, cx, |s| {
24871                let new_ranges = selections.into_iter().map(|range| {
24872                    let start = MultiBufferOffsetUtf16(OffsetUtf16(
24873                        range
24874                            .head()
24875                            .0
24876                            .0
24877                            .saturating_add_signed(relative_utf16_range.start),
24878                    ));
24879                    let end = MultiBufferOffsetUtf16(OffsetUtf16(
24880                        range
24881                            .head()
24882                            .0
24883                            .0
24884                            .saturating_add_signed(relative_utf16_range.end),
24885                    ));
24886                    start..end
24887                });
24888                s.select_ranges(new_ranges);
24889            });
24890        }
24891
24892        self.handle_input(text, window, cx);
24893    }
24894
24895    pub fn is_focused(&self, window: &Window) -> bool {
24896        self.focus_handle.is_focused(window)
24897    }
24898
24899    fn handle_focus(&mut self, window: &mut Window, cx: &mut Context<Self>) {
24900        cx.emit(EditorEvent::Focused);
24901
24902        if let Some(descendant) = self
24903            .last_focused_descendant
24904            .take()
24905            .and_then(|descendant| descendant.upgrade())
24906        {
24907            window.focus(&descendant, cx);
24908        } else {
24909            if let Some(blame) = self.blame.as_ref() {
24910                blame.update(cx, GitBlame::focus)
24911            }
24912
24913            self.blink_manager.update(cx, BlinkManager::enable);
24914            self.show_cursor_names(window, cx);
24915            self.buffer.update(cx, |buffer, cx| {
24916                buffer.finalize_last_transaction(cx);
24917                if self.leader_id.is_none() {
24918                    buffer.set_active_selections(
24919                        &self.selections.disjoint_anchors_arc(),
24920                        self.selections.line_mode(),
24921                        self.cursor_shape,
24922                        cx,
24923                    );
24924                }
24925            });
24926
24927            if let Some(position_map) = self.last_position_map.clone() {
24928                EditorElement::mouse_moved(
24929                    self,
24930                    &MouseMoveEvent {
24931                        position: window.mouse_position(),
24932                        pressed_button: None,
24933                        modifiers: window.modifiers(),
24934                    },
24935                    &position_map,
24936                    None,
24937                    window,
24938                    cx,
24939                );
24940            }
24941        }
24942    }
24943
24944    fn handle_focus_in(&mut self, _: &mut Window, cx: &mut Context<Self>) {
24945        cx.emit(EditorEvent::FocusedIn)
24946    }
24947
24948    fn handle_focus_out(
24949        &mut self,
24950        event: FocusOutEvent,
24951        _window: &mut Window,
24952        cx: &mut Context<Self>,
24953    ) {
24954        if event.blurred != self.focus_handle {
24955            self.last_focused_descendant = Some(event.blurred);
24956        }
24957        self.selection_drag_state = SelectionDragState::None;
24958        self.refresh_inlay_hints(InlayHintRefreshReason::ModifiersChanged(false), cx);
24959    }
24960
24961    pub fn handle_blur(&mut self, window: &mut Window, cx: &mut Context<Self>) {
24962        self.blink_manager.update(cx, BlinkManager::disable);
24963        self.buffer
24964            .update(cx, |buffer, cx| buffer.remove_active_selections(cx));
24965
24966        if let Some(blame) = self.blame.as_ref() {
24967            blame.update(cx, GitBlame::blur)
24968        }
24969        if !self.hover_state.focused(window, cx) {
24970            hide_hover(self, cx);
24971        }
24972        if !self
24973            .context_menu
24974            .borrow()
24975            .as_ref()
24976            .is_some_and(|context_menu| context_menu.focused(window, cx))
24977        {
24978            self.hide_context_menu(window, cx);
24979        }
24980        self.take_active_edit_prediction(cx);
24981        cx.emit(EditorEvent::Blurred);
24982        cx.notify();
24983    }
24984
24985    pub fn observe_pending_input(&mut self, window: &mut Window, cx: &mut Context<Self>) {
24986        let mut pending: String = window
24987            .pending_input_keystrokes()
24988            .into_iter()
24989            .flatten()
24990            .filter_map(|keystroke| keystroke.key_char.clone())
24991            .collect();
24992
24993        if !self.input_enabled || self.read_only || !self.focus_handle.is_focused(window) {
24994            pending = "".to_string();
24995        }
24996
24997        let existing_pending = self
24998            .text_highlights(HighlightKey::PendingInput, cx)
24999            .map(|(_, ranges)| ranges.to_vec());
25000        if existing_pending.is_none() && pending.is_empty() {
25001            return;
25002        }
25003        let transaction =
25004            self.transact(window, cx, |this, window, cx| {
25005                let selections = this
25006                    .selections
25007                    .all::<MultiBufferOffset>(&this.display_snapshot(cx));
25008                let edits = selections
25009                    .iter()
25010                    .map(|selection| (selection.end..selection.end, pending.clone()));
25011                this.edit(edits, cx);
25012                this.change_selections(SelectionEffects::no_scroll(), window, cx, |s| {
25013                    s.select_ranges(selections.into_iter().enumerate().map(|(ix, sel)| {
25014                        sel.start + ix * pending.len()..sel.end + ix * pending.len()
25015                    }));
25016                });
25017                if let Some(existing_ranges) = existing_pending {
25018                    let edits = existing_ranges.iter().map(|range| (range.clone(), ""));
25019                    this.edit(edits, cx);
25020                }
25021            });
25022
25023        let snapshot = self.snapshot(window, cx);
25024        let ranges = self
25025            .selections
25026            .all::<MultiBufferOffset>(&snapshot.display_snapshot)
25027            .into_iter()
25028            .map(|selection| {
25029                snapshot.buffer_snapshot().anchor_after(selection.end)
25030                    ..snapshot
25031                        .buffer_snapshot()
25032                        .anchor_before(selection.end + pending.len())
25033            })
25034            .collect();
25035
25036        if pending.is_empty() {
25037            self.clear_highlights(HighlightKey::PendingInput, cx);
25038        } else {
25039            self.highlight_text(
25040                HighlightKey::PendingInput,
25041                ranges,
25042                HighlightStyle {
25043                    underline: Some(UnderlineStyle {
25044                        thickness: px(1.),
25045                        color: None,
25046                        wavy: false,
25047                    }),
25048                    ..Default::default()
25049                },
25050                cx,
25051            );
25052        }
25053
25054        self.ime_transaction = self.ime_transaction.or(transaction);
25055        if let Some(transaction) = self.ime_transaction {
25056            self.buffer.update(cx, |buffer, cx| {
25057                buffer.group_until_transaction(transaction, cx);
25058            });
25059        }
25060
25061        if self
25062            .text_highlights(HighlightKey::PendingInput, cx)
25063            .is_none()
25064        {
25065            self.ime_transaction.take();
25066        }
25067    }
25068
25069    pub fn register_action_renderer(
25070        &mut self,
25071        listener: impl Fn(&Editor, &mut Window, &mut Context<Editor>) + 'static,
25072    ) -> Subscription {
25073        let id = self.next_editor_action_id.post_inc();
25074        self.editor_actions
25075            .borrow_mut()
25076            .insert(id, Box::new(listener));
25077
25078        let editor_actions = self.editor_actions.clone();
25079        Subscription::new(move || {
25080            editor_actions.borrow_mut().remove(&id);
25081        })
25082    }
25083
25084    pub fn register_action<A: Action>(
25085        &mut self,
25086        listener: impl Fn(&A, &mut Window, &mut App) + 'static,
25087    ) -> Subscription {
25088        let id = self.next_editor_action_id.post_inc();
25089        let listener = Arc::new(listener);
25090        self.editor_actions.borrow_mut().insert(
25091            id,
25092            Box::new(move |_, window, _| {
25093                let listener = listener.clone();
25094                window.on_action(TypeId::of::<A>(), move |action, phase, window, cx| {
25095                    let action = action.downcast_ref().unwrap();
25096                    if phase == DispatchPhase::Bubble {
25097                        listener(action, window, cx)
25098                    }
25099                })
25100            }),
25101        );
25102
25103        let editor_actions = self.editor_actions.clone();
25104        Subscription::new(move || {
25105            editor_actions.borrow_mut().remove(&id);
25106        })
25107    }
25108
25109    pub fn file_header_size(&self) -> u32 {
25110        FILE_HEADER_HEIGHT
25111    }
25112
25113    pub fn restore(
25114        &mut self,
25115        revert_changes: HashMap<BufferId, Vec<(Range<text::Anchor>, Rope)>>,
25116        window: &mut Window,
25117        cx: &mut Context<Self>,
25118    ) {
25119        self.buffer().update(cx, |multi_buffer, cx| {
25120            for (buffer_id, changes) in revert_changes {
25121                if let Some(buffer) = multi_buffer.buffer(buffer_id) {
25122                    buffer.update(cx, |buffer, cx| {
25123                        buffer.edit(
25124                            changes
25125                                .into_iter()
25126                                .map(|(range, text)| (range, text.to_string())),
25127                            None,
25128                            cx,
25129                        );
25130                    });
25131                }
25132            }
25133        });
25134        self.change_selections(SelectionEffects::no_scroll(), window, cx, |selections| {
25135            selections.refresh()
25136        });
25137    }
25138
25139    pub fn to_pixel_point(
25140        &mut self,
25141        source: Anchor,
25142        editor_snapshot: &EditorSnapshot,
25143        window: &mut Window,
25144        cx: &mut App,
25145    ) -> Option<gpui::Point<Pixels>> {
25146        let source_point = source.to_display_point(editor_snapshot);
25147        self.display_to_pixel_point(source_point, editor_snapshot, window, cx)
25148    }
25149
25150    pub fn display_to_pixel_point(
25151        &mut self,
25152        source: DisplayPoint,
25153        editor_snapshot: &EditorSnapshot,
25154        window: &mut Window,
25155        cx: &mut App,
25156    ) -> Option<gpui::Point<Pixels>> {
25157        let line_height = self.style(cx).text.line_height_in_pixels(window.rem_size());
25158        let text_layout_details = self.text_layout_details(window, cx);
25159        let scroll_top = text_layout_details
25160            .scroll_anchor
25161            .scroll_position(editor_snapshot)
25162            .y;
25163
25164        if source.row().as_f64() < scroll_top.floor() {
25165            return None;
25166        }
25167        let source_x = editor_snapshot.x_for_display_point(source, &text_layout_details);
25168        let source_y = line_height * (source.row().as_f64() - scroll_top) as f32;
25169        Some(gpui::Point::new(source_x, source_y))
25170    }
25171
25172    pub fn has_visible_completions_menu(&self) -> bool {
25173        !self.edit_prediction_preview_is_active()
25174            && self.context_menu.borrow().as_ref().is_some_and(|menu| {
25175                menu.visible() && matches!(menu, CodeContextMenu::Completions(_))
25176            })
25177    }
25178
25179    pub fn register_addon<T: Addon>(&mut self, instance: T) {
25180        if self.mode.is_minimap() {
25181            return;
25182        }
25183        self.addons
25184            .insert(std::any::TypeId::of::<T>(), Box::new(instance));
25185    }
25186
25187    pub fn unregister_addon<T: Addon>(&mut self) {
25188        self.addons.remove(&std::any::TypeId::of::<T>());
25189    }
25190
25191    pub fn addon<T: Addon>(&self) -> Option<&T> {
25192        let type_id = std::any::TypeId::of::<T>();
25193        self.addons
25194            .get(&type_id)
25195            .and_then(|item| item.to_any().downcast_ref::<T>())
25196    }
25197
25198    pub fn addon_mut<T: Addon>(&mut self) -> Option<&mut T> {
25199        let type_id = std::any::TypeId::of::<T>();
25200        self.addons
25201            .get_mut(&type_id)
25202            .and_then(|item| item.to_any_mut()?.downcast_mut::<T>())
25203    }
25204
25205    fn character_dimensions(&self, window: &mut Window, cx: &mut App) -> CharacterDimensions {
25206        let text_layout_details = self.text_layout_details(window, cx);
25207        let style = &text_layout_details.editor_style;
25208        let font_id = window.text_system().resolve_font(&style.text.font());
25209        let font_size = style.text.font_size.to_pixels(window.rem_size());
25210        let line_height = style.text.line_height_in_pixels(window.rem_size());
25211        let em_width = window.text_system().em_width(font_id, font_size).unwrap();
25212        let em_advance = window.text_system().em_advance(font_id, font_size).unwrap();
25213
25214        CharacterDimensions {
25215            em_width,
25216            em_advance,
25217            line_height,
25218        }
25219    }
25220
25221    pub fn wait_for_diff_to_load(&self) -> Option<Shared<Task<()>>> {
25222        self.load_diff_task.clone()
25223    }
25224
25225    fn read_metadata_from_db(
25226        &mut self,
25227        item_id: u64,
25228        workspace_id: WorkspaceId,
25229        window: &mut Window,
25230        cx: &mut Context<Editor>,
25231    ) {
25232        if self.buffer_kind(cx) == ItemBufferKind::Singleton
25233            && !self.mode.is_minimap()
25234            && WorkspaceSettings::get(None, cx).restore_on_startup
25235                != RestoreOnStartupBehavior::EmptyTab
25236        {
25237            let buffer_snapshot = OnceCell::new();
25238
25239            if let Some(folds) = DB.get_editor_folds(item_id, workspace_id).log_err()
25240                && !folds.is_empty()
25241            {
25242                let snapshot = buffer_snapshot.get_or_init(|| self.buffer.read(cx).snapshot(cx));
25243                let snapshot_len = snapshot.len().0;
25244
25245                // Helper: search for fingerprint in buffer, return offset if found
25246                let find_fingerprint = |fingerprint: &str, search_start: usize| -> Option<usize> {
25247                    // Ensure we start at a character boundary (defensive)
25248                    let search_start = snapshot
25249                        .clip_offset(MultiBufferOffset(search_start), Bias::Left)
25250                        .0;
25251                    let search_end = snapshot_len.saturating_sub(fingerprint.len());
25252
25253                    let mut byte_offset = search_start;
25254                    for ch in snapshot.chars_at(MultiBufferOffset(search_start)) {
25255                        if byte_offset > search_end {
25256                            break;
25257                        }
25258                        if snapshot.contains_str_at(MultiBufferOffset(byte_offset), fingerprint) {
25259                            return Some(byte_offset);
25260                        }
25261                        byte_offset += ch.len_utf8();
25262                    }
25263                    None
25264                };
25265
25266                // Track search position to handle duplicate fingerprints correctly.
25267                // Folds are stored in document order, so we advance after each match.
25268                let mut search_start = 0usize;
25269
25270                let valid_folds: Vec<_> = folds
25271                    .into_iter()
25272                    .filter_map(|(stored_start, stored_end, start_fp, end_fp)| {
25273                        // Skip folds without fingerprints (old data before migration)
25274                        let sfp = start_fp?;
25275                        let efp = end_fp?;
25276                        let efp_len = efp.len();
25277
25278                        // Fast path: check if fingerprints match at stored offsets
25279                        // Note: end_fp is content BEFORE fold end, so check at (stored_end - efp_len)
25280                        let start_matches = stored_start < snapshot_len
25281                            && snapshot.contains_str_at(MultiBufferOffset(stored_start), &sfp);
25282                        let efp_check_pos = stored_end.saturating_sub(efp_len);
25283                        let end_matches = efp_check_pos >= stored_start
25284                            && stored_end <= snapshot_len
25285                            && snapshot.contains_str_at(MultiBufferOffset(efp_check_pos), &efp);
25286
25287                        let (new_start, new_end) = if start_matches && end_matches {
25288                            // Offsets unchanged, use stored values
25289                            (stored_start, stored_end)
25290                        } else if sfp == efp {
25291                            // Short fold: identical fingerprints can only match once per search
25292                            // Use stored fold length to compute new_end
25293                            let new_start = find_fingerprint(&sfp, search_start)?;
25294                            let fold_len = stored_end - stored_start;
25295                            let new_end = new_start + fold_len;
25296                            (new_start, new_end)
25297                        } else {
25298                            // Slow path: search for fingerprints in buffer
25299                            let new_start = find_fingerprint(&sfp, search_start)?;
25300                            // Search for end_fp after start, then add efp_len to get actual fold end
25301                            let efp_pos = find_fingerprint(&efp, new_start + sfp.len())?;
25302                            let new_end = efp_pos + efp_len;
25303                            (new_start, new_end)
25304                        };
25305
25306                        // Advance search position for next fold
25307                        search_start = new_end;
25308
25309                        // Validate fold makes sense (end must be after start)
25310                        if new_end <= new_start {
25311                            return None;
25312                        }
25313
25314                        Some(
25315                            snapshot.clip_offset(MultiBufferOffset(new_start), Bias::Left)
25316                                ..snapshot.clip_offset(MultiBufferOffset(new_end), Bias::Right),
25317                        )
25318                    })
25319                    .collect();
25320
25321                if !valid_folds.is_empty() {
25322                    self.fold_ranges(valid_folds, false, window, cx);
25323
25324                    // Migrate folds to current entity_id before workspace cleanup runs.
25325                    // Entity IDs change between sessions, but workspace cleanup deletes
25326                    // old editor rows (cascading to folds) based on current entity IDs.
25327                    let new_editor_id = cx.entity().entity_id().as_u64() as ItemId;
25328                    if new_editor_id != item_id {
25329                        cx.spawn(async move |_, _| {
25330                            DB.migrate_editor_folds(item_id, new_editor_id, workspace_id)
25331                                .await
25332                                .log_err();
25333                        })
25334                        .detach();
25335                    }
25336                }
25337            }
25338
25339            if let Some(selections) = DB.get_editor_selections(item_id, workspace_id).log_err()
25340                && !selections.is_empty()
25341            {
25342                let snapshot = buffer_snapshot.get_or_init(|| self.buffer.read(cx).snapshot(cx));
25343                // skip adding the initial selection to selection history
25344                self.selection_history.mode = SelectionHistoryMode::Skipping;
25345                self.change_selections(SelectionEffects::no_scroll(), window, cx, |s| {
25346                    s.select_ranges(selections.into_iter().map(|(start, end)| {
25347                        snapshot.clip_offset(MultiBufferOffset(start), Bias::Left)
25348                            ..snapshot.clip_offset(MultiBufferOffset(end), Bias::Right)
25349                    }));
25350                });
25351                self.selection_history.mode = SelectionHistoryMode::Normal;
25352            };
25353        }
25354
25355        self.read_scroll_position_from_db(item_id, workspace_id, window, cx);
25356    }
25357
25358    fn update_lsp_data(
25359        &mut self,
25360        for_buffer: Option<BufferId>,
25361        window: &mut Window,
25362        cx: &mut Context<'_, Self>,
25363    ) {
25364        if !self.enable_lsp_data {
25365            return;
25366        }
25367
25368        if let Some(buffer_id) = for_buffer {
25369            self.pull_diagnostics(buffer_id, window, cx);
25370        }
25371        self.refresh_semantic_tokens(for_buffer, None, cx);
25372        self.refresh_document_colors(for_buffer, window, cx);
25373        self.refresh_folding_ranges(for_buffer, window, cx);
25374        self.refresh_document_symbols(for_buffer, cx);
25375    }
25376
25377    fn register_visible_buffers(&mut self, cx: &mut Context<Self>) {
25378        if !self.mode().is_full() {
25379            return;
25380        }
25381        for (_, (visible_buffer, _, _)) in self.visible_excerpts(true, cx) {
25382            self.register_buffer(visible_buffer.read(cx).remote_id(), cx);
25383        }
25384    }
25385
25386    fn register_buffer(&mut self, buffer_id: BufferId, cx: &mut Context<Self>) {
25387        if !self.mode().is_full() {
25388            return;
25389        }
25390
25391        if !self.registered_buffers.contains_key(&buffer_id)
25392            && let Some(project) = self.project.as_ref()
25393        {
25394            if let Some(buffer) = self.buffer.read(cx).buffer(buffer_id) {
25395                project.update(cx, |project, cx| {
25396                    self.registered_buffers.insert(
25397                        buffer_id,
25398                        project.register_buffer_with_language_servers(&buffer, cx),
25399                    );
25400                });
25401            } else {
25402                self.registered_buffers.remove(&buffer_id);
25403            }
25404        }
25405    }
25406
25407    fn create_style(&self, cx: &App) -> EditorStyle {
25408        let settings = ThemeSettings::get_global(cx);
25409
25410        let mut text_style = match self.mode {
25411            EditorMode::SingleLine | EditorMode::AutoHeight { .. } => TextStyle {
25412                color: cx.theme().colors().editor_foreground,
25413                font_family: settings.ui_font.family.clone(),
25414                font_features: settings.ui_font.features.clone(),
25415                font_fallbacks: settings.ui_font.fallbacks.clone(),
25416                font_size: rems(0.875).into(),
25417                font_weight: settings.ui_font.weight,
25418                line_height: relative(settings.buffer_line_height.value()),
25419                ..Default::default()
25420            },
25421            EditorMode::Full { .. } | EditorMode::Minimap { .. } => TextStyle {
25422                color: cx.theme().colors().editor_foreground,
25423                font_family: settings.buffer_font.family.clone(),
25424                font_features: settings.buffer_font.features.clone(),
25425                font_fallbacks: settings.buffer_font.fallbacks.clone(),
25426                font_size: settings.buffer_font_size(cx).into(),
25427                font_weight: settings.buffer_font.weight,
25428                line_height: relative(settings.buffer_line_height.value()),
25429                ..Default::default()
25430            },
25431        };
25432        if let Some(text_style_refinement) = &self.text_style_refinement {
25433            text_style.refine(text_style_refinement)
25434        }
25435
25436        let background = match self.mode {
25437            EditorMode::SingleLine => cx.theme().system().transparent,
25438            EditorMode::AutoHeight { .. } => cx.theme().system().transparent,
25439            EditorMode::Full { .. } => cx.theme().colors().editor_background,
25440            EditorMode::Minimap { .. } => cx.theme().colors().editor_background.opacity(0.7),
25441        };
25442
25443        EditorStyle {
25444            background,
25445            border: cx.theme().colors().border,
25446            local_player: cx.theme().players().local(),
25447            text: text_style,
25448            scrollbar_width: EditorElement::SCROLLBAR_WIDTH,
25449            syntax: cx.theme().syntax().clone(),
25450            status: cx.theme().status().clone(),
25451            inlay_hints_style: make_inlay_hints_style(cx),
25452            edit_prediction_styles: make_suggestion_styles(cx),
25453            unnecessary_code_fade: settings.unnecessary_code_fade,
25454            show_underlines: self.diagnostics_enabled(),
25455        }
25456    }
25457
25458    fn breadcrumbs_inner(&self, cx: &App) -> Option<Vec<BreadcrumbText>> {
25459        let multibuffer = self.buffer().read(cx);
25460        let is_singleton = multibuffer.is_singleton();
25461        let (buffer_id, symbols) = self.outline_symbols_at_cursor.as_ref()?;
25462        let buffer = multibuffer.buffer(*buffer_id)?;
25463
25464        let buffer = buffer.read(cx);
25465        let settings = ThemeSettings::get_global(cx);
25466        // In a multi-buffer layout, we don't want to include the filename in the breadcrumbs
25467        let mut breadcrumbs = if is_singleton {
25468            let text = self.breadcrumb_header.clone().unwrap_or_else(|| {
25469                buffer
25470                    .snapshot()
25471                    .resolve_file_path(
25472                        self.project
25473                            .as_ref()
25474                            .map(|project| project.read(cx).visible_worktrees(cx).count() > 1)
25475                            .unwrap_or_default(),
25476                        cx,
25477                    )
25478                    .unwrap_or_else(|| {
25479                        if multibuffer.is_singleton() {
25480                            multibuffer.title(cx).to_string()
25481                        } else {
25482                            "untitled".to_string()
25483                        }
25484                    })
25485            });
25486            vec![BreadcrumbText {
25487                text,
25488                highlights: None,
25489                font: Some(settings.buffer_font.clone()),
25490            }]
25491        } else {
25492            vec![]
25493        };
25494
25495        breadcrumbs.extend(symbols.iter().map(|symbol| BreadcrumbText {
25496            text: symbol.text.clone(),
25497            highlights: Some(symbol.highlight_ranges.clone()),
25498            font: Some(settings.buffer_font.clone()),
25499        }));
25500        Some(breadcrumbs)
25501    }
25502
25503    fn disable_lsp_data(&mut self) {
25504        self.enable_lsp_data = false;
25505    }
25506
25507    fn disable_runnables(&mut self) {
25508        self.enable_runnables = false;
25509    }
25510}
25511
25512fn edit_for_markdown_paste<'a>(
25513    buffer: &MultiBufferSnapshot,
25514    range: Range<MultiBufferOffset>,
25515    to_insert: &'a str,
25516    url: Option<url::Url>,
25517) -> (Range<MultiBufferOffset>, Cow<'a, str>) {
25518    if url.is_none() {
25519        return (range, Cow::Borrowed(to_insert));
25520    };
25521
25522    let old_text = buffer.text_for_range(range.clone()).collect::<String>();
25523
25524    let new_text = if range.is_empty() || url::Url::parse(&old_text).is_ok() {
25525        Cow::Borrowed(to_insert)
25526    } else {
25527        Cow::Owned(format!("[{old_text}]({to_insert})"))
25528    };
25529    (range, new_text)
25530}
25531
25532fn process_completion_for_edit(
25533    completion: &Completion,
25534    intent: CompletionIntent,
25535    buffer: &Entity<Buffer>,
25536    cursor_position: &text::Anchor,
25537    cx: &mut Context<Editor>,
25538) -> CompletionEdit {
25539    let buffer = buffer.read(cx);
25540    let buffer_snapshot = buffer.snapshot();
25541    let (snippet, new_text) = if completion.is_snippet() {
25542        let mut snippet_source = completion.new_text.clone();
25543        // Workaround for typescript language server issues so that methods don't expand within
25544        // strings and functions with type expressions. The previous point is used because the query
25545        // for function identifier doesn't match when the cursor is immediately after. See PR #30312
25546        let previous_point = text::ToPoint::to_point(cursor_position, &buffer_snapshot);
25547        let previous_point = if previous_point.column > 0 {
25548            cursor_position.to_previous_offset(&buffer_snapshot)
25549        } else {
25550            cursor_position.to_offset(&buffer_snapshot)
25551        };
25552        if let Some(scope) = buffer_snapshot.language_scope_at(previous_point)
25553            && scope.prefers_label_for_snippet_in_completion()
25554            && let Some(label) = completion.label()
25555            && matches!(
25556                completion.kind(),
25557                Some(CompletionItemKind::FUNCTION) | Some(CompletionItemKind::METHOD)
25558            )
25559        {
25560            snippet_source = label;
25561        }
25562        match Snippet::parse(&snippet_source).log_err() {
25563            Some(parsed_snippet) => (Some(parsed_snippet.clone()), parsed_snippet.text),
25564            None => (None, completion.new_text.clone()),
25565        }
25566    } else {
25567        (None, completion.new_text.clone())
25568    };
25569
25570    let mut range_to_replace = {
25571        let replace_range = &completion.replace_range;
25572        if let CompletionSource::Lsp {
25573            insert_range: Some(insert_range),
25574            ..
25575        } = &completion.source
25576        {
25577            debug_assert_eq!(
25578                insert_range.start, replace_range.start,
25579                "insert_range and replace_range should start at the same position"
25580            );
25581            debug_assert!(
25582                insert_range
25583                    .start
25584                    .cmp(cursor_position, &buffer_snapshot)
25585                    .is_le(),
25586                "insert_range should start before or at cursor position"
25587            );
25588            debug_assert!(
25589                replace_range
25590                    .start
25591                    .cmp(cursor_position, &buffer_snapshot)
25592                    .is_le(),
25593                "replace_range should start before or at cursor position"
25594            );
25595
25596            let should_replace = match intent {
25597                CompletionIntent::CompleteWithInsert => false,
25598                CompletionIntent::CompleteWithReplace => true,
25599                CompletionIntent::Complete | CompletionIntent::Compose => {
25600                    let insert_mode =
25601                        language_settings(buffer.language().map(|l| l.name()), buffer.file(), cx)
25602                            .completions
25603                            .lsp_insert_mode;
25604                    match insert_mode {
25605                        LspInsertMode::Insert => false,
25606                        LspInsertMode::Replace => true,
25607                        LspInsertMode::ReplaceSubsequence => {
25608                            let mut text_to_replace = buffer.chars_for_range(
25609                                buffer.anchor_before(replace_range.start)
25610                                    ..buffer.anchor_after(replace_range.end),
25611                            );
25612                            let mut current_needle = text_to_replace.next();
25613                            for haystack_ch in completion.label.text.chars() {
25614                                if let Some(needle_ch) = current_needle
25615                                    && haystack_ch.eq_ignore_ascii_case(&needle_ch)
25616                                {
25617                                    current_needle = text_to_replace.next();
25618                                }
25619                            }
25620                            current_needle.is_none()
25621                        }
25622                        LspInsertMode::ReplaceSuffix => {
25623                            if replace_range
25624                                .end
25625                                .cmp(cursor_position, &buffer_snapshot)
25626                                .is_gt()
25627                            {
25628                                let range_after_cursor = *cursor_position..replace_range.end;
25629                                let text_after_cursor = buffer
25630                                    .text_for_range(
25631                                        buffer.anchor_before(range_after_cursor.start)
25632                                            ..buffer.anchor_after(range_after_cursor.end),
25633                                    )
25634                                    .collect::<String>()
25635                                    .to_ascii_lowercase();
25636                                completion
25637                                    .label
25638                                    .text
25639                                    .to_ascii_lowercase()
25640                                    .ends_with(&text_after_cursor)
25641                            } else {
25642                                true
25643                            }
25644                        }
25645                    }
25646                }
25647            };
25648
25649            if should_replace {
25650                replace_range.clone()
25651            } else {
25652                insert_range.clone()
25653            }
25654        } else {
25655            replace_range.clone()
25656        }
25657    };
25658
25659    if range_to_replace
25660        .end
25661        .cmp(cursor_position, &buffer_snapshot)
25662        .is_lt()
25663    {
25664        range_to_replace.end = *cursor_position;
25665    }
25666
25667    let replace_range = range_to_replace.to_offset(buffer);
25668    CompletionEdit {
25669        new_text,
25670        replace_range: BufferOffset(replace_range.start)..BufferOffset(replace_range.end),
25671        snippet,
25672    }
25673}
25674
25675struct CompletionEdit {
25676    new_text: String,
25677    replace_range: Range<BufferOffset>,
25678    snippet: Option<Snippet>,
25679}
25680
25681fn comment_delimiter_for_newline(
25682    start_point: &Point,
25683    buffer: &MultiBufferSnapshot,
25684    language: &LanguageScope,
25685) -> Option<Arc<str>> {
25686    let delimiters = language.line_comment_prefixes();
25687    let max_len_of_delimiter = delimiters.iter().map(|delimiter| delimiter.len()).max()?;
25688    let (snapshot, range) = buffer.buffer_line_for_row(MultiBufferRow(start_point.row))?;
25689
25690    let num_of_whitespaces = snapshot
25691        .chars_for_range(range.clone())
25692        .take_while(|c| c.is_whitespace())
25693        .count();
25694    let comment_candidate = snapshot
25695        .chars_for_range(range.clone())
25696        .skip(num_of_whitespaces)
25697        .take(max_len_of_delimiter)
25698        .collect::<String>();
25699    let (delimiter, trimmed_len) = delimiters
25700        .iter()
25701        .filter_map(|delimiter| {
25702            let prefix = delimiter.trim_end();
25703            if comment_candidate.starts_with(prefix) {
25704                Some((delimiter, prefix.len()))
25705            } else {
25706                None
25707            }
25708        })
25709        .max_by_key(|(_, len)| *len)?;
25710
25711    if let Some(BlockCommentConfig {
25712        start: block_start, ..
25713    }) = language.block_comment()
25714    {
25715        let block_start_trimmed = block_start.trim_end();
25716        if block_start_trimmed.starts_with(delimiter.trim_end()) {
25717            let line_content = snapshot
25718                .chars_for_range(range)
25719                .skip(num_of_whitespaces)
25720                .take(block_start_trimmed.len())
25721                .collect::<String>();
25722
25723            if line_content.starts_with(block_start_trimmed) {
25724                return None;
25725            }
25726        }
25727    }
25728
25729    let cursor_is_placed_after_comment_marker =
25730        num_of_whitespaces + trimmed_len <= start_point.column as usize;
25731    if cursor_is_placed_after_comment_marker {
25732        Some(delimiter.clone())
25733    } else {
25734        None
25735    }
25736}
25737
25738fn documentation_delimiter_for_newline(
25739    start_point: &Point,
25740    buffer: &MultiBufferSnapshot,
25741    language: &LanguageScope,
25742    newline_config: &mut NewlineConfig,
25743) -> Option<Arc<str>> {
25744    let BlockCommentConfig {
25745        start: start_tag,
25746        end: end_tag,
25747        prefix: delimiter,
25748        tab_size: len,
25749    } = language.documentation_comment()?;
25750    let is_within_block_comment = buffer
25751        .language_scope_at(*start_point)
25752        .is_some_and(|scope| scope.override_name() == Some("comment"));
25753    if !is_within_block_comment {
25754        return None;
25755    }
25756
25757    let (snapshot, range) = buffer.buffer_line_for_row(MultiBufferRow(start_point.row))?;
25758
25759    let num_of_whitespaces = snapshot
25760        .chars_for_range(range.clone())
25761        .take_while(|c| c.is_whitespace())
25762        .count();
25763
25764    // 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.
25765    let column = start_point.column;
25766    let cursor_is_after_start_tag = {
25767        let start_tag_len = start_tag.len();
25768        let start_tag_line = snapshot
25769            .chars_for_range(range.clone())
25770            .skip(num_of_whitespaces)
25771            .take(start_tag_len)
25772            .collect::<String>();
25773        if start_tag_line.starts_with(start_tag.as_ref()) {
25774            num_of_whitespaces + start_tag_len <= column as usize
25775        } else {
25776            false
25777        }
25778    };
25779
25780    let cursor_is_after_delimiter = {
25781        let delimiter_trim = delimiter.trim_end();
25782        let delimiter_line = snapshot
25783            .chars_for_range(range.clone())
25784            .skip(num_of_whitespaces)
25785            .take(delimiter_trim.len())
25786            .collect::<String>();
25787        if delimiter_line.starts_with(delimiter_trim) {
25788            num_of_whitespaces + delimiter_trim.len() <= column as usize
25789        } else {
25790            false
25791        }
25792    };
25793
25794    let mut needs_extra_line = false;
25795    let mut extra_line_additional_indent = IndentSize::spaces(0);
25796
25797    let cursor_is_before_end_tag_if_exists = {
25798        let mut char_position = 0u32;
25799        let mut end_tag_offset = None;
25800
25801        'outer: for chunk in snapshot.text_for_range(range) {
25802            if let Some(byte_pos) = chunk.find(&**end_tag) {
25803                let chars_before_match = chunk[..byte_pos].chars().count() as u32;
25804                end_tag_offset = Some(char_position + chars_before_match);
25805                break 'outer;
25806            }
25807            char_position += chunk.chars().count() as u32;
25808        }
25809
25810        if let Some(end_tag_offset) = end_tag_offset {
25811            let cursor_is_before_end_tag = column <= end_tag_offset;
25812            if cursor_is_after_start_tag {
25813                if cursor_is_before_end_tag {
25814                    needs_extra_line = true;
25815                }
25816                let cursor_is_at_start_of_end_tag = column == end_tag_offset;
25817                if cursor_is_at_start_of_end_tag {
25818                    extra_line_additional_indent.len = *len;
25819                }
25820            }
25821            cursor_is_before_end_tag
25822        } else {
25823            true
25824        }
25825    };
25826
25827    if (cursor_is_after_start_tag || cursor_is_after_delimiter)
25828        && cursor_is_before_end_tag_if_exists
25829    {
25830        let additional_indent = if cursor_is_after_start_tag {
25831            IndentSize::spaces(*len)
25832        } else {
25833            IndentSize::spaces(0)
25834        };
25835
25836        *newline_config = NewlineConfig::Newline {
25837            additional_indent,
25838            extra_line_additional_indent: if needs_extra_line {
25839                Some(extra_line_additional_indent)
25840            } else {
25841                None
25842            },
25843            prevent_auto_indent: true,
25844        };
25845        Some(delimiter.clone())
25846    } else {
25847        None
25848    }
25849}
25850
25851const ORDERED_LIST_MAX_MARKER_LEN: usize = 16;
25852
25853fn list_delimiter_for_newline(
25854    start_point: &Point,
25855    buffer: &MultiBufferSnapshot,
25856    language: &LanguageScope,
25857    newline_config: &mut NewlineConfig,
25858) -> Option<Arc<str>> {
25859    let (snapshot, range) = buffer.buffer_line_for_row(MultiBufferRow(start_point.row))?;
25860
25861    let num_of_whitespaces = snapshot
25862        .chars_for_range(range.clone())
25863        .take_while(|c| c.is_whitespace())
25864        .count();
25865
25866    let task_list_entries: Vec<_> = language
25867        .task_list()
25868        .into_iter()
25869        .flat_map(|config| {
25870            config
25871                .prefixes
25872                .iter()
25873                .map(|prefix| (prefix.as_ref(), config.continuation.as_ref()))
25874        })
25875        .collect();
25876    let unordered_list_entries: Vec<_> = language
25877        .unordered_list()
25878        .iter()
25879        .map(|marker| (marker.as_ref(), marker.as_ref()))
25880        .collect();
25881
25882    let all_entries: Vec<_> = task_list_entries
25883        .into_iter()
25884        .chain(unordered_list_entries)
25885        .collect();
25886
25887    if let Some(max_prefix_len) = all_entries.iter().map(|(p, _)| p.len()).max() {
25888        let candidate: String = snapshot
25889            .chars_for_range(range.clone())
25890            .skip(num_of_whitespaces)
25891            .take(max_prefix_len)
25892            .collect();
25893
25894        if let Some((prefix, continuation)) = all_entries
25895            .iter()
25896            .filter(|(prefix, _)| candidate.starts_with(*prefix))
25897            .max_by_key(|(prefix, _)| prefix.len())
25898        {
25899            let end_of_prefix = num_of_whitespaces + prefix.len();
25900            let cursor_is_after_prefix = end_of_prefix <= start_point.column as usize;
25901            let has_content_after_marker = snapshot
25902                .chars_for_range(range)
25903                .skip(end_of_prefix)
25904                .any(|c| !c.is_whitespace());
25905
25906            if has_content_after_marker && cursor_is_after_prefix {
25907                return Some((*continuation).into());
25908            }
25909
25910            if start_point.column as usize == end_of_prefix {
25911                if num_of_whitespaces == 0 {
25912                    *newline_config = NewlineConfig::ClearCurrentLine;
25913                } else {
25914                    *newline_config = NewlineConfig::UnindentCurrentLine {
25915                        continuation: (*continuation).into(),
25916                    };
25917                }
25918            }
25919
25920            return None;
25921        }
25922    }
25923
25924    let candidate: String = snapshot
25925        .chars_for_range(range.clone())
25926        .skip(num_of_whitespaces)
25927        .take(ORDERED_LIST_MAX_MARKER_LEN)
25928        .collect();
25929
25930    for ordered_config in language.ordered_list() {
25931        let regex = match Regex::new(&ordered_config.pattern) {
25932            Ok(r) => r,
25933            Err(_) => continue,
25934        };
25935
25936        if let Some(captures) = regex.captures(&candidate) {
25937            let full_match = captures.get(0)?;
25938            let marker_len = full_match.len();
25939            let end_of_prefix = num_of_whitespaces + marker_len;
25940            let cursor_is_after_prefix = end_of_prefix <= start_point.column as usize;
25941
25942            let has_content_after_marker = snapshot
25943                .chars_for_range(range)
25944                .skip(end_of_prefix)
25945                .any(|c| !c.is_whitespace());
25946
25947            if has_content_after_marker && cursor_is_after_prefix {
25948                let number: u32 = captures.get(1)?.as_str().parse().ok()?;
25949                let continuation = ordered_config
25950                    .format
25951                    .replace("{1}", &(number + 1).to_string());
25952                return Some(continuation.into());
25953            }
25954
25955            if start_point.column as usize == end_of_prefix {
25956                let continuation = ordered_config.format.replace("{1}", "1");
25957                if num_of_whitespaces == 0 {
25958                    *newline_config = NewlineConfig::ClearCurrentLine;
25959                } else {
25960                    *newline_config = NewlineConfig::UnindentCurrentLine {
25961                        continuation: continuation.into(),
25962                    };
25963                }
25964            }
25965
25966            return None;
25967        }
25968    }
25969
25970    None
25971}
25972
25973fn is_list_prefix_row(
25974    row: MultiBufferRow,
25975    buffer: &MultiBufferSnapshot,
25976    language: &LanguageScope,
25977) -> bool {
25978    let Some((snapshot, range)) = buffer.buffer_line_for_row(row) else {
25979        return false;
25980    };
25981
25982    let num_of_whitespaces = snapshot
25983        .chars_for_range(range.clone())
25984        .take_while(|c| c.is_whitespace())
25985        .count();
25986
25987    let task_list_prefixes: Vec<_> = language
25988        .task_list()
25989        .into_iter()
25990        .flat_map(|config| {
25991            config
25992                .prefixes
25993                .iter()
25994                .map(|p| p.as_ref())
25995                .collect::<Vec<_>>()
25996        })
25997        .collect();
25998    let unordered_list_markers: Vec<_> = language
25999        .unordered_list()
26000        .iter()
26001        .map(|marker| marker.as_ref())
26002        .collect();
26003    let all_prefixes: Vec<_> = task_list_prefixes
26004        .into_iter()
26005        .chain(unordered_list_markers)
26006        .collect();
26007    if let Some(max_prefix_len) = all_prefixes.iter().map(|p| p.len()).max() {
26008        let candidate: String = snapshot
26009            .chars_for_range(range.clone())
26010            .skip(num_of_whitespaces)
26011            .take(max_prefix_len)
26012            .collect();
26013        if all_prefixes
26014            .iter()
26015            .any(|prefix| candidate.starts_with(*prefix))
26016        {
26017            return true;
26018        }
26019    }
26020
26021    let ordered_list_candidate: String = snapshot
26022        .chars_for_range(range)
26023        .skip(num_of_whitespaces)
26024        .take(ORDERED_LIST_MAX_MARKER_LEN)
26025        .collect();
26026    for ordered_config in language.ordered_list() {
26027        let regex = match Regex::new(&ordered_config.pattern) {
26028            Ok(r) => r,
26029            Err(_) => continue,
26030        };
26031        if let Some(captures) = regex.captures(&ordered_list_candidate) {
26032            return captures.get(0).is_some();
26033        }
26034    }
26035
26036    false
26037}
26038
26039#[derive(Debug)]
26040enum NewlineConfig {
26041    /// Insert newline with optional additional indent and optional extra blank line
26042    Newline {
26043        additional_indent: IndentSize,
26044        extra_line_additional_indent: Option<IndentSize>,
26045        prevent_auto_indent: bool,
26046    },
26047    /// Clear the current line
26048    ClearCurrentLine,
26049    /// Unindent the current line and add continuation
26050    UnindentCurrentLine { continuation: Arc<str> },
26051}
26052
26053impl NewlineConfig {
26054    fn has_extra_line(&self) -> bool {
26055        matches!(
26056            self,
26057            Self::Newline {
26058                extra_line_additional_indent: Some(_),
26059                ..
26060            }
26061        )
26062    }
26063
26064    fn insert_extra_newline_brackets(
26065        buffer: &MultiBufferSnapshot,
26066        range: Range<MultiBufferOffset>,
26067        language: &language::LanguageScope,
26068    ) -> bool {
26069        let leading_whitespace_len = buffer
26070            .reversed_chars_at(range.start)
26071            .take_while(|c| c.is_whitespace() && *c != '\n')
26072            .map(|c| c.len_utf8())
26073            .sum::<usize>();
26074        let trailing_whitespace_len = buffer
26075            .chars_at(range.end)
26076            .take_while(|c| c.is_whitespace() && *c != '\n')
26077            .map(|c| c.len_utf8())
26078            .sum::<usize>();
26079        let range = range.start - leading_whitespace_len..range.end + trailing_whitespace_len;
26080
26081        language.brackets().any(|(pair, enabled)| {
26082            let pair_start = pair.start.trim_end();
26083            let pair_end = pair.end.trim_start();
26084
26085            enabled
26086                && pair.newline
26087                && buffer.contains_str_at(range.end, pair_end)
26088                && buffer.contains_str_at(
26089                    range.start.saturating_sub_usize(pair_start.len()),
26090                    pair_start,
26091                )
26092        })
26093    }
26094
26095    fn insert_extra_newline_tree_sitter(
26096        buffer: &MultiBufferSnapshot,
26097        range: Range<MultiBufferOffset>,
26098    ) -> bool {
26099        let (buffer, range) = match buffer
26100            .range_to_buffer_ranges(range.start..=range.end)
26101            .as_slice()
26102        {
26103            [(buffer, range, _)] => (*buffer, range.clone()),
26104            _ => return false,
26105        };
26106        let pair = {
26107            let mut result: Option<BracketMatch<usize>> = None;
26108
26109            for pair in buffer
26110                .all_bracket_ranges(range.start.0..range.end.0)
26111                .filter(move |pair| {
26112                    pair.open_range.start <= range.start.0 && pair.close_range.end >= range.end.0
26113                })
26114            {
26115                let len = pair.close_range.end - pair.open_range.start;
26116
26117                if let Some(existing) = &result {
26118                    let existing_len = existing.close_range.end - existing.open_range.start;
26119                    if len > existing_len {
26120                        continue;
26121                    }
26122                }
26123
26124                result = Some(pair);
26125            }
26126
26127            result
26128        };
26129        let Some(pair) = pair else {
26130            return false;
26131        };
26132        pair.newline_only
26133            && buffer
26134                .chars_for_range(pair.open_range.end..range.start.0)
26135                .chain(buffer.chars_for_range(range.end.0..pair.close_range.start))
26136                .all(|c| c.is_whitespace() && c != '\n')
26137    }
26138}
26139
26140fn update_uncommitted_diff_for_buffer(
26141    editor: Entity<Editor>,
26142    project: &Entity<Project>,
26143    buffers: impl IntoIterator<Item = Entity<Buffer>>,
26144    buffer: Entity<MultiBuffer>,
26145    cx: &mut App,
26146) -> Task<()> {
26147    let mut tasks = Vec::new();
26148    project.update(cx, |project, cx| {
26149        for buffer in buffers {
26150            if project::File::from_dyn(buffer.read(cx).file()).is_some() {
26151                tasks.push(project.open_uncommitted_diff(buffer.clone(), cx))
26152            }
26153        }
26154    });
26155    cx.spawn(async move |cx| {
26156        let diffs = future::join_all(tasks).await;
26157        if editor.read_with(cx, |editor, _cx| editor.temporary_diff_override) {
26158            return;
26159        }
26160
26161        buffer.update(cx, |buffer, cx| {
26162            for diff in diffs.into_iter().flatten() {
26163                buffer.add_diff(diff, cx);
26164            }
26165        });
26166    })
26167}
26168
26169fn char_len_with_expanded_tabs(offset: usize, text: &str, tab_size: NonZeroU32) -> usize {
26170    let tab_size = tab_size.get() as usize;
26171    let mut width = offset;
26172
26173    for ch in text.chars() {
26174        width += if ch == '\t' {
26175            tab_size - (width % tab_size)
26176        } else {
26177            1
26178        };
26179    }
26180
26181    width - offset
26182}
26183
26184#[cfg(test)]
26185mod tests {
26186    use super::*;
26187
26188    #[test]
26189    fn test_string_size_with_expanded_tabs() {
26190        let nz = |val| NonZeroU32::new(val).unwrap();
26191        assert_eq!(char_len_with_expanded_tabs(0, "", nz(4)), 0);
26192        assert_eq!(char_len_with_expanded_tabs(0, "hello", nz(4)), 5);
26193        assert_eq!(char_len_with_expanded_tabs(0, "\thello", nz(4)), 9);
26194        assert_eq!(char_len_with_expanded_tabs(0, "abc\tab", nz(4)), 6);
26195        assert_eq!(char_len_with_expanded_tabs(0, "hello\t", nz(4)), 8);
26196        assert_eq!(char_len_with_expanded_tabs(0, "\t\t", nz(8)), 16);
26197        assert_eq!(char_len_with_expanded_tabs(0, "x\t", nz(8)), 8);
26198        assert_eq!(char_len_with_expanded_tabs(7, "x\t", nz(8)), 9);
26199    }
26200}
26201
26202/// Tokenizes a string into runs of text that should stick together, or that is whitespace.
26203struct WordBreakingTokenizer<'a> {
26204    input: &'a str,
26205}
26206
26207impl<'a> WordBreakingTokenizer<'a> {
26208    fn new(input: &'a str) -> Self {
26209        Self { input }
26210    }
26211}
26212
26213fn is_char_ideographic(ch: char) -> bool {
26214    use unicode_script::Script::*;
26215    use unicode_script::UnicodeScript;
26216    matches!(ch.script(), Han | Tangut | Yi)
26217}
26218
26219fn is_grapheme_ideographic(text: &str) -> bool {
26220    text.chars().any(is_char_ideographic)
26221}
26222
26223fn is_grapheme_whitespace(text: &str) -> bool {
26224    text.chars().any(|x| x.is_whitespace())
26225}
26226
26227fn should_stay_with_preceding_ideograph(text: &str) -> bool {
26228    text.chars()
26229        .next()
26230        .is_some_and(|ch| matches!(ch, '。' | '、' | ',' | '?' | '!' | ':' | ';' | '…'))
26231}
26232
26233#[derive(PartialEq, Eq, Debug, Clone, Copy)]
26234enum WordBreakToken<'a> {
26235    Word { token: &'a str, grapheme_len: usize },
26236    InlineWhitespace { token: &'a str, grapheme_len: usize },
26237    Newline,
26238}
26239
26240impl<'a> Iterator for WordBreakingTokenizer<'a> {
26241    /// Yields a span, the count of graphemes in the token, and whether it was
26242    /// whitespace. Note that it also breaks at word boundaries.
26243    type Item = WordBreakToken<'a>;
26244
26245    fn next(&mut self) -> Option<Self::Item> {
26246        use unicode_segmentation::UnicodeSegmentation;
26247        if self.input.is_empty() {
26248            return None;
26249        }
26250
26251        let mut iter = self.input.graphemes(true).peekable();
26252        let mut offset = 0;
26253        let mut grapheme_len = 0;
26254        if let Some(first_grapheme) = iter.next() {
26255            let is_newline = first_grapheme == "\n";
26256            let is_whitespace = is_grapheme_whitespace(first_grapheme);
26257            offset += first_grapheme.len();
26258            grapheme_len += 1;
26259            if is_grapheme_ideographic(first_grapheme) && !is_whitespace {
26260                if let Some(grapheme) = iter.peek().copied()
26261                    && should_stay_with_preceding_ideograph(grapheme)
26262                {
26263                    offset += grapheme.len();
26264                    grapheme_len += 1;
26265                }
26266            } else {
26267                let mut words = self.input[offset..].split_word_bound_indices().peekable();
26268                let mut next_word_bound = words.peek().copied();
26269                if next_word_bound.is_some_and(|(i, _)| i == 0) {
26270                    next_word_bound = words.next();
26271                }
26272                while let Some(grapheme) = iter.peek().copied() {
26273                    if next_word_bound.is_some_and(|(i, _)| i == offset) {
26274                        break;
26275                    };
26276                    if is_grapheme_whitespace(grapheme) != is_whitespace
26277                        || (grapheme == "\n") != is_newline
26278                    {
26279                        break;
26280                    };
26281                    offset += grapheme.len();
26282                    grapheme_len += 1;
26283                    iter.next();
26284                }
26285            }
26286            let token = &self.input[..offset];
26287            self.input = &self.input[offset..];
26288            if token == "\n" {
26289                Some(WordBreakToken::Newline)
26290            } else if is_whitespace {
26291                Some(WordBreakToken::InlineWhitespace {
26292                    token,
26293                    grapheme_len,
26294                })
26295            } else {
26296                Some(WordBreakToken::Word {
26297                    token,
26298                    grapheme_len,
26299                })
26300            }
26301        } else {
26302            None
26303        }
26304    }
26305}
26306
26307#[test]
26308fn test_word_breaking_tokenizer() {
26309    let tests: &[(&str, &[WordBreakToken<'static>])] = &[
26310        ("", &[]),
26311        ("  ", &[whitespace("  ", 2)]),
26312        ("Ʒ", &[word("Ʒ", 1)]),
26313        ("Ǽ", &[word("Ǽ", 1)]),
26314        ("", &[word("", 1)]),
26315        ("⋑⋑", &[word("⋑⋑", 2)]),
26316        (
26317            "原理,进而",
26318            &[word("", 1), word("理,", 2), word("", 1), word("", 1)],
26319        ),
26320        (
26321            "hello world",
26322            &[word("hello", 5), whitespace(" ", 1), word("world", 5)],
26323        ),
26324        (
26325            "hello, world",
26326            &[word("hello,", 6), whitespace(" ", 1), word("world", 5)],
26327        ),
26328        (
26329            "  hello world",
26330            &[
26331                whitespace("  ", 2),
26332                word("hello", 5),
26333                whitespace(" ", 1),
26334                word("world", 5),
26335            ],
26336        ),
26337        (
26338            "这是什么 \n 钢笔",
26339            &[
26340                word("", 1),
26341                word("", 1),
26342                word("", 1),
26343                word("", 1),
26344                whitespace(" ", 1),
26345                newline(),
26346                whitespace(" ", 1),
26347                word("", 1),
26348                word("", 1),
26349            ],
26350        ),
26351        (" mutton", &[whitespace("", 1), word("mutton", 6)]),
26352    ];
26353
26354    fn word(token: &'static str, grapheme_len: usize) -> WordBreakToken<'static> {
26355        WordBreakToken::Word {
26356            token,
26357            grapheme_len,
26358        }
26359    }
26360
26361    fn whitespace(token: &'static str, grapheme_len: usize) -> WordBreakToken<'static> {
26362        WordBreakToken::InlineWhitespace {
26363            token,
26364            grapheme_len,
26365        }
26366    }
26367
26368    fn newline() -> WordBreakToken<'static> {
26369        WordBreakToken::Newline
26370    }
26371
26372    for (input, result) in tests {
26373        assert_eq!(
26374            WordBreakingTokenizer::new(input)
26375                .collect::<Vec<_>>()
26376                .as_slice(),
26377            *result,
26378        );
26379    }
26380}
26381
26382fn wrap_with_prefix(
26383    first_line_prefix: String,
26384    subsequent_lines_prefix: String,
26385    unwrapped_text: String,
26386    wrap_column: usize,
26387    tab_size: NonZeroU32,
26388    preserve_existing_whitespace: bool,
26389) -> String {
26390    let first_line_prefix_len = char_len_with_expanded_tabs(0, &first_line_prefix, tab_size);
26391    let subsequent_lines_prefix_len =
26392        char_len_with_expanded_tabs(0, &subsequent_lines_prefix, tab_size);
26393    let mut wrapped_text = String::new();
26394    let mut current_line = first_line_prefix;
26395    let mut is_first_line = true;
26396
26397    let tokenizer = WordBreakingTokenizer::new(&unwrapped_text);
26398    let mut current_line_len = first_line_prefix_len;
26399    let mut in_whitespace = false;
26400    for token in tokenizer {
26401        let have_preceding_whitespace = in_whitespace;
26402        match token {
26403            WordBreakToken::Word {
26404                token,
26405                grapheme_len,
26406            } => {
26407                in_whitespace = false;
26408                let current_prefix_len = if is_first_line {
26409                    first_line_prefix_len
26410                } else {
26411                    subsequent_lines_prefix_len
26412                };
26413                if current_line_len + grapheme_len > wrap_column
26414                    && current_line_len != current_prefix_len
26415                {
26416                    wrapped_text.push_str(current_line.trim_end());
26417                    wrapped_text.push('\n');
26418                    is_first_line = false;
26419                    current_line = subsequent_lines_prefix.clone();
26420                    current_line_len = subsequent_lines_prefix_len;
26421                }
26422                current_line.push_str(token);
26423                current_line_len += grapheme_len;
26424            }
26425            WordBreakToken::InlineWhitespace {
26426                mut token,
26427                mut grapheme_len,
26428            } => {
26429                in_whitespace = true;
26430                if have_preceding_whitespace && !preserve_existing_whitespace {
26431                    continue;
26432                }
26433                if !preserve_existing_whitespace {
26434                    // Keep a single whitespace grapheme as-is
26435                    if let Some(first) =
26436                        unicode_segmentation::UnicodeSegmentation::graphemes(token, true).next()
26437                    {
26438                        token = first;
26439                    } else {
26440                        token = " ";
26441                    }
26442                    grapheme_len = 1;
26443                }
26444                let current_prefix_len = if is_first_line {
26445                    first_line_prefix_len
26446                } else {
26447                    subsequent_lines_prefix_len
26448                };
26449                if current_line_len + grapheme_len > wrap_column {
26450                    wrapped_text.push_str(current_line.trim_end());
26451                    wrapped_text.push('\n');
26452                    is_first_line = false;
26453                    current_line = subsequent_lines_prefix.clone();
26454                    current_line_len = subsequent_lines_prefix_len;
26455                } else if current_line_len != current_prefix_len || preserve_existing_whitespace {
26456                    current_line.push_str(token);
26457                    current_line_len += grapheme_len;
26458                }
26459            }
26460            WordBreakToken::Newline => {
26461                in_whitespace = true;
26462                let current_prefix_len = if is_first_line {
26463                    first_line_prefix_len
26464                } else {
26465                    subsequent_lines_prefix_len
26466                };
26467                if preserve_existing_whitespace {
26468                    wrapped_text.push_str(current_line.trim_end());
26469                    wrapped_text.push('\n');
26470                    is_first_line = false;
26471                    current_line = subsequent_lines_prefix.clone();
26472                    current_line_len = subsequent_lines_prefix_len;
26473                } else if have_preceding_whitespace {
26474                    continue;
26475                } else if current_line_len + 1 > wrap_column
26476                    && current_line_len != current_prefix_len
26477                {
26478                    wrapped_text.push_str(current_line.trim_end());
26479                    wrapped_text.push('\n');
26480                    is_first_line = false;
26481                    current_line = subsequent_lines_prefix.clone();
26482                    current_line_len = subsequent_lines_prefix_len;
26483                } else if current_line_len != current_prefix_len {
26484                    current_line.push(' ');
26485                    current_line_len += 1;
26486                }
26487            }
26488        }
26489    }
26490
26491    if !current_line.is_empty() {
26492        wrapped_text.push_str(&current_line);
26493    }
26494    wrapped_text
26495}
26496
26497#[test]
26498fn test_wrap_with_prefix() {
26499    assert_eq!(
26500        wrap_with_prefix(
26501            "# ".to_string(),
26502            "# ".to_string(),
26503            "abcdefg".to_string(),
26504            4,
26505            NonZeroU32::new(4).unwrap(),
26506            false,
26507        ),
26508        "# abcdefg"
26509    );
26510    assert_eq!(
26511        wrap_with_prefix(
26512            "".to_string(),
26513            "".to_string(),
26514            "\thello world".to_string(),
26515            8,
26516            NonZeroU32::new(4).unwrap(),
26517            false,
26518        ),
26519        "hello\nworld"
26520    );
26521    assert_eq!(
26522        wrap_with_prefix(
26523            "// ".to_string(),
26524            "// ".to_string(),
26525            "xx \nyy zz aa bb cc".to_string(),
26526            12,
26527            NonZeroU32::new(4).unwrap(),
26528            false,
26529        ),
26530        "// xx yy zz\n// aa bb cc"
26531    );
26532    assert_eq!(
26533        wrap_with_prefix(
26534            String::new(),
26535            String::new(),
26536            "这是什么 \n 钢笔".to_string(),
26537            3,
26538            NonZeroU32::new(4).unwrap(),
26539            false,
26540        ),
26541        "这是什\n么 钢\n"
26542    );
26543    assert_eq!(
26544        wrap_with_prefix(
26545            String::new(),
26546            String::new(),
26547            format!("foo{}bar", '\u{2009}'), // thin space
26548            80,
26549            NonZeroU32::new(4).unwrap(),
26550            false,
26551        ),
26552        format!("foo{}bar", '\u{2009}')
26553    );
26554}
26555
26556pub trait CollaborationHub {
26557    fn collaborators<'a>(&self, cx: &'a App) -> &'a HashMap<PeerId, Collaborator>;
26558    fn user_participant_indices<'a>(&self, cx: &'a App) -> &'a HashMap<u64, ParticipantIndex>;
26559    fn user_names(&self, cx: &App) -> HashMap<u64, SharedString>;
26560}
26561
26562impl CollaborationHub for Entity<Project> {
26563    fn collaborators<'a>(&self, cx: &'a App) -> &'a HashMap<PeerId, Collaborator> {
26564        self.read(cx).collaborators()
26565    }
26566
26567    fn user_participant_indices<'a>(&self, cx: &'a App) -> &'a HashMap<u64, ParticipantIndex> {
26568        self.read(cx).user_store().read(cx).participant_indices()
26569    }
26570
26571    fn user_names(&self, cx: &App) -> HashMap<u64, SharedString> {
26572        let this = self.read(cx);
26573        let user_ids = this.collaborators().values().map(|c| c.user_id);
26574        this.user_store().read(cx).participant_names(user_ids, cx)
26575    }
26576}
26577
26578pub trait SemanticsProvider {
26579    fn hover(
26580        &self,
26581        buffer: &Entity<Buffer>,
26582        position: text::Anchor,
26583        cx: &mut App,
26584    ) -> Option<Task<Option<Vec<project::Hover>>>>;
26585
26586    fn inline_values(
26587        &self,
26588        buffer_handle: Entity<Buffer>,
26589        range: Range<text::Anchor>,
26590        cx: &mut App,
26591    ) -> Option<Task<anyhow::Result<Vec<InlayHint>>>>;
26592
26593    fn applicable_inlay_chunks(
26594        &self,
26595        buffer: &Entity<Buffer>,
26596        ranges: &[Range<text::Anchor>],
26597        cx: &mut App,
26598    ) -> Vec<Range<BufferRow>>;
26599
26600    fn invalidate_inlay_hints(&self, for_buffers: &HashSet<BufferId>, cx: &mut App);
26601
26602    fn inlay_hints(
26603        &self,
26604        invalidate: InvalidationStrategy,
26605        buffer: Entity<Buffer>,
26606        ranges: Vec<Range<text::Anchor>>,
26607        known_chunks: Option<(clock::Global, HashSet<Range<BufferRow>>)>,
26608        cx: &mut App,
26609    ) -> Option<HashMap<Range<BufferRow>, Task<Result<CacheInlayHints>>>>;
26610
26611    fn semantic_tokens(
26612        &self,
26613        buffer: Entity<Buffer>,
26614        refresh: Option<RefreshForServer>,
26615        cx: &mut App,
26616    ) -> Shared<Task<std::result::Result<BufferSemanticTokens, Arc<anyhow::Error>>>>;
26617
26618    fn supports_inlay_hints(&self, buffer: &Entity<Buffer>, cx: &mut App) -> bool;
26619
26620    fn supports_semantic_tokens(&self, buffer: &Entity<Buffer>, cx: &mut App) -> bool;
26621
26622    fn document_highlights(
26623        &self,
26624        buffer: &Entity<Buffer>,
26625        position: text::Anchor,
26626        cx: &mut App,
26627    ) -> Option<Task<Result<Vec<DocumentHighlight>>>>;
26628
26629    fn definitions(
26630        &self,
26631        buffer: &Entity<Buffer>,
26632        position: text::Anchor,
26633        kind: GotoDefinitionKind,
26634        cx: &mut App,
26635    ) -> Option<Task<Result<Option<Vec<LocationLink>>>>>;
26636
26637    fn range_for_rename(
26638        &self,
26639        buffer: &Entity<Buffer>,
26640        position: text::Anchor,
26641        cx: &mut App,
26642    ) -> Option<Task<Result<Option<Range<text::Anchor>>>>>;
26643
26644    fn perform_rename(
26645        &self,
26646        buffer: &Entity<Buffer>,
26647        position: text::Anchor,
26648        new_name: String,
26649        cx: &mut App,
26650    ) -> Option<Task<Result<ProjectTransaction>>>;
26651}
26652
26653pub trait CompletionProvider {
26654    fn completions(
26655        &self,
26656        excerpt_id: ExcerptId,
26657        buffer: &Entity<Buffer>,
26658        buffer_position: text::Anchor,
26659        trigger: CompletionContext,
26660        window: &mut Window,
26661        cx: &mut Context<Editor>,
26662    ) -> Task<Result<Vec<CompletionResponse>>>;
26663
26664    fn resolve_completions(
26665        &self,
26666        _buffer: Entity<Buffer>,
26667        _completion_indices: Vec<usize>,
26668        _completions: Rc<RefCell<Box<[Completion]>>>,
26669        _cx: &mut Context<Editor>,
26670    ) -> Task<Result<bool>> {
26671        Task::ready(Ok(false))
26672    }
26673
26674    fn apply_additional_edits_for_completion(
26675        &self,
26676        _buffer: Entity<Buffer>,
26677        _completions: Rc<RefCell<Box<[Completion]>>>,
26678        _completion_index: usize,
26679        _push_to_history: bool,
26680        _cx: &mut Context<Editor>,
26681    ) -> Task<Result<Option<language::Transaction>>> {
26682        Task::ready(Ok(None))
26683    }
26684
26685    fn is_completion_trigger(
26686        &self,
26687        buffer: &Entity<Buffer>,
26688        position: language::Anchor,
26689        text: &str,
26690        trigger_in_words: bool,
26691        cx: &mut Context<Editor>,
26692    ) -> bool;
26693
26694    fn selection_changed(&self, _mat: Option<&StringMatch>, _window: &mut Window, _cx: &mut App) {}
26695
26696    fn sort_completions(&self) -> bool {
26697        true
26698    }
26699
26700    fn filter_completions(&self) -> bool {
26701        true
26702    }
26703
26704    fn show_snippets(&self) -> bool {
26705        false
26706    }
26707}
26708
26709pub trait CodeActionProvider {
26710    fn id(&self) -> Arc<str>;
26711
26712    fn code_actions(
26713        &self,
26714        buffer: &Entity<Buffer>,
26715        range: Range<text::Anchor>,
26716        window: &mut Window,
26717        cx: &mut App,
26718    ) -> Task<Result<Vec<CodeAction>>>;
26719
26720    fn apply_code_action(
26721        &self,
26722        buffer_handle: Entity<Buffer>,
26723        action: CodeAction,
26724        excerpt_id: ExcerptId,
26725        push_to_history: bool,
26726        window: &mut Window,
26727        cx: &mut App,
26728    ) -> Task<Result<ProjectTransaction>>;
26729}
26730
26731impl CodeActionProvider for Entity<Project> {
26732    fn id(&self) -> Arc<str> {
26733        "project".into()
26734    }
26735
26736    fn code_actions(
26737        &self,
26738        buffer: &Entity<Buffer>,
26739        range: Range<text::Anchor>,
26740        _window: &mut Window,
26741        cx: &mut App,
26742    ) -> Task<Result<Vec<CodeAction>>> {
26743        self.update(cx, |project, cx| {
26744            let code_lens_actions = project.code_lens_actions(buffer, range.clone(), cx);
26745            let code_actions = project.code_actions(buffer, range, None, cx);
26746            cx.background_spawn(async move {
26747                let (code_lens_actions, code_actions) = join(code_lens_actions, code_actions).await;
26748                Ok(code_lens_actions
26749                    .context("code lens fetch")?
26750                    .into_iter()
26751                    .flatten()
26752                    .chain(
26753                        code_actions
26754                            .context("code action fetch")?
26755                            .into_iter()
26756                            .flatten(),
26757                    )
26758                    .collect())
26759            })
26760        })
26761    }
26762
26763    fn apply_code_action(
26764        &self,
26765        buffer_handle: Entity<Buffer>,
26766        action: CodeAction,
26767        _excerpt_id: ExcerptId,
26768        push_to_history: bool,
26769        _window: &mut Window,
26770        cx: &mut App,
26771    ) -> Task<Result<ProjectTransaction>> {
26772        self.update(cx, |project, cx| {
26773            project.apply_code_action(buffer_handle, action, push_to_history, cx)
26774        })
26775    }
26776}
26777
26778fn snippet_completions(
26779    project: &Project,
26780    buffer: &Entity<Buffer>,
26781    buffer_anchor: text::Anchor,
26782    classifier: CharClassifier,
26783    cx: &mut App,
26784) -> Task<Result<CompletionResponse>> {
26785    let languages = buffer.read(cx).languages_at(buffer_anchor);
26786    let snippet_store = project.snippets().read(cx);
26787
26788    let scopes: Vec<_> = languages
26789        .iter()
26790        .filter_map(|language| {
26791            let language_name = language.lsp_id();
26792            let snippets = snippet_store.snippets_for(Some(language_name), cx);
26793
26794            if snippets.is_empty() {
26795                None
26796            } else {
26797                Some((language.default_scope(), snippets))
26798            }
26799        })
26800        .collect();
26801
26802    if scopes.is_empty() {
26803        return Task::ready(Ok(CompletionResponse {
26804            completions: vec![],
26805            display_options: CompletionDisplayOptions::default(),
26806            is_incomplete: false,
26807        }));
26808    }
26809
26810    let snapshot = buffer.read(cx).text_snapshot();
26811    let executor = cx.background_executor().clone();
26812
26813    cx.background_spawn(async move {
26814        let is_word_char = |c| classifier.is_word(c);
26815
26816        let mut is_incomplete = false;
26817        let mut completions: Vec<Completion> = Vec::new();
26818
26819        const MAX_PREFIX_LEN: usize = 128;
26820        let buffer_offset = text::ToOffset::to_offset(&buffer_anchor, &snapshot);
26821        let window_start = buffer_offset.saturating_sub(MAX_PREFIX_LEN);
26822        let window_start = snapshot.clip_offset(window_start, Bias::Left);
26823
26824        let max_buffer_window: String = snapshot
26825            .text_for_range(window_start..buffer_offset)
26826            .collect();
26827
26828        if max_buffer_window.is_empty() {
26829            return Ok(CompletionResponse {
26830                completions: vec![],
26831                display_options: CompletionDisplayOptions::default(),
26832                is_incomplete: true,
26833            });
26834        }
26835
26836        for (_scope, snippets) in scopes.into_iter() {
26837            // Sort snippets by word count to match longer snippet prefixes first.
26838            let mut sorted_snippet_candidates = snippets
26839                .iter()
26840                .enumerate()
26841                .flat_map(|(snippet_ix, snippet)| {
26842                    snippet
26843                        .prefix
26844                        .iter()
26845                        .enumerate()
26846                        .map(move |(prefix_ix, prefix)| {
26847                            let word_count =
26848                                snippet_candidate_suffixes(prefix, is_word_char).count();
26849                            ((snippet_ix, prefix_ix), prefix, word_count)
26850                        })
26851                })
26852                .collect_vec();
26853            sorted_snippet_candidates
26854                .sort_unstable_by_key(|(_, _, word_count)| Reverse(*word_count));
26855
26856            // Each prefix may be matched multiple times; the completion menu must filter out duplicates.
26857
26858            let buffer_windows = snippet_candidate_suffixes(&max_buffer_window, is_word_char)
26859                .take(
26860                    sorted_snippet_candidates
26861                        .first()
26862                        .map(|(_, _, word_count)| *word_count)
26863                        .unwrap_or_default(),
26864                )
26865                .collect_vec();
26866
26867            const MAX_RESULTS: usize = 100;
26868            // Each match also remembers how many characters from the buffer it consumed
26869            let mut matches: Vec<(StringMatch, usize)> = vec![];
26870
26871            let mut snippet_list_cutoff_index = 0;
26872            for (buffer_index, buffer_window) in buffer_windows.iter().enumerate().rev() {
26873                let word_count = buffer_index + 1;
26874                // Increase `snippet_list_cutoff_index` until we have all of the
26875                // snippets with sufficiently many words.
26876                while sorted_snippet_candidates
26877                    .get(snippet_list_cutoff_index)
26878                    .is_some_and(|(_ix, _prefix, snippet_word_count)| {
26879                        *snippet_word_count >= word_count
26880                    })
26881                {
26882                    snippet_list_cutoff_index += 1;
26883                }
26884
26885                // Take only the candidates with at least `word_count` many words
26886                let snippet_candidates_at_word_len =
26887                    &sorted_snippet_candidates[..snippet_list_cutoff_index];
26888
26889                let candidates = snippet_candidates_at_word_len
26890                    .iter()
26891                    .map(|(_snippet_ix, prefix, _snippet_word_count)| prefix)
26892                    .enumerate() // index in `sorted_snippet_candidates`
26893                    // First char must match
26894                    .filter(|(_ix, prefix)| {
26895                        itertools::equal(
26896                            prefix
26897                                .chars()
26898                                .next()
26899                                .into_iter()
26900                                .flat_map(|c| c.to_lowercase()),
26901                            buffer_window
26902                                .chars()
26903                                .next()
26904                                .into_iter()
26905                                .flat_map(|c| c.to_lowercase()),
26906                        )
26907                    })
26908                    .map(|(ix, prefix)| StringMatchCandidate::new(ix, prefix))
26909                    .collect::<Vec<StringMatchCandidate>>();
26910
26911                matches.extend(
26912                    fuzzy::match_strings(
26913                        &candidates,
26914                        &buffer_window,
26915                        buffer_window.chars().any(|c| c.is_uppercase()),
26916                        true,
26917                        MAX_RESULTS - matches.len(), // always prioritize longer snippets
26918                        &Default::default(),
26919                        executor.clone(),
26920                    )
26921                    .await
26922                    .into_iter()
26923                    .map(|string_match| (string_match, buffer_window.len())),
26924                );
26925
26926                if matches.len() >= MAX_RESULTS {
26927                    break;
26928                }
26929            }
26930
26931            let to_lsp = |point: &text::Anchor| {
26932                let end = text::ToPointUtf16::to_point_utf16(point, &snapshot);
26933                point_to_lsp(end)
26934            };
26935            let lsp_end = to_lsp(&buffer_anchor);
26936
26937            if matches.len() >= MAX_RESULTS {
26938                is_incomplete = true;
26939            }
26940
26941            completions.extend(matches.iter().map(|(string_match, buffer_window_len)| {
26942                let ((snippet_index, prefix_index), matching_prefix, _snippet_word_count) =
26943                    sorted_snippet_candidates[string_match.candidate_id];
26944                let snippet = &snippets[snippet_index];
26945                let start = buffer_offset - buffer_window_len;
26946                let start = snapshot.anchor_before(start);
26947                let range = start..buffer_anchor;
26948                let lsp_start = to_lsp(&start);
26949                let lsp_range = lsp::Range {
26950                    start: lsp_start,
26951                    end: lsp_end,
26952                };
26953                Completion {
26954                    replace_range: range,
26955                    new_text: snippet.body.clone(),
26956                    source: CompletionSource::Lsp {
26957                        insert_range: None,
26958                        server_id: LanguageServerId(usize::MAX),
26959                        resolved: true,
26960                        lsp_completion: Box::new(lsp::CompletionItem {
26961                            label: snippet.prefix.first().unwrap().clone(),
26962                            kind: Some(CompletionItemKind::SNIPPET),
26963                            label_details: snippet.description.as_ref().map(|description| {
26964                                lsp::CompletionItemLabelDetails {
26965                                    detail: Some(description.clone()),
26966                                    description: None,
26967                                }
26968                            }),
26969                            insert_text_format: Some(InsertTextFormat::SNIPPET),
26970                            text_edit: Some(lsp::CompletionTextEdit::InsertAndReplace(
26971                                lsp::InsertReplaceEdit {
26972                                    new_text: snippet.body.clone(),
26973                                    insert: lsp_range,
26974                                    replace: lsp_range,
26975                                },
26976                            )),
26977                            filter_text: Some(snippet.body.clone()),
26978                            sort_text: Some(char::MAX.to_string()),
26979                            ..lsp::CompletionItem::default()
26980                        }),
26981                        lsp_defaults: None,
26982                    },
26983                    label: CodeLabel {
26984                        text: matching_prefix.clone(),
26985                        runs: Vec::new(),
26986                        filter_range: 0..matching_prefix.len(),
26987                    },
26988                    icon_path: None,
26989                    documentation: Some(CompletionDocumentation::SingleLineAndMultiLinePlainText {
26990                        single_line: snippet.name.clone().into(),
26991                        plain_text: snippet
26992                            .description
26993                            .clone()
26994                            .map(|description| description.into()),
26995                    }),
26996                    insert_text_mode: None,
26997                    confirm: None,
26998                    match_start: Some(start),
26999                    snippet_deduplication_key: Some((snippet_index, prefix_index)),
27000                }
27001            }));
27002        }
27003
27004        Ok(CompletionResponse {
27005            completions,
27006            display_options: CompletionDisplayOptions::default(),
27007            is_incomplete,
27008        })
27009    })
27010}
27011
27012impl CompletionProvider for Entity<Project> {
27013    fn completions(
27014        &self,
27015        _excerpt_id: ExcerptId,
27016        buffer: &Entity<Buffer>,
27017        buffer_position: text::Anchor,
27018        options: CompletionContext,
27019        _window: &mut Window,
27020        cx: &mut Context<Editor>,
27021    ) -> Task<Result<Vec<CompletionResponse>>> {
27022        self.update(cx, |project, cx| {
27023            let task = project.completions(buffer, buffer_position, options, cx);
27024            cx.background_spawn(task)
27025        })
27026    }
27027
27028    fn resolve_completions(
27029        &self,
27030        buffer: Entity<Buffer>,
27031        completion_indices: Vec<usize>,
27032        completions: Rc<RefCell<Box<[Completion]>>>,
27033        cx: &mut Context<Editor>,
27034    ) -> Task<Result<bool>> {
27035        self.update(cx, |project, cx| {
27036            project.lsp_store().update(cx, |lsp_store, cx| {
27037                lsp_store.resolve_completions(buffer, completion_indices, completions, cx)
27038            })
27039        })
27040    }
27041
27042    fn apply_additional_edits_for_completion(
27043        &self,
27044        buffer: Entity<Buffer>,
27045        completions: Rc<RefCell<Box<[Completion]>>>,
27046        completion_index: usize,
27047        push_to_history: bool,
27048        cx: &mut Context<Editor>,
27049    ) -> Task<Result<Option<language::Transaction>>> {
27050        self.update(cx, |project, cx| {
27051            project.lsp_store().update(cx, |lsp_store, cx| {
27052                lsp_store.apply_additional_edits_for_completion(
27053                    buffer,
27054                    completions,
27055                    completion_index,
27056                    push_to_history,
27057                    cx,
27058                )
27059            })
27060        })
27061    }
27062
27063    fn is_completion_trigger(
27064        &self,
27065        buffer: &Entity<Buffer>,
27066        position: language::Anchor,
27067        text: &str,
27068        trigger_in_words: bool,
27069        cx: &mut Context<Editor>,
27070    ) -> bool {
27071        let mut chars = text.chars();
27072        let char = if let Some(char) = chars.next() {
27073            char
27074        } else {
27075            return false;
27076        };
27077        if chars.next().is_some() {
27078            return false;
27079        }
27080
27081        let buffer = buffer.read(cx);
27082        let snapshot = buffer.snapshot();
27083        let classifier = snapshot
27084            .char_classifier_at(position)
27085            .scope_context(Some(CharScopeContext::Completion));
27086        if trigger_in_words && classifier.is_word(char) {
27087            return true;
27088        }
27089
27090        buffer.completion_triggers().contains(text)
27091    }
27092
27093    fn show_snippets(&self) -> bool {
27094        true
27095    }
27096}
27097
27098impl SemanticsProvider for Entity<Project> {
27099    fn hover(
27100        &self,
27101        buffer: &Entity<Buffer>,
27102        position: text::Anchor,
27103        cx: &mut App,
27104    ) -> Option<Task<Option<Vec<project::Hover>>>> {
27105        Some(self.update(cx, |project, cx| project.hover(buffer, position, cx)))
27106    }
27107
27108    fn document_highlights(
27109        &self,
27110        buffer: &Entity<Buffer>,
27111        position: text::Anchor,
27112        cx: &mut App,
27113    ) -> Option<Task<Result<Vec<DocumentHighlight>>>> {
27114        Some(self.update(cx, |project, cx| {
27115            project.document_highlights(buffer, position, cx)
27116        }))
27117    }
27118
27119    fn definitions(
27120        &self,
27121        buffer: &Entity<Buffer>,
27122        position: text::Anchor,
27123        kind: GotoDefinitionKind,
27124        cx: &mut App,
27125    ) -> Option<Task<Result<Option<Vec<LocationLink>>>>> {
27126        Some(self.update(cx, |project, cx| match kind {
27127            GotoDefinitionKind::Symbol => project.definitions(buffer, position, cx),
27128            GotoDefinitionKind::Declaration => project.declarations(buffer, position, cx),
27129            GotoDefinitionKind::Type => project.type_definitions(buffer, position, cx),
27130            GotoDefinitionKind::Implementation => project.implementations(buffer, position, cx),
27131        }))
27132    }
27133
27134    fn supports_inlay_hints(&self, buffer: &Entity<Buffer>, cx: &mut App) -> bool {
27135        self.update(cx, |project, cx| {
27136            if project
27137                .active_debug_session(cx)
27138                .is_some_and(|(session, _)| session.read(cx).any_stopped_thread())
27139            {
27140                return true;
27141            }
27142
27143            buffer.update(cx, |buffer, cx| {
27144                project.any_language_server_supports_inlay_hints(buffer, cx)
27145            })
27146        })
27147    }
27148
27149    fn supports_semantic_tokens(&self, buffer: &Entity<Buffer>, cx: &mut App) -> bool {
27150        self.update(cx, |project, cx| {
27151            buffer.update(cx, |buffer, cx| {
27152                project.any_language_server_supports_semantic_tokens(buffer, cx)
27153            })
27154        })
27155    }
27156
27157    fn inline_values(
27158        &self,
27159        buffer_handle: Entity<Buffer>,
27160        range: Range<text::Anchor>,
27161        cx: &mut App,
27162    ) -> Option<Task<anyhow::Result<Vec<InlayHint>>>> {
27163        self.update(cx, |project, cx| {
27164            let (session, active_stack_frame) = project.active_debug_session(cx)?;
27165
27166            Some(project.inline_values(session, active_stack_frame, buffer_handle, range, cx))
27167        })
27168    }
27169
27170    fn applicable_inlay_chunks(
27171        &self,
27172        buffer: &Entity<Buffer>,
27173        ranges: &[Range<text::Anchor>],
27174        cx: &mut App,
27175    ) -> Vec<Range<BufferRow>> {
27176        self.read(cx).lsp_store().update(cx, |lsp_store, cx| {
27177            lsp_store.applicable_inlay_chunks(buffer, ranges, cx)
27178        })
27179    }
27180
27181    fn invalidate_inlay_hints(&self, for_buffers: &HashSet<BufferId>, cx: &mut App) {
27182        self.read(cx).lsp_store().update(cx, |lsp_store, _| {
27183            lsp_store.invalidate_inlay_hints(for_buffers)
27184        });
27185    }
27186
27187    fn inlay_hints(
27188        &self,
27189        invalidate: InvalidationStrategy,
27190        buffer: Entity<Buffer>,
27191        ranges: Vec<Range<text::Anchor>>,
27192        known_chunks: Option<(clock::Global, HashSet<Range<BufferRow>>)>,
27193        cx: &mut App,
27194    ) -> Option<HashMap<Range<BufferRow>, Task<Result<CacheInlayHints>>>> {
27195        Some(self.read(cx).lsp_store().update(cx, |lsp_store, cx| {
27196            lsp_store.inlay_hints(invalidate, buffer, ranges, known_chunks, cx)
27197        }))
27198    }
27199
27200    fn semantic_tokens(
27201        &self,
27202        buffer: Entity<Buffer>,
27203        refresh: Option<RefreshForServer>,
27204        cx: &mut App,
27205    ) -> Shared<Task<std::result::Result<BufferSemanticTokens, Arc<anyhow::Error>>>> {
27206        self.read(cx).lsp_store().update(cx, |lsp_store, cx| {
27207            lsp_store.semantic_tokens(buffer, refresh, cx)
27208        })
27209    }
27210
27211    fn range_for_rename(
27212        &self,
27213        buffer: &Entity<Buffer>,
27214        position: text::Anchor,
27215        cx: &mut App,
27216    ) -> Option<Task<Result<Option<Range<text::Anchor>>>>> {
27217        Some(self.update(cx, |project, cx| {
27218            let buffer = buffer.clone();
27219            let task = project.prepare_rename(buffer.clone(), position, cx);
27220            cx.spawn(async move |_, cx| {
27221                Ok(match task.await? {
27222                    PrepareRenameResponse::Success(range) => Some(range),
27223                    PrepareRenameResponse::InvalidPosition => None,
27224                    PrepareRenameResponse::OnlyUnpreparedRenameSupported => {
27225                        // Fallback on using TreeSitter info to determine identifier range
27226                        buffer.read_with(cx, |buffer, _| {
27227                            let snapshot = buffer.snapshot();
27228                            let (range, kind) = snapshot.surrounding_word(position, None);
27229                            if kind != Some(CharKind::Word) {
27230                                return None;
27231                            }
27232                            Some(
27233                                snapshot.anchor_before(range.start)
27234                                    ..snapshot.anchor_after(range.end),
27235                            )
27236                        })
27237                    }
27238                })
27239            })
27240        }))
27241    }
27242
27243    fn perform_rename(
27244        &self,
27245        buffer: &Entity<Buffer>,
27246        position: text::Anchor,
27247        new_name: String,
27248        cx: &mut App,
27249    ) -> Option<Task<Result<ProjectTransaction>>> {
27250        Some(self.update(cx, |project, cx| {
27251            project.perform_rename(buffer.clone(), position, new_name, cx)
27252        }))
27253    }
27254}
27255
27256fn consume_contiguous_rows(
27257    contiguous_row_selections: &mut Vec<Selection<Point>>,
27258    selection: &Selection<Point>,
27259    display_map: &DisplaySnapshot,
27260    selections: &mut Peekable<std::slice::Iter<Selection<Point>>>,
27261) -> (MultiBufferRow, MultiBufferRow) {
27262    contiguous_row_selections.push(selection.clone());
27263    let start_row = starting_row(selection, display_map);
27264    let mut end_row = ending_row(selection, display_map);
27265
27266    while let Some(next_selection) = selections.peek() {
27267        if next_selection.start.row <= end_row.0 {
27268            end_row = ending_row(next_selection, display_map);
27269            contiguous_row_selections.push(selections.next().unwrap().clone());
27270        } else {
27271            break;
27272        }
27273    }
27274    (start_row, end_row)
27275}
27276
27277fn starting_row(selection: &Selection<Point>, display_map: &DisplaySnapshot) -> MultiBufferRow {
27278    if selection.start.column > 0 {
27279        MultiBufferRow(display_map.prev_line_boundary(selection.start).0.row)
27280    } else {
27281        MultiBufferRow(selection.start.row)
27282    }
27283}
27284
27285fn ending_row(next_selection: &Selection<Point>, display_map: &DisplaySnapshot) -> MultiBufferRow {
27286    if next_selection.end.column > 0 || next_selection.is_empty() {
27287        MultiBufferRow(display_map.next_line_boundary(next_selection.end).0.row + 1)
27288    } else {
27289        MultiBufferRow(next_selection.end.row)
27290    }
27291}
27292
27293impl EditorSnapshot {
27294    pub fn remote_selections_in_range<'a>(
27295        &'a self,
27296        range: &'a Range<Anchor>,
27297        collaboration_hub: &dyn CollaborationHub,
27298        cx: &'a App,
27299    ) -> impl 'a + Iterator<Item = RemoteSelection> {
27300        let participant_names = collaboration_hub.user_names(cx);
27301        let participant_indices = collaboration_hub.user_participant_indices(cx);
27302        let collaborators_by_peer_id = collaboration_hub.collaborators(cx);
27303        let collaborators_by_replica_id = collaborators_by_peer_id
27304            .values()
27305            .map(|collaborator| (collaborator.replica_id, collaborator))
27306            .collect::<HashMap<_, _>>();
27307        self.buffer_snapshot()
27308            .selections_in_range(range, false)
27309            .filter_map(move |(replica_id, line_mode, cursor_shape, selection)| {
27310                if replica_id == ReplicaId::AGENT {
27311                    Some(RemoteSelection {
27312                        replica_id,
27313                        selection,
27314                        cursor_shape,
27315                        line_mode,
27316                        collaborator_id: CollaboratorId::Agent,
27317                        user_name: Some("Agent".into()),
27318                        color: cx.theme().players().agent(),
27319                    })
27320                } else {
27321                    let collaborator = collaborators_by_replica_id.get(&replica_id)?;
27322                    let participant_index = participant_indices.get(&collaborator.user_id).copied();
27323                    let user_name = participant_names.get(&collaborator.user_id).cloned();
27324                    Some(RemoteSelection {
27325                        replica_id,
27326                        selection,
27327                        cursor_shape,
27328                        line_mode,
27329                        collaborator_id: CollaboratorId::PeerId(collaborator.peer_id),
27330                        user_name,
27331                        color: if let Some(index) = participant_index {
27332                            cx.theme().players().color_for_participant(index.0)
27333                        } else {
27334                            cx.theme().players().absent()
27335                        },
27336                    })
27337                }
27338            })
27339    }
27340
27341    pub fn hunks_for_ranges(
27342        &self,
27343        ranges: impl IntoIterator<Item = Range<Point>>,
27344    ) -> Vec<MultiBufferDiffHunk> {
27345        let mut hunks = Vec::new();
27346        let mut processed_buffer_rows: HashMap<BufferId, HashSet<Range<text::Anchor>>> =
27347            HashMap::default();
27348        for query_range in ranges {
27349            let query_rows =
27350                MultiBufferRow(query_range.start.row)..MultiBufferRow(query_range.end.row + 1);
27351            for hunk in self.buffer_snapshot().diff_hunks_in_range(
27352                Point::new(query_rows.start.0, 0)..Point::new(query_rows.end.0, 0),
27353            ) {
27354                // Include deleted hunks that are adjacent to the query range, because
27355                // otherwise they would be missed.
27356                let mut intersects_range = hunk.row_range.overlaps(&query_rows);
27357                if hunk.status().is_deleted() {
27358                    intersects_range |= hunk.row_range.start == query_rows.end;
27359                    intersects_range |= hunk.row_range.end == query_rows.start;
27360                }
27361                if intersects_range {
27362                    if !processed_buffer_rows
27363                        .entry(hunk.buffer_id)
27364                        .or_default()
27365                        .insert(hunk.buffer_range.start..hunk.buffer_range.end)
27366                    {
27367                        continue;
27368                    }
27369                    hunks.push(hunk);
27370                }
27371            }
27372        }
27373
27374        hunks
27375    }
27376
27377    fn display_diff_hunks_for_rows<'a>(
27378        &'a self,
27379        display_rows: Range<DisplayRow>,
27380        folded_buffers: &'a HashSet<BufferId>,
27381    ) -> impl 'a + Iterator<Item = DisplayDiffHunk> {
27382        let buffer_start = DisplayPoint::new(display_rows.start, 0).to_point(self);
27383        let buffer_end = DisplayPoint::new(display_rows.end, 0).to_point(self);
27384
27385        self.buffer_snapshot()
27386            .diff_hunks_in_range(buffer_start..buffer_end)
27387            .filter_map(|hunk| {
27388                if folded_buffers.contains(&hunk.buffer_id)
27389                    || (hunk.row_range.is_empty() && self.buffer.all_diff_hunks_expanded())
27390                {
27391                    return None;
27392                }
27393
27394                let hunk_start_point = Point::new(hunk.row_range.start.0, 0);
27395                let hunk_end_point = if hunk.row_range.end > hunk.row_range.start {
27396                    let last_row = MultiBufferRow(hunk.row_range.end.0 - 1);
27397                    let line_len = self.buffer_snapshot().line_len(last_row);
27398                    Point::new(last_row.0, line_len)
27399                } else {
27400                    Point::new(hunk.row_range.end.0, 0)
27401                };
27402
27403                let hunk_display_start = self.point_to_display_point(hunk_start_point, Bias::Left);
27404                let hunk_display_end = self.point_to_display_point(hunk_end_point, Bias::Right);
27405
27406                let display_hunk = if hunk_display_start.column() != 0 {
27407                    DisplayDiffHunk::Folded {
27408                        display_row: hunk_display_start.row(),
27409                    }
27410                } else {
27411                    let mut end_row = hunk_display_end.row();
27412                    if hunk.row_range.end > hunk.row_range.start || hunk_display_end.column() > 0 {
27413                        end_row.0 += 1;
27414                    }
27415                    let is_created_file = hunk.is_created_file();
27416
27417                    DisplayDiffHunk::Unfolded {
27418                        status: hunk.status(),
27419                        diff_base_byte_range: hunk.diff_base_byte_range.start.0
27420                            ..hunk.diff_base_byte_range.end.0,
27421                        word_diffs: hunk.word_diffs,
27422                        display_row_range: hunk_display_start.row()..end_row,
27423                        multi_buffer_range: Anchor::range_in_buffer(
27424                            hunk.excerpt_id,
27425                            hunk.buffer_range,
27426                        ),
27427                        is_created_file,
27428                    }
27429                };
27430
27431                Some(display_hunk)
27432            })
27433    }
27434
27435    pub fn language_at<T: ToOffset>(&self, position: T) -> Option<&Arc<Language>> {
27436        self.display_snapshot
27437            .buffer_snapshot()
27438            .language_at(position)
27439    }
27440
27441    pub fn is_focused(&self) -> bool {
27442        self.is_focused
27443    }
27444
27445    pub fn placeholder_text(&self) -> Option<String> {
27446        self.placeholder_display_snapshot
27447            .as_ref()
27448            .map(|display_map| display_map.text())
27449    }
27450
27451    pub fn scroll_position(&self) -> gpui::Point<ScrollOffset> {
27452        self.scroll_anchor.scroll_position(&self.display_snapshot)
27453    }
27454
27455    pub fn gutter_dimensions(
27456        &self,
27457        font_id: FontId,
27458        font_size: Pixels,
27459        style: &EditorStyle,
27460        window: &mut Window,
27461        cx: &App,
27462    ) -> GutterDimensions {
27463        if self.show_gutter
27464            && let Some(ch_width) = cx.text_system().ch_width(font_id, font_size).log_err()
27465            && let Some(ch_advance) = cx.text_system().ch_advance(font_id, font_size).log_err()
27466        {
27467            let show_git_gutter = self.show_git_diff_gutter.unwrap_or_else(|| {
27468                matches!(
27469                    ProjectSettings::get_global(cx).git.git_gutter,
27470                    GitGutterSetting::TrackedFiles
27471                )
27472            });
27473            let gutter_settings = EditorSettings::get_global(cx).gutter;
27474            let show_line_numbers = self
27475                .show_line_numbers
27476                .unwrap_or(gutter_settings.line_numbers);
27477            let line_gutter_width = if show_line_numbers {
27478                // Avoid flicker-like gutter resizes when the line number gains another digit by
27479                // only resizing the gutter on files with > 10**min_line_number_digits lines.
27480                let min_width_for_number_on_gutter =
27481                    ch_advance * gutter_settings.min_line_number_digits as f32;
27482                self.max_line_number_width(style, window)
27483                    .max(min_width_for_number_on_gutter)
27484            } else {
27485                0.0.into()
27486            };
27487
27488            let show_runnables = self.show_runnables.unwrap_or(gutter_settings.runnables);
27489            let show_breakpoints = self.show_breakpoints.unwrap_or(gutter_settings.breakpoints);
27490
27491            let git_blame_entries_width =
27492                self.git_blame_gutter_max_author_length
27493                    .map(|max_author_length| {
27494                        let renderer = cx.global::<GlobalBlameRenderer>().0.clone();
27495                        const MAX_RELATIVE_TIMESTAMP: &str = "60 minutes ago";
27496
27497                        /// The number of characters to dedicate to gaps and margins.
27498                        const SPACING_WIDTH: usize = 4;
27499
27500                        let max_char_count = max_author_length.min(renderer.max_author_length())
27501                            + ::git::SHORT_SHA_LENGTH
27502                            + MAX_RELATIVE_TIMESTAMP.len()
27503                            + SPACING_WIDTH;
27504
27505                        ch_advance * max_char_count
27506                    });
27507
27508            let is_singleton = self.buffer_snapshot().is_singleton();
27509
27510            let mut left_padding = git_blame_entries_width.unwrap_or(Pixels::ZERO);
27511            left_padding += if !is_singleton {
27512                ch_width * 4.0
27513            } else if show_runnables || show_breakpoints {
27514                ch_width * 3.0
27515            } else if show_git_gutter && show_line_numbers {
27516                ch_width * 2.0
27517            } else if show_git_gutter || show_line_numbers {
27518                ch_width
27519            } else {
27520                px(0.)
27521            };
27522
27523            let shows_folds = is_singleton && gutter_settings.folds;
27524
27525            let right_padding = if shows_folds && show_line_numbers {
27526                ch_width * 4.0
27527            } else if shows_folds || (!is_singleton && show_line_numbers) {
27528                ch_width * 3.0
27529            } else if show_line_numbers {
27530                ch_width
27531            } else {
27532                px(0.)
27533            };
27534
27535            GutterDimensions {
27536                left_padding,
27537                right_padding,
27538                width: line_gutter_width + left_padding + right_padding,
27539                margin: GutterDimensions::default_gutter_margin(font_id, font_size, cx),
27540                git_blame_entries_width,
27541            }
27542        } else if self.offset_content {
27543            GutterDimensions::default_with_margin(font_id, font_size, cx)
27544        } else {
27545            GutterDimensions::default()
27546        }
27547    }
27548
27549    pub fn render_crease_toggle(
27550        &self,
27551        buffer_row: MultiBufferRow,
27552        row_contains_cursor: bool,
27553        editor: Entity<Editor>,
27554        window: &mut Window,
27555        cx: &mut App,
27556    ) -> Option<AnyElement> {
27557        let folded = self.is_line_folded(buffer_row);
27558        let mut is_foldable = false;
27559
27560        if let Some(crease) = self
27561            .crease_snapshot
27562            .query_row(buffer_row, self.buffer_snapshot())
27563        {
27564            is_foldable = true;
27565            match crease {
27566                Crease::Inline { render_toggle, .. } | Crease::Block { render_toggle, .. } => {
27567                    if let Some(render_toggle) = render_toggle {
27568                        let toggle_callback =
27569                            Arc::new(move |folded, window: &mut Window, cx: &mut App| {
27570                                if folded {
27571                                    editor.update(cx, |editor, cx| {
27572                                        editor.fold_at(buffer_row, window, cx)
27573                                    });
27574                                } else {
27575                                    editor.update(cx, |editor, cx| {
27576                                        editor.unfold_at(buffer_row, window, cx)
27577                                    });
27578                                }
27579                            });
27580                        return Some((render_toggle)(
27581                            buffer_row,
27582                            folded,
27583                            toggle_callback,
27584                            window,
27585                            cx,
27586                        ));
27587                    }
27588                }
27589            }
27590        }
27591
27592        is_foldable |= !self.use_lsp_folding_ranges && self.starts_indent(buffer_row);
27593
27594        if folded || (is_foldable && (row_contains_cursor || self.gutter_hovered)) {
27595            Some(
27596                Disclosure::new(("gutter_crease", buffer_row.0), !folded)
27597                    .toggle_state(folded)
27598                    .on_click(window.listener_for(&editor, move |this, _e, window, cx| {
27599                        if folded {
27600                            this.unfold_at(buffer_row, window, cx);
27601                        } else {
27602                            this.fold_at(buffer_row, window, cx);
27603                        }
27604                    }))
27605                    .into_any_element(),
27606            )
27607        } else {
27608            None
27609        }
27610    }
27611
27612    pub fn render_crease_trailer(
27613        &self,
27614        buffer_row: MultiBufferRow,
27615        window: &mut Window,
27616        cx: &mut App,
27617    ) -> Option<AnyElement> {
27618        let folded = self.is_line_folded(buffer_row);
27619        if let Crease::Inline { render_trailer, .. } = self
27620            .crease_snapshot
27621            .query_row(buffer_row, self.buffer_snapshot())?
27622        {
27623            let render_trailer = render_trailer.as_ref()?;
27624            Some(render_trailer(buffer_row, folded, window, cx))
27625        } else {
27626            None
27627        }
27628    }
27629
27630    pub fn max_line_number_width(&self, style: &EditorStyle, window: &mut Window) -> Pixels {
27631        let digit_count = self.widest_line_number().ilog10() + 1;
27632        column_pixels(style, digit_count as usize, window)
27633    }
27634
27635    /// Returns the line delta from `base` to `line` in the multibuffer, ignoring wrapped lines.
27636    ///
27637    /// This is positive if `base` is before `line`.
27638    fn relative_line_delta(
27639        &self,
27640        current_selection_head: DisplayRow,
27641        first_visible_row: DisplayRow,
27642        consider_wrapped_lines: bool,
27643    ) -> i64 {
27644        let current_selection_head = current_selection_head.as_display_point().to_point(self);
27645        let first_visible_row = first_visible_row.as_display_point().to_point(self);
27646
27647        if consider_wrapped_lines {
27648            let wrap_snapshot = self.wrap_snapshot();
27649            let base_wrap_row = wrap_snapshot
27650                .make_wrap_point(current_selection_head, Bias::Left)
27651                .row();
27652            let wrap_row = wrap_snapshot
27653                .make_wrap_point(first_visible_row, Bias::Left)
27654                .row();
27655
27656            wrap_row.0 as i64 - base_wrap_row.0 as i64
27657        } else {
27658            let fold_snapshot = self.fold_snapshot();
27659            let base_fold_row = fold_snapshot
27660                .to_fold_point(self.to_inlay_point(current_selection_head), Bias::Left)
27661                .row();
27662            let fold_row = fold_snapshot
27663                .to_fold_point(self.to_inlay_point(first_visible_row), Bias::Left)
27664                .row();
27665
27666            fold_row as i64 - base_fold_row as i64
27667        }
27668    }
27669
27670    /// Returns the unsigned relative line number to display for each row in `rows`.
27671    ///
27672    /// Wrapped rows are excluded from the hashmap if `count_relative_lines` is `false`.
27673    pub fn calculate_relative_line_numbers(
27674        &self,
27675        rows: &Range<DisplayRow>,
27676        current_selection_head: DisplayRow,
27677        count_wrapped_lines: bool,
27678    ) -> HashMap<DisplayRow, u32> {
27679        let initial_offset =
27680            self.relative_line_delta(current_selection_head, rows.start, count_wrapped_lines);
27681
27682        self.row_infos(rows.start)
27683            .take(rows.len())
27684            .enumerate()
27685            .map(|(i, row_info)| (DisplayRow(rows.start.0 + i as u32), row_info))
27686            .filter(|(_row, row_info)| {
27687                row_info.buffer_row.is_some()
27688                    || (count_wrapped_lines && row_info.wrapped_buffer_row.is_some())
27689            })
27690            .enumerate()
27691            .filter_map(|(i, (row, row_info))| {
27692                // We want to ensure here that the current line has absolute
27693                // numbering, even if we are in a soft-wrapped line. With the
27694                // exception that if we are in a deleted line, we should number this
27695                // relative with 0, as otherwise it would have no line number at all
27696                let relative_line_number = (initial_offset + i as i64).unsigned_abs() as u32;
27697
27698                (relative_line_number != 0
27699                    || row_info
27700                        .diff_status
27701                        .is_some_and(|status| status.is_deleted()))
27702                .then_some((row, relative_line_number))
27703            })
27704            .collect()
27705    }
27706}
27707
27708pub fn column_pixels(style: &EditorStyle, column: usize, window: &Window) -> Pixels {
27709    let font_size = style.text.font_size.to_pixels(window.rem_size());
27710    let layout = window.text_system().shape_line(
27711        SharedString::from(" ".repeat(column)),
27712        font_size,
27713        &[TextRun {
27714            len: column,
27715            font: style.text.font(),
27716            color: Hsla::default(),
27717            ..Default::default()
27718        }],
27719        None,
27720    );
27721
27722    layout.width
27723}
27724
27725impl Deref for EditorSnapshot {
27726    type Target = DisplaySnapshot;
27727
27728    fn deref(&self) -> &Self::Target {
27729        &self.display_snapshot
27730    }
27731}
27732
27733#[derive(Clone, Debug, PartialEq, Eq)]
27734pub enum EditorEvent {
27735    /// Emitted when the stored review comments change (added, removed, or updated).
27736    ReviewCommentsChanged {
27737        /// The new total count of review comments.
27738        total_count: usize,
27739    },
27740    InputIgnored {
27741        text: Arc<str>,
27742    },
27743    InputHandled {
27744        utf16_range_to_replace: Option<Range<isize>>,
27745        text: Arc<str>,
27746    },
27747    ExcerptsAdded {
27748        buffer: Entity<Buffer>,
27749        predecessor: ExcerptId,
27750        excerpts: Vec<(ExcerptId, ExcerptRange<language::Anchor>)>,
27751    },
27752    ExcerptsRemoved {
27753        ids: Vec<ExcerptId>,
27754        removed_buffer_ids: Vec<BufferId>,
27755    },
27756    BufferFoldToggled {
27757        ids: Vec<ExcerptId>,
27758        folded: bool,
27759    },
27760    ExcerptsEdited {
27761        ids: Vec<ExcerptId>,
27762    },
27763    ExcerptsExpanded {
27764        ids: Vec<ExcerptId>,
27765    },
27766    ExpandExcerptsRequested {
27767        excerpt_ids: Vec<ExcerptId>,
27768        lines: u32,
27769        direction: ExpandExcerptDirection,
27770    },
27771    StageOrUnstageRequested {
27772        stage: bool,
27773        hunks: Vec<MultiBufferDiffHunk>,
27774    },
27775    OpenExcerptsRequested {
27776        selections_by_buffer: HashMap<BufferId, (Vec<Range<BufferOffset>>, Option<u32>)>,
27777        split: bool,
27778    },
27779    RestoreRequested {
27780        hunks: Vec<MultiBufferDiffHunk>,
27781    },
27782    BufferEdited,
27783    Edited {
27784        transaction_id: clock::Lamport,
27785    },
27786    Reparsed(BufferId),
27787    Focused,
27788    FocusedIn,
27789    Blurred,
27790    DirtyChanged,
27791    Saved,
27792    TitleChanged,
27793    SelectionsChanged {
27794        local: bool,
27795    },
27796    ScrollPositionChanged {
27797        local: bool,
27798        autoscroll: bool,
27799    },
27800    TransactionUndone {
27801        transaction_id: clock::Lamport,
27802    },
27803    TransactionBegun {
27804        transaction_id: clock::Lamport,
27805    },
27806    CursorShapeChanged,
27807    BreadcrumbsChanged,
27808    OutlineSymbolsChanged,
27809    PushedToNavHistory {
27810        anchor: Anchor,
27811        is_deactivate: bool,
27812    },
27813}
27814
27815impl EventEmitter<EditorEvent> for Editor {}
27816
27817impl Focusable for Editor {
27818    fn focus_handle(&self, _cx: &App) -> FocusHandle {
27819        self.focus_handle.clone()
27820    }
27821}
27822
27823impl Render for Editor {
27824    fn render(&mut self, _: &mut Window, cx: &mut Context<Self>) -> impl IntoElement {
27825        EditorElement::new(&cx.entity(), self.create_style(cx))
27826    }
27827}
27828
27829impl EntityInputHandler for Editor {
27830    fn text_for_range(
27831        &mut self,
27832        range_utf16: Range<usize>,
27833        adjusted_range: &mut Option<Range<usize>>,
27834        _: &mut Window,
27835        cx: &mut Context<Self>,
27836    ) -> Option<String> {
27837        let snapshot = self.buffer.read(cx).read(cx);
27838        let start = snapshot.clip_offset_utf16(
27839            MultiBufferOffsetUtf16(OffsetUtf16(range_utf16.start)),
27840            Bias::Left,
27841        );
27842        let end = snapshot.clip_offset_utf16(
27843            MultiBufferOffsetUtf16(OffsetUtf16(range_utf16.end)),
27844            Bias::Right,
27845        );
27846        if (start.0.0..end.0.0) != range_utf16 {
27847            adjusted_range.replace(start.0.0..end.0.0);
27848        }
27849        Some(snapshot.text_for_range(start..end).collect())
27850    }
27851
27852    fn selected_text_range(
27853        &mut self,
27854        ignore_disabled_input: bool,
27855        _: &mut Window,
27856        cx: &mut Context<Self>,
27857    ) -> Option<UTF16Selection> {
27858        // Prevent the IME menu from appearing when holding down an alphabetic key
27859        // while input is disabled.
27860        if !ignore_disabled_input && !self.input_enabled {
27861            return None;
27862        }
27863
27864        let selection = self
27865            .selections
27866            .newest::<MultiBufferOffsetUtf16>(&self.display_snapshot(cx));
27867        let range = selection.range();
27868
27869        Some(UTF16Selection {
27870            range: range.start.0.0..range.end.0.0,
27871            reversed: selection.reversed,
27872        })
27873    }
27874
27875    fn marked_text_range(&self, _: &mut Window, cx: &mut Context<Self>) -> Option<Range<usize>> {
27876        let snapshot = self.buffer.read(cx).read(cx);
27877        let range = self
27878            .text_highlights(HighlightKey::InputComposition, cx)?
27879            .1
27880            .first()?;
27881        Some(range.start.to_offset_utf16(&snapshot).0.0..range.end.to_offset_utf16(&snapshot).0.0)
27882    }
27883
27884    fn unmark_text(&mut self, _: &mut Window, cx: &mut Context<Self>) {
27885        self.clear_highlights(HighlightKey::InputComposition, cx);
27886        self.ime_transaction.take();
27887    }
27888
27889    fn replace_text_in_range(
27890        &mut self,
27891        range_utf16: Option<Range<usize>>,
27892        text: &str,
27893        window: &mut Window,
27894        cx: &mut Context<Self>,
27895    ) {
27896        if !self.input_enabled {
27897            cx.emit(EditorEvent::InputIgnored { text: text.into() });
27898            return;
27899        }
27900
27901        self.transact(window, cx, |this, window, cx| {
27902            let new_selected_ranges = if let Some(range_utf16) = range_utf16 {
27903                let range_utf16 = MultiBufferOffsetUtf16(OffsetUtf16(range_utf16.start))
27904                    ..MultiBufferOffsetUtf16(OffsetUtf16(range_utf16.end));
27905                Some(this.selection_replacement_ranges(range_utf16, cx))
27906            } else {
27907                this.marked_text_ranges(cx)
27908            };
27909
27910            let range_to_replace = new_selected_ranges.as_ref().and_then(|ranges_to_replace| {
27911                let newest_selection_id = this.selections.newest_anchor().id;
27912                this.selections
27913                    .all::<MultiBufferOffsetUtf16>(&this.display_snapshot(cx))
27914                    .iter()
27915                    .zip(ranges_to_replace.iter())
27916                    .find_map(|(selection, range)| {
27917                        if selection.id == newest_selection_id {
27918                            Some(
27919                                (range.start.0.0 as isize - selection.head().0.0 as isize)
27920                                    ..(range.end.0.0 as isize - selection.head().0.0 as isize),
27921                            )
27922                        } else {
27923                            None
27924                        }
27925                    })
27926            });
27927
27928            cx.emit(EditorEvent::InputHandled {
27929                utf16_range_to_replace: range_to_replace,
27930                text: text.into(),
27931            });
27932
27933            if let Some(new_selected_ranges) = new_selected_ranges {
27934                this.change_selections(SelectionEffects::no_scroll(), window, cx, |selections| {
27935                    selections.select_ranges(new_selected_ranges)
27936                });
27937                this.backspace(&Default::default(), window, cx);
27938            }
27939
27940            this.handle_input(text, window, cx);
27941        });
27942
27943        if let Some(transaction) = self.ime_transaction {
27944            self.buffer.update(cx, |buffer, cx| {
27945                buffer.group_until_transaction(transaction, cx);
27946            });
27947        }
27948
27949        self.unmark_text(window, cx);
27950    }
27951
27952    fn replace_and_mark_text_in_range(
27953        &mut self,
27954        range_utf16: Option<Range<usize>>,
27955        text: &str,
27956        new_selected_range_utf16: Option<Range<usize>>,
27957        window: &mut Window,
27958        cx: &mut Context<Self>,
27959    ) {
27960        if !self.input_enabled {
27961            return;
27962        }
27963
27964        let transaction = self.transact(window, cx, |this, window, cx| {
27965            let ranges_to_replace = if let Some(mut marked_ranges) = this.marked_text_ranges(cx) {
27966                let snapshot = this.buffer.read(cx).read(cx);
27967                if let Some(relative_range_utf16) = range_utf16.as_ref() {
27968                    for marked_range in &mut marked_ranges {
27969                        marked_range.end = marked_range.start + relative_range_utf16.end;
27970                        marked_range.start += relative_range_utf16.start;
27971                        marked_range.start =
27972                            snapshot.clip_offset_utf16(marked_range.start, Bias::Left);
27973                        marked_range.end =
27974                            snapshot.clip_offset_utf16(marked_range.end, Bias::Right);
27975                    }
27976                }
27977                Some(marked_ranges)
27978            } else if let Some(range_utf16) = range_utf16 {
27979                let range_utf16 = MultiBufferOffsetUtf16(OffsetUtf16(range_utf16.start))
27980                    ..MultiBufferOffsetUtf16(OffsetUtf16(range_utf16.end));
27981                Some(this.selection_replacement_ranges(range_utf16, cx))
27982            } else {
27983                None
27984            };
27985
27986            let range_to_replace = ranges_to_replace.as_ref().and_then(|ranges_to_replace| {
27987                let newest_selection_id = this.selections.newest_anchor().id;
27988                this.selections
27989                    .all::<MultiBufferOffsetUtf16>(&this.display_snapshot(cx))
27990                    .iter()
27991                    .zip(ranges_to_replace.iter())
27992                    .find_map(|(selection, range)| {
27993                        if selection.id == newest_selection_id {
27994                            Some(
27995                                (range.start.0.0 as isize - selection.head().0.0 as isize)
27996                                    ..(range.end.0.0 as isize - selection.head().0.0 as isize),
27997                            )
27998                        } else {
27999                            None
28000                        }
28001                    })
28002            });
28003
28004            cx.emit(EditorEvent::InputHandled {
28005                utf16_range_to_replace: range_to_replace,
28006                text: text.into(),
28007            });
28008
28009            if let Some(ranges) = ranges_to_replace {
28010                this.change_selections(SelectionEffects::no_scroll(), window, cx, |s| {
28011                    s.select_ranges(ranges)
28012                });
28013            }
28014
28015            let marked_ranges = {
28016                let snapshot = this.buffer.read(cx).read(cx);
28017                this.selections
28018                    .disjoint_anchors_arc()
28019                    .iter()
28020                    .map(|selection| {
28021                        selection.start.bias_left(&snapshot)..selection.end.bias_right(&snapshot)
28022                    })
28023                    .collect::<Vec<_>>()
28024            };
28025
28026            if text.is_empty() {
28027                this.unmark_text(window, cx);
28028            } else {
28029                this.highlight_text(
28030                    HighlightKey::InputComposition,
28031                    marked_ranges.clone(),
28032                    HighlightStyle {
28033                        underline: Some(UnderlineStyle {
28034                            thickness: px(1.),
28035                            color: None,
28036                            wavy: false,
28037                        }),
28038                        ..Default::default()
28039                    },
28040                    cx,
28041                );
28042            }
28043
28044            // Disable auto-closing when composing text (i.e. typing a `"` on a Brazilian keyboard)
28045            let use_autoclose = this.use_autoclose;
28046            let use_auto_surround = this.use_auto_surround;
28047            this.set_use_autoclose(false);
28048            this.set_use_auto_surround(false);
28049            this.handle_input(text, window, cx);
28050            this.set_use_autoclose(use_autoclose);
28051            this.set_use_auto_surround(use_auto_surround);
28052
28053            if let Some(new_selected_range) = new_selected_range_utf16 {
28054                let snapshot = this.buffer.read(cx).read(cx);
28055                let new_selected_ranges = marked_ranges
28056                    .into_iter()
28057                    .map(|marked_range| {
28058                        let insertion_start = marked_range.start.to_offset_utf16(&snapshot).0;
28059                        let new_start = MultiBufferOffsetUtf16(OffsetUtf16(
28060                            insertion_start.0 + new_selected_range.start,
28061                        ));
28062                        let new_end = MultiBufferOffsetUtf16(OffsetUtf16(
28063                            insertion_start.0 + new_selected_range.end,
28064                        ));
28065                        snapshot.clip_offset_utf16(new_start, Bias::Left)
28066                            ..snapshot.clip_offset_utf16(new_end, Bias::Right)
28067                    })
28068                    .collect::<Vec<_>>();
28069
28070                drop(snapshot);
28071                this.change_selections(SelectionEffects::no_scroll(), window, cx, |selections| {
28072                    selections.select_ranges(new_selected_ranges)
28073                });
28074            }
28075        });
28076
28077        self.ime_transaction = self.ime_transaction.or(transaction);
28078        if let Some(transaction) = self.ime_transaction {
28079            self.buffer.update(cx, |buffer, cx| {
28080                buffer.group_until_transaction(transaction, cx);
28081            });
28082        }
28083
28084        if self
28085            .text_highlights(HighlightKey::InputComposition, cx)
28086            .is_none()
28087        {
28088            self.ime_transaction.take();
28089        }
28090    }
28091
28092    fn bounds_for_range(
28093        &mut self,
28094        range_utf16: Range<usize>,
28095        element_bounds: gpui::Bounds<Pixels>,
28096        window: &mut Window,
28097        cx: &mut Context<Self>,
28098    ) -> Option<gpui::Bounds<Pixels>> {
28099        let text_layout_details = self.text_layout_details(window, cx);
28100        let CharacterDimensions {
28101            em_width,
28102            em_advance,
28103            line_height,
28104        } = self.character_dimensions(window, cx);
28105
28106        let snapshot = self.snapshot(window, cx);
28107        let scroll_position = snapshot.scroll_position();
28108        let scroll_left = scroll_position.x * ScrollOffset::from(em_advance);
28109
28110        let start =
28111            MultiBufferOffsetUtf16(OffsetUtf16(range_utf16.start)).to_display_point(&snapshot);
28112        let x = Pixels::from(
28113            ScrollOffset::from(
28114                snapshot.x_for_display_point(start, &text_layout_details)
28115                    + self.gutter_dimensions.full_width(),
28116            ) - scroll_left,
28117        );
28118        let y = line_height * (start.row().as_f64() - scroll_position.y) as f32;
28119
28120        Some(Bounds {
28121            origin: element_bounds.origin + point(x, y),
28122            size: size(em_width, line_height),
28123        })
28124    }
28125
28126    fn character_index_for_point(
28127        &mut self,
28128        point: gpui::Point<Pixels>,
28129        _window: &mut Window,
28130        _cx: &mut Context<Self>,
28131    ) -> Option<usize> {
28132        let position_map = self.last_position_map.as_ref()?;
28133        if !position_map.text_hitbox.contains(&point) {
28134            return None;
28135        }
28136        let display_point = position_map.point_for_position(point).previous_valid;
28137        let anchor = position_map
28138            .snapshot
28139            .display_point_to_anchor(display_point, Bias::Left);
28140        let utf16_offset = anchor.to_offset_utf16(&position_map.snapshot.buffer_snapshot());
28141        Some(utf16_offset.0.0)
28142    }
28143
28144    fn accepts_text_input(&self, _window: &mut Window, _cx: &mut Context<Self>) -> bool {
28145        self.input_enabled
28146    }
28147}
28148
28149trait SelectionExt {
28150    fn display_range(&self, map: &DisplaySnapshot) -> Range<DisplayPoint>;
28151    fn spanned_rows(
28152        &self,
28153        include_end_if_at_line_start: bool,
28154        map: &DisplaySnapshot,
28155    ) -> Range<MultiBufferRow>;
28156}
28157
28158impl<T: ToPoint + ToOffset> SelectionExt for Selection<T> {
28159    fn display_range(&self, map: &DisplaySnapshot) -> Range<DisplayPoint> {
28160        let start = self
28161            .start
28162            .to_point(map.buffer_snapshot())
28163            .to_display_point(map);
28164        let end = self
28165            .end
28166            .to_point(map.buffer_snapshot())
28167            .to_display_point(map);
28168        if self.reversed {
28169            end..start
28170        } else {
28171            start..end
28172        }
28173    }
28174
28175    fn spanned_rows(
28176        &self,
28177        include_end_if_at_line_start: bool,
28178        map: &DisplaySnapshot,
28179    ) -> Range<MultiBufferRow> {
28180        let start = self.start.to_point(map.buffer_snapshot());
28181        let mut end = self.end.to_point(map.buffer_snapshot());
28182        if !include_end_if_at_line_start && start.row != end.row && end.column == 0 {
28183            end.row -= 1;
28184        }
28185
28186        let buffer_start = map.prev_line_boundary(start).0;
28187        let buffer_end = map.next_line_boundary(end).0;
28188        MultiBufferRow(buffer_start.row)..MultiBufferRow(buffer_end.row + 1)
28189    }
28190}
28191
28192impl<T: InvalidationRegion> InvalidationStack<T> {
28193    fn invalidate<S>(&mut self, selections: &[Selection<S>], buffer: &MultiBufferSnapshot)
28194    where
28195        S: Clone + ToOffset,
28196    {
28197        while let Some(region) = self.last() {
28198            let all_selections_inside_invalidation_ranges =
28199                if selections.len() == region.ranges().len() {
28200                    selections
28201                        .iter()
28202                        .zip(region.ranges().iter().map(|r| r.to_offset(buffer)))
28203                        .all(|(selection, invalidation_range)| {
28204                            let head = selection.head().to_offset(buffer);
28205                            invalidation_range.start <= head && invalidation_range.end >= head
28206                        })
28207                } else {
28208                    false
28209                };
28210
28211            if all_selections_inside_invalidation_ranges {
28212                break;
28213            } else {
28214                self.pop();
28215            }
28216        }
28217    }
28218}
28219
28220#[derive(Clone)]
28221struct ErasedEditorImpl(Entity<Editor>);
28222
28223impl ui_input::ErasedEditor for ErasedEditorImpl {
28224    fn text(&self, cx: &App) -> String {
28225        self.0.read(cx).text(cx)
28226    }
28227
28228    fn set_text(&self, text: &str, window: &mut Window, cx: &mut App) {
28229        self.0.update(cx, |this, cx| {
28230            this.set_text(text, window, cx);
28231        })
28232    }
28233
28234    fn clear(&self, window: &mut Window, cx: &mut App) {
28235        self.0.update(cx, |this, cx| this.clear(window, cx));
28236    }
28237
28238    fn set_placeholder_text(&self, text: &str, window: &mut Window, cx: &mut App) {
28239        self.0.update(cx, |this, cx| {
28240            this.set_placeholder_text(text, window, cx);
28241        });
28242    }
28243
28244    fn focus_handle(&self, cx: &App) -> FocusHandle {
28245        self.0.read(cx).focus_handle(cx)
28246    }
28247
28248    fn render(&self, _: &mut Window, cx: &App) -> AnyElement {
28249        let settings = ThemeSettings::get_global(cx);
28250        let theme_color = cx.theme().colors();
28251
28252        let text_style = TextStyle {
28253            font_family: settings.ui_font.family.clone(),
28254            font_features: settings.ui_font.features.clone(),
28255            font_size: rems(0.875).into(),
28256            font_weight: settings.buffer_font.weight,
28257            font_style: FontStyle::Normal,
28258            line_height: relative(1.2),
28259            color: theme_color.text,
28260            ..Default::default()
28261        };
28262        let editor_style = EditorStyle {
28263            background: theme_color.ghost_element_background,
28264            local_player: cx.theme().players().local(),
28265            syntax: cx.theme().syntax().clone(),
28266            text: text_style,
28267            ..Default::default()
28268        };
28269        EditorElement::new(&self.0, editor_style).into_any()
28270    }
28271
28272    fn as_any(&self) -> &dyn Any {
28273        &self.0
28274    }
28275
28276    fn move_selection_to_end(&self, window: &mut Window, cx: &mut App) {
28277        self.0.update(cx, |editor, cx| {
28278            let editor_offset = editor.buffer().read(cx).len(cx);
28279            editor.change_selections(
28280                SelectionEffects::scroll(Autoscroll::Next),
28281                window,
28282                cx,
28283                |s| s.select_ranges(Some(editor_offset..editor_offset)),
28284            );
28285        });
28286    }
28287
28288    fn subscribe(
28289        &self,
28290        mut callback: Box<dyn FnMut(ui_input::ErasedEditorEvent, &mut Window, &mut App) + 'static>,
28291        window: &mut Window,
28292        cx: &mut App,
28293    ) -> Subscription {
28294        window.subscribe(&self.0, cx, move |_, event: &EditorEvent, window, cx| {
28295            let event = match event {
28296                EditorEvent::BufferEdited => ui_input::ErasedEditorEvent::BufferEdited,
28297                EditorEvent::Blurred => ui_input::ErasedEditorEvent::Blurred,
28298                _ => return,
28299            };
28300            (callback)(event, window, cx);
28301        })
28302    }
28303
28304    fn set_masked(&self, masked: bool, _window: &mut Window, cx: &mut App) {
28305        self.0.update(cx, |editor, cx| {
28306            editor.set_masked(masked, cx);
28307        });
28308    }
28309}
28310impl<T> Default for InvalidationStack<T> {
28311    fn default() -> Self {
28312        Self(Default::default())
28313    }
28314}
28315
28316impl<T> Deref for InvalidationStack<T> {
28317    type Target = Vec<T>;
28318
28319    fn deref(&self) -> &Self::Target {
28320        &self.0
28321    }
28322}
28323
28324impl<T> DerefMut for InvalidationStack<T> {
28325    fn deref_mut(&mut self) -> &mut Self::Target {
28326        &mut self.0
28327    }
28328}
28329
28330impl InvalidationRegion for SnippetState {
28331    fn ranges(&self) -> &[Range<Anchor>] {
28332        &self.ranges[self.active_index]
28333    }
28334}
28335
28336fn edit_prediction_edit_text(
28337    current_snapshot: &BufferSnapshot,
28338    edits: &[(Range<Anchor>, impl AsRef<str>)],
28339    edit_preview: &EditPreview,
28340    include_deletions: bool,
28341    cx: &App,
28342) -> HighlightedText {
28343    let edits = edits
28344        .iter()
28345        .map(|(anchor, text)| (anchor.start.text_anchor..anchor.end.text_anchor, text))
28346        .collect::<Vec<_>>();
28347
28348    edit_preview.highlight_edits(current_snapshot, &edits, include_deletions, cx)
28349}
28350
28351fn edit_prediction_fallback_text(edits: &[(Range<Anchor>, Arc<str>)], cx: &App) -> HighlightedText {
28352    // Fallback for providers that don't provide edit_preview (like Copilot/Supermaven)
28353    // Just show the raw edit text with basic styling
28354    let mut text = String::new();
28355    let mut highlights = Vec::new();
28356
28357    let insertion_highlight_style = HighlightStyle {
28358        color: Some(cx.theme().colors().text),
28359        ..Default::default()
28360    };
28361
28362    for (_, edit_text) in edits {
28363        let start_offset = text.len();
28364        text.push_str(edit_text);
28365        let end_offset = text.len();
28366
28367        if start_offset < end_offset {
28368            highlights.push((start_offset..end_offset, insertion_highlight_style));
28369        }
28370    }
28371
28372    HighlightedText {
28373        text: text.into(),
28374        highlights,
28375    }
28376}
28377
28378pub fn diagnostic_style(severity: lsp::DiagnosticSeverity, colors: &StatusColors) -> Hsla {
28379    match severity {
28380        lsp::DiagnosticSeverity::ERROR => colors.error,
28381        lsp::DiagnosticSeverity::WARNING => colors.warning,
28382        lsp::DiagnosticSeverity::INFORMATION => colors.info,
28383        lsp::DiagnosticSeverity::HINT => colors.info,
28384        _ => colors.ignored,
28385    }
28386}
28387
28388pub fn styled_runs_for_code_label<'a>(
28389    label: &'a CodeLabel,
28390    syntax_theme: &'a theme::SyntaxTheme,
28391    local_player: &'a theme::PlayerColor,
28392) -> impl 'a + Iterator<Item = (Range<usize>, HighlightStyle)> {
28393    let fade_out = HighlightStyle {
28394        fade_out: Some(0.35),
28395        ..Default::default()
28396    };
28397
28398    let mut prev_end = label.filter_range.end;
28399    label
28400        .runs
28401        .iter()
28402        .enumerate()
28403        .flat_map(move |(ix, (range, highlight_id))| {
28404            let style = if *highlight_id == language::HighlightId::TABSTOP_INSERT_ID {
28405                HighlightStyle {
28406                    color: Some(local_player.cursor),
28407                    ..Default::default()
28408                }
28409            } else if *highlight_id == language::HighlightId::TABSTOP_REPLACE_ID {
28410                HighlightStyle {
28411                    background_color: Some(local_player.selection),
28412                    ..Default::default()
28413                }
28414            } else if let Some(style) = highlight_id.style(syntax_theme) {
28415                style
28416            } else {
28417                return Default::default();
28418            };
28419            let muted_style = style.highlight(fade_out);
28420
28421            let mut runs = SmallVec::<[(Range<usize>, HighlightStyle); 3]>::new();
28422            if range.start >= label.filter_range.end {
28423                if range.start > prev_end {
28424                    runs.push((prev_end..range.start, fade_out));
28425                }
28426                runs.push((range.clone(), muted_style));
28427            } else if range.end <= label.filter_range.end {
28428                runs.push((range.clone(), style));
28429            } else {
28430                runs.push((range.start..label.filter_range.end, style));
28431                runs.push((label.filter_range.end..range.end, muted_style));
28432            }
28433            prev_end = cmp::max(prev_end, range.end);
28434
28435            if ix + 1 == label.runs.len() && label.text.len() > prev_end {
28436                runs.push((prev_end..label.text.len(), fade_out));
28437            }
28438
28439            runs
28440        })
28441}
28442
28443pub(crate) fn split_words(text: &str) -> impl std::iter::Iterator<Item = &str> + '_ {
28444    let mut prev_index = 0;
28445    let mut prev_codepoint: Option<char> = None;
28446    text.char_indices()
28447        .chain([(text.len(), '\0')])
28448        .filter_map(move |(index, codepoint)| {
28449            let prev_codepoint = prev_codepoint.replace(codepoint)?;
28450            let is_boundary = index == text.len()
28451                || !prev_codepoint.is_uppercase() && codepoint.is_uppercase()
28452                || !prev_codepoint.is_alphanumeric() && codepoint.is_alphanumeric();
28453            if is_boundary {
28454                let chunk = &text[prev_index..index];
28455                prev_index = index;
28456                Some(chunk)
28457            } else {
28458                None
28459            }
28460        })
28461}
28462
28463/// Given a string of text immediately before the cursor, iterates over possible
28464/// strings a snippet could match to. More precisely: returns an iterator over
28465/// suffixes of `text` created by splitting at word boundaries (before & after
28466/// every non-word character).
28467///
28468/// Shorter suffixes are returned first.
28469pub(crate) fn snippet_candidate_suffixes(
28470    text: &str,
28471    is_word_char: impl Fn(char) -> bool,
28472) -> impl std::iter::Iterator<Item = &str> {
28473    let mut prev_index = text.len();
28474    let mut prev_codepoint = None;
28475    text.char_indices()
28476        .rev()
28477        .chain([(0, '\0')])
28478        .filter_map(move |(index, codepoint)| {
28479            let prev_index = std::mem::replace(&mut prev_index, index);
28480            let prev_codepoint = prev_codepoint.replace(codepoint)?;
28481            if is_word_char(prev_codepoint) && is_word_char(codepoint) {
28482                None
28483            } else {
28484                let chunk = &text[prev_index..]; // go to end of string
28485                Some(chunk)
28486            }
28487        })
28488}
28489
28490pub trait RangeToAnchorExt: Sized {
28491    fn to_anchors(self, snapshot: &MultiBufferSnapshot) -> Range<Anchor>;
28492
28493    fn to_display_points(self, snapshot: &EditorSnapshot) -> Range<DisplayPoint> {
28494        let anchor_range = self.to_anchors(&snapshot.buffer_snapshot());
28495        anchor_range.start.to_display_point(snapshot)..anchor_range.end.to_display_point(snapshot)
28496    }
28497}
28498
28499impl<T: ToOffset> RangeToAnchorExt for Range<T> {
28500    fn to_anchors(self, snapshot: &MultiBufferSnapshot) -> Range<Anchor> {
28501        let start_offset = self.start.to_offset(snapshot);
28502        let end_offset = self.end.to_offset(snapshot);
28503        if start_offset == end_offset {
28504            snapshot.anchor_before(start_offset)..snapshot.anchor_before(end_offset)
28505        } else {
28506            snapshot.anchor_after(self.start)..snapshot.anchor_before(self.end)
28507        }
28508    }
28509}
28510
28511pub trait RowExt {
28512    fn as_f64(&self) -> f64;
28513
28514    fn next_row(&self) -> Self;
28515
28516    fn previous_row(&self) -> Self;
28517
28518    fn minus(&self, other: Self) -> u32;
28519}
28520
28521impl RowExt for DisplayRow {
28522    fn as_f64(&self) -> f64 {
28523        self.0 as _
28524    }
28525
28526    fn next_row(&self) -> Self {
28527        Self(self.0 + 1)
28528    }
28529
28530    fn previous_row(&self) -> Self {
28531        Self(self.0.saturating_sub(1))
28532    }
28533
28534    fn minus(&self, other: Self) -> u32 {
28535        self.0 - other.0
28536    }
28537}
28538
28539impl RowExt for MultiBufferRow {
28540    fn as_f64(&self) -> f64 {
28541        self.0 as _
28542    }
28543
28544    fn next_row(&self) -> Self {
28545        Self(self.0 + 1)
28546    }
28547
28548    fn previous_row(&self) -> Self {
28549        Self(self.0.saturating_sub(1))
28550    }
28551
28552    fn minus(&self, other: Self) -> u32 {
28553        self.0 - other.0
28554    }
28555}
28556
28557trait RowRangeExt {
28558    type Row;
28559
28560    fn len(&self) -> usize;
28561
28562    fn iter_rows(&self) -> impl DoubleEndedIterator<Item = Self::Row>;
28563}
28564
28565impl RowRangeExt for Range<MultiBufferRow> {
28566    type Row = MultiBufferRow;
28567
28568    fn len(&self) -> usize {
28569        (self.end.0 - self.start.0) as usize
28570    }
28571
28572    fn iter_rows(&self) -> impl DoubleEndedIterator<Item = MultiBufferRow> {
28573        (self.start.0..self.end.0).map(MultiBufferRow)
28574    }
28575}
28576
28577impl RowRangeExt for Range<DisplayRow> {
28578    type Row = DisplayRow;
28579
28580    fn len(&self) -> usize {
28581        (self.end.0 - self.start.0) as usize
28582    }
28583
28584    fn iter_rows(&self) -> impl DoubleEndedIterator<Item = DisplayRow> {
28585        (self.start.0..self.end.0).map(DisplayRow)
28586    }
28587}
28588
28589/// If select range has more than one line, we
28590/// just point the cursor to range.start.
28591fn collapse_multiline_range(range: Range<Point>) -> Range<Point> {
28592    if range.start.row == range.end.row {
28593        range
28594    } else {
28595        range.start..range.start
28596    }
28597}
28598pub struct KillRing(ClipboardItem);
28599impl Global for KillRing {}
28600
28601const UPDATE_DEBOUNCE: Duration = Duration::from_millis(50);
28602
28603enum BreakpointPromptEditAction {
28604    Log,
28605    Condition,
28606    HitCondition,
28607}
28608
28609struct BreakpointPromptEditor {
28610    pub(crate) prompt: Entity<Editor>,
28611    editor: WeakEntity<Editor>,
28612    breakpoint_anchor: Anchor,
28613    breakpoint: Breakpoint,
28614    edit_action: BreakpointPromptEditAction,
28615    block_ids: HashSet<CustomBlockId>,
28616    editor_margins: Arc<Mutex<EditorMargins>>,
28617    _subscriptions: Vec<Subscription>,
28618}
28619
28620impl BreakpointPromptEditor {
28621    const MAX_LINES: u8 = 4;
28622
28623    fn new(
28624        editor: WeakEntity<Editor>,
28625        breakpoint_anchor: Anchor,
28626        breakpoint: Breakpoint,
28627        edit_action: BreakpointPromptEditAction,
28628        window: &mut Window,
28629        cx: &mut Context<Self>,
28630    ) -> Self {
28631        let base_text = match edit_action {
28632            BreakpointPromptEditAction::Log => breakpoint.message.as_ref(),
28633            BreakpointPromptEditAction::Condition => breakpoint.condition.as_ref(),
28634            BreakpointPromptEditAction::HitCondition => breakpoint.hit_condition.as_ref(),
28635        }
28636        .map(|msg| msg.to_string())
28637        .unwrap_or_default();
28638
28639        let buffer = cx.new(|cx| Buffer::local(base_text, cx));
28640        let buffer = cx.new(|cx| MultiBuffer::singleton(buffer, cx));
28641
28642        let prompt = cx.new(|cx| {
28643            let mut prompt = Editor::new(
28644                EditorMode::AutoHeight {
28645                    min_lines: 1,
28646                    max_lines: Some(Self::MAX_LINES as usize),
28647                },
28648                buffer,
28649                None,
28650                window,
28651                cx,
28652            );
28653            prompt.set_soft_wrap_mode(language::language_settings::SoftWrap::EditorWidth, cx);
28654            prompt.set_show_cursor_when_unfocused(false, cx);
28655            prompt.set_placeholder_text(
28656                match edit_action {
28657                    BreakpointPromptEditAction::Log => "Message to log when a breakpoint is hit. Expressions within {} are interpolated.",
28658                    BreakpointPromptEditAction::Condition => "Condition when a breakpoint is hit. Expressions within {} are interpolated.",
28659                    BreakpointPromptEditAction::HitCondition => "How many breakpoint hits to ignore",
28660                },
28661                window,
28662                cx,
28663            );
28664
28665            prompt
28666        });
28667
28668        Self {
28669            prompt,
28670            editor,
28671            breakpoint_anchor,
28672            breakpoint,
28673            edit_action,
28674            editor_margins: Arc::new(Mutex::new(EditorMargins::default())),
28675            block_ids: Default::default(),
28676            _subscriptions: vec![],
28677        }
28678    }
28679
28680    pub(crate) fn add_block_ids(&mut self, block_ids: Vec<CustomBlockId>) {
28681        self.block_ids.extend(block_ids)
28682    }
28683
28684    fn confirm(&mut self, _: &menu::Confirm, window: &mut Window, cx: &mut Context<Self>) {
28685        if let Some(editor) = self.editor.upgrade() {
28686            let message = self
28687                .prompt
28688                .read(cx)
28689                .buffer
28690                .read(cx)
28691                .as_singleton()
28692                .expect("A multi buffer in breakpoint prompt isn't possible")
28693                .read(cx)
28694                .as_rope()
28695                .to_string();
28696
28697            editor.update(cx, |editor, cx| {
28698                editor.edit_breakpoint_at_anchor(
28699                    self.breakpoint_anchor,
28700                    self.breakpoint.clone(),
28701                    match self.edit_action {
28702                        BreakpointPromptEditAction::Log => {
28703                            BreakpointEditAction::EditLogMessage(message.into())
28704                        }
28705                        BreakpointPromptEditAction::Condition => {
28706                            BreakpointEditAction::EditCondition(message.into())
28707                        }
28708                        BreakpointPromptEditAction::HitCondition => {
28709                            BreakpointEditAction::EditHitCondition(message.into())
28710                        }
28711                    },
28712                    cx,
28713                );
28714
28715                editor.remove_blocks(self.block_ids.clone(), None, cx);
28716                cx.focus_self(window);
28717            });
28718        }
28719    }
28720
28721    fn cancel(&mut self, _: &menu::Cancel, window: &mut Window, cx: &mut Context<Self>) {
28722        self.editor
28723            .update(cx, |editor, cx| {
28724                editor.remove_blocks(self.block_ids.clone(), None, cx);
28725                window.focus(&editor.focus_handle, cx);
28726            })
28727            .log_err();
28728    }
28729
28730    fn render_prompt_editor(&self, cx: &mut Context<Self>) -> impl IntoElement {
28731        let settings = ThemeSettings::get_global(cx);
28732        let text_style = TextStyle {
28733            color: if self.prompt.read(cx).read_only(cx) {
28734                cx.theme().colors().text_disabled
28735            } else {
28736                cx.theme().colors().text
28737            },
28738            font_family: settings.buffer_font.family.clone(),
28739            font_fallbacks: settings.buffer_font.fallbacks.clone(),
28740            font_size: settings.buffer_font_size(cx).into(),
28741            font_weight: settings.buffer_font.weight,
28742            line_height: relative(settings.buffer_line_height.value()),
28743            ..Default::default()
28744        };
28745        EditorElement::new(
28746            &self.prompt,
28747            EditorStyle {
28748                background: cx.theme().colors().editor_background,
28749                local_player: cx.theme().players().local(),
28750                text: text_style,
28751                ..Default::default()
28752            },
28753        )
28754    }
28755}
28756
28757impl Render for BreakpointPromptEditor {
28758    fn render(&mut self, window: &mut Window, cx: &mut Context<Self>) -> impl IntoElement {
28759        let editor_margins = *self.editor_margins.lock();
28760        let gutter_dimensions = editor_margins.gutter;
28761        h_flex()
28762            .key_context("Editor")
28763            .bg(cx.theme().colors().editor_background)
28764            .border_y_1()
28765            .border_color(cx.theme().status().info_border)
28766            .size_full()
28767            .py(window.line_height() / 2.5)
28768            .on_action(cx.listener(Self::confirm))
28769            .on_action(cx.listener(Self::cancel))
28770            .child(h_flex().w(gutter_dimensions.full_width() + (gutter_dimensions.margin / 2.0)))
28771            .child(div().flex_1().child(self.render_prompt_editor(cx)))
28772    }
28773}
28774
28775impl Focusable for BreakpointPromptEditor {
28776    fn focus_handle(&self, cx: &App) -> FocusHandle {
28777        self.prompt.focus_handle(cx)
28778    }
28779}
28780
28781fn all_edits_insertions_or_deletions(
28782    edits: &Vec<(Range<Anchor>, Arc<str>)>,
28783    snapshot: &MultiBufferSnapshot,
28784) -> bool {
28785    let mut all_insertions = true;
28786    let mut all_deletions = true;
28787
28788    for (range, new_text) in edits.iter() {
28789        let range_is_empty = range.to_offset(snapshot).is_empty();
28790        let text_is_empty = new_text.is_empty();
28791
28792        if range_is_empty != text_is_empty {
28793            if range_is_empty {
28794                all_deletions = false;
28795            } else {
28796                all_insertions = false;
28797            }
28798        } else {
28799            return false;
28800        }
28801
28802        if !all_insertions && !all_deletions {
28803            return false;
28804        }
28805    }
28806    all_insertions || all_deletions
28807}
28808
28809struct MissingEditPredictionKeybindingTooltip;
28810
28811impl Render for MissingEditPredictionKeybindingTooltip {
28812    fn render(&mut self, _: &mut Window, cx: &mut Context<Self>) -> impl IntoElement {
28813        ui::tooltip_container(cx, |container, cx| {
28814            container
28815                .flex_shrink_0()
28816                .max_w_80()
28817                .min_h(rems_from_px(124.))
28818                .justify_between()
28819                .child(
28820                    v_flex()
28821                        .flex_1()
28822                        .text_ui_sm(cx)
28823                        .child(Label::new("Conflict with Accept Keybinding"))
28824                        .child("Your keymap currently overrides the default accept keybinding. To continue, assign one keybinding for the `editor::AcceptEditPrediction` action.")
28825                )
28826                .child(
28827                    h_flex()
28828                        .pb_1()
28829                        .gap_1()
28830                        .items_end()
28831                        .w_full()
28832                        .child(Button::new("open-keymap", "Assign Keybinding").size(ButtonSize::Compact).on_click(|_ev, window, cx| {
28833                            window.dispatch_action(zed_actions::OpenKeymapFile.boxed_clone(), cx)
28834                        }))
28835                        .child(Button::new("see-docs", "See Docs").size(ButtonSize::Compact).on_click(|_ev, _window, cx| {
28836                            cx.open_url("https://zed.dev/docs/completions#edit-predictions-missing-keybinding");
28837                        })),
28838                )
28839        })
28840    }
28841}
28842
28843#[derive(Debug, Clone, Copy, PartialEq)]
28844pub struct LineHighlight {
28845    pub background: Background,
28846    pub border: Option<gpui::Hsla>,
28847    pub include_gutter: bool,
28848    pub type_id: Option<TypeId>,
28849}
28850
28851struct LineManipulationResult {
28852    pub new_text: String,
28853    pub line_count_before: usize,
28854    pub line_count_after: usize,
28855}
28856
28857fn render_diff_hunk_controls(
28858    row: u32,
28859    status: &DiffHunkStatus,
28860    hunk_range: Range<Anchor>,
28861    is_created_file: bool,
28862    line_height: Pixels,
28863    editor: &Entity<Editor>,
28864    _window: &mut Window,
28865    cx: &mut App,
28866) -> AnyElement {
28867    h_flex()
28868        .h(line_height)
28869        .mr_1()
28870        .gap_1()
28871        .px_0p5()
28872        .pb_1()
28873        .border_x_1()
28874        .border_b_1()
28875        .border_color(cx.theme().colors().border_variant)
28876        .rounded_b_lg()
28877        .bg(cx.theme().colors().editor_background)
28878        .gap_1()
28879        .block_mouse_except_scroll()
28880        .shadow_md()
28881        .child(if status.has_secondary_hunk() {
28882            Button::new(("stage", row as u64), "Stage")
28883                .alpha(if status.is_pending() { 0.66 } else { 1.0 })
28884                .tooltip({
28885                    let focus_handle = editor.focus_handle(cx);
28886                    move |_window, cx| {
28887                        Tooltip::for_action_in(
28888                            "Stage Hunk",
28889                            &::git::ToggleStaged,
28890                            &focus_handle,
28891                            cx,
28892                        )
28893                    }
28894                })
28895                .on_click({
28896                    let editor = editor.clone();
28897                    move |_event, _window, cx| {
28898                        editor.update(cx, |editor, cx| {
28899                            editor.stage_or_unstage_diff_hunks(
28900                                true,
28901                                vec![hunk_range.start..hunk_range.start],
28902                                cx,
28903                            );
28904                        });
28905                    }
28906                })
28907        } else {
28908            Button::new(("unstage", row as u64), "Unstage")
28909                .alpha(if status.is_pending() { 0.66 } else { 1.0 })
28910                .tooltip({
28911                    let focus_handle = editor.focus_handle(cx);
28912                    move |_window, cx| {
28913                        Tooltip::for_action_in(
28914                            "Unstage Hunk",
28915                            &::git::ToggleStaged,
28916                            &focus_handle,
28917                            cx,
28918                        )
28919                    }
28920                })
28921                .on_click({
28922                    let editor = editor.clone();
28923                    move |_event, _window, cx| {
28924                        editor.update(cx, |editor, cx| {
28925                            editor.stage_or_unstage_diff_hunks(
28926                                false,
28927                                vec![hunk_range.start..hunk_range.start],
28928                                cx,
28929                            );
28930                        });
28931                    }
28932                })
28933        })
28934        .child(
28935            Button::new(("restore", row as u64), "Restore")
28936                .tooltip({
28937                    let focus_handle = editor.focus_handle(cx);
28938                    move |_window, cx| {
28939                        Tooltip::for_action_in("Restore Hunk", &::git::Restore, &focus_handle, cx)
28940                    }
28941                })
28942                .on_click({
28943                    let editor = editor.clone();
28944                    move |_event, window, cx| {
28945                        editor.update(cx, |editor, cx| {
28946                            let snapshot = editor.snapshot(window, cx);
28947                            let point = hunk_range.start.to_point(&snapshot.buffer_snapshot());
28948                            editor.restore_hunks_in_ranges(vec![point..point], window, cx);
28949                        });
28950                    }
28951                })
28952                .disabled(is_created_file),
28953        )
28954        .when(
28955            !editor.read(cx).buffer().read(cx).all_diff_hunks_expanded(),
28956            |el| {
28957                el.child(
28958                    IconButton::new(("next-hunk", row as u64), IconName::ArrowDown)
28959                        .shape(IconButtonShape::Square)
28960                        .icon_size(IconSize::Small)
28961                        // .disabled(!has_multiple_hunks)
28962                        .tooltip({
28963                            let focus_handle = editor.focus_handle(cx);
28964                            move |_window, cx| {
28965                                Tooltip::for_action_in("Next Hunk", &GoToHunk, &focus_handle, cx)
28966                            }
28967                        })
28968                        .on_click({
28969                            let editor = editor.clone();
28970                            move |_event, window, cx| {
28971                                editor.update(cx, |editor, cx| {
28972                                    let snapshot = editor.snapshot(window, cx);
28973                                    let position =
28974                                        hunk_range.end.to_point(&snapshot.buffer_snapshot());
28975                                    editor.go_to_hunk_before_or_after_position(
28976                                        &snapshot,
28977                                        position,
28978                                        Direction::Next,
28979                                        window,
28980                                        cx,
28981                                    );
28982                                    editor.expand_selected_diff_hunks(cx);
28983                                });
28984                            }
28985                        }),
28986                )
28987                .child(
28988                    IconButton::new(("prev-hunk", row as u64), IconName::ArrowUp)
28989                        .shape(IconButtonShape::Square)
28990                        .icon_size(IconSize::Small)
28991                        // .disabled(!has_multiple_hunks)
28992                        .tooltip({
28993                            let focus_handle = editor.focus_handle(cx);
28994                            move |_window, cx| {
28995                                Tooltip::for_action_in(
28996                                    "Previous Hunk",
28997                                    &GoToPreviousHunk,
28998                                    &focus_handle,
28999                                    cx,
29000                                )
29001                            }
29002                        })
29003                        .on_click({
29004                            let editor = editor.clone();
29005                            move |_event, window, cx| {
29006                                editor.update(cx, |editor, cx| {
29007                                    let snapshot = editor.snapshot(window, cx);
29008                                    let point =
29009                                        hunk_range.start.to_point(&snapshot.buffer_snapshot());
29010                                    editor.go_to_hunk_before_or_after_position(
29011                                        &snapshot,
29012                                        point,
29013                                        Direction::Prev,
29014                                        window,
29015                                        cx,
29016                                    );
29017                                    editor.expand_selected_diff_hunks(cx);
29018                                });
29019                            }
29020                        }),
29021                )
29022            },
29023        )
29024        .into_any_element()
29025}
29026
29027pub fn multibuffer_context_lines(cx: &App) -> u32 {
29028    EditorSettings::try_get(cx)
29029        .map(|settings| settings.excerpt_context_lines)
29030        .unwrap_or(2)
29031        .min(32)
29032}