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 MULTI_BUFFER_EXCERPT_HEADER_HEIGHT: u32 = 1;
  240const CURSOR_BLINK_INTERVAL: Duration = Duration::from_millis(500);
  241const MAX_LINE_LEN: usize = 1024;
  242const MIN_NAVIGATION_HISTORY_ROW_DELTA: i64 = 10;
  243const MAX_SELECTION_HISTORY_LEN: usize = 1024;
  244pub(crate) const CURSORS_VISIBLE_FOR: Duration = Duration::from_millis(2000);
  245#[doc(hidden)]
  246pub const CODE_ACTIONS_DEBOUNCE_TIMEOUT: Duration = Duration::from_millis(250);
  247pub const SELECTION_HIGHLIGHT_DEBOUNCE_TIMEOUT: Duration = Duration::from_millis(100);
  248
  249pub(crate) const CODE_ACTION_TIMEOUT: Duration = Duration::from_secs(5);
  250pub(crate) const FORMAT_TIMEOUT: Duration = Duration::from_secs(5);
  251pub(crate) const SCROLL_CENTER_TOP_BOTTOM_DEBOUNCE_TIMEOUT: Duration = Duration::from_secs(1);
  252pub const LSP_REQUEST_DEBOUNCE_TIMEOUT: Duration = Duration::from_millis(50);
  253
  254pub(crate) const EDIT_PREDICTION_KEY_CONTEXT: &str = "edit_prediction";
  255pub(crate) const EDIT_PREDICTION_CONFLICT_KEY_CONTEXT: &str = "edit_prediction_conflict";
  256pub(crate) const MINIMAP_FONT_SIZE: AbsoluteLength = AbsoluteLength::Pixels(px(2.));
  257
  258pub type RenderDiffHunkControlsFn = Arc<
  259    dyn Fn(
  260        u32,
  261        &DiffHunkStatus,
  262        Range<Anchor>,
  263        bool,
  264        Pixels,
  265        &Entity<Editor>,
  266        &mut Window,
  267        &mut App,
  268    ) -> AnyElement,
  269>;
  270
  271enum ReportEditorEvent {
  272    Saved { auto_saved: bool },
  273    EditorOpened,
  274    Closed,
  275}
  276
  277impl ReportEditorEvent {
  278    pub fn event_type(&self) -> &'static str {
  279        match self {
  280            Self::Saved { .. } => "Editor Saved",
  281            Self::EditorOpened => "Editor Opened",
  282            Self::Closed => "Editor Closed",
  283        }
  284    }
  285}
  286
  287pub enum ActiveDebugLine {}
  288pub enum DebugStackFrameLine {}
  289
  290pub enum ConflictsOuter {}
  291pub enum ConflictsOurs {}
  292pub enum ConflictsTheirs {}
  293pub enum ConflictsOursMarker {}
  294pub enum ConflictsTheirsMarker {}
  295
  296pub struct HunkAddedColor;
  297pub struct HunkRemovedColor;
  298
  299#[derive(Debug, Copy, Clone, PartialEq, Eq)]
  300pub enum Navigated {
  301    Yes,
  302    No,
  303}
  304
  305impl Navigated {
  306    pub fn from_bool(yes: bool) -> Navigated {
  307        if yes { Navigated::Yes } else { Navigated::No }
  308    }
  309}
  310
  311#[derive(Debug, Clone, PartialEq, Eq)]
  312enum DisplayDiffHunk {
  313    Folded {
  314        display_row: DisplayRow,
  315    },
  316    Unfolded {
  317        is_created_file: bool,
  318        diff_base_byte_range: Range<usize>,
  319        display_row_range: Range<DisplayRow>,
  320        multi_buffer_range: Range<Anchor>,
  321        status: DiffHunkStatus,
  322        word_diffs: Vec<Range<MultiBufferOffset>>,
  323    },
  324}
  325
  326pub enum HideMouseCursorOrigin {
  327    TypingAction,
  328    MovementAction,
  329}
  330
  331pub fn init(cx: &mut App) {
  332    cx.set_global(GlobalBlameRenderer(Arc::new(())));
  333    cx.set_global(breadcrumbs::RenderBreadcrumbText(render_breadcrumb_text));
  334
  335    workspace::register_project_item::<Editor>(cx);
  336    workspace::FollowableViewRegistry::register::<Editor>(cx);
  337    workspace::register_serializable_item::<Editor>(cx);
  338
  339    cx.observe_new(
  340        |workspace: &mut Workspace, _: Option<&mut Window>, _cx: &mut Context<Workspace>| {
  341            workspace.register_action(Editor::new_file);
  342            workspace.register_action(Editor::new_file_split);
  343            workspace.register_action(Editor::new_file_vertical);
  344            workspace.register_action(Editor::new_file_horizontal);
  345            workspace.register_action(Editor::cancel_language_server_work);
  346            workspace.register_action(Editor::toggle_focus);
  347        },
  348    )
  349    .detach();
  350
  351    cx.on_action(move |_: &workspace::NewFile, cx| {
  352        let app_state = workspace::AppState::global(cx);
  353        if let Some(app_state) = app_state.upgrade() {
  354            workspace::open_new(
  355                Default::default(),
  356                app_state,
  357                cx,
  358                |workspace, window, cx| {
  359                    Editor::new_file(workspace, &Default::default(), window, cx)
  360                },
  361            )
  362            .detach();
  363        }
  364    })
  365    .on_action(move |_: &workspace::NewWindow, cx| {
  366        let app_state = workspace::AppState::global(cx);
  367        if let Some(app_state) = app_state.upgrade() {
  368            workspace::open_new(
  369                Default::default(),
  370                app_state,
  371                cx,
  372                |workspace, window, cx| {
  373                    cx.activate(true);
  374                    Editor::new_file(workspace, &Default::default(), window, cx)
  375                },
  376            )
  377            .detach();
  378        }
  379    });
  380    _ = ui_input::ERASED_EDITOR_FACTORY.set(|window, cx| {
  381        Arc::new(ErasedEditorImpl(
  382            cx.new(|cx| Editor::single_line(window, cx)),
  383        )) as Arc<dyn ErasedEditor>
  384    });
  385    _ = multi_buffer::EXCERPT_CONTEXT_LINES.set(multibuffer_context_lines);
  386}
  387
  388pub fn set_blame_renderer(renderer: impl BlameRenderer + 'static, cx: &mut App) {
  389    cx.set_global(GlobalBlameRenderer(Arc::new(renderer)));
  390}
  391
  392pub trait DiagnosticRenderer {
  393    fn render_group(
  394        &self,
  395        diagnostic_group: Vec<DiagnosticEntryRef<'_, Point>>,
  396        buffer_id: BufferId,
  397        snapshot: EditorSnapshot,
  398        editor: WeakEntity<Editor>,
  399        language_registry: Option<Arc<LanguageRegistry>>,
  400        cx: &mut App,
  401    ) -> Vec<BlockProperties<Anchor>>;
  402
  403    fn render_hover(
  404        &self,
  405        diagnostic_group: Vec<DiagnosticEntryRef<'_, Point>>,
  406        range: Range<Point>,
  407        buffer_id: BufferId,
  408        language_registry: Option<Arc<LanguageRegistry>>,
  409        cx: &mut App,
  410    ) -> Option<Entity<markdown::Markdown>>;
  411
  412    fn open_link(
  413        &self,
  414        editor: &mut Editor,
  415        link: SharedString,
  416        window: &mut Window,
  417        cx: &mut Context<Editor>,
  418    );
  419}
  420
  421pub(crate) struct GlobalDiagnosticRenderer(pub Arc<dyn DiagnosticRenderer>);
  422
  423impl GlobalDiagnosticRenderer {
  424    fn global(cx: &App) -> Option<Arc<dyn DiagnosticRenderer>> {
  425        cx.try_global::<Self>().map(|g| g.0.clone())
  426    }
  427}
  428
  429impl gpui::Global for GlobalDiagnosticRenderer {}
  430pub fn set_diagnostic_renderer(renderer: impl DiagnosticRenderer + 'static, cx: &mut App) {
  431    cx.set_global(GlobalDiagnosticRenderer(Arc::new(renderer)));
  432}
  433
  434pub struct SearchWithinRange;
  435
  436trait InvalidationRegion {
  437    fn ranges(&self) -> &[Range<Anchor>];
  438}
  439
  440#[derive(Clone, Debug, PartialEq)]
  441pub enum SelectPhase {
  442    Begin {
  443        position: DisplayPoint,
  444        add: bool,
  445        click_count: usize,
  446    },
  447    BeginColumnar {
  448        position: DisplayPoint,
  449        reset: bool,
  450        mode: ColumnarMode,
  451        goal_column: u32,
  452    },
  453    Extend {
  454        position: DisplayPoint,
  455        click_count: usize,
  456    },
  457    Update {
  458        position: DisplayPoint,
  459        goal_column: u32,
  460        scroll_delta: gpui::Point<f32>,
  461    },
  462    End,
  463}
  464
  465#[derive(Clone, Debug, PartialEq)]
  466pub enum ColumnarMode {
  467    FromMouse,
  468    FromSelection,
  469}
  470
  471#[derive(Clone, Debug)]
  472pub enum SelectMode {
  473    Character,
  474    Word(Range<Anchor>),
  475    Line(Range<Anchor>),
  476    All,
  477}
  478
  479#[derive(Copy, Clone, Default, PartialEq, Eq, Debug)]
  480pub enum SizingBehavior {
  481    /// The editor will layout itself using `size_full` and will include the vertical
  482    /// scroll margin as requested by user settings.
  483    #[default]
  484    Default,
  485    /// The editor will layout itself using `size_full`, but will not have any
  486    /// vertical overscroll.
  487    ExcludeOverscrollMargin,
  488    /// The editor will request a vertical size according to its content and will be
  489    /// layouted without a vertical scroll margin.
  490    SizeByContent,
  491}
  492
  493#[derive(Clone, PartialEq, Eq, Debug)]
  494pub enum EditorMode {
  495    SingleLine,
  496    AutoHeight {
  497        min_lines: usize,
  498        max_lines: Option<usize>,
  499    },
  500    Full {
  501        /// When set to `true`, the editor will scale its UI elements with the buffer font size.
  502        scale_ui_elements_with_buffer_font_size: bool,
  503        /// When set to `true`, the editor will render a background for the active line.
  504        show_active_line_background: bool,
  505        /// Determines the sizing behavior for this editor
  506        sizing_behavior: SizingBehavior,
  507    },
  508    Minimap {
  509        parent: WeakEntity<Editor>,
  510    },
  511}
  512
  513impl EditorMode {
  514    pub fn full() -> Self {
  515        Self::Full {
  516            scale_ui_elements_with_buffer_font_size: true,
  517            show_active_line_background: true,
  518            sizing_behavior: SizingBehavior::Default,
  519        }
  520    }
  521
  522    #[inline]
  523    pub fn is_full(&self) -> bool {
  524        matches!(self, Self::Full { .. })
  525    }
  526
  527    #[inline]
  528    pub fn is_single_line(&self) -> bool {
  529        matches!(self, Self::SingleLine { .. })
  530    }
  531
  532    #[inline]
  533    fn is_minimap(&self) -> bool {
  534        matches!(self, Self::Minimap { .. })
  535    }
  536}
  537
  538#[derive(Copy, Clone, Debug)]
  539pub enum SoftWrap {
  540    /// Prefer not to wrap at all.
  541    ///
  542    /// Note: this is currently internal, as actually limited by [`crate::MAX_LINE_LEN`] until it wraps.
  543    /// The mode is used inside git diff hunks, where it's seems currently more useful to not wrap as much as possible.
  544    GitDiff,
  545    /// Prefer a single line generally, unless an overly long line is encountered.
  546    None,
  547    /// Soft wrap lines that exceed the editor width.
  548    EditorWidth,
  549    /// Soft wrap lines at the preferred line length.
  550    Column(u32),
  551    /// Soft wrap line at the preferred line length or the editor width (whichever is smaller).
  552    Bounded(u32),
  553}
  554
  555#[derive(Clone)]
  556pub struct EditorStyle {
  557    pub background: Hsla,
  558    pub border: Hsla,
  559    pub local_player: PlayerColor,
  560    pub text: TextStyle,
  561    pub scrollbar_width: Pixels,
  562    pub syntax: Arc<SyntaxTheme>,
  563    pub status: StatusColors,
  564    pub inlay_hints_style: HighlightStyle,
  565    pub edit_prediction_styles: EditPredictionStyles,
  566    pub unnecessary_code_fade: f32,
  567    pub show_underlines: bool,
  568}
  569
  570impl Default for EditorStyle {
  571    fn default() -> Self {
  572        Self {
  573            background: Hsla::default(),
  574            border: Hsla::default(),
  575            local_player: PlayerColor::default(),
  576            text: TextStyle::default(),
  577            scrollbar_width: Pixels::default(),
  578            syntax: Default::default(),
  579            // HACK: Status colors don't have a real default.
  580            // We should look into removing the status colors from the editor
  581            // style and retrieve them directly from the theme.
  582            status: StatusColors::dark(),
  583            inlay_hints_style: HighlightStyle::default(),
  584            edit_prediction_styles: EditPredictionStyles {
  585                insertion: HighlightStyle::default(),
  586                whitespace: HighlightStyle::default(),
  587            },
  588            unnecessary_code_fade: Default::default(),
  589            show_underlines: true,
  590        }
  591    }
  592}
  593
  594pub fn make_inlay_hints_style(cx: &App) -> HighlightStyle {
  595    let show_background = language_settings::language_settings(None, None, cx)
  596        .inlay_hints
  597        .show_background;
  598
  599    let mut style = cx.theme().syntax().get("hint");
  600
  601    if style.color.is_none() {
  602        style.color = Some(cx.theme().status().hint);
  603    }
  604
  605    if !show_background {
  606        style.background_color = None;
  607        return style;
  608    }
  609
  610    if style.background_color.is_none() {
  611        style.background_color = Some(cx.theme().status().hint_background);
  612    }
  613
  614    style
  615}
  616
  617pub fn make_suggestion_styles(cx: &App) -> EditPredictionStyles {
  618    EditPredictionStyles {
  619        insertion: HighlightStyle {
  620            color: Some(cx.theme().status().predictive),
  621            ..HighlightStyle::default()
  622        },
  623        whitespace: HighlightStyle {
  624            background_color: Some(cx.theme().status().created_background),
  625            ..HighlightStyle::default()
  626        },
  627    }
  628}
  629
  630type CompletionId = usize;
  631
  632pub(crate) enum EditDisplayMode {
  633    TabAccept,
  634    DiffPopover,
  635    Inline,
  636}
  637
  638enum EditPrediction {
  639    Edit {
  640        edits: Vec<(Range<Anchor>, Arc<str>)>,
  641        /// Predicted cursor position as (anchor, offset_from_anchor).
  642        /// The anchor is in multibuffer coordinates; after applying edits,
  643        /// resolve the anchor and add the offset to get the final cursor position.
  644        cursor_position: Option<(Anchor, usize)>,
  645        edit_preview: Option<EditPreview>,
  646        display_mode: EditDisplayMode,
  647        snapshot: BufferSnapshot,
  648    },
  649    /// Move to a specific location in the active editor
  650    MoveWithin {
  651        target: Anchor,
  652        snapshot: BufferSnapshot,
  653    },
  654    /// Move to a specific location in a different editor (not the active one)
  655    MoveOutside {
  656        target: language::Anchor,
  657        snapshot: BufferSnapshot,
  658    },
  659}
  660
  661struct EditPredictionState {
  662    inlay_ids: Vec<InlayId>,
  663    completion: EditPrediction,
  664    completion_id: Option<SharedString>,
  665    invalidation_range: Option<Range<Anchor>>,
  666}
  667
  668enum EditPredictionSettings {
  669    Disabled,
  670    Enabled {
  671        show_in_menu: bool,
  672        preview_requires_modifier: bool,
  673    },
  674}
  675
  676#[derive(Debug, Clone)]
  677struct InlineDiagnostic {
  678    message: SharedString,
  679    group_id: usize,
  680    is_primary: bool,
  681    start: Point,
  682    severity: lsp::DiagnosticSeverity,
  683}
  684
  685pub enum MenuEditPredictionsPolicy {
  686    Never,
  687    ByProvider,
  688}
  689
  690pub enum EditPredictionPreview {
  691    /// Modifier is not pressed
  692    Inactive { released_too_fast: bool },
  693    /// Modifier pressed
  694    Active {
  695        since: Instant,
  696        previous_scroll_position: Option<SharedScrollAnchor>,
  697    },
  698}
  699
  700impl EditPredictionPreview {
  701    pub fn released_too_fast(&self) -> bool {
  702        match self {
  703            EditPredictionPreview::Inactive { released_too_fast } => *released_too_fast,
  704            EditPredictionPreview::Active { .. } => false,
  705        }
  706    }
  707
  708    pub fn set_previous_scroll_position(&mut self, scroll_position: Option<SharedScrollAnchor>) {
  709        if let EditPredictionPreview::Active {
  710            previous_scroll_position,
  711            ..
  712        } = self
  713        {
  714            *previous_scroll_position = scroll_position;
  715        }
  716    }
  717}
  718
  719pub struct ContextMenuOptions {
  720    pub min_entries_visible: usize,
  721    pub max_entries_visible: usize,
  722    pub placement: Option<ContextMenuPlacement>,
  723}
  724
  725#[derive(Debug, Clone, PartialEq, Eq)]
  726pub enum ContextMenuPlacement {
  727    Above,
  728    Below,
  729}
  730
  731#[derive(Copy, Clone, Eq, PartialEq, PartialOrd, Ord, Debug, Default)]
  732struct EditorActionId(usize);
  733
  734impl EditorActionId {
  735    pub fn post_inc(&mut self) -> Self {
  736        let answer = self.0;
  737
  738        *self = Self(answer + 1);
  739
  740        Self(answer)
  741    }
  742}
  743
  744// type GetFieldEditorTheme = dyn Fn(&theme::Theme) -> theme::FieldEditor;
  745// type OverrideTextStyle = dyn Fn(&EditorStyle) -> Option<HighlightStyle>;
  746
  747type BackgroundHighlight = (
  748    Arc<dyn Fn(&usize, &Theme) -> Hsla + Send + Sync>,
  749    Arc<[Range<Anchor>]>,
  750);
  751type GutterHighlight = (fn(&App) -> Hsla, Vec<Range<Anchor>>);
  752
  753#[derive(Default)]
  754struct ScrollbarMarkerState {
  755    scrollbar_size: Size<Pixels>,
  756    dirty: bool,
  757    markers: Arc<[PaintQuad]>,
  758    pending_refresh: Option<Task<Result<()>>>,
  759}
  760
  761impl ScrollbarMarkerState {
  762    fn should_refresh(&self, scrollbar_size: Size<Pixels>) -> bool {
  763        self.pending_refresh.is_none() && (self.scrollbar_size != scrollbar_size || self.dirty)
  764    }
  765}
  766
  767#[derive(Clone, Copy, PartialEq, Eq)]
  768pub enum MinimapVisibility {
  769    Disabled,
  770    Enabled {
  771        /// The configuration currently present in the users settings.
  772        setting_configuration: bool,
  773        /// Whether to override the currently set visibility from the users setting.
  774        toggle_override: bool,
  775    },
  776}
  777
  778impl MinimapVisibility {
  779    fn for_mode(mode: &EditorMode, cx: &App) -> Self {
  780        if mode.is_full() {
  781            Self::Enabled {
  782                setting_configuration: EditorSettings::get_global(cx).minimap.minimap_enabled(),
  783                toggle_override: false,
  784            }
  785        } else {
  786            Self::Disabled
  787        }
  788    }
  789
  790    fn hidden(&self) -> Self {
  791        match *self {
  792            Self::Enabled {
  793                setting_configuration,
  794                ..
  795            } => Self::Enabled {
  796                setting_configuration,
  797                toggle_override: setting_configuration,
  798            },
  799            Self::Disabled => Self::Disabled,
  800        }
  801    }
  802
  803    fn disabled(&self) -> bool {
  804        matches!(*self, Self::Disabled)
  805    }
  806
  807    fn settings_visibility(&self) -> bool {
  808        match *self {
  809            Self::Enabled {
  810                setting_configuration,
  811                ..
  812            } => setting_configuration,
  813            _ => false,
  814        }
  815    }
  816
  817    fn visible(&self) -> bool {
  818        match *self {
  819            Self::Enabled {
  820                setting_configuration,
  821                toggle_override,
  822            } => setting_configuration ^ toggle_override,
  823            _ => false,
  824        }
  825    }
  826
  827    fn toggle_visibility(&self) -> Self {
  828        match *self {
  829            Self::Enabled {
  830                toggle_override,
  831                setting_configuration,
  832            } => Self::Enabled {
  833                setting_configuration,
  834                toggle_override: !toggle_override,
  835            },
  836            Self::Disabled => Self::Disabled,
  837        }
  838    }
  839}
  840
  841#[derive(Debug, Clone, Copy, PartialEq, Eq)]
  842pub enum BufferSerialization {
  843    All,
  844    NonDirtyBuffers,
  845}
  846
  847impl BufferSerialization {
  848    fn new(restore_unsaved_buffers: bool) -> Self {
  849        if restore_unsaved_buffers {
  850            Self::All
  851        } else {
  852            Self::NonDirtyBuffers
  853        }
  854    }
  855}
  856
  857#[derive(Clone, Debug)]
  858struct RunnableTasks {
  859    templates: Vec<(TaskSourceKind, TaskTemplate)>,
  860    offset: multi_buffer::Anchor,
  861    // We need the column at which the task context evaluation should take place (when we're spawning it via gutter).
  862    column: u32,
  863    // Values of all named captures, including those starting with '_'
  864    extra_variables: HashMap<String, String>,
  865    // Full range of the tagged region. We use it to determine which `extra_variables` to grab for context resolution in e.g. a modal.
  866    context_range: Range<BufferOffset>,
  867}
  868
  869impl RunnableTasks {
  870    fn resolve<'a>(
  871        &'a self,
  872        cx: &'a task::TaskContext,
  873    ) -> impl Iterator<Item = (TaskSourceKind, ResolvedTask)> + 'a {
  874        self.templates.iter().filter_map(|(kind, template)| {
  875            template
  876                .resolve_task(&kind.to_id_base(), cx)
  877                .map(|task| (kind.clone(), task))
  878        })
  879    }
  880}
  881
  882#[derive(Clone)]
  883pub struct ResolvedTasks {
  884    templates: SmallVec<[(TaskSourceKind, ResolvedTask); 1]>,
  885    position: Anchor,
  886}
  887
  888/// Addons allow storing per-editor state in other crates (e.g. Vim)
  889pub trait Addon: 'static {
  890    fn extend_key_context(&self, _: &mut KeyContext, _: &App) {}
  891
  892    fn render_buffer_header_controls(
  893        &self,
  894        _: &ExcerptInfo,
  895        _: &Window,
  896        _: &App,
  897    ) -> Option<AnyElement> {
  898        None
  899    }
  900
  901    fn override_status_for_buffer_id(&self, _: BufferId, _: &App) -> Option<FileStatus> {
  902        None
  903    }
  904
  905    fn to_any(&self) -> &dyn std::any::Any;
  906
  907    fn to_any_mut(&mut self) -> Option<&mut dyn std::any::Any> {
  908        None
  909    }
  910}
  911
  912struct ChangeLocation {
  913    current: Option<Vec<Anchor>>,
  914    original: Vec<Anchor>,
  915}
  916impl ChangeLocation {
  917    fn locations(&self) -> &[Anchor] {
  918        self.current.as_ref().unwrap_or(&self.original)
  919    }
  920}
  921
  922/// A set of caret positions, registered when the editor was edited.
  923pub struct ChangeList {
  924    changes: Vec<ChangeLocation>,
  925    /// Currently "selected" change.
  926    position: Option<usize>,
  927}
  928
  929impl ChangeList {
  930    pub fn new() -> Self {
  931        Self {
  932            changes: Vec::new(),
  933            position: None,
  934        }
  935    }
  936
  937    /// Moves to the next change in the list (based on the direction given) and returns the caret positions for the next change.
  938    /// If reaches the end of the list in the direction, returns the corresponding change until called for a different direction.
  939    pub fn next_change(&mut self, count: usize, direction: Direction) -> Option<&[Anchor]> {
  940        if self.changes.is_empty() {
  941            return None;
  942        }
  943
  944        let prev = self.position.unwrap_or(self.changes.len());
  945        let next = if direction == Direction::Prev {
  946            prev.saturating_sub(count)
  947        } else {
  948            (prev + count).min(self.changes.len() - 1)
  949        };
  950        self.position = Some(next);
  951        self.changes.get(next).map(|change| change.locations())
  952    }
  953
  954    /// Adds a new change to the list, resetting the change list position.
  955    pub fn push_to_change_list(&mut self, group: bool, new_positions: Vec<Anchor>) {
  956        self.position.take();
  957        if let Some(last) = self.changes.last_mut()
  958            && group
  959        {
  960            last.current = Some(new_positions)
  961        } else {
  962            self.changes.push(ChangeLocation {
  963                original: new_positions,
  964                current: None,
  965            });
  966        }
  967    }
  968
  969    pub fn last(&self) -> Option<&[Anchor]> {
  970        self.changes.last().map(|change| change.locations())
  971    }
  972
  973    pub fn last_before_grouping(&self) -> Option<&[Anchor]> {
  974        self.changes.last().map(|change| change.original.as_slice())
  975    }
  976
  977    pub fn invert_last_group(&mut self) {
  978        if let Some(last) = self.changes.last_mut()
  979            && let Some(current) = last.current.as_mut()
  980        {
  981            mem::swap(&mut last.original, current);
  982        }
  983    }
  984}
  985
  986#[derive(Clone)]
  987struct InlineBlamePopoverState {
  988    scroll_handle: ScrollHandle,
  989    commit_message: Option<ParsedCommitMessage>,
  990    markdown: Entity<Markdown>,
  991}
  992
  993struct InlineBlamePopover {
  994    position: gpui::Point<Pixels>,
  995    hide_task: Option<Task<()>>,
  996    popover_bounds: Option<Bounds<Pixels>>,
  997    popover_state: InlineBlamePopoverState,
  998    keyboard_grace: bool,
  999}
 1000
 1001enum SelectionDragState {
 1002    /// State when no drag related activity is detected.
 1003    None,
 1004    /// State when the mouse is down on a selection that is about to be dragged.
 1005    ReadyToDrag {
 1006        selection: Selection<Anchor>,
 1007        click_position: gpui::Point<Pixels>,
 1008        mouse_down_time: Instant,
 1009    },
 1010    /// State when the mouse is dragging the selection in the editor.
 1011    Dragging {
 1012        selection: Selection<Anchor>,
 1013        drop_cursor: Selection<Anchor>,
 1014        hide_drop_cursor: bool,
 1015    },
 1016}
 1017
 1018enum ColumnarSelectionState {
 1019    FromMouse {
 1020        selection_tail: Anchor,
 1021        display_point: Option<DisplayPoint>,
 1022    },
 1023    FromSelection {
 1024        selection_tail: Anchor,
 1025    },
 1026}
 1027
 1028/// Represents a breakpoint indicator that shows up when hovering over lines in the gutter that don't have
 1029/// a breakpoint on them.
 1030#[derive(Clone, Copy, Debug, PartialEq, Eq)]
 1031struct PhantomBreakpointIndicator {
 1032    display_row: DisplayRow,
 1033    /// There's a small debounce between hovering over the line and showing the indicator.
 1034    /// We don't want to show the indicator when moving the mouse from editor to e.g. project panel.
 1035    is_active: bool,
 1036    collides_with_existing_breakpoint: bool,
 1037}
 1038
 1039/// Represents a diff review button indicator that shows up when hovering over lines in the gutter
 1040/// in diff view mode.
 1041#[derive(Clone, Copy, Debug, PartialEq, Eq)]
 1042pub(crate) struct PhantomDiffReviewIndicator {
 1043    /// The starting anchor of the selection (or the only row if not dragging).
 1044    pub start: Anchor,
 1045    /// The ending anchor of the selection. Equal to start_anchor for single-line selection.
 1046    pub end: Anchor,
 1047    /// There's a small debounce between hovering over the line and showing the indicator.
 1048    /// We don't want to show the indicator when moving the mouse from editor to e.g. project panel.
 1049    pub is_active: bool,
 1050}
 1051
 1052#[derive(Clone, Debug)]
 1053pub(crate) struct DiffReviewDragState {
 1054    pub start_anchor: Anchor,
 1055    pub current_anchor: Anchor,
 1056}
 1057
 1058impl DiffReviewDragState {
 1059    pub fn row_range(&self, snapshot: &DisplaySnapshot) -> std::ops::RangeInclusive<DisplayRow> {
 1060        let start = self.start_anchor.to_display_point(snapshot).row();
 1061        let current = self.current_anchor.to_display_point(snapshot).row();
 1062
 1063        (start..=current).sorted()
 1064    }
 1065}
 1066
 1067/// Identifies a specific hunk in the diff buffer.
 1068/// Used as a key to group comments by their location.
 1069#[derive(Clone, Debug)]
 1070pub struct DiffHunkKey {
 1071    /// The file path (relative to worktree) this hunk belongs to.
 1072    pub file_path: Arc<util::rel_path::RelPath>,
 1073    /// An anchor at the start of the hunk. This tracks position as the buffer changes.
 1074    pub hunk_start_anchor: Anchor,
 1075}
 1076
 1077/// A review comment stored locally before being sent to the Agent panel.
 1078#[derive(Clone)]
 1079pub struct StoredReviewComment {
 1080    /// Unique identifier for this comment (for edit/delete operations).
 1081    pub id: usize,
 1082    /// The comment text entered by the user.
 1083    pub comment: String,
 1084    /// Anchors for the code range being reviewed.
 1085    pub range: Range<Anchor>,
 1086    /// Timestamp when the comment was created (for chronological ordering).
 1087    pub created_at: Instant,
 1088    /// Whether this comment is currently being edited inline.
 1089    pub is_editing: bool,
 1090}
 1091
 1092impl StoredReviewComment {
 1093    pub fn new(id: usize, comment: String, anchor_range: Range<Anchor>) -> Self {
 1094        Self {
 1095            id,
 1096            comment,
 1097            range: anchor_range,
 1098            created_at: Instant::now(),
 1099            is_editing: false,
 1100        }
 1101    }
 1102}
 1103
 1104/// Represents an active diff review overlay that appears when clicking the "Add Review" button.
 1105pub(crate) struct DiffReviewOverlay {
 1106    pub anchor_range: Range<Anchor>,
 1107    /// The block ID for the overlay.
 1108    pub block_id: CustomBlockId,
 1109    /// The editor entity for the review input.
 1110    pub prompt_editor: Entity<Editor>,
 1111    /// The hunk key this overlay belongs to.
 1112    pub hunk_key: DiffHunkKey,
 1113    /// Whether the comments section is expanded.
 1114    pub comments_expanded: bool,
 1115    /// Editors for comments currently being edited inline.
 1116    /// Key: comment ID, Value: Editor entity for inline editing.
 1117    pub inline_edit_editors: HashMap<usize, Entity<Editor>>,
 1118    /// Subscriptions for inline edit editors' action handlers.
 1119    /// Key: comment ID, Value: Subscription keeping the Newline action handler alive.
 1120    pub inline_edit_subscriptions: HashMap<usize, Subscription>,
 1121    /// The current user's avatar URI for display in comment rows.
 1122    pub user_avatar_uri: Option<SharedUri>,
 1123    /// Subscription to keep the action handler alive.
 1124    _subscription: Subscription,
 1125}
 1126
 1127/// Zed's primary implementation of text input, allowing users to edit a [`MultiBuffer`].
 1128///
 1129/// See the [module level documentation](self) for more information.
 1130pub struct Editor {
 1131    focus_handle: FocusHandle,
 1132    last_focused_descendant: Option<WeakFocusHandle>,
 1133    /// The text buffer being edited
 1134    buffer: Entity<MultiBuffer>,
 1135    /// Map of how text in the buffer should be displayed.
 1136    /// Handles soft wraps, folds, fake inlay text insertions, etc.
 1137    pub display_map: Entity<DisplayMap>,
 1138    placeholder_display_map: Option<Entity<DisplayMap>>,
 1139    pub selections: SelectionsCollection,
 1140    pub scroll_manager: ScrollManager,
 1141    /// When inline assist editors are linked, they all render cursors because
 1142    /// typing enters text into each of them, even the ones that aren't focused.
 1143    pub(crate) show_cursor_when_unfocused: bool,
 1144    columnar_selection_state: Option<ColumnarSelectionState>,
 1145    add_selections_state: Option<AddSelectionsState>,
 1146    select_next_state: Option<SelectNextState>,
 1147    select_prev_state: Option<SelectNextState>,
 1148    selection_history: SelectionHistory,
 1149    defer_selection_effects: bool,
 1150    deferred_selection_effects_state: Option<DeferredSelectionEffectsState>,
 1151    autoclose_regions: Vec<AutocloseRegion>,
 1152    snippet_stack: InvalidationStack<SnippetState>,
 1153    select_syntax_node_history: SelectSyntaxNodeHistory,
 1154    ime_transaction: Option<TransactionId>,
 1155    pub diagnostics_max_severity: DiagnosticSeverity,
 1156    active_diagnostics: ActiveDiagnostic,
 1157    show_inline_diagnostics: bool,
 1158    inline_diagnostics_update: Task<()>,
 1159    inline_diagnostics_enabled: bool,
 1160    diagnostics_enabled: bool,
 1161    word_completions_enabled: bool,
 1162    inline_diagnostics: Vec<(Anchor, InlineDiagnostic)>,
 1163    soft_wrap_mode_override: Option<language_settings::SoftWrap>,
 1164    hard_wrap: Option<usize>,
 1165    project: Option<Entity<Project>>,
 1166    semantics_provider: Option<Rc<dyn SemanticsProvider>>,
 1167    completion_provider: Option<Rc<dyn CompletionProvider>>,
 1168    collaboration_hub: Option<Box<dyn CollaborationHub>>,
 1169    blink_manager: Entity<BlinkManager>,
 1170    show_cursor_names: bool,
 1171    hovered_cursors: HashMap<HoveredCursor, Task<()>>,
 1172    pub show_local_selections: bool,
 1173    mode: EditorMode,
 1174    show_breadcrumbs: bool,
 1175    show_gutter: bool,
 1176    show_scrollbars: ScrollbarAxes,
 1177    minimap_visibility: MinimapVisibility,
 1178    offset_content: bool,
 1179    disable_expand_excerpt_buttons: bool,
 1180    delegate_expand_excerpts: bool,
 1181    delegate_stage_and_restore: bool,
 1182    delegate_open_excerpts: bool,
 1183    enable_lsp_data: bool,
 1184    enable_runnables: bool,
 1185    show_line_numbers: Option<bool>,
 1186    use_relative_line_numbers: Option<bool>,
 1187    show_git_diff_gutter: Option<bool>,
 1188    show_code_actions: Option<bool>,
 1189    show_runnables: Option<bool>,
 1190    show_breakpoints: Option<bool>,
 1191    show_diff_review_button: bool,
 1192    show_wrap_guides: Option<bool>,
 1193    show_indent_guides: Option<bool>,
 1194    buffers_with_disabled_indent_guides: HashSet<BufferId>,
 1195    highlight_order: usize,
 1196    highlighted_rows: HashMap<TypeId, Vec<RowHighlight>>,
 1197    background_highlights: HashMap<HighlightKey, BackgroundHighlight>,
 1198    gutter_highlights: HashMap<TypeId, GutterHighlight>,
 1199    scrollbar_marker_state: ScrollbarMarkerState,
 1200    active_indent_guides_state: ActiveIndentGuidesState,
 1201    nav_history: Option<ItemNavHistory>,
 1202    context_menu: RefCell<Option<CodeContextMenu>>,
 1203    context_menu_options: Option<ContextMenuOptions>,
 1204    mouse_context_menu: Option<MouseContextMenu>,
 1205    completion_tasks: Vec<(CompletionId, Task<()>)>,
 1206    inline_blame_popover: Option<InlineBlamePopover>,
 1207    inline_blame_popover_show_task: Option<Task<()>>,
 1208    signature_help_state: SignatureHelpState,
 1209    auto_signature_help: Option<bool>,
 1210    find_all_references_task_sources: Vec<Anchor>,
 1211    next_completion_id: CompletionId,
 1212    available_code_actions: Option<(Location, Rc<[AvailableCodeAction]>)>,
 1213    code_actions_task: Option<Task<Result<()>>>,
 1214    quick_selection_highlight_task: Option<(Range<Anchor>, Task<()>)>,
 1215    debounced_selection_highlight_task: Option<(Range<Anchor>, Task<()>)>,
 1216    debounced_selection_highlight_complete: bool,
 1217    document_highlights_task: Option<Task<()>>,
 1218    linked_editing_range_task: Option<Task<Option<()>>>,
 1219    linked_edit_ranges: linked_editing_ranges::LinkedEditingRanges,
 1220    pending_rename: Option<RenameState>,
 1221    searchable: bool,
 1222    cursor_shape: CursorShape,
 1223    /// Whether the cursor is offset one character to the left when something is
 1224    /// selected (needed for vim visual mode)
 1225    cursor_offset_on_selection: bool,
 1226    current_line_highlight: Option<CurrentLineHighlight>,
 1227    /// Whether to collapse search match ranges to just their start position.
 1228    /// When true, navigating to a match positions the cursor at the match
 1229    /// without selecting the matched text.
 1230    collapse_matches: bool,
 1231    autoindent_mode: Option<AutoindentMode>,
 1232    workspace: Option<(WeakEntity<Workspace>, Option<WorkspaceId>)>,
 1233    input_enabled: bool,
 1234    use_modal_editing: bool,
 1235    read_only: bool,
 1236    leader_id: Option<CollaboratorId>,
 1237    remote_id: Option<ViewId>,
 1238    pub hover_state: HoverState,
 1239    pending_mouse_down: Option<Rc<RefCell<Option<MouseDownEvent>>>>,
 1240    prev_pressure_stage: Option<PressureStage>,
 1241    gutter_hovered: bool,
 1242    hovered_link_state: Option<HoveredLinkState>,
 1243    edit_prediction_provider: Option<RegisteredEditPredictionDelegate>,
 1244    code_action_providers: Vec<Rc<dyn CodeActionProvider>>,
 1245    active_edit_prediction: Option<EditPredictionState>,
 1246    /// Used to prevent flickering as the user types while the menu is open
 1247    stale_edit_prediction_in_menu: Option<EditPredictionState>,
 1248    edit_prediction_settings: EditPredictionSettings,
 1249    edit_predictions_hidden_for_vim_mode: bool,
 1250    show_edit_predictions_override: Option<bool>,
 1251    show_completions_on_input_override: Option<bool>,
 1252    menu_edit_predictions_policy: MenuEditPredictionsPolicy,
 1253    edit_prediction_preview: EditPredictionPreview,
 1254    edit_prediction_indent_conflict: bool,
 1255    edit_prediction_requires_modifier_in_indent_conflict: bool,
 1256    next_inlay_id: usize,
 1257    next_color_inlay_id: usize,
 1258    _subscriptions: Vec<Subscription>,
 1259    pixel_position_of_newest_cursor: Option<gpui::Point<Pixels>>,
 1260    gutter_dimensions: GutterDimensions,
 1261    style: Option<EditorStyle>,
 1262    text_style_refinement: Option<TextStyleRefinement>,
 1263    next_editor_action_id: EditorActionId,
 1264    editor_actions: Rc<
 1265        RefCell<BTreeMap<EditorActionId, Box<dyn Fn(&Editor, &mut Window, &mut Context<Self>)>>>,
 1266    >,
 1267    use_autoclose: bool,
 1268    use_auto_surround: bool,
 1269    auto_replace_emoji_shortcode: bool,
 1270    jsx_tag_auto_close_enabled_in_any_buffer: bool,
 1271    show_git_blame_gutter: bool,
 1272    show_git_blame_inline: bool,
 1273    show_git_blame_inline_delay_task: Option<Task<()>>,
 1274    git_blame_inline_enabled: bool,
 1275    render_diff_hunk_controls: RenderDiffHunkControlsFn,
 1276    buffer_serialization: Option<BufferSerialization>,
 1277    show_selection_menu: Option<bool>,
 1278    blame: Option<Entity<GitBlame>>,
 1279    blame_subscription: Option<Subscription>,
 1280    custom_context_menu: Option<
 1281        Box<
 1282            dyn 'static
 1283                + Fn(
 1284                    &mut Self,
 1285                    DisplayPoint,
 1286                    &mut Window,
 1287                    &mut Context<Self>,
 1288                ) -> Option<Entity<ui::ContextMenu>>,
 1289        >,
 1290    >,
 1291    last_bounds: Option<Bounds<Pixels>>,
 1292    last_position_map: Option<Rc<PositionMap>>,
 1293    expect_bounds_change: Option<Bounds<Pixels>>,
 1294    tasks: BTreeMap<(BufferId, BufferRow), RunnableTasks>,
 1295    tasks_update_task: Option<Task<()>>,
 1296    breakpoint_store: Option<Entity<BreakpointStore>>,
 1297    gutter_breakpoint_indicator: (Option<PhantomBreakpointIndicator>, Option<Task<()>>),
 1298    pub(crate) gutter_diff_review_indicator: (Option<PhantomDiffReviewIndicator>, Option<Task<()>>),
 1299    pub(crate) diff_review_drag_state: Option<DiffReviewDragState>,
 1300    /// Active diff review overlays. Multiple overlays can be open simultaneously
 1301    /// when hunks have comments stored.
 1302    pub(crate) diff_review_overlays: Vec<DiffReviewOverlay>,
 1303    /// Stored review comments grouped by hunk.
 1304    /// Uses a Vec instead of HashMap because DiffHunkKey contains an Anchor
 1305    /// which doesn't implement Hash/Eq in a way suitable for HashMap keys.
 1306    stored_review_comments: Vec<(DiffHunkKey, Vec<StoredReviewComment>)>,
 1307    /// Counter for generating unique comment IDs.
 1308    next_review_comment_id: usize,
 1309    hovered_diff_hunk_row: Option<DisplayRow>,
 1310    pull_diagnostics_task: Task<()>,
 1311    in_project_search: bool,
 1312    previous_search_ranges: Option<Arc<[Range<Anchor>]>>,
 1313    breadcrumb_header: Option<String>,
 1314    focused_block: Option<FocusedBlock>,
 1315    next_scroll_position: NextScrollCursorCenterTopBottom,
 1316    addons: HashMap<TypeId, Box<dyn Addon>>,
 1317    registered_buffers: HashMap<BufferId, OpenLspBufferHandle>,
 1318    load_diff_task: Option<Shared<Task<()>>>,
 1319    /// Whether we are temporarily displaying a diff other than git's
 1320    temporary_diff_override: bool,
 1321    selection_mark_mode: bool,
 1322    toggle_fold_multiple_buffers: Task<()>,
 1323    _scroll_cursor_center_top_bottom_task: Task<()>,
 1324    serialize_selections: Task<()>,
 1325    serialize_folds: Task<()>,
 1326    mouse_cursor_hidden: bool,
 1327    minimap: Option<Entity<Self>>,
 1328    hide_mouse_mode: HideMouseMode,
 1329    pub change_list: ChangeList,
 1330    inline_value_cache: InlineValueCache,
 1331    number_deleted_lines: bool,
 1332
 1333    selection_drag_state: SelectionDragState,
 1334    colors: Option<LspColorData>,
 1335    post_scroll_update: Task<()>,
 1336    refresh_colors_task: Task<()>,
 1337    use_document_folding_ranges: bool,
 1338    refresh_folding_ranges_task: Task<()>,
 1339    inlay_hints: Option<LspInlayHintData>,
 1340    folding_newlines: Task<()>,
 1341    select_next_is_case_sensitive: Option<bool>,
 1342    pub lookup_key: Option<Box<dyn Any + Send + Sync>>,
 1343    on_local_selections_changed:
 1344        Option<Box<dyn Fn(Point, &mut Window, &mut Context<Self>) + 'static>>,
 1345    suppress_selection_callback: bool,
 1346    applicable_language_settings: HashMap<Option<LanguageName>, LanguageSettings>,
 1347    accent_data: Option<AccentData>,
 1348    fetched_tree_sitter_chunks: HashMap<ExcerptId, HashSet<Range<BufferRow>>>,
 1349    semantic_token_state: SemanticTokenState,
 1350    pub(crate) refresh_matching_bracket_highlights_task: Task<()>,
 1351    refresh_document_symbols_task: Shared<Task<()>>,
 1352    lsp_document_symbols: HashMap<BufferId, Vec<OutlineItem<text::Anchor>>>,
 1353    refresh_outline_symbols_at_cursor_at_cursor_task: Task<()>,
 1354    outline_symbols_at_cursor: Option<(BufferId, Vec<OutlineItem<Anchor>>)>,
 1355    sticky_headers_task: Task<()>,
 1356    sticky_headers: Option<Vec<OutlineItem<Anchor>>>,
 1357}
 1358
 1359#[derive(Debug, PartialEq)]
 1360struct AccentData {
 1361    colors: AccentColors,
 1362    overrides: Vec<SharedString>,
 1363}
 1364
 1365fn debounce_value(debounce_ms: u64) -> Option<Duration> {
 1366    if debounce_ms > 0 {
 1367        Some(Duration::from_millis(debounce_ms))
 1368    } else {
 1369        None
 1370    }
 1371}
 1372
 1373#[derive(Copy, Clone, Debug, PartialEq, Eq, Default)]
 1374enum NextScrollCursorCenterTopBottom {
 1375    #[default]
 1376    Center,
 1377    Top,
 1378    Bottom,
 1379}
 1380
 1381impl NextScrollCursorCenterTopBottom {
 1382    fn next(&self) -> Self {
 1383        match self {
 1384            Self::Center => Self::Top,
 1385            Self::Top => Self::Bottom,
 1386            Self::Bottom => Self::Center,
 1387        }
 1388    }
 1389}
 1390
 1391#[derive(Clone)]
 1392pub struct EditorSnapshot {
 1393    pub mode: EditorMode,
 1394    show_gutter: bool,
 1395    offset_content: bool,
 1396    show_line_numbers: Option<bool>,
 1397    number_deleted_lines: bool,
 1398    show_git_diff_gutter: Option<bool>,
 1399    show_code_actions: Option<bool>,
 1400    show_runnables: Option<bool>,
 1401    show_breakpoints: Option<bool>,
 1402    git_blame_gutter_max_author_length: Option<usize>,
 1403    pub display_snapshot: DisplaySnapshot,
 1404    pub placeholder_display_snapshot: Option<DisplaySnapshot>,
 1405    is_focused: bool,
 1406    scroll_anchor: SharedScrollAnchor,
 1407    ongoing_scroll: OngoingScroll,
 1408    current_line_highlight: CurrentLineHighlight,
 1409    gutter_hovered: bool,
 1410    semantic_tokens_enabled: bool,
 1411}
 1412
 1413#[derive(Default, Debug, Clone, Copy)]
 1414pub struct GutterDimensions {
 1415    pub left_padding: Pixels,
 1416    pub right_padding: Pixels,
 1417    pub width: Pixels,
 1418    pub margin: Pixels,
 1419    pub git_blame_entries_width: Option<Pixels>,
 1420}
 1421
 1422impl GutterDimensions {
 1423    fn default_with_margin(font_id: FontId, font_size: Pixels, cx: &App) -> Self {
 1424        Self {
 1425            margin: Self::default_gutter_margin(font_id, font_size, cx),
 1426            ..Default::default()
 1427        }
 1428    }
 1429
 1430    fn default_gutter_margin(font_id: FontId, font_size: Pixels, cx: &App) -> Pixels {
 1431        -cx.text_system().descent(font_id, font_size)
 1432    }
 1433    /// The full width of the space taken up by the gutter.
 1434    pub fn full_width(&self) -> Pixels {
 1435        self.margin + self.width
 1436    }
 1437
 1438    /// The width of the space reserved for the fold indicators,
 1439    /// use alongside 'justify_end' and `gutter_width` to
 1440    /// right align content with the line numbers
 1441    pub fn fold_area_width(&self) -> Pixels {
 1442        self.margin + self.right_padding
 1443    }
 1444}
 1445
 1446struct CharacterDimensions {
 1447    em_width: Pixels,
 1448    em_advance: Pixels,
 1449    line_height: Pixels,
 1450}
 1451
 1452#[derive(Debug)]
 1453pub struct RemoteSelection {
 1454    pub replica_id: ReplicaId,
 1455    pub selection: Selection<Anchor>,
 1456    pub cursor_shape: CursorShape,
 1457    pub collaborator_id: CollaboratorId,
 1458    pub line_mode: bool,
 1459    pub user_name: Option<SharedString>,
 1460    pub color: PlayerColor,
 1461}
 1462
 1463#[derive(Clone, Debug)]
 1464struct SelectionHistoryEntry {
 1465    selections: Arc<[Selection<Anchor>]>,
 1466    select_next_state: Option<SelectNextState>,
 1467    select_prev_state: Option<SelectNextState>,
 1468    add_selections_state: Option<AddSelectionsState>,
 1469}
 1470
 1471#[derive(Copy, Clone, Default, Debug, PartialEq, Eq)]
 1472enum SelectionHistoryMode {
 1473    #[default]
 1474    Normal,
 1475    Undoing,
 1476    Redoing,
 1477    Skipping,
 1478}
 1479
 1480#[derive(Clone, PartialEq, Eq, Hash)]
 1481struct HoveredCursor {
 1482    replica_id: ReplicaId,
 1483    selection_id: usize,
 1484}
 1485
 1486#[derive(Debug)]
 1487/// SelectionEffects controls the side-effects of updating the selection.
 1488///
 1489/// The default behaviour does "what you mostly want":
 1490/// - it pushes to the nav history if the cursor moved by >10 lines
 1491/// - it re-triggers completion requests
 1492/// - it scrolls to fit
 1493///
 1494/// You might want to modify these behaviours. For example when doing a "jump"
 1495/// like go to definition, we always want to add to nav history; but when scrolling
 1496/// in vim mode we never do.
 1497///
 1498/// Similarly, you might want to disable scrolling if you don't want the viewport to
 1499/// move.
 1500#[derive(Clone)]
 1501pub struct SelectionEffects {
 1502    nav_history: Option<bool>,
 1503    completions: bool,
 1504    scroll: Option<Autoscroll>,
 1505}
 1506
 1507impl Default for SelectionEffects {
 1508    fn default() -> Self {
 1509        Self {
 1510            nav_history: None,
 1511            completions: true,
 1512            scroll: Some(Autoscroll::fit()),
 1513        }
 1514    }
 1515}
 1516impl SelectionEffects {
 1517    pub fn scroll(scroll: Autoscroll) -> Self {
 1518        Self {
 1519            scroll: Some(scroll),
 1520            ..Default::default()
 1521        }
 1522    }
 1523
 1524    pub fn no_scroll() -> Self {
 1525        Self {
 1526            scroll: None,
 1527            ..Default::default()
 1528        }
 1529    }
 1530
 1531    pub fn completions(self, completions: bool) -> Self {
 1532        Self {
 1533            completions,
 1534            ..self
 1535        }
 1536    }
 1537
 1538    pub fn nav_history(self, nav_history: bool) -> Self {
 1539        Self {
 1540            nav_history: Some(nav_history),
 1541            ..self
 1542        }
 1543    }
 1544}
 1545
 1546struct DeferredSelectionEffectsState {
 1547    changed: bool,
 1548    effects: SelectionEffects,
 1549    old_cursor_position: Anchor,
 1550    history_entry: SelectionHistoryEntry,
 1551}
 1552
 1553#[derive(Default)]
 1554struct SelectionHistory {
 1555    #[allow(clippy::type_complexity)]
 1556    selections_by_transaction:
 1557        HashMap<TransactionId, (Arc<[Selection<Anchor>]>, Option<Arc<[Selection<Anchor>]>>)>,
 1558    mode: SelectionHistoryMode,
 1559    undo_stack: VecDeque<SelectionHistoryEntry>,
 1560    redo_stack: VecDeque<SelectionHistoryEntry>,
 1561}
 1562
 1563impl SelectionHistory {
 1564    #[track_caller]
 1565    fn insert_transaction(
 1566        &mut self,
 1567        transaction_id: TransactionId,
 1568        selections: Arc<[Selection<Anchor>]>,
 1569    ) {
 1570        if selections.is_empty() {
 1571            log::error!(
 1572                "SelectionHistory::insert_transaction called with empty selections. Caller: {}",
 1573                std::panic::Location::caller()
 1574            );
 1575            return;
 1576        }
 1577        self.selections_by_transaction
 1578            .insert(transaction_id, (selections, None));
 1579    }
 1580
 1581    #[allow(clippy::type_complexity)]
 1582    fn transaction(
 1583        &self,
 1584        transaction_id: TransactionId,
 1585    ) -> Option<&(Arc<[Selection<Anchor>]>, Option<Arc<[Selection<Anchor>]>>)> {
 1586        self.selections_by_transaction.get(&transaction_id)
 1587    }
 1588
 1589    #[allow(clippy::type_complexity)]
 1590    fn transaction_mut(
 1591        &mut self,
 1592        transaction_id: TransactionId,
 1593    ) -> Option<&mut (Arc<[Selection<Anchor>]>, Option<Arc<[Selection<Anchor>]>>)> {
 1594        self.selections_by_transaction.get_mut(&transaction_id)
 1595    }
 1596
 1597    fn push(&mut self, entry: SelectionHistoryEntry) {
 1598        if !entry.selections.is_empty() {
 1599            match self.mode {
 1600                SelectionHistoryMode::Normal => {
 1601                    self.push_undo(entry);
 1602                    self.redo_stack.clear();
 1603                }
 1604                SelectionHistoryMode::Undoing => self.push_redo(entry),
 1605                SelectionHistoryMode::Redoing => self.push_undo(entry),
 1606                SelectionHistoryMode::Skipping => {}
 1607            }
 1608        }
 1609    }
 1610
 1611    fn push_undo(&mut self, entry: SelectionHistoryEntry) {
 1612        if self
 1613            .undo_stack
 1614            .back()
 1615            .is_none_or(|e| e.selections != entry.selections)
 1616        {
 1617            self.undo_stack.push_back(entry);
 1618            if self.undo_stack.len() > MAX_SELECTION_HISTORY_LEN {
 1619                self.undo_stack.pop_front();
 1620            }
 1621        }
 1622    }
 1623
 1624    fn push_redo(&mut self, entry: SelectionHistoryEntry) {
 1625        if self
 1626            .redo_stack
 1627            .back()
 1628            .is_none_or(|e| e.selections != entry.selections)
 1629        {
 1630            self.redo_stack.push_back(entry);
 1631            if self.redo_stack.len() > MAX_SELECTION_HISTORY_LEN {
 1632                self.redo_stack.pop_front();
 1633            }
 1634        }
 1635    }
 1636}
 1637
 1638#[derive(Clone, Copy)]
 1639pub struct RowHighlightOptions {
 1640    pub autoscroll: bool,
 1641    pub include_gutter: bool,
 1642}
 1643
 1644impl Default for RowHighlightOptions {
 1645    fn default() -> Self {
 1646        Self {
 1647            autoscroll: Default::default(),
 1648            include_gutter: true,
 1649        }
 1650    }
 1651}
 1652
 1653struct RowHighlight {
 1654    index: usize,
 1655    range: Range<Anchor>,
 1656    color: Hsla,
 1657    options: RowHighlightOptions,
 1658    type_id: TypeId,
 1659}
 1660
 1661#[derive(Clone, Debug)]
 1662struct AddSelectionsState {
 1663    groups: Vec<AddSelectionsGroup>,
 1664}
 1665
 1666#[derive(Clone, Debug)]
 1667struct AddSelectionsGroup {
 1668    above: bool,
 1669    stack: Vec<usize>,
 1670}
 1671
 1672#[derive(Clone)]
 1673struct SelectNextState {
 1674    query: AhoCorasick,
 1675    wordwise: bool,
 1676    done: bool,
 1677}
 1678
 1679impl std::fmt::Debug for SelectNextState {
 1680    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
 1681        f.debug_struct(std::any::type_name::<Self>())
 1682            .field("wordwise", &self.wordwise)
 1683            .field("done", &self.done)
 1684            .finish()
 1685    }
 1686}
 1687
 1688#[derive(Debug)]
 1689struct AutocloseRegion {
 1690    selection_id: usize,
 1691    range: Range<Anchor>,
 1692    pair: BracketPair,
 1693}
 1694
 1695#[derive(Debug)]
 1696struct SnippetState {
 1697    ranges: Vec<Vec<Range<Anchor>>>,
 1698    active_index: usize,
 1699    choices: Vec<Option<Vec<String>>>,
 1700}
 1701
 1702#[doc(hidden)]
 1703pub struct RenameState {
 1704    pub range: Range<Anchor>,
 1705    pub old_name: Arc<str>,
 1706    pub editor: Entity<Editor>,
 1707    block_id: CustomBlockId,
 1708}
 1709
 1710struct InvalidationStack<T>(Vec<T>);
 1711
 1712struct RegisteredEditPredictionDelegate {
 1713    provider: Arc<dyn EditPredictionDelegateHandle>,
 1714    _subscription: Subscription,
 1715}
 1716
 1717#[derive(Debug, PartialEq, Eq)]
 1718pub struct ActiveDiagnosticGroup {
 1719    pub active_range: Range<Anchor>,
 1720    pub active_message: String,
 1721    pub group_id: usize,
 1722    pub blocks: HashSet<CustomBlockId>,
 1723}
 1724
 1725#[derive(Debug, PartialEq, Eq)]
 1726
 1727pub(crate) enum ActiveDiagnostic {
 1728    None,
 1729    All,
 1730    Group(ActiveDiagnosticGroup),
 1731}
 1732
 1733#[derive(Serialize, Deserialize, Clone, Debug)]
 1734pub struct ClipboardSelection {
 1735    /// The number of bytes in this selection.
 1736    pub len: usize,
 1737    /// Whether this was a full-line selection.
 1738    pub is_entire_line: bool,
 1739    /// The indentation of the first line when this content was originally copied.
 1740    pub first_line_indent: u32,
 1741    #[serde(default)]
 1742    pub file_path: Option<PathBuf>,
 1743    #[serde(default)]
 1744    pub line_range: Option<RangeInclusive<u32>>,
 1745}
 1746
 1747impl ClipboardSelection {
 1748    pub fn for_buffer(
 1749        len: usize,
 1750        is_entire_line: bool,
 1751        range: Range<Point>,
 1752        buffer: &MultiBufferSnapshot,
 1753        project: Option<&Entity<Project>>,
 1754        cx: &App,
 1755    ) -> Self {
 1756        let first_line_indent = buffer
 1757            .indent_size_for_line(MultiBufferRow(range.start.row))
 1758            .len;
 1759
 1760        let file_path = util::maybe!({
 1761            let project = project?.read(cx);
 1762            let file = buffer.file_at(range.start)?;
 1763            let project_path = ProjectPath {
 1764                worktree_id: file.worktree_id(cx),
 1765                path: file.path().clone(),
 1766            };
 1767            project.absolute_path(&project_path, cx)
 1768        });
 1769
 1770        let line_range = file_path.as_ref().and_then(|_| {
 1771            let (_, start_point, start_excerpt_id) = buffer.point_to_buffer_point(range.start)?;
 1772            let (_, end_point, end_excerpt_id) = buffer.point_to_buffer_point(range.end)?;
 1773            if start_excerpt_id == end_excerpt_id {
 1774                Some(start_point.row..=end_point.row)
 1775            } else {
 1776                None
 1777            }
 1778        });
 1779
 1780        Self {
 1781            len,
 1782            is_entire_line,
 1783            first_line_indent,
 1784            file_path,
 1785            line_range,
 1786        }
 1787    }
 1788}
 1789
 1790// selections, scroll behavior, was newest selection reversed
 1791type SelectSyntaxNodeHistoryState = (
 1792    Box<[Selection<MultiBufferOffset>]>,
 1793    SelectSyntaxNodeScrollBehavior,
 1794    bool,
 1795);
 1796
 1797#[derive(Default)]
 1798struct SelectSyntaxNodeHistory {
 1799    stack: Vec<SelectSyntaxNodeHistoryState>,
 1800    // disable temporarily to allow changing selections without losing the stack
 1801    pub disable_clearing: bool,
 1802}
 1803
 1804impl SelectSyntaxNodeHistory {
 1805    pub fn try_clear(&mut self) {
 1806        if !self.disable_clearing {
 1807            self.stack.clear();
 1808        }
 1809    }
 1810
 1811    pub fn push(&mut self, selection: SelectSyntaxNodeHistoryState) {
 1812        self.stack.push(selection);
 1813    }
 1814
 1815    pub fn pop(&mut self) -> Option<SelectSyntaxNodeHistoryState> {
 1816        self.stack.pop()
 1817    }
 1818}
 1819
 1820enum SelectSyntaxNodeScrollBehavior {
 1821    CursorTop,
 1822    FitSelection,
 1823    CursorBottom,
 1824}
 1825
 1826#[derive(Debug, Clone, Copy)]
 1827pub(crate) struct NavigationData {
 1828    cursor_anchor: Anchor,
 1829    cursor_position: Point,
 1830    scroll_anchor: ScrollAnchor,
 1831    scroll_top_row: u32,
 1832}
 1833
 1834#[derive(Debug, Clone, Copy, PartialEq, Eq)]
 1835pub enum GotoDefinitionKind {
 1836    Symbol,
 1837    Declaration,
 1838    Type,
 1839    Implementation,
 1840}
 1841
 1842pub enum FormatTarget {
 1843    Buffers(HashSet<Entity<Buffer>>),
 1844    Ranges(Vec<Range<MultiBufferPoint>>),
 1845}
 1846
 1847pub(crate) struct FocusedBlock {
 1848    id: BlockId,
 1849    focus_handle: WeakFocusHandle,
 1850}
 1851
 1852#[derive(Clone, Debug)]
 1853pub enum JumpData {
 1854    MultiBufferRow {
 1855        row: MultiBufferRow,
 1856        line_offset_from_top: u32,
 1857    },
 1858    MultiBufferPoint {
 1859        excerpt_id: ExcerptId,
 1860        position: Point,
 1861        anchor: text::Anchor,
 1862        line_offset_from_top: u32,
 1863    },
 1864}
 1865
 1866pub enum MultibufferSelectionMode {
 1867    First,
 1868    All,
 1869}
 1870
 1871#[derive(Clone, Copy, Debug, Default)]
 1872pub struct RewrapOptions {
 1873    pub override_language_settings: bool,
 1874    pub preserve_existing_whitespace: bool,
 1875}
 1876
 1877impl Editor {
 1878    pub fn single_line(window: &mut Window, cx: &mut Context<Self>) -> Self {
 1879        let buffer = cx.new(|cx| Buffer::local("", cx));
 1880        let buffer = cx.new(|cx| MultiBuffer::singleton(buffer, cx));
 1881        Self::new(EditorMode::SingleLine, buffer, None, window, cx)
 1882    }
 1883
 1884    pub fn multi_line(window: &mut Window, cx: &mut Context<Self>) -> Self {
 1885        let buffer = cx.new(|cx| Buffer::local("", cx));
 1886        let buffer = cx.new(|cx| MultiBuffer::singleton(buffer, cx));
 1887        Self::new(EditorMode::full(), buffer, None, window, cx)
 1888    }
 1889
 1890    pub fn auto_height(
 1891        min_lines: usize,
 1892        max_lines: usize,
 1893        window: &mut Window,
 1894        cx: &mut Context<Self>,
 1895    ) -> Self {
 1896        let buffer = cx.new(|cx| Buffer::local("", cx));
 1897        let buffer = cx.new(|cx| MultiBuffer::singleton(buffer, cx));
 1898        Self::new(
 1899            EditorMode::AutoHeight {
 1900                min_lines,
 1901                max_lines: Some(max_lines),
 1902            },
 1903            buffer,
 1904            None,
 1905            window,
 1906            cx,
 1907        )
 1908    }
 1909
 1910    /// Creates a new auto-height editor with a minimum number of lines but no maximum.
 1911    /// The editor grows as tall as needed to fit its content.
 1912    pub fn auto_height_unbounded(
 1913        min_lines: usize,
 1914        window: &mut Window,
 1915        cx: &mut Context<Self>,
 1916    ) -> Self {
 1917        let buffer = cx.new(|cx| Buffer::local("", cx));
 1918        let buffer = cx.new(|cx| MultiBuffer::singleton(buffer, cx));
 1919        Self::new(
 1920            EditorMode::AutoHeight {
 1921                min_lines,
 1922                max_lines: None,
 1923            },
 1924            buffer,
 1925            None,
 1926            window,
 1927            cx,
 1928        )
 1929    }
 1930
 1931    pub fn for_buffer(
 1932        buffer: Entity<Buffer>,
 1933        project: Option<Entity<Project>>,
 1934        window: &mut Window,
 1935        cx: &mut Context<Self>,
 1936    ) -> Self {
 1937        let buffer = cx.new(|cx| MultiBuffer::singleton(buffer, cx));
 1938        Self::new(EditorMode::full(), buffer, project, window, cx)
 1939    }
 1940
 1941    pub fn for_multibuffer(
 1942        buffer: Entity<MultiBuffer>,
 1943        project: Option<Entity<Project>>,
 1944        window: &mut Window,
 1945        cx: &mut Context<Self>,
 1946    ) -> Self {
 1947        Self::new(EditorMode::full(), buffer, project, window, cx)
 1948    }
 1949
 1950    pub fn clone(&self, window: &mut Window, cx: &mut Context<Self>) -> Self {
 1951        let mut clone = Self::new(
 1952            self.mode.clone(),
 1953            self.buffer.clone(),
 1954            self.project.clone(),
 1955            window,
 1956            cx,
 1957        );
 1958        let my_snapshot = self.display_map.update(cx, |display_map, cx| {
 1959            let snapshot = display_map.snapshot(cx);
 1960            clone.display_map.update(cx, |display_map, cx| {
 1961                display_map.set_state(&snapshot, cx);
 1962            });
 1963            snapshot
 1964        });
 1965        let clone_snapshot = clone.display_map.update(cx, |map, cx| map.snapshot(cx));
 1966        clone.folds_did_change(cx);
 1967        clone.selections.clone_state(&self.selections);
 1968        clone
 1969            .scroll_manager
 1970            .clone_state(&self.scroll_manager, &my_snapshot, &clone_snapshot, cx);
 1971        clone.searchable = self.searchable;
 1972        clone.read_only = self.read_only;
 1973        clone
 1974    }
 1975
 1976    pub fn new(
 1977        mode: EditorMode,
 1978        buffer: Entity<MultiBuffer>,
 1979        project: Option<Entity<Project>>,
 1980        window: &mut Window,
 1981        cx: &mut Context<Self>,
 1982    ) -> Self {
 1983        Editor::new_internal(mode, buffer, project, None, window, cx)
 1984    }
 1985
 1986    pub fn refresh_sticky_headers(
 1987        &mut self,
 1988        display_snapshot: &DisplaySnapshot,
 1989        cx: &mut Context<Editor>,
 1990    ) {
 1991        if !self.mode.is_full() {
 1992            return;
 1993        }
 1994        let multi_buffer = display_snapshot.buffer_snapshot();
 1995        let scroll_anchor = self
 1996            .scroll_manager
 1997            .native_anchor(display_snapshot, cx)
 1998            .anchor;
 1999        let Some((excerpt_id, _, buffer)) = multi_buffer.as_singleton() else {
 2000            return;
 2001        };
 2002        let buffer = buffer.clone();
 2003
 2004        let buffer_visible_start = scroll_anchor.text_anchor.to_point(&buffer);
 2005        let max_row = buffer.max_point().row;
 2006        let start_row = buffer_visible_start.row.min(max_row);
 2007        let end_row = (buffer_visible_start.row + 10).min(max_row);
 2008
 2009        let syntax = self.style(cx).syntax.clone();
 2010        let background_task = cx.background_spawn(async move {
 2011            buffer
 2012                .outline_items_containing(
 2013                    Point::new(start_row, 0)..Point::new(end_row, 0),
 2014                    true,
 2015                    Some(syntax.as_ref()),
 2016                )
 2017                .into_iter()
 2018                .map(|outline_item| OutlineItem {
 2019                    depth: outline_item.depth,
 2020                    range: Anchor::range_in_buffer(excerpt_id, outline_item.range),
 2021                    source_range_for_text: Anchor::range_in_buffer(
 2022                        excerpt_id,
 2023                        outline_item.source_range_for_text,
 2024                    ),
 2025                    text: outline_item.text,
 2026                    highlight_ranges: outline_item.highlight_ranges,
 2027                    name_ranges: outline_item.name_ranges,
 2028                    body_range: outline_item
 2029                        .body_range
 2030                        .map(|range| Anchor::range_in_buffer(excerpt_id, range)),
 2031                    annotation_range: outline_item
 2032                        .annotation_range
 2033                        .map(|range| Anchor::range_in_buffer(excerpt_id, range)),
 2034                })
 2035                .collect()
 2036        });
 2037        self.sticky_headers_task = cx.spawn(async move |this, cx| {
 2038            let sticky_headers = background_task.await;
 2039            this.update(cx, |this, cx| {
 2040                this.sticky_headers = Some(sticky_headers);
 2041                cx.notify();
 2042            })
 2043            .ok();
 2044        });
 2045    }
 2046
 2047    fn new_internal(
 2048        mode: EditorMode,
 2049        multi_buffer: Entity<MultiBuffer>,
 2050        project: Option<Entity<Project>>,
 2051        display_map: Option<Entity<DisplayMap>>,
 2052        window: &mut Window,
 2053        cx: &mut Context<Self>,
 2054    ) -> Self {
 2055        debug_assert!(
 2056            display_map.is_none() || mode.is_minimap(),
 2057            "Providing a display map for a new editor is only intended for the minimap and might have unintended side effects otherwise!"
 2058        );
 2059
 2060        let full_mode = mode.is_full();
 2061        let is_minimap = mode.is_minimap();
 2062        let diagnostics_max_severity = if full_mode {
 2063            EditorSettings::get_global(cx)
 2064                .diagnostics_max_severity
 2065                .unwrap_or(DiagnosticSeverity::Hint)
 2066        } else {
 2067            DiagnosticSeverity::Off
 2068        };
 2069        let style = window.text_style();
 2070        let font_size = style.font_size.to_pixels(window.rem_size());
 2071        let editor = cx.entity().downgrade();
 2072        let fold_placeholder = FoldPlaceholder {
 2073            constrain_width: false,
 2074            render: Arc::new(move |fold_id, fold_range, cx| {
 2075                let editor = editor.clone();
 2076                FoldPlaceholder::fold_element(fold_id, cx)
 2077                    .cursor_pointer()
 2078                    .child("")
 2079                    .on_mouse_down(MouseButton::Left, |_, _, cx| cx.stop_propagation())
 2080                    .on_click(move |_, _window, cx| {
 2081                        editor
 2082                            .update(cx, |editor, cx| {
 2083                                editor.unfold_ranges(
 2084                                    &[fold_range.start..fold_range.end],
 2085                                    true,
 2086                                    false,
 2087                                    cx,
 2088                                );
 2089                                cx.stop_propagation();
 2090                            })
 2091                            .ok();
 2092                    })
 2093                    .into_any()
 2094            }),
 2095            merge_adjacent: true,
 2096            ..FoldPlaceholder::default()
 2097        };
 2098        let display_map = display_map.unwrap_or_else(|| {
 2099            cx.new(|cx| {
 2100                DisplayMap::new(
 2101                    multi_buffer.clone(),
 2102                    style.font(),
 2103                    font_size,
 2104                    None,
 2105                    FILE_HEADER_HEIGHT,
 2106                    MULTI_BUFFER_EXCERPT_HEADER_HEIGHT,
 2107                    fold_placeholder,
 2108                    diagnostics_max_severity,
 2109                    cx,
 2110                )
 2111            })
 2112        });
 2113
 2114        let selections = SelectionsCollection::new();
 2115
 2116        let blink_manager = cx.new(|cx| {
 2117            let mut blink_manager = BlinkManager::new(
 2118                CURSOR_BLINK_INTERVAL,
 2119                |cx| EditorSettings::get_global(cx).cursor_blink,
 2120                cx,
 2121            );
 2122            if is_minimap {
 2123                blink_manager.disable(cx);
 2124            }
 2125            blink_manager
 2126        });
 2127
 2128        let soft_wrap_mode_override =
 2129            matches!(mode, EditorMode::SingleLine).then(|| language_settings::SoftWrap::None);
 2130
 2131        let mut project_subscriptions = Vec::new();
 2132        if full_mode && let Some(project) = project.as_ref() {
 2133            project_subscriptions.push(cx.subscribe_in(
 2134                project,
 2135                window,
 2136                |editor, _, event, window, cx| match event {
 2137                    project::Event::RefreshCodeLens => {
 2138                        // we always query lens with actions, without storing them, always refreshing them
 2139                    }
 2140                    project::Event::RefreshInlayHints {
 2141                        server_id,
 2142                        request_id,
 2143                    } => {
 2144                        editor.refresh_inlay_hints(
 2145                            InlayHintRefreshReason::RefreshRequested {
 2146                                server_id: *server_id,
 2147                                request_id: *request_id,
 2148                            },
 2149                            cx,
 2150                        );
 2151                    }
 2152                    project::Event::RefreshSemanticTokens {
 2153                        server_id,
 2154                        request_id,
 2155                    } => {
 2156                        editor.refresh_semantic_tokens(
 2157                            None,
 2158                            Some(RefreshForServer {
 2159                                server_id: *server_id,
 2160                                request_id: *request_id,
 2161                            }),
 2162                            cx,
 2163                        );
 2164                    }
 2165                    project::Event::LanguageServerRemoved(_) => {
 2166                        editor.registered_buffers.clear();
 2167                        editor.register_visible_buffers(cx);
 2168                        editor.invalidate_semantic_tokens(None);
 2169                        editor.update_lsp_data(None, window, cx);
 2170                        editor.refresh_inlay_hints(InlayHintRefreshReason::ServerRemoved, cx);
 2171                        if editor.tasks_update_task.is_none() {
 2172                            editor.tasks_update_task = Some(editor.refresh_runnables(window, cx));
 2173                        }
 2174                    }
 2175                    project::Event::LanguageServerAdded(..) => {
 2176                        if editor.tasks_update_task.is_none() {
 2177                            editor.tasks_update_task = Some(editor.refresh_runnables(window, cx));
 2178                        }
 2179                    }
 2180                    project::Event::SnippetEdit(id, snippet_edits) => {
 2181                        // todo(lw): Non singletons
 2182                        if let Some(buffer) = editor.buffer.read(cx).as_singleton() {
 2183                            let snapshot = buffer.read(cx).snapshot();
 2184                            let focus_handle = editor.focus_handle(cx);
 2185                            if snapshot.remote_id() == *id && focus_handle.is_focused(window) {
 2186                                for (range, snippet) in snippet_edits {
 2187                                    let buffer_range =
 2188                                        language::range_from_lsp(*range).to_offset(&snapshot);
 2189                                    editor
 2190                                        .insert_snippet(
 2191                                            &[MultiBufferOffset(buffer_range.start)
 2192                                                ..MultiBufferOffset(buffer_range.end)],
 2193                                            snippet.clone(),
 2194                                            window,
 2195                                            cx,
 2196                                        )
 2197                                        .ok();
 2198                                }
 2199                            }
 2200                        }
 2201                    }
 2202                    project::Event::LanguageServerBufferRegistered { buffer_id, .. } => {
 2203                        let buffer_id = *buffer_id;
 2204                        if editor.buffer().read(cx).buffer(buffer_id).is_some() {
 2205                            editor.register_buffer(buffer_id, cx);
 2206                            editor.update_lsp_data(Some(buffer_id), window, cx);
 2207                            editor.refresh_inlay_hints(InlayHintRefreshReason::NewLinesShown, cx);
 2208                            refresh_linked_ranges(editor, window, cx);
 2209                            editor.refresh_code_actions(window, cx);
 2210                            editor.refresh_document_highlights(cx);
 2211                        }
 2212                    }
 2213
 2214                    project::Event::EntryRenamed(transaction, project_path, abs_path) => {
 2215                        let Some(workspace) = editor.workspace() else {
 2216                            return;
 2217                        };
 2218                        let Some(active_editor) = workspace.read(cx).active_item_as::<Self>(cx)
 2219                        else {
 2220                            return;
 2221                        };
 2222
 2223                        if active_editor.entity_id() == cx.entity_id() {
 2224                            let entity_id = cx.entity_id();
 2225                            workspace.update(cx, |this, cx| {
 2226                                this.panes_mut()
 2227                                    .iter_mut()
 2228                                    .filter(|pane| pane.entity_id() != entity_id)
 2229                                    .for_each(|p| {
 2230                                        p.update(cx, |pane, _| {
 2231                                            pane.nav_history_mut().rename_item(
 2232                                                entity_id,
 2233                                                project_path.clone(),
 2234                                                abs_path.clone().into(),
 2235                                            );
 2236                                        })
 2237                                    });
 2238                            });
 2239
 2240                            Self::open_transaction_for_hidden_buffers(
 2241                                workspace,
 2242                                transaction.clone(),
 2243                                "Rename".to_string(),
 2244                                window,
 2245                                cx,
 2246                            );
 2247                        }
 2248                    }
 2249
 2250                    project::Event::WorkspaceEditApplied(transaction) => {
 2251                        let Some(workspace) = editor.workspace() else {
 2252                            return;
 2253                        };
 2254                        let Some(active_editor) = workspace.read(cx).active_item_as::<Self>(cx)
 2255                        else {
 2256                            return;
 2257                        };
 2258
 2259                        if active_editor.entity_id() == cx.entity_id() {
 2260                            Self::open_transaction_for_hidden_buffers(
 2261                                workspace,
 2262                                transaction.clone(),
 2263                                "LSP Edit".to_string(),
 2264                                window,
 2265                                cx,
 2266                            );
 2267                        }
 2268                    }
 2269
 2270                    _ => {}
 2271                },
 2272            ));
 2273            if let Some(task_inventory) = project
 2274                .read(cx)
 2275                .task_store()
 2276                .read(cx)
 2277                .task_inventory()
 2278                .cloned()
 2279            {
 2280                project_subscriptions.push(cx.observe_in(
 2281                    &task_inventory,
 2282                    window,
 2283                    |editor, _, window, cx| {
 2284                        editor.tasks_update_task = Some(editor.refresh_runnables(window, cx));
 2285                    },
 2286                ));
 2287            };
 2288
 2289            project_subscriptions.push(cx.subscribe_in(
 2290                &project.read(cx).breakpoint_store(),
 2291                window,
 2292                |editor, _, event, window, cx| match event {
 2293                    BreakpointStoreEvent::ClearDebugLines => {
 2294                        editor.clear_row_highlights::<ActiveDebugLine>();
 2295                        editor.refresh_inline_values(cx);
 2296                    }
 2297                    BreakpointStoreEvent::SetDebugLine => {
 2298                        if editor.go_to_active_debug_line(window, cx) {
 2299                            cx.stop_propagation();
 2300                        }
 2301
 2302                        editor.refresh_inline_values(cx);
 2303                    }
 2304                    _ => {}
 2305                },
 2306            ));
 2307            let git_store = project.read(cx).git_store().clone();
 2308            let project = project.clone();
 2309            project_subscriptions.push(cx.subscribe(&git_store, move |this, _, event, cx| {
 2310                if let GitStoreEvent::RepositoryAdded = event {
 2311                    this.load_diff_task = Some(
 2312                        update_uncommitted_diff_for_buffer(
 2313                            cx.entity(),
 2314                            &project,
 2315                            this.buffer.read(cx).all_buffers(),
 2316                            this.buffer.clone(),
 2317                            cx,
 2318                        )
 2319                        .shared(),
 2320                    );
 2321                }
 2322            }));
 2323        }
 2324
 2325        let buffer_snapshot = multi_buffer.read(cx).snapshot(cx);
 2326
 2327        let inlay_hint_settings =
 2328            inlay_hint_settings(selections.newest_anchor().head(), &buffer_snapshot, cx);
 2329        let focus_handle = cx.focus_handle();
 2330        if !is_minimap {
 2331            cx.on_focus(&focus_handle, window, Self::handle_focus)
 2332                .detach();
 2333            cx.on_focus_in(&focus_handle, window, Self::handle_focus_in)
 2334                .detach();
 2335            cx.on_focus_out(&focus_handle, window, Self::handle_focus_out)
 2336                .detach();
 2337            cx.on_blur(&focus_handle, window, Self::handle_blur)
 2338                .detach();
 2339            cx.observe_pending_input(window, Self::observe_pending_input)
 2340                .detach();
 2341        }
 2342
 2343        let show_indent_guides =
 2344            if matches!(mode, EditorMode::SingleLine | EditorMode::Minimap { .. }) {
 2345                Some(false)
 2346            } else {
 2347                None
 2348            };
 2349
 2350        let breakpoint_store = match (&mode, project.as_ref()) {
 2351            (EditorMode::Full { .. }, Some(project)) => Some(project.read(cx).breakpoint_store()),
 2352            _ => None,
 2353        };
 2354
 2355        let mut code_action_providers = Vec::new();
 2356        let mut load_uncommitted_diff = None;
 2357        if let Some(project) = project.clone() {
 2358            load_uncommitted_diff = Some(
 2359                update_uncommitted_diff_for_buffer(
 2360                    cx.entity(),
 2361                    &project,
 2362                    multi_buffer.read(cx).all_buffers(),
 2363                    multi_buffer.clone(),
 2364                    cx,
 2365                )
 2366                .shared(),
 2367            );
 2368            code_action_providers.push(Rc::new(project) as Rc<_>);
 2369        }
 2370
 2371        let mut editor = Self {
 2372            focus_handle,
 2373            show_cursor_when_unfocused: false,
 2374            last_focused_descendant: None,
 2375            buffer: multi_buffer.clone(),
 2376            display_map: display_map.clone(),
 2377            placeholder_display_map: None,
 2378            selections,
 2379            scroll_manager: ScrollManager::new(cx),
 2380            columnar_selection_state: None,
 2381            add_selections_state: None,
 2382            select_next_state: None,
 2383            select_prev_state: None,
 2384            selection_history: SelectionHistory::default(),
 2385            defer_selection_effects: false,
 2386            deferred_selection_effects_state: None,
 2387            autoclose_regions: Vec::new(),
 2388            snippet_stack: InvalidationStack::default(),
 2389            select_syntax_node_history: SelectSyntaxNodeHistory::default(),
 2390            ime_transaction: None,
 2391            active_diagnostics: ActiveDiagnostic::None,
 2392            show_inline_diagnostics: ProjectSettings::get_global(cx).diagnostics.inline.enabled,
 2393            inline_diagnostics_update: Task::ready(()),
 2394            inline_diagnostics: Vec::new(),
 2395            soft_wrap_mode_override,
 2396            diagnostics_max_severity,
 2397            hard_wrap: None,
 2398            completion_provider: project.clone().map(|project| Rc::new(project) as _),
 2399            semantics_provider: project.clone().map(|project| Rc::new(project) as _),
 2400            collaboration_hub: project.clone().map(|project| Box::new(project) as _),
 2401            project,
 2402            blink_manager: blink_manager.clone(),
 2403            show_local_selections: true,
 2404            show_scrollbars: ScrollbarAxes {
 2405                horizontal: full_mode,
 2406                vertical: full_mode,
 2407            },
 2408            minimap_visibility: MinimapVisibility::for_mode(&mode, cx),
 2409            offset_content: !matches!(mode, EditorMode::SingleLine),
 2410            show_breadcrumbs: EditorSettings::get_global(cx).toolbar.breadcrumbs,
 2411            show_gutter: full_mode,
 2412            show_line_numbers: (!full_mode).then_some(false),
 2413            use_relative_line_numbers: None,
 2414            disable_expand_excerpt_buttons: !full_mode,
 2415            delegate_expand_excerpts: false,
 2416            delegate_stage_and_restore: false,
 2417            delegate_open_excerpts: false,
 2418            enable_lsp_data: true,
 2419            enable_runnables: true,
 2420            show_git_diff_gutter: None,
 2421            show_code_actions: None,
 2422            show_runnables: None,
 2423            show_breakpoints: None,
 2424            show_diff_review_button: false,
 2425            show_wrap_guides: None,
 2426            show_indent_guides,
 2427            buffers_with_disabled_indent_guides: HashSet::default(),
 2428            highlight_order: 0,
 2429            highlighted_rows: HashMap::default(),
 2430            background_highlights: HashMap::default(),
 2431            gutter_highlights: HashMap::default(),
 2432            scrollbar_marker_state: ScrollbarMarkerState::default(),
 2433            active_indent_guides_state: ActiveIndentGuidesState::default(),
 2434            nav_history: None,
 2435            context_menu: RefCell::new(None),
 2436            context_menu_options: None,
 2437            mouse_context_menu: None,
 2438            completion_tasks: Vec::new(),
 2439            inline_blame_popover: None,
 2440            inline_blame_popover_show_task: None,
 2441            signature_help_state: SignatureHelpState::default(),
 2442            auto_signature_help: None,
 2443            find_all_references_task_sources: Vec::new(),
 2444            next_completion_id: 0,
 2445            next_inlay_id: 0,
 2446            code_action_providers,
 2447            available_code_actions: None,
 2448            code_actions_task: None,
 2449            quick_selection_highlight_task: None,
 2450            debounced_selection_highlight_task: None,
 2451            debounced_selection_highlight_complete: false,
 2452            document_highlights_task: None,
 2453            linked_editing_range_task: None,
 2454            pending_rename: None,
 2455            searchable: !is_minimap,
 2456            cursor_shape: EditorSettings::get_global(cx)
 2457                .cursor_shape
 2458                .unwrap_or_default(),
 2459            cursor_offset_on_selection: false,
 2460            current_line_highlight: None,
 2461            autoindent_mode: Some(AutoindentMode::EachLine),
 2462            collapse_matches: false,
 2463            workspace: None,
 2464            input_enabled: !is_minimap,
 2465            use_modal_editing: full_mode,
 2466            read_only: is_minimap,
 2467            use_autoclose: true,
 2468            use_auto_surround: true,
 2469            auto_replace_emoji_shortcode: false,
 2470            jsx_tag_auto_close_enabled_in_any_buffer: false,
 2471            leader_id: None,
 2472            remote_id: None,
 2473            hover_state: HoverState::default(),
 2474            pending_mouse_down: None,
 2475            prev_pressure_stage: None,
 2476            hovered_link_state: None,
 2477            edit_prediction_provider: None,
 2478            active_edit_prediction: None,
 2479            stale_edit_prediction_in_menu: None,
 2480            edit_prediction_preview: EditPredictionPreview::Inactive {
 2481                released_too_fast: false,
 2482            },
 2483            inline_diagnostics_enabled: full_mode,
 2484            diagnostics_enabled: full_mode,
 2485            word_completions_enabled: full_mode,
 2486            inline_value_cache: InlineValueCache::new(inlay_hint_settings.show_value_hints),
 2487            gutter_hovered: false,
 2488            pixel_position_of_newest_cursor: None,
 2489            last_bounds: None,
 2490            last_position_map: None,
 2491            expect_bounds_change: None,
 2492            gutter_dimensions: GutterDimensions::default(),
 2493            style: None,
 2494            show_cursor_names: false,
 2495            hovered_cursors: HashMap::default(),
 2496            next_editor_action_id: EditorActionId::default(),
 2497            editor_actions: Rc::default(),
 2498            edit_predictions_hidden_for_vim_mode: false,
 2499            show_edit_predictions_override: None,
 2500            show_completions_on_input_override: None,
 2501            menu_edit_predictions_policy: MenuEditPredictionsPolicy::ByProvider,
 2502            edit_prediction_settings: EditPredictionSettings::Disabled,
 2503            edit_prediction_indent_conflict: false,
 2504            edit_prediction_requires_modifier_in_indent_conflict: true,
 2505            custom_context_menu: None,
 2506            show_git_blame_gutter: false,
 2507            show_git_blame_inline: false,
 2508            show_selection_menu: None,
 2509            show_git_blame_inline_delay_task: None,
 2510            git_blame_inline_enabled: full_mode
 2511                && ProjectSettings::get_global(cx).git.inline_blame.enabled,
 2512            render_diff_hunk_controls: Arc::new(render_diff_hunk_controls),
 2513            buffer_serialization: is_minimap.not().then(|| {
 2514                BufferSerialization::new(
 2515                    ProjectSettings::get_global(cx)
 2516                        .session
 2517                        .restore_unsaved_buffers,
 2518                )
 2519            }),
 2520            blame: None,
 2521            blame_subscription: None,
 2522            tasks: BTreeMap::default(),
 2523
 2524            breakpoint_store,
 2525            gutter_breakpoint_indicator: (None, None),
 2526            gutter_diff_review_indicator: (None, None),
 2527            diff_review_drag_state: None,
 2528            diff_review_overlays: Vec::new(),
 2529            stored_review_comments: Vec::new(),
 2530            next_review_comment_id: 0,
 2531            hovered_diff_hunk_row: None,
 2532            _subscriptions: (!is_minimap)
 2533                .then(|| {
 2534                    vec![
 2535                        cx.observe(&multi_buffer, Self::on_buffer_changed),
 2536                        cx.subscribe_in(&multi_buffer, window, Self::on_buffer_event),
 2537                        cx.observe_in(&display_map, window, Self::on_display_map_changed),
 2538                        cx.observe(&blink_manager, |_, _, cx| cx.notify()),
 2539                        cx.observe_global_in::<SettingsStore>(window, Self::settings_changed),
 2540                        cx.observe_global_in::<GlobalTheme>(window, Self::theme_changed),
 2541                        observe_buffer_font_size_adjustment(cx, |_, cx| cx.notify()),
 2542                        cx.observe_window_activation(window, |editor, window, cx| {
 2543                            let active = window.is_window_active();
 2544                            editor.blink_manager.update(cx, |blink_manager, cx| {
 2545                                if active {
 2546                                    blink_manager.enable(cx);
 2547                                } else {
 2548                                    blink_manager.disable(cx);
 2549                                }
 2550                            });
 2551                            if active {
 2552                                editor.show_mouse_cursor(cx);
 2553                            }
 2554                        }),
 2555                    ]
 2556                })
 2557                .unwrap_or_default(),
 2558            tasks_update_task: None,
 2559            pull_diagnostics_task: Task::ready(()),
 2560            colors: None,
 2561            refresh_colors_task: Task::ready(()),
 2562            use_document_folding_ranges: false,
 2563            refresh_folding_ranges_task: Task::ready(()),
 2564            inlay_hints: None,
 2565            next_color_inlay_id: 0,
 2566            post_scroll_update: Task::ready(()),
 2567            linked_edit_ranges: Default::default(),
 2568            in_project_search: false,
 2569            previous_search_ranges: None,
 2570            breadcrumb_header: None,
 2571            focused_block: None,
 2572            next_scroll_position: NextScrollCursorCenterTopBottom::default(),
 2573            addons: HashMap::default(),
 2574            registered_buffers: HashMap::default(),
 2575            _scroll_cursor_center_top_bottom_task: Task::ready(()),
 2576            selection_mark_mode: false,
 2577            toggle_fold_multiple_buffers: Task::ready(()),
 2578            serialize_selections: Task::ready(()),
 2579            serialize_folds: Task::ready(()),
 2580            text_style_refinement: None,
 2581            load_diff_task: load_uncommitted_diff,
 2582            temporary_diff_override: false,
 2583            mouse_cursor_hidden: false,
 2584            minimap: None,
 2585            hide_mouse_mode: EditorSettings::get_global(cx)
 2586                .hide_mouse
 2587                .unwrap_or_default(),
 2588            change_list: ChangeList::new(),
 2589            mode,
 2590            selection_drag_state: SelectionDragState::None,
 2591            folding_newlines: Task::ready(()),
 2592            lookup_key: None,
 2593            select_next_is_case_sensitive: None,
 2594            on_local_selections_changed: None,
 2595            suppress_selection_callback: false,
 2596            applicable_language_settings: HashMap::default(),
 2597            semantic_token_state: SemanticTokenState::new(cx, full_mode),
 2598            accent_data: None,
 2599            fetched_tree_sitter_chunks: HashMap::default(),
 2600            number_deleted_lines: false,
 2601            refresh_matching_bracket_highlights_task: Task::ready(()),
 2602            refresh_document_symbols_task: Task::ready(()).shared(),
 2603            lsp_document_symbols: HashMap::default(),
 2604            refresh_outline_symbols_at_cursor_at_cursor_task: Task::ready(()),
 2605            outline_symbols_at_cursor: None,
 2606            sticky_headers_task: Task::ready(()),
 2607            sticky_headers: None,
 2608        };
 2609
 2610        if is_minimap {
 2611            return editor;
 2612        }
 2613
 2614        editor.applicable_language_settings = editor.fetch_applicable_language_settings(cx);
 2615        editor.accent_data = editor.fetch_accent_data(cx);
 2616
 2617        if let Some(breakpoints) = editor.breakpoint_store.as_ref() {
 2618            editor
 2619                ._subscriptions
 2620                .push(cx.observe(breakpoints, |_, _, cx| {
 2621                    cx.notify();
 2622                }));
 2623        }
 2624        editor.tasks_update_task = Some(editor.refresh_runnables(window, cx));
 2625        editor._subscriptions.extend(project_subscriptions);
 2626
 2627        editor._subscriptions.push(cx.subscribe_in(
 2628            &cx.entity(),
 2629            window,
 2630            |editor, _, e: &EditorEvent, window, cx| match e {
 2631                EditorEvent::ScrollPositionChanged { local, .. } => {
 2632                    if *local {
 2633                        editor.hide_signature_help(cx, SignatureHelpHiddenBy::Escape);
 2634                        editor.inline_blame_popover.take();
 2635                        let snapshot = editor.snapshot(window, cx);
 2636                        let new_anchor = editor
 2637                            .scroll_manager
 2638                            .native_anchor(&snapshot.display_snapshot, cx);
 2639                        editor.update_restoration_data(cx, move |data| {
 2640                            data.scroll_position = (
 2641                                new_anchor.top_row(snapshot.buffer_snapshot()),
 2642                                new_anchor.offset,
 2643                            );
 2644                        });
 2645
 2646                        editor.post_scroll_update = cx.spawn_in(window, async move |editor, cx| {
 2647                            cx.background_executor()
 2648                                .timer(Duration::from_millis(50))
 2649                                .await;
 2650                            editor
 2651                                .update_in(cx, |editor, window, cx| {
 2652                                    editor.register_visible_buffers(cx);
 2653                                    editor.colorize_brackets(false, cx);
 2654                                    editor.refresh_inlay_hints(
 2655                                        InlayHintRefreshReason::NewLinesShown,
 2656                                        cx,
 2657                                    );
 2658                                    if !editor.buffer().read(cx).is_singleton() {
 2659                                        editor.update_lsp_data(None, window, cx);
 2660                                    }
 2661                                })
 2662                                .ok();
 2663                        });
 2664                    }
 2665                    editor.refresh_sticky_headers(&editor.snapshot(window, cx), cx);
 2666                }
 2667                EditorEvent::Edited { .. } => {
 2668                    let vim_mode = vim_mode_setting::VimModeSetting::try_get(cx)
 2669                        .map(|vim_mode| vim_mode.0)
 2670                        .unwrap_or(false);
 2671                    if !vim_mode {
 2672                        let display_map = editor.display_snapshot(cx);
 2673                        let selections = editor.selections.all_adjusted_display(&display_map);
 2674                        let pop_state = editor
 2675                            .change_list
 2676                            .last()
 2677                            .map(|previous| {
 2678                                previous.len() == selections.len()
 2679                                    && previous.iter().enumerate().all(|(ix, p)| {
 2680                                        p.to_display_point(&display_map).row()
 2681                                            == selections[ix].head().row()
 2682                                    })
 2683                            })
 2684                            .unwrap_or(false);
 2685                        let new_positions = selections
 2686                            .into_iter()
 2687                            .map(|s| display_map.display_point_to_anchor(s.head(), Bias::Left))
 2688                            .collect();
 2689                        editor
 2690                            .change_list
 2691                            .push_to_change_list(pop_state, new_positions);
 2692                    }
 2693                }
 2694                _ => (),
 2695            },
 2696        ));
 2697
 2698        if let Some(dap_store) = editor
 2699            .project
 2700            .as_ref()
 2701            .map(|project| project.read(cx).dap_store())
 2702        {
 2703            let weak_editor = cx.weak_entity();
 2704
 2705            editor
 2706                ._subscriptions
 2707                .push(
 2708                    cx.observe_new::<project::debugger::session::Session>(move |_, _, cx| {
 2709                        let session_entity = cx.entity();
 2710                        weak_editor
 2711                            .update(cx, |editor, cx| {
 2712                                editor._subscriptions.push(
 2713                                    cx.subscribe(&session_entity, Self::on_debug_session_event),
 2714                                );
 2715                            })
 2716                            .ok();
 2717                    }),
 2718                );
 2719
 2720            for session in dap_store.read(cx).sessions().cloned().collect::<Vec<_>>() {
 2721                editor
 2722                    ._subscriptions
 2723                    .push(cx.subscribe(&session, Self::on_debug_session_event));
 2724            }
 2725        }
 2726
 2727        // skip adding the initial selection to selection history
 2728        editor.selection_history.mode = SelectionHistoryMode::Skipping;
 2729        editor.end_selection(window, cx);
 2730        editor.selection_history.mode = SelectionHistoryMode::Normal;
 2731
 2732        editor.scroll_manager.show_scrollbars(window, cx);
 2733        jsx_tag_auto_close::refresh_enabled_in_any_buffer(&mut editor, &multi_buffer, cx);
 2734
 2735        if full_mode {
 2736            let should_auto_hide_scrollbars = cx.should_auto_hide_scrollbars();
 2737            cx.set_global(ScrollbarAutoHide(should_auto_hide_scrollbars));
 2738
 2739            if editor.git_blame_inline_enabled {
 2740                editor.start_git_blame_inline(false, window, cx);
 2741            }
 2742
 2743            editor.go_to_active_debug_line(window, cx);
 2744
 2745            editor.minimap =
 2746                editor.create_minimap(EditorSettings::get_global(cx).minimap, window, cx);
 2747            editor.colors = Some(LspColorData::new(cx));
 2748            editor.use_document_folding_ranges = true;
 2749            editor.inlay_hints = Some(LspInlayHintData::new(inlay_hint_settings));
 2750
 2751            if let Some(buffer) = multi_buffer.read(cx).as_singleton() {
 2752                editor.register_buffer(buffer.read(cx).remote_id(), cx);
 2753            }
 2754            editor.report_editor_event(ReportEditorEvent::EditorOpened, None, cx);
 2755        }
 2756
 2757        editor
 2758    }
 2759
 2760    pub fn display_snapshot(&self, cx: &mut App) -> DisplaySnapshot {
 2761        self.display_map.update(cx, |map, cx| map.snapshot(cx))
 2762    }
 2763
 2764    pub fn deploy_mouse_context_menu(
 2765        &mut self,
 2766        position: gpui::Point<Pixels>,
 2767        context_menu: Entity<ContextMenu>,
 2768        window: &mut Window,
 2769        cx: &mut Context<Self>,
 2770    ) {
 2771        self.mouse_context_menu = Some(MouseContextMenu::new(
 2772            self,
 2773            crate::mouse_context_menu::MenuPosition::PinnedToScreen(position),
 2774            context_menu,
 2775            window,
 2776            cx,
 2777        ));
 2778    }
 2779
 2780    pub fn mouse_menu_is_focused(&self, window: &Window, cx: &App) -> bool {
 2781        self.mouse_context_menu
 2782            .as_ref()
 2783            .is_some_and(|menu| menu.context_menu.focus_handle(cx).is_focused(window))
 2784    }
 2785
 2786    pub fn is_range_selected(&mut self, range: &Range<Anchor>, cx: &mut Context<Self>) -> bool {
 2787        if self
 2788            .selections
 2789            .pending_anchor()
 2790            .is_some_and(|pending_selection| {
 2791                let snapshot = self.buffer().read(cx).snapshot(cx);
 2792                pending_selection.range().includes(range, &snapshot)
 2793            })
 2794        {
 2795            return true;
 2796        }
 2797
 2798        self.selections
 2799            .disjoint_in_range::<MultiBufferOffset>(range.clone(), &self.display_snapshot(cx))
 2800            .into_iter()
 2801            .any(|selection| {
 2802                // This is needed to cover a corner case, if we just check for an existing
 2803                // selection in the fold range, having a cursor at the start of the fold
 2804                // marks it as selected. Non-empty selections don't cause this.
 2805                let length = selection.end - selection.start;
 2806                length > 0
 2807            })
 2808    }
 2809
 2810    pub fn key_context(&self, window: &mut Window, cx: &mut App) -> KeyContext {
 2811        self.key_context_internal(self.has_active_edit_prediction(), window, cx)
 2812    }
 2813
 2814    fn key_context_internal(
 2815        &self,
 2816        has_active_edit_prediction: bool,
 2817        window: &mut Window,
 2818        cx: &mut App,
 2819    ) -> KeyContext {
 2820        let mut key_context = KeyContext::new_with_defaults();
 2821        key_context.add("Editor");
 2822        let mode = match self.mode {
 2823            EditorMode::SingleLine => "single_line",
 2824            EditorMode::AutoHeight { .. } => "auto_height",
 2825            EditorMode::Minimap { .. } => "minimap",
 2826            EditorMode::Full { .. } => "full",
 2827        };
 2828
 2829        if EditorSettings::jupyter_enabled(cx) {
 2830            key_context.add("jupyter");
 2831        }
 2832
 2833        key_context.set("mode", mode);
 2834        if self.pending_rename.is_some() {
 2835            key_context.add("renaming");
 2836        }
 2837
 2838        if let Some(snippet_stack) = self.snippet_stack.last() {
 2839            key_context.add("in_snippet");
 2840
 2841            if snippet_stack.active_index > 0 {
 2842                key_context.add("has_previous_tabstop");
 2843            }
 2844
 2845            if snippet_stack.active_index < snippet_stack.ranges.len().saturating_sub(1) {
 2846                key_context.add("has_next_tabstop");
 2847            }
 2848        }
 2849
 2850        match self.context_menu.borrow().as_ref() {
 2851            Some(CodeContextMenu::Completions(menu)) => {
 2852                if menu.visible() {
 2853                    key_context.add("menu");
 2854                    key_context.add("showing_completions");
 2855                }
 2856            }
 2857            Some(CodeContextMenu::CodeActions(menu)) => {
 2858                if menu.visible() {
 2859                    key_context.add("menu");
 2860                    key_context.add("showing_code_actions")
 2861                }
 2862            }
 2863            None => {}
 2864        }
 2865
 2866        if self.signature_help_state.has_multiple_signatures() {
 2867            key_context.add("showing_signature_help");
 2868        }
 2869
 2870        // Disable vim contexts when a sub-editor (e.g. rename/inline assistant) is focused.
 2871        if !self.focus_handle(cx).contains_focused(window, cx)
 2872            || (self.is_focused(window) || self.mouse_menu_is_focused(window, cx))
 2873        {
 2874            for addon in self.addons.values() {
 2875                addon.extend_key_context(&mut key_context, cx)
 2876            }
 2877        }
 2878
 2879        if let Some(singleton_buffer) = self.buffer.read(cx).as_singleton() {
 2880            if let Some(extension) = singleton_buffer.read(cx).file().and_then(|file| {
 2881                Some(
 2882                    file.full_path(cx)
 2883                        .extension()?
 2884                        .to_string_lossy()
 2885                        .to_lowercase(),
 2886                )
 2887            }) {
 2888                key_context.set("extension", extension);
 2889            }
 2890        } else {
 2891            key_context.add("multibuffer");
 2892        }
 2893
 2894        if has_active_edit_prediction {
 2895            if self.edit_prediction_in_conflict() {
 2896                key_context.add(EDIT_PREDICTION_CONFLICT_KEY_CONTEXT);
 2897            } else {
 2898                key_context.add(EDIT_PREDICTION_KEY_CONTEXT);
 2899                key_context.add("copilot_suggestion");
 2900            }
 2901        }
 2902
 2903        if self.selection_mark_mode {
 2904            key_context.add("selection_mode");
 2905        }
 2906
 2907        let disjoint = self.selections.disjoint_anchors();
 2908        let snapshot = self.snapshot(window, cx);
 2909        let snapshot = snapshot.buffer_snapshot();
 2910        if self.mode == EditorMode::SingleLine
 2911            && let [selection] = disjoint
 2912            && selection.start == selection.end
 2913            && selection.end.to_offset(snapshot) == snapshot.len()
 2914        {
 2915            key_context.add("end_of_input");
 2916        }
 2917
 2918        if self.has_any_expanded_diff_hunks(cx) {
 2919            key_context.add("diffs_expanded");
 2920        }
 2921
 2922        key_context
 2923    }
 2924
 2925    pub fn last_bounds(&self) -> Option<&Bounds<Pixels>> {
 2926        self.last_bounds.as_ref()
 2927    }
 2928
 2929    fn show_mouse_cursor(&mut self, cx: &mut Context<Self>) {
 2930        if self.mouse_cursor_hidden {
 2931            self.mouse_cursor_hidden = false;
 2932            cx.notify();
 2933        }
 2934    }
 2935
 2936    pub fn hide_mouse_cursor(&mut self, origin: HideMouseCursorOrigin, cx: &mut Context<Self>) {
 2937        let hide_mouse_cursor = match origin {
 2938            HideMouseCursorOrigin::TypingAction => {
 2939                matches!(
 2940                    self.hide_mouse_mode,
 2941                    HideMouseMode::OnTyping | HideMouseMode::OnTypingAndMovement
 2942                )
 2943            }
 2944            HideMouseCursorOrigin::MovementAction => {
 2945                matches!(self.hide_mouse_mode, HideMouseMode::OnTypingAndMovement)
 2946            }
 2947        };
 2948        if self.mouse_cursor_hidden != hide_mouse_cursor {
 2949            self.mouse_cursor_hidden = hide_mouse_cursor;
 2950            cx.notify();
 2951        }
 2952    }
 2953
 2954    pub fn edit_prediction_in_conflict(&self) -> bool {
 2955        if !self.show_edit_predictions_in_menu() {
 2956            return false;
 2957        }
 2958
 2959        let showing_completions = self
 2960            .context_menu
 2961            .borrow()
 2962            .as_ref()
 2963            .is_some_and(|context| matches!(context, CodeContextMenu::Completions(_)));
 2964
 2965        showing_completions
 2966            || self.edit_prediction_requires_modifier()
 2967            // Require modifier key when the cursor is on leading whitespace, to allow `tab`
 2968            // bindings to insert tab characters.
 2969            || (self.edit_prediction_requires_modifier_in_indent_conflict && self.edit_prediction_indent_conflict)
 2970    }
 2971
 2972    pub fn accept_edit_prediction_keybind(
 2973        &self,
 2974        granularity: EditPredictionGranularity,
 2975        window: &mut Window,
 2976        cx: &mut App,
 2977    ) -> AcceptEditPredictionBinding {
 2978        let key_context = self.key_context_internal(true, window, cx);
 2979        let in_conflict = self.edit_prediction_in_conflict();
 2980
 2981        let bindings =
 2982            match granularity {
 2983                EditPredictionGranularity::Word => window
 2984                    .bindings_for_action_in_context(&AcceptNextWordEditPrediction, key_context),
 2985                EditPredictionGranularity::Line => window
 2986                    .bindings_for_action_in_context(&AcceptNextLineEditPrediction, key_context),
 2987                EditPredictionGranularity::Full => {
 2988                    window.bindings_for_action_in_context(&AcceptEditPrediction, key_context)
 2989                }
 2990            };
 2991
 2992        AcceptEditPredictionBinding(bindings.into_iter().rev().find(|binding| {
 2993            !in_conflict
 2994                || binding
 2995                    .keystrokes()
 2996                    .first()
 2997                    .is_some_and(|keystroke| keystroke.modifiers().modified())
 2998        }))
 2999    }
 3000
 3001    pub fn new_file(
 3002        workspace: &mut Workspace,
 3003        _: &workspace::NewFile,
 3004        window: &mut Window,
 3005        cx: &mut Context<Workspace>,
 3006    ) {
 3007        Self::new_in_workspace(workspace, window, cx).detach_and_prompt_err(
 3008            "Failed to create buffer",
 3009            window,
 3010            cx,
 3011            |e, _, _| match e.error_code() {
 3012                ErrorCode::RemoteUpgradeRequired => Some(format!(
 3013                "The remote instance of Zed does not support this yet. It must be upgraded to {}",
 3014                e.error_tag("required").unwrap_or("the latest version")
 3015            )),
 3016                _ => None,
 3017            },
 3018        );
 3019    }
 3020
 3021    pub fn new_in_workspace(
 3022        workspace: &mut Workspace,
 3023        window: &mut Window,
 3024        cx: &mut Context<Workspace>,
 3025    ) -> Task<Result<Entity<Editor>>> {
 3026        let project = workspace.project().clone();
 3027        let create = project.update(cx, |project, cx| project.create_buffer(None, true, cx));
 3028
 3029        cx.spawn_in(window, async move |workspace, cx| {
 3030            let buffer = create.await?;
 3031            workspace.update_in(cx, |workspace, window, cx| {
 3032                let editor =
 3033                    cx.new(|cx| Editor::for_buffer(buffer, Some(project.clone()), window, cx));
 3034                workspace.add_item_to_active_pane(Box::new(editor.clone()), None, true, window, cx);
 3035                editor
 3036            })
 3037        })
 3038    }
 3039
 3040    fn new_file_vertical(
 3041        workspace: &mut Workspace,
 3042        _: &workspace::NewFileSplitVertical,
 3043        window: &mut Window,
 3044        cx: &mut Context<Workspace>,
 3045    ) {
 3046        Self::new_file_in_direction(workspace, SplitDirection::vertical(cx), window, cx)
 3047    }
 3048
 3049    fn new_file_horizontal(
 3050        workspace: &mut Workspace,
 3051        _: &workspace::NewFileSplitHorizontal,
 3052        window: &mut Window,
 3053        cx: &mut Context<Workspace>,
 3054    ) {
 3055        Self::new_file_in_direction(workspace, SplitDirection::horizontal(cx), window, cx)
 3056    }
 3057
 3058    fn new_file_split(
 3059        workspace: &mut Workspace,
 3060        action: &workspace::NewFileSplit,
 3061        window: &mut Window,
 3062        cx: &mut Context<Workspace>,
 3063    ) {
 3064        Self::new_file_in_direction(workspace, action.0, window, cx)
 3065    }
 3066
 3067    fn new_file_in_direction(
 3068        workspace: &mut Workspace,
 3069        direction: SplitDirection,
 3070        window: &mut Window,
 3071        cx: &mut Context<Workspace>,
 3072    ) {
 3073        let project = workspace.project().clone();
 3074        let create = project.update(cx, |project, cx| project.create_buffer(None, true, cx));
 3075
 3076        cx.spawn_in(window, async move |workspace, cx| {
 3077            let buffer = create.await?;
 3078            workspace.update_in(cx, move |workspace, window, cx| {
 3079                workspace.split_item(
 3080                    direction,
 3081                    Box::new(
 3082                        cx.new(|cx| Editor::for_buffer(buffer, Some(project.clone()), window, cx)),
 3083                    ),
 3084                    window,
 3085                    cx,
 3086                )
 3087            })?;
 3088            anyhow::Ok(())
 3089        })
 3090        .detach_and_prompt_err("Failed to create buffer", window, cx, |e, _, _| {
 3091            match e.error_code() {
 3092                ErrorCode::RemoteUpgradeRequired => Some(format!(
 3093                "The remote instance of Zed does not support this yet. It must be upgraded to {}",
 3094                e.error_tag("required").unwrap_or("the latest version")
 3095            )),
 3096                _ => None,
 3097            }
 3098        });
 3099    }
 3100
 3101    pub fn leader_id(&self) -> Option<CollaboratorId> {
 3102        self.leader_id
 3103    }
 3104
 3105    pub fn buffer(&self) -> &Entity<MultiBuffer> {
 3106        &self.buffer
 3107    }
 3108
 3109    pub fn project(&self) -> Option<&Entity<Project>> {
 3110        self.project.as_ref()
 3111    }
 3112
 3113    pub fn workspace(&self) -> Option<Entity<Workspace>> {
 3114        self.workspace.as_ref()?.0.upgrade()
 3115    }
 3116
 3117    /// Detaches a task and shows an error notification in the workspace if available,
 3118    /// otherwise just logs the error.
 3119    pub fn detach_and_notify_err<R, E>(
 3120        &self,
 3121        task: Task<Result<R, E>>,
 3122        window: &mut Window,
 3123        cx: &mut App,
 3124    ) where
 3125        E: std::fmt::Debug + std::fmt::Display + 'static,
 3126        R: 'static,
 3127    {
 3128        if let Some(workspace) = self.workspace() {
 3129            task.detach_and_notify_err(workspace.downgrade(), window, cx);
 3130        } else {
 3131            task.detach_and_log_err(cx);
 3132        }
 3133    }
 3134
 3135    /// Returns the workspace serialization ID if this editor should be serialized.
 3136    fn workspace_serialization_id(&self, _cx: &App) -> Option<WorkspaceId> {
 3137        self.workspace
 3138            .as_ref()
 3139            .filter(|_| self.should_serialize_buffer())
 3140            .and_then(|workspace| workspace.1)
 3141    }
 3142
 3143    pub fn title<'a>(&self, cx: &'a App) -> Cow<'a, str> {
 3144        self.buffer().read(cx).title(cx)
 3145    }
 3146
 3147    pub fn snapshot(&self, window: &Window, cx: &mut App) -> EditorSnapshot {
 3148        let git_blame_gutter_max_author_length = self
 3149            .render_git_blame_gutter(cx)
 3150            .then(|| {
 3151                if let Some(blame) = self.blame.as_ref() {
 3152                    let max_author_length =
 3153                        blame.update(cx, |blame, cx| blame.max_author_length(cx));
 3154                    Some(max_author_length)
 3155                } else {
 3156                    None
 3157                }
 3158            })
 3159            .flatten();
 3160
 3161        let display_snapshot = self.display_map.update(cx, |map, cx| map.snapshot(cx));
 3162
 3163        EditorSnapshot {
 3164            mode: self.mode.clone(),
 3165            show_gutter: self.show_gutter,
 3166            offset_content: self.offset_content,
 3167            show_line_numbers: self.show_line_numbers,
 3168            number_deleted_lines: self.number_deleted_lines,
 3169            show_git_diff_gutter: self.show_git_diff_gutter,
 3170            semantic_tokens_enabled: self.semantic_token_state.enabled(),
 3171            show_code_actions: self.show_code_actions,
 3172            show_runnables: self.show_runnables,
 3173            show_breakpoints: self.show_breakpoints,
 3174            git_blame_gutter_max_author_length,
 3175            scroll_anchor: self.scroll_manager.shared_scroll_anchor(cx),
 3176            display_snapshot,
 3177            placeholder_display_snapshot: self
 3178                .placeholder_display_map
 3179                .as_ref()
 3180                .map(|display_map| display_map.update(cx, |map, cx| map.snapshot(cx))),
 3181            ongoing_scroll: self.scroll_manager.ongoing_scroll(),
 3182            is_focused: self.focus_handle.is_focused(window),
 3183            current_line_highlight: self
 3184                .current_line_highlight
 3185                .unwrap_or_else(|| EditorSettings::get_global(cx).current_line_highlight),
 3186            gutter_hovered: self.gutter_hovered,
 3187        }
 3188    }
 3189
 3190    pub fn language_at<T: ToOffset>(&self, point: T, cx: &App) -> Option<Arc<Language>> {
 3191        self.buffer.read(cx).language_at(point, cx)
 3192    }
 3193
 3194    pub fn file_at<T: ToOffset>(&self, point: T, cx: &App) -> Option<Arc<dyn language::File>> {
 3195        self.buffer.read(cx).read(cx).file_at(point).cloned()
 3196    }
 3197
 3198    pub fn active_excerpt(
 3199        &self,
 3200        cx: &App,
 3201    ) -> Option<(ExcerptId, Entity<Buffer>, Range<text::Anchor>)> {
 3202        self.buffer
 3203            .read(cx)
 3204            .excerpt_containing(self.selections.newest_anchor().head(), cx)
 3205    }
 3206
 3207    pub fn mode(&self) -> &EditorMode {
 3208        &self.mode
 3209    }
 3210
 3211    pub fn set_mode(&mut self, mode: EditorMode) {
 3212        self.mode = mode;
 3213    }
 3214
 3215    pub fn collaboration_hub(&self) -> Option<&dyn CollaborationHub> {
 3216        self.collaboration_hub.as_deref()
 3217    }
 3218
 3219    pub fn set_collaboration_hub(&mut self, hub: Box<dyn CollaborationHub>) {
 3220        self.collaboration_hub = Some(hub);
 3221    }
 3222
 3223    pub fn set_in_project_search(&mut self, in_project_search: bool) {
 3224        self.in_project_search = in_project_search;
 3225    }
 3226
 3227    pub fn set_custom_context_menu(
 3228        &mut self,
 3229        f: impl 'static
 3230        + Fn(
 3231            &mut Self,
 3232            DisplayPoint,
 3233            &mut Window,
 3234            &mut Context<Self>,
 3235        ) -> Option<Entity<ui::ContextMenu>>,
 3236    ) {
 3237        self.custom_context_menu = Some(Box::new(f))
 3238    }
 3239
 3240    pub fn set_completion_provider(&mut self, provider: Option<Rc<dyn CompletionProvider>>) {
 3241        self.completion_provider = provider;
 3242    }
 3243
 3244    #[cfg(any(test, feature = "test-support"))]
 3245    pub fn completion_provider(&self) -> Option<Rc<dyn CompletionProvider>> {
 3246        self.completion_provider.clone()
 3247    }
 3248
 3249    pub fn semantics_provider(&self) -> Option<Rc<dyn SemanticsProvider>> {
 3250        self.semantics_provider.clone()
 3251    }
 3252
 3253    pub fn set_semantics_provider(&mut self, provider: Option<Rc<dyn SemanticsProvider>>) {
 3254        self.semantics_provider = provider;
 3255    }
 3256
 3257    pub fn set_edit_prediction_provider<T>(
 3258        &mut self,
 3259        provider: Option<Entity<T>>,
 3260        window: &mut Window,
 3261        cx: &mut Context<Self>,
 3262    ) where
 3263        T: EditPredictionDelegate,
 3264    {
 3265        self.edit_prediction_provider = provider.map(|provider| RegisteredEditPredictionDelegate {
 3266            _subscription: cx.observe_in(&provider, window, |this, _, window, cx| {
 3267                if this.focus_handle.is_focused(window) {
 3268                    this.update_visible_edit_prediction(window, cx);
 3269                }
 3270            }),
 3271            provider: Arc::new(provider),
 3272        });
 3273        self.update_edit_prediction_settings(cx);
 3274        self.refresh_edit_prediction(false, false, window, cx);
 3275    }
 3276
 3277    pub fn placeholder_text(&self, cx: &mut App) -> Option<String> {
 3278        self.placeholder_display_map
 3279            .as_ref()
 3280            .map(|display_map| display_map.update(cx, |map, cx| map.snapshot(cx)).text())
 3281    }
 3282
 3283    pub fn set_placeholder_text(
 3284        &mut self,
 3285        placeholder_text: &str,
 3286        window: &mut Window,
 3287        cx: &mut Context<Self>,
 3288    ) {
 3289        let multibuffer = cx
 3290            .new(|cx| MultiBuffer::singleton(cx.new(|cx| Buffer::local(placeholder_text, cx)), cx));
 3291
 3292        let style = window.text_style();
 3293
 3294        self.placeholder_display_map = Some(cx.new(|cx| {
 3295            DisplayMap::new(
 3296                multibuffer,
 3297                style.font(),
 3298                style.font_size.to_pixels(window.rem_size()),
 3299                None,
 3300                FILE_HEADER_HEIGHT,
 3301                MULTI_BUFFER_EXCERPT_HEADER_HEIGHT,
 3302                Default::default(),
 3303                DiagnosticSeverity::Off,
 3304                cx,
 3305            )
 3306        }));
 3307        cx.notify();
 3308    }
 3309
 3310    pub fn set_cursor_shape(&mut self, cursor_shape: CursorShape, cx: &mut Context<Self>) {
 3311        self.cursor_shape = cursor_shape;
 3312
 3313        // Disrupt blink for immediate user feedback that the cursor shape has changed
 3314        self.blink_manager.update(cx, BlinkManager::show_cursor);
 3315
 3316        cx.notify();
 3317    }
 3318
 3319    pub fn cursor_shape(&self) -> CursorShape {
 3320        self.cursor_shape
 3321    }
 3322
 3323    pub fn set_cursor_offset_on_selection(&mut self, set_cursor_offset_on_selection: bool) {
 3324        self.cursor_offset_on_selection = set_cursor_offset_on_selection;
 3325    }
 3326
 3327    pub fn set_current_line_highlight(
 3328        &mut self,
 3329        current_line_highlight: Option<CurrentLineHighlight>,
 3330    ) {
 3331        self.current_line_highlight = current_line_highlight;
 3332    }
 3333
 3334    pub fn set_collapse_matches(&mut self, collapse_matches: bool) {
 3335        self.collapse_matches = collapse_matches;
 3336    }
 3337
 3338    pub fn range_for_match<T: std::marker::Copy>(&self, range: &Range<T>) -> Range<T> {
 3339        if self.collapse_matches {
 3340            return range.start..range.start;
 3341        }
 3342        range.clone()
 3343    }
 3344
 3345    pub fn clip_at_line_ends(&mut self, cx: &mut Context<Self>) -> bool {
 3346        self.display_map.read(cx).clip_at_line_ends
 3347    }
 3348
 3349    pub fn set_clip_at_line_ends(&mut self, clip: bool, cx: &mut Context<Self>) {
 3350        if self.display_map.read(cx).clip_at_line_ends != clip {
 3351            self.display_map
 3352                .update(cx, |map, _| map.clip_at_line_ends = clip);
 3353        }
 3354    }
 3355
 3356    pub fn set_input_enabled(&mut self, input_enabled: bool) {
 3357        self.input_enabled = input_enabled;
 3358    }
 3359
 3360    pub fn set_edit_predictions_hidden_for_vim_mode(
 3361        &mut self,
 3362        hidden: bool,
 3363        window: &mut Window,
 3364        cx: &mut Context<Self>,
 3365    ) {
 3366        if hidden != self.edit_predictions_hidden_for_vim_mode {
 3367            self.edit_predictions_hidden_for_vim_mode = hidden;
 3368            if hidden {
 3369                self.update_visible_edit_prediction(window, cx);
 3370            } else {
 3371                self.refresh_edit_prediction(true, false, window, cx);
 3372            }
 3373        }
 3374    }
 3375
 3376    pub fn set_menu_edit_predictions_policy(&mut self, value: MenuEditPredictionsPolicy) {
 3377        self.menu_edit_predictions_policy = value;
 3378    }
 3379
 3380    pub fn set_autoindent(&mut self, autoindent: bool) {
 3381        if autoindent {
 3382            self.autoindent_mode = Some(AutoindentMode::EachLine);
 3383        } else {
 3384            self.autoindent_mode = None;
 3385        }
 3386    }
 3387
 3388    pub fn capability(&self, cx: &App) -> Capability {
 3389        if self.read_only {
 3390            Capability::ReadOnly
 3391        } else {
 3392            self.buffer.read(cx).capability()
 3393        }
 3394    }
 3395
 3396    pub fn read_only(&self, cx: &App) -> bool {
 3397        self.read_only || self.buffer.read(cx).read_only()
 3398    }
 3399
 3400    pub fn set_read_only(&mut self, read_only: bool) {
 3401        self.read_only = read_only;
 3402    }
 3403
 3404    pub fn set_use_autoclose(&mut self, autoclose: bool) {
 3405        self.use_autoclose = autoclose;
 3406    }
 3407
 3408    pub fn set_use_auto_surround(&mut self, auto_surround: bool) {
 3409        self.use_auto_surround = auto_surround;
 3410    }
 3411
 3412    pub fn set_auto_replace_emoji_shortcode(&mut self, auto_replace: bool) {
 3413        self.auto_replace_emoji_shortcode = auto_replace;
 3414    }
 3415
 3416    pub fn set_should_serialize(&mut self, should_serialize: bool, cx: &App) {
 3417        self.buffer_serialization = should_serialize.then(|| {
 3418            BufferSerialization::new(
 3419                ProjectSettings::get_global(cx)
 3420                    .session
 3421                    .restore_unsaved_buffers,
 3422            )
 3423        })
 3424    }
 3425
 3426    fn should_serialize_buffer(&self) -> bool {
 3427        self.buffer_serialization.is_some()
 3428    }
 3429
 3430    pub fn toggle_edit_predictions(
 3431        &mut self,
 3432        _: &ToggleEditPrediction,
 3433        window: &mut Window,
 3434        cx: &mut Context<Self>,
 3435    ) {
 3436        if self.show_edit_predictions_override.is_some() {
 3437            self.set_show_edit_predictions(None, window, cx);
 3438        } else {
 3439            let show_edit_predictions = !self.edit_predictions_enabled();
 3440            self.set_show_edit_predictions(Some(show_edit_predictions), window, cx);
 3441        }
 3442    }
 3443
 3444    pub fn set_show_completions_on_input(&mut self, show_completions_on_input: Option<bool>) {
 3445        self.show_completions_on_input_override = show_completions_on_input;
 3446    }
 3447
 3448    pub fn set_show_edit_predictions(
 3449        &mut self,
 3450        show_edit_predictions: Option<bool>,
 3451        window: &mut Window,
 3452        cx: &mut Context<Self>,
 3453    ) {
 3454        self.show_edit_predictions_override = show_edit_predictions;
 3455        self.update_edit_prediction_settings(cx);
 3456
 3457        if let Some(false) = show_edit_predictions {
 3458            self.discard_edit_prediction(EditPredictionDiscardReason::Ignored, cx);
 3459        } else {
 3460            self.refresh_edit_prediction(false, true, window, cx);
 3461        }
 3462    }
 3463
 3464    fn edit_predictions_disabled_in_scope(
 3465        &self,
 3466        buffer: &Entity<Buffer>,
 3467        buffer_position: language::Anchor,
 3468        cx: &App,
 3469    ) -> bool {
 3470        let snapshot = buffer.read(cx).snapshot();
 3471        let settings = snapshot.settings_at(buffer_position, cx);
 3472
 3473        let Some(scope) = snapshot.language_scope_at(buffer_position) else {
 3474            return false;
 3475        };
 3476
 3477        scope.override_name().is_some_and(|scope_name| {
 3478            settings
 3479                .edit_predictions_disabled_in
 3480                .iter()
 3481                .any(|s| s == scope_name)
 3482        })
 3483    }
 3484
 3485    pub fn set_use_modal_editing(&mut self, to: bool) {
 3486        self.use_modal_editing = to;
 3487    }
 3488
 3489    pub fn use_modal_editing(&self) -> bool {
 3490        self.use_modal_editing
 3491    }
 3492
 3493    fn selections_did_change(
 3494        &mut self,
 3495        local: bool,
 3496        old_cursor_position: &Anchor,
 3497        effects: SelectionEffects,
 3498        window: &mut Window,
 3499        cx: &mut Context<Self>,
 3500    ) {
 3501        window.invalidate_character_coordinates();
 3502
 3503        // Copy selections to primary selection buffer
 3504        #[cfg(any(target_os = "linux", target_os = "freebsd"))]
 3505        if local {
 3506            let selections = self
 3507                .selections
 3508                .all::<MultiBufferOffset>(&self.display_snapshot(cx));
 3509            let buffer_handle = self.buffer.read(cx).read(cx);
 3510
 3511            let mut text = String::new();
 3512            for (index, selection) in selections.iter().enumerate() {
 3513                let text_for_selection = buffer_handle
 3514                    .text_for_range(selection.start..selection.end)
 3515                    .collect::<String>();
 3516
 3517                text.push_str(&text_for_selection);
 3518                if index != selections.len() - 1 {
 3519                    text.push('\n');
 3520                }
 3521            }
 3522
 3523            if !text.is_empty() {
 3524                cx.write_to_primary(ClipboardItem::new_string(text));
 3525            }
 3526        }
 3527
 3528        let selection_anchors = self.selections.disjoint_anchors_arc();
 3529
 3530        if self.focus_handle.is_focused(window) && self.leader_id.is_none() {
 3531            self.buffer.update(cx, |buffer, cx| {
 3532                buffer.set_active_selections(
 3533                    &selection_anchors,
 3534                    self.selections.line_mode(),
 3535                    self.cursor_shape,
 3536                    cx,
 3537                )
 3538            });
 3539        }
 3540        let display_map = self
 3541            .display_map
 3542            .update(cx, |display_map, cx| display_map.snapshot(cx));
 3543        let buffer = display_map.buffer_snapshot();
 3544        if self.selections.count() == 1 {
 3545            self.add_selections_state = None;
 3546        }
 3547        self.select_next_state = None;
 3548        self.select_prev_state = None;
 3549        self.select_syntax_node_history.try_clear();
 3550        self.invalidate_autoclose_regions(&selection_anchors, buffer);
 3551        self.snippet_stack.invalidate(&selection_anchors, buffer);
 3552        self.take_rename(false, window, cx);
 3553
 3554        let newest_selection = self.selections.newest_anchor();
 3555        let new_cursor_position = newest_selection.head();
 3556        let selection_start = newest_selection.start;
 3557
 3558        if effects.nav_history.is_none() || effects.nav_history == Some(true) {
 3559            self.push_to_nav_history(
 3560                *old_cursor_position,
 3561                Some(new_cursor_position.to_point(buffer)),
 3562                false,
 3563                effects.nav_history == Some(true),
 3564                cx,
 3565            );
 3566        }
 3567
 3568        if local {
 3569            if let Some(buffer_id) = new_cursor_position.text_anchor.buffer_id {
 3570                self.register_buffer(buffer_id, cx);
 3571            }
 3572
 3573            let mut context_menu = self.context_menu.borrow_mut();
 3574            let completion_menu = match context_menu.as_ref() {
 3575                Some(CodeContextMenu::Completions(menu)) => Some(menu),
 3576                Some(CodeContextMenu::CodeActions(_)) => {
 3577                    *context_menu = None;
 3578                    None
 3579                }
 3580                None => None,
 3581            };
 3582            let completion_position = completion_menu.map(|menu| menu.initial_position);
 3583            drop(context_menu);
 3584
 3585            if effects.completions
 3586                && let Some(completion_position) = completion_position
 3587            {
 3588                let start_offset = selection_start.to_offset(buffer);
 3589                let position_matches = start_offset == completion_position.to_offset(buffer);
 3590                let continue_showing = if let Some((snap, ..)) =
 3591                    buffer.point_to_buffer_offset(completion_position)
 3592                    && !snap.capability.editable()
 3593                {
 3594                    false
 3595                } else if position_matches {
 3596                    if self.snippet_stack.is_empty() {
 3597                        buffer.char_kind_before(start_offset, Some(CharScopeContext::Completion))
 3598                            == Some(CharKind::Word)
 3599                    } else {
 3600                        // Snippet choices can be shown even when the cursor is in whitespace.
 3601                        // Dismissing the menu with actions like backspace is handled by
 3602                        // invalidation regions.
 3603                        true
 3604                    }
 3605                } else {
 3606                    false
 3607                };
 3608
 3609                if continue_showing {
 3610                    self.open_or_update_completions_menu(None, None, false, window, cx);
 3611                } else {
 3612                    self.hide_context_menu(window, cx);
 3613                }
 3614            }
 3615
 3616            hide_hover(self, cx);
 3617
 3618            if old_cursor_position.to_display_point(&display_map).row()
 3619                != new_cursor_position.to_display_point(&display_map).row()
 3620            {
 3621                self.available_code_actions.take();
 3622            }
 3623            self.refresh_code_actions(window, cx);
 3624            self.refresh_document_highlights(cx);
 3625            refresh_linked_ranges(self, window, cx);
 3626
 3627            self.refresh_selected_text_highlights(false, window, cx);
 3628            self.refresh_matching_bracket_highlights(&display_map, cx);
 3629            self.refresh_outline_symbols_at_cursor(cx);
 3630            self.update_visible_edit_prediction(window, cx);
 3631            self.edit_prediction_requires_modifier_in_indent_conflict = true;
 3632            self.inline_blame_popover.take();
 3633            if self.git_blame_inline_enabled {
 3634                self.start_inline_blame_timer(window, cx);
 3635            }
 3636        }
 3637
 3638        self.blink_manager.update(cx, BlinkManager::pause_blinking);
 3639
 3640        if local && !self.suppress_selection_callback {
 3641            if let Some(callback) = self.on_local_selections_changed.as_ref() {
 3642                let cursor_position = self.selections.newest::<Point>(&display_map).head();
 3643                callback(cursor_position, window, cx);
 3644            }
 3645        }
 3646
 3647        cx.emit(EditorEvent::SelectionsChanged { local });
 3648
 3649        let selections = &self.selections.disjoint_anchors_arc();
 3650        if selections.len() == 1 {
 3651            cx.emit(SearchEvent::ActiveMatchChanged)
 3652        }
 3653        if local && let Some((_, _, buffer_snapshot)) = buffer.as_singleton() {
 3654            let inmemory_selections = selections
 3655                .iter()
 3656                .map(|s| {
 3657                    text::ToPoint::to_point(&s.range().start.text_anchor, buffer_snapshot)
 3658                        ..text::ToPoint::to_point(&s.range().end.text_anchor, buffer_snapshot)
 3659                })
 3660                .collect();
 3661            self.update_restoration_data(cx, |data| {
 3662                data.selections = inmemory_selections;
 3663            });
 3664
 3665            if WorkspaceSettings::get(None, cx).restore_on_startup
 3666                != RestoreOnStartupBehavior::EmptyTab
 3667                && let Some(workspace_id) = self.workspace_serialization_id(cx)
 3668            {
 3669                let snapshot = self.buffer().read(cx).snapshot(cx);
 3670                let selections = selections.clone();
 3671                let background_executor = cx.background_executor().clone();
 3672                let editor_id = cx.entity().entity_id().as_u64() as ItemId;
 3673                self.serialize_selections = cx.background_spawn(async move {
 3674                    background_executor.timer(SERIALIZATION_THROTTLE_TIME).await;
 3675                    let db_selections = selections
 3676                        .iter()
 3677                        .map(|selection| {
 3678                            (
 3679                                selection.start.to_offset(&snapshot).0,
 3680                                selection.end.to_offset(&snapshot).0,
 3681                            )
 3682                        })
 3683                        .collect();
 3684
 3685                    DB.save_editor_selections(editor_id, workspace_id, db_selections)
 3686                        .await
 3687                        .with_context(|| {
 3688                            format!(
 3689                                "persisting editor selections for editor {editor_id}, \
 3690                                workspace {workspace_id:?}"
 3691                            )
 3692                        })
 3693                        .log_err();
 3694                });
 3695            }
 3696        }
 3697
 3698        cx.notify();
 3699    }
 3700
 3701    fn folds_did_change(&mut self, cx: &mut Context<Self>) {
 3702        use text::ToOffset as _;
 3703        use text::ToPoint as _;
 3704
 3705        if self.mode.is_minimap()
 3706            || WorkspaceSettings::get(None, cx).restore_on_startup
 3707                == RestoreOnStartupBehavior::EmptyTab
 3708        {
 3709            return;
 3710        }
 3711
 3712        if !self.buffer().read(cx).is_singleton() {
 3713            return;
 3714        }
 3715
 3716        let display_snapshot = self
 3717            .display_map
 3718            .update(cx, |display_map, cx| display_map.snapshot(cx));
 3719        let Some((.., snapshot)) = display_snapshot.buffer_snapshot().as_singleton() else {
 3720            return;
 3721        };
 3722        let inmemory_folds = display_snapshot
 3723            .folds_in_range(MultiBufferOffset(0)..display_snapshot.buffer_snapshot().len())
 3724            .map(|fold| {
 3725                fold.range.start.text_anchor.to_point(&snapshot)
 3726                    ..fold.range.end.text_anchor.to_point(&snapshot)
 3727            })
 3728            .collect();
 3729        self.update_restoration_data(cx, |data| {
 3730            data.folds = inmemory_folds;
 3731        });
 3732
 3733        let Some(workspace_id) = self.workspace_serialization_id(cx) else {
 3734            return;
 3735        };
 3736        let background_executor = cx.background_executor().clone();
 3737        let editor_id = cx.entity().entity_id().as_u64() as ItemId;
 3738        const FINGERPRINT_LEN: usize = 32;
 3739        let db_folds = display_snapshot
 3740            .folds_in_range(MultiBufferOffset(0)..display_snapshot.buffer_snapshot().len())
 3741            .map(|fold| {
 3742                let start = fold.range.start.text_anchor.to_offset(&snapshot);
 3743                let end = fold.range.end.text_anchor.to_offset(&snapshot);
 3744
 3745                // Extract fingerprints - content at fold boundaries for validation on restore
 3746                // Both fingerprints must be INSIDE the fold to avoid capturing surrounding
 3747                // content that might change independently.
 3748                // start_fp: first min(32, fold_len) bytes of fold content
 3749                // end_fp: last min(32, fold_len) bytes of fold content
 3750                // Clip to character boundaries to handle multibyte UTF-8 characters.
 3751                let fold_len = end - start;
 3752                let start_fp_end = snapshot
 3753                    .clip_offset(start + std::cmp::min(FINGERPRINT_LEN, fold_len), Bias::Left);
 3754                let start_fp: String = snapshot.text_for_range(start..start_fp_end).collect();
 3755                let end_fp_start = snapshot
 3756                    .clip_offset(end.saturating_sub(FINGERPRINT_LEN).max(start), Bias::Right);
 3757                let end_fp: String = snapshot.text_for_range(end_fp_start..end).collect();
 3758
 3759                (start, end, start_fp, end_fp)
 3760            })
 3761            .collect::<Vec<_>>();
 3762        self.serialize_folds = cx.background_spawn(async move {
 3763            background_executor.timer(SERIALIZATION_THROTTLE_TIME).await;
 3764            DB.save_editor_folds(editor_id, workspace_id, db_folds)
 3765                .await
 3766                .with_context(|| {
 3767                    format!(
 3768                        "persisting editor folds for editor {editor_id}, workspace {workspace_id:?}"
 3769                    )
 3770                })
 3771                .log_err();
 3772        });
 3773    }
 3774
 3775    pub fn sync_selections(
 3776        &mut self,
 3777        other: Entity<Editor>,
 3778        cx: &mut Context<Self>,
 3779    ) -> gpui::Subscription {
 3780        let other_selections = other.read(cx).selections.disjoint_anchors().to_vec();
 3781        if !other_selections.is_empty() {
 3782            self.selections
 3783                .change_with(&self.display_snapshot(cx), |selections| {
 3784                    selections.select_anchors(other_selections);
 3785                });
 3786        }
 3787
 3788        let other_subscription = cx.subscribe(&other, |this, other, other_evt, cx| {
 3789            if let EditorEvent::SelectionsChanged { local: true } = other_evt {
 3790                let other_selections = other.read(cx).selections.disjoint_anchors().to_vec();
 3791                if other_selections.is_empty() {
 3792                    return;
 3793                }
 3794                let snapshot = this.display_snapshot(cx);
 3795                this.selections.change_with(&snapshot, |selections| {
 3796                    selections.select_anchors(other_selections);
 3797                });
 3798            }
 3799        });
 3800
 3801        let this_subscription = cx.subscribe_self::<EditorEvent>(move |this, this_evt, cx| {
 3802            if let EditorEvent::SelectionsChanged { local: true } = this_evt {
 3803                let these_selections = this.selections.disjoint_anchors().to_vec();
 3804                if these_selections.is_empty() {
 3805                    return;
 3806                }
 3807                other.update(cx, |other_editor, cx| {
 3808                    let snapshot = other_editor.display_snapshot(cx);
 3809                    other_editor
 3810                        .selections
 3811                        .change_with(&snapshot, |selections| {
 3812                            selections.select_anchors(these_selections);
 3813                        })
 3814                });
 3815            }
 3816        });
 3817
 3818        Subscription::join(other_subscription, this_subscription)
 3819    }
 3820
 3821    fn unfold_buffers_with_selections(&mut self, cx: &mut Context<Self>) {
 3822        if self.buffer().read(cx).is_singleton() {
 3823            return;
 3824        }
 3825        let snapshot = self.buffer.read(cx).snapshot(cx);
 3826        let buffer_ids: HashSet<BufferId> = self
 3827            .selections
 3828            .disjoint_anchor_ranges()
 3829            .flat_map(|range| snapshot.buffer_ids_for_range(range))
 3830            .collect();
 3831        for buffer_id in buffer_ids {
 3832            self.unfold_buffer(buffer_id, cx);
 3833        }
 3834    }
 3835
 3836    /// Changes selections using the provided mutation function. Changes to `self.selections` occur
 3837    /// immediately, but when run within `transact` or `with_selection_effects_deferred` other
 3838    /// effects of selection change occur at the end of the transaction.
 3839    pub fn change_selections<R>(
 3840        &mut self,
 3841        effects: SelectionEffects,
 3842        window: &mut Window,
 3843        cx: &mut Context<Self>,
 3844        change: impl FnOnce(&mut MutableSelectionsCollection<'_, '_>) -> R,
 3845    ) -> R {
 3846        let snapshot = self.display_snapshot(cx);
 3847        if let Some(state) = &mut self.deferred_selection_effects_state {
 3848            state.effects.scroll = effects.scroll.or(state.effects.scroll);
 3849            state.effects.completions = effects.completions;
 3850            state.effects.nav_history = effects.nav_history.or(state.effects.nav_history);
 3851            let (changed, result) = self.selections.change_with(&snapshot, change);
 3852            state.changed |= changed;
 3853            return result;
 3854        }
 3855        let mut state = DeferredSelectionEffectsState {
 3856            changed: false,
 3857            effects,
 3858            old_cursor_position: self.selections.newest_anchor().head(),
 3859            history_entry: SelectionHistoryEntry {
 3860                selections: self.selections.disjoint_anchors_arc(),
 3861                select_next_state: self.select_next_state.clone(),
 3862                select_prev_state: self.select_prev_state.clone(),
 3863                add_selections_state: self.add_selections_state.clone(),
 3864            },
 3865        };
 3866        let (changed, result) = self.selections.change_with(&snapshot, change);
 3867        state.changed = state.changed || changed;
 3868        if self.defer_selection_effects {
 3869            self.deferred_selection_effects_state = Some(state);
 3870        } else {
 3871            self.apply_selection_effects(state, window, cx);
 3872        }
 3873        result
 3874    }
 3875
 3876    /// Defers the effects of selection change, so that the effects of multiple calls to
 3877    /// `change_selections` are applied at the end. This way these intermediate states aren't added
 3878    /// to selection history and the state of popovers based on selection position aren't
 3879    /// erroneously updated.
 3880    pub fn with_selection_effects_deferred<R>(
 3881        &mut self,
 3882        window: &mut Window,
 3883        cx: &mut Context<Self>,
 3884        update: impl FnOnce(&mut Self, &mut Window, &mut Context<Self>) -> R,
 3885    ) -> R {
 3886        let already_deferred = self.defer_selection_effects;
 3887        self.defer_selection_effects = true;
 3888        let result = update(self, window, cx);
 3889        if !already_deferred {
 3890            self.defer_selection_effects = false;
 3891            if let Some(state) = self.deferred_selection_effects_state.take() {
 3892                self.apply_selection_effects(state, window, cx);
 3893            }
 3894        }
 3895        result
 3896    }
 3897
 3898    fn apply_selection_effects(
 3899        &mut self,
 3900        state: DeferredSelectionEffectsState,
 3901        window: &mut Window,
 3902        cx: &mut Context<Self>,
 3903    ) {
 3904        if state.changed {
 3905            self.selection_history.push(state.history_entry);
 3906
 3907            if let Some(autoscroll) = state.effects.scroll {
 3908                self.request_autoscroll(autoscroll, cx);
 3909            }
 3910
 3911            let old_cursor_position = &state.old_cursor_position;
 3912
 3913            self.selections_did_change(true, old_cursor_position, state.effects, window, cx);
 3914
 3915            if self.should_open_signature_help_automatically(old_cursor_position, cx) {
 3916                self.show_signature_help_auto(window, cx);
 3917            }
 3918        }
 3919    }
 3920
 3921    pub fn edit<I, S, T>(&mut self, edits: I, cx: &mut Context<Self>)
 3922    where
 3923        I: IntoIterator<Item = (Range<S>, T)>,
 3924        S: ToOffset,
 3925        T: Into<Arc<str>>,
 3926    {
 3927        if self.read_only(cx) {
 3928            return;
 3929        }
 3930
 3931        self.buffer
 3932            .update(cx, |buffer, cx| buffer.edit(edits, None, cx));
 3933    }
 3934
 3935    pub fn edit_with_autoindent<I, S, T>(&mut self, edits: I, cx: &mut Context<Self>)
 3936    where
 3937        I: IntoIterator<Item = (Range<S>, T)>,
 3938        S: ToOffset,
 3939        T: Into<Arc<str>>,
 3940    {
 3941        if self.read_only(cx) {
 3942            return;
 3943        }
 3944
 3945        self.buffer.update(cx, |buffer, cx| {
 3946            buffer.edit(edits, self.autoindent_mode.clone(), cx)
 3947        });
 3948    }
 3949
 3950    pub fn edit_with_block_indent<I, S, T>(
 3951        &mut self,
 3952        edits: I,
 3953        original_indent_columns: Vec<Option<u32>>,
 3954        cx: &mut Context<Self>,
 3955    ) where
 3956        I: IntoIterator<Item = (Range<S>, T)>,
 3957        S: ToOffset,
 3958        T: Into<Arc<str>>,
 3959    {
 3960        if self.read_only(cx) {
 3961            return;
 3962        }
 3963
 3964        self.buffer.update(cx, |buffer, cx| {
 3965            buffer.edit(
 3966                edits,
 3967                Some(AutoindentMode::Block {
 3968                    original_indent_columns,
 3969                }),
 3970                cx,
 3971            )
 3972        });
 3973    }
 3974
 3975    fn select(&mut self, phase: SelectPhase, window: &mut Window, cx: &mut Context<Self>) {
 3976        self.hide_context_menu(window, cx);
 3977
 3978        match phase {
 3979            SelectPhase::Begin {
 3980                position,
 3981                add,
 3982                click_count,
 3983            } => self.begin_selection(position, add, click_count, window, cx),
 3984            SelectPhase::BeginColumnar {
 3985                position,
 3986                goal_column,
 3987                reset,
 3988                mode,
 3989            } => self.begin_columnar_selection(position, goal_column, reset, mode, window, cx),
 3990            SelectPhase::Extend {
 3991                position,
 3992                click_count,
 3993            } => self.extend_selection(position, click_count, window, cx),
 3994            SelectPhase::Update {
 3995                position,
 3996                goal_column,
 3997                scroll_delta,
 3998            } => self.update_selection(position, goal_column, scroll_delta, window, cx),
 3999            SelectPhase::End => self.end_selection(window, cx),
 4000        }
 4001    }
 4002
 4003    fn extend_selection(
 4004        &mut self,
 4005        position: DisplayPoint,
 4006        click_count: usize,
 4007        window: &mut Window,
 4008        cx: &mut Context<Self>,
 4009    ) {
 4010        let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
 4011        let tail = self
 4012            .selections
 4013            .newest::<MultiBufferOffset>(&display_map)
 4014            .tail();
 4015        let click_count = click_count.max(match self.selections.select_mode() {
 4016            SelectMode::Character => 1,
 4017            SelectMode::Word(_) => 2,
 4018            SelectMode::Line(_) => 3,
 4019            SelectMode::All => 4,
 4020        });
 4021        self.begin_selection(position, false, click_count, window, cx);
 4022
 4023        let tail_anchor = display_map.buffer_snapshot().anchor_before(tail);
 4024
 4025        let current_selection = match self.selections.select_mode() {
 4026            SelectMode::Character | SelectMode::All => tail_anchor..tail_anchor,
 4027            SelectMode::Word(range) | SelectMode::Line(range) => range.clone(),
 4028        };
 4029
 4030        let mut pending_selection = self
 4031            .selections
 4032            .pending_anchor()
 4033            .cloned()
 4034            .expect("extend_selection not called with pending selection");
 4035
 4036        if pending_selection
 4037            .start
 4038            .cmp(&current_selection.start, display_map.buffer_snapshot())
 4039            == Ordering::Greater
 4040        {
 4041            pending_selection.start = current_selection.start;
 4042        }
 4043        if pending_selection
 4044            .end
 4045            .cmp(&current_selection.end, display_map.buffer_snapshot())
 4046            == Ordering::Less
 4047        {
 4048            pending_selection.end = current_selection.end;
 4049            pending_selection.reversed = true;
 4050        }
 4051
 4052        let mut pending_mode = self.selections.pending_mode().unwrap();
 4053        match &mut pending_mode {
 4054            SelectMode::Word(range) | SelectMode::Line(range) => *range = current_selection,
 4055            _ => {}
 4056        }
 4057
 4058        let effects = if EditorSettings::get_global(cx).autoscroll_on_clicks {
 4059            SelectionEffects::scroll(Autoscroll::fit())
 4060        } else {
 4061            SelectionEffects::no_scroll()
 4062        };
 4063
 4064        self.change_selections(effects, window, cx, |s| {
 4065            s.set_pending(pending_selection.clone(), pending_mode);
 4066            s.set_is_extending(true);
 4067        });
 4068    }
 4069
 4070    fn begin_selection(
 4071        &mut self,
 4072        position: DisplayPoint,
 4073        add: bool,
 4074        click_count: usize,
 4075        window: &mut Window,
 4076        cx: &mut Context<Self>,
 4077    ) {
 4078        if !self.focus_handle.is_focused(window) {
 4079            self.last_focused_descendant = None;
 4080            window.focus(&self.focus_handle, cx);
 4081        }
 4082
 4083        let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
 4084        let buffer = display_map.buffer_snapshot();
 4085        let position = display_map.clip_point(position, Bias::Left);
 4086
 4087        let start;
 4088        let end;
 4089        let mode;
 4090        let mut auto_scroll;
 4091        match click_count {
 4092            1 => {
 4093                start = buffer.anchor_before(position.to_point(&display_map));
 4094                end = start;
 4095                mode = SelectMode::Character;
 4096                auto_scroll = true;
 4097            }
 4098            2 => {
 4099                let position = display_map
 4100                    .clip_point(position, Bias::Left)
 4101                    .to_offset(&display_map, Bias::Left);
 4102                let (range, _) = buffer.surrounding_word(position, None);
 4103                start = buffer.anchor_before(range.start);
 4104                end = buffer.anchor_before(range.end);
 4105                mode = SelectMode::Word(start..end);
 4106                auto_scroll = true;
 4107            }
 4108            3 => {
 4109                let position = display_map
 4110                    .clip_point(position, Bias::Left)
 4111                    .to_point(&display_map);
 4112                let line_start = display_map.prev_line_boundary(position).0;
 4113                let next_line_start = buffer.clip_point(
 4114                    display_map.next_line_boundary(position).0 + Point::new(1, 0),
 4115                    Bias::Left,
 4116                );
 4117                start = buffer.anchor_before(line_start);
 4118                end = buffer.anchor_before(next_line_start);
 4119                mode = SelectMode::Line(start..end);
 4120                auto_scroll = true;
 4121            }
 4122            _ => {
 4123                start = buffer.anchor_before(MultiBufferOffset(0));
 4124                end = buffer.anchor_before(buffer.len());
 4125                mode = SelectMode::All;
 4126                auto_scroll = false;
 4127            }
 4128        }
 4129        auto_scroll &= EditorSettings::get_global(cx).autoscroll_on_clicks;
 4130
 4131        let point_to_delete: Option<usize> = {
 4132            let selected_points: Vec<Selection<Point>> =
 4133                self.selections.disjoint_in_range(start..end, &display_map);
 4134
 4135            if !add || click_count > 1 {
 4136                None
 4137            } else if !selected_points.is_empty() {
 4138                Some(selected_points[0].id)
 4139            } else {
 4140                let clicked_point_already_selected =
 4141                    self.selections.disjoint_anchors().iter().find(|selection| {
 4142                        selection.start.to_point(buffer) == start.to_point(buffer)
 4143                            || selection.end.to_point(buffer) == end.to_point(buffer)
 4144                    });
 4145
 4146                clicked_point_already_selected.map(|selection| selection.id)
 4147            }
 4148        };
 4149
 4150        let selections_count = self.selections.count();
 4151        let effects = if auto_scroll {
 4152            SelectionEffects::default()
 4153        } else {
 4154            SelectionEffects::no_scroll()
 4155        };
 4156
 4157        self.change_selections(effects, window, cx, |s| {
 4158            if let Some(point_to_delete) = point_to_delete {
 4159                s.delete(point_to_delete);
 4160
 4161                if selections_count == 1 {
 4162                    s.set_pending_anchor_range(start..end, mode);
 4163                }
 4164            } else {
 4165                if !add {
 4166                    s.clear_disjoint();
 4167                }
 4168
 4169                s.set_pending_anchor_range(start..end, mode);
 4170            }
 4171        });
 4172    }
 4173
 4174    fn begin_columnar_selection(
 4175        &mut self,
 4176        position: DisplayPoint,
 4177        goal_column: u32,
 4178        reset: bool,
 4179        mode: ColumnarMode,
 4180        window: &mut Window,
 4181        cx: &mut Context<Self>,
 4182    ) {
 4183        if !self.focus_handle.is_focused(window) {
 4184            self.last_focused_descendant = None;
 4185            window.focus(&self.focus_handle, cx);
 4186        }
 4187
 4188        let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
 4189
 4190        if reset {
 4191            let pointer_position = display_map
 4192                .buffer_snapshot()
 4193                .anchor_before(position.to_point(&display_map));
 4194
 4195            self.change_selections(
 4196                SelectionEffects::scroll(Autoscroll::newest()),
 4197                window,
 4198                cx,
 4199                |s| {
 4200                    s.clear_disjoint();
 4201                    s.set_pending_anchor_range(
 4202                        pointer_position..pointer_position,
 4203                        SelectMode::Character,
 4204                    );
 4205                },
 4206            );
 4207        };
 4208
 4209        let tail = self.selections.newest::<Point>(&display_map).tail();
 4210        let selection_anchor = display_map.buffer_snapshot().anchor_before(tail);
 4211        self.columnar_selection_state = match mode {
 4212            ColumnarMode::FromMouse => Some(ColumnarSelectionState::FromMouse {
 4213                selection_tail: selection_anchor,
 4214                display_point: if reset {
 4215                    if position.column() != goal_column {
 4216                        Some(DisplayPoint::new(position.row(), goal_column))
 4217                    } else {
 4218                        None
 4219                    }
 4220                } else {
 4221                    None
 4222                },
 4223            }),
 4224            ColumnarMode::FromSelection => Some(ColumnarSelectionState::FromSelection {
 4225                selection_tail: selection_anchor,
 4226            }),
 4227        };
 4228
 4229        if !reset {
 4230            self.select_columns(position, goal_column, &display_map, window, cx);
 4231        }
 4232    }
 4233
 4234    fn update_selection(
 4235        &mut self,
 4236        position: DisplayPoint,
 4237        goal_column: u32,
 4238        scroll_delta: gpui::Point<f32>,
 4239        window: &mut Window,
 4240        cx: &mut Context<Self>,
 4241    ) {
 4242        let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
 4243
 4244        if self.columnar_selection_state.is_some() {
 4245            self.select_columns(position, goal_column, &display_map, window, cx);
 4246        } else if let Some(mut pending) = self.selections.pending_anchor().cloned() {
 4247            let buffer = display_map.buffer_snapshot();
 4248            let head;
 4249            let tail;
 4250            let mode = self.selections.pending_mode().unwrap();
 4251            match &mode {
 4252                SelectMode::Character => {
 4253                    head = position.to_point(&display_map);
 4254                    tail = pending.tail().to_point(buffer);
 4255                }
 4256                SelectMode::Word(original_range) => {
 4257                    let offset = display_map
 4258                        .clip_point(position, Bias::Left)
 4259                        .to_offset(&display_map, Bias::Left);
 4260                    let original_range = original_range.to_offset(buffer);
 4261
 4262                    let head_offset = if buffer.is_inside_word(offset, None)
 4263                        || original_range.contains(&offset)
 4264                    {
 4265                        let (word_range, _) = buffer.surrounding_word(offset, None);
 4266                        if word_range.start < original_range.start {
 4267                            word_range.start
 4268                        } else {
 4269                            word_range.end
 4270                        }
 4271                    } else {
 4272                        offset
 4273                    };
 4274
 4275                    head = head_offset.to_point(buffer);
 4276                    if head_offset <= original_range.start {
 4277                        tail = original_range.end.to_point(buffer);
 4278                    } else {
 4279                        tail = original_range.start.to_point(buffer);
 4280                    }
 4281                }
 4282                SelectMode::Line(original_range) => {
 4283                    let original_range = original_range.to_point(display_map.buffer_snapshot());
 4284
 4285                    let position = display_map
 4286                        .clip_point(position, Bias::Left)
 4287                        .to_point(&display_map);
 4288                    let line_start = display_map.prev_line_boundary(position).0;
 4289                    let next_line_start = buffer.clip_point(
 4290                        display_map.next_line_boundary(position).0 + Point::new(1, 0),
 4291                        Bias::Left,
 4292                    );
 4293
 4294                    if line_start < original_range.start {
 4295                        head = line_start
 4296                    } else {
 4297                        head = next_line_start
 4298                    }
 4299
 4300                    if head <= original_range.start {
 4301                        tail = original_range.end;
 4302                    } else {
 4303                        tail = original_range.start;
 4304                    }
 4305                }
 4306                SelectMode::All => {
 4307                    return;
 4308                }
 4309            };
 4310
 4311            if head < tail {
 4312                pending.start = buffer.anchor_before(head);
 4313                pending.end = buffer.anchor_before(tail);
 4314                pending.reversed = true;
 4315            } else {
 4316                pending.start = buffer.anchor_before(tail);
 4317                pending.end = buffer.anchor_before(head);
 4318                pending.reversed = false;
 4319            }
 4320
 4321            self.change_selections(SelectionEffects::no_scroll(), window, cx, |s| {
 4322                s.set_pending(pending.clone(), mode);
 4323            });
 4324        } else {
 4325            log::error!("update_selection dispatched with no pending selection");
 4326            return;
 4327        }
 4328
 4329        self.apply_scroll_delta(scroll_delta, window, cx);
 4330        cx.notify();
 4331    }
 4332
 4333    fn end_selection(&mut self, window: &mut Window, cx: &mut Context<Self>) {
 4334        self.columnar_selection_state.take();
 4335        if let Some(pending_mode) = self.selections.pending_mode() {
 4336            let selections = self
 4337                .selections
 4338                .all::<MultiBufferOffset>(&self.display_snapshot(cx));
 4339            self.change_selections(SelectionEffects::no_scroll(), window, cx, |s| {
 4340                s.select(selections);
 4341                s.clear_pending();
 4342                if s.is_extending() {
 4343                    s.set_is_extending(false);
 4344                } else {
 4345                    s.set_select_mode(pending_mode);
 4346                }
 4347            });
 4348        }
 4349    }
 4350
 4351    fn select_columns(
 4352        &mut self,
 4353        head: DisplayPoint,
 4354        goal_column: u32,
 4355        display_map: &DisplaySnapshot,
 4356        window: &mut Window,
 4357        cx: &mut Context<Self>,
 4358    ) {
 4359        let Some(columnar_state) = self.columnar_selection_state.as_ref() else {
 4360            return;
 4361        };
 4362
 4363        let tail = match columnar_state {
 4364            ColumnarSelectionState::FromMouse {
 4365                selection_tail,
 4366                display_point,
 4367            } => display_point.unwrap_or_else(|| selection_tail.to_display_point(display_map)),
 4368            ColumnarSelectionState::FromSelection { selection_tail } => {
 4369                selection_tail.to_display_point(display_map)
 4370            }
 4371        };
 4372
 4373        let start_row = cmp::min(tail.row(), head.row());
 4374        let end_row = cmp::max(tail.row(), head.row());
 4375        let start_column = cmp::min(tail.column(), goal_column);
 4376        let end_column = cmp::max(tail.column(), goal_column);
 4377        let reversed = start_column < tail.column();
 4378
 4379        let selection_ranges = (start_row.0..=end_row.0)
 4380            .map(DisplayRow)
 4381            .filter_map(|row| {
 4382                if (matches!(columnar_state, ColumnarSelectionState::FromMouse { .. })
 4383                    || start_column <= display_map.line_len(row))
 4384                    && !display_map.is_block_line(row)
 4385                {
 4386                    let start = display_map
 4387                        .clip_point(DisplayPoint::new(row, start_column), Bias::Left)
 4388                        .to_point(display_map);
 4389                    let end = display_map
 4390                        .clip_point(DisplayPoint::new(row, end_column), Bias::Right)
 4391                        .to_point(display_map);
 4392                    if reversed {
 4393                        Some(end..start)
 4394                    } else {
 4395                        Some(start..end)
 4396                    }
 4397                } else {
 4398                    None
 4399                }
 4400            })
 4401            .collect::<Vec<_>>();
 4402        if selection_ranges.is_empty() {
 4403            return;
 4404        }
 4405
 4406        let ranges = match columnar_state {
 4407            ColumnarSelectionState::FromMouse { .. } => {
 4408                let mut non_empty_ranges = selection_ranges
 4409                    .iter()
 4410                    .filter(|selection_range| selection_range.start != selection_range.end)
 4411                    .peekable();
 4412                if non_empty_ranges.peek().is_some() {
 4413                    non_empty_ranges.cloned().collect()
 4414                } else {
 4415                    selection_ranges
 4416                }
 4417            }
 4418            _ => selection_ranges,
 4419        };
 4420
 4421        self.change_selections(SelectionEffects::no_scroll(), window, cx, |s| {
 4422            s.select_ranges(ranges);
 4423        });
 4424        cx.notify();
 4425    }
 4426
 4427    pub fn has_non_empty_selection(&self, snapshot: &DisplaySnapshot) -> bool {
 4428        self.selections
 4429            .all_adjusted(snapshot)
 4430            .iter()
 4431            .any(|selection| !selection.is_empty())
 4432    }
 4433
 4434    pub fn has_pending_nonempty_selection(&self) -> bool {
 4435        let pending_nonempty_selection = match self.selections.pending_anchor() {
 4436            Some(Selection { start, end, .. }) => start != end,
 4437            None => false,
 4438        };
 4439
 4440        pending_nonempty_selection
 4441            || (self.columnar_selection_state.is_some()
 4442                && self.selections.disjoint_anchors().len() > 1)
 4443    }
 4444
 4445    pub fn has_pending_selection(&self) -> bool {
 4446        self.selections.pending_anchor().is_some() || self.columnar_selection_state.is_some()
 4447    }
 4448
 4449    pub fn cancel(&mut self, _: &Cancel, window: &mut Window, cx: &mut Context<Self>) {
 4450        self.selection_mark_mode = false;
 4451        self.selection_drag_state = SelectionDragState::None;
 4452
 4453        if self.dismiss_menus_and_popups(true, window, cx) {
 4454            cx.notify();
 4455            return;
 4456        }
 4457        if self.clear_expanded_diff_hunks(cx) {
 4458            cx.notify();
 4459            return;
 4460        }
 4461        if self.show_git_blame_gutter {
 4462            self.show_git_blame_gutter = false;
 4463            cx.notify();
 4464            return;
 4465        }
 4466
 4467        if self.mode.is_full()
 4468            && self.change_selections(Default::default(), window, cx, |s| s.try_cancel())
 4469        {
 4470            cx.notify();
 4471            return;
 4472        }
 4473
 4474        cx.propagate();
 4475    }
 4476
 4477    pub fn dismiss_menus_and_popups(
 4478        &mut self,
 4479        is_user_requested: bool,
 4480        window: &mut Window,
 4481        cx: &mut Context<Self>,
 4482    ) -> bool {
 4483        let mut dismissed = false;
 4484
 4485        dismissed |= self.take_rename(false, window, cx).is_some();
 4486        dismissed |= self.hide_blame_popover(true, cx);
 4487        dismissed |= hide_hover(self, cx);
 4488        dismissed |= self.hide_signature_help(cx, SignatureHelpHiddenBy::Escape);
 4489        dismissed |= self.hide_context_menu(window, cx).is_some();
 4490        dismissed |= self.mouse_context_menu.take().is_some();
 4491        dismissed |= is_user_requested
 4492            && self.discard_edit_prediction(EditPredictionDiscardReason::Rejected, cx);
 4493        dismissed |= self.snippet_stack.pop().is_some();
 4494        if self.diff_review_drag_state.is_some() {
 4495            self.cancel_diff_review_drag(cx);
 4496            dismissed = true;
 4497        }
 4498        if !self.diff_review_overlays.is_empty() {
 4499            self.dismiss_all_diff_review_overlays(cx);
 4500            dismissed = true;
 4501        }
 4502
 4503        if self.mode.is_full() && matches!(self.active_diagnostics, ActiveDiagnostic::Group(_)) {
 4504            self.dismiss_diagnostics(cx);
 4505            dismissed = true;
 4506        }
 4507
 4508        dismissed
 4509    }
 4510
 4511    fn linked_editing_ranges_for(
 4512        &self,
 4513        selection: Range<text::Anchor>,
 4514        cx: &App,
 4515    ) -> Option<HashMap<Entity<Buffer>, Vec<Range<text::Anchor>>>> {
 4516        if self.linked_edit_ranges.is_empty() {
 4517            return None;
 4518        }
 4519        let ((base_range, linked_ranges), buffer_snapshot, buffer) =
 4520            selection.end.buffer_id.and_then(|end_buffer_id| {
 4521                if selection.start.buffer_id != Some(end_buffer_id) {
 4522                    return None;
 4523                }
 4524                let buffer = self.buffer.read(cx).buffer(end_buffer_id)?;
 4525                let snapshot = buffer.read(cx).snapshot();
 4526                self.linked_edit_ranges
 4527                    .get(end_buffer_id, selection.start..selection.end, &snapshot)
 4528                    .map(|ranges| (ranges, snapshot, buffer))
 4529            })?;
 4530        use text::ToOffset as TO;
 4531        // find offset from the start of current range to current cursor position
 4532        let start_byte_offset = TO::to_offset(&base_range.start, &buffer_snapshot);
 4533
 4534        let start_offset = TO::to_offset(&selection.start, &buffer_snapshot);
 4535        let start_difference = start_offset - start_byte_offset;
 4536        let end_offset = TO::to_offset(&selection.end, &buffer_snapshot);
 4537        let end_difference = end_offset - start_byte_offset;
 4538        // Current range has associated linked ranges.
 4539        let mut linked_edits = HashMap::<_, Vec<_>>::default();
 4540        for range in linked_ranges.iter() {
 4541            let start_offset = TO::to_offset(&range.start, &buffer_snapshot);
 4542            let end_offset = start_offset + end_difference;
 4543            let start_offset = start_offset + start_difference;
 4544            if start_offset > buffer_snapshot.len() || end_offset > buffer_snapshot.len() {
 4545                continue;
 4546            }
 4547            if self.selections.disjoint_anchor_ranges().any(|s| {
 4548                if s.start.text_anchor.buffer_id != selection.start.buffer_id
 4549                    || s.end.text_anchor.buffer_id != selection.end.buffer_id
 4550                {
 4551                    return false;
 4552                }
 4553                TO::to_offset(&s.start.text_anchor, &buffer_snapshot) <= end_offset
 4554                    && TO::to_offset(&s.end.text_anchor, &buffer_snapshot) >= start_offset
 4555            }) {
 4556                continue;
 4557            }
 4558            let start = buffer_snapshot.anchor_after(start_offset);
 4559            let end = buffer_snapshot.anchor_after(end_offset);
 4560            linked_edits
 4561                .entry(buffer.clone())
 4562                .or_default()
 4563                .push(start..end);
 4564        }
 4565        Some(linked_edits)
 4566    }
 4567
 4568    pub fn handle_input(&mut self, text: &str, window: &mut Window, cx: &mut Context<Self>) {
 4569        let text: Arc<str> = text.into();
 4570
 4571        if self.read_only(cx) {
 4572            return;
 4573        }
 4574
 4575        self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
 4576
 4577        self.unfold_buffers_with_selections(cx);
 4578
 4579        let selections = self.selections.all_adjusted(&self.display_snapshot(cx));
 4580        let mut bracket_inserted = false;
 4581        let mut edits = Vec::new();
 4582        let mut linked_edits = HashMap::<_, Vec<_>>::default();
 4583        let mut new_selections = Vec::with_capacity(selections.len());
 4584        let mut new_autoclose_regions = Vec::new();
 4585        let snapshot = self.buffer.read(cx).read(cx);
 4586        let mut clear_linked_edit_ranges = false;
 4587        let mut all_selections_read_only = true;
 4588        let mut has_adjacent_edits = false;
 4589        let mut in_adjacent_group = false;
 4590
 4591        let mut regions = self
 4592            .selections_with_autoclose_regions(selections, &snapshot)
 4593            .peekable();
 4594
 4595        while let Some((selection, autoclose_region)) = regions.next() {
 4596            if snapshot
 4597                .point_to_buffer_point(selection.head())
 4598                .is_none_or(|(snapshot, ..)| !snapshot.capability.editable())
 4599            {
 4600                continue;
 4601            }
 4602            if snapshot
 4603                .point_to_buffer_point(selection.tail())
 4604                .is_none_or(|(snapshot, ..)| !snapshot.capability.editable())
 4605            {
 4606                // note, ideally we'd clip the tail to the closest writeable region towards the head
 4607                continue;
 4608            }
 4609            all_selections_read_only = false;
 4610
 4611            if let Some(scope) = snapshot.language_scope_at(selection.head()) {
 4612                // Determine if the inserted text matches the opening or closing
 4613                // bracket of any of this language's bracket pairs.
 4614                let mut bracket_pair = None;
 4615                let mut is_bracket_pair_start = false;
 4616                let mut is_bracket_pair_end = false;
 4617                if !text.is_empty() {
 4618                    let mut bracket_pair_matching_end = None;
 4619                    // `text` can be empty when a user is using IME (e.g. Chinese Wubi Simplified)
 4620                    //  and they are removing the character that triggered IME popup.
 4621                    for (pair, enabled) in scope.brackets() {
 4622                        if !pair.close && !pair.surround {
 4623                            continue;
 4624                        }
 4625
 4626                        if enabled && pair.start.ends_with(text.as_ref()) {
 4627                            let prefix_len = pair.start.len() - text.len();
 4628                            let preceding_text_matches_prefix = prefix_len == 0
 4629                                || (selection.start.column >= (prefix_len as u32)
 4630                                    && snapshot.contains_str_at(
 4631                                        Point::new(
 4632                                            selection.start.row,
 4633                                            selection.start.column - (prefix_len as u32),
 4634                                        ),
 4635                                        &pair.start[..prefix_len],
 4636                                    ));
 4637                            if preceding_text_matches_prefix {
 4638                                bracket_pair = Some(pair.clone());
 4639                                is_bracket_pair_start = true;
 4640                                break;
 4641                            }
 4642                        }
 4643                        if pair.end.as_str() == text.as_ref() && bracket_pair_matching_end.is_none()
 4644                        {
 4645                            // take first bracket pair matching end, but don't break in case a later bracket
 4646                            // pair matches start
 4647                            bracket_pair_matching_end = Some(pair.clone());
 4648                        }
 4649                    }
 4650                    if let Some(end) = bracket_pair_matching_end
 4651                        && bracket_pair.is_none()
 4652                    {
 4653                        bracket_pair = Some(end);
 4654                        is_bracket_pair_end = true;
 4655                    }
 4656                }
 4657
 4658                if let Some(bracket_pair) = bracket_pair {
 4659                    let snapshot_settings = snapshot.language_settings_at(selection.start, cx);
 4660                    let autoclose = self.use_autoclose && snapshot_settings.use_autoclose;
 4661                    let auto_surround =
 4662                        self.use_auto_surround && snapshot_settings.use_auto_surround;
 4663                    if selection.is_empty() {
 4664                        if is_bracket_pair_start {
 4665                            // If the inserted text is a suffix of an opening bracket and the
 4666                            // selection is preceded by the rest of the opening bracket, then
 4667                            // insert the closing bracket.
 4668                            let following_text_allows_autoclose = snapshot
 4669                                .chars_at(selection.start)
 4670                                .next()
 4671                                .is_none_or(|c| scope.should_autoclose_before(c));
 4672
 4673                            let preceding_text_allows_autoclose = selection.start.column == 0
 4674                                || snapshot
 4675                                    .reversed_chars_at(selection.start)
 4676                                    .next()
 4677                                    .is_none_or(|c| {
 4678                                        bracket_pair.start != bracket_pair.end
 4679                                            || !snapshot
 4680                                                .char_classifier_at(selection.start)
 4681                                                .is_word(c)
 4682                                    });
 4683
 4684                            let is_closing_quote = if bracket_pair.end == bracket_pair.start
 4685                                && bracket_pair.start.len() == 1
 4686                            {
 4687                                let target = bracket_pair.start.chars().next().unwrap();
 4688                                let mut byte_offset = 0u32;
 4689                                let current_line_count = snapshot
 4690                                    .reversed_chars_at(selection.start)
 4691                                    .take_while(|&c| c != '\n')
 4692                                    .filter(|c| {
 4693                                        byte_offset += c.len_utf8() as u32;
 4694                                        if *c != target {
 4695                                            return false;
 4696                                        }
 4697
 4698                                        let point = Point::new(
 4699                                            selection.start.row,
 4700                                            selection.start.column.saturating_sub(byte_offset),
 4701                                        );
 4702
 4703                                        let is_enabled = snapshot
 4704                                            .language_scope_at(point)
 4705                                            .and_then(|scope| {
 4706                                                scope
 4707                                                    .brackets()
 4708                                                    .find(|(pair, _)| {
 4709                                                        pair.start == bracket_pair.start
 4710                                                    })
 4711                                                    .map(|(_, enabled)| enabled)
 4712                                            })
 4713                                            .unwrap_or(true);
 4714
 4715                                        let is_delimiter = snapshot
 4716                                            .language_scope_at(Point::new(
 4717                                                point.row,
 4718                                                point.column + 1,
 4719                                            ))
 4720                                            .and_then(|scope| {
 4721                                                scope
 4722                                                    .brackets()
 4723                                                    .find(|(pair, _)| {
 4724                                                        pair.start == bracket_pair.start
 4725                                                    })
 4726                                                    .map(|(_, enabled)| !enabled)
 4727                                            })
 4728                                            .unwrap_or(false);
 4729
 4730                                        is_enabled && !is_delimiter
 4731                                    })
 4732                                    .count();
 4733                                current_line_count % 2 == 1
 4734                            } else {
 4735                                false
 4736                            };
 4737
 4738                            if autoclose
 4739                                && bracket_pair.close
 4740                                && following_text_allows_autoclose
 4741                                && preceding_text_allows_autoclose
 4742                                && !is_closing_quote
 4743                            {
 4744                                let anchor = snapshot.anchor_before(selection.end);
 4745                                new_selections.push((selection.map(|_| anchor), text.len()));
 4746                                new_autoclose_regions.push((
 4747                                    anchor,
 4748                                    text.len(),
 4749                                    selection.id,
 4750                                    bracket_pair.clone(),
 4751                                ));
 4752                                edits.push((
 4753                                    selection.range(),
 4754                                    format!("{}{}", text, bracket_pair.end).into(),
 4755                                ));
 4756                                bracket_inserted = true;
 4757                                continue;
 4758                            }
 4759                        }
 4760
 4761                        if let Some(region) = autoclose_region {
 4762                            // If the selection is followed by an auto-inserted closing bracket,
 4763                            // then don't insert that closing bracket again; just move the selection
 4764                            // past the closing bracket.
 4765                            let should_skip = selection.end == region.range.end.to_point(&snapshot)
 4766                                && text.as_ref() == region.pair.end.as_str()
 4767                                && snapshot.contains_str_at(region.range.end, text.as_ref());
 4768                            if should_skip {
 4769                                let anchor = snapshot.anchor_after(selection.end);
 4770                                new_selections
 4771                                    .push((selection.map(|_| anchor), region.pair.end.len()));
 4772                                continue;
 4773                            }
 4774                        }
 4775
 4776                        let always_treat_brackets_as_autoclosed = snapshot
 4777                            .language_settings_at(selection.start, cx)
 4778                            .always_treat_brackets_as_autoclosed;
 4779                        if always_treat_brackets_as_autoclosed
 4780                            && is_bracket_pair_end
 4781                            && snapshot.contains_str_at(selection.end, text.as_ref())
 4782                        {
 4783                            // Otherwise, when `always_treat_brackets_as_autoclosed` is set to `true
 4784                            // and the inserted text is a closing bracket and the selection is followed
 4785                            // by the closing bracket then move the selection past the closing bracket.
 4786                            let anchor = snapshot.anchor_after(selection.end);
 4787                            new_selections.push((selection.map(|_| anchor), text.len()));
 4788                            continue;
 4789                        }
 4790                    }
 4791                    // If an opening bracket is 1 character long and is typed while
 4792                    // text is selected, then surround that text with the bracket pair.
 4793                    else if auto_surround
 4794                        && bracket_pair.surround
 4795                        && is_bracket_pair_start
 4796                        && bracket_pair.start.chars().count() == 1
 4797                    {
 4798                        edits.push((selection.start..selection.start, text.clone()));
 4799                        edits.push((
 4800                            selection.end..selection.end,
 4801                            bracket_pair.end.as_str().into(),
 4802                        ));
 4803                        bracket_inserted = true;
 4804                        new_selections.push((
 4805                            Selection {
 4806                                id: selection.id,
 4807                                start: snapshot.anchor_after(selection.start),
 4808                                end: snapshot.anchor_before(selection.end),
 4809                                reversed: selection.reversed,
 4810                                goal: selection.goal,
 4811                            },
 4812                            0,
 4813                        ));
 4814                        continue;
 4815                    }
 4816                }
 4817            }
 4818
 4819            if self.auto_replace_emoji_shortcode
 4820                && selection.is_empty()
 4821                && text.as_ref().ends_with(':')
 4822                && let Some(possible_emoji_short_code) =
 4823                    Self::find_possible_emoji_shortcode_at_position(&snapshot, selection.start)
 4824                && !possible_emoji_short_code.is_empty()
 4825                && let Some(emoji) = emojis::get_by_shortcode(&possible_emoji_short_code)
 4826            {
 4827                let emoji_shortcode_start = Point::new(
 4828                    selection.start.row,
 4829                    selection.start.column - possible_emoji_short_code.len() as u32 - 1,
 4830                );
 4831
 4832                // Remove shortcode from buffer
 4833                edits.push((
 4834                    emoji_shortcode_start..selection.start,
 4835                    "".to_string().into(),
 4836                ));
 4837                new_selections.push((
 4838                    Selection {
 4839                        id: selection.id,
 4840                        start: snapshot.anchor_after(emoji_shortcode_start),
 4841                        end: snapshot.anchor_before(selection.start),
 4842                        reversed: selection.reversed,
 4843                        goal: selection.goal,
 4844                    },
 4845                    0,
 4846                ));
 4847
 4848                // Insert emoji
 4849                let selection_start_anchor = snapshot.anchor_after(selection.start);
 4850                new_selections.push((selection.map(|_| selection_start_anchor), 0));
 4851                edits.push((selection.start..selection.end, emoji.to_string().into()));
 4852
 4853                continue;
 4854            }
 4855
 4856            let next_is_adjacent = regions
 4857                .peek()
 4858                .is_some_and(|(next, _)| selection.end == next.start);
 4859
 4860            // If not handling any auto-close operation, then just replace the selected
 4861            // text with the given input and move the selection to the end of the
 4862            // newly inserted text.
 4863            let anchor = if in_adjacent_group || next_is_adjacent {
 4864                // After edits the right bias would shift those anchor to the next visible fragment
 4865                // but we want to resolve to the previous one
 4866                snapshot.anchor_before(selection.end)
 4867            } else {
 4868                snapshot.anchor_after(selection.end)
 4869            };
 4870
 4871            if !self.linked_edit_ranges.is_empty() {
 4872                let start_anchor = snapshot.anchor_before(selection.start);
 4873
 4874                let is_word_char = text.chars().next().is_none_or(|char| {
 4875                    let classifier = snapshot
 4876                        .char_classifier_at(start_anchor.to_offset(&snapshot))
 4877                        .scope_context(Some(CharScopeContext::LinkedEdit));
 4878                    classifier.is_word(char)
 4879                });
 4880
 4881                if is_word_char {
 4882                    if let Some(ranges) = self
 4883                        .linked_editing_ranges_for(start_anchor.text_anchor..anchor.text_anchor, cx)
 4884                    {
 4885                        for (buffer, edits) in ranges {
 4886                            linked_edits
 4887                                .entry(buffer.clone())
 4888                                .or_default()
 4889                                .extend(edits.into_iter().map(|range| (range, text.clone())));
 4890                        }
 4891                    }
 4892                } else {
 4893                    clear_linked_edit_ranges = true;
 4894                }
 4895            }
 4896
 4897            new_selections.push((selection.map(|_| anchor), 0));
 4898            edits.push((selection.start..selection.end, text.clone()));
 4899
 4900            has_adjacent_edits |= next_is_adjacent;
 4901            in_adjacent_group = next_is_adjacent;
 4902        }
 4903
 4904        if all_selections_read_only {
 4905            return;
 4906        }
 4907
 4908        drop(regions);
 4909        drop(snapshot);
 4910
 4911        self.transact(window, cx, |this, window, cx| {
 4912            if clear_linked_edit_ranges {
 4913                this.linked_edit_ranges.clear();
 4914            }
 4915            let initial_buffer_versions =
 4916                jsx_tag_auto_close::construct_initial_buffer_versions_map(this, &edits, cx);
 4917
 4918            this.buffer.update(cx, |buffer, cx| {
 4919                if has_adjacent_edits {
 4920                    buffer.edit_non_coalesce(edits, this.autoindent_mode.clone(), cx);
 4921                } else {
 4922                    buffer.edit(edits, this.autoindent_mode.clone(), cx);
 4923                }
 4924            });
 4925            for (buffer, edits) in linked_edits {
 4926                buffer.update(cx, |buffer, cx| {
 4927                    let snapshot = buffer.snapshot();
 4928                    let edits = edits
 4929                        .into_iter()
 4930                        .map(|(range, text)| {
 4931                            use text::ToPoint as TP;
 4932                            let end_point = TP::to_point(&range.end, &snapshot);
 4933                            let start_point = TP::to_point(&range.start, &snapshot);
 4934                            (start_point..end_point, text)
 4935                        })
 4936                        .sorted_by_key(|(range, _)| range.start);
 4937                    buffer.edit(edits, None, cx);
 4938                })
 4939            }
 4940            let new_anchor_selections = new_selections.iter().map(|e| &e.0);
 4941            let new_selection_deltas = new_selections.iter().map(|e| e.1);
 4942            let map = this.display_map.update(cx, |map, cx| map.snapshot(cx));
 4943            let new_selections = resolve_selections_wrapping_blocks::<MultiBufferOffset, _>(
 4944                new_anchor_selections,
 4945                &map,
 4946            )
 4947            .zip(new_selection_deltas)
 4948            .map(|(selection, delta)| Selection {
 4949                id: selection.id,
 4950                start: selection.start + delta,
 4951                end: selection.end + delta,
 4952                reversed: selection.reversed,
 4953                goal: SelectionGoal::None,
 4954            })
 4955            .collect::<Vec<_>>();
 4956
 4957            let mut i = 0;
 4958            for (position, delta, selection_id, pair) in new_autoclose_regions {
 4959                let position = position.to_offset(map.buffer_snapshot()) + delta;
 4960                let start = map.buffer_snapshot().anchor_before(position);
 4961                let end = map.buffer_snapshot().anchor_after(position);
 4962                while let Some(existing_state) = this.autoclose_regions.get(i) {
 4963                    match existing_state
 4964                        .range
 4965                        .start
 4966                        .cmp(&start, map.buffer_snapshot())
 4967                    {
 4968                        Ordering::Less => i += 1,
 4969                        Ordering::Greater => break,
 4970                        Ordering::Equal => {
 4971                            match end.cmp(&existing_state.range.end, map.buffer_snapshot()) {
 4972                                Ordering::Less => i += 1,
 4973                                Ordering::Equal => break,
 4974                                Ordering::Greater => break,
 4975                            }
 4976                        }
 4977                    }
 4978                }
 4979                this.autoclose_regions.insert(
 4980                    i,
 4981                    AutocloseRegion {
 4982                        selection_id,
 4983                        range: start..end,
 4984                        pair,
 4985                    },
 4986                );
 4987            }
 4988
 4989            let had_active_edit_prediction = this.has_active_edit_prediction();
 4990            this.change_selections(
 4991                SelectionEffects::scroll(Autoscroll::fit()).completions(false),
 4992                window,
 4993                cx,
 4994                |s| s.select(new_selections),
 4995            );
 4996
 4997            if !bracket_inserted
 4998                && let Some(on_type_format_task) =
 4999                    this.trigger_on_type_formatting(text.to_string(), window, cx)
 5000            {
 5001                on_type_format_task.detach_and_log_err(cx);
 5002            }
 5003
 5004            let editor_settings = EditorSettings::get_global(cx);
 5005            if bracket_inserted
 5006                && (editor_settings.auto_signature_help
 5007                    || editor_settings.show_signature_help_after_edits)
 5008            {
 5009                this.show_signature_help(&ShowSignatureHelp, window, cx);
 5010            }
 5011
 5012            let trigger_in_words =
 5013                this.show_edit_predictions_in_menu() || !had_active_edit_prediction;
 5014            if this.hard_wrap.is_some() {
 5015                let latest: Range<Point> = this.selections.newest(&map).range();
 5016                if latest.is_empty()
 5017                    && this
 5018                        .buffer()
 5019                        .read(cx)
 5020                        .snapshot(cx)
 5021                        .line_len(MultiBufferRow(latest.start.row))
 5022                        == latest.start.column
 5023                {
 5024                    this.rewrap_impl(
 5025                        RewrapOptions {
 5026                            override_language_settings: true,
 5027                            preserve_existing_whitespace: true,
 5028                        },
 5029                        cx,
 5030                    )
 5031                }
 5032            }
 5033            this.trigger_completion_on_input(&text, trigger_in_words, window, cx);
 5034            refresh_linked_ranges(this, window, cx);
 5035            this.refresh_edit_prediction(true, false, window, cx);
 5036            jsx_tag_auto_close::handle_from(this, initial_buffer_versions, window, cx);
 5037        });
 5038    }
 5039
 5040    fn find_possible_emoji_shortcode_at_position(
 5041        snapshot: &MultiBufferSnapshot,
 5042        position: Point,
 5043    ) -> Option<String> {
 5044        let mut chars = Vec::new();
 5045        let mut found_colon = false;
 5046        for char in snapshot.reversed_chars_at(position).take(100) {
 5047            // Found a possible emoji shortcode in the middle of the buffer
 5048            if found_colon {
 5049                if char.is_whitespace() {
 5050                    chars.reverse();
 5051                    return Some(chars.iter().collect());
 5052                }
 5053                // If the previous character is not a whitespace, we are in the middle of a word
 5054                // and we only want to complete the shortcode if the word is made up of other emojis
 5055                let mut containing_word = String::new();
 5056                for ch in snapshot
 5057                    .reversed_chars_at(position)
 5058                    .skip(chars.len() + 1)
 5059                    .take(100)
 5060                {
 5061                    if ch.is_whitespace() {
 5062                        break;
 5063                    }
 5064                    containing_word.push(ch);
 5065                }
 5066                let containing_word = containing_word.chars().rev().collect::<String>();
 5067                if util::word_consists_of_emojis(containing_word.as_str()) {
 5068                    chars.reverse();
 5069                    return Some(chars.iter().collect());
 5070                }
 5071            }
 5072
 5073            if char.is_whitespace() || !char.is_ascii() {
 5074                return None;
 5075            }
 5076            if char == ':' {
 5077                found_colon = true;
 5078            } else {
 5079                chars.push(char);
 5080            }
 5081        }
 5082        // Found a possible emoji shortcode at the beginning of the buffer
 5083        chars.reverse();
 5084        Some(chars.iter().collect())
 5085    }
 5086
 5087    pub fn newline(&mut self, _: &Newline, window: &mut Window, cx: &mut Context<Self>) {
 5088        self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
 5089        self.transact(window, cx, |this, window, cx| {
 5090            let (edits_with_flags, selection_info): (Vec<_>, Vec<_>) = {
 5091                let selections = this
 5092                    .selections
 5093                    .all::<MultiBufferOffset>(&this.display_snapshot(cx));
 5094                let multi_buffer = this.buffer.read(cx);
 5095                let buffer = multi_buffer.snapshot(cx);
 5096                selections
 5097                    .iter()
 5098                    .map(|selection| {
 5099                        let start_point = selection.start.to_point(&buffer);
 5100                        let mut existing_indent =
 5101                            buffer.indent_size_for_line(MultiBufferRow(start_point.row));
 5102                        existing_indent.len = cmp::min(existing_indent.len, start_point.column);
 5103                        let start = selection.start;
 5104                        let end = selection.end;
 5105                        let selection_is_empty = start == end;
 5106                        let language_scope = buffer.language_scope_at(start);
 5107                        let (delimiter, newline_config) = if let Some(language) = &language_scope {
 5108                            let needs_extra_newline = NewlineConfig::insert_extra_newline_brackets(
 5109                                &buffer,
 5110                                start..end,
 5111                                language,
 5112                            )
 5113                                || NewlineConfig::insert_extra_newline_tree_sitter(
 5114                                    &buffer,
 5115                                    start..end,
 5116                                );
 5117
 5118                            let mut newline_config = NewlineConfig::Newline {
 5119                                additional_indent: IndentSize::spaces(0),
 5120                                extra_line_additional_indent: if needs_extra_newline {
 5121                                    Some(IndentSize::spaces(0))
 5122                                } else {
 5123                                    None
 5124                                },
 5125                                prevent_auto_indent: false,
 5126                            };
 5127
 5128                            let comment_delimiter = maybe!({
 5129                                if !selection_is_empty {
 5130                                    return None;
 5131                                }
 5132
 5133                                if !multi_buffer.language_settings(cx).extend_comment_on_newline {
 5134                                    return None;
 5135                                }
 5136
 5137                                return comment_delimiter_for_newline(
 5138                                    &start_point,
 5139                                    &buffer,
 5140                                    language,
 5141                                );
 5142                            });
 5143
 5144                            let doc_delimiter = maybe!({
 5145                                if !selection_is_empty {
 5146                                    return None;
 5147                                }
 5148
 5149                                if !multi_buffer.language_settings(cx).extend_comment_on_newline {
 5150                                    return None;
 5151                                }
 5152
 5153                                return documentation_delimiter_for_newline(
 5154                                    &start_point,
 5155                                    &buffer,
 5156                                    language,
 5157                                    &mut newline_config,
 5158                                );
 5159                            });
 5160
 5161                            let list_delimiter = maybe!({
 5162                                if !selection_is_empty {
 5163                                    return None;
 5164                                }
 5165
 5166                                if !multi_buffer.language_settings(cx).extend_list_on_newline {
 5167                                    return None;
 5168                                }
 5169
 5170                                return list_delimiter_for_newline(
 5171                                    &start_point,
 5172                                    &buffer,
 5173                                    language,
 5174                                    &mut newline_config,
 5175                                );
 5176                            });
 5177
 5178                            (
 5179                                comment_delimiter.or(doc_delimiter).or(list_delimiter),
 5180                                newline_config,
 5181                            )
 5182                        } else {
 5183                            (
 5184                                None,
 5185                                NewlineConfig::Newline {
 5186                                    additional_indent: IndentSize::spaces(0),
 5187                                    extra_line_additional_indent: None,
 5188                                    prevent_auto_indent: false,
 5189                                },
 5190                            )
 5191                        };
 5192
 5193                        let (edit_start, new_text, prevent_auto_indent) = match &newline_config {
 5194                            NewlineConfig::ClearCurrentLine => {
 5195                                let row_start =
 5196                                    buffer.point_to_offset(Point::new(start_point.row, 0));
 5197                                (row_start, String::new(), false)
 5198                            }
 5199                            NewlineConfig::UnindentCurrentLine { continuation } => {
 5200                                let row_start =
 5201                                    buffer.point_to_offset(Point::new(start_point.row, 0));
 5202                                let tab_size = buffer.language_settings_at(start, cx).tab_size;
 5203                                let tab_size_indent = IndentSize::spaces(tab_size.get());
 5204                                let reduced_indent =
 5205                                    existing_indent.with_delta(Ordering::Less, tab_size_indent);
 5206                                let mut new_text = String::new();
 5207                                new_text.extend(reduced_indent.chars());
 5208                                new_text.push_str(continuation);
 5209                                (row_start, new_text, true)
 5210                            }
 5211                            NewlineConfig::Newline {
 5212                                additional_indent,
 5213                                extra_line_additional_indent,
 5214                                prevent_auto_indent,
 5215                            } => {
 5216                                let capacity_for_delimiter =
 5217                                    delimiter.as_deref().map(str::len).unwrap_or_default();
 5218                                let extra_line_len = extra_line_additional_indent
 5219                                    .map(|i| 1 + existing_indent.len as usize + i.len as usize)
 5220                                    .unwrap_or(0);
 5221                                let mut new_text = String::with_capacity(
 5222                                    1 + capacity_for_delimiter
 5223                                        + existing_indent.len as usize
 5224                                        + additional_indent.len as usize
 5225                                        + extra_line_len,
 5226                                );
 5227                                new_text.push('\n');
 5228                                new_text.extend(existing_indent.chars());
 5229                                new_text.extend(additional_indent.chars());
 5230                                if let Some(delimiter) = &delimiter {
 5231                                    new_text.push_str(delimiter);
 5232                                }
 5233                                if let Some(extra_indent) = extra_line_additional_indent {
 5234                                    new_text.push('\n');
 5235                                    new_text.extend(existing_indent.chars());
 5236                                    new_text.extend(extra_indent.chars());
 5237                                }
 5238                                (start, new_text, *prevent_auto_indent)
 5239                            }
 5240                        };
 5241
 5242                        let anchor = buffer.anchor_after(end);
 5243                        let new_selection = selection.map(|_| anchor);
 5244                        (
 5245                            ((edit_start..end, new_text), prevent_auto_indent),
 5246                            (newline_config.has_extra_line(), new_selection),
 5247                        )
 5248                    })
 5249                    .unzip()
 5250            };
 5251
 5252            let mut auto_indent_edits = Vec::new();
 5253            let mut edits = Vec::new();
 5254            for (edit, prevent_auto_indent) in edits_with_flags {
 5255                if prevent_auto_indent {
 5256                    edits.push(edit);
 5257                } else {
 5258                    auto_indent_edits.push(edit);
 5259                }
 5260            }
 5261            if !edits.is_empty() {
 5262                this.edit(edits, cx);
 5263            }
 5264            if !auto_indent_edits.is_empty() {
 5265                this.edit_with_autoindent(auto_indent_edits, cx);
 5266            }
 5267
 5268            let buffer = this.buffer.read(cx).snapshot(cx);
 5269            let new_selections = selection_info
 5270                .into_iter()
 5271                .map(|(extra_newline_inserted, new_selection)| {
 5272                    let mut cursor = new_selection.end.to_point(&buffer);
 5273                    if extra_newline_inserted {
 5274                        cursor.row -= 1;
 5275                        cursor.column = buffer.line_len(MultiBufferRow(cursor.row));
 5276                    }
 5277                    new_selection.map(|_| cursor)
 5278                })
 5279                .collect();
 5280
 5281            this.change_selections(Default::default(), window, cx, |s| s.select(new_selections));
 5282            this.refresh_edit_prediction(true, false, window, cx);
 5283            if let Some(task) = this.trigger_on_type_formatting("\n".to_owned(), window, cx) {
 5284                task.detach_and_log_err(cx);
 5285            }
 5286        });
 5287    }
 5288
 5289    pub fn newline_above(&mut self, _: &NewlineAbove, window: &mut Window, cx: &mut Context<Self>) {
 5290        self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
 5291
 5292        let buffer = self.buffer.read(cx);
 5293        let snapshot = buffer.snapshot(cx);
 5294
 5295        let mut edits = Vec::new();
 5296        let mut rows = Vec::new();
 5297
 5298        for (rows_inserted, selection) in self
 5299            .selections
 5300            .all_adjusted(&self.display_snapshot(cx))
 5301            .into_iter()
 5302            .enumerate()
 5303        {
 5304            let cursor = selection.head();
 5305            let row = cursor.row;
 5306
 5307            let start_of_line = snapshot.clip_point(Point::new(row, 0), Bias::Left);
 5308
 5309            let newline = "\n".to_string();
 5310            edits.push((start_of_line..start_of_line, newline));
 5311
 5312            rows.push(row + rows_inserted as u32);
 5313        }
 5314
 5315        self.transact(window, cx, |editor, window, cx| {
 5316            editor.edit(edits, cx);
 5317
 5318            editor.change_selections(Default::default(), window, cx, |s| {
 5319                let mut index = 0;
 5320                s.move_cursors_with(&mut |map, _, _| {
 5321                    let row = rows[index];
 5322                    index += 1;
 5323
 5324                    let point = Point::new(row, 0);
 5325                    let boundary = map.next_line_boundary(point).1;
 5326                    let clipped = map.clip_point(boundary, Bias::Left);
 5327
 5328                    (clipped, SelectionGoal::None)
 5329                });
 5330            });
 5331
 5332            let mut indent_edits = Vec::new();
 5333            let multibuffer_snapshot = editor.buffer.read(cx).snapshot(cx);
 5334            for row in rows {
 5335                let indents = multibuffer_snapshot.suggested_indents(row..row + 1, cx);
 5336                for (row, indent) in indents {
 5337                    if indent.len == 0 {
 5338                        continue;
 5339                    }
 5340
 5341                    let text = match indent.kind {
 5342                        IndentKind::Space => " ".repeat(indent.len as usize),
 5343                        IndentKind::Tab => "\t".repeat(indent.len as usize),
 5344                    };
 5345                    let point = Point::new(row.0, 0);
 5346                    indent_edits.push((point..point, text));
 5347                }
 5348            }
 5349            editor.edit(indent_edits, cx);
 5350            if let Some(format) = editor.trigger_on_type_formatting("\n".to_owned(), window, cx) {
 5351                format.detach_and_log_err(cx);
 5352            }
 5353        });
 5354    }
 5355
 5356    pub fn newline_below(&mut self, _: &NewlineBelow, window: &mut Window, cx: &mut Context<Self>) {
 5357        self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
 5358
 5359        let mut buffer_edits: HashMap<EntityId, (Entity<Buffer>, Vec<Point>)> = HashMap::default();
 5360        let mut rows = Vec::new();
 5361        let mut rows_inserted = 0;
 5362
 5363        for selection in self.selections.all_adjusted(&self.display_snapshot(cx)) {
 5364            let cursor = selection.head();
 5365            let row = cursor.row;
 5366
 5367            let point = Point::new(row, 0);
 5368            let Some((buffer_handle, buffer_point, _)) =
 5369                self.buffer.read(cx).point_to_buffer_point(point, cx)
 5370            else {
 5371                continue;
 5372            };
 5373
 5374            buffer_edits
 5375                .entry(buffer_handle.entity_id())
 5376                .or_insert_with(|| (buffer_handle, Vec::new()))
 5377                .1
 5378                .push(buffer_point);
 5379
 5380            rows_inserted += 1;
 5381            rows.push(row + rows_inserted);
 5382        }
 5383
 5384        self.transact(window, cx, |editor, window, cx| {
 5385            for (_, (buffer_handle, points)) in &buffer_edits {
 5386                buffer_handle.update(cx, |buffer, cx| {
 5387                    let edits: Vec<_> = points
 5388                        .iter()
 5389                        .map(|point| {
 5390                            let target = Point::new(point.row + 1, 0);
 5391                            let start_of_line = buffer.point_to_offset(target).min(buffer.len());
 5392                            (start_of_line..start_of_line, "\n")
 5393                        })
 5394                        .collect();
 5395                    buffer.edit(edits, None, cx);
 5396                });
 5397            }
 5398
 5399            editor.change_selections(Default::default(), window, cx, |s| {
 5400                let mut index = 0;
 5401                s.move_cursors_with(&mut |map, _, _| {
 5402                    let row = rows[index];
 5403                    index += 1;
 5404
 5405                    let point = Point::new(row, 0);
 5406                    let boundary = map.next_line_boundary(point).1;
 5407                    let clipped = map.clip_point(boundary, Bias::Left);
 5408
 5409                    (clipped, SelectionGoal::None)
 5410                });
 5411            });
 5412
 5413            let mut indent_edits = Vec::new();
 5414            let multibuffer_snapshot = editor.buffer.read(cx).snapshot(cx);
 5415            for row in rows {
 5416                let indents = multibuffer_snapshot.suggested_indents(row..row + 1, cx);
 5417                for (row, indent) in indents {
 5418                    if indent.len == 0 {
 5419                        continue;
 5420                    }
 5421
 5422                    let text = match indent.kind {
 5423                        IndentKind::Space => " ".repeat(indent.len as usize),
 5424                        IndentKind::Tab => "\t".repeat(indent.len as usize),
 5425                    };
 5426                    let point = Point::new(row.0, 0);
 5427                    indent_edits.push((point..point, text));
 5428                }
 5429            }
 5430            editor.edit(indent_edits, cx);
 5431            if let Some(format) = editor.trigger_on_type_formatting("\n".to_owned(), window, cx) {
 5432                format.detach_and_log_err(cx);
 5433            }
 5434        });
 5435    }
 5436
 5437    pub fn insert(&mut self, text: &str, window: &mut Window, cx: &mut Context<Self>) {
 5438        let autoindent = text.is_empty().not().then(|| AutoindentMode::Block {
 5439            original_indent_columns: Vec::new(),
 5440        });
 5441        self.insert_with_autoindent_mode(text, autoindent, window, cx);
 5442    }
 5443
 5444    fn insert_with_autoindent_mode(
 5445        &mut self,
 5446        text: &str,
 5447        autoindent_mode: Option<AutoindentMode>,
 5448        window: &mut Window,
 5449        cx: &mut Context<Self>,
 5450    ) {
 5451        if self.read_only(cx) {
 5452            return;
 5453        }
 5454
 5455        let text: Arc<str> = text.into();
 5456        self.transact(window, cx, |this, window, cx| {
 5457            let old_selections = this.selections.all_adjusted(&this.display_snapshot(cx));
 5458            let selection_anchors = this.buffer.update(cx, |buffer, cx| {
 5459                let anchors = {
 5460                    let snapshot = buffer.read(cx);
 5461                    old_selections
 5462                        .iter()
 5463                        .map(|s| {
 5464                            let anchor = snapshot.anchor_after(s.head());
 5465                            s.map(|_| anchor)
 5466                        })
 5467                        .collect::<Vec<_>>()
 5468                };
 5469                buffer.edit(
 5470                    old_selections
 5471                        .iter()
 5472                        .map(|s| (s.start..s.end, text.clone())),
 5473                    autoindent_mode,
 5474                    cx,
 5475                );
 5476                anchors
 5477            });
 5478
 5479            this.change_selections(Default::default(), window, cx, |s| {
 5480                s.select_anchors(selection_anchors);
 5481            });
 5482
 5483            cx.notify();
 5484        });
 5485    }
 5486
 5487    fn trigger_completion_on_input(
 5488        &mut self,
 5489        text: &str,
 5490        trigger_in_words: bool,
 5491        window: &mut Window,
 5492        cx: &mut Context<Self>,
 5493    ) {
 5494        let completions_source = self
 5495            .context_menu
 5496            .borrow()
 5497            .as_ref()
 5498            .and_then(|menu| match menu {
 5499                CodeContextMenu::Completions(completions_menu) => Some(completions_menu.source),
 5500                CodeContextMenu::CodeActions(_) => None,
 5501            });
 5502
 5503        match completions_source {
 5504            Some(CompletionsMenuSource::Words { .. }) => {
 5505                self.open_or_update_completions_menu(
 5506                    Some(CompletionsMenuSource::Words {
 5507                        ignore_threshold: false,
 5508                    }),
 5509                    None,
 5510                    trigger_in_words,
 5511                    window,
 5512                    cx,
 5513                );
 5514            }
 5515            _ => self.open_or_update_completions_menu(
 5516                None,
 5517                Some(text.to_owned()).filter(|x| !x.is_empty()),
 5518                true,
 5519                window,
 5520                cx,
 5521            ),
 5522        }
 5523    }
 5524
 5525    /// If any empty selections is touching the start of its innermost containing autoclose
 5526    /// region, expand it to select the brackets.
 5527    fn select_autoclose_pair(&mut self, window: &mut Window, cx: &mut Context<Self>) {
 5528        let selections = self
 5529            .selections
 5530            .all::<MultiBufferOffset>(&self.display_snapshot(cx));
 5531        let buffer = self.buffer.read(cx).read(cx);
 5532        let new_selections = self
 5533            .selections_with_autoclose_regions(selections, &buffer)
 5534            .map(|(mut selection, region)| {
 5535                if !selection.is_empty() {
 5536                    return selection;
 5537                }
 5538
 5539                if let Some(region) = region {
 5540                    let mut range = region.range.to_offset(&buffer);
 5541                    if selection.start == range.start && range.start.0 >= region.pair.start.len() {
 5542                        range.start -= region.pair.start.len();
 5543                        if buffer.contains_str_at(range.start, &region.pair.start)
 5544                            && buffer.contains_str_at(range.end, &region.pair.end)
 5545                        {
 5546                            range.end += region.pair.end.len();
 5547                            selection.start = range.start;
 5548                            selection.end = range.end;
 5549
 5550                            return selection;
 5551                        }
 5552                    }
 5553                }
 5554
 5555                let always_treat_brackets_as_autoclosed = buffer
 5556                    .language_settings_at(selection.start, cx)
 5557                    .always_treat_brackets_as_autoclosed;
 5558
 5559                if !always_treat_brackets_as_autoclosed {
 5560                    return selection;
 5561                }
 5562
 5563                if let Some(scope) = buffer.language_scope_at(selection.start) {
 5564                    for (pair, enabled) in scope.brackets() {
 5565                        if !enabled || !pair.close {
 5566                            continue;
 5567                        }
 5568
 5569                        if buffer.contains_str_at(selection.start, &pair.end) {
 5570                            let pair_start_len = pair.start.len();
 5571                            if buffer.contains_str_at(
 5572                                selection.start.saturating_sub_usize(pair_start_len),
 5573                                &pair.start,
 5574                            ) {
 5575                                selection.start -= pair_start_len;
 5576                                selection.end += pair.end.len();
 5577
 5578                                return selection;
 5579                            }
 5580                        }
 5581                    }
 5582                }
 5583
 5584                selection
 5585            })
 5586            .collect();
 5587
 5588        drop(buffer);
 5589        self.change_selections(SelectionEffects::no_scroll(), window, cx, |selections| {
 5590            selections.select(new_selections)
 5591        });
 5592    }
 5593
 5594    /// Iterate the given selections, and for each one, find the smallest surrounding
 5595    /// autoclose region. This uses the ordering of the selections and the autoclose
 5596    /// regions to avoid repeated comparisons.
 5597    fn selections_with_autoclose_regions<'a, D: ToOffset + Clone>(
 5598        &'a self,
 5599        selections: impl IntoIterator<Item = Selection<D>>,
 5600        buffer: &'a MultiBufferSnapshot,
 5601    ) -> impl Iterator<Item = (Selection<D>, Option<&'a AutocloseRegion>)> {
 5602        let mut i = 0;
 5603        let mut regions = self.autoclose_regions.as_slice();
 5604        selections.into_iter().map(move |selection| {
 5605            let range = selection.start.to_offset(buffer)..selection.end.to_offset(buffer);
 5606
 5607            let mut enclosing = None;
 5608            while let Some(pair_state) = regions.get(i) {
 5609                if pair_state.range.end.to_offset(buffer) < range.start {
 5610                    regions = &regions[i + 1..];
 5611                    i = 0;
 5612                } else if pair_state.range.start.to_offset(buffer) > range.end {
 5613                    break;
 5614                } else {
 5615                    if pair_state.selection_id == selection.id {
 5616                        enclosing = Some(pair_state);
 5617                    }
 5618                    i += 1;
 5619                }
 5620            }
 5621
 5622            (selection, enclosing)
 5623        })
 5624    }
 5625
 5626    /// Remove any autoclose regions that no longer contain their selection or have invalid anchors in ranges.
 5627    fn invalidate_autoclose_regions(
 5628        &mut self,
 5629        mut selections: &[Selection<Anchor>],
 5630        buffer: &MultiBufferSnapshot,
 5631    ) {
 5632        self.autoclose_regions.retain(|state| {
 5633            if !state.range.start.is_valid(buffer) || !state.range.end.is_valid(buffer) {
 5634                return false;
 5635            }
 5636
 5637            let mut i = 0;
 5638            while let Some(selection) = selections.get(i) {
 5639                if selection.end.cmp(&state.range.start, buffer).is_lt() {
 5640                    selections = &selections[1..];
 5641                    continue;
 5642                }
 5643                if selection.start.cmp(&state.range.end, buffer).is_gt() {
 5644                    break;
 5645                }
 5646                if selection.id == state.selection_id {
 5647                    return true;
 5648                } else {
 5649                    i += 1;
 5650                }
 5651            }
 5652            false
 5653        });
 5654    }
 5655
 5656    fn completion_query(buffer: &MultiBufferSnapshot, position: impl ToOffset) -> Option<String> {
 5657        let offset = position.to_offset(buffer);
 5658        let (word_range, kind) =
 5659            buffer.surrounding_word(offset, Some(CharScopeContext::Completion));
 5660        if offset > word_range.start && kind == Some(CharKind::Word) {
 5661            Some(
 5662                buffer
 5663                    .text_for_range(word_range.start..offset)
 5664                    .collect::<String>(),
 5665            )
 5666        } else {
 5667            None
 5668        }
 5669    }
 5670
 5671    pub fn visible_excerpts(
 5672        &self,
 5673        lsp_related_only: bool,
 5674        cx: &mut Context<Editor>,
 5675    ) -> HashMap<ExcerptId, (Entity<Buffer>, clock::Global, Range<usize>)> {
 5676        let project = self.project().cloned();
 5677        let display_snapshot = self.display_map.update(cx, |map, cx| map.snapshot(cx));
 5678        let multi_buffer = self.buffer().read(cx);
 5679        let multi_buffer_snapshot = multi_buffer.snapshot(cx);
 5680        let multi_buffer_visible_start = self
 5681            .scroll_manager
 5682            .native_anchor(&display_snapshot, cx)
 5683            .anchor
 5684            .to_point(&multi_buffer_snapshot);
 5685        let multi_buffer_visible_end = multi_buffer_snapshot.clip_point(
 5686            multi_buffer_visible_start
 5687                + Point::new(self.visible_line_count().unwrap_or(0.).ceil() as u32, 0),
 5688            Bias::Left,
 5689        );
 5690        multi_buffer_snapshot
 5691            .range_to_buffer_ranges(multi_buffer_visible_start..=multi_buffer_visible_end)
 5692            .into_iter()
 5693            .filter(|(_, excerpt_visible_range, _)| !excerpt_visible_range.is_empty())
 5694            .filter_map(|(buffer, excerpt_visible_range, excerpt_id)| {
 5695                if !lsp_related_only {
 5696                    return Some((
 5697                        excerpt_id,
 5698                        (
 5699                            multi_buffer.buffer(buffer.remote_id()).unwrap(),
 5700                            buffer.version().clone(),
 5701                            excerpt_visible_range.start.0..excerpt_visible_range.end.0,
 5702                        ),
 5703                    ));
 5704                }
 5705
 5706                let project = project.as_ref()?.read(cx);
 5707                let buffer_file = project::File::from_dyn(buffer.file())?;
 5708                let buffer_worktree = project.worktree_for_id(buffer_file.worktree_id(cx), cx)?;
 5709                let worktree_entry = buffer_worktree
 5710                    .read(cx)
 5711                    .entry_for_id(buffer_file.project_entry_id()?)?;
 5712                if worktree_entry.is_ignored {
 5713                    None
 5714                } else {
 5715                    Some((
 5716                        excerpt_id,
 5717                        (
 5718                            multi_buffer.buffer(buffer.remote_id()).unwrap(),
 5719                            buffer.version().clone(),
 5720                            excerpt_visible_range.start.0..excerpt_visible_range.end.0,
 5721                        ),
 5722                    ))
 5723                }
 5724            })
 5725            .collect()
 5726    }
 5727
 5728    pub fn text_layout_details(&self, window: &mut Window, cx: &mut App) -> TextLayoutDetails {
 5729        TextLayoutDetails {
 5730            text_system: window.text_system().clone(),
 5731            editor_style: self.style.clone().unwrap(),
 5732            rem_size: window.rem_size(),
 5733            scroll_anchor: self.scroll_manager.shared_scroll_anchor(cx),
 5734            visible_rows: self.visible_line_count(),
 5735            vertical_scroll_margin: self.scroll_manager.vertical_scroll_margin,
 5736        }
 5737    }
 5738
 5739    fn trigger_on_type_formatting(
 5740        &self,
 5741        input: String,
 5742        window: &mut Window,
 5743        cx: &mut Context<Self>,
 5744    ) -> Option<Task<Result<()>>> {
 5745        if input.chars().count() != 1 {
 5746            return None;
 5747        }
 5748
 5749        let project = self.project()?;
 5750        let position = self.selections.newest_anchor().head();
 5751        let (buffer, buffer_position) = self
 5752            .buffer
 5753            .read(cx)
 5754            .text_anchor_for_position(position, cx)?;
 5755
 5756        let settings = language_settings::language_settings(
 5757            buffer
 5758                .read(cx)
 5759                .language_at(buffer_position)
 5760                .map(|l| l.name()),
 5761            buffer.read(cx).file(),
 5762            cx,
 5763        );
 5764        if !settings.use_on_type_format {
 5765            return None;
 5766        }
 5767
 5768        // OnTypeFormatting returns a list of edits, no need to pass them between Zed instances,
 5769        // hence we do LSP request & edit on host side only — add formats to host's history.
 5770        let push_to_lsp_host_history = true;
 5771        // If this is not the host, append its history with new edits.
 5772        let push_to_client_history = project.read(cx).is_via_collab();
 5773
 5774        let on_type_formatting = project.update(cx, |project, cx| {
 5775            project.on_type_format(
 5776                buffer.clone(),
 5777                buffer_position,
 5778                input,
 5779                push_to_lsp_host_history,
 5780                cx,
 5781            )
 5782        });
 5783        Some(cx.spawn_in(window, async move |editor, cx| {
 5784            if let Some(transaction) = on_type_formatting.await? {
 5785                if push_to_client_history {
 5786                    buffer.update(cx, |buffer, _| {
 5787                        buffer.push_transaction(transaction, Instant::now());
 5788                        buffer.finalize_last_transaction();
 5789                    });
 5790                }
 5791                editor.update(cx, |editor, cx| {
 5792                    editor.refresh_document_highlights(cx);
 5793                })?;
 5794            }
 5795            Ok(())
 5796        }))
 5797    }
 5798
 5799    pub fn show_word_completions(
 5800        &mut self,
 5801        _: &ShowWordCompletions,
 5802        window: &mut Window,
 5803        cx: &mut Context<Self>,
 5804    ) {
 5805        self.open_or_update_completions_menu(
 5806            Some(CompletionsMenuSource::Words {
 5807                ignore_threshold: true,
 5808            }),
 5809            None,
 5810            false,
 5811            window,
 5812            cx,
 5813        );
 5814    }
 5815
 5816    pub fn show_completions(
 5817        &mut self,
 5818        _: &ShowCompletions,
 5819        window: &mut Window,
 5820        cx: &mut Context<Self>,
 5821    ) {
 5822        self.open_or_update_completions_menu(None, None, false, window, cx);
 5823    }
 5824
 5825    fn open_or_update_completions_menu(
 5826        &mut self,
 5827        requested_source: Option<CompletionsMenuSource>,
 5828        trigger: Option<String>,
 5829        trigger_in_words: bool,
 5830        window: &mut Window,
 5831        cx: &mut Context<Self>,
 5832    ) {
 5833        if self.pending_rename.is_some() {
 5834            return;
 5835        }
 5836
 5837        let completions_source = self
 5838            .context_menu
 5839            .borrow()
 5840            .as_ref()
 5841            .and_then(|menu| match menu {
 5842                CodeContextMenu::Completions(completions_menu) => Some(completions_menu.source),
 5843                CodeContextMenu::CodeActions(_) => None,
 5844            });
 5845
 5846        let multibuffer_snapshot = self.buffer.read(cx).read(cx);
 5847
 5848        // Typically `start` == `end`, but with snippet tabstop choices the default choice is
 5849        // inserted and selected. To handle that case, the start of the selection is used so that
 5850        // the menu starts with all choices.
 5851        let position = self
 5852            .selections
 5853            .newest_anchor()
 5854            .start
 5855            .bias_right(&multibuffer_snapshot);
 5856        if position.diff_base_anchor.is_some() {
 5857            return;
 5858        }
 5859        let buffer_position = multibuffer_snapshot.anchor_before(position);
 5860        let Some(buffer) = buffer_position
 5861            .text_anchor
 5862            .buffer_id
 5863            .and_then(|buffer_id| self.buffer.read(cx).buffer(buffer_id))
 5864        else {
 5865            return;
 5866        };
 5867        let buffer_snapshot = buffer.read(cx).snapshot();
 5868
 5869        let menu_is_open = matches!(
 5870            self.context_menu.borrow().as_ref(),
 5871            Some(CodeContextMenu::Completions(_))
 5872        );
 5873
 5874        let language = buffer_snapshot
 5875            .language_at(buffer_position.text_anchor)
 5876            .map(|language| language.name());
 5877
 5878        let language_settings = language_settings(language.clone(), buffer_snapshot.file(), cx);
 5879        let completion_settings = language_settings.completions.clone();
 5880
 5881        let show_completions_on_input = self
 5882            .show_completions_on_input_override
 5883            .unwrap_or(language_settings.show_completions_on_input);
 5884        if !menu_is_open && trigger.is_some() && !show_completions_on_input {
 5885            return;
 5886        }
 5887
 5888        let query: Option<Arc<String>> =
 5889            Self::completion_query(&multibuffer_snapshot, buffer_position)
 5890                .map(|query| query.into());
 5891
 5892        drop(multibuffer_snapshot);
 5893
 5894        // Hide the current completions menu when query is empty. Without this, cached
 5895        // completions from before the trigger char may be reused (#32774).
 5896        if query.is_none() && menu_is_open {
 5897            self.hide_context_menu(window, cx);
 5898        }
 5899
 5900        let mut ignore_word_threshold = false;
 5901        let provider = match requested_source {
 5902            Some(CompletionsMenuSource::Normal) | None => self.completion_provider.clone(),
 5903            Some(CompletionsMenuSource::Words { ignore_threshold }) => {
 5904                ignore_word_threshold = ignore_threshold;
 5905                None
 5906            }
 5907            Some(CompletionsMenuSource::SnippetChoices)
 5908            | Some(CompletionsMenuSource::SnippetsOnly) => {
 5909                log::error!("bug: SnippetChoices requested_source is not handled");
 5910                None
 5911            }
 5912        };
 5913
 5914        let sort_completions = provider
 5915            .as_ref()
 5916            .is_some_and(|provider| provider.sort_completions());
 5917
 5918        let filter_completions = provider
 5919            .as_ref()
 5920            .is_none_or(|provider| provider.filter_completions());
 5921
 5922        let was_snippets_only = matches!(
 5923            completions_source,
 5924            Some(CompletionsMenuSource::SnippetsOnly)
 5925        );
 5926
 5927        if let Some(CodeContextMenu::Completions(menu)) = self.context_menu.borrow_mut().as_mut() {
 5928            if filter_completions {
 5929                menu.filter(
 5930                    query.clone().unwrap_or_default(),
 5931                    buffer_position.text_anchor,
 5932                    &buffer,
 5933                    provider.clone(),
 5934                    window,
 5935                    cx,
 5936                );
 5937            }
 5938            // When `is_incomplete` is false, no need to re-query completions when the current query
 5939            // is a suffix of the initial query.
 5940            let was_complete = !menu.is_incomplete;
 5941            if was_complete && !was_snippets_only {
 5942                // If the new query is a suffix of the old query (typing more characters) and
 5943                // the previous result was complete, the existing completions can be filtered.
 5944                //
 5945                // Note that snippet completions are always complete.
 5946                let query_matches = match (&menu.initial_query, &query) {
 5947                    (Some(initial_query), Some(query)) => query.starts_with(initial_query.as_ref()),
 5948                    (None, _) => true,
 5949                    _ => false,
 5950                };
 5951                if query_matches {
 5952                    let position_matches = if menu.initial_position == position {
 5953                        true
 5954                    } else {
 5955                        let snapshot = self.buffer.read(cx).read(cx);
 5956                        menu.initial_position.to_offset(&snapshot) == position.to_offset(&snapshot)
 5957                    };
 5958                    if position_matches {
 5959                        return;
 5960                    }
 5961                }
 5962            }
 5963        };
 5964
 5965        let Anchor {
 5966            excerpt_id: buffer_excerpt_id,
 5967            text_anchor: buffer_position,
 5968            ..
 5969        } = buffer_position;
 5970
 5971        let (word_replace_range, word_to_exclude) = if let (word_range, Some(CharKind::Word)) =
 5972            buffer_snapshot.surrounding_word(buffer_position, None)
 5973        {
 5974            let word_to_exclude = buffer_snapshot
 5975                .text_for_range(word_range.clone())
 5976                .collect::<String>();
 5977            (
 5978                buffer_snapshot.anchor_before(word_range.start)
 5979                    ..buffer_snapshot.anchor_after(buffer_position),
 5980                Some(word_to_exclude),
 5981            )
 5982        } else {
 5983            (buffer_position..buffer_position, None)
 5984        };
 5985
 5986        let show_completion_documentation = buffer_snapshot
 5987            .settings_at(buffer_position, cx)
 5988            .show_completion_documentation;
 5989
 5990        // The document can be large, so stay in reasonable bounds when searching for words,
 5991        // otherwise completion pop-up might be slow to appear.
 5992        const WORD_LOOKUP_ROWS: u32 = 5_000;
 5993        let buffer_row = text::ToPoint::to_point(&buffer_position, &buffer_snapshot).row;
 5994        let min_word_search = buffer_snapshot.clip_point(
 5995            Point::new(buffer_row.saturating_sub(WORD_LOOKUP_ROWS), 0),
 5996            Bias::Left,
 5997        );
 5998        let max_word_search = buffer_snapshot.clip_point(
 5999            Point::new(buffer_row + WORD_LOOKUP_ROWS, 0).min(buffer_snapshot.max_point()),
 6000            Bias::Right,
 6001        );
 6002        let word_search_range = buffer_snapshot.point_to_offset(min_word_search)
 6003            ..buffer_snapshot.point_to_offset(max_word_search);
 6004
 6005        let skip_digits = query
 6006            .as_ref()
 6007            .is_none_or(|query| !query.chars().any(|c| c.is_digit(10)));
 6008
 6009        let load_provider_completions = provider.as_ref().is_some_and(|provider| {
 6010            trigger.as_ref().is_none_or(|trigger| {
 6011                provider.is_completion_trigger(
 6012                    &buffer,
 6013                    position.text_anchor,
 6014                    trigger,
 6015                    trigger_in_words,
 6016                    cx,
 6017                )
 6018            })
 6019        });
 6020
 6021        let provider_responses = if let Some(provider) = &provider
 6022            && load_provider_completions
 6023        {
 6024            let trigger_character =
 6025                trigger.filter(|trigger| buffer.read(cx).completion_triggers().contains(trigger));
 6026            let completion_context = CompletionContext {
 6027                trigger_kind: match &trigger_character {
 6028                    Some(_) => CompletionTriggerKind::TRIGGER_CHARACTER,
 6029                    None => CompletionTriggerKind::INVOKED,
 6030                },
 6031                trigger_character,
 6032            };
 6033
 6034            provider.completions(
 6035                buffer_excerpt_id,
 6036                &buffer,
 6037                buffer_position,
 6038                completion_context,
 6039                window,
 6040                cx,
 6041            )
 6042        } else {
 6043            Task::ready(Ok(Vec::new()))
 6044        };
 6045
 6046        let load_word_completions = if !self.word_completions_enabled {
 6047            false
 6048        } else if requested_source
 6049            == Some(CompletionsMenuSource::Words {
 6050                ignore_threshold: true,
 6051            })
 6052        {
 6053            true
 6054        } else {
 6055            load_provider_completions
 6056                && completion_settings.words != WordsCompletionMode::Disabled
 6057                && (ignore_word_threshold || {
 6058                    let words_min_length = completion_settings.words_min_length;
 6059                    // check whether word has at least `words_min_length` characters
 6060                    let query_chars = query.iter().flat_map(|q| q.chars());
 6061                    query_chars.take(words_min_length).count() == words_min_length
 6062                })
 6063        };
 6064
 6065        let mut words = if load_word_completions {
 6066            cx.background_spawn({
 6067                let buffer_snapshot = buffer_snapshot.clone();
 6068                async move {
 6069                    buffer_snapshot.words_in_range(WordsQuery {
 6070                        fuzzy_contents: None,
 6071                        range: word_search_range,
 6072                        skip_digits,
 6073                    })
 6074                }
 6075            })
 6076        } else {
 6077            Task::ready(BTreeMap::default())
 6078        };
 6079
 6080        let snippets = if let Some(provider) = &provider
 6081            && provider.show_snippets()
 6082            && let Some(project) = self.project()
 6083        {
 6084            let char_classifier = buffer_snapshot
 6085                .char_classifier_at(buffer_position)
 6086                .scope_context(Some(CharScopeContext::Completion));
 6087            project.update(cx, |project, cx| {
 6088                snippet_completions(project, &buffer, buffer_position, char_classifier, cx)
 6089            })
 6090        } else {
 6091            Task::ready(Ok(CompletionResponse {
 6092                completions: Vec::new(),
 6093                display_options: Default::default(),
 6094                is_incomplete: false,
 6095            }))
 6096        };
 6097
 6098        let snippet_sort_order = EditorSettings::get_global(cx).snippet_sort_order;
 6099
 6100        let id = post_inc(&mut self.next_completion_id);
 6101        let task = cx.spawn_in(window, async move |editor, cx| {
 6102            let Ok(()) = editor.update(cx, |this, _| {
 6103                this.completion_tasks.retain(|(task_id, _)| *task_id >= id);
 6104            }) else {
 6105                return;
 6106            };
 6107
 6108            // TODO: Ideally completions from different sources would be selectively re-queried, so
 6109            // that having one source with `is_incomplete: true` doesn't cause all to be re-queried.
 6110            let mut completions = Vec::new();
 6111            let mut is_incomplete = false;
 6112            let mut display_options: Option<CompletionDisplayOptions> = None;
 6113            if let Some(provider_responses) = provider_responses.await.log_err()
 6114                && !provider_responses.is_empty()
 6115            {
 6116                for response in provider_responses {
 6117                    completions.extend(response.completions);
 6118                    is_incomplete = is_incomplete || response.is_incomplete;
 6119                    match display_options.as_mut() {
 6120                        None => {
 6121                            display_options = Some(response.display_options);
 6122                        }
 6123                        Some(options) => options.merge(&response.display_options),
 6124                    }
 6125                }
 6126                if completion_settings.words == WordsCompletionMode::Fallback {
 6127                    words = Task::ready(BTreeMap::default());
 6128                }
 6129            }
 6130            let display_options = display_options.unwrap_or_default();
 6131
 6132            let mut words = words.await;
 6133            if let Some(word_to_exclude) = &word_to_exclude {
 6134                words.remove(word_to_exclude);
 6135            }
 6136            for lsp_completion in &completions {
 6137                words.remove(&lsp_completion.new_text);
 6138            }
 6139            completions.extend(words.into_iter().map(|(word, word_range)| Completion {
 6140                replace_range: word_replace_range.clone(),
 6141                new_text: word.clone(),
 6142                label: CodeLabel::plain(word, None),
 6143                match_start: None,
 6144                snippet_deduplication_key: None,
 6145                icon_path: None,
 6146                documentation: None,
 6147                source: CompletionSource::BufferWord {
 6148                    word_range,
 6149                    resolved: false,
 6150                },
 6151                insert_text_mode: Some(InsertTextMode::AS_IS),
 6152                confirm: None,
 6153            }));
 6154
 6155            completions.extend(
 6156                snippets
 6157                    .await
 6158                    .into_iter()
 6159                    .flat_map(|response| response.completions),
 6160            );
 6161
 6162            let menu = if completions.is_empty() {
 6163                None
 6164            } else {
 6165                let Ok((mut menu, matches_task)) = editor.update(cx, |editor, cx| {
 6166                    let languages = editor
 6167                        .workspace
 6168                        .as_ref()
 6169                        .and_then(|(workspace, _)| workspace.upgrade())
 6170                        .map(|workspace| workspace.read(cx).app_state().languages.clone());
 6171                    let menu = CompletionsMenu::new(
 6172                        id,
 6173                        requested_source.unwrap_or(if load_provider_completions {
 6174                            CompletionsMenuSource::Normal
 6175                        } else {
 6176                            CompletionsMenuSource::SnippetsOnly
 6177                        }),
 6178                        sort_completions,
 6179                        show_completion_documentation,
 6180                        position,
 6181                        query.clone(),
 6182                        is_incomplete,
 6183                        buffer.clone(),
 6184                        completions.into(),
 6185                        editor
 6186                            .context_menu()
 6187                            .borrow_mut()
 6188                            .as_ref()
 6189                            .map(|menu| menu.primary_scroll_handle()),
 6190                        display_options,
 6191                        snippet_sort_order,
 6192                        languages,
 6193                        language,
 6194                        cx,
 6195                    );
 6196
 6197                    let query = if filter_completions { query } else { None };
 6198                    let matches_task = menu.do_async_filtering(
 6199                        query.unwrap_or_default(),
 6200                        buffer_position,
 6201                        &buffer,
 6202                        cx,
 6203                    );
 6204                    (menu, matches_task)
 6205                }) else {
 6206                    return;
 6207                };
 6208
 6209                let matches = matches_task.await;
 6210
 6211                let Ok(()) = editor.update_in(cx, |editor, window, cx| {
 6212                    // Newer menu already set, so exit.
 6213                    if let Some(CodeContextMenu::Completions(prev_menu)) =
 6214                        editor.context_menu.borrow().as_ref()
 6215                        && prev_menu.id > id
 6216                    {
 6217                        return;
 6218                    };
 6219
 6220                    // Only valid to take prev_menu because either the new menu is immediately set
 6221                    // below, or the menu is hidden.
 6222                    if let Some(CodeContextMenu::Completions(prev_menu)) =
 6223                        editor.context_menu.borrow_mut().take()
 6224                    {
 6225                        let position_matches =
 6226                            if prev_menu.initial_position == menu.initial_position {
 6227                                true
 6228                            } else {
 6229                                let snapshot = editor.buffer.read(cx).read(cx);
 6230                                prev_menu.initial_position.to_offset(&snapshot)
 6231                                    == menu.initial_position.to_offset(&snapshot)
 6232                            };
 6233                        if position_matches {
 6234                            // Preserve markdown cache before `set_filter_results` because it will
 6235                            // try to populate the documentation cache.
 6236                            menu.preserve_markdown_cache(prev_menu);
 6237                        }
 6238                    };
 6239
 6240                    menu.set_filter_results(matches, provider, window, cx);
 6241                }) else {
 6242                    return;
 6243                };
 6244
 6245                menu.visible().then_some(menu)
 6246            };
 6247
 6248            editor
 6249                .update_in(cx, |editor, window, cx| {
 6250                    if editor.focus_handle.is_focused(window)
 6251                        && let Some(menu) = menu
 6252                    {
 6253                        *editor.context_menu.borrow_mut() =
 6254                            Some(CodeContextMenu::Completions(menu));
 6255
 6256                        crate::hover_popover::hide_hover(editor, cx);
 6257                        if editor.show_edit_predictions_in_menu() {
 6258                            editor.update_visible_edit_prediction(window, cx);
 6259                        } else {
 6260                            editor
 6261                                .discard_edit_prediction(EditPredictionDiscardReason::Ignored, cx);
 6262                        }
 6263
 6264                        cx.notify();
 6265                        return;
 6266                    }
 6267
 6268                    if editor.completion_tasks.len() <= 1 {
 6269                        // If there are no more completion tasks and the last menu was empty, we should hide it.
 6270                        let was_hidden = editor.hide_context_menu(window, cx).is_none();
 6271                        // If it was already hidden and we don't show edit predictions in the menu,
 6272                        // we should also show the edit prediction when available.
 6273                        if was_hidden && editor.show_edit_predictions_in_menu() {
 6274                            editor.update_visible_edit_prediction(window, cx);
 6275                        }
 6276                    }
 6277                })
 6278                .ok();
 6279        });
 6280
 6281        self.completion_tasks.push((id, task));
 6282    }
 6283
 6284    #[cfg(feature = "test-support")]
 6285    pub fn current_completions(&self) -> Option<Vec<project::Completion>> {
 6286        let menu = self.context_menu.borrow();
 6287        if let CodeContextMenu::Completions(menu) = menu.as_ref()? {
 6288            let completions = menu.completions.borrow();
 6289            Some(completions.to_vec())
 6290        } else {
 6291            None
 6292        }
 6293    }
 6294
 6295    pub fn with_completions_menu_matching_id<R>(
 6296        &self,
 6297        id: CompletionId,
 6298        f: impl FnOnce(Option<&mut CompletionsMenu>) -> R,
 6299    ) -> R {
 6300        let mut context_menu = self.context_menu.borrow_mut();
 6301        let Some(CodeContextMenu::Completions(completions_menu)) = &mut *context_menu else {
 6302            return f(None);
 6303        };
 6304        if completions_menu.id != id {
 6305            return f(None);
 6306        }
 6307        f(Some(completions_menu))
 6308    }
 6309
 6310    pub fn confirm_completion(
 6311        &mut self,
 6312        action: &ConfirmCompletion,
 6313        window: &mut Window,
 6314        cx: &mut Context<Self>,
 6315    ) -> Option<Task<Result<()>>> {
 6316        self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
 6317        self.do_completion(action.item_ix, CompletionIntent::Complete, window, cx)
 6318    }
 6319
 6320    pub fn confirm_completion_insert(
 6321        &mut self,
 6322        _: &ConfirmCompletionInsert,
 6323        window: &mut Window,
 6324        cx: &mut Context<Self>,
 6325    ) -> Option<Task<Result<()>>> {
 6326        self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
 6327        self.do_completion(None, CompletionIntent::CompleteWithInsert, window, cx)
 6328    }
 6329
 6330    pub fn confirm_completion_replace(
 6331        &mut self,
 6332        _: &ConfirmCompletionReplace,
 6333        window: &mut Window,
 6334        cx: &mut Context<Self>,
 6335    ) -> Option<Task<Result<()>>> {
 6336        self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
 6337        self.do_completion(None, CompletionIntent::CompleteWithReplace, window, cx)
 6338    }
 6339
 6340    pub fn compose_completion(
 6341        &mut self,
 6342        action: &ComposeCompletion,
 6343        window: &mut Window,
 6344        cx: &mut Context<Self>,
 6345    ) -> Option<Task<Result<()>>> {
 6346        self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
 6347        self.do_completion(action.item_ix, CompletionIntent::Compose, window, cx)
 6348    }
 6349
 6350    fn do_completion(
 6351        &mut self,
 6352        item_ix: Option<usize>,
 6353        intent: CompletionIntent,
 6354        window: &mut Window,
 6355        cx: &mut Context<Editor>,
 6356    ) -> Option<Task<Result<()>>> {
 6357        use language::ToOffset as _;
 6358
 6359        let CodeContextMenu::Completions(completions_menu) = self.hide_context_menu(window, cx)?
 6360        else {
 6361            return None;
 6362        };
 6363
 6364        let candidate_id = {
 6365            let entries = completions_menu.entries.borrow();
 6366            let mat = entries.get(item_ix.unwrap_or(completions_menu.selected_item))?;
 6367            if self.show_edit_predictions_in_menu() {
 6368                self.discard_edit_prediction(EditPredictionDiscardReason::Rejected, cx);
 6369            }
 6370            mat.candidate_id
 6371        };
 6372
 6373        let completion = completions_menu
 6374            .completions
 6375            .borrow()
 6376            .get(candidate_id)?
 6377            .clone();
 6378        cx.stop_propagation();
 6379
 6380        let buffer_handle = completions_menu.buffer.clone();
 6381
 6382        let CompletionEdit {
 6383            new_text,
 6384            snippet,
 6385            replace_range,
 6386        } = process_completion_for_edit(
 6387            &completion,
 6388            intent,
 6389            &buffer_handle,
 6390            &completions_menu.initial_position.text_anchor,
 6391            cx,
 6392        );
 6393
 6394        let buffer = buffer_handle.read(cx);
 6395        let snapshot = self.buffer.read(cx).snapshot(cx);
 6396        let newest_anchor = self.selections.newest_anchor();
 6397        let replace_range_multibuffer = {
 6398            let mut excerpt = snapshot.excerpt_containing(newest_anchor.range()).unwrap();
 6399            excerpt.map_range_from_buffer(replace_range.clone())
 6400        };
 6401        if snapshot.buffer_id_for_anchor(newest_anchor.head()) != Some(buffer.remote_id()) {
 6402            return None;
 6403        }
 6404
 6405        let old_text = buffer
 6406            .text_for_range(replace_range.clone())
 6407            .collect::<String>();
 6408        let lookbehind = newest_anchor
 6409            .start
 6410            .text_anchor
 6411            .to_offset(buffer)
 6412            .saturating_sub(replace_range.start.0);
 6413        let lookahead = replace_range
 6414            .end
 6415            .0
 6416            .saturating_sub(newest_anchor.end.text_anchor.to_offset(buffer));
 6417        let prefix = &old_text[..old_text.len().saturating_sub(lookahead)];
 6418        let suffix = &old_text[lookbehind.min(old_text.len())..];
 6419
 6420        let selections = self
 6421            .selections
 6422            .all::<MultiBufferOffset>(&self.display_snapshot(cx));
 6423        let mut ranges = Vec::new();
 6424        let mut linked_edits = HashMap::<_, Vec<_>>::default();
 6425
 6426        for selection in &selections {
 6427            let range = if selection.id == newest_anchor.id {
 6428                replace_range_multibuffer.clone()
 6429            } else {
 6430                let mut range = selection.range();
 6431
 6432                // if prefix is present, don't duplicate it
 6433                if snapshot.contains_str_at(range.start.saturating_sub_usize(lookbehind), prefix) {
 6434                    range.start = range.start.saturating_sub_usize(lookbehind);
 6435
 6436                    // if suffix is also present, mimic the newest cursor and replace it
 6437                    if selection.id != newest_anchor.id
 6438                        && snapshot.contains_str_at(range.end, suffix)
 6439                    {
 6440                        range.end += lookahead;
 6441                    }
 6442                }
 6443                range
 6444            };
 6445
 6446            ranges.push(range.clone());
 6447
 6448            if !self.linked_edit_ranges.is_empty() {
 6449                let start_anchor = snapshot.anchor_before(range.start);
 6450                let end_anchor = snapshot.anchor_after(range.end);
 6451                if let Some(ranges) = self
 6452                    .linked_editing_ranges_for(start_anchor.text_anchor..end_anchor.text_anchor, cx)
 6453                {
 6454                    for (buffer, edits) in ranges {
 6455                        linked_edits
 6456                            .entry(buffer.clone())
 6457                            .or_default()
 6458                            .extend(edits.into_iter().map(|range| (range, new_text.to_owned())));
 6459                    }
 6460                }
 6461            }
 6462        }
 6463
 6464        let common_prefix_len = old_text
 6465            .chars()
 6466            .zip(new_text.chars())
 6467            .take_while(|(a, b)| a == b)
 6468            .map(|(a, _)| a.len_utf8())
 6469            .sum::<usize>();
 6470
 6471        cx.emit(EditorEvent::InputHandled {
 6472            utf16_range_to_replace: None,
 6473            text: new_text[common_prefix_len..].into(),
 6474        });
 6475
 6476        self.transact(window, cx, |editor, window, cx| {
 6477            if let Some(mut snippet) = snippet {
 6478                snippet.text = new_text.to_string();
 6479                editor
 6480                    .insert_snippet(&ranges, snippet, window, cx)
 6481                    .log_err();
 6482            } else {
 6483                editor.buffer.update(cx, |multi_buffer, cx| {
 6484                    let auto_indent = match completion.insert_text_mode {
 6485                        Some(InsertTextMode::AS_IS) => None,
 6486                        _ => editor.autoindent_mode.clone(),
 6487                    };
 6488                    let edits = ranges.into_iter().map(|range| (range, new_text.as_str()));
 6489                    multi_buffer.edit(edits, auto_indent, cx);
 6490                });
 6491            }
 6492            for (buffer, edits) in linked_edits {
 6493                buffer.update(cx, |buffer, cx| {
 6494                    let snapshot = buffer.snapshot();
 6495                    let edits = edits
 6496                        .into_iter()
 6497                        .map(|(range, text)| {
 6498                            use text::ToPoint as TP;
 6499                            let end_point = TP::to_point(&range.end, &snapshot);
 6500                            let start_point = TP::to_point(&range.start, &snapshot);
 6501                            (start_point..end_point, text)
 6502                        })
 6503                        .sorted_by_key(|(range, _)| range.start);
 6504                    buffer.edit(edits, None, cx);
 6505                })
 6506            }
 6507
 6508            editor.refresh_edit_prediction(true, false, window, cx);
 6509        });
 6510        self.invalidate_autoclose_regions(&self.selections.disjoint_anchors_arc(), &snapshot);
 6511
 6512        let show_new_completions_on_confirm = completion
 6513            .confirm
 6514            .as_ref()
 6515            .is_some_and(|confirm| confirm(intent, window, cx));
 6516        if show_new_completions_on_confirm {
 6517            self.open_or_update_completions_menu(None, None, false, window, cx);
 6518        }
 6519
 6520        let provider = self.completion_provider.as_ref()?;
 6521
 6522        let lsp_store = self.project().map(|project| project.read(cx).lsp_store());
 6523        let command = lsp_store.as_ref().and_then(|lsp_store| {
 6524            let CompletionSource::Lsp {
 6525                lsp_completion,
 6526                server_id,
 6527                ..
 6528            } = &completion.source
 6529            else {
 6530                return None;
 6531            };
 6532            let lsp_command = lsp_completion.command.as_ref()?;
 6533            let available_commands = lsp_store
 6534                .read(cx)
 6535                .lsp_server_capabilities
 6536                .get(server_id)
 6537                .and_then(|server_capabilities| {
 6538                    server_capabilities
 6539                        .execute_command_provider
 6540                        .as_ref()
 6541                        .map(|options| options.commands.as_slice())
 6542                })?;
 6543            if available_commands.contains(&lsp_command.command) {
 6544                Some(CodeAction {
 6545                    server_id: *server_id,
 6546                    range: language::Anchor::MIN..language::Anchor::MIN,
 6547                    lsp_action: LspAction::Command(lsp_command.clone()),
 6548                    resolved: false,
 6549                })
 6550            } else {
 6551                None
 6552            }
 6553        });
 6554
 6555        drop(completion);
 6556        let apply_edits = provider.apply_additional_edits_for_completion(
 6557            buffer_handle.clone(),
 6558            completions_menu.completions.clone(),
 6559            candidate_id,
 6560            true,
 6561            cx,
 6562        );
 6563
 6564        let editor_settings = EditorSettings::get_global(cx);
 6565        if editor_settings.show_signature_help_after_edits || editor_settings.auto_signature_help {
 6566            // After the code completion is finished, users often want to know what signatures are needed.
 6567            // so we should automatically call signature_help
 6568            self.show_signature_help(&ShowSignatureHelp, window, cx);
 6569        }
 6570
 6571        Some(cx.spawn_in(window, async move |editor, cx| {
 6572            apply_edits.await?;
 6573
 6574            if let Some((lsp_store, command)) = lsp_store.zip(command) {
 6575                let title = command.lsp_action.title().to_owned();
 6576                let project_transaction = lsp_store
 6577                    .update(cx, |lsp_store, cx| {
 6578                        lsp_store.apply_code_action(buffer_handle, command, false, cx)
 6579                    })
 6580                    .await
 6581                    .context("applying post-completion command")?;
 6582                if let Some(workspace) = editor.read_with(cx, |editor, _| editor.workspace())? {
 6583                    Self::open_project_transaction(
 6584                        &editor,
 6585                        workspace.downgrade(),
 6586                        project_transaction,
 6587                        title,
 6588                        cx,
 6589                    )
 6590                    .await?;
 6591                }
 6592            }
 6593
 6594            Ok(())
 6595        }))
 6596    }
 6597
 6598    pub fn toggle_code_actions(
 6599        &mut self,
 6600        action: &ToggleCodeActions,
 6601        window: &mut Window,
 6602        cx: &mut Context<Self>,
 6603    ) {
 6604        let quick_launch = action.quick_launch;
 6605        let mut context_menu = self.context_menu.borrow_mut();
 6606        if let Some(CodeContextMenu::CodeActions(code_actions)) = context_menu.as_ref() {
 6607            if code_actions.deployed_from == action.deployed_from {
 6608                // Toggle if we're selecting the same one
 6609                *context_menu = None;
 6610                cx.notify();
 6611                return;
 6612            } else {
 6613                // Otherwise, clear it and start a new one
 6614                *context_menu = None;
 6615                cx.notify();
 6616            }
 6617        }
 6618        drop(context_menu);
 6619        let snapshot = self.snapshot(window, cx);
 6620        let deployed_from = action.deployed_from.clone();
 6621        let action = action.clone();
 6622        self.completion_tasks.clear();
 6623        self.discard_edit_prediction(EditPredictionDiscardReason::Ignored, cx);
 6624
 6625        let multibuffer_point = match &action.deployed_from {
 6626            Some(CodeActionSource::Indicator(row)) | Some(CodeActionSource::RunMenu(row)) => {
 6627                DisplayPoint::new(*row, 0).to_point(&snapshot)
 6628            }
 6629            _ => self
 6630                .selections
 6631                .newest::<Point>(&snapshot.display_snapshot)
 6632                .head(),
 6633        };
 6634        let Some((buffer, buffer_row)) = snapshot
 6635            .buffer_snapshot()
 6636            .buffer_line_for_row(MultiBufferRow(multibuffer_point.row))
 6637            .and_then(|(buffer_snapshot, range)| {
 6638                self.buffer()
 6639                    .read(cx)
 6640                    .buffer(buffer_snapshot.remote_id())
 6641                    .map(|buffer| (buffer, range.start.row))
 6642            })
 6643        else {
 6644            return;
 6645        };
 6646        let buffer_id = buffer.read(cx).remote_id();
 6647        let tasks = self
 6648            .tasks
 6649            .get(&(buffer_id, buffer_row))
 6650            .map(|t| Arc::new(t.to_owned()));
 6651
 6652        if !self.focus_handle.is_focused(window) {
 6653            return;
 6654        }
 6655        let project = self.project.clone();
 6656
 6657        let code_actions_task = match deployed_from {
 6658            Some(CodeActionSource::RunMenu(_)) => Task::ready(None),
 6659            _ => self.code_actions(buffer_row, window, cx),
 6660        };
 6661
 6662        let runnable_task = match deployed_from {
 6663            Some(CodeActionSource::Indicator(_)) => Task::ready(Ok(Default::default())),
 6664            _ => {
 6665                let mut task_context_task = Task::ready(None);
 6666                if let Some(tasks) = &tasks
 6667                    && let Some(project) = project
 6668                {
 6669                    task_context_task =
 6670                        Self::build_tasks_context(&project, &buffer, buffer_row, tasks, cx);
 6671                }
 6672
 6673                cx.spawn_in(window, {
 6674                    let buffer = buffer.clone();
 6675                    async move |editor, cx| {
 6676                        let task_context = task_context_task.await;
 6677
 6678                        let resolved_tasks =
 6679                            tasks
 6680                                .zip(task_context.clone())
 6681                                .map(|(tasks, task_context)| ResolvedTasks {
 6682                                    templates: tasks.resolve(&task_context).collect(),
 6683                                    position: snapshot.buffer_snapshot().anchor_before(Point::new(
 6684                                        multibuffer_point.row,
 6685                                        tasks.column,
 6686                                    )),
 6687                                });
 6688                        let debug_scenarios = editor
 6689                            .update(cx, |editor, cx| {
 6690                                editor.debug_scenarios(&resolved_tasks, &buffer, cx)
 6691                            })?
 6692                            .await;
 6693                        anyhow::Ok((resolved_tasks, debug_scenarios, task_context))
 6694                    }
 6695                })
 6696            }
 6697        };
 6698
 6699        cx.spawn_in(window, async move |editor, cx| {
 6700            let (resolved_tasks, debug_scenarios, task_context) = runnable_task.await?;
 6701            let code_actions = code_actions_task.await;
 6702            let spawn_straight_away = quick_launch
 6703                && resolved_tasks
 6704                    .as_ref()
 6705                    .is_some_and(|tasks| tasks.templates.len() == 1)
 6706                && code_actions
 6707                    .as_ref()
 6708                    .is_none_or(|actions| actions.is_empty())
 6709                && debug_scenarios.is_empty();
 6710
 6711            editor.update_in(cx, |editor, window, cx| {
 6712                crate::hover_popover::hide_hover(editor, cx);
 6713                let actions = CodeActionContents::new(
 6714                    resolved_tasks,
 6715                    code_actions,
 6716                    debug_scenarios,
 6717                    task_context.unwrap_or_default(),
 6718                );
 6719
 6720                // Don't show the menu if there are no actions available
 6721                if actions.is_empty() {
 6722                    cx.notify();
 6723                    return Task::ready(Ok(()));
 6724                }
 6725
 6726                *editor.context_menu.borrow_mut() =
 6727                    Some(CodeContextMenu::CodeActions(CodeActionsMenu {
 6728                        buffer,
 6729                        actions,
 6730                        selected_item: Default::default(),
 6731                        scroll_handle: UniformListScrollHandle::default(),
 6732                        deployed_from,
 6733                    }));
 6734                cx.notify();
 6735                if spawn_straight_away
 6736                    && let Some(task) = editor.confirm_code_action(
 6737                        &ConfirmCodeAction { item_ix: Some(0) },
 6738                        window,
 6739                        cx,
 6740                    )
 6741                {
 6742                    return task;
 6743                }
 6744
 6745                Task::ready(Ok(()))
 6746            })
 6747        })
 6748        .detach_and_log_err(cx);
 6749    }
 6750
 6751    fn debug_scenarios(
 6752        &mut self,
 6753        resolved_tasks: &Option<ResolvedTasks>,
 6754        buffer: &Entity<Buffer>,
 6755        cx: &mut App,
 6756    ) -> Task<Vec<task::DebugScenario>> {
 6757        maybe!({
 6758            let project = self.project()?;
 6759            let dap_store = project.read(cx).dap_store();
 6760            let mut scenarios = vec![];
 6761            let resolved_tasks = resolved_tasks.as_ref()?;
 6762            let buffer = buffer.read(cx);
 6763            let language = buffer.language()?;
 6764            let file = buffer.file();
 6765            let debug_adapter = language_settings(language.name().into(), file, cx)
 6766                .debuggers
 6767                .first()
 6768                .map(SharedString::from)
 6769                .or_else(|| language.config().debuggers.first().map(SharedString::from))?;
 6770
 6771            dap_store.update(cx, |dap_store, cx| {
 6772                for (_, task) in &resolved_tasks.templates {
 6773                    let maybe_scenario = dap_store.debug_scenario_for_build_task(
 6774                        task.original_task().clone(),
 6775                        debug_adapter.clone().into(),
 6776                        task.display_label().to_owned().into(),
 6777                        cx,
 6778                    );
 6779                    scenarios.push(maybe_scenario);
 6780                }
 6781            });
 6782            Some(cx.background_spawn(async move {
 6783                futures::future::join_all(scenarios)
 6784                    .await
 6785                    .into_iter()
 6786                    .flatten()
 6787                    .collect::<Vec<_>>()
 6788            }))
 6789        })
 6790        .unwrap_or_else(|| Task::ready(vec![]))
 6791    }
 6792
 6793    fn code_actions(
 6794        &mut self,
 6795        buffer_row: u32,
 6796        window: &mut Window,
 6797        cx: &mut Context<Self>,
 6798    ) -> Task<Option<Rc<[AvailableCodeAction]>>> {
 6799        let mut task = self.code_actions_task.take();
 6800        cx.spawn_in(window, async move |editor, cx| {
 6801            while let Some(prev_task) = task {
 6802                prev_task.await.log_err();
 6803                task = editor
 6804                    .update(cx, |this, _| this.code_actions_task.take())
 6805                    .ok()?;
 6806            }
 6807
 6808            editor
 6809                .update(cx, |editor, cx| {
 6810                    editor
 6811                        .available_code_actions
 6812                        .clone()
 6813                        .and_then(|(location, code_actions)| {
 6814                            let snapshot = location.buffer.read(cx).snapshot();
 6815                            let point_range = location.range.to_point(&snapshot);
 6816                            let point_range = point_range.start.row..=point_range.end.row;
 6817                            if point_range.contains(&buffer_row) {
 6818                                Some(code_actions)
 6819                            } else {
 6820                                None
 6821                            }
 6822                        })
 6823                })
 6824                .ok()
 6825                .flatten()
 6826        })
 6827    }
 6828
 6829    pub fn confirm_code_action(
 6830        &mut self,
 6831        action: &ConfirmCodeAction,
 6832        window: &mut Window,
 6833        cx: &mut Context<Self>,
 6834    ) -> Option<Task<Result<()>>> {
 6835        self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
 6836
 6837        let actions_menu =
 6838            if let CodeContextMenu::CodeActions(menu) = self.hide_context_menu(window, cx)? {
 6839                menu
 6840            } else {
 6841                return None;
 6842            };
 6843
 6844        let action_ix = action.item_ix.unwrap_or(actions_menu.selected_item);
 6845        let action = actions_menu.actions.get(action_ix)?;
 6846        let title = action.label();
 6847        let buffer = actions_menu.buffer;
 6848        let workspace = self.workspace()?;
 6849
 6850        match action {
 6851            CodeActionsItem::Task(task_source_kind, resolved_task) => {
 6852                workspace.update(cx, |workspace, cx| {
 6853                    workspace.schedule_resolved_task(
 6854                        task_source_kind,
 6855                        resolved_task,
 6856                        false,
 6857                        window,
 6858                        cx,
 6859                    );
 6860
 6861                    Some(Task::ready(Ok(())))
 6862                })
 6863            }
 6864            CodeActionsItem::CodeAction {
 6865                excerpt_id,
 6866                action,
 6867                provider,
 6868            } => {
 6869                let apply_code_action =
 6870                    provider.apply_code_action(buffer, action, excerpt_id, true, window, cx);
 6871                let workspace = workspace.downgrade();
 6872                Some(cx.spawn_in(window, async move |editor, cx| {
 6873                    let project_transaction = apply_code_action.await?;
 6874                    Self::open_project_transaction(
 6875                        &editor,
 6876                        workspace,
 6877                        project_transaction,
 6878                        title,
 6879                        cx,
 6880                    )
 6881                    .await
 6882                }))
 6883            }
 6884            CodeActionsItem::DebugScenario(scenario) => {
 6885                let context = actions_menu.actions.context.into();
 6886
 6887                workspace.update(cx, |workspace, cx| {
 6888                    dap::send_telemetry(&scenario, TelemetrySpawnLocation::Gutter, cx);
 6889                    workspace.start_debug_session(
 6890                        scenario,
 6891                        context,
 6892                        Some(buffer),
 6893                        None,
 6894                        window,
 6895                        cx,
 6896                    );
 6897                });
 6898                Some(Task::ready(Ok(())))
 6899            }
 6900        }
 6901    }
 6902
 6903    fn open_transaction_for_hidden_buffers(
 6904        workspace: Entity<Workspace>,
 6905        transaction: ProjectTransaction,
 6906        title: String,
 6907        window: &mut Window,
 6908        cx: &mut Context<Self>,
 6909    ) {
 6910        if transaction.0.is_empty() {
 6911            return;
 6912        }
 6913
 6914        let edited_buffers_already_open = {
 6915            let other_editors: Vec<Entity<Editor>> = workspace
 6916                .read(cx)
 6917                .panes()
 6918                .iter()
 6919                .flat_map(|pane| pane.read(cx).items_of_type::<Editor>())
 6920                .filter(|editor| editor.entity_id() != cx.entity_id())
 6921                .collect();
 6922
 6923            transaction.0.keys().all(|buffer| {
 6924                other_editors.iter().any(|editor| {
 6925                    let multi_buffer = editor.read(cx).buffer();
 6926                    multi_buffer.read(cx).is_singleton()
 6927                        && multi_buffer
 6928                            .read(cx)
 6929                            .as_singleton()
 6930                            .map_or(false, |singleton| {
 6931                                singleton.entity_id() == buffer.entity_id()
 6932                            })
 6933                })
 6934            })
 6935        };
 6936        if !edited_buffers_already_open {
 6937            let workspace = workspace.downgrade();
 6938            cx.defer_in(window, move |_, window, cx| {
 6939                cx.spawn_in(window, async move |editor, cx| {
 6940                    Self::open_project_transaction(&editor, workspace, transaction, title, cx)
 6941                        .await
 6942                        .ok()
 6943                })
 6944                .detach();
 6945            });
 6946        }
 6947    }
 6948
 6949    pub async fn open_project_transaction(
 6950        editor: &WeakEntity<Editor>,
 6951        workspace: WeakEntity<Workspace>,
 6952        transaction: ProjectTransaction,
 6953        title: String,
 6954        cx: &mut AsyncWindowContext,
 6955    ) -> Result<()> {
 6956        let mut entries = transaction.0.into_iter().collect::<Vec<_>>();
 6957        cx.update(|_, cx| {
 6958            entries.sort_unstable_by_key(|(buffer, _)| {
 6959                buffer.read(cx).file().map(|f| f.path().clone())
 6960            });
 6961        })?;
 6962        if entries.is_empty() {
 6963            return Ok(());
 6964        }
 6965
 6966        // If the project transaction's edits are all contained within this editor, then
 6967        // avoid opening a new editor to display them.
 6968
 6969        if let [(buffer, transaction)] = &*entries {
 6970            let excerpt = editor.update(cx, |editor, cx| {
 6971                editor
 6972                    .buffer()
 6973                    .read(cx)
 6974                    .excerpt_containing(editor.selections.newest_anchor().head(), cx)
 6975            })?;
 6976            if let Some((_, excerpted_buffer, excerpt_range)) = excerpt
 6977                && excerpted_buffer == *buffer
 6978            {
 6979                let all_edits_within_excerpt = buffer.read_with(cx, |buffer, _| {
 6980                    let excerpt_range = excerpt_range.to_offset(buffer);
 6981                    buffer
 6982                        .edited_ranges_for_transaction::<usize>(transaction)
 6983                        .all(|range| {
 6984                            excerpt_range.start <= range.start && excerpt_range.end >= range.end
 6985                        })
 6986                });
 6987
 6988                if all_edits_within_excerpt {
 6989                    return Ok(());
 6990                }
 6991            }
 6992        }
 6993
 6994        let mut ranges_to_highlight = Vec::new();
 6995        let excerpt_buffer = cx.new(|cx| {
 6996            let mut multibuffer = MultiBuffer::new(Capability::ReadWrite).with_title(title);
 6997            for (buffer_handle, transaction) in &entries {
 6998                let edited_ranges = buffer_handle
 6999                    .read(cx)
 7000                    .edited_ranges_for_transaction::<Point>(transaction)
 7001                    .collect::<Vec<_>>();
 7002                let (ranges, _) = multibuffer.set_excerpts_for_path(
 7003                    PathKey::for_buffer(buffer_handle, cx),
 7004                    buffer_handle.clone(),
 7005                    edited_ranges,
 7006                    multibuffer_context_lines(cx),
 7007                    cx,
 7008                );
 7009
 7010                ranges_to_highlight.extend(ranges);
 7011            }
 7012            multibuffer.push_transaction(entries.iter().map(|(b, t)| (b, t)), cx);
 7013            multibuffer
 7014        });
 7015
 7016        workspace.update_in(cx, |workspace, window, cx| {
 7017            let project = workspace.project().clone();
 7018            let editor =
 7019                cx.new(|cx| Editor::for_multibuffer(excerpt_buffer, Some(project), window, cx));
 7020            workspace.add_item_to_active_pane(Box::new(editor.clone()), None, true, window, cx);
 7021            editor.update(cx, |editor, cx| {
 7022                editor.highlight_background(
 7023                    HighlightKey::Editor,
 7024                    &ranges_to_highlight,
 7025                    |_, theme| theme.colors().editor_highlighted_line_background,
 7026                    cx,
 7027                );
 7028            });
 7029        })?;
 7030
 7031        Ok(())
 7032    }
 7033
 7034    pub fn clear_code_action_providers(&mut self) {
 7035        self.code_action_providers.clear();
 7036        self.available_code_actions.take();
 7037    }
 7038
 7039    pub fn add_code_action_provider(
 7040        &mut self,
 7041        provider: Rc<dyn CodeActionProvider>,
 7042        window: &mut Window,
 7043        cx: &mut Context<Self>,
 7044    ) {
 7045        if self
 7046            .code_action_providers
 7047            .iter()
 7048            .any(|existing_provider| existing_provider.id() == provider.id())
 7049        {
 7050            return;
 7051        }
 7052
 7053        self.code_action_providers.push(provider);
 7054        self.refresh_code_actions(window, cx);
 7055    }
 7056
 7057    pub fn remove_code_action_provider(
 7058        &mut self,
 7059        id: Arc<str>,
 7060        window: &mut Window,
 7061        cx: &mut Context<Self>,
 7062    ) {
 7063        self.code_action_providers
 7064            .retain(|provider| provider.id() != id);
 7065        self.refresh_code_actions(window, cx);
 7066    }
 7067
 7068    pub fn code_actions_enabled_for_toolbar(&self, cx: &App) -> bool {
 7069        !self.code_action_providers.is_empty()
 7070            && EditorSettings::get_global(cx).toolbar.code_actions
 7071    }
 7072
 7073    pub fn has_available_code_actions(&self) -> bool {
 7074        self.available_code_actions
 7075            .as_ref()
 7076            .is_some_and(|(_, actions)| !actions.is_empty())
 7077    }
 7078
 7079    fn render_inline_code_actions(
 7080        &self,
 7081        icon_size: ui::IconSize,
 7082        display_row: DisplayRow,
 7083        is_active: bool,
 7084        cx: &mut Context<Self>,
 7085    ) -> AnyElement {
 7086        let show_tooltip = !self.context_menu_visible();
 7087        IconButton::new("inline_code_actions", ui::IconName::BoltFilled)
 7088            .icon_size(icon_size)
 7089            .shape(ui::IconButtonShape::Square)
 7090            .icon_color(ui::Color::Hidden)
 7091            .toggle_state(is_active)
 7092            .when(show_tooltip, |this| {
 7093                this.tooltip({
 7094                    let focus_handle = self.focus_handle.clone();
 7095                    move |_window, cx| {
 7096                        Tooltip::for_action_in(
 7097                            "Toggle Code Actions",
 7098                            &ToggleCodeActions {
 7099                                deployed_from: None,
 7100                                quick_launch: false,
 7101                            },
 7102                            &focus_handle,
 7103                            cx,
 7104                        )
 7105                    }
 7106                })
 7107            })
 7108            .on_click(cx.listener(move |editor, _: &ClickEvent, window, cx| {
 7109                window.focus(&editor.focus_handle(cx), cx);
 7110                editor.toggle_code_actions(
 7111                    &crate::actions::ToggleCodeActions {
 7112                        deployed_from: Some(crate::actions::CodeActionSource::Indicator(
 7113                            display_row,
 7114                        )),
 7115                        quick_launch: false,
 7116                    },
 7117                    window,
 7118                    cx,
 7119                );
 7120            }))
 7121            .into_any_element()
 7122    }
 7123
 7124    pub fn context_menu(&self) -> &RefCell<Option<CodeContextMenu>> {
 7125        &self.context_menu
 7126    }
 7127
 7128    fn refresh_code_actions(&mut self, window: &mut Window, cx: &mut Context<Self>) {
 7129        self.code_actions_task = Some(cx.spawn_in(window, async move |this, cx| {
 7130            cx.background_executor()
 7131                .timer(CODE_ACTIONS_DEBOUNCE_TIMEOUT)
 7132                .await;
 7133
 7134            let (start_buffer, start, _, end, newest_selection) = this
 7135                .update(cx, |this, cx| {
 7136                    let newest_selection = this.selections.newest_anchor().clone();
 7137                    if newest_selection.head().diff_base_anchor.is_some() {
 7138                        return None;
 7139                    }
 7140                    let display_snapshot = this.display_snapshot(cx);
 7141                    let newest_selection_adjusted =
 7142                        this.selections.newest_adjusted(&display_snapshot);
 7143                    let buffer = this.buffer.read(cx);
 7144
 7145                    let (start_buffer, start) =
 7146                        buffer.text_anchor_for_position(newest_selection_adjusted.start, cx)?;
 7147                    let (end_buffer, end) =
 7148                        buffer.text_anchor_for_position(newest_selection_adjusted.end, cx)?;
 7149
 7150                    Some((start_buffer, start, end_buffer, end, newest_selection))
 7151                })?
 7152                .filter(|(start_buffer, _, end_buffer, _, _)| start_buffer == end_buffer)
 7153                .context(
 7154                    "Expected selection to lie in a single buffer when refreshing code actions",
 7155                )?;
 7156            let (providers, tasks) = this.update_in(cx, |this, window, cx| {
 7157                let providers = this.code_action_providers.clone();
 7158                let tasks = this
 7159                    .code_action_providers
 7160                    .iter()
 7161                    .map(|provider| provider.code_actions(&start_buffer, start..end, window, cx))
 7162                    .collect::<Vec<_>>();
 7163                (providers, tasks)
 7164            })?;
 7165
 7166            let mut actions = Vec::new();
 7167            for (provider, provider_actions) in
 7168                providers.into_iter().zip(future::join_all(tasks).await)
 7169            {
 7170                if let Some(provider_actions) = provider_actions.log_err() {
 7171                    actions.extend(provider_actions.into_iter().map(|action| {
 7172                        AvailableCodeAction {
 7173                            excerpt_id: newest_selection.start.excerpt_id,
 7174                            action,
 7175                            provider: provider.clone(),
 7176                        }
 7177                    }));
 7178                }
 7179            }
 7180
 7181            this.update(cx, |this, cx| {
 7182                this.available_code_actions = if actions.is_empty() {
 7183                    None
 7184                } else {
 7185                    Some((
 7186                        Location {
 7187                            buffer: start_buffer,
 7188                            range: start..end,
 7189                        },
 7190                        actions.into(),
 7191                    ))
 7192                };
 7193                cx.notify();
 7194            })
 7195        }));
 7196    }
 7197
 7198    fn start_inline_blame_timer(&mut self, window: &mut Window, cx: &mut Context<Self>) {
 7199        if let Some(delay) = ProjectSettings::get_global(cx).git.inline_blame_delay() {
 7200            self.show_git_blame_inline = false;
 7201
 7202            self.show_git_blame_inline_delay_task =
 7203                Some(cx.spawn_in(window, async move |this, cx| {
 7204                    cx.background_executor().timer(delay).await;
 7205
 7206                    this.update(cx, |this, cx| {
 7207                        this.show_git_blame_inline = true;
 7208                        cx.notify();
 7209                    })
 7210                    .log_err();
 7211                }));
 7212        }
 7213    }
 7214
 7215    pub fn blame_hover(&mut self, _: &BlameHover, window: &mut Window, cx: &mut Context<Self>) {
 7216        let snapshot = self.snapshot(window, cx);
 7217        let cursor = self
 7218            .selections
 7219            .newest::<Point>(&snapshot.display_snapshot)
 7220            .head();
 7221        let Some((buffer, point, _)) = snapshot.buffer_snapshot().point_to_buffer_point(cursor)
 7222        else {
 7223            return;
 7224        };
 7225
 7226        if self.blame.is_none() {
 7227            self.start_git_blame(true, window, cx);
 7228        }
 7229        let Some(blame) = self.blame.as_ref() else {
 7230            return;
 7231        };
 7232
 7233        let row_info = RowInfo {
 7234            buffer_id: Some(buffer.remote_id()),
 7235            buffer_row: Some(point.row),
 7236            ..Default::default()
 7237        };
 7238        let Some((buffer, blame_entry)) = blame
 7239            .update(cx, |blame, cx| blame.blame_for_rows(&[row_info], cx).next())
 7240            .flatten()
 7241        else {
 7242            return;
 7243        };
 7244
 7245        let anchor = self.selections.newest_anchor().head();
 7246        let position = self.to_pixel_point(anchor, &snapshot, window, cx);
 7247        if let (Some(position), Some(last_bounds)) = (position, self.last_bounds) {
 7248            self.show_blame_popover(
 7249                buffer,
 7250                &blame_entry,
 7251                position + last_bounds.origin,
 7252                true,
 7253                cx,
 7254            );
 7255        };
 7256    }
 7257
 7258    fn show_blame_popover(
 7259        &mut self,
 7260        buffer: BufferId,
 7261        blame_entry: &BlameEntry,
 7262        position: gpui::Point<Pixels>,
 7263        ignore_timeout: bool,
 7264        cx: &mut Context<Self>,
 7265    ) {
 7266        if let Some(state) = &mut self.inline_blame_popover {
 7267            state.hide_task.take();
 7268        } else {
 7269            let blame_popover_delay = EditorSettings::get_global(cx).hover_popover_delay.0;
 7270            let blame_entry = blame_entry.clone();
 7271            let show_task = cx.spawn(async move |editor, cx| {
 7272                if !ignore_timeout {
 7273                    cx.background_executor()
 7274                        .timer(std::time::Duration::from_millis(blame_popover_delay))
 7275                        .await;
 7276                }
 7277                editor
 7278                    .update(cx, |editor, cx| {
 7279                        editor.inline_blame_popover_show_task.take();
 7280                        let Some(blame) = editor.blame.as_ref() else {
 7281                            return;
 7282                        };
 7283                        let blame = blame.read(cx);
 7284                        let details = blame.details_for_entry(buffer, &blame_entry);
 7285                        let markdown = cx.new(|cx| {
 7286                            Markdown::new(
 7287                                details
 7288                                    .as_ref()
 7289                                    .map(|message| message.message.clone())
 7290                                    .unwrap_or_default(),
 7291                                None,
 7292                                None,
 7293                                cx,
 7294                            )
 7295                        });
 7296                        editor.inline_blame_popover = Some(InlineBlamePopover {
 7297                            position,
 7298                            hide_task: None,
 7299                            popover_bounds: None,
 7300                            popover_state: InlineBlamePopoverState {
 7301                                scroll_handle: ScrollHandle::new(),
 7302                                commit_message: details,
 7303                                markdown,
 7304                            },
 7305                            keyboard_grace: ignore_timeout,
 7306                        });
 7307                        cx.notify();
 7308                    })
 7309                    .ok();
 7310            });
 7311            self.inline_blame_popover_show_task = Some(show_task);
 7312        }
 7313    }
 7314
 7315    pub fn has_mouse_context_menu(&self) -> bool {
 7316        self.mouse_context_menu.is_some()
 7317    }
 7318
 7319    pub fn hide_blame_popover(&mut self, ignore_timeout: bool, cx: &mut Context<Self>) -> bool {
 7320        self.inline_blame_popover_show_task.take();
 7321        if let Some(state) = &mut self.inline_blame_popover {
 7322            let hide_task = cx.spawn(async move |editor, cx| {
 7323                if !ignore_timeout {
 7324                    cx.background_executor()
 7325                        .timer(std::time::Duration::from_millis(100))
 7326                        .await;
 7327                }
 7328                editor
 7329                    .update(cx, |editor, cx| {
 7330                        editor.inline_blame_popover.take();
 7331                        cx.notify();
 7332                    })
 7333                    .ok();
 7334            });
 7335            state.hide_task = Some(hide_task);
 7336            true
 7337        } else {
 7338            false
 7339        }
 7340    }
 7341
 7342    fn refresh_document_highlights(&mut self, cx: &mut Context<Self>) -> Option<()> {
 7343        if self.pending_rename.is_some() {
 7344            return None;
 7345        }
 7346
 7347        let provider = self.semantics_provider.clone()?;
 7348        let buffer = self.buffer.read(cx);
 7349        let newest_selection = self.selections.newest_anchor().clone();
 7350        let cursor_position = newest_selection.head();
 7351        let (cursor_buffer, cursor_buffer_position) =
 7352            buffer.text_anchor_for_position(cursor_position, cx)?;
 7353        let (tail_buffer, tail_buffer_position) =
 7354            buffer.text_anchor_for_position(newest_selection.tail(), cx)?;
 7355        if cursor_buffer != tail_buffer {
 7356            return None;
 7357        }
 7358
 7359        let snapshot = cursor_buffer.read(cx).snapshot();
 7360        let word_ranges = cx.background_spawn(async move {
 7361            // this might look odd to put on the background thread, but
 7362            // `surrounding_word` can be quite expensive as it calls into
 7363            // tree-sitter language scopes
 7364            let (start_word_range, _) = snapshot.surrounding_word(cursor_buffer_position, None);
 7365            let (end_word_range, _) = snapshot.surrounding_word(tail_buffer_position, None);
 7366            (start_word_range, end_word_range)
 7367        });
 7368
 7369        let debounce = EditorSettings::get_global(cx).lsp_highlight_debounce.0;
 7370        self.document_highlights_task = Some(cx.spawn(async move |this, cx| {
 7371            let (start_word_range, end_word_range) = word_ranges.await;
 7372            if start_word_range != end_word_range {
 7373                this.update(cx, |this, cx| {
 7374                    this.document_highlights_task.take();
 7375                    this.clear_background_highlights(HighlightKey::DocumentHighlightRead, cx);
 7376                    this.clear_background_highlights(HighlightKey::DocumentHighlightWrite, cx);
 7377                })
 7378                .ok();
 7379                return;
 7380            }
 7381            cx.background_executor()
 7382                .timer(Duration::from_millis(debounce))
 7383                .await;
 7384
 7385            let highlights = if let Some(highlights) = cx.update(|cx| {
 7386                provider.document_highlights(&cursor_buffer, cursor_buffer_position, cx)
 7387            }) {
 7388                highlights.await.log_err()
 7389            } else {
 7390                None
 7391            };
 7392
 7393            if let Some(highlights) = highlights {
 7394                this.update(cx, |this, cx| {
 7395                    if this.pending_rename.is_some() {
 7396                        return;
 7397                    }
 7398
 7399                    let buffer = this.buffer.read(cx);
 7400                    if buffer
 7401                        .text_anchor_for_position(cursor_position, cx)
 7402                        .is_none_or(|(buffer, _)| buffer != cursor_buffer)
 7403                    {
 7404                        return;
 7405                    }
 7406
 7407                    let cursor_buffer_snapshot = cursor_buffer.read(cx);
 7408                    let mut write_ranges = Vec::new();
 7409                    let mut read_ranges = Vec::new();
 7410                    for highlight in highlights {
 7411                        let buffer_id = cursor_buffer.read(cx).remote_id();
 7412                        for (excerpt_id, excerpt_range) in buffer.excerpts_for_buffer(buffer_id, cx)
 7413                        {
 7414                            let start = highlight
 7415                                .range
 7416                                .start
 7417                                .max(&excerpt_range.context.start, cursor_buffer_snapshot);
 7418                            let end = highlight
 7419                                .range
 7420                                .end
 7421                                .min(&excerpt_range.context.end, cursor_buffer_snapshot);
 7422                            if start.cmp(&end, cursor_buffer_snapshot).is_ge() {
 7423                                continue;
 7424                            }
 7425
 7426                            let range = Anchor::range_in_buffer(excerpt_id, *start..*end);
 7427                            if highlight.kind == lsp::DocumentHighlightKind::WRITE {
 7428                                write_ranges.push(range);
 7429                            } else {
 7430                                read_ranges.push(range);
 7431                            }
 7432                        }
 7433                    }
 7434
 7435                    this.highlight_background(
 7436                        HighlightKey::DocumentHighlightRead,
 7437                        &read_ranges,
 7438                        |_, theme| theme.colors().editor_document_highlight_read_background,
 7439                        cx,
 7440                    );
 7441                    this.highlight_background(
 7442                        HighlightKey::DocumentHighlightWrite,
 7443                        &write_ranges,
 7444                        |_, theme| theme.colors().editor_document_highlight_write_background,
 7445                        cx,
 7446                    );
 7447                    cx.notify();
 7448                })
 7449                .log_err();
 7450            }
 7451        }));
 7452        None
 7453    }
 7454
 7455    fn prepare_highlight_query_from_selection(
 7456        &mut self,
 7457        window: &Window,
 7458        cx: &mut Context<Editor>,
 7459    ) -> Option<(String, Range<Anchor>)> {
 7460        if matches!(self.mode, EditorMode::SingleLine) {
 7461            return None;
 7462        }
 7463        if !EditorSettings::get_global(cx).selection_highlight {
 7464            return None;
 7465        }
 7466        if self.selections.count() != 1 || self.selections.line_mode() {
 7467            return None;
 7468        }
 7469        let snapshot = self.snapshot(window, cx);
 7470        let selection = self.selections.newest::<Point>(&snapshot);
 7471        // If the selection spans multiple rows OR it is empty
 7472        if selection.start.row != selection.end.row
 7473            || selection.start.column == selection.end.column
 7474        {
 7475            return None;
 7476        }
 7477        let selection_anchor_range = selection.range().to_anchors(snapshot.buffer_snapshot());
 7478        let query = snapshot
 7479            .buffer_snapshot()
 7480            .text_for_range(selection_anchor_range.clone())
 7481            .collect::<String>();
 7482        if query.trim().is_empty() {
 7483            return None;
 7484        }
 7485        Some((query, selection_anchor_range))
 7486    }
 7487
 7488    #[ztracing::instrument(skip_all)]
 7489    fn update_selection_occurrence_highlights(
 7490        &mut self,
 7491        query_text: String,
 7492        query_range: Range<Anchor>,
 7493        multi_buffer_range_to_query: Range<Point>,
 7494        use_debounce: bool,
 7495        window: &mut Window,
 7496        cx: &mut Context<Editor>,
 7497    ) -> Task<()> {
 7498        let multi_buffer_snapshot = self.buffer().read(cx).snapshot(cx);
 7499        cx.spawn_in(window, async move |editor, cx| {
 7500            if use_debounce {
 7501                cx.background_executor()
 7502                    .timer(SELECTION_HIGHLIGHT_DEBOUNCE_TIMEOUT)
 7503                    .await;
 7504            }
 7505            let match_task = cx.background_spawn(async move {
 7506                let buffer_ranges = multi_buffer_snapshot
 7507                    .range_to_buffer_ranges(
 7508                        multi_buffer_range_to_query.start..=multi_buffer_range_to_query.end,
 7509                    )
 7510                    .into_iter()
 7511                    .filter(|(_, excerpt_visible_range, _)| !excerpt_visible_range.is_empty());
 7512                let mut match_ranges = Vec::new();
 7513                let Ok(regex) = project::search::SearchQuery::text(
 7514                    query_text.clone(),
 7515                    false,
 7516                    false,
 7517                    false,
 7518                    Default::default(),
 7519                    Default::default(),
 7520                    false,
 7521                    None,
 7522                ) else {
 7523                    return Vec::default();
 7524                };
 7525                let query_range = query_range.to_anchors(&multi_buffer_snapshot);
 7526                for (buffer_snapshot, search_range, excerpt_id) in buffer_ranges {
 7527                    match_ranges.extend(
 7528                        regex
 7529                            .search(
 7530                                buffer_snapshot,
 7531                                Some(search_range.start.0..search_range.end.0),
 7532                            )
 7533                            .await
 7534                            .into_iter()
 7535                            .filter_map(|match_range| {
 7536                                let match_start = buffer_snapshot
 7537                                    .anchor_after(search_range.start + match_range.start);
 7538                                let match_end = buffer_snapshot
 7539                                    .anchor_before(search_range.start + match_range.end);
 7540                                let match_anchor_range =
 7541                                    Anchor::range_in_buffer(excerpt_id, match_start..match_end);
 7542                                (match_anchor_range != query_range).then_some(match_anchor_range)
 7543                            }),
 7544                    );
 7545                }
 7546                match_ranges
 7547            });
 7548            let match_ranges = match_task.await;
 7549            editor
 7550                .update_in(cx, |editor, _, cx| {
 7551                    if use_debounce {
 7552                        editor.clear_background_highlights(HighlightKey::SelectedTextHighlight, cx);
 7553                        editor.debounced_selection_highlight_complete = true;
 7554                    } else if editor.debounced_selection_highlight_complete {
 7555                        return;
 7556                    }
 7557                    if !match_ranges.is_empty() {
 7558                        editor.highlight_background(
 7559                            HighlightKey::SelectedTextHighlight,
 7560                            &match_ranges,
 7561                            |_, theme| theme.colors().editor_document_highlight_bracket_background,
 7562                            cx,
 7563                        )
 7564                    }
 7565                })
 7566                .log_err();
 7567        })
 7568    }
 7569
 7570    fn refresh_single_line_folds(&mut self, window: &mut Window, cx: &mut Context<Editor>) {
 7571        struct NewlineFold;
 7572        let type_id = std::any::TypeId::of::<NewlineFold>();
 7573        if !self.mode.is_single_line() {
 7574            return;
 7575        }
 7576        let snapshot = self.snapshot(window, cx);
 7577        if snapshot.buffer_snapshot().max_point().row == 0 {
 7578            return;
 7579        }
 7580        let task = cx.background_spawn(async move {
 7581            let new_newlines = snapshot
 7582                .buffer_chars_at(MultiBufferOffset(0))
 7583                .filter_map(|(c, i)| {
 7584                    if c == '\n' {
 7585                        Some(
 7586                            snapshot.buffer_snapshot().anchor_after(i)
 7587                                ..snapshot.buffer_snapshot().anchor_before(i + 1usize),
 7588                        )
 7589                    } else {
 7590                        None
 7591                    }
 7592                })
 7593                .collect::<Vec<_>>();
 7594            let existing_newlines = snapshot
 7595                .folds_in_range(MultiBufferOffset(0)..snapshot.buffer_snapshot().len())
 7596                .filter_map(|fold| {
 7597                    if fold.placeholder.type_tag == Some(type_id) {
 7598                        Some(fold.range.start..fold.range.end)
 7599                    } else {
 7600                        None
 7601                    }
 7602                })
 7603                .collect::<Vec<_>>();
 7604
 7605            (new_newlines, existing_newlines)
 7606        });
 7607        self.folding_newlines = cx.spawn(async move |this, cx| {
 7608            let (new_newlines, existing_newlines) = task.await;
 7609            if new_newlines == existing_newlines {
 7610                return;
 7611            }
 7612            let placeholder = FoldPlaceholder {
 7613                render: Arc::new(move |_, _, cx| {
 7614                    div()
 7615                        .bg(cx.theme().status().hint_background)
 7616                        .border_b_1()
 7617                        .size_full()
 7618                        .font(ThemeSettings::get_global(cx).buffer_font.clone())
 7619                        .border_color(cx.theme().status().hint)
 7620                        .child("\\n")
 7621                        .into_any()
 7622                }),
 7623                constrain_width: false,
 7624                merge_adjacent: false,
 7625                type_tag: Some(type_id),
 7626                collapsed_text: None,
 7627            };
 7628            let creases = new_newlines
 7629                .into_iter()
 7630                .map(|range| Crease::simple(range, placeholder.clone()))
 7631                .collect();
 7632            this.update(cx, |this, cx| {
 7633                this.display_map.update(cx, |display_map, cx| {
 7634                    display_map.remove_folds_with_type(existing_newlines, type_id, cx);
 7635                    display_map.fold(creases, cx);
 7636                });
 7637            })
 7638            .ok();
 7639        });
 7640    }
 7641
 7642    #[ztracing::instrument(skip_all)]
 7643    fn refresh_outline_symbols_at_cursor(&mut self, cx: &mut Context<Editor>) {
 7644        if !self.mode.is_full() {
 7645            return;
 7646        }
 7647        let cursor = self.selections.newest_anchor().head();
 7648        let multi_buffer_snapshot = self.buffer().read(cx).snapshot(cx);
 7649
 7650        if self.uses_lsp_document_symbols(cursor, &multi_buffer_snapshot, cx) {
 7651            self.outline_symbols_at_cursor =
 7652                self.lsp_symbols_at_cursor(cursor, &multi_buffer_snapshot, cx);
 7653            cx.emit(EditorEvent::OutlineSymbolsChanged);
 7654            cx.notify();
 7655        } else {
 7656            let syntax = cx.theme().syntax().clone();
 7657            let background_task = cx.background_spawn(async move {
 7658                multi_buffer_snapshot.symbols_containing(cursor, Some(&syntax))
 7659            });
 7660            self.refresh_outline_symbols_at_cursor_at_cursor_task =
 7661                cx.spawn(async move |this, cx| {
 7662                    let symbols = background_task.await;
 7663                    this.update(cx, |this, cx| {
 7664                        this.outline_symbols_at_cursor = symbols;
 7665                        cx.emit(EditorEvent::OutlineSymbolsChanged);
 7666                        cx.notify();
 7667                    })
 7668                    .ok();
 7669                });
 7670        }
 7671    }
 7672
 7673    #[ztracing::instrument(skip_all)]
 7674    fn refresh_selected_text_highlights(
 7675        &mut self,
 7676        on_buffer_edit: bool,
 7677        window: &mut Window,
 7678        cx: &mut Context<Editor>,
 7679    ) {
 7680        let Some((query_text, query_range)) =
 7681            self.prepare_highlight_query_from_selection(window, cx)
 7682        else {
 7683            self.clear_background_highlights(HighlightKey::SelectedTextHighlight, cx);
 7684            self.quick_selection_highlight_task.take();
 7685            self.debounced_selection_highlight_task.take();
 7686            self.debounced_selection_highlight_complete = false;
 7687            return;
 7688        };
 7689        let display_snapshot = self.display_map.update(cx, |map, cx| map.snapshot(cx));
 7690        let multi_buffer_snapshot = self.buffer().read(cx).snapshot(cx);
 7691        let query_changed = self
 7692            .quick_selection_highlight_task
 7693            .as_ref()
 7694            .is_none_or(|(prev_anchor_range, _)| prev_anchor_range != &query_range);
 7695        if query_changed {
 7696            self.debounced_selection_highlight_complete = false;
 7697        }
 7698        if on_buffer_edit || query_changed {
 7699            let multi_buffer_visible_start = self
 7700                .scroll_manager
 7701                .native_anchor(&display_snapshot, cx)
 7702                .anchor
 7703                .to_point(&multi_buffer_snapshot);
 7704            let multi_buffer_visible_end = multi_buffer_snapshot.clip_point(
 7705                multi_buffer_visible_start
 7706                    + Point::new(self.visible_line_count().unwrap_or(0.).ceil() as u32, 0),
 7707                Bias::Left,
 7708            );
 7709            let multi_buffer_visible_range = multi_buffer_visible_start..multi_buffer_visible_end;
 7710            self.quick_selection_highlight_task = Some((
 7711                query_range.clone(),
 7712                self.update_selection_occurrence_highlights(
 7713                    query_text.clone(),
 7714                    query_range.clone(),
 7715                    multi_buffer_visible_range,
 7716                    false,
 7717                    window,
 7718                    cx,
 7719                ),
 7720            ));
 7721        }
 7722        if on_buffer_edit
 7723            || self
 7724                .debounced_selection_highlight_task
 7725                .as_ref()
 7726                .is_none_or(|(prev_anchor_range, _)| prev_anchor_range != &query_range)
 7727        {
 7728            let multi_buffer_start = multi_buffer_snapshot
 7729                .anchor_before(MultiBufferOffset(0))
 7730                .to_point(&multi_buffer_snapshot);
 7731            let multi_buffer_end = multi_buffer_snapshot
 7732                .anchor_after(multi_buffer_snapshot.len())
 7733                .to_point(&multi_buffer_snapshot);
 7734            let multi_buffer_full_range = multi_buffer_start..multi_buffer_end;
 7735            self.debounced_selection_highlight_task = Some((
 7736                query_range.clone(),
 7737                self.update_selection_occurrence_highlights(
 7738                    query_text,
 7739                    query_range,
 7740                    multi_buffer_full_range,
 7741                    true,
 7742                    window,
 7743                    cx,
 7744                ),
 7745            ));
 7746        }
 7747    }
 7748
 7749    pub fn refresh_edit_prediction(
 7750        &mut self,
 7751        debounce: bool,
 7752        user_requested: bool,
 7753        window: &mut Window,
 7754        cx: &mut Context<Self>,
 7755    ) -> Option<()> {
 7756        let provider = self.edit_prediction_provider()?;
 7757        let cursor = self.selections.newest_anchor().head();
 7758        let (buffer, cursor_buffer_position) =
 7759            self.buffer.read(cx).text_anchor_for_position(cursor, cx)?;
 7760
 7761        if DisableAiSettings::is_ai_disabled_for_buffer(Some(&buffer), cx) {
 7762            return None;
 7763        }
 7764
 7765        if !self.edit_predictions_enabled_in_buffer(&buffer, cursor_buffer_position, cx) {
 7766            self.discard_edit_prediction(EditPredictionDiscardReason::Ignored, cx);
 7767            return None;
 7768        }
 7769
 7770        self.update_visible_edit_prediction(window, cx);
 7771
 7772        if !user_requested
 7773            && (!self.should_show_edit_predictions()
 7774                || !self.is_focused(window)
 7775                || buffer.read(cx).is_empty())
 7776        {
 7777            self.discard_edit_prediction(EditPredictionDiscardReason::Ignored, cx);
 7778            return None;
 7779        }
 7780
 7781        provider.refresh(buffer, cursor_buffer_position, debounce, cx);
 7782        Some(())
 7783    }
 7784
 7785    fn show_edit_predictions_in_menu(&self) -> bool {
 7786        match self.edit_prediction_settings {
 7787            EditPredictionSettings::Disabled => false,
 7788            EditPredictionSettings::Enabled { show_in_menu, .. } => show_in_menu,
 7789        }
 7790    }
 7791
 7792    pub fn edit_predictions_enabled(&self) -> bool {
 7793        match self.edit_prediction_settings {
 7794            EditPredictionSettings::Disabled => false,
 7795            EditPredictionSettings::Enabled { .. } => true,
 7796        }
 7797    }
 7798
 7799    fn edit_prediction_requires_modifier(&self) -> bool {
 7800        match self.edit_prediction_settings {
 7801            EditPredictionSettings::Disabled => false,
 7802            EditPredictionSettings::Enabled {
 7803                preview_requires_modifier,
 7804                ..
 7805            } => preview_requires_modifier,
 7806        }
 7807    }
 7808
 7809    pub fn update_edit_prediction_settings(&mut self, cx: &mut Context<Self>) {
 7810        if self.edit_prediction_provider.is_none() {
 7811            self.edit_prediction_settings = EditPredictionSettings::Disabled;
 7812            self.discard_edit_prediction(EditPredictionDiscardReason::Ignored, cx);
 7813            return;
 7814        }
 7815
 7816        let selection = self.selections.newest_anchor();
 7817        let cursor = selection.head();
 7818
 7819        if let Some((buffer, cursor_buffer_position)) =
 7820            self.buffer.read(cx).text_anchor_for_position(cursor, cx)
 7821        {
 7822            if DisableAiSettings::is_ai_disabled_for_buffer(Some(&buffer), cx) {
 7823                self.edit_prediction_settings = EditPredictionSettings::Disabled;
 7824                self.discard_edit_prediction(EditPredictionDiscardReason::Ignored, cx);
 7825                return;
 7826            }
 7827            self.edit_prediction_settings =
 7828                self.edit_prediction_settings_at_position(&buffer, cursor_buffer_position, cx);
 7829        }
 7830    }
 7831
 7832    fn edit_prediction_settings_at_position(
 7833        &self,
 7834        buffer: &Entity<Buffer>,
 7835        buffer_position: language::Anchor,
 7836        cx: &App,
 7837    ) -> EditPredictionSettings {
 7838        if !self.mode.is_full()
 7839            || !self.show_edit_predictions_override.unwrap_or(true)
 7840            || self.edit_predictions_disabled_in_scope(buffer, buffer_position, cx)
 7841        {
 7842            return EditPredictionSettings::Disabled;
 7843        }
 7844
 7845        let buffer = buffer.read(cx);
 7846
 7847        let file = buffer.file();
 7848
 7849        if !language_settings(buffer.language().map(|l| l.name()), file, cx).show_edit_predictions {
 7850            return EditPredictionSettings::Disabled;
 7851        };
 7852
 7853        let by_provider = matches!(
 7854            self.menu_edit_predictions_policy,
 7855            MenuEditPredictionsPolicy::ByProvider
 7856        );
 7857
 7858        let show_in_menu = by_provider
 7859            && self
 7860                .edit_prediction_provider
 7861                .as_ref()
 7862                .is_some_and(|provider| provider.provider.show_predictions_in_menu());
 7863
 7864        let preview_requires_modifier =
 7865            all_language_settings(file, cx).edit_predictions_mode() == EditPredictionsMode::Subtle;
 7866
 7867        EditPredictionSettings::Enabled {
 7868            show_in_menu,
 7869            preview_requires_modifier,
 7870        }
 7871    }
 7872
 7873    fn should_show_edit_predictions(&self) -> bool {
 7874        self.snippet_stack.is_empty() && self.edit_predictions_enabled()
 7875    }
 7876
 7877    pub fn edit_prediction_preview_is_active(&self) -> bool {
 7878        matches!(
 7879            self.edit_prediction_preview,
 7880            EditPredictionPreview::Active { .. }
 7881        )
 7882    }
 7883
 7884    pub fn edit_predictions_enabled_at_cursor(&self, cx: &App) -> bool {
 7885        let cursor = self.selections.newest_anchor().head();
 7886        if let Some((buffer, cursor_position)) =
 7887            self.buffer.read(cx).text_anchor_for_position(cursor, cx)
 7888        {
 7889            self.edit_predictions_enabled_in_buffer(&buffer, cursor_position, cx)
 7890        } else {
 7891            false
 7892        }
 7893    }
 7894
 7895    pub fn supports_minimap(&self, cx: &App) -> bool {
 7896        !self.minimap_visibility.disabled() && self.buffer_kind(cx) == ItemBufferKind::Singleton
 7897    }
 7898
 7899    fn edit_predictions_enabled_in_buffer(
 7900        &self,
 7901        buffer: &Entity<Buffer>,
 7902        buffer_position: language::Anchor,
 7903        cx: &App,
 7904    ) -> bool {
 7905        maybe!({
 7906            if self.read_only(cx) {
 7907                return Some(false);
 7908            }
 7909            let provider = self.edit_prediction_provider()?;
 7910            if !provider.is_enabled(buffer, buffer_position, cx) {
 7911                return Some(false);
 7912            }
 7913            let buffer = buffer.read(cx);
 7914            let Some(file) = buffer.file() else {
 7915                return Some(true);
 7916            };
 7917            let settings = all_language_settings(Some(file), cx);
 7918            Some(settings.edit_predictions_enabled_for_file(file, cx))
 7919        })
 7920        .unwrap_or(false)
 7921    }
 7922
 7923    pub fn show_edit_prediction(
 7924        &mut self,
 7925        _: &ShowEditPrediction,
 7926        window: &mut Window,
 7927        cx: &mut Context<Self>,
 7928    ) {
 7929        if !self.has_active_edit_prediction() {
 7930            self.refresh_edit_prediction(false, true, window, cx);
 7931            return;
 7932        }
 7933
 7934        self.update_visible_edit_prediction(window, cx);
 7935    }
 7936
 7937    pub fn display_cursor_names(
 7938        &mut self,
 7939        _: &DisplayCursorNames,
 7940        window: &mut Window,
 7941        cx: &mut Context<Self>,
 7942    ) {
 7943        self.show_cursor_names(window, cx);
 7944    }
 7945
 7946    fn show_cursor_names(&mut self, window: &mut Window, cx: &mut Context<Self>) {
 7947        self.show_cursor_names = true;
 7948        cx.notify();
 7949        cx.spawn_in(window, async move |this, cx| {
 7950            cx.background_executor().timer(CURSORS_VISIBLE_FOR).await;
 7951            this.update(cx, |this, cx| {
 7952                this.show_cursor_names = false;
 7953                cx.notify()
 7954            })
 7955            .ok()
 7956        })
 7957        .detach();
 7958    }
 7959
 7960    pub fn accept_partial_edit_prediction(
 7961        &mut self,
 7962        granularity: EditPredictionGranularity,
 7963        window: &mut Window,
 7964        cx: &mut Context<Self>,
 7965    ) {
 7966        if self.show_edit_predictions_in_menu() {
 7967            self.hide_context_menu(window, cx);
 7968        }
 7969
 7970        let Some(active_edit_prediction) = self.active_edit_prediction.as_ref() else {
 7971            return;
 7972        };
 7973
 7974        if !matches!(granularity, EditPredictionGranularity::Full) && self.selections.count() != 1 {
 7975            return;
 7976        }
 7977
 7978        match &active_edit_prediction.completion {
 7979            EditPrediction::MoveWithin { target, .. } => {
 7980                let target = *target;
 7981
 7982                if matches!(granularity, EditPredictionGranularity::Full) {
 7983                    if let Some(position_map) = &self.last_position_map {
 7984                        let target_row = target.to_display_point(&position_map.snapshot).row();
 7985                        let is_visible = position_map.visible_row_range.contains(&target_row);
 7986
 7987                        if is_visible || !self.edit_prediction_requires_modifier() {
 7988                            self.unfold_ranges(&[target..target], true, false, cx);
 7989                            self.change_selections(
 7990                                SelectionEffects::scroll(Autoscroll::newest()),
 7991                                window,
 7992                                cx,
 7993                                |selections| {
 7994                                    selections.select_anchor_ranges([target..target]);
 7995                                },
 7996                            );
 7997                            self.clear_row_highlights::<EditPredictionPreview>();
 7998                            self.edit_prediction_preview
 7999                                .set_previous_scroll_position(None);
 8000                        } else {
 8001                            // Highlight and request scroll
 8002                            self.edit_prediction_preview
 8003                                .set_previous_scroll_position(Some(
 8004                                    position_map.snapshot.scroll_anchor,
 8005                                ));
 8006                            self.highlight_rows::<EditPredictionPreview>(
 8007                                target..target,
 8008                                cx.theme().colors().editor_highlighted_line_background,
 8009                                RowHighlightOptions {
 8010                                    autoscroll: true,
 8011                                    ..Default::default()
 8012                                },
 8013                                cx,
 8014                            );
 8015                            self.request_autoscroll(Autoscroll::fit(), cx);
 8016                        }
 8017                    }
 8018                } else {
 8019                    self.change_selections(
 8020                        SelectionEffects::scroll(Autoscroll::newest()),
 8021                        window,
 8022                        cx,
 8023                        |selections| {
 8024                            selections.select_anchor_ranges([target..target]);
 8025                        },
 8026                    );
 8027                }
 8028            }
 8029            EditPrediction::MoveOutside { snapshot, target } => {
 8030                if let Some(workspace) = self.workspace() {
 8031                    Self::open_editor_at_anchor(snapshot, *target, &workspace, window, cx)
 8032                        .detach_and_log_err(cx);
 8033                }
 8034            }
 8035            EditPrediction::Edit {
 8036                edits,
 8037                cursor_position,
 8038                ..
 8039            } => {
 8040                self.report_edit_prediction_event(
 8041                    active_edit_prediction.completion_id.clone(),
 8042                    true,
 8043                    cx,
 8044                );
 8045
 8046                match granularity {
 8047                    EditPredictionGranularity::Full => {
 8048                        let transaction_id_prev = self.buffer.read(cx).last_transaction_id(cx);
 8049
 8050                        // Compute fallback cursor position BEFORE applying the edit,
 8051                        // so the anchor tracks through the edit correctly
 8052                        let fallback_cursor_target = {
 8053                            let snapshot = self.buffer.read(cx).snapshot(cx);
 8054                            edits.last().unwrap().0.end.bias_right(&snapshot)
 8055                        };
 8056
 8057                        self.buffer.update(cx, |buffer, cx| {
 8058                            buffer.edit(edits.iter().cloned(), None, cx)
 8059                        });
 8060
 8061                        if let Some(provider) = self.edit_prediction_provider() {
 8062                            provider.accept(cx);
 8063                        }
 8064
 8065                        // Resolve cursor position after the edit is applied
 8066                        let cursor_target = if let Some((anchor, offset)) = cursor_position {
 8067                            // The anchor tracks through the edit, then we add the offset
 8068                            let snapshot = self.buffer.read(cx).snapshot(cx);
 8069                            let base_offset = anchor.to_offset(&snapshot).0;
 8070                            let target_offset =
 8071                                MultiBufferOffset((base_offset + offset).min(snapshot.len().0));
 8072                            snapshot.anchor_after(target_offset)
 8073                        } else {
 8074                            fallback_cursor_target
 8075                        };
 8076
 8077                        self.change_selections(SelectionEffects::no_scroll(), window, cx, |s| {
 8078                            s.select_anchor_ranges([cursor_target..cursor_target]);
 8079                        });
 8080
 8081                        let selections = self.selections.disjoint_anchors_arc();
 8082                        if let Some(transaction_id_now) =
 8083                            self.buffer.read(cx).last_transaction_id(cx)
 8084                        {
 8085                            if transaction_id_prev != Some(transaction_id_now) {
 8086                                self.selection_history
 8087                                    .insert_transaction(transaction_id_now, selections);
 8088                            }
 8089                        }
 8090
 8091                        self.update_visible_edit_prediction(window, cx);
 8092                        if self.active_edit_prediction.is_none() {
 8093                            self.refresh_edit_prediction(true, true, window, cx);
 8094                        }
 8095                        cx.notify();
 8096                    }
 8097                    _ => {
 8098                        let snapshot = self.buffer.read(cx).snapshot(cx);
 8099                        let cursor_offset = self
 8100                            .selections
 8101                            .newest::<MultiBufferOffset>(&self.display_snapshot(cx))
 8102                            .head();
 8103
 8104                        let insertion = edits.iter().find_map(|(range, text)| {
 8105                            let range = range.to_offset(&snapshot);
 8106                            if range.is_empty() && range.start == cursor_offset {
 8107                                Some(text)
 8108                            } else {
 8109                                None
 8110                            }
 8111                        });
 8112
 8113                        if let Some(text) = insertion {
 8114                            let text_to_insert = match granularity {
 8115                                EditPredictionGranularity::Word => {
 8116                                    let mut partial = text
 8117                                        .chars()
 8118                                        .by_ref()
 8119                                        .take_while(|c| c.is_alphabetic())
 8120                                        .collect::<String>();
 8121                                    if partial.is_empty() {
 8122                                        partial = text
 8123                                            .chars()
 8124                                            .by_ref()
 8125                                            .take_while(|c| c.is_whitespace() || !c.is_alphabetic())
 8126                                            .collect::<String>();
 8127                                    }
 8128                                    partial
 8129                                }
 8130                                EditPredictionGranularity::Line => {
 8131                                    if let Some(line) = text.split_inclusive('\n').next() {
 8132                                        line.to_string()
 8133                                    } else {
 8134                                        text.to_string()
 8135                                    }
 8136                                }
 8137                                EditPredictionGranularity::Full => unreachable!(),
 8138                            };
 8139
 8140                            cx.emit(EditorEvent::InputHandled {
 8141                                utf16_range_to_replace: None,
 8142                                text: text_to_insert.clone().into(),
 8143                            });
 8144
 8145                            self.insert_with_autoindent_mode(&text_to_insert, None, window, cx);
 8146                            self.refresh_edit_prediction(true, true, window, cx);
 8147                            cx.notify();
 8148                        } else {
 8149                            self.accept_partial_edit_prediction(
 8150                                EditPredictionGranularity::Full,
 8151                                window,
 8152                                cx,
 8153                            );
 8154                        }
 8155                    }
 8156                }
 8157            }
 8158        }
 8159
 8160        self.edit_prediction_requires_modifier_in_indent_conflict = false;
 8161    }
 8162
 8163    pub fn accept_next_word_edit_prediction(
 8164        &mut self,
 8165        _: &AcceptNextWordEditPrediction,
 8166        window: &mut Window,
 8167        cx: &mut Context<Self>,
 8168    ) {
 8169        self.accept_partial_edit_prediction(EditPredictionGranularity::Word, window, cx);
 8170    }
 8171
 8172    pub fn accept_next_line_edit_prediction(
 8173        &mut self,
 8174        _: &AcceptNextLineEditPrediction,
 8175        window: &mut Window,
 8176        cx: &mut Context<Self>,
 8177    ) {
 8178        self.accept_partial_edit_prediction(EditPredictionGranularity::Line, window, cx);
 8179    }
 8180
 8181    pub fn accept_edit_prediction(
 8182        &mut self,
 8183        _: &AcceptEditPrediction,
 8184        window: &mut Window,
 8185        cx: &mut Context<Self>,
 8186    ) {
 8187        self.accept_partial_edit_prediction(EditPredictionGranularity::Full, window, cx);
 8188    }
 8189
 8190    fn discard_edit_prediction(
 8191        &mut self,
 8192        reason: EditPredictionDiscardReason,
 8193        cx: &mut Context<Self>,
 8194    ) -> bool {
 8195        if reason == EditPredictionDiscardReason::Rejected {
 8196            let completion_id = self
 8197                .active_edit_prediction
 8198                .as_ref()
 8199                .and_then(|active_completion| active_completion.completion_id.clone());
 8200
 8201            self.report_edit_prediction_event(completion_id, false, cx);
 8202        }
 8203
 8204        if let Some(provider) = self.edit_prediction_provider() {
 8205            provider.discard(reason, cx);
 8206        }
 8207
 8208        self.take_active_edit_prediction(cx)
 8209    }
 8210
 8211    fn report_edit_prediction_event(&self, id: Option<SharedString>, accepted: bool, cx: &App) {
 8212        let Some(provider) = self.edit_prediction_provider() else {
 8213            return;
 8214        };
 8215
 8216        let Some((_, buffer, _)) = self
 8217            .buffer
 8218            .read(cx)
 8219            .excerpt_containing(self.selections.newest_anchor().head(), cx)
 8220        else {
 8221            return;
 8222        };
 8223
 8224        let extension = buffer
 8225            .read(cx)
 8226            .file()
 8227            .and_then(|file| Some(file.path().extension()?.to_string()));
 8228
 8229        let event_type = match accepted {
 8230            true => "Edit Prediction Accepted",
 8231            false => "Edit Prediction Discarded",
 8232        };
 8233        telemetry::event!(
 8234            event_type,
 8235            provider = provider.name(),
 8236            prediction_id = id,
 8237            suggestion_accepted = accepted,
 8238            file_extension = extension,
 8239        );
 8240    }
 8241
 8242    fn open_editor_at_anchor(
 8243        snapshot: &language::BufferSnapshot,
 8244        target: language::Anchor,
 8245        workspace: &Entity<Workspace>,
 8246        window: &mut Window,
 8247        cx: &mut App,
 8248    ) -> Task<Result<()>> {
 8249        workspace.update(cx, |workspace, cx| {
 8250            let path = snapshot.file().map(|file| file.full_path(cx));
 8251            let Some(path) =
 8252                path.and_then(|path| workspace.project().read(cx).find_project_path(path, cx))
 8253            else {
 8254                return Task::ready(Err(anyhow::anyhow!("Project path not found")));
 8255            };
 8256            let target = text::ToPoint::to_point(&target, snapshot);
 8257            let item = workspace.open_path(path, None, true, window, cx);
 8258            window.spawn(cx, async move |cx| {
 8259                let Some(editor) = item.await?.downcast::<Editor>() else {
 8260                    return Ok(());
 8261                };
 8262                editor
 8263                    .update_in(cx, |editor, window, cx| {
 8264                        editor.go_to_singleton_buffer_point(target, window, cx);
 8265                    })
 8266                    .ok();
 8267                anyhow::Ok(())
 8268            })
 8269        })
 8270    }
 8271
 8272    pub fn has_active_edit_prediction(&self) -> bool {
 8273        self.active_edit_prediction.is_some()
 8274    }
 8275
 8276    fn take_active_edit_prediction(&mut self, cx: &mut Context<Self>) -> bool {
 8277        let Some(active_edit_prediction) = self.active_edit_prediction.take() else {
 8278            return false;
 8279        };
 8280
 8281        self.splice_inlays(&active_edit_prediction.inlay_ids, Default::default(), cx);
 8282        self.clear_highlights(HighlightKey::EditPredictionHighlight, cx);
 8283        self.stale_edit_prediction_in_menu = Some(active_edit_prediction);
 8284        true
 8285    }
 8286
 8287    /// Returns true when we're displaying the edit prediction popover below the cursor
 8288    /// like we are not previewing and the LSP autocomplete menu is visible
 8289    /// or we are in `when_holding_modifier` mode.
 8290    pub fn edit_prediction_visible_in_cursor_popover(&self, has_completion: bool) -> bool {
 8291        if self.edit_prediction_preview_is_active()
 8292            || !self.show_edit_predictions_in_menu()
 8293            || !self.edit_predictions_enabled()
 8294        {
 8295            return false;
 8296        }
 8297
 8298        if self.has_visible_completions_menu() {
 8299            return true;
 8300        }
 8301
 8302        has_completion && self.edit_prediction_requires_modifier()
 8303    }
 8304
 8305    fn handle_modifiers_changed(
 8306        &mut self,
 8307        modifiers: Modifiers,
 8308        position_map: &PositionMap,
 8309        window: &mut Window,
 8310        cx: &mut Context<Self>,
 8311    ) {
 8312        // Ensure that the edit prediction preview is updated, even when not
 8313        // enabled, if there's an active edit prediction preview.
 8314        if self.show_edit_predictions_in_menu()
 8315            || matches!(
 8316                self.edit_prediction_preview,
 8317                EditPredictionPreview::Active { .. }
 8318            )
 8319        {
 8320            self.update_edit_prediction_preview(&modifiers, window, cx);
 8321        }
 8322
 8323        self.update_selection_mode(&modifiers, position_map, window, cx);
 8324
 8325        let mouse_position = window.mouse_position();
 8326        if !position_map.text_hitbox.is_hovered(window) {
 8327            return;
 8328        }
 8329
 8330        self.update_hovered_link(
 8331            position_map.point_for_position(mouse_position),
 8332            &position_map.snapshot,
 8333            modifiers,
 8334            window,
 8335            cx,
 8336        )
 8337    }
 8338
 8339    fn is_cmd_or_ctrl_pressed(modifiers: &Modifiers, cx: &mut Context<Self>) -> bool {
 8340        match EditorSettings::get_global(cx).multi_cursor_modifier {
 8341            MultiCursorModifier::Alt => modifiers.secondary(),
 8342            MultiCursorModifier::CmdOrCtrl => modifiers.alt,
 8343        }
 8344    }
 8345
 8346    fn is_alt_pressed(modifiers: &Modifiers, cx: &mut Context<Self>) -> bool {
 8347        match EditorSettings::get_global(cx).multi_cursor_modifier {
 8348            MultiCursorModifier::Alt => modifiers.alt,
 8349            MultiCursorModifier::CmdOrCtrl => modifiers.secondary(),
 8350        }
 8351    }
 8352
 8353    fn columnar_selection_mode(
 8354        modifiers: &Modifiers,
 8355        cx: &mut Context<Self>,
 8356    ) -> Option<ColumnarMode> {
 8357        if modifiers.shift && modifiers.number_of_modifiers() == 2 {
 8358            if Self::is_cmd_or_ctrl_pressed(modifiers, cx) {
 8359                Some(ColumnarMode::FromMouse)
 8360            } else if Self::is_alt_pressed(modifiers, cx) {
 8361                Some(ColumnarMode::FromSelection)
 8362            } else {
 8363                None
 8364            }
 8365        } else {
 8366            None
 8367        }
 8368    }
 8369
 8370    fn update_selection_mode(
 8371        &mut self,
 8372        modifiers: &Modifiers,
 8373        position_map: &PositionMap,
 8374        window: &mut Window,
 8375        cx: &mut Context<Self>,
 8376    ) {
 8377        let Some(mode) = Self::columnar_selection_mode(modifiers, cx) else {
 8378            return;
 8379        };
 8380        if self.selections.pending_anchor().is_none() {
 8381            return;
 8382        }
 8383
 8384        let mouse_position = window.mouse_position();
 8385        let point_for_position = position_map.point_for_position(mouse_position);
 8386        let position = point_for_position.previous_valid;
 8387
 8388        self.select(
 8389            SelectPhase::BeginColumnar {
 8390                position,
 8391                reset: false,
 8392                mode,
 8393                goal_column: point_for_position.exact_unclipped.column(),
 8394            },
 8395            window,
 8396            cx,
 8397        );
 8398    }
 8399
 8400    fn update_edit_prediction_preview(
 8401        &mut self,
 8402        modifiers: &Modifiers,
 8403        window: &mut Window,
 8404        cx: &mut Context<Self>,
 8405    ) {
 8406        let mut modifiers_held = false;
 8407
 8408        // Check bindings for all granularities.
 8409        // If the user holds the key for Word, Line, or Full, we want to show the preview.
 8410        let granularities = [
 8411            EditPredictionGranularity::Full,
 8412            EditPredictionGranularity::Line,
 8413            EditPredictionGranularity::Word,
 8414        ];
 8415
 8416        for granularity in granularities {
 8417            if let Some(keystroke) = self
 8418                .accept_edit_prediction_keybind(granularity, window, cx)
 8419                .keystroke()
 8420            {
 8421                modifiers_held = modifiers_held
 8422                    || (keystroke.modifiers() == modifiers && keystroke.modifiers().modified());
 8423            }
 8424        }
 8425
 8426        if modifiers_held {
 8427            if matches!(
 8428                self.edit_prediction_preview,
 8429                EditPredictionPreview::Inactive { .. }
 8430            ) {
 8431                self.edit_prediction_preview = EditPredictionPreview::Active {
 8432                    previous_scroll_position: None,
 8433                    since: Instant::now(),
 8434                };
 8435
 8436                self.update_visible_edit_prediction(window, cx);
 8437                cx.notify();
 8438            }
 8439        } else if let EditPredictionPreview::Active {
 8440            previous_scroll_position,
 8441            since,
 8442        } = self.edit_prediction_preview
 8443        {
 8444            if let (Some(previous_scroll_position), Some(position_map)) =
 8445                (previous_scroll_position, self.last_position_map.as_ref())
 8446            {
 8447                self.set_scroll_position(
 8448                    previous_scroll_position
 8449                        .scroll_position(&position_map.snapshot.display_snapshot),
 8450                    window,
 8451                    cx,
 8452                );
 8453            }
 8454
 8455            self.edit_prediction_preview = EditPredictionPreview::Inactive {
 8456                released_too_fast: since.elapsed() < Duration::from_millis(200),
 8457            };
 8458            self.clear_row_highlights::<EditPredictionPreview>();
 8459            self.update_visible_edit_prediction(window, cx);
 8460            cx.notify();
 8461        }
 8462    }
 8463
 8464    fn update_visible_edit_prediction(
 8465        &mut self,
 8466        _window: &mut Window,
 8467        cx: &mut Context<Self>,
 8468    ) -> Option<()> {
 8469        if self.ime_transaction.is_some() {
 8470            self.discard_edit_prediction(EditPredictionDiscardReason::Ignored, cx);
 8471            return None;
 8472        }
 8473
 8474        let selection = self.selections.newest_anchor();
 8475        let cursor = selection.head();
 8476        let multibuffer = self.buffer.read(cx).snapshot(cx);
 8477
 8478        // Check project-level disable_ai setting for the current buffer
 8479        if let Some((buffer, _)) = self.buffer.read(cx).text_anchor_for_position(cursor, cx) {
 8480            if DisableAiSettings::is_ai_disabled_for_buffer(Some(&buffer), cx) {
 8481                return None;
 8482            }
 8483        }
 8484        let offset_selection = selection.map(|endpoint| endpoint.to_offset(&multibuffer));
 8485        let excerpt_id = cursor.excerpt_id;
 8486
 8487        let show_in_menu = self.show_edit_predictions_in_menu();
 8488        let completions_menu_has_precedence = !show_in_menu
 8489            && (self.context_menu.borrow().is_some()
 8490                || (!self.completion_tasks.is_empty() && !self.has_active_edit_prediction()));
 8491
 8492        if completions_menu_has_precedence
 8493            || !offset_selection.is_empty()
 8494            || self
 8495                .active_edit_prediction
 8496                .as_ref()
 8497                .is_some_and(|completion| {
 8498                    let Some(invalidation_range) = completion.invalidation_range.as_ref() else {
 8499                        return false;
 8500                    };
 8501                    let invalidation_range = invalidation_range.to_offset(&multibuffer);
 8502                    let invalidation_range = invalidation_range.start..=invalidation_range.end;
 8503                    !invalidation_range.contains(&offset_selection.head())
 8504                })
 8505        {
 8506            self.discard_edit_prediction(EditPredictionDiscardReason::Ignored, cx);
 8507            return None;
 8508        }
 8509
 8510        self.take_active_edit_prediction(cx);
 8511        let Some(provider) = self.edit_prediction_provider() else {
 8512            self.edit_prediction_settings = EditPredictionSettings::Disabled;
 8513            return None;
 8514        };
 8515
 8516        let (buffer, cursor_buffer_position) =
 8517            self.buffer.read(cx).text_anchor_for_position(cursor, cx)?;
 8518
 8519        self.edit_prediction_settings =
 8520            self.edit_prediction_settings_at_position(&buffer, cursor_buffer_position, cx);
 8521
 8522        self.edit_prediction_indent_conflict = multibuffer.is_line_whitespace_upto(cursor);
 8523
 8524        if self.edit_prediction_indent_conflict {
 8525            let cursor_point = cursor.to_point(&multibuffer);
 8526            let mut suggested_indent = None;
 8527            multibuffer.suggested_indents_callback(
 8528                cursor_point.row..cursor_point.row + 1,
 8529                &mut |_, indent| {
 8530                    suggested_indent = Some(indent);
 8531                    ControlFlow::Break(())
 8532                },
 8533                cx,
 8534            );
 8535
 8536            if let Some(indent) = suggested_indent
 8537                && indent.len == cursor_point.column
 8538            {
 8539                self.edit_prediction_indent_conflict = false;
 8540            }
 8541        }
 8542
 8543        let edit_prediction = provider.suggest(&buffer, cursor_buffer_position, cx)?;
 8544
 8545        let (completion_id, edits, predicted_cursor_position, edit_preview) = match edit_prediction
 8546        {
 8547            edit_prediction_types::EditPrediction::Local {
 8548                id,
 8549                edits,
 8550                cursor_position,
 8551                edit_preview,
 8552            } => (id, edits, cursor_position, edit_preview),
 8553            edit_prediction_types::EditPrediction::Jump {
 8554                id,
 8555                snapshot,
 8556                target,
 8557            } => {
 8558                if let Some(provider) = &self.edit_prediction_provider {
 8559                    provider.provider.did_show(SuggestionDisplayType::Jump, cx);
 8560                }
 8561                self.stale_edit_prediction_in_menu = None;
 8562                self.active_edit_prediction = Some(EditPredictionState {
 8563                    inlay_ids: vec![],
 8564                    completion: EditPrediction::MoveOutside { snapshot, target },
 8565                    completion_id: id,
 8566                    invalidation_range: None,
 8567                });
 8568                cx.notify();
 8569                return Some(());
 8570            }
 8571        };
 8572
 8573        let edits = edits
 8574            .into_iter()
 8575            .flat_map(|(range, new_text)| {
 8576                Some((
 8577                    multibuffer.anchor_range_in_excerpt(excerpt_id, range)?,
 8578                    new_text,
 8579                ))
 8580            })
 8581            .collect::<Vec<_>>();
 8582        if edits.is_empty() {
 8583            return None;
 8584        }
 8585
 8586        let cursor_position = predicted_cursor_position.and_then(|predicted| {
 8587            let anchor = multibuffer.anchor_in_excerpt(excerpt_id, predicted.anchor)?;
 8588            Some((anchor, predicted.offset))
 8589        });
 8590
 8591        let first_edit_start = edits.first().unwrap().0.start;
 8592        let first_edit_start_point = first_edit_start.to_point(&multibuffer);
 8593        let edit_start_row = first_edit_start_point.row.saturating_sub(2);
 8594
 8595        let last_edit_end = edits.last().unwrap().0.end;
 8596        let last_edit_end_point = last_edit_end.to_point(&multibuffer);
 8597        let edit_end_row = cmp::min(multibuffer.max_point().row, last_edit_end_point.row + 2);
 8598
 8599        let cursor_row = cursor.to_point(&multibuffer).row;
 8600
 8601        let snapshot = multibuffer.buffer_for_excerpt(excerpt_id).cloned()?;
 8602
 8603        let mut inlay_ids = Vec::new();
 8604        let invalidation_row_range;
 8605        let move_invalidation_row_range = if cursor_row < edit_start_row {
 8606            Some(cursor_row..edit_end_row)
 8607        } else if cursor_row > edit_end_row {
 8608            Some(edit_start_row..cursor_row)
 8609        } else {
 8610            None
 8611        };
 8612        let supports_jump = self
 8613            .edit_prediction_provider
 8614            .as_ref()
 8615            .map(|provider| provider.provider.supports_jump_to_edit())
 8616            .unwrap_or(true);
 8617
 8618        let is_move = supports_jump
 8619            && (move_invalidation_row_range.is_some() || self.edit_predictions_hidden_for_vim_mode);
 8620        let completion = if is_move {
 8621            if let Some(provider) = &self.edit_prediction_provider {
 8622                provider.provider.did_show(SuggestionDisplayType::Jump, cx);
 8623            }
 8624            invalidation_row_range =
 8625                move_invalidation_row_range.unwrap_or(edit_start_row..edit_end_row);
 8626            let target = first_edit_start;
 8627            EditPrediction::MoveWithin { target, snapshot }
 8628        } else {
 8629            let show_completions_in_buffer = !self.edit_prediction_visible_in_cursor_popover(true)
 8630                && !self.edit_predictions_hidden_for_vim_mode;
 8631
 8632            let display_mode = if all_edits_insertions_or_deletions(&edits, &multibuffer) {
 8633                if provider.show_tab_accept_marker() {
 8634                    EditDisplayMode::TabAccept
 8635                } else {
 8636                    EditDisplayMode::Inline
 8637                }
 8638            } else {
 8639                EditDisplayMode::DiffPopover
 8640            };
 8641
 8642            if show_completions_in_buffer {
 8643                if let Some(provider) = &self.edit_prediction_provider {
 8644                    let suggestion_display_type = match display_mode {
 8645                        EditDisplayMode::DiffPopover => SuggestionDisplayType::DiffPopover,
 8646                        EditDisplayMode::Inline | EditDisplayMode::TabAccept => {
 8647                            SuggestionDisplayType::GhostText
 8648                        }
 8649                    };
 8650                    provider.provider.did_show(suggestion_display_type, cx);
 8651                }
 8652                if edits
 8653                    .iter()
 8654                    .all(|(range, _)| range.to_offset(&multibuffer).is_empty())
 8655                {
 8656                    let mut inlays = Vec::new();
 8657                    for (range, new_text) in &edits {
 8658                        let inlay = Inlay::edit_prediction(
 8659                            post_inc(&mut self.next_inlay_id),
 8660                            range.start,
 8661                            new_text.as_ref(),
 8662                        );
 8663                        inlay_ids.push(inlay.id);
 8664                        inlays.push(inlay);
 8665                    }
 8666
 8667                    self.splice_inlays(&[], inlays, cx);
 8668                } else {
 8669                    let background_color = cx.theme().status().deleted_background;
 8670                    self.highlight_text(
 8671                        HighlightKey::EditPredictionHighlight,
 8672                        edits.iter().map(|(range, _)| range.clone()).collect(),
 8673                        HighlightStyle {
 8674                            background_color: Some(background_color),
 8675                            ..Default::default()
 8676                        },
 8677                        cx,
 8678                    );
 8679                }
 8680            }
 8681
 8682            invalidation_row_range = edit_start_row..edit_end_row;
 8683
 8684            EditPrediction::Edit {
 8685                edits,
 8686                cursor_position,
 8687                edit_preview,
 8688                display_mode,
 8689                snapshot,
 8690            }
 8691        };
 8692
 8693        let invalidation_range = multibuffer
 8694            .anchor_before(Point::new(invalidation_row_range.start, 0))
 8695            ..multibuffer.anchor_after(Point::new(
 8696                invalidation_row_range.end,
 8697                multibuffer.line_len(MultiBufferRow(invalidation_row_range.end)),
 8698            ));
 8699
 8700        self.stale_edit_prediction_in_menu = None;
 8701        self.active_edit_prediction = Some(EditPredictionState {
 8702            inlay_ids,
 8703            completion,
 8704            completion_id,
 8705            invalidation_range: Some(invalidation_range),
 8706        });
 8707
 8708        cx.notify();
 8709
 8710        Some(())
 8711    }
 8712
 8713    pub fn edit_prediction_provider(&self) -> Option<Arc<dyn EditPredictionDelegateHandle>> {
 8714        Some(self.edit_prediction_provider.as_ref()?.provider.clone())
 8715    }
 8716
 8717    fn clear_tasks(&mut self) {
 8718        self.tasks.clear()
 8719    }
 8720
 8721    fn insert_tasks(&mut self, key: (BufferId, BufferRow), value: RunnableTasks) {
 8722        if self.tasks.insert(key, value).is_some() {
 8723            // This case should hopefully be rare, but just in case...
 8724            log::error!(
 8725                "multiple different run targets found on a single line, only the last target will be rendered"
 8726            )
 8727        }
 8728    }
 8729
 8730    /// Get all display points of breakpoints that will be rendered within editor
 8731    ///
 8732    /// This function is used to handle overlaps between breakpoints and Code action/runner symbol.
 8733    /// It's also used to set the color of line numbers with breakpoints to the breakpoint color.
 8734    /// TODO debugger: Use this function to color toggle symbols that house nested breakpoints
 8735    fn active_breakpoints(
 8736        &self,
 8737        range: Range<DisplayRow>,
 8738        window: &mut Window,
 8739        cx: &mut Context<Self>,
 8740    ) -> HashMap<DisplayRow, (Anchor, Breakpoint, Option<BreakpointSessionState>)> {
 8741        let mut breakpoint_display_points = HashMap::default();
 8742
 8743        let Some(breakpoint_store) = self.breakpoint_store.clone() else {
 8744            return breakpoint_display_points;
 8745        };
 8746
 8747        let snapshot = self.snapshot(window, cx);
 8748
 8749        let multi_buffer_snapshot = snapshot.buffer_snapshot();
 8750        let Some(project) = self.project() else {
 8751            return breakpoint_display_points;
 8752        };
 8753
 8754        let range = snapshot.display_point_to_point(DisplayPoint::new(range.start, 0), Bias::Left)
 8755            ..snapshot.display_point_to_point(DisplayPoint::new(range.end, 0), Bias::Right);
 8756
 8757        for (buffer_snapshot, range, excerpt_id) in
 8758            multi_buffer_snapshot.range_to_buffer_ranges(range.start..=range.end)
 8759        {
 8760            let Some(buffer) = project
 8761                .read(cx)
 8762                .buffer_for_id(buffer_snapshot.remote_id(), cx)
 8763            else {
 8764                continue;
 8765            };
 8766            let breakpoints = breakpoint_store.read(cx).breakpoints(
 8767                &buffer,
 8768                Some(
 8769                    buffer_snapshot.anchor_before(range.start)
 8770                        ..buffer_snapshot.anchor_after(range.end),
 8771                ),
 8772                buffer_snapshot,
 8773                cx,
 8774            );
 8775            for (breakpoint, state) in breakpoints {
 8776                let multi_buffer_anchor = Anchor::in_buffer(excerpt_id, breakpoint.position);
 8777                let position = multi_buffer_anchor
 8778                    .to_point(&multi_buffer_snapshot)
 8779                    .to_display_point(&snapshot);
 8780
 8781                breakpoint_display_points.insert(
 8782                    position.row(),
 8783                    (multi_buffer_anchor, breakpoint.bp.clone(), state),
 8784                );
 8785            }
 8786        }
 8787
 8788        breakpoint_display_points
 8789    }
 8790
 8791    fn breakpoint_context_menu(
 8792        &self,
 8793        anchor: Anchor,
 8794        window: &mut Window,
 8795        cx: &mut Context<Self>,
 8796    ) -> Entity<ui::ContextMenu> {
 8797        let weak_editor = cx.weak_entity();
 8798        let focus_handle = self.focus_handle(cx);
 8799
 8800        let row = self
 8801            .buffer
 8802            .read(cx)
 8803            .snapshot(cx)
 8804            .summary_for_anchor::<Point>(&anchor)
 8805            .row;
 8806
 8807        let breakpoint = self
 8808            .breakpoint_at_row(row, window, cx)
 8809            .map(|(anchor, bp)| (anchor, Arc::from(bp)));
 8810
 8811        let log_breakpoint_msg = if breakpoint.as_ref().is_some_and(|bp| bp.1.message.is_some()) {
 8812            "Edit Log Breakpoint"
 8813        } else {
 8814            "Set Log Breakpoint"
 8815        };
 8816
 8817        let condition_breakpoint_msg = if breakpoint
 8818            .as_ref()
 8819            .is_some_and(|bp| bp.1.condition.is_some())
 8820        {
 8821            "Edit Condition Breakpoint"
 8822        } else {
 8823            "Set Condition Breakpoint"
 8824        };
 8825
 8826        let hit_condition_breakpoint_msg = if breakpoint
 8827            .as_ref()
 8828            .is_some_and(|bp| bp.1.hit_condition.is_some())
 8829        {
 8830            "Edit Hit Condition Breakpoint"
 8831        } else {
 8832            "Set Hit Condition Breakpoint"
 8833        };
 8834
 8835        let set_breakpoint_msg = if breakpoint.as_ref().is_some() {
 8836            "Unset Breakpoint"
 8837        } else {
 8838            "Set Breakpoint"
 8839        };
 8840
 8841        let run_to_cursor = window.is_action_available(&RunToCursor, cx);
 8842
 8843        let toggle_state_msg = breakpoint.as_ref().map_or(None, |bp| match bp.1.state {
 8844            BreakpointState::Enabled => Some("Disable"),
 8845            BreakpointState::Disabled => Some("Enable"),
 8846        });
 8847
 8848        let (anchor, breakpoint) =
 8849            breakpoint.unwrap_or_else(|| (anchor, Arc::new(Breakpoint::new_standard())));
 8850
 8851        ui::ContextMenu::build(window, cx, |menu, _, _cx| {
 8852            menu.on_blur_subscription(Subscription::new(|| {}))
 8853                .context(focus_handle)
 8854                .when(run_to_cursor, |this| {
 8855                    let weak_editor = weak_editor.clone();
 8856                    this.entry("Run to cursor", None, move |window, cx| {
 8857                        weak_editor
 8858                            .update(cx, |editor, cx| {
 8859                                editor.change_selections(
 8860                                    SelectionEffects::no_scroll(),
 8861                                    window,
 8862                                    cx,
 8863                                    |s| s.select_ranges([Point::new(row, 0)..Point::new(row, 0)]),
 8864                                );
 8865                            })
 8866                            .ok();
 8867
 8868                        window.dispatch_action(Box::new(RunToCursor), cx);
 8869                    })
 8870                    .separator()
 8871                })
 8872                .when_some(toggle_state_msg, |this, msg| {
 8873                    this.entry(msg, None, {
 8874                        let weak_editor = weak_editor.clone();
 8875                        let breakpoint = breakpoint.clone();
 8876                        move |_window, cx| {
 8877                            weak_editor
 8878                                .update(cx, |this, cx| {
 8879                                    this.edit_breakpoint_at_anchor(
 8880                                        anchor,
 8881                                        breakpoint.as_ref().clone(),
 8882                                        BreakpointEditAction::InvertState,
 8883                                        cx,
 8884                                    );
 8885                                })
 8886                                .log_err();
 8887                        }
 8888                    })
 8889                })
 8890                .entry(set_breakpoint_msg, None, {
 8891                    let weak_editor = weak_editor.clone();
 8892                    let breakpoint = breakpoint.clone();
 8893                    move |_window, cx| {
 8894                        weak_editor
 8895                            .update(cx, |this, cx| {
 8896                                this.edit_breakpoint_at_anchor(
 8897                                    anchor,
 8898                                    breakpoint.as_ref().clone(),
 8899                                    BreakpointEditAction::Toggle,
 8900                                    cx,
 8901                                );
 8902                            })
 8903                            .log_err();
 8904                    }
 8905                })
 8906                .entry(log_breakpoint_msg, None, {
 8907                    let breakpoint = breakpoint.clone();
 8908                    let weak_editor = weak_editor.clone();
 8909                    move |window, cx| {
 8910                        weak_editor
 8911                            .update(cx, |this, cx| {
 8912                                this.add_edit_breakpoint_block(
 8913                                    anchor,
 8914                                    breakpoint.as_ref(),
 8915                                    BreakpointPromptEditAction::Log,
 8916                                    window,
 8917                                    cx,
 8918                                );
 8919                            })
 8920                            .log_err();
 8921                    }
 8922                })
 8923                .entry(condition_breakpoint_msg, None, {
 8924                    let breakpoint = breakpoint.clone();
 8925                    let weak_editor = weak_editor.clone();
 8926                    move |window, cx| {
 8927                        weak_editor
 8928                            .update(cx, |this, cx| {
 8929                                this.add_edit_breakpoint_block(
 8930                                    anchor,
 8931                                    breakpoint.as_ref(),
 8932                                    BreakpointPromptEditAction::Condition,
 8933                                    window,
 8934                                    cx,
 8935                                );
 8936                            })
 8937                            .log_err();
 8938                    }
 8939                })
 8940                .entry(hit_condition_breakpoint_msg, None, move |window, cx| {
 8941                    weak_editor
 8942                        .update(cx, |this, cx| {
 8943                            this.add_edit_breakpoint_block(
 8944                                anchor,
 8945                                breakpoint.as_ref(),
 8946                                BreakpointPromptEditAction::HitCondition,
 8947                                window,
 8948                                cx,
 8949                            );
 8950                        })
 8951                        .log_err();
 8952                })
 8953        })
 8954    }
 8955
 8956    fn render_breakpoint(
 8957        &self,
 8958        position: Anchor,
 8959        row: DisplayRow,
 8960        breakpoint: &Breakpoint,
 8961        state: Option<BreakpointSessionState>,
 8962        cx: &mut Context<Self>,
 8963    ) -> IconButton {
 8964        let is_rejected = state.is_some_and(|s| !s.verified);
 8965        // Is it a breakpoint that shows up when hovering over gutter?
 8966        let (is_phantom, collides_with_existing) = self.gutter_breakpoint_indicator.0.map_or(
 8967            (false, false),
 8968            |PhantomBreakpointIndicator {
 8969                 is_active,
 8970                 display_row,
 8971                 collides_with_existing_breakpoint,
 8972             }| {
 8973                (
 8974                    is_active && display_row == row,
 8975                    collides_with_existing_breakpoint,
 8976                )
 8977            },
 8978        );
 8979
 8980        let (color, icon) = {
 8981            let icon = match (&breakpoint.message.is_some(), breakpoint.is_disabled()) {
 8982                (false, false) => ui::IconName::DebugBreakpoint,
 8983                (true, false) => ui::IconName::DebugLogBreakpoint,
 8984                (false, true) => ui::IconName::DebugDisabledBreakpoint,
 8985                (true, true) => ui::IconName::DebugDisabledLogBreakpoint,
 8986            };
 8987
 8988            let theme_colors = cx.theme().colors();
 8989
 8990            let color = if is_phantom {
 8991                if collides_with_existing {
 8992                    Color::Custom(
 8993                        theme_colors
 8994                            .debugger_accent
 8995                            .blend(theme_colors.text.opacity(0.6)),
 8996                    )
 8997                } else {
 8998                    Color::Hint
 8999                }
 9000            } else if is_rejected {
 9001                Color::Disabled
 9002            } else {
 9003                Color::Debugger
 9004            };
 9005
 9006            (color, icon)
 9007        };
 9008
 9009        let breakpoint = Arc::from(breakpoint.clone());
 9010
 9011        let alt_as_text = gpui::Keystroke {
 9012            modifiers: Modifiers::secondary_key(),
 9013            ..Default::default()
 9014        };
 9015        let primary_action_text = if breakpoint.is_disabled() {
 9016            "Enable breakpoint"
 9017        } else if is_phantom && !collides_with_existing {
 9018            "Set breakpoint"
 9019        } else {
 9020            "Unset breakpoint"
 9021        };
 9022        let focus_handle = self.focus_handle.clone();
 9023
 9024        let meta = if is_rejected {
 9025            SharedString::from("No executable code is associated with this line.")
 9026        } else if collides_with_existing && !breakpoint.is_disabled() {
 9027            SharedString::from(format!(
 9028                "{alt_as_text}-click to disable,\nright-click for more options."
 9029            ))
 9030        } else {
 9031            SharedString::from("Right-click for more options.")
 9032        };
 9033        IconButton::new(("breakpoint_indicator", row.0 as usize), icon)
 9034            .icon_size(IconSize::XSmall)
 9035            .size(ui::ButtonSize::None)
 9036            .when(is_rejected, |this| {
 9037                this.indicator(Indicator::icon(Icon::new(IconName::Warning)).color(Color::Warning))
 9038            })
 9039            .icon_color(color)
 9040            .style(ButtonStyle::Transparent)
 9041            .on_click(cx.listener({
 9042                move |editor, event: &ClickEvent, window, cx| {
 9043                    let edit_action = if event.modifiers().platform || breakpoint.is_disabled() {
 9044                        BreakpointEditAction::InvertState
 9045                    } else {
 9046                        BreakpointEditAction::Toggle
 9047                    };
 9048
 9049                    window.focus(&editor.focus_handle(cx), cx);
 9050                    editor.update_breakpoint_collision_on_toggle(row, &edit_action);
 9051                    editor.edit_breakpoint_at_anchor(
 9052                        position,
 9053                        breakpoint.as_ref().clone(),
 9054                        edit_action,
 9055                        cx,
 9056                    );
 9057                }
 9058            }))
 9059            .on_right_click(cx.listener(move |editor, event: &ClickEvent, window, cx| {
 9060                editor.set_breakpoint_context_menu(
 9061                    row,
 9062                    Some(position),
 9063                    event.position(),
 9064                    window,
 9065                    cx,
 9066                );
 9067            }))
 9068            .tooltip(move |_window, cx| {
 9069                Tooltip::with_meta_in(
 9070                    primary_action_text,
 9071                    Some(&ToggleBreakpoint),
 9072                    meta.clone(),
 9073                    &focus_handle,
 9074                    cx,
 9075                )
 9076            })
 9077    }
 9078
 9079    fn build_tasks_context(
 9080        project: &Entity<Project>,
 9081        buffer: &Entity<Buffer>,
 9082        buffer_row: u32,
 9083        tasks: &Arc<RunnableTasks>,
 9084        cx: &mut Context<Self>,
 9085    ) -> Task<Option<task::TaskContext>> {
 9086        let position = Point::new(buffer_row, tasks.column);
 9087        let range_start = buffer.read(cx).anchor_at(position, Bias::Right);
 9088        let location = Location {
 9089            buffer: buffer.clone(),
 9090            range: range_start..range_start,
 9091        };
 9092        // Fill in the environmental variables from the tree-sitter captures
 9093        let mut captured_task_variables = TaskVariables::default();
 9094        for (capture_name, value) in tasks.extra_variables.clone() {
 9095            captured_task_variables.insert(
 9096                task::VariableName::Custom(capture_name.into()),
 9097                value.clone(),
 9098            );
 9099        }
 9100        project.update(cx, |project, cx| {
 9101            project.task_store().update(cx, |task_store, cx| {
 9102                task_store.task_context_for_location(captured_task_variables, location, cx)
 9103            })
 9104        })
 9105    }
 9106
 9107    pub fn spawn_nearest_task(
 9108        &mut self,
 9109        action: &SpawnNearestTask,
 9110        window: &mut Window,
 9111        cx: &mut Context<Self>,
 9112    ) {
 9113        let Some((workspace, _)) = self.workspace.clone() else {
 9114            return;
 9115        };
 9116        let Some(project) = self.project.clone() else {
 9117            return;
 9118        };
 9119
 9120        // Try to find a closest, enclosing node using tree-sitter that has a task
 9121        let Some((buffer, buffer_row, tasks)) = self
 9122            .find_enclosing_node_task(cx)
 9123            // Or find the task that's closest in row-distance.
 9124            .or_else(|| self.find_closest_task(cx))
 9125        else {
 9126            return;
 9127        };
 9128
 9129        let reveal_strategy = action.reveal;
 9130        let task_context = Self::build_tasks_context(&project, &buffer, buffer_row, &tasks, cx);
 9131        cx.spawn_in(window, async move |_, cx| {
 9132            let context = task_context.await?;
 9133            let (task_source_kind, mut resolved_task) = tasks.resolve(&context).next()?;
 9134
 9135            let resolved = &mut resolved_task.resolved;
 9136            resolved.reveal = reveal_strategy;
 9137
 9138            workspace
 9139                .update_in(cx, |workspace, window, cx| {
 9140                    workspace.schedule_resolved_task(
 9141                        task_source_kind,
 9142                        resolved_task,
 9143                        false,
 9144                        window,
 9145                        cx,
 9146                    );
 9147                })
 9148                .ok()
 9149        })
 9150        .detach();
 9151    }
 9152
 9153    fn find_closest_task(
 9154        &mut self,
 9155        cx: &mut Context<Self>,
 9156    ) -> Option<(Entity<Buffer>, u32, Arc<RunnableTasks>)> {
 9157        let cursor_row = self
 9158            .selections
 9159            .newest_adjusted(&self.display_snapshot(cx))
 9160            .head()
 9161            .row;
 9162
 9163        let ((buffer_id, row), tasks) = self
 9164            .tasks
 9165            .iter()
 9166            .min_by_key(|((_, row), _)| cursor_row.abs_diff(*row))?;
 9167
 9168        let buffer = self.buffer.read(cx).buffer(*buffer_id)?;
 9169        let tasks = Arc::new(tasks.to_owned());
 9170        Some((buffer, *row, tasks))
 9171    }
 9172
 9173    fn find_enclosing_node_task(
 9174        &mut self,
 9175        cx: &mut Context<Self>,
 9176    ) -> Option<(Entity<Buffer>, u32, Arc<RunnableTasks>)> {
 9177        let snapshot = self.buffer.read(cx).snapshot(cx);
 9178        let offset = self
 9179            .selections
 9180            .newest::<MultiBufferOffset>(&self.display_snapshot(cx))
 9181            .head();
 9182        let mut excerpt = snapshot.excerpt_containing(offset..offset)?;
 9183        let offset = excerpt.map_offset_to_buffer(offset);
 9184        let buffer_id = excerpt.buffer().remote_id();
 9185
 9186        let layer = excerpt.buffer().syntax_layer_at(offset)?;
 9187        let mut cursor = layer.node().walk();
 9188
 9189        while cursor.goto_first_child_for_byte(offset.0).is_some() {
 9190            if cursor.node().end_byte() == offset.0 {
 9191                cursor.goto_next_sibling();
 9192            }
 9193        }
 9194
 9195        // Ascend to the smallest ancestor that contains the range and has a task.
 9196        loop {
 9197            let node = cursor.node();
 9198            let node_range = node.byte_range();
 9199            let symbol_start_row = excerpt.buffer().offset_to_point(node.start_byte()).row;
 9200
 9201            // Check if this node contains our offset
 9202            if node_range.start <= offset.0 && node_range.end >= offset.0 {
 9203                // If it contains offset, check for task
 9204                if let Some(tasks) = self.tasks.get(&(buffer_id, symbol_start_row)) {
 9205                    let buffer = self.buffer.read(cx).buffer(buffer_id)?;
 9206                    return Some((buffer, symbol_start_row, Arc::new(tasks.to_owned())));
 9207                }
 9208            }
 9209
 9210            if !cursor.goto_parent() {
 9211                break;
 9212            }
 9213        }
 9214        None
 9215    }
 9216
 9217    fn render_run_indicator(
 9218        &self,
 9219        _style: &EditorStyle,
 9220        is_active: bool,
 9221        row: DisplayRow,
 9222        breakpoint: Option<(Anchor, Breakpoint, Option<BreakpointSessionState>)>,
 9223        cx: &mut Context<Self>,
 9224    ) -> IconButton {
 9225        let color = Color::Muted;
 9226        let position = breakpoint.as_ref().map(|(anchor, _, _)| *anchor);
 9227
 9228        IconButton::new(
 9229            ("run_indicator", row.0 as usize),
 9230            ui::IconName::PlayOutlined,
 9231        )
 9232        .shape(ui::IconButtonShape::Square)
 9233        .icon_size(IconSize::XSmall)
 9234        .icon_color(color)
 9235        .toggle_state(is_active)
 9236        .on_click(cx.listener(move |editor, e: &ClickEvent, window, cx| {
 9237            let quick_launch = match e {
 9238                ClickEvent::Keyboard(_) => true,
 9239                ClickEvent::Mouse(e) => e.down.button == MouseButton::Left,
 9240            };
 9241
 9242            window.focus(&editor.focus_handle(cx), cx);
 9243            editor.toggle_code_actions(
 9244                &ToggleCodeActions {
 9245                    deployed_from: Some(CodeActionSource::RunMenu(row)),
 9246                    quick_launch,
 9247                },
 9248                window,
 9249                cx,
 9250            );
 9251        }))
 9252        .on_right_click(cx.listener(move |editor, event: &ClickEvent, window, cx| {
 9253            editor.set_breakpoint_context_menu(row, position, event.position(), window, cx);
 9254        }))
 9255    }
 9256
 9257    pub fn context_menu_visible(&self) -> bool {
 9258        !self.edit_prediction_preview_is_active()
 9259            && self
 9260                .context_menu
 9261                .borrow()
 9262                .as_ref()
 9263                .is_some_and(|menu| menu.visible())
 9264    }
 9265
 9266    pub fn context_menu_origin(&self) -> Option<ContextMenuOrigin> {
 9267        self.context_menu
 9268            .borrow()
 9269            .as_ref()
 9270            .map(|menu| menu.origin())
 9271    }
 9272
 9273    pub fn set_context_menu_options(&mut self, options: ContextMenuOptions) {
 9274        self.context_menu_options = Some(options);
 9275    }
 9276
 9277    const EDIT_PREDICTION_POPOVER_PADDING_X: Pixels = px(24.);
 9278    const EDIT_PREDICTION_POPOVER_PADDING_Y: Pixels = px(2.);
 9279
 9280    fn render_edit_prediction_popover(
 9281        &mut self,
 9282        text_bounds: &Bounds<Pixels>,
 9283        content_origin: gpui::Point<Pixels>,
 9284        right_margin: Pixels,
 9285        editor_snapshot: &EditorSnapshot,
 9286        visible_row_range: Range<DisplayRow>,
 9287        scroll_top: ScrollOffset,
 9288        scroll_bottom: ScrollOffset,
 9289        line_layouts: &[LineWithInvisibles],
 9290        line_height: Pixels,
 9291        scroll_position: gpui::Point<ScrollOffset>,
 9292        scroll_pixel_position: gpui::Point<ScrollPixelOffset>,
 9293        newest_selection_head: Option<DisplayPoint>,
 9294        editor_width: Pixels,
 9295        style: &EditorStyle,
 9296        window: &mut Window,
 9297        cx: &mut App,
 9298    ) -> Option<(AnyElement, gpui::Point<Pixels>)> {
 9299        if self.mode().is_minimap() {
 9300            return None;
 9301        }
 9302        let active_edit_prediction = self.active_edit_prediction.as_ref()?;
 9303
 9304        if self.edit_prediction_visible_in_cursor_popover(true) {
 9305            return None;
 9306        }
 9307
 9308        match &active_edit_prediction.completion {
 9309            EditPrediction::MoveWithin { target, .. } => {
 9310                let target_display_point = target.to_display_point(editor_snapshot);
 9311
 9312                if self.edit_prediction_requires_modifier() {
 9313                    if !self.edit_prediction_preview_is_active() {
 9314                        return None;
 9315                    }
 9316
 9317                    self.render_edit_prediction_modifier_jump_popover(
 9318                        text_bounds,
 9319                        content_origin,
 9320                        visible_row_range,
 9321                        line_layouts,
 9322                        line_height,
 9323                        scroll_pixel_position,
 9324                        newest_selection_head,
 9325                        target_display_point,
 9326                        window,
 9327                        cx,
 9328                    )
 9329                } else {
 9330                    self.render_edit_prediction_eager_jump_popover(
 9331                        text_bounds,
 9332                        content_origin,
 9333                        editor_snapshot,
 9334                        visible_row_range,
 9335                        scroll_top,
 9336                        scroll_bottom,
 9337                        line_height,
 9338                        scroll_pixel_position,
 9339                        target_display_point,
 9340                        editor_width,
 9341                        window,
 9342                        cx,
 9343                    )
 9344                }
 9345            }
 9346            EditPrediction::Edit {
 9347                display_mode: EditDisplayMode::Inline,
 9348                ..
 9349            } => None,
 9350            EditPrediction::Edit {
 9351                display_mode: EditDisplayMode::TabAccept,
 9352                edits,
 9353                ..
 9354            } => {
 9355                let range = &edits.first()?.0;
 9356                let target_display_point = range.end.to_display_point(editor_snapshot);
 9357
 9358                self.render_edit_prediction_end_of_line_popover(
 9359                    "Accept",
 9360                    editor_snapshot,
 9361                    visible_row_range,
 9362                    target_display_point,
 9363                    line_height,
 9364                    scroll_pixel_position,
 9365                    content_origin,
 9366                    editor_width,
 9367                    window,
 9368                    cx,
 9369                )
 9370            }
 9371            EditPrediction::Edit {
 9372                edits,
 9373                edit_preview,
 9374                display_mode: EditDisplayMode::DiffPopover,
 9375                snapshot,
 9376                ..
 9377            } => self.render_edit_prediction_diff_popover(
 9378                text_bounds,
 9379                content_origin,
 9380                right_margin,
 9381                editor_snapshot,
 9382                visible_row_range,
 9383                line_layouts,
 9384                line_height,
 9385                scroll_position,
 9386                scroll_pixel_position,
 9387                newest_selection_head,
 9388                editor_width,
 9389                style,
 9390                edits,
 9391                edit_preview,
 9392                snapshot,
 9393                window,
 9394                cx,
 9395            ),
 9396            EditPrediction::MoveOutside { snapshot, .. } => {
 9397                let mut element = self
 9398                    .render_edit_prediction_jump_outside_popover(snapshot, window, cx)
 9399                    .into_any();
 9400
 9401                let size = element.layout_as_root(AvailableSpace::min_size(), window, cx);
 9402                let origin_x = text_bounds.size.width - size.width - px(30.);
 9403                let origin = text_bounds.origin + gpui::Point::new(origin_x, px(16.));
 9404                element.prepaint_at(origin, window, cx);
 9405
 9406                Some((element, origin))
 9407            }
 9408        }
 9409    }
 9410
 9411    fn render_edit_prediction_modifier_jump_popover(
 9412        &mut self,
 9413        text_bounds: &Bounds<Pixels>,
 9414        content_origin: gpui::Point<Pixels>,
 9415        visible_row_range: Range<DisplayRow>,
 9416        line_layouts: &[LineWithInvisibles],
 9417        line_height: Pixels,
 9418        scroll_pixel_position: gpui::Point<ScrollPixelOffset>,
 9419        newest_selection_head: Option<DisplayPoint>,
 9420        target_display_point: DisplayPoint,
 9421        window: &mut Window,
 9422        cx: &mut App,
 9423    ) -> Option<(AnyElement, gpui::Point<Pixels>)> {
 9424        let scrolled_content_origin =
 9425            content_origin - gpui::Point::new(scroll_pixel_position.x.into(), Pixels::ZERO);
 9426
 9427        const SCROLL_PADDING_Y: Pixels = px(12.);
 9428
 9429        if target_display_point.row() < visible_row_range.start {
 9430            return self.render_edit_prediction_scroll_popover(
 9431                |_| SCROLL_PADDING_Y,
 9432                IconName::ArrowUp,
 9433                visible_row_range,
 9434                line_layouts,
 9435                newest_selection_head,
 9436                scrolled_content_origin,
 9437                window,
 9438                cx,
 9439            );
 9440        } else if target_display_point.row() >= visible_row_range.end {
 9441            return self.render_edit_prediction_scroll_popover(
 9442                |size| text_bounds.size.height - size.height - SCROLL_PADDING_Y,
 9443                IconName::ArrowDown,
 9444                visible_row_range,
 9445                line_layouts,
 9446                newest_selection_head,
 9447                scrolled_content_origin,
 9448                window,
 9449                cx,
 9450            );
 9451        }
 9452
 9453        const POLE_WIDTH: Pixels = px(2.);
 9454
 9455        let line_layout =
 9456            line_layouts.get(target_display_point.row().minus(visible_row_range.start) as usize)?;
 9457        let target_column = target_display_point.column() as usize;
 9458
 9459        let target_x = line_layout.x_for_index(target_column);
 9460        let target_y = (target_display_point.row().as_f64() * f64::from(line_height))
 9461            - scroll_pixel_position.y;
 9462
 9463        let flag_on_right = target_x < text_bounds.size.width / 2.;
 9464
 9465        let mut border_color = Self::edit_prediction_callout_popover_border_color(cx);
 9466        border_color.l += 0.001;
 9467
 9468        let mut element = v_flex()
 9469            .items_end()
 9470            .when(flag_on_right, |el| el.items_start())
 9471            .child(if flag_on_right {
 9472                self.render_edit_prediction_line_popover("Jump", None, window, cx)
 9473                    .rounded_bl(px(0.))
 9474                    .rounded_tl(px(0.))
 9475                    .border_l_2()
 9476                    .border_color(border_color)
 9477            } else {
 9478                self.render_edit_prediction_line_popover("Jump", None, window, cx)
 9479                    .rounded_br(px(0.))
 9480                    .rounded_tr(px(0.))
 9481                    .border_r_2()
 9482                    .border_color(border_color)
 9483            })
 9484            .child(div().w(POLE_WIDTH).bg(border_color).h(line_height))
 9485            .into_any();
 9486
 9487        let size = element.layout_as_root(AvailableSpace::min_size(), window, cx);
 9488
 9489        let mut origin = scrolled_content_origin + point(target_x, target_y.into())
 9490            - point(
 9491                if flag_on_right {
 9492                    POLE_WIDTH
 9493                } else {
 9494                    size.width - POLE_WIDTH
 9495                },
 9496                size.height - line_height,
 9497            );
 9498
 9499        origin.x = origin.x.max(content_origin.x);
 9500
 9501        element.prepaint_at(origin, window, cx);
 9502
 9503        Some((element, origin))
 9504    }
 9505
 9506    fn render_edit_prediction_scroll_popover(
 9507        &mut self,
 9508        to_y: impl Fn(Size<Pixels>) -> Pixels,
 9509        scroll_icon: IconName,
 9510        visible_row_range: Range<DisplayRow>,
 9511        line_layouts: &[LineWithInvisibles],
 9512        newest_selection_head: Option<DisplayPoint>,
 9513        scrolled_content_origin: gpui::Point<Pixels>,
 9514        window: &mut Window,
 9515        cx: &mut App,
 9516    ) -> Option<(AnyElement, gpui::Point<Pixels>)> {
 9517        let mut element = self
 9518            .render_edit_prediction_line_popover("Scroll", Some(scroll_icon), window, cx)
 9519            .into_any();
 9520
 9521        let size = element.layout_as_root(AvailableSpace::min_size(), window, cx);
 9522
 9523        let cursor = newest_selection_head?;
 9524        let cursor_row_layout =
 9525            line_layouts.get(cursor.row().minus(visible_row_range.start) as usize)?;
 9526        let cursor_column = cursor.column() as usize;
 9527
 9528        let cursor_character_x = cursor_row_layout.x_for_index(cursor_column);
 9529
 9530        let origin = scrolled_content_origin + point(cursor_character_x, to_y(size));
 9531
 9532        element.prepaint_at(origin, window, cx);
 9533        Some((element, origin))
 9534    }
 9535
 9536    fn render_edit_prediction_eager_jump_popover(
 9537        &mut self,
 9538        text_bounds: &Bounds<Pixels>,
 9539        content_origin: gpui::Point<Pixels>,
 9540        editor_snapshot: &EditorSnapshot,
 9541        visible_row_range: Range<DisplayRow>,
 9542        scroll_top: ScrollOffset,
 9543        scroll_bottom: ScrollOffset,
 9544        line_height: Pixels,
 9545        scroll_pixel_position: gpui::Point<ScrollPixelOffset>,
 9546        target_display_point: DisplayPoint,
 9547        editor_width: Pixels,
 9548        window: &mut Window,
 9549        cx: &mut App,
 9550    ) -> Option<(AnyElement, gpui::Point<Pixels>)> {
 9551        if target_display_point.row().as_f64() < scroll_top {
 9552            let mut element = self
 9553                .render_edit_prediction_line_popover(
 9554                    "Jump to Edit",
 9555                    Some(IconName::ArrowUp),
 9556                    window,
 9557                    cx,
 9558                )
 9559                .into_any();
 9560
 9561            let size = element.layout_as_root(AvailableSpace::min_size(), window, cx);
 9562            let offset = point(
 9563                (text_bounds.size.width - size.width) / 2.,
 9564                Self::EDIT_PREDICTION_POPOVER_PADDING_Y,
 9565            );
 9566
 9567            let origin = text_bounds.origin + offset;
 9568            element.prepaint_at(origin, window, cx);
 9569            Some((element, origin))
 9570        } else if (target_display_point.row().as_f64() + 1.) > scroll_bottom {
 9571            let mut element = self
 9572                .render_edit_prediction_line_popover(
 9573                    "Jump to Edit",
 9574                    Some(IconName::ArrowDown),
 9575                    window,
 9576                    cx,
 9577                )
 9578                .into_any();
 9579
 9580            let size = element.layout_as_root(AvailableSpace::min_size(), window, cx);
 9581            let offset = point(
 9582                (text_bounds.size.width - size.width) / 2.,
 9583                text_bounds.size.height - size.height - Self::EDIT_PREDICTION_POPOVER_PADDING_Y,
 9584            );
 9585
 9586            let origin = text_bounds.origin + offset;
 9587            element.prepaint_at(origin, window, cx);
 9588            Some((element, origin))
 9589        } else {
 9590            self.render_edit_prediction_end_of_line_popover(
 9591                "Jump to Edit",
 9592                editor_snapshot,
 9593                visible_row_range,
 9594                target_display_point,
 9595                line_height,
 9596                scroll_pixel_position,
 9597                content_origin,
 9598                editor_width,
 9599                window,
 9600                cx,
 9601            )
 9602        }
 9603    }
 9604
 9605    fn render_edit_prediction_end_of_line_popover(
 9606        self: &mut Editor,
 9607        label: &'static str,
 9608        editor_snapshot: &EditorSnapshot,
 9609        visible_row_range: Range<DisplayRow>,
 9610        target_display_point: DisplayPoint,
 9611        line_height: Pixels,
 9612        scroll_pixel_position: gpui::Point<ScrollPixelOffset>,
 9613        content_origin: gpui::Point<Pixels>,
 9614        editor_width: Pixels,
 9615        window: &mut Window,
 9616        cx: &mut App,
 9617    ) -> Option<(AnyElement, gpui::Point<Pixels>)> {
 9618        let target_line_end = DisplayPoint::new(
 9619            target_display_point.row(),
 9620            editor_snapshot.line_len(target_display_point.row()),
 9621        );
 9622
 9623        let mut element = self
 9624            .render_edit_prediction_line_popover(label, None, window, cx)
 9625            .into_any();
 9626
 9627        let size = element.layout_as_root(AvailableSpace::min_size(), window, cx);
 9628
 9629        let line_origin =
 9630            self.display_to_pixel_point(target_line_end, editor_snapshot, window, cx)?;
 9631
 9632        let start_point = content_origin - point(scroll_pixel_position.x.into(), Pixels::ZERO);
 9633        let mut origin = start_point
 9634            + line_origin
 9635            + point(Self::EDIT_PREDICTION_POPOVER_PADDING_X, Pixels::ZERO);
 9636        origin.x = origin.x.max(content_origin.x);
 9637
 9638        let max_x = content_origin.x + editor_width - size.width;
 9639
 9640        if origin.x > max_x {
 9641            let offset = line_height + Self::EDIT_PREDICTION_POPOVER_PADDING_Y;
 9642
 9643            let icon = if visible_row_range.contains(&(target_display_point.row() + 2)) {
 9644                origin.y += offset;
 9645                IconName::ArrowUp
 9646            } else {
 9647                origin.y -= offset;
 9648                IconName::ArrowDown
 9649            };
 9650
 9651            element = self
 9652                .render_edit_prediction_line_popover(label, Some(icon), window, cx)
 9653                .into_any();
 9654
 9655            let size = element.layout_as_root(AvailableSpace::min_size(), window, cx);
 9656
 9657            origin.x = content_origin.x + editor_width - size.width - px(2.);
 9658        }
 9659
 9660        element.prepaint_at(origin, window, cx);
 9661        Some((element, origin))
 9662    }
 9663
 9664    fn render_edit_prediction_diff_popover(
 9665        self: &Editor,
 9666        text_bounds: &Bounds<Pixels>,
 9667        content_origin: gpui::Point<Pixels>,
 9668        right_margin: Pixels,
 9669        editor_snapshot: &EditorSnapshot,
 9670        visible_row_range: Range<DisplayRow>,
 9671        line_layouts: &[LineWithInvisibles],
 9672        line_height: Pixels,
 9673        scroll_position: gpui::Point<ScrollOffset>,
 9674        scroll_pixel_position: gpui::Point<ScrollPixelOffset>,
 9675        newest_selection_head: Option<DisplayPoint>,
 9676        editor_width: Pixels,
 9677        style: &EditorStyle,
 9678        edits: &Vec<(Range<Anchor>, Arc<str>)>,
 9679        edit_preview: &Option<language::EditPreview>,
 9680        snapshot: &language::BufferSnapshot,
 9681        window: &mut Window,
 9682        cx: &mut App,
 9683    ) -> Option<(AnyElement, gpui::Point<Pixels>)> {
 9684        let edit_start = edits
 9685            .first()
 9686            .unwrap()
 9687            .0
 9688            .start
 9689            .to_display_point(editor_snapshot);
 9690        let edit_end = edits
 9691            .last()
 9692            .unwrap()
 9693            .0
 9694            .end
 9695            .to_display_point(editor_snapshot);
 9696
 9697        let is_visible = visible_row_range.contains(&edit_start.row())
 9698            || visible_row_range.contains(&edit_end.row());
 9699        if !is_visible {
 9700            return None;
 9701        }
 9702
 9703        let highlighted_edits = if let Some(edit_preview) = edit_preview.as_ref() {
 9704            crate::edit_prediction_edit_text(snapshot, edits, edit_preview, false, cx)
 9705        } else {
 9706            // Fallback for providers without edit_preview
 9707            crate::edit_prediction_fallback_text(edits, cx)
 9708        };
 9709
 9710        let styled_text = highlighted_edits.to_styled_text(&style.text);
 9711        let line_count = highlighted_edits.text.lines().count();
 9712
 9713        const BORDER_WIDTH: Pixels = px(1.);
 9714
 9715        let keybind = self.render_edit_prediction_accept_keybind(window, cx);
 9716        let has_keybind = keybind.is_some();
 9717
 9718        let mut element = h_flex()
 9719            .items_start()
 9720            .child(
 9721                h_flex()
 9722                    .bg(cx.theme().colors().editor_background)
 9723                    .border(BORDER_WIDTH)
 9724                    .shadow_xs()
 9725                    .border_color(cx.theme().colors().border)
 9726                    .rounded_l_lg()
 9727                    .when(line_count > 1, |el| el.rounded_br_lg())
 9728                    .pr_1()
 9729                    .child(styled_text),
 9730            )
 9731            .child(
 9732                h_flex()
 9733                    .h(line_height + BORDER_WIDTH * 2.)
 9734                    .px_1p5()
 9735                    .gap_1()
 9736                    // Workaround: For some reason, there's a gap if we don't do this
 9737                    .ml(-BORDER_WIDTH)
 9738                    .shadow(vec![gpui::BoxShadow {
 9739                        color: gpui::black().opacity(0.05),
 9740                        offset: point(px(1.), px(1.)),
 9741                        blur_radius: px(2.),
 9742                        spread_radius: px(0.),
 9743                    }])
 9744                    .bg(Editor::edit_prediction_line_popover_bg_color(cx))
 9745                    .border(BORDER_WIDTH)
 9746                    .border_color(cx.theme().colors().border)
 9747                    .rounded_r_lg()
 9748                    .id("edit_prediction_diff_popover_keybind")
 9749                    .when(!has_keybind, |el| {
 9750                        let status_colors = cx.theme().status();
 9751
 9752                        el.bg(status_colors.error_background)
 9753                            .border_color(status_colors.error.opacity(0.6))
 9754                            .child(Icon::new(IconName::Info).color(Color::Error))
 9755                            .cursor_default()
 9756                            .hoverable_tooltip(move |_window, cx| {
 9757                                cx.new(|_| MissingEditPredictionKeybindingTooltip).into()
 9758                            })
 9759                    })
 9760                    .children(keybind),
 9761            )
 9762            .into_any();
 9763
 9764        let longest_row =
 9765            editor_snapshot.longest_row_in_range(edit_start.row()..edit_end.row() + 1);
 9766        let longest_line_width = if visible_row_range.contains(&longest_row) {
 9767            line_layouts[(longest_row.0 - visible_row_range.start.0) as usize].width
 9768        } else {
 9769            layout_line(
 9770                longest_row,
 9771                editor_snapshot,
 9772                style,
 9773                editor_width,
 9774                |_| false,
 9775                window,
 9776                cx,
 9777            )
 9778            .width
 9779        };
 9780
 9781        let viewport_bounds =
 9782            Bounds::new(Default::default(), window.viewport_size()).extend(Edges {
 9783                right: -right_margin,
 9784                ..Default::default()
 9785            });
 9786
 9787        let x_after_longest = Pixels::from(
 9788            ScrollPixelOffset::from(
 9789                text_bounds.origin.x + longest_line_width + Self::EDIT_PREDICTION_POPOVER_PADDING_X,
 9790            ) - scroll_pixel_position.x,
 9791        );
 9792
 9793        let element_bounds = element.layout_as_root(AvailableSpace::min_size(), window, cx);
 9794
 9795        // Fully visible if it can be displayed within the window (allow overlapping other
 9796        // panes). However, this is only allowed if the popover starts within text_bounds.
 9797        let can_position_to_the_right = x_after_longest < text_bounds.right()
 9798            && x_after_longest + element_bounds.width < viewport_bounds.right();
 9799
 9800        let mut origin = if can_position_to_the_right {
 9801            point(
 9802                x_after_longest,
 9803                text_bounds.origin.y
 9804                    + Pixels::from(
 9805                        edit_start.row().as_f64() * ScrollPixelOffset::from(line_height)
 9806                            - scroll_pixel_position.y,
 9807                    ),
 9808            )
 9809        } else {
 9810            let cursor_row = newest_selection_head.map(|head| head.row());
 9811            let above_edit = edit_start
 9812                .row()
 9813                .0
 9814                .checked_sub(line_count as u32)
 9815                .map(DisplayRow);
 9816            let below_edit = Some(edit_end.row() + 1);
 9817            let above_cursor =
 9818                cursor_row.and_then(|row| row.0.checked_sub(line_count as u32).map(DisplayRow));
 9819            let below_cursor = cursor_row.map(|cursor_row| cursor_row + 1);
 9820
 9821            // Place the edit popover adjacent to the edit if there is a location
 9822            // available that is onscreen and does not obscure the cursor. Otherwise,
 9823            // place it adjacent to the cursor.
 9824            let row_target = [above_edit, below_edit, above_cursor, below_cursor]
 9825                .into_iter()
 9826                .flatten()
 9827                .find(|&start_row| {
 9828                    let end_row = start_row + line_count as u32;
 9829                    visible_row_range.contains(&start_row)
 9830                        && visible_row_range.contains(&end_row)
 9831                        && cursor_row
 9832                            .is_none_or(|cursor_row| !((start_row..end_row).contains(&cursor_row)))
 9833                })?;
 9834
 9835            content_origin
 9836                + point(
 9837                    Pixels::from(-scroll_pixel_position.x),
 9838                    Pixels::from(
 9839                        (row_target.as_f64() - scroll_position.y) * f64::from(line_height),
 9840                    ),
 9841                )
 9842        };
 9843
 9844        origin.x -= BORDER_WIDTH;
 9845
 9846        window.defer_draw(element, origin, 1);
 9847
 9848        // Do not return an element, since it will already be drawn due to defer_draw.
 9849        None
 9850    }
 9851
 9852    fn edit_prediction_cursor_popover_height(&self) -> Pixels {
 9853        px(30.)
 9854    }
 9855
 9856    fn current_user_player_color(&self, cx: &mut App) -> PlayerColor {
 9857        if self.read_only(cx) {
 9858            cx.theme().players().read_only()
 9859        } else {
 9860            self.style.as_ref().unwrap().local_player
 9861        }
 9862    }
 9863
 9864    fn render_edit_prediction_accept_keybind(
 9865        &self,
 9866        window: &mut Window,
 9867        cx: &mut App,
 9868    ) -> Option<AnyElement> {
 9869        let accept_binding =
 9870            self.accept_edit_prediction_keybind(EditPredictionGranularity::Full, window, cx);
 9871        let accept_keystroke = accept_binding.keystroke()?;
 9872
 9873        let is_platform_style_mac = PlatformStyle::platform() == PlatformStyle::Mac;
 9874
 9875        let modifiers_color = if *accept_keystroke.modifiers() == window.modifiers() {
 9876            Color::Accent
 9877        } else {
 9878            Color::Muted
 9879        };
 9880
 9881        h_flex()
 9882            .px_0p5()
 9883            .when(is_platform_style_mac, |parent| parent.gap_0p5())
 9884            .font(theme::ThemeSettings::get_global(cx).buffer_font.clone())
 9885            .text_size(TextSize::XSmall.rems(cx))
 9886            .child(h_flex().children(ui::render_modifiers(
 9887                accept_keystroke.modifiers(),
 9888                PlatformStyle::platform(),
 9889                Some(modifiers_color),
 9890                Some(IconSize::XSmall.rems().into()),
 9891                true,
 9892            )))
 9893            .when(is_platform_style_mac, |parent| {
 9894                parent.child(accept_keystroke.key().to_string())
 9895            })
 9896            .when(!is_platform_style_mac, |parent| {
 9897                parent.child(
 9898                    Key::new(
 9899                        util::capitalize(accept_keystroke.key()),
 9900                        Some(Color::Default),
 9901                    )
 9902                    .size(Some(IconSize::XSmall.rems().into())),
 9903                )
 9904            })
 9905            .into_any()
 9906            .into()
 9907    }
 9908
 9909    fn render_edit_prediction_line_popover(
 9910        &self,
 9911        label: impl Into<SharedString>,
 9912        icon: Option<IconName>,
 9913        window: &mut Window,
 9914        cx: &mut App,
 9915    ) -> Stateful<Div> {
 9916        let padding_right = if icon.is_some() { px(4.) } else { px(8.) };
 9917
 9918        let keybind = self.render_edit_prediction_accept_keybind(window, cx);
 9919        let has_keybind = keybind.is_some();
 9920        let icons = Self::get_prediction_provider_icons(&self.edit_prediction_provider, cx);
 9921
 9922        h_flex()
 9923            .id("ep-line-popover")
 9924            .py_0p5()
 9925            .pl_1()
 9926            .pr(padding_right)
 9927            .gap_1()
 9928            .rounded_md()
 9929            .border_1()
 9930            .bg(Self::edit_prediction_line_popover_bg_color(cx))
 9931            .border_color(Self::edit_prediction_callout_popover_border_color(cx))
 9932            .shadow_xs()
 9933            .when(!has_keybind, |el| {
 9934                let status_colors = cx.theme().status();
 9935
 9936                el.bg(status_colors.error_background)
 9937                    .border_color(status_colors.error.opacity(0.6))
 9938                    .pl_2()
 9939                    .child(Icon::new(icons.error).color(Color::Error))
 9940                    .cursor_default()
 9941                    .hoverable_tooltip(move |_window, cx| {
 9942                        cx.new(|_| MissingEditPredictionKeybindingTooltip).into()
 9943                    })
 9944            })
 9945            .children(keybind)
 9946            .child(
 9947                Label::new(label)
 9948                    .size(LabelSize::Small)
 9949                    .when(!has_keybind, |el| {
 9950                        el.color(cx.theme().status().error.into()).strikethrough()
 9951                    }),
 9952            )
 9953            .when(!has_keybind, |el| {
 9954                el.child(
 9955                    h_flex().ml_1().child(
 9956                        Icon::new(IconName::Info)
 9957                            .size(IconSize::Small)
 9958                            .color(cx.theme().status().error.into()),
 9959                    ),
 9960                )
 9961            })
 9962            .when_some(icon, |element, icon| {
 9963                element.child(
 9964                    div()
 9965                        .mt(px(1.5))
 9966                        .child(Icon::new(icon).size(IconSize::Small)),
 9967                )
 9968            })
 9969    }
 9970
 9971    fn render_edit_prediction_jump_outside_popover(
 9972        &self,
 9973        snapshot: &BufferSnapshot,
 9974        window: &mut Window,
 9975        cx: &mut App,
 9976    ) -> Stateful<Div> {
 9977        let keybind = self.render_edit_prediction_accept_keybind(window, cx);
 9978        let has_keybind = keybind.is_some();
 9979        let icons = Self::get_prediction_provider_icons(&self.edit_prediction_provider, cx);
 9980
 9981        let file_name = snapshot
 9982            .file()
 9983            .map(|file| SharedString::new(file.file_name(cx)))
 9984            .unwrap_or(SharedString::new_static("untitled"));
 9985
 9986        h_flex()
 9987            .id("ep-jump-outside-popover")
 9988            .py_1()
 9989            .px_2()
 9990            .gap_1()
 9991            .rounded_md()
 9992            .border_1()
 9993            .bg(Self::edit_prediction_line_popover_bg_color(cx))
 9994            .border_color(Self::edit_prediction_callout_popover_border_color(cx))
 9995            .shadow_xs()
 9996            .when(!has_keybind, |el| {
 9997                let status_colors = cx.theme().status();
 9998
 9999                el.bg(status_colors.error_background)
10000                    .border_color(status_colors.error.opacity(0.6))
10001                    .pl_2()
10002                    .child(Icon::new(icons.error).color(Color::Error))
10003                    .cursor_default()
10004                    .hoverable_tooltip(move |_window, cx| {
10005                        cx.new(|_| MissingEditPredictionKeybindingTooltip).into()
10006                    })
10007            })
10008            .children(keybind)
10009            .child(
10010                Label::new(file_name)
10011                    .size(LabelSize::Small)
10012                    .buffer_font(cx)
10013                    .when(!has_keybind, |el| {
10014                        el.color(cx.theme().status().error.into()).strikethrough()
10015                    }),
10016            )
10017            .when(!has_keybind, |el| {
10018                el.child(
10019                    h_flex().ml_1().child(
10020                        Icon::new(IconName::Info)
10021                            .size(IconSize::Small)
10022                            .color(cx.theme().status().error.into()),
10023                    ),
10024                )
10025            })
10026            .child(
10027                div()
10028                    .mt(px(1.5))
10029                    .child(Icon::new(IconName::ArrowUpRight).size(IconSize::Small)),
10030            )
10031    }
10032
10033    fn edit_prediction_line_popover_bg_color(cx: &App) -> Hsla {
10034        let accent_color = cx.theme().colors().text_accent;
10035        let editor_bg_color = cx.theme().colors().editor_background;
10036        editor_bg_color.blend(accent_color.opacity(0.1))
10037    }
10038
10039    fn edit_prediction_callout_popover_border_color(cx: &App) -> Hsla {
10040        let accent_color = cx.theme().colors().text_accent;
10041        let editor_bg_color = cx.theme().colors().editor_background;
10042        editor_bg_color.blend(accent_color.opacity(0.6))
10043    }
10044    fn get_prediction_provider_icons(
10045        provider: &Option<RegisteredEditPredictionDelegate>,
10046        cx: &App,
10047    ) -> edit_prediction_types::EditPredictionIconSet {
10048        match provider {
10049            Some(provider) => provider.provider.icons(cx),
10050            None => edit_prediction_types::EditPredictionIconSet::new(IconName::ZedPredict),
10051        }
10052    }
10053
10054    fn render_edit_prediction_cursor_popover(
10055        &self,
10056        min_width: Pixels,
10057        max_width: Pixels,
10058        cursor_point: Point,
10059        style: &EditorStyle,
10060        accept_keystroke: Option<&gpui::KeybindingKeystroke>,
10061        _window: &Window,
10062        cx: &mut Context<Editor>,
10063    ) -> Option<AnyElement> {
10064        let provider = self.edit_prediction_provider.as_ref()?;
10065        let icons = Self::get_prediction_provider_icons(&self.edit_prediction_provider, cx);
10066
10067        let is_refreshing = provider.provider.is_refreshing(cx);
10068
10069        fn pending_completion_container(icon: IconName) -> Div {
10070            h_flex().h_full().flex_1().gap_2().child(Icon::new(icon))
10071        }
10072
10073        let completion = match &self.active_edit_prediction {
10074            Some(prediction) => {
10075                if !self.has_visible_completions_menu() {
10076                    const RADIUS: Pixels = px(6.);
10077                    const BORDER_WIDTH: Pixels = px(1.);
10078
10079                    return Some(
10080                        h_flex()
10081                            .elevation_2(cx)
10082                            .border(BORDER_WIDTH)
10083                            .border_color(cx.theme().colors().border)
10084                            .when(accept_keystroke.is_none(), |el| {
10085                                el.border_color(cx.theme().status().error)
10086                            })
10087                            .rounded(RADIUS)
10088                            .rounded_tl(px(0.))
10089                            .overflow_hidden()
10090                            .child(div().px_1p5().child(match &prediction.completion {
10091                                EditPrediction::MoveWithin { target, snapshot } => {
10092                                    use text::ToPoint as _;
10093                                    if target.text_anchor.to_point(snapshot).row > cursor_point.row
10094                                    {
10095                                        Icon::new(icons.down)
10096                                    } else {
10097                                        Icon::new(icons.up)
10098                                    }
10099                                }
10100                                EditPrediction::MoveOutside { .. } => {
10101                                    // TODO [zeta2] custom icon for external jump?
10102                                    Icon::new(icons.base)
10103                                }
10104                                EditPrediction::Edit { .. } => Icon::new(icons.base),
10105                            }))
10106                            .child(
10107                                h_flex()
10108                                    .gap_1()
10109                                    .py_1()
10110                                    .px_2()
10111                                    .rounded_r(RADIUS - BORDER_WIDTH)
10112                                    .border_l_1()
10113                                    .border_color(cx.theme().colors().border)
10114                                    .bg(Self::edit_prediction_line_popover_bg_color(cx))
10115                                    .when(self.edit_prediction_preview.released_too_fast(), |el| {
10116                                        el.child(
10117                                            Label::new("Hold")
10118                                                .size(LabelSize::Small)
10119                                                .when(accept_keystroke.is_none(), |el| {
10120                                                    el.strikethrough()
10121                                                })
10122                                                .line_height_style(LineHeightStyle::UiLabel),
10123                                        )
10124                                    })
10125                                    .id("edit_prediction_cursor_popover_keybind")
10126                                    .when(accept_keystroke.is_none(), |el| {
10127                                        let status_colors = cx.theme().status();
10128
10129                                        el.bg(status_colors.error_background)
10130                                            .border_color(status_colors.error.opacity(0.6))
10131                                            .child(Icon::new(IconName::Info).color(Color::Error))
10132                                            .cursor_default()
10133                                            .hoverable_tooltip(move |_window, cx| {
10134                                                cx.new(|_| MissingEditPredictionKeybindingTooltip)
10135                                                    .into()
10136                                            })
10137                                    })
10138                                    .when_some(
10139                                        accept_keystroke.as_ref(),
10140                                        |el, accept_keystroke| {
10141                                            el.child(h_flex().children(ui::render_modifiers(
10142                                                accept_keystroke.modifiers(),
10143                                                PlatformStyle::platform(),
10144                                                Some(Color::Default),
10145                                                Some(IconSize::XSmall.rems().into()),
10146                                                false,
10147                                            )))
10148                                        },
10149                                    ),
10150                            )
10151                            .into_any(),
10152                    );
10153                }
10154
10155                self.render_edit_prediction_cursor_popover_preview(
10156                    prediction,
10157                    cursor_point,
10158                    style,
10159                    cx,
10160                )?
10161            }
10162
10163            None if is_refreshing => match &self.stale_edit_prediction_in_menu {
10164                Some(stale_completion) => self.render_edit_prediction_cursor_popover_preview(
10165                    stale_completion,
10166                    cursor_point,
10167                    style,
10168                    cx,
10169                )?,
10170
10171                None => pending_completion_container(icons.base)
10172                    .child(Label::new("...").size(LabelSize::Small)),
10173            },
10174
10175            None => pending_completion_container(icons.base)
10176                .child(Label::new("...").size(LabelSize::Small)),
10177        };
10178
10179        let completion = if is_refreshing || self.active_edit_prediction.is_none() {
10180            completion
10181                .with_animation(
10182                    "loading-completion",
10183                    Animation::new(Duration::from_secs(2))
10184                        .repeat()
10185                        .with_easing(pulsating_between(0.4, 0.8)),
10186                    |label, delta| label.opacity(delta),
10187                )
10188                .into_any_element()
10189        } else {
10190            completion.into_any_element()
10191        };
10192
10193        let has_completion = self.active_edit_prediction.is_some();
10194
10195        let is_platform_style_mac = PlatformStyle::platform() == PlatformStyle::Mac;
10196        Some(
10197            h_flex()
10198                .min_w(min_width)
10199                .max_w(max_width)
10200                .flex_1()
10201                .elevation_2(cx)
10202                .border_color(cx.theme().colors().border)
10203                .child(
10204                    div()
10205                        .flex_1()
10206                        .py_1()
10207                        .px_2()
10208                        .overflow_hidden()
10209                        .child(completion),
10210                )
10211                .when_some(accept_keystroke, |el, accept_keystroke| {
10212                    if !accept_keystroke.modifiers().modified() {
10213                        return el;
10214                    }
10215
10216                    el.child(
10217                        h_flex()
10218                            .h_full()
10219                            .border_l_1()
10220                            .rounded_r_lg()
10221                            .border_color(cx.theme().colors().border)
10222                            .bg(Self::edit_prediction_line_popover_bg_color(cx))
10223                            .gap_1()
10224                            .py_1()
10225                            .px_2()
10226                            .child(
10227                                h_flex()
10228                                    .font(theme::ThemeSettings::get_global(cx).buffer_font.clone())
10229                                    .when(is_platform_style_mac, |parent| parent.gap_1())
10230                                    .child(h_flex().children(ui::render_modifiers(
10231                                        accept_keystroke.modifiers(),
10232                                        PlatformStyle::platform(),
10233                                        Some(if !has_completion {
10234                                            Color::Muted
10235                                        } else {
10236                                            Color::Default
10237                                        }),
10238                                        None,
10239                                        false,
10240                                    ))),
10241                            )
10242                            .child(Label::new("Preview").into_any_element())
10243                            .opacity(if has_completion { 1.0 } else { 0.4 }),
10244                    )
10245                })
10246                .into_any(),
10247        )
10248    }
10249
10250    fn render_edit_prediction_cursor_popover_preview(
10251        &self,
10252        completion: &EditPredictionState,
10253        cursor_point: Point,
10254        style: &EditorStyle,
10255        cx: &mut Context<Editor>,
10256    ) -> Option<Div> {
10257        use text::ToPoint as _;
10258
10259        fn render_relative_row_jump(
10260            prefix: impl Into<String>,
10261            current_row: u32,
10262            target_row: u32,
10263        ) -> Div {
10264            let (row_diff, arrow) = if target_row < current_row {
10265                (current_row - target_row, IconName::ArrowUp)
10266            } else {
10267                (target_row - current_row, IconName::ArrowDown)
10268            };
10269
10270            h_flex()
10271                .child(
10272                    Label::new(format!("{}{}", prefix.into(), row_diff))
10273                        .color(Color::Muted)
10274                        .size(LabelSize::Small),
10275                )
10276                .child(Icon::new(arrow).color(Color::Muted).size(IconSize::Small))
10277        }
10278
10279        let supports_jump = self
10280            .edit_prediction_provider
10281            .as_ref()
10282            .map(|provider| provider.provider.supports_jump_to_edit())
10283            .unwrap_or(true);
10284
10285        let icons = Self::get_prediction_provider_icons(&self.edit_prediction_provider, cx);
10286
10287        match &completion.completion {
10288            EditPrediction::MoveWithin {
10289                target, snapshot, ..
10290            } => {
10291                if !supports_jump {
10292                    return None;
10293                }
10294
10295                Some(
10296                    h_flex()
10297                        .px_2()
10298                        .gap_2()
10299                        .flex_1()
10300                        .child(
10301                            if target.text_anchor.to_point(snapshot).row > cursor_point.row {
10302                                Icon::new(icons.down)
10303                            } else {
10304                                Icon::new(icons.up)
10305                            },
10306                        )
10307                        .child(Label::new("Jump to Edit")),
10308                )
10309            }
10310            EditPrediction::MoveOutside { snapshot, .. } => {
10311                let file_name = snapshot
10312                    .file()
10313                    .map(|file| file.file_name(cx))
10314                    .unwrap_or("untitled");
10315                Some(
10316                    h_flex()
10317                        .px_2()
10318                        .gap_2()
10319                        .flex_1()
10320                        .child(Icon::new(icons.base))
10321                        .child(Label::new(format!("Jump to {file_name}"))),
10322                )
10323            }
10324            EditPrediction::Edit {
10325                edits,
10326                edit_preview,
10327                snapshot,
10328                ..
10329            } => {
10330                let first_edit_row = edits.first()?.0.start.text_anchor.to_point(snapshot).row;
10331
10332                let (highlighted_edits, has_more_lines) =
10333                    if let Some(edit_preview) = edit_preview.as_ref() {
10334                        crate::edit_prediction_edit_text(snapshot, edits, edit_preview, true, cx)
10335                            .first_line_preview()
10336                    } else {
10337                        crate::edit_prediction_fallback_text(edits, cx).first_line_preview()
10338                    };
10339
10340                let styled_text = gpui::StyledText::new(highlighted_edits.text)
10341                    .with_default_highlights(&style.text, highlighted_edits.highlights);
10342
10343                let preview = h_flex()
10344                    .gap_1()
10345                    .min_w_16()
10346                    .child(styled_text)
10347                    .when(has_more_lines, |parent| parent.child(""));
10348
10349                let left = if supports_jump && first_edit_row != cursor_point.row {
10350                    render_relative_row_jump("", cursor_point.row, first_edit_row)
10351                        .into_any_element()
10352                } else {
10353                    Icon::new(icons.base).into_any_element()
10354                };
10355
10356                Some(
10357                    h_flex()
10358                        .h_full()
10359                        .flex_1()
10360                        .gap_2()
10361                        .pr_1()
10362                        .overflow_x_hidden()
10363                        .font(theme::ThemeSettings::get_global(cx).buffer_font.clone())
10364                        .child(left)
10365                        .child(preview),
10366                )
10367            }
10368        }
10369    }
10370
10371    pub fn render_context_menu(
10372        &mut self,
10373        max_height_in_lines: u32,
10374        window: &mut Window,
10375        cx: &mut Context<Editor>,
10376    ) -> Option<AnyElement> {
10377        let menu = self.context_menu.borrow();
10378        let menu = menu.as_ref()?;
10379        if !menu.visible() {
10380            return None;
10381        };
10382        self.style
10383            .as_ref()
10384            .map(|style| menu.render(style, max_height_in_lines, window, cx))
10385    }
10386
10387    fn render_context_menu_aside(
10388        &mut self,
10389        max_size: Size<Pixels>,
10390        window: &mut Window,
10391        cx: &mut Context<Editor>,
10392    ) -> Option<AnyElement> {
10393        self.context_menu.borrow_mut().as_mut().and_then(|menu| {
10394            if menu.visible() {
10395                menu.render_aside(max_size, window, cx)
10396            } else {
10397                None
10398            }
10399        })
10400    }
10401
10402    fn hide_context_menu(
10403        &mut self,
10404        window: &mut Window,
10405        cx: &mut Context<Self>,
10406    ) -> Option<CodeContextMenu> {
10407        cx.notify();
10408        self.completion_tasks.clear();
10409        let context_menu = self.context_menu.borrow_mut().take();
10410        self.stale_edit_prediction_in_menu.take();
10411        self.update_visible_edit_prediction(window, cx);
10412        if let Some(CodeContextMenu::Completions(_)) = &context_menu
10413            && let Some(completion_provider) = &self.completion_provider
10414        {
10415            completion_provider.selection_changed(None, window, cx);
10416        }
10417        context_menu
10418    }
10419
10420    fn show_snippet_choices(
10421        &mut self,
10422        choices: &Vec<String>,
10423        selection: Range<Anchor>,
10424        cx: &mut Context<Self>,
10425    ) {
10426        let Some((_, buffer, _)) = self
10427            .buffer()
10428            .read(cx)
10429            .excerpt_containing(selection.start, cx)
10430        else {
10431            return;
10432        };
10433        let Some((_, end_buffer, _)) = self.buffer().read(cx).excerpt_containing(selection.end, cx)
10434        else {
10435            return;
10436        };
10437        if buffer != end_buffer {
10438            log::error!("expected anchor range to have matching buffer IDs");
10439            return;
10440        }
10441
10442        let id = post_inc(&mut self.next_completion_id);
10443        let snippet_sort_order = EditorSettings::get_global(cx).snippet_sort_order;
10444        let mut context_menu = self.context_menu.borrow_mut();
10445        let old_menu = context_menu.take();
10446        *context_menu = Some(CodeContextMenu::Completions(
10447            CompletionsMenu::new_snippet_choices(
10448                id,
10449                true,
10450                choices,
10451                selection,
10452                buffer,
10453                old_menu.map(|menu| menu.primary_scroll_handle()),
10454                snippet_sort_order,
10455            ),
10456        ));
10457    }
10458
10459    pub fn insert_snippet(
10460        &mut self,
10461        insertion_ranges: &[Range<MultiBufferOffset>],
10462        snippet: Snippet,
10463        window: &mut Window,
10464        cx: &mut Context<Self>,
10465    ) -> Result<()> {
10466        struct Tabstop<T> {
10467            is_end_tabstop: bool,
10468            ranges: Vec<Range<T>>,
10469            choices: Option<Vec<String>>,
10470        }
10471
10472        let tabstops = self.buffer.update(cx, |buffer, cx| {
10473            let snippet_text: Arc<str> = snippet.text.clone().into();
10474            let edits = insertion_ranges
10475                .iter()
10476                .cloned()
10477                .map(|range| (range, snippet_text.clone()));
10478            let autoindent_mode = AutoindentMode::Block {
10479                original_indent_columns: Vec::new(),
10480            };
10481            buffer.edit(edits, Some(autoindent_mode), cx);
10482
10483            let snapshot = &*buffer.read(cx);
10484            let snippet = &snippet;
10485            snippet
10486                .tabstops
10487                .iter()
10488                .map(|tabstop| {
10489                    let is_end_tabstop = tabstop.ranges.first().is_some_and(|tabstop| {
10490                        tabstop.is_empty() && tabstop.start == snippet.text.len() as isize
10491                    });
10492                    let mut tabstop_ranges = tabstop
10493                        .ranges
10494                        .iter()
10495                        .flat_map(|tabstop_range| {
10496                            let mut delta = 0_isize;
10497                            insertion_ranges.iter().map(move |insertion_range| {
10498                                let insertion_start = insertion_range.start + delta;
10499                                delta += snippet.text.len() as isize
10500                                    - (insertion_range.end - insertion_range.start) as isize;
10501
10502                                let start =
10503                                    (insertion_start + tabstop_range.start).min(snapshot.len());
10504                                let end = (insertion_start + tabstop_range.end).min(snapshot.len());
10505                                snapshot.anchor_before(start)..snapshot.anchor_after(end)
10506                            })
10507                        })
10508                        .collect::<Vec<_>>();
10509                    tabstop_ranges.sort_unstable_by(|a, b| a.start.cmp(&b.start, snapshot));
10510
10511                    Tabstop {
10512                        is_end_tabstop,
10513                        ranges: tabstop_ranges,
10514                        choices: tabstop.choices.clone(),
10515                    }
10516                })
10517                .collect::<Vec<_>>()
10518        });
10519        if let Some(tabstop) = tabstops.first() {
10520            self.change_selections(Default::default(), window, cx, |s| {
10521                // Reverse order so that the first range is the newest created selection.
10522                // Completions will use it and autoscroll will prioritize it.
10523                s.select_ranges(tabstop.ranges.iter().rev().cloned());
10524            });
10525
10526            if let Some(choices) = &tabstop.choices
10527                && let Some(selection) = tabstop.ranges.first()
10528            {
10529                self.show_snippet_choices(choices, selection.clone(), cx)
10530            }
10531
10532            // If we're already at the last tabstop and it's at the end of the snippet,
10533            // we're done, we don't need to keep the state around.
10534            if !tabstop.is_end_tabstop {
10535                let choices = tabstops
10536                    .iter()
10537                    .map(|tabstop| tabstop.choices.clone())
10538                    .collect();
10539
10540                let ranges = tabstops
10541                    .into_iter()
10542                    .map(|tabstop| tabstop.ranges)
10543                    .collect::<Vec<_>>();
10544
10545                self.snippet_stack.push(SnippetState {
10546                    active_index: 0,
10547                    ranges,
10548                    choices,
10549                });
10550            }
10551
10552            // Check whether the just-entered snippet ends with an auto-closable bracket.
10553            if self.autoclose_regions.is_empty() {
10554                let snapshot = self.buffer.read(cx).snapshot(cx);
10555                for selection in &mut self.selections.all::<Point>(&self.display_snapshot(cx)) {
10556                    let selection_head = selection.head();
10557                    let Some(scope) = snapshot.language_scope_at(selection_head) else {
10558                        continue;
10559                    };
10560
10561                    let mut bracket_pair = None;
10562                    let max_lookup_length = scope
10563                        .brackets()
10564                        .map(|(pair, _)| {
10565                            pair.start
10566                                .as_str()
10567                                .chars()
10568                                .count()
10569                                .max(pair.end.as_str().chars().count())
10570                        })
10571                        .max();
10572                    if let Some(max_lookup_length) = max_lookup_length {
10573                        let next_text = snapshot
10574                            .chars_at(selection_head)
10575                            .take(max_lookup_length)
10576                            .collect::<String>();
10577                        let prev_text = snapshot
10578                            .reversed_chars_at(selection_head)
10579                            .take(max_lookup_length)
10580                            .collect::<String>();
10581
10582                        for (pair, enabled) in scope.brackets() {
10583                            if enabled
10584                                && pair.close
10585                                && prev_text.starts_with(pair.start.as_str())
10586                                && next_text.starts_with(pair.end.as_str())
10587                            {
10588                                bracket_pair = Some(pair.clone());
10589                                break;
10590                            }
10591                        }
10592                    }
10593
10594                    if let Some(pair) = bracket_pair {
10595                        let snapshot_settings = snapshot.language_settings_at(selection_head, cx);
10596                        let autoclose_enabled =
10597                            self.use_autoclose && snapshot_settings.use_autoclose;
10598                        if autoclose_enabled {
10599                            let start = snapshot.anchor_after(selection_head);
10600                            let end = snapshot.anchor_after(selection_head);
10601                            self.autoclose_regions.push(AutocloseRegion {
10602                                selection_id: selection.id,
10603                                range: start..end,
10604                                pair,
10605                            });
10606                        }
10607                    }
10608                }
10609            }
10610        }
10611        Ok(())
10612    }
10613
10614    pub fn move_to_next_snippet_tabstop(
10615        &mut self,
10616        window: &mut Window,
10617        cx: &mut Context<Self>,
10618    ) -> bool {
10619        self.move_to_snippet_tabstop(Bias::Right, window, cx)
10620    }
10621
10622    pub fn move_to_prev_snippet_tabstop(
10623        &mut self,
10624        window: &mut Window,
10625        cx: &mut Context<Self>,
10626    ) -> bool {
10627        self.move_to_snippet_tabstop(Bias::Left, window, cx)
10628    }
10629
10630    pub fn move_to_snippet_tabstop(
10631        &mut self,
10632        bias: Bias,
10633        window: &mut Window,
10634        cx: &mut Context<Self>,
10635    ) -> bool {
10636        if let Some(mut snippet) = self.snippet_stack.pop() {
10637            match bias {
10638                Bias::Left => {
10639                    if snippet.active_index > 0 {
10640                        snippet.active_index -= 1;
10641                    } else {
10642                        self.snippet_stack.push(snippet);
10643                        return false;
10644                    }
10645                }
10646                Bias::Right => {
10647                    if snippet.active_index + 1 < snippet.ranges.len() {
10648                        snippet.active_index += 1;
10649                    } else {
10650                        self.snippet_stack.push(snippet);
10651                        return false;
10652                    }
10653                }
10654            }
10655            if let Some(current_ranges) = snippet.ranges.get(snippet.active_index) {
10656                self.change_selections(Default::default(), window, cx, |s| {
10657                    // Reverse order so that the first range is the newest created selection.
10658                    // Completions will use it and autoscroll will prioritize it.
10659                    s.select_ranges(current_ranges.iter().rev().cloned())
10660                });
10661
10662                if let Some(choices) = &snippet.choices[snippet.active_index]
10663                    && let Some(selection) = current_ranges.first()
10664                {
10665                    self.show_snippet_choices(choices, selection.clone(), cx);
10666                }
10667
10668                // If snippet state is not at the last tabstop, push it back on the stack
10669                if snippet.active_index + 1 < snippet.ranges.len() {
10670                    self.snippet_stack.push(snippet);
10671                }
10672                return true;
10673            }
10674        }
10675
10676        false
10677    }
10678
10679    pub fn clear(&mut self, window: &mut Window, cx: &mut Context<Self>) {
10680        self.transact(window, cx, |this, window, cx| {
10681            this.select_all(&SelectAll, window, cx);
10682            this.insert("", window, cx);
10683        });
10684    }
10685
10686    pub fn backspace(&mut self, _: &Backspace, window: &mut Window, cx: &mut Context<Self>) {
10687        if self.read_only(cx) {
10688            return;
10689        }
10690        self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
10691        self.transact(window, cx, |this, window, cx| {
10692            this.select_autoclose_pair(window, cx);
10693
10694            let display_map = this.display_map.update(cx, |map, cx| map.snapshot(cx));
10695
10696            let mut linked_ranges = HashMap::<_, Vec<_>>::default();
10697            if !this.linked_edit_ranges.is_empty() {
10698                let selections = this.selections.all::<MultiBufferPoint>(&display_map);
10699                let snapshot = this.buffer.read(cx).snapshot(cx);
10700
10701                for selection in selections.iter() {
10702                    let selection_start = snapshot.anchor_before(selection.start).text_anchor;
10703                    let selection_end = snapshot.anchor_after(selection.end).text_anchor;
10704                    if selection_start.buffer_id != selection_end.buffer_id {
10705                        continue;
10706                    }
10707                    if let Some(ranges) =
10708                        this.linked_editing_ranges_for(selection_start..selection_end, cx)
10709                    {
10710                        for (buffer, entries) in ranges {
10711                            linked_ranges.entry(buffer).or_default().extend(entries);
10712                        }
10713                    }
10714                }
10715            }
10716
10717            let mut selections = this.selections.all::<MultiBufferPoint>(&display_map);
10718            for selection in &mut selections {
10719                if selection.is_empty() {
10720                    let old_head = selection.head();
10721                    let mut new_head =
10722                        movement::left(&display_map, old_head.to_display_point(&display_map))
10723                            .to_point(&display_map);
10724                    if let Some((buffer, line_buffer_range)) = display_map
10725                        .buffer_snapshot()
10726                        .buffer_line_for_row(MultiBufferRow(old_head.row))
10727                    {
10728                        let indent_size = buffer.indent_size_for_line(line_buffer_range.start.row);
10729                        let indent_len = match indent_size.kind {
10730                            IndentKind::Space => {
10731                                buffer.settings_at(line_buffer_range.start, cx).tab_size
10732                            }
10733                            IndentKind::Tab => NonZeroU32::new(1).unwrap(),
10734                        };
10735                        if old_head.column <= indent_size.len && old_head.column > 0 {
10736                            let indent_len = indent_len.get();
10737                            new_head = cmp::min(
10738                                new_head,
10739                                MultiBufferPoint::new(
10740                                    old_head.row,
10741                                    ((old_head.column - 1) / indent_len) * indent_len,
10742                                ),
10743                            );
10744                        }
10745                    }
10746
10747                    selection.set_head(new_head, SelectionGoal::None);
10748                }
10749            }
10750
10751            this.change_selections(Default::default(), window, cx, |s| s.select(selections));
10752            this.insert("", window, cx);
10753            let empty_str: Arc<str> = Arc::from("");
10754            for (buffer, edits) in linked_ranges {
10755                let snapshot = buffer.read(cx).snapshot();
10756                use text::ToPoint as TP;
10757
10758                let edits = edits
10759                    .into_iter()
10760                    .map(|range| {
10761                        let end_point = TP::to_point(&range.end, &snapshot);
10762                        let mut start_point = TP::to_point(&range.start, &snapshot);
10763
10764                        if end_point == start_point {
10765                            let offset = text::ToOffset::to_offset(&range.start, &snapshot)
10766                                .saturating_sub(1);
10767                            start_point =
10768                                snapshot.clip_point(TP::to_point(&offset, &snapshot), Bias::Left);
10769                        };
10770
10771                        (start_point..end_point, empty_str.clone())
10772                    })
10773                    .sorted_by_key(|(range, _)| range.start)
10774                    .collect::<Vec<_>>();
10775                buffer.update(cx, |this, cx| {
10776                    this.edit(edits, None, cx);
10777                })
10778            }
10779            this.refresh_edit_prediction(true, false, window, cx);
10780            refresh_linked_ranges(this, window, cx);
10781        });
10782    }
10783
10784    pub fn delete(&mut self, _: &Delete, window: &mut Window, cx: &mut Context<Self>) {
10785        if self.read_only(cx) {
10786            return;
10787        }
10788        self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
10789        self.transact(window, cx, |this, window, cx| {
10790            this.change_selections(Default::default(), window, cx, |s| {
10791                s.move_with(&mut |map, selection| {
10792                    if selection.is_empty() {
10793                        let cursor = movement::right(map, selection.head());
10794                        selection.end = cursor;
10795                        selection.reversed = true;
10796                        selection.goal = SelectionGoal::None;
10797                    }
10798                })
10799            });
10800            this.insert("", window, cx);
10801            this.refresh_edit_prediction(true, false, window, cx);
10802        });
10803    }
10804
10805    pub fn backtab(&mut self, _: &Backtab, window: &mut Window, cx: &mut Context<Self>) {
10806        if self.mode.is_single_line() {
10807            cx.propagate();
10808            return;
10809        }
10810
10811        self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
10812        if self.move_to_prev_snippet_tabstop(window, cx) {
10813            return;
10814        }
10815        self.outdent(&Outdent, window, cx);
10816    }
10817
10818    pub fn next_snippet_tabstop(
10819        &mut self,
10820        _: &NextSnippetTabstop,
10821        window: &mut Window,
10822        cx: &mut Context<Self>,
10823    ) {
10824        if self.mode.is_single_line() || self.snippet_stack.is_empty() {
10825            cx.propagate();
10826            return;
10827        }
10828
10829        if self.move_to_next_snippet_tabstop(window, cx) {
10830            self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
10831            return;
10832        }
10833        cx.propagate();
10834    }
10835
10836    pub fn previous_snippet_tabstop(
10837        &mut self,
10838        _: &PreviousSnippetTabstop,
10839        window: &mut Window,
10840        cx: &mut Context<Self>,
10841    ) {
10842        if self.mode.is_single_line() || self.snippet_stack.is_empty() {
10843            cx.propagate();
10844            return;
10845        }
10846
10847        if self.move_to_prev_snippet_tabstop(window, cx) {
10848            self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
10849            return;
10850        }
10851        cx.propagate();
10852    }
10853
10854    pub fn tab(&mut self, _: &Tab, window: &mut Window, cx: &mut Context<Self>) {
10855        if self.mode.is_single_line() {
10856            cx.propagate();
10857            return;
10858        }
10859
10860        if self.move_to_next_snippet_tabstop(window, cx) {
10861            self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
10862            return;
10863        }
10864        if self.read_only(cx) {
10865            return;
10866        }
10867        self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
10868        let mut selections = self.selections.all_adjusted(&self.display_snapshot(cx));
10869        let buffer = self.buffer.read(cx);
10870        let snapshot = buffer.snapshot(cx);
10871        let rows_iter = selections.iter().map(|s| s.head().row);
10872        let suggested_indents = snapshot.suggested_indents(rows_iter, cx);
10873
10874        let has_some_cursor_in_whitespace = selections
10875            .iter()
10876            .filter(|selection| selection.is_empty())
10877            .any(|selection| {
10878                let cursor = selection.head();
10879                let current_indent = snapshot.indent_size_for_line(MultiBufferRow(cursor.row));
10880                cursor.column < current_indent.len
10881            });
10882
10883        let mut edits = Vec::new();
10884        let mut prev_edited_row = 0;
10885        let mut row_delta = 0;
10886        for selection in &mut selections {
10887            if selection.start.row != prev_edited_row {
10888                row_delta = 0;
10889            }
10890            prev_edited_row = selection.end.row;
10891
10892            // If cursor is after a list prefix, make selection non-empty to trigger line indent
10893            if selection.is_empty() {
10894                let cursor = selection.head();
10895                let settings = buffer.language_settings_at(cursor, cx);
10896                if settings.indent_list_on_tab {
10897                    if let Some(language) = snapshot.language_scope_at(Point::new(cursor.row, 0)) {
10898                        if is_list_prefix_row(MultiBufferRow(cursor.row), &snapshot, &language) {
10899                            row_delta = Self::indent_selection(
10900                                buffer, &snapshot, selection, &mut edits, row_delta, cx,
10901                            );
10902                            continue;
10903                        }
10904                    }
10905                }
10906            }
10907
10908            // If the selection is non-empty, then increase the indentation of the selected lines.
10909            if !selection.is_empty() {
10910                row_delta =
10911                    Self::indent_selection(buffer, &snapshot, selection, &mut edits, row_delta, cx);
10912                continue;
10913            }
10914
10915            let cursor = selection.head();
10916            let current_indent = snapshot.indent_size_for_line(MultiBufferRow(cursor.row));
10917            if let Some(suggested_indent) =
10918                suggested_indents.get(&MultiBufferRow(cursor.row)).copied()
10919            {
10920                // Don't do anything if already at suggested indent
10921                // and there is any other cursor which is not
10922                if has_some_cursor_in_whitespace
10923                    && cursor.column == current_indent.len
10924                    && current_indent.len == suggested_indent.len
10925                {
10926                    continue;
10927                }
10928
10929                // Adjust line and move cursor to suggested indent
10930                // if cursor is not at suggested indent
10931                if cursor.column < suggested_indent.len
10932                    && cursor.column <= current_indent.len
10933                    && current_indent.len <= suggested_indent.len
10934                {
10935                    selection.start = Point::new(cursor.row, suggested_indent.len);
10936                    selection.end = selection.start;
10937                    if row_delta == 0 {
10938                        edits.extend(Buffer::edit_for_indent_size_adjustment(
10939                            cursor.row,
10940                            current_indent,
10941                            suggested_indent,
10942                        ));
10943                        row_delta = suggested_indent.len - current_indent.len;
10944                    }
10945                    continue;
10946                }
10947
10948                // If current indent is more than suggested indent
10949                // only move cursor to current indent and skip indent
10950                if cursor.column < current_indent.len && current_indent.len > suggested_indent.len {
10951                    selection.start = Point::new(cursor.row, current_indent.len);
10952                    selection.end = selection.start;
10953                    continue;
10954                }
10955            }
10956
10957            // Otherwise, insert a hard or soft tab.
10958            let settings = buffer.language_settings_at(cursor, cx);
10959            let tab_size = if settings.hard_tabs {
10960                IndentSize::tab()
10961            } else {
10962                let tab_size = settings.tab_size.get();
10963                let indent_remainder = snapshot
10964                    .text_for_range(Point::new(cursor.row, 0)..cursor)
10965                    .flat_map(str::chars)
10966                    .fold(row_delta % tab_size, |counter: u32, c| {
10967                        if c == '\t' {
10968                            0
10969                        } else {
10970                            (counter + 1) % tab_size
10971                        }
10972                    });
10973
10974                let chars_to_next_tab_stop = tab_size - indent_remainder;
10975                IndentSize::spaces(chars_to_next_tab_stop)
10976            };
10977            selection.start = Point::new(cursor.row, cursor.column + row_delta + tab_size.len);
10978            selection.end = selection.start;
10979            edits.push((cursor..cursor, tab_size.chars().collect::<String>()));
10980            row_delta += tab_size.len;
10981        }
10982
10983        self.transact(window, cx, |this, window, cx| {
10984            this.buffer.update(cx, |b, cx| b.edit(edits, None, cx));
10985            this.change_selections(Default::default(), window, cx, |s| s.select(selections));
10986            this.refresh_edit_prediction(true, false, window, cx);
10987        });
10988    }
10989
10990    pub fn indent(&mut self, _: &Indent, window: &mut Window, cx: &mut Context<Self>) {
10991        if self.read_only(cx) {
10992            return;
10993        }
10994        if self.mode.is_single_line() {
10995            cx.propagate();
10996            return;
10997        }
10998
10999        self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
11000        let mut selections = self.selections.all::<Point>(&self.display_snapshot(cx));
11001        let mut prev_edited_row = 0;
11002        let mut row_delta = 0;
11003        let mut edits = Vec::new();
11004        let buffer = self.buffer.read(cx);
11005        let snapshot = buffer.snapshot(cx);
11006        for selection in &mut selections {
11007            if selection.start.row != prev_edited_row {
11008                row_delta = 0;
11009            }
11010            prev_edited_row = selection.end.row;
11011
11012            row_delta =
11013                Self::indent_selection(buffer, &snapshot, selection, &mut edits, row_delta, cx);
11014        }
11015
11016        self.transact(window, cx, |this, window, cx| {
11017            this.buffer.update(cx, |b, cx| b.edit(edits, None, cx));
11018            this.change_selections(Default::default(), window, cx, |s| s.select(selections));
11019        });
11020    }
11021
11022    fn indent_selection(
11023        buffer: &MultiBuffer,
11024        snapshot: &MultiBufferSnapshot,
11025        selection: &mut Selection<Point>,
11026        edits: &mut Vec<(Range<Point>, String)>,
11027        delta_for_start_row: u32,
11028        cx: &App,
11029    ) -> u32 {
11030        let settings = buffer.language_settings_at(selection.start, cx);
11031        let tab_size = settings.tab_size.get();
11032        let indent_kind = if settings.hard_tabs {
11033            IndentKind::Tab
11034        } else {
11035            IndentKind::Space
11036        };
11037        let mut start_row = selection.start.row;
11038        let mut end_row = selection.end.row + 1;
11039
11040        // If a selection ends at the beginning of a line, don't indent
11041        // that last line.
11042        if selection.end.column == 0 && selection.end.row > selection.start.row {
11043            end_row -= 1;
11044        }
11045
11046        // Avoid re-indenting a row that has already been indented by a
11047        // previous selection, but still update this selection's column
11048        // to reflect that indentation.
11049        if delta_for_start_row > 0 {
11050            start_row += 1;
11051            selection.start.column += delta_for_start_row;
11052            if selection.end.row == selection.start.row {
11053                selection.end.column += delta_for_start_row;
11054            }
11055        }
11056
11057        let mut delta_for_end_row = 0;
11058        let has_multiple_rows = start_row + 1 != end_row;
11059        for row in start_row..end_row {
11060            let current_indent = snapshot.indent_size_for_line(MultiBufferRow(row));
11061            let indent_delta = match (current_indent.kind, indent_kind) {
11062                (IndentKind::Space, IndentKind::Space) => {
11063                    let columns_to_next_tab_stop = tab_size - (current_indent.len % tab_size);
11064                    IndentSize::spaces(columns_to_next_tab_stop)
11065                }
11066                (IndentKind::Tab, IndentKind::Space) => IndentSize::spaces(tab_size),
11067                (_, IndentKind::Tab) => IndentSize::tab(),
11068            };
11069
11070            let start = if has_multiple_rows || current_indent.len < selection.start.column {
11071                0
11072            } else {
11073                selection.start.column
11074            };
11075            let row_start = Point::new(row, start);
11076            edits.push((
11077                row_start..row_start,
11078                indent_delta.chars().collect::<String>(),
11079            ));
11080
11081            // Update this selection's endpoints to reflect the indentation.
11082            if row == selection.start.row {
11083                selection.start.column += indent_delta.len;
11084            }
11085            if row == selection.end.row {
11086                selection.end.column += indent_delta.len;
11087                delta_for_end_row = indent_delta.len;
11088            }
11089        }
11090
11091        if selection.start.row == selection.end.row {
11092            delta_for_start_row + delta_for_end_row
11093        } else {
11094            delta_for_end_row
11095        }
11096    }
11097
11098    pub fn outdent(&mut self, _: &Outdent, window: &mut Window, cx: &mut Context<Self>) {
11099        if self.read_only(cx) {
11100            return;
11101        }
11102        if self.mode.is_single_line() {
11103            cx.propagate();
11104            return;
11105        }
11106
11107        self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
11108        let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
11109        let selections = self.selections.all::<Point>(&display_map);
11110        let mut deletion_ranges = Vec::new();
11111        let mut last_outdent = None;
11112        {
11113            let buffer = self.buffer.read(cx);
11114            let snapshot = buffer.snapshot(cx);
11115            for selection in &selections {
11116                let settings = buffer.language_settings_at(selection.start, cx);
11117                let tab_size = settings.tab_size.get();
11118                let mut rows = selection.spanned_rows(false, &display_map);
11119
11120                // Avoid re-outdenting a row that has already been outdented by a
11121                // previous selection.
11122                if let Some(last_row) = last_outdent
11123                    && last_row == rows.start
11124                {
11125                    rows.start = rows.start.next_row();
11126                }
11127                let has_multiple_rows = rows.len() > 1;
11128                for row in rows.iter_rows() {
11129                    let indent_size = snapshot.indent_size_for_line(row);
11130                    if indent_size.len > 0 {
11131                        let deletion_len = match indent_size.kind {
11132                            IndentKind::Space => {
11133                                let columns_to_prev_tab_stop = indent_size.len % tab_size;
11134                                if columns_to_prev_tab_stop == 0 {
11135                                    tab_size
11136                                } else {
11137                                    columns_to_prev_tab_stop
11138                                }
11139                            }
11140                            IndentKind::Tab => 1,
11141                        };
11142                        let start = if has_multiple_rows
11143                            || deletion_len > selection.start.column
11144                            || indent_size.len < selection.start.column
11145                        {
11146                            0
11147                        } else {
11148                            selection.start.column - deletion_len
11149                        };
11150                        deletion_ranges.push(
11151                            Point::new(row.0, start)..Point::new(row.0, start + deletion_len),
11152                        );
11153                        last_outdent = Some(row);
11154                    }
11155                }
11156            }
11157        }
11158
11159        self.transact(window, cx, |this, window, cx| {
11160            this.buffer.update(cx, |buffer, cx| {
11161                let empty_str: Arc<str> = Arc::default();
11162                buffer.edit(
11163                    deletion_ranges
11164                        .into_iter()
11165                        .map(|range| (range, empty_str.clone())),
11166                    None,
11167                    cx,
11168                );
11169            });
11170            let selections = this
11171                .selections
11172                .all::<MultiBufferOffset>(&this.display_snapshot(cx));
11173            this.change_selections(Default::default(), window, cx, |s| s.select(selections));
11174        });
11175    }
11176
11177    pub fn autoindent(&mut self, _: &AutoIndent, window: &mut Window, cx: &mut Context<Self>) {
11178        if self.read_only(cx) {
11179            return;
11180        }
11181        if self.mode.is_single_line() {
11182            cx.propagate();
11183            return;
11184        }
11185
11186        self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
11187        let selections = self
11188            .selections
11189            .all::<MultiBufferOffset>(&self.display_snapshot(cx))
11190            .into_iter()
11191            .map(|s| s.range());
11192
11193        self.transact(window, cx, |this, window, cx| {
11194            this.buffer.update(cx, |buffer, cx| {
11195                buffer.autoindent_ranges(selections, cx);
11196            });
11197            let selections = this
11198                .selections
11199                .all::<MultiBufferOffset>(&this.display_snapshot(cx));
11200            this.change_selections(Default::default(), window, cx, |s| s.select(selections));
11201        });
11202    }
11203
11204    pub fn delete_line(&mut self, _: &DeleteLine, window: &mut Window, cx: &mut Context<Self>) {
11205        self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
11206        let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
11207        let selections = self.selections.all::<Point>(&display_map);
11208
11209        let mut new_cursors = Vec::new();
11210        let mut edit_ranges = Vec::new();
11211        let mut selections = selections.iter().peekable();
11212        while let Some(selection) = selections.next() {
11213            let mut rows = selection.spanned_rows(false, &display_map);
11214
11215            // Accumulate contiguous regions of rows that we want to delete.
11216            while let Some(next_selection) = selections.peek() {
11217                let next_rows = next_selection.spanned_rows(false, &display_map);
11218                if next_rows.start <= rows.end {
11219                    rows.end = next_rows.end;
11220                    selections.next().unwrap();
11221                } else {
11222                    break;
11223                }
11224            }
11225
11226            let buffer = display_map.buffer_snapshot();
11227            let mut edit_start = ToOffset::to_offset(&Point::new(rows.start.0, 0), buffer);
11228            let (edit_end, target_row) = if buffer.max_point().row >= rows.end.0 {
11229                // If there's a line after the range, delete the \n from the end of the row range
11230                (
11231                    ToOffset::to_offset(&Point::new(rows.end.0, 0), buffer),
11232                    rows.end,
11233                )
11234            } else {
11235                // If there isn't a line after the range, delete the \n from the line before the
11236                // start of the row range
11237                edit_start = edit_start.saturating_sub_usize(1);
11238                (buffer.len(), rows.start.previous_row())
11239            };
11240
11241            let text_layout_details = self.text_layout_details(window, cx);
11242            let x = display_map.x_for_display_point(
11243                selection.head().to_display_point(&display_map),
11244                &text_layout_details,
11245            );
11246            let row = Point::new(target_row.0, 0)
11247                .to_display_point(&display_map)
11248                .row();
11249            let column = display_map.display_column_for_x(row, x, &text_layout_details);
11250
11251            new_cursors.push((
11252                selection.id,
11253                buffer.anchor_after(DisplayPoint::new(row, column).to_point(&display_map)),
11254                SelectionGoal::None,
11255            ));
11256            edit_ranges.push(edit_start..edit_end);
11257        }
11258
11259        self.transact(window, cx, |this, window, cx| {
11260            let buffer = this.buffer.update(cx, |buffer, cx| {
11261                let empty_str: Arc<str> = Arc::default();
11262                buffer.edit(
11263                    edit_ranges
11264                        .into_iter()
11265                        .map(|range| (range, empty_str.clone())),
11266                    None,
11267                    cx,
11268                );
11269                buffer.snapshot(cx)
11270            });
11271            let new_selections = new_cursors
11272                .into_iter()
11273                .map(|(id, cursor, goal)| {
11274                    let cursor = cursor.to_point(&buffer);
11275                    Selection {
11276                        id,
11277                        start: cursor,
11278                        end: cursor,
11279                        reversed: false,
11280                        goal,
11281                    }
11282                })
11283                .collect();
11284
11285            this.change_selections(Default::default(), window, cx, |s| {
11286                s.select(new_selections);
11287            });
11288        });
11289    }
11290
11291    pub fn join_lines_impl(
11292        &mut self,
11293        insert_whitespace: bool,
11294        window: &mut Window,
11295        cx: &mut Context<Self>,
11296    ) {
11297        if self.read_only(cx) {
11298            return;
11299        }
11300        let mut row_ranges = Vec::<Range<MultiBufferRow>>::new();
11301        for selection in self.selections.all::<Point>(&self.display_snapshot(cx)) {
11302            let start = MultiBufferRow(selection.start.row);
11303            // Treat single line selections as if they include the next line. Otherwise this action
11304            // would do nothing for single line selections individual cursors.
11305            let end = if selection.start.row == selection.end.row {
11306                MultiBufferRow(selection.start.row + 1)
11307            } else {
11308                MultiBufferRow(selection.end.row)
11309            };
11310
11311            if let Some(last_row_range) = row_ranges.last_mut()
11312                && start <= last_row_range.end
11313            {
11314                last_row_range.end = end;
11315                continue;
11316            }
11317            row_ranges.push(start..end);
11318        }
11319
11320        let snapshot = self.buffer.read(cx).snapshot(cx);
11321        let mut cursor_positions = Vec::new();
11322        for row_range in &row_ranges {
11323            let anchor = snapshot.anchor_before(Point::new(
11324                row_range.end.previous_row().0,
11325                snapshot.line_len(row_range.end.previous_row()),
11326            ));
11327            cursor_positions.push(anchor..anchor);
11328        }
11329
11330        self.transact(window, cx, |this, window, cx| {
11331            for row_range in row_ranges.into_iter().rev() {
11332                for row in row_range.iter_rows().rev() {
11333                    let end_of_line = Point::new(row.0, snapshot.line_len(row));
11334                    let next_line_row = row.next_row();
11335                    let indent = snapshot.indent_size_for_line(next_line_row);
11336                    let mut join_start_column = indent.len;
11337
11338                    if let Some(language_scope) =
11339                        snapshot.language_scope_at(Point::new(next_line_row.0, indent.len))
11340                    {
11341                        let line_end =
11342                            Point::new(next_line_row.0, snapshot.line_len(next_line_row));
11343                        let line_text_after_indent = snapshot
11344                            .text_for_range(Point::new(next_line_row.0, indent.len)..line_end)
11345                            .collect::<String>();
11346
11347                        if !line_text_after_indent.is_empty() {
11348                            let block_prefix = language_scope
11349                                .block_comment()
11350                                .map(|c| c.prefix.as_ref())
11351                                .filter(|p| !p.is_empty());
11352                            let doc_prefix = language_scope
11353                                .documentation_comment()
11354                                .map(|c| c.prefix.as_ref())
11355                                .filter(|p| !p.is_empty());
11356                            let all_prefixes = language_scope
11357                                .line_comment_prefixes()
11358                                .iter()
11359                                .map(|p| p.as_ref())
11360                                .chain(block_prefix)
11361                                .chain(doc_prefix)
11362                                .chain(language_scope.unordered_list().iter().map(|p| p.as_ref()));
11363
11364                            let mut longest_prefix_len = None;
11365                            for prefix in all_prefixes {
11366                                let trimmed = prefix.trim_end();
11367                                if line_text_after_indent.starts_with(trimmed) {
11368                                    let candidate_len =
11369                                        if line_text_after_indent.starts_with(prefix) {
11370                                            prefix.len()
11371                                        } else {
11372                                            trimmed.len()
11373                                        };
11374                                    if longest_prefix_len.map_or(true, |len| candidate_len > len) {
11375                                        longest_prefix_len = Some(candidate_len);
11376                                    }
11377                                }
11378                            }
11379
11380                            if let Some(prefix_len) = longest_prefix_len {
11381                                join_start_column =
11382                                    join_start_column.saturating_add(prefix_len as u32);
11383                            }
11384                        }
11385                    }
11386
11387                    let start_of_next_line = Point::new(next_line_row.0, join_start_column);
11388
11389                    let replace = if snapshot.line_len(next_line_row) > join_start_column
11390                        && insert_whitespace
11391                    {
11392                        " "
11393                    } else {
11394                        ""
11395                    };
11396
11397                    this.buffer.update(cx, |buffer, cx| {
11398                        buffer.edit([(end_of_line..start_of_next_line, replace)], None, cx)
11399                    });
11400                }
11401            }
11402
11403            this.change_selections(Default::default(), window, cx, |s| {
11404                s.select_anchor_ranges(cursor_positions)
11405            });
11406        });
11407    }
11408
11409    pub fn join_lines(&mut self, _: &JoinLines, window: &mut Window, cx: &mut Context<Self>) {
11410        self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
11411        self.join_lines_impl(true, window, cx);
11412    }
11413
11414    pub fn sort_lines_case_sensitive(
11415        &mut self,
11416        _: &SortLinesCaseSensitive,
11417        window: &mut Window,
11418        cx: &mut Context<Self>,
11419    ) {
11420        self.manipulate_immutable_lines(window, cx, |lines| lines.sort())
11421    }
11422
11423    pub fn sort_lines_by_length(
11424        &mut self,
11425        _: &SortLinesByLength,
11426        window: &mut Window,
11427        cx: &mut Context<Self>,
11428    ) {
11429        self.manipulate_immutable_lines(window, cx, |lines| {
11430            lines.sort_by_key(|&line| line.chars().count())
11431        })
11432    }
11433
11434    pub fn sort_lines_case_insensitive(
11435        &mut self,
11436        _: &SortLinesCaseInsensitive,
11437        window: &mut Window,
11438        cx: &mut Context<Self>,
11439    ) {
11440        self.manipulate_immutable_lines(window, cx, |lines| {
11441            lines.sort_by_key(|line| line.to_lowercase())
11442        })
11443    }
11444
11445    pub fn unique_lines_case_insensitive(
11446        &mut self,
11447        _: &UniqueLinesCaseInsensitive,
11448        window: &mut Window,
11449        cx: &mut Context<Self>,
11450    ) {
11451        self.manipulate_immutable_lines(window, cx, |lines| {
11452            let mut seen = HashSet::default();
11453            lines.retain(|line| seen.insert(line.to_lowercase()));
11454        })
11455    }
11456
11457    pub fn unique_lines_case_sensitive(
11458        &mut self,
11459        _: &UniqueLinesCaseSensitive,
11460        window: &mut Window,
11461        cx: &mut Context<Self>,
11462    ) {
11463        self.manipulate_immutable_lines(window, cx, |lines| {
11464            let mut seen = HashSet::default();
11465            lines.retain(|line| seen.insert(*line));
11466        })
11467    }
11468
11469    fn enable_wrap_selections_in_tag(&self, cx: &App) -> bool {
11470        let snapshot = self.buffer.read(cx).snapshot(cx);
11471        for selection in self.selections.disjoint_anchors_arc().iter() {
11472            if snapshot
11473                .language_at(selection.start)
11474                .and_then(|lang| lang.config().wrap_characters.as_ref())
11475                .is_some()
11476            {
11477                return true;
11478            }
11479        }
11480        false
11481    }
11482
11483    fn wrap_selections_in_tag(
11484        &mut self,
11485        _: &WrapSelectionsInTag,
11486        window: &mut Window,
11487        cx: &mut Context<Self>,
11488    ) {
11489        self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
11490
11491        let snapshot = self.buffer.read(cx).snapshot(cx);
11492
11493        let mut edits = Vec::new();
11494        let mut boundaries = Vec::new();
11495
11496        for selection in self
11497            .selections
11498            .all_adjusted(&self.display_snapshot(cx))
11499            .iter()
11500        {
11501            let Some(wrap_config) = snapshot
11502                .language_at(selection.start)
11503                .and_then(|lang| lang.config().wrap_characters.clone())
11504            else {
11505                continue;
11506            };
11507
11508            let open_tag = format!("{}{}", wrap_config.start_prefix, wrap_config.start_suffix);
11509            let close_tag = format!("{}{}", wrap_config.end_prefix, wrap_config.end_suffix);
11510
11511            let start_before = snapshot.anchor_before(selection.start);
11512            let end_after = snapshot.anchor_after(selection.end);
11513
11514            edits.push((start_before..start_before, open_tag));
11515            edits.push((end_after..end_after, close_tag));
11516
11517            boundaries.push((
11518                start_before,
11519                end_after,
11520                wrap_config.start_prefix.len(),
11521                wrap_config.end_suffix.len(),
11522            ));
11523        }
11524
11525        if edits.is_empty() {
11526            return;
11527        }
11528
11529        self.transact(window, cx, |this, window, cx| {
11530            let buffer = this.buffer.update(cx, |buffer, cx| {
11531                buffer.edit(edits, None, cx);
11532                buffer.snapshot(cx)
11533            });
11534
11535            let mut new_selections = Vec::with_capacity(boundaries.len() * 2);
11536            for (start_before, end_after, start_prefix_len, end_suffix_len) in
11537                boundaries.into_iter()
11538            {
11539                let open_offset = start_before.to_offset(&buffer) + start_prefix_len;
11540                let close_offset = end_after
11541                    .to_offset(&buffer)
11542                    .saturating_sub_usize(end_suffix_len);
11543                new_selections.push(open_offset..open_offset);
11544                new_selections.push(close_offset..close_offset);
11545            }
11546
11547            this.change_selections(Default::default(), window, cx, |s| {
11548                s.select_ranges(new_selections);
11549            });
11550
11551            this.request_autoscroll(Autoscroll::fit(), cx);
11552        });
11553    }
11554
11555    pub fn toggle_read_only(
11556        &mut self,
11557        _: &workspace::ToggleReadOnlyFile,
11558        _: &mut Window,
11559        cx: &mut Context<Self>,
11560    ) {
11561        if let Some(buffer) = self.buffer.read(cx).as_singleton() {
11562            buffer.update(cx, |buffer, cx| {
11563                buffer.set_capability(
11564                    match buffer.capability() {
11565                        Capability::ReadWrite => Capability::Read,
11566                        Capability::Read => Capability::ReadWrite,
11567                        Capability::ReadOnly => Capability::ReadOnly,
11568                    },
11569                    cx,
11570                );
11571            })
11572        }
11573    }
11574
11575    pub fn reload_file(&mut self, _: &ReloadFile, window: &mut Window, cx: &mut Context<Self>) {
11576        let Some(project) = self.project.clone() else {
11577            return;
11578        };
11579        let task = self.reload(project, window, cx);
11580        self.detach_and_notify_err(task, window, cx);
11581    }
11582
11583    pub fn restore_file(
11584        &mut self,
11585        _: &::git::RestoreFile,
11586        window: &mut Window,
11587        cx: &mut Context<Self>,
11588    ) {
11589        self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
11590        let mut buffer_ids = HashSet::default();
11591        let snapshot = self.buffer().read(cx).snapshot(cx);
11592        for selection in self
11593            .selections
11594            .all::<MultiBufferOffset>(&self.display_snapshot(cx))
11595        {
11596            buffer_ids.extend(snapshot.buffer_ids_for_range(selection.range()))
11597        }
11598
11599        let buffer = self.buffer().read(cx);
11600        let ranges = buffer_ids
11601            .into_iter()
11602            .flat_map(|buffer_id| buffer.excerpt_ranges_for_buffer(buffer_id, cx))
11603            .collect::<Vec<_>>();
11604
11605        self.restore_hunks_in_ranges(ranges, window, cx);
11606    }
11607
11608    pub fn git_restore(&mut self, _: &Restore, window: &mut Window, cx: &mut Context<Self>) {
11609        self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
11610        let selections = self
11611            .selections
11612            .all(&self.display_snapshot(cx))
11613            .into_iter()
11614            .map(|s| s.range())
11615            .collect();
11616        self.restore_hunks_in_ranges(selections, window, cx);
11617    }
11618
11619    pub fn restore_hunks_in_ranges(
11620        &mut self,
11621        ranges: Vec<Range<Point>>,
11622        window: &mut Window,
11623        cx: &mut Context<Editor>,
11624    ) {
11625        if self.delegate_stage_and_restore {
11626            let hunks = self.snapshot(window, cx).hunks_for_ranges(ranges);
11627            if !hunks.is_empty() {
11628                cx.emit(EditorEvent::RestoreRequested { hunks });
11629            }
11630            return;
11631        }
11632        let hunks = self.snapshot(window, cx).hunks_for_ranges(ranges);
11633        self.transact(window, cx, |editor, window, cx| {
11634            editor.restore_diff_hunks(hunks, cx);
11635            editor.change_selections(SelectionEffects::no_scroll(), window, cx, |selections| {
11636                selections.refresh()
11637            });
11638        });
11639    }
11640
11641    pub(crate) fn restore_diff_hunks(&self, hunks: Vec<MultiBufferDiffHunk>, cx: &mut App) {
11642        let mut revert_changes = HashMap::default();
11643        let chunk_by = hunks.into_iter().chunk_by(|hunk| hunk.buffer_id);
11644        for (buffer_id, hunks) in &chunk_by {
11645            let hunks = hunks.collect::<Vec<_>>();
11646            for hunk in &hunks {
11647                self.prepare_restore_change(&mut revert_changes, hunk, cx);
11648            }
11649            self.do_stage_or_unstage(false, buffer_id, hunks.into_iter(), cx);
11650        }
11651        if !revert_changes.is_empty() {
11652            self.buffer().update(cx, |multi_buffer, cx| {
11653                for (buffer_id, changes) in revert_changes {
11654                    if let Some(buffer) = multi_buffer.buffer(buffer_id) {
11655                        buffer.update(cx, |buffer, cx| {
11656                            buffer.edit(
11657                                changes
11658                                    .into_iter()
11659                                    .map(|(range, text)| (range, text.to_string())),
11660                                None,
11661                                cx,
11662                            );
11663                        });
11664                    }
11665                }
11666            });
11667        }
11668    }
11669
11670    pub fn status_for_buffer_id(&self, buffer_id: BufferId, cx: &App) -> Option<FileStatus> {
11671        if let Some(status) = self
11672            .addons
11673            .iter()
11674            .find_map(|(_, addon)| addon.override_status_for_buffer_id(buffer_id, cx))
11675        {
11676            return Some(status);
11677        }
11678        self.project
11679            .as_ref()?
11680            .read(cx)
11681            .status_for_buffer_id(buffer_id, cx)
11682    }
11683
11684    pub fn open_active_item_in_terminal(
11685        &mut self,
11686        _: &OpenInTerminal,
11687        window: &mut Window,
11688        cx: &mut Context<Self>,
11689    ) {
11690        if let Some(working_directory) = self.active_excerpt(cx).and_then(|(_, buffer, _)| {
11691            let project_path = buffer.read(cx).project_path(cx)?;
11692            let project = self.project()?.read(cx);
11693            let entry = project.entry_for_path(&project_path, cx)?;
11694            let parent = match &entry.canonical_path {
11695                Some(canonical_path) => canonical_path.to_path_buf(),
11696                None => project.absolute_path(&project_path, cx)?,
11697            }
11698            .parent()?
11699            .to_path_buf();
11700            Some(parent)
11701        }) {
11702            window.dispatch_action(
11703                OpenTerminal {
11704                    working_directory,
11705                    local: false,
11706                }
11707                .boxed_clone(),
11708                cx,
11709            );
11710        }
11711    }
11712
11713    fn set_breakpoint_context_menu(
11714        &mut self,
11715        display_row: DisplayRow,
11716        position: Option<Anchor>,
11717        clicked_point: gpui::Point<Pixels>,
11718        window: &mut Window,
11719        cx: &mut Context<Self>,
11720    ) {
11721        let source = self
11722            .buffer
11723            .read(cx)
11724            .snapshot(cx)
11725            .anchor_before(Point::new(display_row.0, 0u32));
11726
11727        let context_menu = self.breakpoint_context_menu(position.unwrap_or(source), window, cx);
11728
11729        self.mouse_context_menu = MouseContextMenu::pinned_to_editor(
11730            self,
11731            source,
11732            clicked_point,
11733            context_menu,
11734            window,
11735            cx,
11736        );
11737    }
11738
11739    fn add_edit_breakpoint_block(
11740        &mut self,
11741        anchor: Anchor,
11742        breakpoint: &Breakpoint,
11743        edit_action: BreakpointPromptEditAction,
11744        window: &mut Window,
11745        cx: &mut Context<Self>,
11746    ) {
11747        let weak_editor = cx.weak_entity();
11748        let bp_prompt = cx.new(|cx| {
11749            BreakpointPromptEditor::new(
11750                weak_editor,
11751                anchor,
11752                breakpoint.clone(),
11753                edit_action,
11754                window,
11755                cx,
11756            )
11757        });
11758
11759        let height = bp_prompt.update(cx, |this, cx| {
11760            this.prompt
11761                .update(cx, |prompt, cx| prompt.max_point(cx).row().0 + 1 + 2)
11762        });
11763        let cloned_prompt = bp_prompt.clone();
11764        let blocks = vec![BlockProperties {
11765            style: BlockStyle::Sticky,
11766            placement: BlockPlacement::Above(anchor),
11767            height: Some(height),
11768            render: Arc::new(move |cx| {
11769                *cloned_prompt.read(cx).editor_margins.lock() = *cx.margins;
11770                cloned_prompt.clone().into_any_element()
11771            }),
11772            priority: 0,
11773        }];
11774
11775        let focus_handle = bp_prompt.focus_handle(cx);
11776        window.focus(&focus_handle, cx);
11777
11778        let block_ids = self.insert_blocks(blocks, None, cx);
11779        bp_prompt.update(cx, |prompt, _| {
11780            prompt.add_block_ids(block_ids);
11781        });
11782    }
11783
11784    pub(crate) fn breakpoint_at_row(
11785        &self,
11786        row: u32,
11787        window: &mut Window,
11788        cx: &mut Context<Self>,
11789    ) -> Option<(Anchor, Breakpoint)> {
11790        let snapshot = self.snapshot(window, cx);
11791        let breakpoint_position = snapshot.buffer_snapshot().anchor_before(Point::new(row, 0));
11792
11793        self.breakpoint_at_anchor(breakpoint_position, &snapshot, cx)
11794    }
11795
11796    pub(crate) fn breakpoint_at_anchor(
11797        &self,
11798        breakpoint_position: Anchor,
11799        snapshot: &EditorSnapshot,
11800        cx: &mut Context<Self>,
11801    ) -> Option<(Anchor, Breakpoint)> {
11802        let buffer = self
11803            .buffer
11804            .read(cx)
11805            .buffer_for_anchor(breakpoint_position, cx)?;
11806
11807        let enclosing_excerpt = breakpoint_position.excerpt_id;
11808        let buffer_snapshot = buffer.read(cx).snapshot();
11809
11810        let row = buffer_snapshot
11811            .summary_for_anchor::<text::PointUtf16>(&breakpoint_position.text_anchor)
11812            .row;
11813
11814        let line_len = snapshot.buffer_snapshot().line_len(MultiBufferRow(row));
11815        let anchor_end = snapshot
11816            .buffer_snapshot()
11817            .anchor_after(Point::new(row, line_len));
11818
11819        self.breakpoint_store
11820            .as_ref()?
11821            .read_with(cx, |breakpoint_store, cx| {
11822                breakpoint_store
11823                    .breakpoints(
11824                        &buffer,
11825                        Some(breakpoint_position.text_anchor..anchor_end.text_anchor),
11826                        &buffer_snapshot,
11827                        cx,
11828                    )
11829                    .next()
11830                    .and_then(|(bp, _)| {
11831                        let breakpoint_row = buffer_snapshot
11832                            .summary_for_anchor::<text::PointUtf16>(&bp.position)
11833                            .row;
11834
11835                        if breakpoint_row == row {
11836                            snapshot
11837                                .buffer_snapshot()
11838                                .anchor_in_excerpt(enclosing_excerpt, bp.position)
11839                                .map(|position| (position, bp.bp.clone()))
11840                        } else {
11841                            None
11842                        }
11843                    })
11844            })
11845    }
11846
11847    pub fn edit_log_breakpoint(
11848        &mut self,
11849        _: &EditLogBreakpoint,
11850        window: &mut Window,
11851        cx: &mut Context<Self>,
11852    ) {
11853        if self.breakpoint_store.is_none() {
11854            return;
11855        }
11856
11857        for (anchor, breakpoint) in self.breakpoints_at_cursors(window, cx) {
11858            let breakpoint = breakpoint.unwrap_or_else(|| Breakpoint {
11859                message: None,
11860                state: BreakpointState::Enabled,
11861                condition: None,
11862                hit_condition: None,
11863            });
11864
11865            self.add_edit_breakpoint_block(
11866                anchor,
11867                &breakpoint,
11868                BreakpointPromptEditAction::Log,
11869                window,
11870                cx,
11871            );
11872        }
11873    }
11874
11875    fn breakpoints_at_cursors(
11876        &self,
11877        window: &mut Window,
11878        cx: &mut Context<Self>,
11879    ) -> Vec<(Anchor, Option<Breakpoint>)> {
11880        let snapshot = self.snapshot(window, cx);
11881        let cursors = self
11882            .selections
11883            .disjoint_anchors_arc()
11884            .iter()
11885            .map(|selection| {
11886                let cursor_position: Point = selection.head().to_point(&snapshot.buffer_snapshot());
11887
11888                let breakpoint_position = self
11889                    .breakpoint_at_row(cursor_position.row, window, cx)
11890                    .map(|bp| bp.0)
11891                    .unwrap_or_else(|| {
11892                        snapshot
11893                            .display_snapshot
11894                            .buffer_snapshot()
11895                            .anchor_after(Point::new(cursor_position.row, 0))
11896                    });
11897
11898                let breakpoint = self
11899                    .breakpoint_at_anchor(breakpoint_position, &snapshot, cx)
11900                    .map(|(anchor, breakpoint)| (anchor, Some(breakpoint)));
11901
11902                breakpoint.unwrap_or_else(|| (breakpoint_position, None))
11903            })
11904            // 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.
11905            .collect::<HashMap<Anchor, _>>();
11906
11907        cursors.into_iter().collect()
11908    }
11909
11910    pub fn enable_breakpoint(
11911        &mut self,
11912        _: &crate::actions::EnableBreakpoint,
11913        window: &mut Window,
11914        cx: &mut Context<Self>,
11915    ) {
11916        if self.breakpoint_store.is_none() {
11917            return;
11918        }
11919
11920        for (anchor, breakpoint) in self.breakpoints_at_cursors(window, cx) {
11921            let Some(breakpoint) = breakpoint.filter(|breakpoint| breakpoint.is_disabled()) else {
11922                continue;
11923            };
11924            self.edit_breakpoint_at_anchor(
11925                anchor,
11926                breakpoint,
11927                BreakpointEditAction::InvertState,
11928                cx,
11929            );
11930        }
11931    }
11932
11933    pub fn disable_breakpoint(
11934        &mut self,
11935        _: &crate::actions::DisableBreakpoint,
11936        window: &mut Window,
11937        cx: &mut Context<Self>,
11938    ) {
11939        if self.breakpoint_store.is_none() {
11940            return;
11941        }
11942
11943        for (anchor, breakpoint) in self.breakpoints_at_cursors(window, cx) {
11944            let Some(breakpoint) = breakpoint.filter(|breakpoint| breakpoint.is_enabled()) else {
11945                continue;
11946            };
11947            self.edit_breakpoint_at_anchor(
11948                anchor,
11949                breakpoint,
11950                BreakpointEditAction::InvertState,
11951                cx,
11952            );
11953        }
11954    }
11955
11956    pub fn toggle_breakpoint(
11957        &mut self,
11958        _: &crate::actions::ToggleBreakpoint,
11959        window: &mut Window,
11960        cx: &mut Context<Self>,
11961    ) {
11962        if self.breakpoint_store.is_none() {
11963            return;
11964        }
11965
11966        let snapshot = self.snapshot(window, cx);
11967        for (anchor, breakpoint) in self.breakpoints_at_cursors(window, cx) {
11968            if self.gutter_breakpoint_indicator.0.is_some() {
11969                let display_row = anchor
11970                    .to_point(snapshot.buffer_snapshot())
11971                    .to_display_point(&snapshot.display_snapshot)
11972                    .row();
11973                self.update_breakpoint_collision_on_toggle(
11974                    display_row,
11975                    &BreakpointEditAction::Toggle,
11976                );
11977            }
11978
11979            if let Some(breakpoint) = breakpoint {
11980                self.edit_breakpoint_at_anchor(
11981                    anchor,
11982                    breakpoint,
11983                    BreakpointEditAction::Toggle,
11984                    cx,
11985                );
11986            } else {
11987                self.edit_breakpoint_at_anchor(
11988                    anchor,
11989                    Breakpoint::new_standard(),
11990                    BreakpointEditAction::Toggle,
11991                    cx,
11992                );
11993            }
11994        }
11995    }
11996
11997    fn update_breakpoint_collision_on_toggle(
11998        &mut self,
11999        display_row: DisplayRow,
12000        edit_action: &BreakpointEditAction,
12001    ) {
12002        if let Some(ref mut breakpoint_indicator) = self.gutter_breakpoint_indicator.0 {
12003            if breakpoint_indicator.display_row == display_row
12004                && matches!(edit_action, BreakpointEditAction::Toggle)
12005            {
12006                breakpoint_indicator.collides_with_existing_breakpoint =
12007                    !breakpoint_indicator.collides_with_existing_breakpoint;
12008            }
12009        }
12010    }
12011
12012    pub fn edit_breakpoint_at_anchor(
12013        &mut self,
12014        breakpoint_position: Anchor,
12015        breakpoint: Breakpoint,
12016        edit_action: BreakpointEditAction,
12017        cx: &mut Context<Self>,
12018    ) {
12019        let Some(breakpoint_store) = &self.breakpoint_store else {
12020            return;
12021        };
12022
12023        let Some(buffer) = self
12024            .buffer
12025            .read(cx)
12026            .buffer_for_anchor(breakpoint_position, cx)
12027        else {
12028            return;
12029        };
12030
12031        breakpoint_store.update(cx, |breakpoint_store, cx| {
12032            breakpoint_store.toggle_breakpoint(
12033                buffer,
12034                BreakpointWithPosition {
12035                    position: breakpoint_position.text_anchor,
12036                    bp: breakpoint,
12037                },
12038                edit_action,
12039                cx,
12040            );
12041        });
12042
12043        cx.notify();
12044    }
12045
12046    #[cfg(any(test, feature = "test-support"))]
12047    pub fn breakpoint_store(&self) -> Option<Entity<BreakpointStore>> {
12048        self.breakpoint_store.clone()
12049    }
12050
12051    pub fn prepare_restore_change(
12052        &self,
12053        revert_changes: &mut HashMap<BufferId, Vec<(Range<text::Anchor>, Rope)>>,
12054        hunk: &MultiBufferDiffHunk,
12055        cx: &mut App,
12056    ) -> Option<()> {
12057        if hunk.is_created_file() {
12058            return None;
12059        }
12060        let buffer = self.buffer.read(cx);
12061        let diff = buffer.diff_for(hunk.buffer_id)?;
12062        let buffer = buffer.buffer(hunk.buffer_id)?;
12063        let buffer = buffer.read(cx);
12064        let original_text = diff
12065            .read(cx)
12066            .base_text(cx)
12067            .as_rope()
12068            .slice(hunk.diff_base_byte_range.start.0..hunk.diff_base_byte_range.end.0);
12069        let buffer_snapshot = buffer.snapshot();
12070        let buffer_revert_changes = revert_changes.entry(buffer.remote_id()).or_default();
12071        if let Err(i) = buffer_revert_changes.binary_search_by(|probe| {
12072            probe
12073                .0
12074                .start
12075                .cmp(&hunk.buffer_range.start, &buffer_snapshot)
12076                .then(probe.0.end.cmp(&hunk.buffer_range.end, &buffer_snapshot))
12077        }) {
12078            buffer_revert_changes.insert(i, (hunk.buffer_range.clone(), original_text));
12079            Some(())
12080        } else {
12081            None
12082        }
12083    }
12084
12085    pub fn reverse_lines(&mut self, _: &ReverseLines, window: &mut Window, cx: &mut Context<Self>) {
12086        self.manipulate_immutable_lines(window, cx, |lines| lines.reverse())
12087    }
12088
12089    pub fn shuffle_lines(&mut self, _: &ShuffleLines, window: &mut Window, cx: &mut Context<Self>) {
12090        self.manipulate_immutable_lines(window, cx, |lines| lines.shuffle(&mut rand::rng()))
12091    }
12092
12093    pub fn rotate_selections_forward(
12094        &mut self,
12095        _: &RotateSelectionsForward,
12096        window: &mut Window,
12097        cx: &mut Context<Self>,
12098    ) {
12099        self.rotate_selections(window, cx, false)
12100    }
12101
12102    pub fn rotate_selections_backward(
12103        &mut self,
12104        _: &RotateSelectionsBackward,
12105        window: &mut Window,
12106        cx: &mut Context<Self>,
12107    ) {
12108        self.rotate_selections(window, cx, true)
12109    }
12110
12111    fn rotate_selections(&mut self, window: &mut Window, cx: &mut Context<Self>, reverse: bool) {
12112        self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
12113        let display_snapshot = self.display_snapshot(cx);
12114        let selections = self.selections.all::<MultiBufferOffset>(&display_snapshot);
12115
12116        if selections.len() < 2 {
12117            return;
12118        }
12119
12120        let (edits, new_selections) = {
12121            let buffer = self.buffer.read(cx).read(cx);
12122            let has_selections = selections.iter().any(|s| !s.is_empty());
12123            if has_selections {
12124                let mut selected_texts: Vec<String> = selections
12125                    .iter()
12126                    .map(|selection| {
12127                        buffer
12128                            .text_for_range(selection.start..selection.end)
12129                            .collect()
12130                    })
12131                    .collect();
12132
12133                if reverse {
12134                    selected_texts.rotate_left(1);
12135                } else {
12136                    selected_texts.rotate_right(1);
12137                }
12138
12139                let mut offset_delta: i64 = 0;
12140                let mut new_selections = Vec::new();
12141                let edits: Vec<_> = selections
12142                    .iter()
12143                    .zip(selected_texts.iter())
12144                    .map(|(selection, new_text)| {
12145                        let old_len = (selection.end.0 - selection.start.0) as i64;
12146                        let new_len = new_text.len() as i64;
12147                        let adjusted_start =
12148                            MultiBufferOffset((selection.start.0 as i64 + offset_delta) as usize);
12149                        let adjusted_end =
12150                            MultiBufferOffset((adjusted_start.0 as i64 + new_len) as usize);
12151
12152                        new_selections.push(Selection {
12153                            id: selection.id,
12154                            start: adjusted_start,
12155                            end: adjusted_end,
12156                            reversed: selection.reversed,
12157                            goal: selection.goal,
12158                        });
12159
12160                        offset_delta += new_len - old_len;
12161                        (selection.start..selection.end, new_text.clone())
12162                    })
12163                    .collect();
12164                (edits, new_selections)
12165            } else {
12166                let mut all_rows: Vec<u32> = selections
12167                    .iter()
12168                    .map(|selection| buffer.offset_to_point(selection.start).row)
12169                    .collect();
12170                all_rows.sort_unstable();
12171                all_rows.dedup();
12172
12173                if all_rows.len() < 2 {
12174                    return;
12175                }
12176
12177                let line_ranges: Vec<Range<MultiBufferOffset>> = all_rows
12178                    .iter()
12179                    .map(|&row| {
12180                        let start = Point::new(row, 0);
12181                        let end = Point::new(row, buffer.line_len(MultiBufferRow(row)));
12182                        buffer.point_to_offset(start)..buffer.point_to_offset(end)
12183                    })
12184                    .collect();
12185
12186                let mut line_texts: Vec<String> = line_ranges
12187                    .iter()
12188                    .map(|range| buffer.text_for_range(range.clone()).collect())
12189                    .collect();
12190
12191                if reverse {
12192                    line_texts.rotate_left(1);
12193                } else {
12194                    line_texts.rotate_right(1);
12195                }
12196
12197                let edits = line_ranges
12198                    .iter()
12199                    .zip(line_texts.iter())
12200                    .map(|(range, new_text)| (range.clone(), new_text.clone()))
12201                    .collect();
12202
12203                let num_rows = all_rows.len();
12204                let row_to_index: std::collections::HashMap<u32, usize> = all_rows
12205                    .iter()
12206                    .enumerate()
12207                    .map(|(i, &row)| (row, i))
12208                    .collect();
12209
12210                // Compute new line start offsets after rotation (handles CRLF)
12211                let newline_len = line_ranges[1].start.0 - line_ranges[0].end.0;
12212                let first_line_start = line_ranges[0].start.0;
12213                let mut new_line_starts: Vec<usize> = vec![first_line_start];
12214                for text in line_texts.iter().take(num_rows - 1) {
12215                    let prev_start = *new_line_starts.last().unwrap();
12216                    new_line_starts.push(prev_start + text.len() + newline_len);
12217                }
12218
12219                let new_selections = selections
12220                    .iter()
12221                    .map(|selection| {
12222                        let point = buffer.offset_to_point(selection.start);
12223                        let old_index = row_to_index[&point.row];
12224                        let new_index = if reverse {
12225                            (old_index + num_rows - 1) % num_rows
12226                        } else {
12227                            (old_index + 1) % num_rows
12228                        };
12229                        let new_offset =
12230                            MultiBufferOffset(new_line_starts[new_index] + point.column as usize);
12231                        Selection {
12232                            id: selection.id,
12233                            start: new_offset,
12234                            end: new_offset,
12235                            reversed: selection.reversed,
12236                            goal: selection.goal,
12237                        }
12238                    })
12239                    .collect();
12240
12241                (edits, new_selections)
12242            }
12243        };
12244
12245        self.transact(window, cx, |this, window, cx| {
12246            this.buffer.update(cx, |buffer, cx| {
12247                buffer.edit(edits, None, cx);
12248            });
12249            this.change_selections(Default::default(), window, cx, |s| {
12250                s.select(new_selections);
12251            });
12252        });
12253    }
12254
12255    fn manipulate_lines<M>(
12256        &mut self,
12257        window: &mut Window,
12258        cx: &mut Context<Self>,
12259        mut manipulate: M,
12260    ) where
12261        M: FnMut(&str) -> LineManipulationResult,
12262    {
12263        self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
12264
12265        let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
12266        let buffer = self.buffer.read(cx).snapshot(cx);
12267
12268        let mut edits = Vec::new();
12269
12270        let selections = self.selections.all::<Point>(&display_map);
12271        let mut selections = selections.iter().peekable();
12272        let mut contiguous_row_selections = Vec::new();
12273        let mut new_selections = Vec::new();
12274        let mut added_lines = 0;
12275        let mut removed_lines = 0;
12276
12277        while let Some(selection) = selections.next() {
12278            let (start_row, end_row) = consume_contiguous_rows(
12279                &mut contiguous_row_selections,
12280                selection,
12281                &display_map,
12282                &mut selections,
12283            );
12284
12285            let start_point = Point::new(start_row.0, 0);
12286            let end_point = Point::new(
12287                end_row.previous_row().0,
12288                buffer.line_len(end_row.previous_row()),
12289            );
12290            let text = buffer
12291                .text_for_range(start_point..end_point)
12292                .collect::<String>();
12293
12294            let LineManipulationResult {
12295                new_text,
12296                line_count_before,
12297                line_count_after,
12298            } = manipulate(&text);
12299
12300            edits.push((start_point..end_point, new_text));
12301
12302            // Selections must change based on added and removed line count
12303            let start_row =
12304                MultiBufferRow(start_point.row + added_lines as u32 - removed_lines as u32);
12305            let end_row = MultiBufferRow(start_row.0 + line_count_after.saturating_sub(1) as u32);
12306            new_selections.push(Selection {
12307                id: selection.id,
12308                start: start_row,
12309                end: end_row,
12310                goal: SelectionGoal::None,
12311                reversed: selection.reversed,
12312            });
12313
12314            if line_count_after > line_count_before {
12315                added_lines += line_count_after - line_count_before;
12316            } else if line_count_before > line_count_after {
12317                removed_lines += line_count_before - line_count_after;
12318            }
12319        }
12320
12321        self.transact(window, cx, |this, window, cx| {
12322            let buffer = this.buffer.update(cx, |buffer, cx| {
12323                buffer.edit(edits, None, cx);
12324                buffer.snapshot(cx)
12325            });
12326
12327            // Recalculate offsets on newly edited buffer
12328            let new_selections = new_selections
12329                .iter()
12330                .map(|s| {
12331                    let start_point = Point::new(s.start.0, 0);
12332                    let end_point = Point::new(s.end.0, buffer.line_len(s.end));
12333                    Selection {
12334                        id: s.id,
12335                        start: buffer.point_to_offset(start_point),
12336                        end: buffer.point_to_offset(end_point),
12337                        goal: s.goal,
12338                        reversed: s.reversed,
12339                    }
12340                })
12341                .collect();
12342
12343            this.change_selections(Default::default(), window, cx, |s| {
12344                s.select(new_selections);
12345            });
12346
12347            this.request_autoscroll(Autoscroll::fit(), cx);
12348        });
12349    }
12350
12351    fn manipulate_immutable_lines<Fn>(
12352        &mut self,
12353        window: &mut Window,
12354        cx: &mut Context<Self>,
12355        mut callback: Fn,
12356    ) where
12357        Fn: FnMut(&mut Vec<&str>),
12358    {
12359        self.manipulate_lines(window, cx, |text| {
12360            let mut lines: Vec<&str> = text.split('\n').collect();
12361            let line_count_before = lines.len();
12362
12363            callback(&mut lines);
12364
12365            LineManipulationResult {
12366                new_text: lines.join("\n"),
12367                line_count_before,
12368                line_count_after: lines.len(),
12369            }
12370        });
12371    }
12372
12373    fn manipulate_mutable_lines<Fn>(
12374        &mut self,
12375        window: &mut Window,
12376        cx: &mut Context<Self>,
12377        mut callback: Fn,
12378    ) where
12379        Fn: FnMut(&mut Vec<Cow<'_, str>>),
12380    {
12381        self.manipulate_lines(window, cx, |text| {
12382            let mut lines: Vec<Cow<str>> = text.split('\n').map(Cow::from).collect();
12383            let line_count_before = lines.len();
12384
12385            callback(&mut lines);
12386
12387            LineManipulationResult {
12388                new_text: lines.join("\n"),
12389                line_count_before,
12390                line_count_after: lines.len(),
12391            }
12392        });
12393    }
12394
12395    pub fn convert_indentation_to_spaces(
12396        &mut self,
12397        _: &ConvertIndentationToSpaces,
12398        window: &mut Window,
12399        cx: &mut Context<Self>,
12400    ) {
12401        let settings = self.buffer.read(cx).language_settings(cx);
12402        let tab_size = settings.tab_size.get() as usize;
12403
12404        self.manipulate_mutable_lines(window, cx, |lines| {
12405            // Allocates a reasonably sized scratch buffer once for the whole loop
12406            let mut reindented_line = String::with_capacity(MAX_LINE_LEN);
12407            // Avoids recomputing spaces that could be inserted many times
12408            let space_cache: Vec<Vec<char>> = (1..=tab_size)
12409                .map(|n| IndentSize::spaces(n as u32).chars().collect())
12410                .collect();
12411
12412            for line in lines.iter_mut().filter(|line| !line.is_empty()) {
12413                let mut chars = line.as_ref().chars();
12414                let mut col = 0;
12415                let mut changed = false;
12416
12417                for ch in chars.by_ref() {
12418                    match ch {
12419                        ' ' => {
12420                            reindented_line.push(' ');
12421                            col += 1;
12422                        }
12423                        '\t' => {
12424                            // \t are converted to spaces depending on the current column
12425                            let spaces_len = tab_size - (col % tab_size);
12426                            reindented_line.extend(&space_cache[spaces_len - 1]);
12427                            col += spaces_len;
12428                            changed = true;
12429                        }
12430                        _ => {
12431                            // If we dont append before break, the character is consumed
12432                            reindented_line.push(ch);
12433                            break;
12434                        }
12435                    }
12436                }
12437
12438                if !changed {
12439                    reindented_line.clear();
12440                    continue;
12441                }
12442                // Append the rest of the line and replace old reference with new one
12443                reindented_line.extend(chars);
12444                *line = Cow::Owned(reindented_line.clone());
12445                reindented_line.clear();
12446            }
12447        });
12448    }
12449
12450    pub fn convert_indentation_to_tabs(
12451        &mut self,
12452        _: &ConvertIndentationToTabs,
12453        window: &mut Window,
12454        cx: &mut Context<Self>,
12455    ) {
12456        let settings = self.buffer.read(cx).language_settings(cx);
12457        let tab_size = settings.tab_size.get() as usize;
12458
12459        self.manipulate_mutable_lines(window, cx, |lines| {
12460            // Allocates a reasonably sized buffer once for the whole loop
12461            let mut reindented_line = String::with_capacity(MAX_LINE_LEN);
12462            // Avoids recomputing spaces that could be inserted many times
12463            let space_cache: Vec<Vec<char>> = (1..=tab_size)
12464                .map(|n| IndentSize::spaces(n as u32).chars().collect())
12465                .collect();
12466
12467            for line in lines.iter_mut().filter(|line| !line.is_empty()) {
12468                let mut chars = line.chars();
12469                let mut spaces_count = 0;
12470                let mut first_non_indent_char = None;
12471                let mut changed = false;
12472
12473                for ch in chars.by_ref() {
12474                    match ch {
12475                        ' ' => {
12476                            // Keep track of spaces. Append \t when we reach tab_size
12477                            spaces_count += 1;
12478                            changed = true;
12479                            if spaces_count == tab_size {
12480                                reindented_line.push('\t');
12481                                spaces_count = 0;
12482                            }
12483                        }
12484                        '\t' => {
12485                            reindented_line.push('\t');
12486                            spaces_count = 0;
12487                        }
12488                        _ => {
12489                            // Dont append it yet, we might have remaining spaces
12490                            first_non_indent_char = Some(ch);
12491                            break;
12492                        }
12493                    }
12494                }
12495
12496                if !changed {
12497                    reindented_line.clear();
12498                    continue;
12499                }
12500                // Remaining spaces that didn't make a full tab stop
12501                if spaces_count > 0 {
12502                    reindented_line.extend(&space_cache[spaces_count - 1]);
12503                }
12504                // If we consume an extra character that was not indentation, add it back
12505                if let Some(extra_char) = first_non_indent_char {
12506                    reindented_line.push(extra_char);
12507                }
12508                // Append the rest of the line and replace old reference with new one
12509                reindented_line.extend(chars);
12510                *line = Cow::Owned(reindented_line.clone());
12511                reindented_line.clear();
12512            }
12513        });
12514    }
12515
12516    pub fn convert_to_upper_case(
12517        &mut self,
12518        _: &ConvertToUpperCase,
12519        window: &mut Window,
12520        cx: &mut Context<Self>,
12521    ) {
12522        self.manipulate_text(window, cx, |text| text.to_uppercase())
12523    }
12524
12525    pub fn convert_to_lower_case(
12526        &mut self,
12527        _: &ConvertToLowerCase,
12528        window: &mut Window,
12529        cx: &mut Context<Self>,
12530    ) {
12531        self.manipulate_text(window, cx, |text| text.to_lowercase())
12532    }
12533
12534    pub fn convert_to_title_case(
12535        &mut self,
12536        _: &ConvertToTitleCase,
12537        window: &mut Window,
12538        cx: &mut Context<Self>,
12539    ) {
12540        self.manipulate_text(window, cx, |text| {
12541            text.split('\n')
12542                .map(|line| line.to_case(Case::Title))
12543                .join("\n")
12544        })
12545    }
12546
12547    pub fn convert_to_snake_case(
12548        &mut self,
12549        _: &ConvertToSnakeCase,
12550        window: &mut Window,
12551        cx: &mut Context<Self>,
12552    ) {
12553        self.manipulate_text(window, cx, |text| text.to_case(Case::Snake))
12554    }
12555
12556    pub fn convert_to_kebab_case(
12557        &mut self,
12558        _: &ConvertToKebabCase,
12559        window: &mut Window,
12560        cx: &mut Context<Self>,
12561    ) {
12562        self.manipulate_text(window, cx, |text| text.to_case(Case::Kebab))
12563    }
12564
12565    pub fn convert_to_upper_camel_case(
12566        &mut self,
12567        _: &ConvertToUpperCamelCase,
12568        window: &mut Window,
12569        cx: &mut Context<Self>,
12570    ) {
12571        self.manipulate_text(window, cx, |text| {
12572            text.split('\n')
12573                .map(|line| line.to_case(Case::UpperCamel))
12574                .join("\n")
12575        })
12576    }
12577
12578    pub fn convert_to_lower_camel_case(
12579        &mut self,
12580        _: &ConvertToLowerCamelCase,
12581        window: &mut Window,
12582        cx: &mut Context<Self>,
12583    ) {
12584        self.manipulate_text(window, cx, |text| text.to_case(Case::Camel))
12585    }
12586
12587    pub fn convert_to_opposite_case(
12588        &mut self,
12589        _: &ConvertToOppositeCase,
12590        window: &mut Window,
12591        cx: &mut Context<Self>,
12592    ) {
12593        self.manipulate_text(window, cx, |text| {
12594            text.chars()
12595                .fold(String::with_capacity(text.len()), |mut t, c| {
12596                    if c.is_uppercase() {
12597                        t.extend(c.to_lowercase());
12598                    } else {
12599                        t.extend(c.to_uppercase());
12600                    }
12601                    t
12602                })
12603        })
12604    }
12605
12606    pub fn convert_to_sentence_case(
12607        &mut self,
12608        _: &ConvertToSentenceCase,
12609        window: &mut Window,
12610        cx: &mut Context<Self>,
12611    ) {
12612        self.manipulate_text(window, cx, |text| text.to_case(Case::Sentence))
12613    }
12614
12615    pub fn toggle_case(&mut self, _: &ToggleCase, window: &mut Window, cx: &mut Context<Self>) {
12616        self.manipulate_text(window, cx, |text| {
12617            let has_upper_case_characters = text.chars().any(|c| c.is_uppercase());
12618            if has_upper_case_characters {
12619                text.to_lowercase()
12620            } else {
12621                text.to_uppercase()
12622            }
12623        })
12624    }
12625
12626    pub fn convert_to_rot13(
12627        &mut self,
12628        _: &ConvertToRot13,
12629        window: &mut Window,
12630        cx: &mut Context<Self>,
12631    ) {
12632        self.manipulate_text(window, cx, |text| {
12633            text.chars()
12634                .map(|c| match c {
12635                    'A'..='M' | 'a'..='m' => ((c as u8) + 13) as char,
12636                    'N'..='Z' | 'n'..='z' => ((c as u8) - 13) as char,
12637                    _ => c,
12638                })
12639                .collect()
12640        })
12641    }
12642
12643    pub fn convert_to_rot47(
12644        &mut self,
12645        _: &ConvertToRot47,
12646        window: &mut Window,
12647        cx: &mut Context<Self>,
12648    ) {
12649        self.manipulate_text(window, cx, |text| {
12650            text.chars()
12651                .map(|c| {
12652                    let code_point = c as u32;
12653                    if code_point >= 33 && code_point <= 126 {
12654                        return char::from_u32(33 + ((code_point + 14) % 94)).unwrap();
12655                    }
12656                    c
12657                })
12658                .collect()
12659        })
12660    }
12661
12662    fn manipulate_text<Fn>(&mut self, window: &mut Window, cx: &mut Context<Self>, mut callback: Fn)
12663    where
12664        Fn: FnMut(&str) -> String,
12665    {
12666        let buffer = self.buffer.read(cx).snapshot(cx);
12667
12668        let mut new_selections = Vec::new();
12669        let mut edits = Vec::new();
12670        let mut selection_adjustment = 0isize;
12671
12672        for selection in self.selections.all_adjusted(&self.display_snapshot(cx)) {
12673            let selection_is_empty = selection.is_empty();
12674
12675            let (start, end) = if selection_is_empty {
12676                let (word_range, _) = buffer.surrounding_word(selection.start, None);
12677                (word_range.start, word_range.end)
12678            } else {
12679                (
12680                    buffer.point_to_offset(selection.start),
12681                    buffer.point_to_offset(selection.end),
12682                )
12683            };
12684
12685            let text = buffer.text_for_range(start..end).collect::<String>();
12686            let old_length = text.len() as isize;
12687            let text = callback(&text);
12688
12689            new_selections.push(Selection {
12690                start: MultiBufferOffset((start.0 as isize - selection_adjustment) as usize),
12691                end: MultiBufferOffset(
12692                    ((start.0 + text.len()) as isize - selection_adjustment) as usize,
12693                ),
12694                goal: SelectionGoal::None,
12695                id: selection.id,
12696                reversed: selection.reversed,
12697            });
12698
12699            selection_adjustment += old_length - text.len() as isize;
12700
12701            edits.push((start..end, text));
12702        }
12703
12704        self.transact(window, cx, |this, window, cx| {
12705            this.buffer.update(cx, |buffer, cx| {
12706                buffer.edit(edits, None, cx);
12707            });
12708
12709            this.change_selections(Default::default(), window, cx, |s| {
12710                s.select(new_selections);
12711            });
12712
12713            this.request_autoscroll(Autoscroll::fit(), cx);
12714        });
12715    }
12716
12717    pub fn move_selection_on_drop(
12718        &mut self,
12719        selection: &Selection<Anchor>,
12720        target: DisplayPoint,
12721        is_cut: bool,
12722        window: &mut Window,
12723        cx: &mut Context<Self>,
12724    ) {
12725        let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
12726        let buffer = display_map.buffer_snapshot();
12727        let mut edits = Vec::new();
12728        let insert_point = display_map
12729            .clip_point(target, Bias::Left)
12730            .to_point(&display_map);
12731        let text = buffer
12732            .text_for_range(selection.start..selection.end)
12733            .collect::<String>();
12734        if is_cut {
12735            edits.push(((selection.start..selection.end), String::new()));
12736        }
12737        let insert_anchor = buffer.anchor_before(insert_point);
12738        edits.push(((insert_anchor..insert_anchor), text));
12739        let last_edit_start = insert_anchor.bias_left(buffer);
12740        let last_edit_end = insert_anchor.bias_right(buffer);
12741        self.transact(window, cx, |this, window, cx| {
12742            this.buffer.update(cx, |buffer, cx| {
12743                buffer.edit(edits, None, cx);
12744            });
12745            this.change_selections(Default::default(), window, cx, |s| {
12746                s.select_anchor_ranges([last_edit_start..last_edit_end]);
12747            });
12748        });
12749    }
12750
12751    pub fn clear_selection_drag_state(&mut self) {
12752        self.selection_drag_state = SelectionDragState::None;
12753    }
12754
12755    pub fn duplicate(
12756        &mut self,
12757        upwards: bool,
12758        whole_lines: bool,
12759        window: &mut Window,
12760        cx: &mut Context<Self>,
12761    ) {
12762        self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
12763
12764        let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
12765        let buffer = display_map.buffer_snapshot();
12766        let selections = self.selections.all::<Point>(&display_map);
12767
12768        let mut edits = Vec::new();
12769        let mut selections_iter = selections.iter().peekable();
12770        while let Some(selection) = selections_iter.next() {
12771            let mut rows = selection.spanned_rows(false, &display_map);
12772            // duplicate line-wise
12773            if whole_lines || selection.start == selection.end {
12774                // Avoid duplicating the same lines twice.
12775                while let Some(next_selection) = selections_iter.peek() {
12776                    let next_rows = next_selection.spanned_rows(false, &display_map);
12777                    if next_rows.start < rows.end {
12778                        rows.end = next_rows.end;
12779                        selections_iter.next().unwrap();
12780                    } else {
12781                        break;
12782                    }
12783                }
12784
12785                // Copy the text from the selected row region and splice it either at the start
12786                // or end of the region.
12787                let start = Point::new(rows.start.0, 0);
12788                let end = Point::new(
12789                    rows.end.previous_row().0,
12790                    buffer.line_len(rows.end.previous_row()),
12791                );
12792
12793                let mut text = buffer.text_for_range(start..end).collect::<String>();
12794
12795                let insert_location = if upwards {
12796                    // When duplicating upward, we need to insert before the current line.
12797                    // If we're on the last line and it doesn't end with a newline,
12798                    // we need to add a newline before the duplicated content.
12799                    let needs_leading_newline = rows.end.0 >= buffer.max_point().row
12800                        && buffer.max_point().column > 0
12801                        && !text.ends_with('\n');
12802
12803                    if needs_leading_newline {
12804                        text.insert(0, '\n');
12805                        end
12806                    } else {
12807                        text.push('\n');
12808                        Point::new(rows.start.0, 0)
12809                    }
12810                } else {
12811                    text.push('\n');
12812                    start
12813                };
12814                edits.push((insert_location..insert_location, text));
12815            } else {
12816                // duplicate character-wise
12817                let start = selection.start;
12818                let end = selection.end;
12819                let text = buffer.text_for_range(start..end).collect::<String>();
12820                edits.push((selection.end..selection.end, text));
12821            }
12822        }
12823
12824        self.transact(window, cx, |this, window, cx| {
12825            this.buffer.update(cx, |buffer, cx| {
12826                buffer.edit(edits, None, cx);
12827            });
12828
12829            // When duplicating upward with whole lines, move the cursor to the duplicated line
12830            if upwards && whole_lines {
12831                let display_map = this.display_map.update(cx, |map, cx| map.snapshot(cx));
12832
12833                this.change_selections(SelectionEffects::no_scroll(), window, cx, |s| {
12834                    let mut new_ranges = Vec::new();
12835                    let selections = s.all::<Point>(&display_map);
12836                    let mut selections_iter = selections.iter().peekable();
12837
12838                    while let Some(first_selection) = selections_iter.next() {
12839                        // Group contiguous selections together to find the total row span
12840                        let mut group_selections = vec![first_selection];
12841                        let mut rows = first_selection.spanned_rows(false, &display_map);
12842
12843                        while let Some(next_selection) = selections_iter.peek() {
12844                            let next_rows = next_selection.spanned_rows(false, &display_map);
12845                            if next_rows.start < rows.end {
12846                                rows.end = next_rows.end;
12847                                group_selections.push(selections_iter.next().unwrap());
12848                            } else {
12849                                break;
12850                            }
12851                        }
12852
12853                        let row_count = rows.end.0 - rows.start.0;
12854
12855                        // Move all selections in this group up by the total number of duplicated rows
12856                        for selection in group_selections {
12857                            let new_start = Point::new(
12858                                selection.start.row.saturating_sub(row_count),
12859                                selection.start.column,
12860                            );
12861
12862                            let new_end = Point::new(
12863                                selection.end.row.saturating_sub(row_count),
12864                                selection.end.column,
12865                            );
12866
12867                            new_ranges.push(new_start..new_end);
12868                        }
12869                    }
12870
12871                    s.select_ranges(new_ranges);
12872                });
12873            }
12874
12875            this.request_autoscroll(Autoscroll::fit(), cx);
12876        });
12877    }
12878
12879    pub fn duplicate_line_up(
12880        &mut self,
12881        _: &DuplicateLineUp,
12882        window: &mut Window,
12883        cx: &mut Context<Self>,
12884    ) {
12885        self.duplicate(true, true, window, cx);
12886    }
12887
12888    pub fn duplicate_line_down(
12889        &mut self,
12890        _: &DuplicateLineDown,
12891        window: &mut Window,
12892        cx: &mut Context<Self>,
12893    ) {
12894        self.duplicate(false, true, window, cx);
12895    }
12896
12897    pub fn duplicate_selection(
12898        &mut self,
12899        _: &DuplicateSelection,
12900        window: &mut Window,
12901        cx: &mut Context<Self>,
12902    ) {
12903        self.duplicate(false, false, window, cx);
12904    }
12905
12906    pub fn move_line_up(&mut self, _: &MoveLineUp, window: &mut Window, cx: &mut Context<Self>) {
12907        self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
12908        if self.mode.is_single_line() {
12909            cx.propagate();
12910            return;
12911        }
12912
12913        let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
12914        let buffer = self.buffer.read(cx).snapshot(cx);
12915
12916        let mut edits = Vec::new();
12917        let mut unfold_ranges = Vec::new();
12918        let mut refold_creases = Vec::new();
12919
12920        let selections = self.selections.all::<Point>(&display_map);
12921        let mut selections = selections.iter().peekable();
12922        let mut contiguous_row_selections = Vec::new();
12923        let mut new_selections = Vec::new();
12924
12925        while let Some(selection) = selections.next() {
12926            // Find all the selections that span a contiguous row range
12927            let (start_row, end_row) = consume_contiguous_rows(
12928                &mut contiguous_row_selections,
12929                selection,
12930                &display_map,
12931                &mut selections,
12932            );
12933
12934            // Move the text spanned by the row range to be before the line preceding the row range
12935            if start_row.0 > 0 {
12936                let range_to_move = Point::new(
12937                    start_row.previous_row().0,
12938                    buffer.line_len(start_row.previous_row()),
12939                )
12940                    ..Point::new(
12941                        end_row.previous_row().0,
12942                        buffer.line_len(end_row.previous_row()),
12943                    );
12944                let insertion_point = display_map
12945                    .prev_line_boundary(Point::new(start_row.previous_row().0, 0))
12946                    .0;
12947
12948                // Don't move lines across excerpts
12949                if buffer
12950                    .excerpt_containing(insertion_point..range_to_move.end)
12951                    .is_some()
12952                {
12953                    let text = buffer
12954                        .text_for_range(range_to_move.clone())
12955                        .flat_map(|s| s.chars())
12956                        .skip(1)
12957                        .chain(['\n'])
12958                        .collect::<String>();
12959
12960                    edits.push((
12961                        buffer.anchor_after(range_to_move.start)
12962                            ..buffer.anchor_before(range_to_move.end),
12963                        String::new(),
12964                    ));
12965                    let insertion_anchor = buffer.anchor_after(insertion_point);
12966                    edits.push((insertion_anchor..insertion_anchor, text));
12967
12968                    let row_delta = range_to_move.start.row - insertion_point.row + 1;
12969
12970                    // Move selections up
12971                    new_selections.extend(contiguous_row_selections.drain(..).map(
12972                        |mut selection| {
12973                            selection.start.row -= row_delta;
12974                            selection.end.row -= row_delta;
12975                            selection
12976                        },
12977                    ));
12978
12979                    // Move folds up
12980                    unfold_ranges.push(range_to_move.clone());
12981                    for fold in display_map.folds_in_range(
12982                        buffer.anchor_before(range_to_move.start)
12983                            ..buffer.anchor_after(range_to_move.end),
12984                    ) {
12985                        let mut start = fold.range.start.to_point(&buffer);
12986                        let mut end = fold.range.end.to_point(&buffer);
12987                        start.row -= row_delta;
12988                        end.row -= row_delta;
12989                        refold_creases.push(Crease::simple(start..end, fold.placeholder.clone()));
12990                    }
12991                }
12992            }
12993
12994            // If we didn't move line(s), preserve the existing selections
12995            new_selections.append(&mut contiguous_row_selections);
12996        }
12997
12998        self.transact(window, cx, |this, window, cx| {
12999            this.unfold_ranges(&unfold_ranges, true, true, cx);
13000            this.buffer.update(cx, |buffer, cx| {
13001                for (range, text) in edits {
13002                    buffer.edit([(range, text)], None, cx);
13003                }
13004            });
13005            this.fold_creases(refold_creases, true, window, cx);
13006            this.change_selections(Default::default(), window, cx, |s| {
13007                s.select(new_selections);
13008            })
13009        });
13010    }
13011
13012    pub fn move_line_down(
13013        &mut self,
13014        _: &MoveLineDown,
13015        window: &mut Window,
13016        cx: &mut Context<Self>,
13017    ) {
13018        self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
13019        if self.mode.is_single_line() {
13020            cx.propagate();
13021            return;
13022        }
13023
13024        let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
13025        let buffer = self.buffer.read(cx).snapshot(cx);
13026
13027        let mut edits = Vec::new();
13028        let mut unfold_ranges = Vec::new();
13029        let mut refold_creases = Vec::new();
13030
13031        let selections = self.selections.all::<Point>(&display_map);
13032        let mut selections = selections.iter().peekable();
13033        let mut contiguous_row_selections = Vec::new();
13034        let mut new_selections = Vec::new();
13035
13036        while let Some(selection) = selections.next() {
13037            // Find all the selections that span a contiguous row range
13038            let (start_row, end_row) = consume_contiguous_rows(
13039                &mut contiguous_row_selections,
13040                selection,
13041                &display_map,
13042                &mut selections,
13043            );
13044
13045            // Move the text spanned by the row range to be after the last line of the row range
13046            if end_row.0 <= buffer.max_point().row {
13047                let range_to_move =
13048                    MultiBufferPoint::new(start_row.0, 0)..MultiBufferPoint::new(end_row.0, 0);
13049                let insertion_point = display_map
13050                    .next_line_boundary(MultiBufferPoint::new(end_row.0, 0))
13051                    .0;
13052
13053                // Don't move lines across excerpt boundaries
13054                if buffer
13055                    .excerpt_containing(range_to_move.start..insertion_point)
13056                    .is_some()
13057                {
13058                    let mut text = String::from("\n");
13059                    text.extend(buffer.text_for_range(range_to_move.clone()));
13060                    text.pop(); // Drop trailing newline
13061                    edits.push((
13062                        buffer.anchor_after(range_to_move.start)
13063                            ..buffer.anchor_before(range_to_move.end),
13064                        String::new(),
13065                    ));
13066                    let insertion_anchor = buffer.anchor_after(insertion_point);
13067                    edits.push((insertion_anchor..insertion_anchor, text));
13068
13069                    let row_delta = insertion_point.row - range_to_move.end.row + 1;
13070
13071                    // Move selections down
13072                    new_selections.extend(contiguous_row_selections.drain(..).map(
13073                        |mut selection| {
13074                            selection.start.row += row_delta;
13075                            selection.end.row += row_delta;
13076                            selection
13077                        },
13078                    ));
13079
13080                    // Move folds down
13081                    unfold_ranges.push(range_to_move.clone());
13082                    for fold in display_map.folds_in_range(
13083                        buffer.anchor_before(range_to_move.start)
13084                            ..buffer.anchor_after(range_to_move.end),
13085                    ) {
13086                        let mut start = fold.range.start.to_point(&buffer);
13087                        let mut end = fold.range.end.to_point(&buffer);
13088                        start.row += row_delta;
13089                        end.row += row_delta;
13090                        refold_creases.push(Crease::simple(start..end, fold.placeholder.clone()));
13091                    }
13092                }
13093            }
13094
13095            // If we didn't move line(s), preserve the existing selections
13096            new_selections.append(&mut contiguous_row_selections);
13097        }
13098
13099        self.transact(window, cx, |this, window, cx| {
13100            this.unfold_ranges(&unfold_ranges, true, true, cx);
13101            this.buffer.update(cx, |buffer, cx| {
13102                for (range, text) in edits {
13103                    buffer.edit([(range, text)], None, cx);
13104                }
13105            });
13106            this.fold_creases(refold_creases, true, window, cx);
13107            this.change_selections(Default::default(), window, cx, |s| s.select(new_selections));
13108        });
13109    }
13110
13111    pub fn transpose(&mut self, _: &Transpose, window: &mut Window, cx: &mut Context<Self>) {
13112        self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
13113        let text_layout_details = &self.text_layout_details(window, cx);
13114        self.transact(window, cx, |this, window, cx| {
13115            let edits = this.change_selections(Default::default(), window, cx, |s| {
13116                let mut edits: Vec<(Range<MultiBufferOffset>, String)> = Default::default();
13117                s.move_with(&mut |display_map, selection| {
13118                    if !selection.is_empty() {
13119                        return;
13120                    }
13121
13122                    let mut head = selection.head();
13123                    let mut transpose_offset = head.to_offset(display_map, Bias::Right);
13124                    if head.column() == display_map.line_len(head.row()) {
13125                        transpose_offset = display_map
13126                            .buffer_snapshot()
13127                            .clip_offset(transpose_offset.saturating_sub_usize(1), Bias::Left);
13128                    }
13129
13130                    if transpose_offset == MultiBufferOffset(0) {
13131                        return;
13132                    }
13133
13134                    *head.column_mut() += 1;
13135                    head = display_map.clip_point(head, Bias::Right);
13136                    let goal = SelectionGoal::HorizontalPosition(
13137                        display_map
13138                            .x_for_display_point(head, text_layout_details)
13139                            .into(),
13140                    );
13141                    selection.collapse_to(head, goal);
13142
13143                    let transpose_start = display_map
13144                        .buffer_snapshot()
13145                        .clip_offset(transpose_offset.saturating_sub_usize(1), Bias::Left);
13146                    if edits.last().is_none_or(|e| e.0.end <= transpose_start) {
13147                        let transpose_end = display_map
13148                            .buffer_snapshot()
13149                            .clip_offset(transpose_offset + 1usize, Bias::Right);
13150                        if let Some(ch) = display_map
13151                            .buffer_snapshot()
13152                            .chars_at(transpose_start)
13153                            .next()
13154                        {
13155                            edits.push((transpose_start..transpose_offset, String::new()));
13156                            edits.push((transpose_end..transpose_end, ch.to_string()));
13157                        }
13158                    }
13159                });
13160                edits
13161            });
13162            this.buffer
13163                .update(cx, |buffer, cx| buffer.edit(edits, None, cx));
13164            let selections = this
13165                .selections
13166                .all::<MultiBufferOffset>(&this.display_snapshot(cx));
13167            this.change_selections(Default::default(), window, cx, |s| {
13168                s.select(selections);
13169            });
13170        });
13171    }
13172
13173    pub fn rewrap(&mut self, _: &Rewrap, _: &mut Window, cx: &mut Context<Self>) {
13174        self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
13175        if self.mode.is_single_line() {
13176            cx.propagate();
13177            return;
13178        }
13179
13180        self.rewrap_impl(RewrapOptions::default(), cx)
13181    }
13182
13183    pub fn rewrap_impl(&mut self, options: RewrapOptions, cx: &mut Context<Self>) {
13184        let buffer = self.buffer.read(cx).snapshot(cx);
13185        let selections = self.selections.all::<Point>(&self.display_snapshot(cx));
13186
13187        #[derive(Clone, Debug, PartialEq)]
13188        enum CommentFormat {
13189            /// single line comment, with prefix for line
13190            Line(String),
13191            /// single line within a block comment, with prefix for line
13192            BlockLine(String),
13193            /// a single line of a block comment that includes the initial delimiter
13194            BlockCommentWithStart(BlockCommentConfig),
13195            /// a single line of a block comment that includes the ending delimiter
13196            BlockCommentWithEnd(BlockCommentConfig),
13197        }
13198
13199        // Split selections to respect paragraph, indent, and comment prefix boundaries.
13200        let wrap_ranges = selections.into_iter().flat_map(|selection| {
13201            let mut non_blank_rows_iter = (selection.start.row..=selection.end.row)
13202                .filter(|row| !buffer.is_line_blank(MultiBufferRow(*row)))
13203                .peekable();
13204
13205            let first_row = if let Some(&row) = non_blank_rows_iter.peek() {
13206                row
13207            } else {
13208                return Vec::new();
13209            };
13210
13211            let language_settings = buffer.language_settings_at(selection.head(), cx);
13212            let language_scope = buffer.language_scope_at(selection.head());
13213
13214            let indent_and_prefix_for_row =
13215                |row: u32| -> (IndentSize, Option<CommentFormat>, Option<String>) {
13216                    let indent = buffer.indent_size_for_line(MultiBufferRow(row));
13217                    let (comment_prefix, rewrap_prefix) = if let Some(language_scope) =
13218                        &language_scope
13219                    {
13220                        let indent_end = Point::new(row, indent.len);
13221                        let line_end = Point::new(row, buffer.line_len(MultiBufferRow(row)));
13222                        let line_text_after_indent = buffer
13223                            .text_for_range(indent_end..line_end)
13224                            .collect::<String>();
13225
13226                        let is_within_comment_override = buffer
13227                            .language_scope_at(indent_end)
13228                            .is_some_and(|scope| scope.override_name() == Some("comment"));
13229                        let comment_delimiters = if is_within_comment_override {
13230                            // we are within a comment syntax node, but we don't
13231                            // yet know what kind of comment: block, doc or line
13232                            match (
13233                                language_scope.documentation_comment(),
13234                                language_scope.block_comment(),
13235                            ) {
13236                                (Some(config), _) | (_, Some(config))
13237                                    if buffer.contains_str_at(indent_end, &config.start) =>
13238                                {
13239                                    Some(CommentFormat::BlockCommentWithStart(config.clone()))
13240                                }
13241                                (Some(config), _) | (_, Some(config))
13242                                    if line_text_after_indent.ends_with(config.end.as_ref()) =>
13243                                {
13244                                    Some(CommentFormat::BlockCommentWithEnd(config.clone()))
13245                                }
13246                                (Some(config), _) | (_, Some(config))
13247                                    if buffer.contains_str_at(indent_end, &config.prefix) =>
13248                                {
13249                                    Some(CommentFormat::BlockLine(config.prefix.to_string()))
13250                                }
13251                                (_, _) => language_scope
13252                                    .line_comment_prefixes()
13253                                    .iter()
13254                                    .find(|prefix| buffer.contains_str_at(indent_end, prefix))
13255                                    .map(|prefix| CommentFormat::Line(prefix.to_string())),
13256                            }
13257                        } else {
13258                            // we not in an overridden comment node, but we may
13259                            // be within a non-overridden line comment node
13260                            language_scope
13261                                .line_comment_prefixes()
13262                                .iter()
13263                                .find(|prefix| buffer.contains_str_at(indent_end, prefix))
13264                                .map(|prefix| CommentFormat::Line(prefix.to_string()))
13265                        };
13266
13267                        let rewrap_prefix = language_scope
13268                            .rewrap_prefixes()
13269                            .iter()
13270                            .find_map(|prefix_regex| {
13271                                prefix_regex.find(&line_text_after_indent).map(|mat| {
13272                                    if mat.start() == 0 {
13273                                        Some(mat.as_str().to_string())
13274                                    } else {
13275                                        None
13276                                    }
13277                                })
13278                            })
13279                            .flatten();
13280                        (comment_delimiters, rewrap_prefix)
13281                    } else {
13282                        (None, None)
13283                    };
13284                    (indent, comment_prefix, rewrap_prefix)
13285                };
13286
13287            let mut ranges = Vec::new();
13288            let from_empty_selection = selection.is_empty();
13289
13290            let mut current_range_start = first_row;
13291            let mut prev_row = first_row;
13292            let (
13293                mut current_range_indent,
13294                mut current_range_comment_delimiters,
13295                mut current_range_rewrap_prefix,
13296            ) = indent_and_prefix_for_row(first_row);
13297
13298            for row in non_blank_rows_iter.skip(1) {
13299                let has_paragraph_break = row > prev_row + 1;
13300
13301                let (row_indent, row_comment_delimiters, row_rewrap_prefix) =
13302                    indent_and_prefix_for_row(row);
13303
13304                let has_indent_change = row_indent != current_range_indent;
13305                let has_comment_change = row_comment_delimiters != current_range_comment_delimiters;
13306
13307                let has_boundary_change = has_comment_change
13308                    || row_rewrap_prefix.is_some()
13309                    || (has_indent_change && current_range_comment_delimiters.is_some());
13310
13311                if has_paragraph_break || has_boundary_change {
13312                    ranges.push((
13313                        language_settings.clone(),
13314                        Point::new(current_range_start, 0)
13315                            ..Point::new(prev_row, buffer.line_len(MultiBufferRow(prev_row))),
13316                        current_range_indent,
13317                        current_range_comment_delimiters.clone(),
13318                        current_range_rewrap_prefix.clone(),
13319                        from_empty_selection,
13320                    ));
13321                    current_range_start = row;
13322                    current_range_indent = row_indent;
13323                    current_range_comment_delimiters = row_comment_delimiters;
13324                    current_range_rewrap_prefix = row_rewrap_prefix;
13325                }
13326                prev_row = row;
13327            }
13328
13329            ranges.push((
13330                language_settings.clone(),
13331                Point::new(current_range_start, 0)
13332                    ..Point::new(prev_row, buffer.line_len(MultiBufferRow(prev_row))),
13333                current_range_indent,
13334                current_range_comment_delimiters,
13335                current_range_rewrap_prefix,
13336                from_empty_selection,
13337            ));
13338
13339            ranges
13340        });
13341
13342        let mut edits = Vec::new();
13343        let mut rewrapped_row_ranges = Vec::<RangeInclusive<u32>>::new();
13344
13345        for (
13346            language_settings,
13347            wrap_range,
13348            mut indent_size,
13349            comment_prefix,
13350            rewrap_prefix,
13351            from_empty_selection,
13352        ) in wrap_ranges
13353        {
13354            let mut start_row = wrap_range.start.row;
13355            let mut end_row = wrap_range.end.row;
13356
13357            // Skip selections that overlap with a range that has already been rewrapped.
13358            let selection_range = start_row..end_row;
13359            if rewrapped_row_ranges
13360                .iter()
13361                .any(|range| range.overlaps(&selection_range))
13362            {
13363                continue;
13364            }
13365
13366            let tab_size = language_settings.tab_size;
13367
13368            let (line_prefix, inside_comment) = match &comment_prefix {
13369                Some(CommentFormat::Line(prefix) | CommentFormat::BlockLine(prefix)) => {
13370                    (Some(prefix.as_str()), true)
13371                }
13372                Some(CommentFormat::BlockCommentWithEnd(BlockCommentConfig { prefix, .. })) => {
13373                    (Some(prefix.as_ref()), true)
13374                }
13375                Some(CommentFormat::BlockCommentWithStart(BlockCommentConfig {
13376                    start: _,
13377                    end: _,
13378                    prefix,
13379                    tab_size,
13380                })) => {
13381                    indent_size.len += tab_size;
13382                    (Some(prefix.as_ref()), true)
13383                }
13384                None => (None, false),
13385            };
13386            let indent_prefix = indent_size.chars().collect::<String>();
13387            let line_prefix = format!("{indent_prefix}{}", line_prefix.unwrap_or(""));
13388
13389            let allow_rewrap_based_on_language = match language_settings.allow_rewrap {
13390                RewrapBehavior::InComments => inside_comment,
13391                RewrapBehavior::InSelections => !wrap_range.is_empty(),
13392                RewrapBehavior::Anywhere => true,
13393            };
13394
13395            let should_rewrap = options.override_language_settings
13396                || allow_rewrap_based_on_language
13397                || self.hard_wrap.is_some();
13398            if !should_rewrap {
13399                continue;
13400            }
13401
13402            if from_empty_selection {
13403                'expand_upwards: while start_row > 0 {
13404                    let prev_row = start_row - 1;
13405                    if buffer.contains_str_at(Point::new(prev_row, 0), &line_prefix)
13406                        && buffer.line_len(MultiBufferRow(prev_row)) as usize > line_prefix.len()
13407                        && !buffer.is_line_blank(MultiBufferRow(prev_row))
13408                    {
13409                        start_row = prev_row;
13410                    } else {
13411                        break 'expand_upwards;
13412                    }
13413                }
13414
13415                'expand_downwards: while end_row < buffer.max_point().row {
13416                    let next_row = end_row + 1;
13417                    if buffer.contains_str_at(Point::new(next_row, 0), &line_prefix)
13418                        && buffer.line_len(MultiBufferRow(next_row)) as usize > line_prefix.len()
13419                        && !buffer.is_line_blank(MultiBufferRow(next_row))
13420                    {
13421                        end_row = next_row;
13422                    } else {
13423                        break 'expand_downwards;
13424                    }
13425                }
13426            }
13427
13428            let start = Point::new(start_row, 0);
13429            let start_offset = ToOffset::to_offset(&start, &buffer);
13430            let end = Point::new(end_row, buffer.line_len(MultiBufferRow(end_row)));
13431            let selection_text = buffer.text_for_range(start..end).collect::<String>();
13432            let mut first_line_delimiter = None;
13433            let mut last_line_delimiter = None;
13434            let Some(lines_without_prefixes) = selection_text
13435                .lines()
13436                .enumerate()
13437                .map(|(ix, line)| {
13438                    let line_trimmed = line.trim_start();
13439                    if rewrap_prefix.is_some() && ix > 0 {
13440                        Ok(line_trimmed)
13441                    } else if let Some(
13442                        CommentFormat::BlockCommentWithStart(BlockCommentConfig {
13443                            start,
13444                            prefix,
13445                            end,
13446                            tab_size,
13447                        })
13448                        | CommentFormat::BlockCommentWithEnd(BlockCommentConfig {
13449                            start,
13450                            prefix,
13451                            end,
13452                            tab_size,
13453                        }),
13454                    ) = &comment_prefix
13455                    {
13456                        let line_trimmed = line_trimmed
13457                            .strip_prefix(start.as_ref())
13458                            .map(|s| {
13459                                let mut indent_size = indent_size;
13460                                indent_size.len -= tab_size;
13461                                let indent_prefix: String = indent_size.chars().collect();
13462                                first_line_delimiter = Some((indent_prefix, start));
13463                                s.trim_start()
13464                            })
13465                            .unwrap_or(line_trimmed);
13466                        let line_trimmed = line_trimmed
13467                            .strip_suffix(end.as_ref())
13468                            .map(|s| {
13469                                last_line_delimiter = Some(end);
13470                                s.trim_end()
13471                            })
13472                            .unwrap_or(line_trimmed);
13473                        let line_trimmed = line_trimmed
13474                            .strip_prefix(prefix.as_ref())
13475                            .unwrap_or(line_trimmed);
13476                        Ok(line_trimmed)
13477                    } else if let Some(CommentFormat::BlockLine(prefix)) = &comment_prefix {
13478                        line_trimmed.strip_prefix(prefix).with_context(|| {
13479                            format!("line did not start with prefix {prefix:?}: {line:?}")
13480                        })
13481                    } else {
13482                        line_trimmed
13483                            .strip_prefix(&line_prefix.trim_start())
13484                            .with_context(|| {
13485                                format!("line did not start with prefix {line_prefix:?}: {line:?}")
13486                            })
13487                    }
13488                })
13489                .collect::<Result<Vec<_>, _>>()
13490                .log_err()
13491            else {
13492                continue;
13493            };
13494
13495            let wrap_column = self.hard_wrap.unwrap_or_else(|| {
13496                buffer
13497                    .language_settings_at(Point::new(start_row, 0), cx)
13498                    .preferred_line_length as usize
13499            });
13500
13501            let subsequent_lines_prefix = if let Some(rewrap_prefix_str) = &rewrap_prefix {
13502                format!("{}{}", indent_prefix, " ".repeat(rewrap_prefix_str.len()))
13503            } else {
13504                line_prefix.clone()
13505            };
13506
13507            let wrapped_text = {
13508                let mut wrapped_text = wrap_with_prefix(
13509                    line_prefix,
13510                    subsequent_lines_prefix,
13511                    lines_without_prefixes.join("\n"),
13512                    wrap_column,
13513                    tab_size,
13514                    options.preserve_existing_whitespace,
13515                );
13516
13517                if let Some((indent, delimiter)) = first_line_delimiter {
13518                    wrapped_text = format!("{indent}{delimiter}\n{wrapped_text}");
13519                }
13520                if let Some(last_line) = last_line_delimiter {
13521                    wrapped_text = format!("{wrapped_text}\n{indent_prefix}{last_line}");
13522                }
13523
13524                wrapped_text
13525            };
13526
13527            // TODO: should always use char-based diff while still supporting cursor behavior that
13528            // matches vim.
13529            let mut diff_options = DiffOptions::default();
13530            if options.override_language_settings {
13531                diff_options.max_word_diff_len = 0;
13532                diff_options.max_word_diff_line_count = 0;
13533            } else {
13534                diff_options.max_word_diff_len = usize::MAX;
13535                diff_options.max_word_diff_line_count = usize::MAX;
13536            }
13537
13538            for (old_range, new_text) in
13539                text_diff_with_options(&selection_text, &wrapped_text, diff_options)
13540            {
13541                let edit_start = buffer.anchor_after(start_offset + old_range.start);
13542                let edit_end = buffer.anchor_after(start_offset + old_range.end);
13543                edits.push((edit_start..edit_end, new_text));
13544            }
13545
13546            rewrapped_row_ranges.push(start_row..=end_row);
13547        }
13548
13549        self.buffer
13550            .update(cx, |buffer, cx| buffer.edit(edits, None, cx));
13551    }
13552
13553    pub fn cut_common(
13554        &mut self,
13555        cut_no_selection_line: bool,
13556        window: &mut Window,
13557        cx: &mut Context<Self>,
13558    ) -> ClipboardItem {
13559        let mut text = String::new();
13560        let buffer = self.buffer.read(cx).snapshot(cx);
13561        let mut selections = self.selections.all::<Point>(&self.display_snapshot(cx));
13562        let mut clipboard_selections = Vec::with_capacity(selections.len());
13563        {
13564            let max_point = buffer.max_point();
13565            let mut is_first = true;
13566            let mut prev_selection_was_entire_line = false;
13567            for selection in &mut selections {
13568                let is_entire_line =
13569                    (selection.is_empty() && cut_no_selection_line) || self.selections.line_mode();
13570                if is_entire_line {
13571                    selection.start = Point::new(selection.start.row, 0);
13572                    if !selection.is_empty() && selection.end.column == 0 {
13573                        selection.end = cmp::min(max_point, selection.end);
13574                    } else {
13575                        selection.end = cmp::min(max_point, Point::new(selection.end.row + 1, 0));
13576                    }
13577                    selection.goal = SelectionGoal::None;
13578                }
13579                if is_first {
13580                    is_first = false;
13581                } else if !prev_selection_was_entire_line {
13582                    text += "\n";
13583                }
13584                prev_selection_was_entire_line = is_entire_line;
13585                let mut len = 0;
13586                for chunk in buffer.text_for_range(selection.start..selection.end) {
13587                    text.push_str(chunk);
13588                    len += chunk.len();
13589                }
13590
13591                clipboard_selections.push(ClipboardSelection::for_buffer(
13592                    len,
13593                    is_entire_line,
13594                    selection.range(),
13595                    &buffer,
13596                    self.project.as_ref(),
13597                    cx,
13598                ));
13599            }
13600        }
13601
13602        self.transact(window, cx, |this, window, cx| {
13603            this.change_selections(Default::default(), window, cx, |s| {
13604                s.select(selections);
13605            });
13606            this.insert("", window, cx);
13607        });
13608        ClipboardItem::new_string_with_json_metadata(text, clipboard_selections)
13609    }
13610
13611    pub fn cut(&mut self, _: &Cut, window: &mut Window, cx: &mut Context<Self>) {
13612        self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
13613        let item = self.cut_common(true, window, cx);
13614        cx.write_to_clipboard(item);
13615    }
13616
13617    pub fn kill_ring_cut(&mut self, _: &KillRingCut, window: &mut Window, cx: &mut Context<Self>) {
13618        self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
13619        self.change_selections(SelectionEffects::no_scroll(), window, cx, |s| {
13620            s.move_with(&mut |snapshot, sel| {
13621                if sel.is_empty() {
13622                    sel.end = DisplayPoint::new(sel.end.row(), snapshot.line_len(sel.end.row()));
13623                }
13624                if sel.is_empty() {
13625                    sel.end = DisplayPoint::new(sel.end.row() + 1_u32, 0);
13626                }
13627            });
13628        });
13629        let item = self.cut_common(false, window, cx);
13630        cx.set_global(KillRing(item))
13631    }
13632
13633    pub fn kill_ring_yank(
13634        &mut self,
13635        _: &KillRingYank,
13636        window: &mut Window,
13637        cx: &mut Context<Self>,
13638    ) {
13639        self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
13640        let (text, metadata) = if let Some(KillRing(item)) = cx.try_global() {
13641            if let Some(ClipboardEntry::String(kill_ring)) = item.entries().first() {
13642                (kill_ring.text().to_string(), kill_ring.metadata_json())
13643            } else {
13644                return;
13645            }
13646        } else {
13647            return;
13648        };
13649        self.do_paste(&text, metadata, false, window, cx);
13650    }
13651
13652    pub fn copy_and_trim(&mut self, _: &CopyAndTrim, _: &mut Window, cx: &mut Context<Self>) {
13653        self.do_copy(true, cx);
13654    }
13655
13656    pub fn copy(&mut self, _: &Copy, _: &mut Window, cx: &mut Context<Self>) {
13657        self.do_copy(false, cx);
13658    }
13659
13660    fn do_copy(&self, strip_leading_indents: bool, cx: &mut Context<Self>) {
13661        let selections = self.selections.all::<Point>(&self.display_snapshot(cx));
13662        let buffer = self.buffer.read(cx).read(cx);
13663        let mut text = String::new();
13664
13665        let mut clipboard_selections = Vec::with_capacity(selections.len());
13666        {
13667            let max_point = buffer.max_point();
13668            let mut is_first = true;
13669            let mut prev_selection_was_entire_line = false;
13670            for selection in &selections {
13671                let mut start = selection.start;
13672                let mut end = selection.end;
13673                let is_entire_line = selection.is_empty() || self.selections.line_mode();
13674                let mut add_trailing_newline = false;
13675                if is_entire_line {
13676                    start = Point::new(start.row, 0);
13677                    let next_line_start = Point::new(end.row + 1, 0);
13678                    if next_line_start <= max_point {
13679                        end = next_line_start;
13680                    } else {
13681                        // We're on the last line without a trailing newline.
13682                        // Copy to the end of the line and add a newline afterwards.
13683                        end = Point::new(end.row, buffer.line_len(MultiBufferRow(end.row)));
13684                        add_trailing_newline = true;
13685                    }
13686                }
13687
13688                let mut trimmed_selections = Vec::new();
13689                if strip_leading_indents && end.row.saturating_sub(start.row) > 0 {
13690                    let row = MultiBufferRow(start.row);
13691                    let first_indent = buffer.indent_size_for_line(row);
13692                    if first_indent.len == 0 || start.column > first_indent.len {
13693                        trimmed_selections.push(start..end);
13694                    } else {
13695                        trimmed_selections.push(
13696                            Point::new(row.0, first_indent.len)
13697                                ..Point::new(row.0, buffer.line_len(row)),
13698                        );
13699                        for row in start.row + 1..=end.row {
13700                            let mut line_len = buffer.line_len(MultiBufferRow(row));
13701                            if row == end.row {
13702                                line_len = end.column;
13703                            }
13704                            if line_len == 0 {
13705                                trimmed_selections
13706                                    .push(Point::new(row, 0)..Point::new(row, line_len));
13707                                continue;
13708                            }
13709                            let row_indent_size = buffer.indent_size_for_line(MultiBufferRow(row));
13710                            if row_indent_size.len >= first_indent.len {
13711                                trimmed_selections.push(
13712                                    Point::new(row, first_indent.len)..Point::new(row, line_len),
13713                                );
13714                            } else {
13715                                trimmed_selections.clear();
13716                                trimmed_selections.push(start..end);
13717                                break;
13718                            }
13719                        }
13720                    }
13721                } else {
13722                    trimmed_selections.push(start..end);
13723                }
13724
13725                let is_multiline_trim = trimmed_selections.len() > 1;
13726                for trimmed_range in trimmed_selections {
13727                    if is_first {
13728                        is_first = false;
13729                    } else if is_multiline_trim || !prev_selection_was_entire_line {
13730                        text += "\n";
13731                    }
13732                    prev_selection_was_entire_line = is_entire_line && !is_multiline_trim;
13733                    let mut len = 0;
13734                    for chunk in buffer.text_for_range(trimmed_range.start..trimmed_range.end) {
13735                        text.push_str(chunk);
13736                        len += chunk.len();
13737                    }
13738                    if add_trailing_newline {
13739                        text.push('\n');
13740                        len += 1;
13741                    }
13742                    clipboard_selections.push(ClipboardSelection::for_buffer(
13743                        len,
13744                        is_entire_line,
13745                        trimmed_range,
13746                        &buffer,
13747                        self.project.as_ref(),
13748                        cx,
13749                    ));
13750                }
13751            }
13752        }
13753
13754        cx.write_to_clipboard(ClipboardItem::new_string_with_json_metadata(
13755            text,
13756            clipboard_selections,
13757        ));
13758    }
13759
13760    pub fn do_paste(
13761        &mut self,
13762        text: &String,
13763        clipboard_selections: Option<Vec<ClipboardSelection>>,
13764        handle_entire_lines: bool,
13765        window: &mut Window,
13766        cx: &mut Context<Self>,
13767    ) {
13768        if self.read_only(cx) {
13769            return;
13770        }
13771
13772        let clipboard_text = Cow::Borrowed(text.as_str());
13773
13774        self.transact(window, cx, |this, window, cx| {
13775            let had_active_edit_prediction = this.has_active_edit_prediction();
13776            let display_map = this.display_snapshot(cx);
13777            let old_selections = this.selections.all::<MultiBufferOffset>(&display_map);
13778            let cursor_offset = this
13779                .selections
13780                .last::<MultiBufferOffset>(&display_map)
13781                .head();
13782
13783            if let Some(mut clipboard_selections) = clipboard_selections {
13784                let all_selections_were_entire_line =
13785                    clipboard_selections.iter().all(|s| s.is_entire_line);
13786                let first_selection_indent_column =
13787                    clipboard_selections.first().map(|s| s.first_line_indent);
13788                if clipboard_selections.len() != old_selections.len() {
13789                    clipboard_selections.drain(..);
13790                }
13791                let mut auto_indent_on_paste = true;
13792
13793                this.buffer.update(cx, |buffer, cx| {
13794                    let snapshot = buffer.read(cx);
13795                    auto_indent_on_paste = snapshot
13796                        .language_settings_at(cursor_offset, cx)
13797                        .auto_indent_on_paste;
13798
13799                    let mut start_offset = 0;
13800                    let mut edits = Vec::new();
13801                    let mut original_indent_columns = Vec::new();
13802                    for (ix, selection) in old_selections.iter().enumerate() {
13803                        let to_insert;
13804                        let entire_line;
13805                        let original_indent_column;
13806                        if let Some(clipboard_selection) = clipboard_selections.get(ix) {
13807                            let end_offset = start_offset + clipboard_selection.len;
13808                            to_insert = &clipboard_text[start_offset..end_offset];
13809                            entire_line = clipboard_selection.is_entire_line;
13810                            start_offset = if entire_line {
13811                                end_offset
13812                            } else {
13813                                end_offset + 1
13814                            };
13815                            original_indent_column = Some(clipboard_selection.first_line_indent);
13816                        } else {
13817                            to_insert = &*clipboard_text;
13818                            entire_line = all_selections_were_entire_line;
13819                            original_indent_column = first_selection_indent_column
13820                        }
13821
13822                        let (range, to_insert) =
13823                            if selection.is_empty() && handle_entire_lines && entire_line {
13824                                // If the corresponding selection was empty when this slice of the
13825                                // clipboard text was written, then the entire line containing the
13826                                // selection was copied. If this selection is also currently empty,
13827                                // then paste the line before the current line of the buffer.
13828                                let column = selection.start.to_point(&snapshot).column as usize;
13829                                let line_start = selection.start - column;
13830                                (line_start..line_start, Cow::Borrowed(to_insert))
13831                            } else {
13832                                let language = snapshot.language_at(selection.head());
13833                                let range = selection.range();
13834                                if let Some(language) = language
13835                                    && language.name() == "Markdown".into()
13836                                {
13837                                    edit_for_markdown_paste(
13838                                        &snapshot,
13839                                        range,
13840                                        to_insert,
13841                                        url::Url::parse(to_insert).ok(),
13842                                    )
13843                                } else {
13844                                    (range, Cow::Borrowed(to_insert))
13845                                }
13846                            };
13847
13848                        edits.push((range, to_insert));
13849                        original_indent_columns.push(original_indent_column);
13850                    }
13851                    drop(snapshot);
13852
13853                    buffer.edit(
13854                        edits,
13855                        if auto_indent_on_paste {
13856                            Some(AutoindentMode::Block {
13857                                original_indent_columns,
13858                            })
13859                        } else {
13860                            None
13861                        },
13862                        cx,
13863                    );
13864                });
13865
13866                let selections = this
13867                    .selections
13868                    .all::<MultiBufferOffset>(&this.display_snapshot(cx));
13869                this.change_selections(Default::default(), window, cx, |s| s.select(selections));
13870            } else {
13871                let url = url::Url::parse(&clipboard_text).ok();
13872
13873                let auto_indent_mode = if !clipboard_text.is_empty() {
13874                    Some(AutoindentMode::Block {
13875                        original_indent_columns: Vec::new(),
13876                    })
13877                } else {
13878                    None
13879                };
13880
13881                let selection_anchors = this.buffer.update(cx, |buffer, cx| {
13882                    let snapshot = buffer.snapshot(cx);
13883
13884                    let anchors = old_selections
13885                        .iter()
13886                        .map(|s| {
13887                            let anchor = snapshot.anchor_after(s.head());
13888                            s.map(|_| anchor)
13889                        })
13890                        .collect::<Vec<_>>();
13891
13892                    let mut edits = Vec::new();
13893
13894                    for selection in old_selections.iter() {
13895                        let language = snapshot.language_at(selection.head());
13896                        let range = selection.range();
13897
13898                        let (edit_range, edit_text) = if let Some(language) = language
13899                            && language.name() == "Markdown".into()
13900                        {
13901                            edit_for_markdown_paste(&snapshot, range, &clipboard_text, url.clone())
13902                        } else {
13903                            (range, clipboard_text.clone())
13904                        };
13905
13906                        edits.push((edit_range, edit_text));
13907                    }
13908
13909                    drop(snapshot);
13910                    buffer.edit(edits, auto_indent_mode, cx);
13911
13912                    anchors
13913                });
13914
13915                this.change_selections(Default::default(), window, cx, |s| {
13916                    s.select_anchors(selection_anchors);
13917                });
13918            }
13919
13920            //   🤔                 |    ..     | show_in_menu |
13921            // | ..                  |   true        true
13922            // | had_edit_prediction |   false       true
13923
13924            let trigger_in_words =
13925                this.show_edit_predictions_in_menu() || !had_active_edit_prediction;
13926
13927            this.trigger_completion_on_input(text, trigger_in_words, window, cx);
13928        });
13929    }
13930
13931    pub fn diff_clipboard_with_selection(
13932        &mut self,
13933        _: &DiffClipboardWithSelection,
13934        window: &mut Window,
13935        cx: &mut Context<Self>,
13936    ) {
13937        let selections = self
13938            .selections
13939            .all::<MultiBufferOffset>(&self.display_snapshot(cx));
13940
13941        if selections.is_empty() {
13942            log::warn!("There should always be at least one selection in Zed. This is a bug.");
13943            return;
13944        };
13945
13946        let clipboard_text = match cx.read_from_clipboard() {
13947            Some(item) => match item.entries().first() {
13948                Some(ClipboardEntry::String(text)) => Some(text.text().to_string()),
13949                _ => None,
13950            },
13951            None => None,
13952        };
13953
13954        let Some(clipboard_text) = clipboard_text else {
13955            log::warn!("Clipboard doesn't contain text.");
13956            return;
13957        };
13958
13959        window.dispatch_action(
13960            Box::new(DiffClipboardWithSelectionData {
13961                clipboard_text,
13962                editor: cx.entity(),
13963            }),
13964            cx,
13965        );
13966    }
13967
13968    pub fn paste(&mut self, _: &Paste, window: &mut Window, cx: &mut Context<Self>) {
13969        self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
13970        if let Some(item) = cx.read_from_clipboard() {
13971            let entries = item.entries();
13972
13973            match entries.first() {
13974                // For now, we only support applying metadata if there's one string. In the future, we can incorporate all the selections
13975                // of all the pasted entries.
13976                Some(ClipboardEntry::String(clipboard_string)) if entries.len() == 1 => self
13977                    .do_paste(
13978                        clipboard_string.text(),
13979                        clipboard_string.metadata_json::<Vec<ClipboardSelection>>(),
13980                        true,
13981                        window,
13982                        cx,
13983                    ),
13984                _ => self.do_paste(&item.text().unwrap_or_default(), None, true, window, cx),
13985            }
13986        }
13987    }
13988
13989    pub fn undo(&mut self, _: &Undo, window: &mut Window, cx: &mut Context<Self>) {
13990        if self.read_only(cx) {
13991            return;
13992        }
13993
13994        self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
13995
13996        if let Some(transaction_id) = self.buffer.update(cx, |buffer, cx| buffer.undo(cx)) {
13997            if let Some((selections, _)) =
13998                self.selection_history.transaction(transaction_id).cloned()
13999            {
14000                self.change_selections(SelectionEffects::no_scroll(), window, cx, |s| {
14001                    s.select_anchors(selections.to_vec());
14002                });
14003            } else {
14004                log::error!(
14005                    "No entry in selection_history found for undo. \
14006                     This may correspond to a bug where undo does not update the selection. \
14007                     If this is occurring, please add details to \
14008                     https://github.com/zed-industries/zed/issues/22692"
14009                );
14010            }
14011            self.request_autoscroll(Autoscroll::fit(), cx);
14012            self.unmark_text(window, cx);
14013            self.refresh_edit_prediction(true, false, window, cx);
14014            cx.emit(EditorEvent::Edited { transaction_id });
14015            cx.emit(EditorEvent::TransactionUndone { transaction_id });
14016        }
14017    }
14018
14019    pub fn redo(&mut self, _: &Redo, window: &mut Window, cx: &mut Context<Self>) {
14020        if self.read_only(cx) {
14021            return;
14022        }
14023
14024        self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
14025
14026        if let Some(transaction_id) = self.buffer.update(cx, |buffer, cx| buffer.redo(cx)) {
14027            if let Some((_, Some(selections))) =
14028                self.selection_history.transaction(transaction_id).cloned()
14029            {
14030                self.change_selections(SelectionEffects::no_scroll(), window, cx, |s| {
14031                    s.select_anchors(selections.to_vec());
14032                });
14033            } else {
14034                log::error!(
14035                    "No entry in selection_history found for redo. \
14036                     This may correspond to a bug where undo does not update the selection. \
14037                     If this is occurring, please add details to \
14038                     https://github.com/zed-industries/zed/issues/22692"
14039                );
14040            }
14041            self.request_autoscroll(Autoscroll::fit(), cx);
14042            self.unmark_text(window, cx);
14043            self.refresh_edit_prediction(true, false, window, cx);
14044            cx.emit(EditorEvent::Edited { transaction_id });
14045        }
14046    }
14047
14048    pub fn finalize_last_transaction(&mut self, cx: &mut Context<Self>) {
14049        self.buffer
14050            .update(cx, |buffer, cx| buffer.finalize_last_transaction(cx));
14051    }
14052
14053    pub fn group_until_transaction(&mut self, tx_id: TransactionId, cx: &mut Context<Self>) {
14054        self.buffer
14055            .update(cx, |buffer, cx| buffer.group_until_transaction(tx_id, cx));
14056    }
14057
14058    pub fn move_left(&mut self, _: &MoveLeft, window: &mut Window, cx: &mut Context<Self>) {
14059        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
14060        self.change_selections(Default::default(), window, cx, |s| {
14061            s.move_with(&mut |map, selection| {
14062                let cursor = if selection.is_empty() {
14063                    movement::left(map, selection.start)
14064                } else {
14065                    selection.start
14066                };
14067                selection.collapse_to(cursor, SelectionGoal::None);
14068            });
14069        })
14070    }
14071
14072    pub fn select_left(&mut self, _: &SelectLeft, window: &mut Window, cx: &mut Context<Self>) {
14073        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
14074        self.change_selections(Default::default(), window, cx, |s| {
14075            s.move_heads_with(&mut |map, head, _| (movement::left(map, head), SelectionGoal::None));
14076        })
14077    }
14078
14079    pub fn move_right(&mut self, _: &MoveRight, window: &mut Window, cx: &mut Context<Self>) {
14080        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
14081        self.change_selections(Default::default(), window, cx, |s| {
14082            s.move_with(&mut |map, selection| {
14083                let cursor = if selection.is_empty() {
14084                    movement::right(map, selection.end)
14085                } else {
14086                    selection.end
14087                };
14088                selection.collapse_to(cursor, SelectionGoal::None)
14089            });
14090        })
14091    }
14092
14093    pub fn select_right(&mut self, _: &SelectRight, window: &mut Window, cx: &mut Context<Self>) {
14094        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
14095        self.change_selections(Default::default(), window, cx, |s| {
14096            s.move_heads_with(&mut |map, head, _| {
14097                (movement::right(map, head), SelectionGoal::None)
14098            });
14099        });
14100    }
14101
14102    pub fn move_up(&mut self, _: &MoveUp, window: &mut Window, cx: &mut Context<Self>) {
14103        if self.take_rename(true, window, cx).is_some() {
14104            return;
14105        }
14106
14107        if self.mode.is_single_line() {
14108            cx.propagate();
14109            return;
14110        }
14111
14112        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
14113
14114        let text_layout_details = &self.text_layout_details(window, cx);
14115        let selection_count = self.selections.count();
14116        let first_selection = self.selections.first_anchor();
14117
14118        self.change_selections(Default::default(), window, cx, |s| {
14119            s.move_with(&mut |map, selection| {
14120                if !selection.is_empty() {
14121                    selection.goal = SelectionGoal::None;
14122                }
14123                let (cursor, goal) = movement::up(
14124                    map,
14125                    selection.start,
14126                    selection.goal,
14127                    false,
14128                    text_layout_details,
14129                );
14130                selection.collapse_to(cursor, goal);
14131            });
14132        });
14133
14134        if selection_count == 1 && first_selection.range() == self.selections.first_anchor().range()
14135        {
14136            cx.propagate();
14137        }
14138    }
14139
14140    pub fn move_up_by_lines(
14141        &mut self,
14142        action: &MoveUpByLines,
14143        window: &mut Window,
14144        cx: &mut Context<Self>,
14145    ) {
14146        if self.take_rename(true, window, cx).is_some() {
14147            return;
14148        }
14149
14150        if self.mode.is_single_line() {
14151            cx.propagate();
14152            return;
14153        }
14154
14155        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
14156
14157        let text_layout_details = &self.text_layout_details(window, cx);
14158
14159        self.change_selections(Default::default(), window, cx, |s| {
14160            s.move_with(&mut |map, selection| {
14161                if !selection.is_empty() {
14162                    selection.goal = SelectionGoal::None;
14163                }
14164                let (cursor, goal) = movement::up_by_rows(
14165                    map,
14166                    selection.start,
14167                    action.lines,
14168                    selection.goal,
14169                    false,
14170                    text_layout_details,
14171                );
14172                selection.collapse_to(cursor, goal);
14173            });
14174        })
14175    }
14176
14177    pub fn move_down_by_lines(
14178        &mut self,
14179        action: &MoveDownByLines,
14180        window: &mut Window,
14181        cx: &mut Context<Self>,
14182    ) {
14183        if self.take_rename(true, window, cx).is_some() {
14184            return;
14185        }
14186
14187        if self.mode.is_single_line() {
14188            cx.propagate();
14189            return;
14190        }
14191
14192        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
14193
14194        let text_layout_details = &self.text_layout_details(window, cx);
14195
14196        self.change_selections(Default::default(), window, cx, |s| {
14197            s.move_with(&mut |map, selection| {
14198                if !selection.is_empty() {
14199                    selection.goal = SelectionGoal::None;
14200                }
14201                let (cursor, goal) = movement::down_by_rows(
14202                    map,
14203                    selection.start,
14204                    action.lines,
14205                    selection.goal,
14206                    false,
14207                    text_layout_details,
14208                );
14209                selection.collapse_to(cursor, goal);
14210            });
14211        })
14212    }
14213
14214    pub fn select_down_by_lines(
14215        &mut self,
14216        action: &SelectDownByLines,
14217        window: &mut Window,
14218        cx: &mut Context<Self>,
14219    ) {
14220        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
14221        let text_layout_details = &self.text_layout_details(window, cx);
14222        self.change_selections(Default::default(), window, cx, |s| {
14223            s.move_heads_with(&mut |map, head, goal| {
14224                movement::down_by_rows(map, head, action.lines, goal, false, text_layout_details)
14225            })
14226        })
14227    }
14228
14229    pub fn select_up_by_lines(
14230        &mut self,
14231        action: &SelectUpByLines,
14232        window: &mut Window,
14233        cx: &mut Context<Self>,
14234    ) {
14235        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
14236        let text_layout_details = &self.text_layout_details(window, cx);
14237        self.change_selections(Default::default(), window, cx, |s| {
14238            s.move_heads_with(&mut |map, head, goal| {
14239                movement::up_by_rows(map, head, action.lines, goal, false, text_layout_details)
14240            })
14241        })
14242    }
14243
14244    pub fn select_page_up(
14245        &mut self,
14246        _: &SelectPageUp,
14247        window: &mut Window,
14248        cx: &mut Context<Self>,
14249    ) {
14250        let Some(row_count) = self.visible_row_count() else {
14251            return;
14252        };
14253
14254        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
14255
14256        let text_layout_details = &self.text_layout_details(window, cx);
14257
14258        self.change_selections(Default::default(), window, cx, |s| {
14259            s.move_heads_with(&mut |map, head, goal| {
14260                movement::up_by_rows(map, head, row_count, goal, false, text_layout_details)
14261            })
14262        })
14263    }
14264
14265    pub fn move_page_up(
14266        &mut self,
14267        action: &MovePageUp,
14268        window: &mut Window,
14269        cx: &mut Context<Self>,
14270    ) {
14271        if self.take_rename(true, window, cx).is_some() {
14272            return;
14273        }
14274
14275        if self
14276            .context_menu
14277            .borrow_mut()
14278            .as_mut()
14279            .map(|menu| menu.select_first(self.completion_provider.as_deref(), window, cx))
14280            .unwrap_or(false)
14281        {
14282            return;
14283        }
14284
14285        if matches!(self.mode, EditorMode::SingleLine) {
14286            cx.propagate();
14287            return;
14288        }
14289
14290        let Some(row_count) = self.visible_row_count() else {
14291            return;
14292        };
14293
14294        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
14295
14296        let effects = if action.center_cursor {
14297            SelectionEffects::scroll(Autoscroll::center())
14298        } else {
14299            SelectionEffects::default()
14300        };
14301
14302        let text_layout_details = &self.text_layout_details(window, cx);
14303
14304        self.change_selections(effects, window, cx, |s| {
14305            s.move_with(&mut |map, selection| {
14306                if !selection.is_empty() {
14307                    selection.goal = SelectionGoal::None;
14308                }
14309                let (cursor, goal) = movement::up_by_rows(
14310                    map,
14311                    selection.end,
14312                    row_count,
14313                    selection.goal,
14314                    false,
14315                    text_layout_details,
14316                );
14317                selection.collapse_to(cursor, goal);
14318            });
14319        });
14320    }
14321
14322    pub fn select_up(&mut self, _: &SelectUp, window: &mut Window, cx: &mut Context<Self>) {
14323        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
14324        let text_layout_details = &self.text_layout_details(window, cx);
14325        self.change_selections(Default::default(), window, cx, |s| {
14326            s.move_heads_with(&mut |map, head, goal| {
14327                movement::up(map, head, goal, false, text_layout_details)
14328            })
14329        })
14330    }
14331
14332    pub fn move_down(&mut self, _: &MoveDown, window: &mut Window, cx: &mut Context<Self>) {
14333        self.take_rename(true, window, cx);
14334
14335        if self.mode.is_single_line() {
14336            cx.propagate();
14337            return;
14338        }
14339
14340        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
14341
14342        let text_layout_details = &self.text_layout_details(window, cx);
14343        let selection_count = self.selections.count();
14344        let first_selection = self.selections.first_anchor();
14345
14346        self.change_selections(Default::default(), window, cx, |s| {
14347            s.move_with(&mut |map, selection| {
14348                if !selection.is_empty() {
14349                    selection.goal = SelectionGoal::None;
14350                }
14351                let (cursor, goal) = movement::down(
14352                    map,
14353                    selection.end,
14354                    selection.goal,
14355                    false,
14356                    text_layout_details,
14357                );
14358                selection.collapse_to(cursor, goal);
14359            });
14360        });
14361
14362        if selection_count == 1 && first_selection.range() == self.selections.first_anchor().range()
14363        {
14364            cx.propagate();
14365        }
14366    }
14367
14368    pub fn select_page_down(
14369        &mut self,
14370        _: &SelectPageDown,
14371        window: &mut Window,
14372        cx: &mut Context<Self>,
14373    ) {
14374        let Some(row_count) = self.visible_row_count() else {
14375            return;
14376        };
14377
14378        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
14379
14380        let text_layout_details = &self.text_layout_details(window, cx);
14381
14382        self.change_selections(Default::default(), window, cx, |s| {
14383            s.move_heads_with(&mut |map, head, goal| {
14384                movement::down_by_rows(map, head, row_count, goal, false, text_layout_details)
14385            })
14386        })
14387    }
14388
14389    pub fn move_page_down(
14390        &mut self,
14391        action: &MovePageDown,
14392        window: &mut Window,
14393        cx: &mut Context<Self>,
14394    ) {
14395        if self.take_rename(true, window, cx).is_some() {
14396            return;
14397        }
14398
14399        if self
14400            .context_menu
14401            .borrow_mut()
14402            .as_mut()
14403            .map(|menu| menu.select_last(self.completion_provider.as_deref(), window, cx))
14404            .unwrap_or(false)
14405        {
14406            return;
14407        }
14408
14409        if matches!(self.mode, EditorMode::SingleLine) {
14410            cx.propagate();
14411            return;
14412        }
14413
14414        let Some(row_count) = self.visible_row_count() else {
14415            return;
14416        };
14417
14418        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
14419
14420        let effects = if action.center_cursor {
14421            SelectionEffects::scroll(Autoscroll::center())
14422        } else {
14423            SelectionEffects::default()
14424        };
14425
14426        let text_layout_details = &self.text_layout_details(window, cx);
14427        self.change_selections(effects, window, cx, |s| {
14428            s.move_with(&mut |map, selection| {
14429                if !selection.is_empty() {
14430                    selection.goal = SelectionGoal::None;
14431                }
14432                let (cursor, goal) = movement::down_by_rows(
14433                    map,
14434                    selection.end,
14435                    row_count,
14436                    selection.goal,
14437                    false,
14438                    text_layout_details,
14439                );
14440                selection.collapse_to(cursor, goal);
14441            });
14442        });
14443    }
14444
14445    pub fn select_down(&mut self, _: &SelectDown, window: &mut Window, cx: &mut Context<Self>) {
14446        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
14447        let text_layout_details = &self.text_layout_details(window, cx);
14448        self.change_selections(Default::default(), window, cx, |s| {
14449            s.move_heads_with(&mut |map, head, goal| {
14450                movement::down(map, head, goal, false, text_layout_details)
14451            })
14452        });
14453    }
14454
14455    pub fn context_menu_first(
14456        &mut self,
14457        _: &ContextMenuFirst,
14458        window: &mut Window,
14459        cx: &mut Context<Self>,
14460    ) {
14461        if let Some(context_menu) = self.context_menu.borrow_mut().as_mut() {
14462            context_menu.select_first(self.completion_provider.as_deref(), window, cx);
14463        }
14464    }
14465
14466    pub fn context_menu_prev(
14467        &mut self,
14468        _: &ContextMenuPrevious,
14469        window: &mut Window,
14470        cx: &mut Context<Self>,
14471    ) {
14472        if let Some(context_menu) = self.context_menu.borrow_mut().as_mut() {
14473            context_menu.select_prev(self.completion_provider.as_deref(), window, cx);
14474        }
14475    }
14476
14477    pub fn context_menu_next(
14478        &mut self,
14479        _: &ContextMenuNext,
14480        window: &mut Window,
14481        cx: &mut Context<Self>,
14482    ) {
14483        if let Some(context_menu) = self.context_menu.borrow_mut().as_mut() {
14484            context_menu.select_next(self.completion_provider.as_deref(), window, cx);
14485        }
14486    }
14487
14488    pub fn context_menu_last(
14489        &mut self,
14490        _: &ContextMenuLast,
14491        window: &mut Window,
14492        cx: &mut Context<Self>,
14493    ) {
14494        if let Some(context_menu) = self.context_menu.borrow_mut().as_mut() {
14495            context_menu.select_last(self.completion_provider.as_deref(), window, cx);
14496        }
14497    }
14498
14499    pub fn signature_help_prev(
14500        &mut self,
14501        _: &SignatureHelpPrevious,
14502        _: &mut Window,
14503        cx: &mut Context<Self>,
14504    ) {
14505        if let Some(popover) = self.signature_help_state.popover_mut() {
14506            if popover.current_signature == 0 {
14507                popover.current_signature = popover.signatures.len() - 1;
14508            } else {
14509                popover.current_signature -= 1;
14510            }
14511            cx.notify();
14512        }
14513    }
14514
14515    pub fn signature_help_next(
14516        &mut self,
14517        _: &SignatureHelpNext,
14518        _: &mut Window,
14519        cx: &mut Context<Self>,
14520    ) {
14521        if let Some(popover) = self.signature_help_state.popover_mut() {
14522            if popover.current_signature + 1 == popover.signatures.len() {
14523                popover.current_signature = 0;
14524            } else {
14525                popover.current_signature += 1;
14526            }
14527            cx.notify();
14528        }
14529    }
14530
14531    pub fn move_to_previous_word_start(
14532        &mut self,
14533        _: &MoveToPreviousWordStart,
14534        window: &mut Window,
14535        cx: &mut Context<Self>,
14536    ) {
14537        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
14538        self.change_selections(Default::default(), window, cx, |s| {
14539            s.move_cursors_with(&mut |map, head, _| {
14540                (
14541                    movement::previous_word_start(map, head),
14542                    SelectionGoal::None,
14543                )
14544            });
14545        })
14546    }
14547
14548    pub fn move_to_previous_subword_start(
14549        &mut self,
14550        _: &MoveToPreviousSubwordStart,
14551        window: &mut Window,
14552        cx: &mut Context<Self>,
14553    ) {
14554        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
14555        self.change_selections(Default::default(), window, cx, |s| {
14556            s.move_cursors_with(&mut |map, head, _| {
14557                (
14558                    movement::previous_subword_start(map, head),
14559                    SelectionGoal::None,
14560                )
14561            });
14562        })
14563    }
14564
14565    pub fn select_to_previous_word_start(
14566        &mut self,
14567        _: &SelectToPreviousWordStart,
14568        window: &mut Window,
14569        cx: &mut Context<Self>,
14570    ) {
14571        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
14572        self.change_selections(Default::default(), window, cx, |s| {
14573            s.move_heads_with(&mut |map, head, _| {
14574                (
14575                    movement::previous_word_start(map, head),
14576                    SelectionGoal::None,
14577                )
14578            });
14579        })
14580    }
14581
14582    pub fn select_to_previous_subword_start(
14583        &mut self,
14584        _: &SelectToPreviousSubwordStart,
14585        window: &mut Window,
14586        cx: &mut Context<Self>,
14587    ) {
14588        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
14589        self.change_selections(Default::default(), window, cx, |s| {
14590            s.move_heads_with(&mut |map, head, _| {
14591                (
14592                    movement::previous_subword_start(map, head),
14593                    SelectionGoal::None,
14594                )
14595            });
14596        })
14597    }
14598
14599    pub fn delete_to_previous_word_start(
14600        &mut self,
14601        action: &DeleteToPreviousWordStart,
14602        window: &mut Window,
14603        cx: &mut Context<Self>,
14604    ) {
14605        self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
14606        self.transact(window, cx, |this, window, cx| {
14607            this.select_autoclose_pair(window, cx);
14608            this.change_selections(Default::default(), window, cx, |s| {
14609                s.move_with(&mut |map, selection| {
14610                    if selection.is_empty() {
14611                        let mut cursor = if action.ignore_newlines {
14612                            movement::previous_word_start(map, selection.head())
14613                        } else {
14614                            movement::previous_word_start_or_newline(map, selection.head())
14615                        };
14616                        cursor = movement::adjust_greedy_deletion(
14617                            map,
14618                            selection.head(),
14619                            cursor,
14620                            action.ignore_brackets,
14621                        );
14622                        selection.set_head(cursor, SelectionGoal::None);
14623                    }
14624                });
14625            });
14626            this.insert("", window, cx);
14627        });
14628    }
14629
14630    pub fn delete_to_previous_subword_start(
14631        &mut self,
14632        action: &DeleteToPreviousSubwordStart,
14633        window: &mut Window,
14634        cx: &mut Context<Self>,
14635    ) {
14636        self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
14637        self.transact(window, cx, |this, window, cx| {
14638            this.select_autoclose_pair(window, cx);
14639            this.change_selections(Default::default(), window, cx, |s| {
14640                s.move_with(&mut |map, selection| {
14641                    if selection.is_empty() {
14642                        let mut cursor = if action.ignore_newlines {
14643                            movement::previous_subword_start(map, selection.head())
14644                        } else {
14645                            movement::previous_subword_start_or_newline(map, selection.head())
14646                        };
14647                        cursor = movement::adjust_greedy_deletion(
14648                            map,
14649                            selection.head(),
14650                            cursor,
14651                            action.ignore_brackets,
14652                        );
14653                        selection.set_head(cursor, SelectionGoal::None);
14654                    }
14655                });
14656            });
14657            this.insert("", window, cx);
14658        });
14659    }
14660
14661    pub fn move_to_next_word_end(
14662        &mut self,
14663        _: &MoveToNextWordEnd,
14664        window: &mut Window,
14665        cx: &mut Context<Self>,
14666    ) {
14667        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
14668        self.change_selections(Default::default(), window, cx, |s| {
14669            s.move_cursors_with(&mut |map, head, _| {
14670                (movement::next_word_end(map, head), SelectionGoal::None)
14671            });
14672        })
14673    }
14674
14675    pub fn move_to_next_subword_end(
14676        &mut self,
14677        _: &MoveToNextSubwordEnd,
14678        window: &mut Window,
14679        cx: &mut Context<Self>,
14680    ) {
14681        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
14682        self.change_selections(Default::default(), window, cx, |s| {
14683            s.move_cursors_with(&mut |map, head, _| {
14684                (movement::next_subword_end(map, head), SelectionGoal::None)
14685            });
14686        })
14687    }
14688
14689    pub fn select_to_next_word_end(
14690        &mut self,
14691        _: &SelectToNextWordEnd,
14692        window: &mut Window,
14693        cx: &mut Context<Self>,
14694    ) {
14695        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
14696        self.change_selections(Default::default(), window, cx, |s| {
14697            s.move_heads_with(&mut |map, head, _| {
14698                (movement::next_word_end(map, head), SelectionGoal::None)
14699            });
14700        })
14701    }
14702
14703    pub fn select_to_next_subword_end(
14704        &mut self,
14705        _: &SelectToNextSubwordEnd,
14706        window: &mut Window,
14707        cx: &mut Context<Self>,
14708    ) {
14709        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
14710        self.change_selections(Default::default(), window, cx, |s| {
14711            s.move_heads_with(&mut |map, head, _| {
14712                (movement::next_subword_end(map, head), SelectionGoal::None)
14713            });
14714        })
14715    }
14716
14717    pub fn delete_to_next_word_end(
14718        &mut self,
14719        action: &DeleteToNextWordEnd,
14720        window: &mut Window,
14721        cx: &mut Context<Self>,
14722    ) {
14723        self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
14724        self.transact(window, cx, |this, window, cx| {
14725            this.change_selections(Default::default(), window, cx, |s| {
14726                s.move_with(&mut |map, selection| {
14727                    if selection.is_empty() {
14728                        let mut cursor = if action.ignore_newlines {
14729                            movement::next_word_end(map, selection.head())
14730                        } else {
14731                            movement::next_word_end_or_newline(map, selection.head())
14732                        };
14733                        cursor = movement::adjust_greedy_deletion(
14734                            map,
14735                            selection.head(),
14736                            cursor,
14737                            action.ignore_brackets,
14738                        );
14739                        selection.set_head(cursor, SelectionGoal::None);
14740                    }
14741                });
14742            });
14743            this.insert("", window, cx);
14744        });
14745    }
14746
14747    pub fn delete_to_next_subword_end(
14748        &mut self,
14749        action: &DeleteToNextSubwordEnd,
14750        window: &mut Window,
14751        cx: &mut Context<Self>,
14752    ) {
14753        self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
14754        self.transact(window, cx, |this, window, cx| {
14755            this.change_selections(Default::default(), window, cx, |s| {
14756                s.move_with(&mut |map, selection| {
14757                    if selection.is_empty() {
14758                        let mut cursor = if action.ignore_newlines {
14759                            movement::next_subword_end(map, selection.head())
14760                        } else {
14761                            movement::next_subword_end_or_newline(map, selection.head())
14762                        };
14763                        cursor = movement::adjust_greedy_deletion(
14764                            map,
14765                            selection.head(),
14766                            cursor,
14767                            action.ignore_brackets,
14768                        );
14769                        selection.set_head(cursor, SelectionGoal::None);
14770                    }
14771                });
14772            });
14773            this.insert("", window, cx);
14774        });
14775    }
14776
14777    pub fn move_to_beginning_of_line(
14778        &mut self,
14779        action: &MoveToBeginningOfLine,
14780        window: &mut Window,
14781        cx: &mut Context<Self>,
14782    ) {
14783        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
14784        self.change_selections(Default::default(), window, cx, |s| {
14785            s.move_cursors_with(&mut |map, head, _| {
14786                (
14787                    movement::indented_line_beginning(
14788                        map,
14789                        head,
14790                        action.stop_at_soft_wraps,
14791                        action.stop_at_indent,
14792                    ),
14793                    SelectionGoal::None,
14794                )
14795            });
14796        })
14797    }
14798
14799    pub fn select_to_beginning_of_line(
14800        &mut self,
14801        action: &SelectToBeginningOfLine,
14802        window: &mut Window,
14803        cx: &mut Context<Self>,
14804    ) {
14805        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
14806        self.change_selections(Default::default(), window, cx, |s| {
14807            s.move_heads_with(&mut |map, head, _| {
14808                (
14809                    movement::indented_line_beginning(
14810                        map,
14811                        head,
14812                        action.stop_at_soft_wraps,
14813                        action.stop_at_indent,
14814                    ),
14815                    SelectionGoal::None,
14816                )
14817            });
14818        });
14819    }
14820
14821    pub fn delete_to_beginning_of_line(
14822        &mut self,
14823        action: &DeleteToBeginningOfLine,
14824        window: &mut Window,
14825        cx: &mut Context<Self>,
14826    ) {
14827        self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
14828        self.transact(window, cx, |this, window, cx| {
14829            this.change_selections(Default::default(), window, cx, |s| {
14830                s.move_with(&mut |_, selection| {
14831                    selection.reversed = true;
14832                });
14833            });
14834
14835            this.select_to_beginning_of_line(
14836                &SelectToBeginningOfLine {
14837                    stop_at_soft_wraps: false,
14838                    stop_at_indent: action.stop_at_indent,
14839                },
14840                window,
14841                cx,
14842            );
14843            this.backspace(&Backspace, window, cx);
14844        });
14845    }
14846
14847    pub fn move_to_end_of_line(
14848        &mut self,
14849        action: &MoveToEndOfLine,
14850        window: &mut Window,
14851        cx: &mut Context<Self>,
14852    ) {
14853        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
14854        self.change_selections(Default::default(), window, cx, |s| {
14855            s.move_cursors_with(&mut |map, head, _| {
14856                (
14857                    movement::line_end(map, head, action.stop_at_soft_wraps),
14858                    SelectionGoal::None,
14859                )
14860            });
14861        })
14862    }
14863
14864    pub fn select_to_end_of_line(
14865        &mut self,
14866        action: &SelectToEndOfLine,
14867        window: &mut Window,
14868        cx: &mut Context<Self>,
14869    ) {
14870        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
14871        self.change_selections(Default::default(), window, cx, |s| {
14872            s.move_heads_with(&mut |map, head, _| {
14873                (
14874                    movement::line_end(map, head, action.stop_at_soft_wraps),
14875                    SelectionGoal::None,
14876                )
14877            });
14878        })
14879    }
14880
14881    pub fn delete_to_end_of_line(
14882        &mut self,
14883        _: &DeleteToEndOfLine,
14884        window: &mut Window,
14885        cx: &mut Context<Self>,
14886    ) {
14887        self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
14888        self.transact(window, cx, |this, window, cx| {
14889            this.select_to_end_of_line(
14890                &SelectToEndOfLine {
14891                    stop_at_soft_wraps: false,
14892                },
14893                window,
14894                cx,
14895            );
14896            this.delete(&Delete, window, cx);
14897        });
14898    }
14899
14900    pub fn cut_to_end_of_line(
14901        &mut self,
14902        action: &CutToEndOfLine,
14903        window: &mut Window,
14904        cx: &mut Context<Self>,
14905    ) {
14906        self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
14907        self.transact(window, cx, |this, window, cx| {
14908            this.select_to_end_of_line(
14909                &SelectToEndOfLine {
14910                    stop_at_soft_wraps: false,
14911                },
14912                window,
14913                cx,
14914            );
14915            if !action.stop_at_newlines {
14916                this.change_selections(Default::default(), window, cx, |s| {
14917                    s.move_with(&mut |_, sel| {
14918                        if sel.is_empty() {
14919                            sel.end = DisplayPoint::new(sel.end.row() + 1_u32, 0);
14920                        }
14921                    });
14922                });
14923            }
14924            this.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
14925            let item = this.cut_common(false, window, cx);
14926            cx.write_to_clipboard(item);
14927        });
14928    }
14929
14930    pub fn move_to_start_of_paragraph(
14931        &mut self,
14932        _: &MoveToStartOfParagraph,
14933        window: &mut Window,
14934        cx: &mut Context<Self>,
14935    ) {
14936        if matches!(self.mode, EditorMode::SingleLine) {
14937            cx.propagate();
14938            return;
14939        }
14940        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
14941        self.change_selections(Default::default(), window, cx, |s| {
14942            s.move_with(&mut |map, selection| {
14943                selection.collapse_to(
14944                    movement::start_of_paragraph(map, selection.head(), 1),
14945                    SelectionGoal::None,
14946                )
14947            });
14948        })
14949    }
14950
14951    pub fn move_to_end_of_paragraph(
14952        &mut self,
14953        _: &MoveToEndOfParagraph,
14954        window: &mut Window,
14955        cx: &mut Context<Self>,
14956    ) {
14957        if matches!(self.mode, EditorMode::SingleLine) {
14958            cx.propagate();
14959            return;
14960        }
14961        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
14962        self.change_selections(Default::default(), window, cx, |s| {
14963            s.move_with(&mut |map, selection| {
14964                selection.collapse_to(
14965                    movement::end_of_paragraph(map, selection.head(), 1),
14966                    SelectionGoal::None,
14967                )
14968            });
14969        })
14970    }
14971
14972    pub fn select_to_start_of_paragraph(
14973        &mut self,
14974        _: &SelectToStartOfParagraph,
14975        window: &mut Window,
14976        cx: &mut Context<Self>,
14977    ) {
14978        if matches!(self.mode, EditorMode::SingleLine) {
14979            cx.propagate();
14980            return;
14981        }
14982        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
14983        self.change_selections(Default::default(), window, cx, |s| {
14984            s.move_heads_with(&mut |map, head, _| {
14985                (
14986                    movement::start_of_paragraph(map, head, 1),
14987                    SelectionGoal::None,
14988                )
14989            });
14990        })
14991    }
14992
14993    pub fn select_to_end_of_paragraph(
14994        &mut self,
14995        _: &SelectToEndOfParagraph,
14996        window: &mut Window,
14997        cx: &mut Context<Self>,
14998    ) {
14999        if matches!(self.mode, EditorMode::SingleLine) {
15000            cx.propagate();
15001            return;
15002        }
15003        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
15004        self.change_selections(Default::default(), window, cx, |s| {
15005            s.move_heads_with(&mut |map, head, _| {
15006                (
15007                    movement::end_of_paragraph(map, head, 1),
15008                    SelectionGoal::None,
15009                )
15010            });
15011        })
15012    }
15013
15014    pub fn move_to_start_of_excerpt(
15015        &mut self,
15016        _: &MoveToStartOfExcerpt,
15017        window: &mut Window,
15018        cx: &mut Context<Self>,
15019    ) {
15020        if matches!(self.mode, EditorMode::SingleLine) {
15021            cx.propagate();
15022            return;
15023        }
15024        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
15025        self.change_selections(Default::default(), window, cx, |s| {
15026            s.move_with(&mut |map, selection| {
15027                selection.collapse_to(
15028                    movement::start_of_excerpt(
15029                        map,
15030                        selection.head(),
15031                        workspace::searchable::Direction::Prev,
15032                    ),
15033                    SelectionGoal::None,
15034                )
15035            });
15036        })
15037    }
15038
15039    pub fn move_to_start_of_next_excerpt(
15040        &mut self,
15041        _: &MoveToStartOfNextExcerpt,
15042        window: &mut Window,
15043        cx: &mut Context<Self>,
15044    ) {
15045        if matches!(self.mode, EditorMode::SingleLine) {
15046            cx.propagate();
15047            return;
15048        }
15049
15050        self.change_selections(Default::default(), window, cx, |s| {
15051            s.move_with(&mut |map, selection| {
15052                selection.collapse_to(
15053                    movement::start_of_excerpt(
15054                        map,
15055                        selection.head(),
15056                        workspace::searchable::Direction::Next,
15057                    ),
15058                    SelectionGoal::None,
15059                )
15060            });
15061        })
15062    }
15063
15064    pub fn move_to_end_of_excerpt(
15065        &mut self,
15066        _: &MoveToEndOfExcerpt,
15067        window: &mut Window,
15068        cx: &mut Context<Self>,
15069    ) {
15070        if matches!(self.mode, EditorMode::SingleLine) {
15071            cx.propagate();
15072            return;
15073        }
15074        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
15075        self.change_selections(Default::default(), window, cx, |s| {
15076            s.move_with(&mut |map, selection| {
15077                selection.collapse_to(
15078                    movement::end_of_excerpt(
15079                        map,
15080                        selection.head(),
15081                        workspace::searchable::Direction::Next,
15082                    ),
15083                    SelectionGoal::None,
15084                )
15085            });
15086        })
15087    }
15088
15089    pub fn move_to_end_of_previous_excerpt(
15090        &mut self,
15091        _: &MoveToEndOfPreviousExcerpt,
15092        window: &mut Window,
15093        cx: &mut Context<Self>,
15094    ) {
15095        if matches!(self.mode, EditorMode::SingleLine) {
15096            cx.propagate();
15097            return;
15098        }
15099        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
15100        self.change_selections(Default::default(), window, cx, |s| {
15101            s.move_with(&mut |map, selection| {
15102                selection.collapse_to(
15103                    movement::end_of_excerpt(
15104                        map,
15105                        selection.head(),
15106                        workspace::searchable::Direction::Prev,
15107                    ),
15108                    SelectionGoal::None,
15109                )
15110            });
15111        })
15112    }
15113
15114    pub fn select_to_start_of_excerpt(
15115        &mut self,
15116        _: &SelectToStartOfExcerpt,
15117        window: &mut Window,
15118        cx: &mut Context<Self>,
15119    ) {
15120        if matches!(self.mode, EditorMode::SingleLine) {
15121            cx.propagate();
15122            return;
15123        }
15124        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
15125        self.change_selections(Default::default(), window, cx, |s| {
15126            s.move_heads_with(&mut |map, head, _| {
15127                (
15128                    movement::start_of_excerpt(map, head, workspace::searchable::Direction::Prev),
15129                    SelectionGoal::None,
15130                )
15131            });
15132        })
15133    }
15134
15135    pub fn select_to_start_of_next_excerpt(
15136        &mut self,
15137        _: &SelectToStartOfNextExcerpt,
15138        window: &mut Window,
15139        cx: &mut Context<Self>,
15140    ) {
15141        if matches!(self.mode, EditorMode::SingleLine) {
15142            cx.propagate();
15143            return;
15144        }
15145        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
15146        self.change_selections(Default::default(), window, cx, |s| {
15147            s.move_heads_with(&mut |map, head, _| {
15148                (
15149                    movement::start_of_excerpt(map, head, workspace::searchable::Direction::Next),
15150                    SelectionGoal::None,
15151                )
15152            });
15153        })
15154    }
15155
15156    pub fn select_to_end_of_excerpt(
15157        &mut self,
15158        _: &SelectToEndOfExcerpt,
15159        window: &mut Window,
15160        cx: &mut Context<Self>,
15161    ) {
15162        if matches!(self.mode, EditorMode::SingleLine) {
15163            cx.propagate();
15164            return;
15165        }
15166        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
15167        self.change_selections(Default::default(), window, cx, |s| {
15168            s.move_heads_with(&mut |map, head, _| {
15169                (
15170                    movement::end_of_excerpt(map, head, workspace::searchable::Direction::Next),
15171                    SelectionGoal::None,
15172                )
15173            });
15174        })
15175    }
15176
15177    pub fn select_to_end_of_previous_excerpt(
15178        &mut self,
15179        _: &SelectToEndOfPreviousExcerpt,
15180        window: &mut Window,
15181        cx: &mut Context<Self>,
15182    ) {
15183        if matches!(self.mode, EditorMode::SingleLine) {
15184            cx.propagate();
15185            return;
15186        }
15187        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
15188        self.change_selections(Default::default(), window, cx, |s| {
15189            s.move_heads_with(&mut |map, head, _| {
15190                (
15191                    movement::end_of_excerpt(map, head, workspace::searchable::Direction::Prev),
15192                    SelectionGoal::None,
15193                )
15194            });
15195        })
15196    }
15197
15198    pub fn move_to_beginning(
15199        &mut self,
15200        _: &MoveToBeginning,
15201        window: &mut Window,
15202        cx: &mut Context<Self>,
15203    ) {
15204        if matches!(self.mode, EditorMode::SingleLine) {
15205            cx.propagate();
15206            return;
15207        }
15208        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
15209        self.change_selections(Default::default(), window, cx, |s| {
15210            s.select_ranges(vec![Anchor::min()..Anchor::min()]);
15211        });
15212    }
15213
15214    pub fn select_to_beginning(
15215        &mut self,
15216        _: &SelectToBeginning,
15217        window: &mut Window,
15218        cx: &mut Context<Self>,
15219    ) {
15220        let mut selection = self.selections.last::<Point>(&self.display_snapshot(cx));
15221        selection.set_head(Point::zero(), SelectionGoal::None);
15222        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
15223        self.change_selections(Default::default(), window, cx, |s| {
15224            s.select(vec![selection]);
15225        });
15226    }
15227
15228    pub fn move_to_end(&mut self, _: &MoveToEnd, window: &mut Window, cx: &mut Context<Self>) {
15229        if matches!(self.mode, EditorMode::SingleLine) {
15230            cx.propagate();
15231            return;
15232        }
15233        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
15234        let cursor = self.buffer.read(cx).read(cx).len();
15235        self.change_selections(Default::default(), window, cx, |s| {
15236            s.select_ranges(vec![cursor..cursor])
15237        });
15238    }
15239
15240    pub fn set_nav_history(&mut self, nav_history: Option<ItemNavHistory>) {
15241        self.nav_history = nav_history;
15242    }
15243
15244    pub fn nav_history(&self) -> Option<&ItemNavHistory> {
15245        self.nav_history.as_ref()
15246    }
15247
15248    pub fn create_nav_history_entry(&mut self, cx: &mut Context<Self>) {
15249        self.push_to_nav_history(
15250            self.selections.newest_anchor().head(),
15251            None,
15252            false,
15253            true,
15254            cx,
15255        );
15256    }
15257
15258    fn navigation_data(&self, cursor_anchor: Anchor, cx: &mut Context<Self>) -> NavigationData {
15259        let display_snapshot = self.display_map.update(cx, |map, cx| map.snapshot(cx));
15260        let buffer = self.buffer.read(cx).read(cx);
15261        let cursor_position = cursor_anchor.to_point(&buffer);
15262        let scroll_anchor = self.scroll_manager.native_anchor(&display_snapshot, cx);
15263        let scroll_top_row = scroll_anchor.top_row(&buffer);
15264        drop(buffer);
15265
15266        NavigationData {
15267            cursor_anchor,
15268            cursor_position,
15269            scroll_anchor,
15270            scroll_top_row,
15271        }
15272    }
15273
15274    fn navigation_entry(
15275        &self,
15276        cursor_anchor: Anchor,
15277        cx: &mut Context<Self>,
15278    ) -> Option<NavigationEntry> {
15279        let Some(history) = self.nav_history.clone() else {
15280            return None;
15281        };
15282        let data = self.navigation_data(cursor_anchor, cx);
15283        Some(history.navigation_entry(Some(Arc::new(data) as Arc<dyn Any + Send + Sync>)))
15284    }
15285
15286    fn push_to_nav_history(
15287        &mut self,
15288        cursor_anchor: Anchor,
15289        new_position: Option<Point>,
15290        is_deactivate: bool,
15291        always: bool,
15292        cx: &mut Context<Self>,
15293    ) {
15294        let data = self.navigation_data(cursor_anchor, cx);
15295        if let Some(nav_history) = self.nav_history.as_mut() {
15296            if let Some(new_position) = new_position {
15297                let row_delta = (new_position.row as i64 - data.cursor_position.row as i64).abs();
15298                if row_delta == 0 || (row_delta < MIN_NAVIGATION_HISTORY_ROW_DELTA && !always) {
15299                    return;
15300                }
15301            }
15302
15303            nav_history.push(Some(data), cx);
15304            cx.emit(EditorEvent::PushedToNavHistory {
15305                anchor: cursor_anchor,
15306                is_deactivate,
15307            })
15308        }
15309    }
15310
15311    pub fn select_to_end(&mut self, _: &SelectToEnd, window: &mut Window, cx: &mut Context<Self>) {
15312        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
15313        let buffer = self.buffer.read(cx).snapshot(cx);
15314        let mut selection = self
15315            .selections
15316            .first::<MultiBufferOffset>(&self.display_snapshot(cx));
15317        selection.set_head(buffer.len(), SelectionGoal::None);
15318        self.change_selections(Default::default(), window, cx, |s| {
15319            s.select(vec![selection]);
15320        });
15321    }
15322
15323    pub fn select_all(&mut self, _: &SelectAll, window: &mut Window, cx: &mut Context<Self>) {
15324        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
15325        self.change_selections(SelectionEffects::no_scroll(), window, cx, |s| {
15326            s.select_ranges(vec![Anchor::min()..Anchor::max()]);
15327        });
15328    }
15329
15330    pub fn select_line(&mut self, _: &SelectLine, window: &mut Window, cx: &mut Context<Self>) {
15331        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
15332        let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
15333        let mut selections = self.selections.all::<Point>(&display_map);
15334        let max_point = display_map.buffer_snapshot().max_point();
15335        for selection in &mut selections {
15336            let rows = selection.spanned_rows(true, &display_map);
15337            selection.start = Point::new(rows.start.0, 0);
15338            selection.end = cmp::min(max_point, Point::new(rows.end.0, 0));
15339            selection.reversed = false;
15340        }
15341        self.change_selections(Default::default(), window, cx, |s| {
15342            s.select(selections);
15343        });
15344    }
15345
15346    pub fn split_selection_into_lines(
15347        &mut self,
15348        action: &SplitSelectionIntoLines,
15349        window: &mut Window,
15350        cx: &mut Context<Self>,
15351    ) {
15352        let selections = self
15353            .selections
15354            .all::<Point>(&self.display_snapshot(cx))
15355            .into_iter()
15356            .map(|selection| selection.start..selection.end)
15357            .collect::<Vec<_>>();
15358        self.unfold_ranges(&selections, true, true, cx);
15359
15360        let mut new_selection_ranges = Vec::new();
15361        {
15362            let buffer = self.buffer.read(cx).read(cx);
15363            for selection in selections {
15364                for row in selection.start.row..selection.end.row {
15365                    let line_start = Point::new(row, 0);
15366                    let line_end = Point::new(row, buffer.line_len(MultiBufferRow(row)));
15367
15368                    if action.keep_selections {
15369                        // Keep the selection range for each line
15370                        let selection_start = if row == selection.start.row {
15371                            selection.start
15372                        } else {
15373                            line_start
15374                        };
15375                        new_selection_ranges.push(selection_start..line_end);
15376                    } else {
15377                        // Collapse to cursor at end of line
15378                        new_selection_ranges.push(line_end..line_end);
15379                    }
15380                }
15381
15382                let is_multiline_selection = selection.start.row != selection.end.row;
15383                // Don't insert last one if it's a multi-line selection ending at the start of a line,
15384                // so this action feels more ergonomic when paired with other selection operations
15385                let should_skip_last = is_multiline_selection && selection.end.column == 0;
15386                if !should_skip_last {
15387                    if action.keep_selections {
15388                        if is_multiline_selection {
15389                            let line_start = Point::new(selection.end.row, 0);
15390                            new_selection_ranges.push(line_start..selection.end);
15391                        } else {
15392                            new_selection_ranges.push(selection.start..selection.end);
15393                        }
15394                    } else {
15395                        new_selection_ranges.push(selection.end..selection.end);
15396                    }
15397                }
15398            }
15399        }
15400        self.change_selections(Default::default(), window, cx, |s| {
15401            s.select_ranges(new_selection_ranges);
15402        });
15403    }
15404
15405    pub fn add_selection_above(
15406        &mut self,
15407        action: &AddSelectionAbove,
15408        window: &mut Window,
15409        cx: &mut Context<Self>,
15410    ) {
15411        self.add_selection(true, action.skip_soft_wrap, window, cx);
15412    }
15413
15414    pub fn add_selection_below(
15415        &mut self,
15416        action: &AddSelectionBelow,
15417        window: &mut Window,
15418        cx: &mut Context<Self>,
15419    ) {
15420        self.add_selection(false, action.skip_soft_wrap, window, cx);
15421    }
15422
15423    fn add_selection(
15424        &mut self,
15425        above: bool,
15426        skip_soft_wrap: bool,
15427        window: &mut Window,
15428        cx: &mut Context<Self>,
15429    ) {
15430        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
15431
15432        let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
15433        let all_selections = self.selections.all::<Point>(&display_map);
15434        let text_layout_details = self.text_layout_details(window, cx);
15435
15436        let (mut columnar_selections, new_selections_to_columnarize) = {
15437            if let Some(state) = self.add_selections_state.as_ref() {
15438                let columnar_selection_ids: HashSet<_> = state
15439                    .groups
15440                    .iter()
15441                    .flat_map(|group| group.stack.iter())
15442                    .copied()
15443                    .collect();
15444
15445                all_selections
15446                    .into_iter()
15447                    .partition(|s| columnar_selection_ids.contains(&s.id))
15448            } else {
15449                (Vec::new(), all_selections)
15450            }
15451        };
15452
15453        let mut state = self
15454            .add_selections_state
15455            .take()
15456            .unwrap_or_else(|| AddSelectionsState { groups: Vec::new() });
15457
15458        for selection in new_selections_to_columnarize {
15459            let range = selection.display_range(&display_map).sorted();
15460            let start_x = display_map.x_for_display_point(range.start, &text_layout_details);
15461            let end_x = display_map.x_for_display_point(range.end, &text_layout_details);
15462            let positions = start_x.min(end_x)..start_x.max(end_x);
15463            let mut stack = Vec::new();
15464            for row in range.start.row().0..=range.end.row().0 {
15465                if let Some(selection) = self.selections.build_columnar_selection(
15466                    &display_map,
15467                    DisplayRow(row),
15468                    &positions,
15469                    selection.reversed,
15470                    &text_layout_details,
15471                ) {
15472                    stack.push(selection.id);
15473                    columnar_selections.push(selection);
15474                }
15475            }
15476            if !stack.is_empty() {
15477                if above {
15478                    stack.reverse();
15479                }
15480                state.groups.push(AddSelectionsGroup { above, stack });
15481            }
15482        }
15483
15484        let mut final_selections = Vec::new();
15485        let end_row = if above {
15486            DisplayRow(0)
15487        } else {
15488            display_map.max_point().row()
15489        };
15490
15491        // When `skip_soft_wrap` is true, we use buffer columns instead of pixel
15492        // positions to place new selections, so we need to keep track of the
15493        // column range of the oldest selection in each group, because
15494        // intermediate selections may have been clamped to shorter lines.
15495        // selections may have been clamped to shorter lines.
15496        let mut goal_columns_by_selection_id = if skip_soft_wrap {
15497            let mut map = HashMap::default();
15498            for group in state.groups.iter() {
15499                if let Some(oldest_id) = group.stack.first() {
15500                    if let Some(oldest_selection) =
15501                        columnar_selections.iter().find(|s| s.id == *oldest_id)
15502                    {
15503                        let start_col = oldest_selection.start.column;
15504                        let end_col = oldest_selection.end.column;
15505                        let goal_columns = start_col.min(end_col)..start_col.max(end_col);
15506                        for id in &group.stack {
15507                            map.insert(*id, goal_columns.clone());
15508                        }
15509                    }
15510                }
15511            }
15512            map
15513        } else {
15514            HashMap::default()
15515        };
15516
15517        let mut last_added_item_per_group = HashMap::default();
15518        for group in state.groups.iter_mut() {
15519            if let Some(last_id) = group.stack.last() {
15520                last_added_item_per_group.insert(*last_id, group);
15521            }
15522        }
15523
15524        for selection in columnar_selections {
15525            if let Some(group) = last_added_item_per_group.get_mut(&selection.id) {
15526                if above == group.above {
15527                    let range = selection.display_range(&display_map).sorted();
15528                    debug_assert_eq!(range.start.row(), range.end.row());
15529                    let row = range.start.row();
15530                    let positions =
15531                        if let SelectionGoal::HorizontalRange { start, end } = selection.goal {
15532                            Pixels::from(start)..Pixels::from(end)
15533                        } else {
15534                            let start_x =
15535                                display_map.x_for_display_point(range.start, &text_layout_details);
15536                            let end_x =
15537                                display_map.x_for_display_point(range.end, &text_layout_details);
15538                            start_x.min(end_x)..start_x.max(end_x)
15539                        };
15540
15541                    let maybe_new_selection = if skip_soft_wrap {
15542                        let goal_columns = goal_columns_by_selection_id
15543                            .remove(&selection.id)
15544                            .unwrap_or_else(|| {
15545                                let start_col = selection.start.column;
15546                                let end_col = selection.end.column;
15547                                start_col.min(end_col)..start_col.max(end_col)
15548                            });
15549                        self.selections.find_next_columnar_selection_by_buffer_row(
15550                            &display_map,
15551                            row,
15552                            end_row,
15553                            above,
15554                            &goal_columns,
15555                            selection.reversed,
15556                            &text_layout_details,
15557                        )
15558                    } else {
15559                        self.selections.find_next_columnar_selection_by_display_row(
15560                            &display_map,
15561                            row,
15562                            end_row,
15563                            above,
15564                            &positions,
15565                            selection.reversed,
15566                            &text_layout_details,
15567                        )
15568                    };
15569
15570                    if let Some(new_selection) = maybe_new_selection {
15571                        group.stack.push(new_selection.id);
15572                        if above {
15573                            final_selections.push(new_selection);
15574                            final_selections.push(selection);
15575                        } else {
15576                            final_selections.push(selection);
15577                            final_selections.push(new_selection);
15578                        }
15579                    } else {
15580                        final_selections.push(selection);
15581                    }
15582                } else {
15583                    group.stack.pop();
15584                }
15585            } else {
15586                final_selections.push(selection);
15587            }
15588        }
15589
15590        self.change_selections(Default::default(), window, cx, |s| {
15591            s.select(final_selections);
15592        });
15593
15594        let final_selection_ids: HashSet<_> = self
15595            .selections
15596            .all::<Point>(&display_map)
15597            .iter()
15598            .map(|s| s.id)
15599            .collect();
15600        state.groups.retain_mut(|group| {
15601            // selections might get merged above so we remove invalid items from stacks
15602            group.stack.retain(|id| final_selection_ids.contains(id));
15603
15604            // single selection in stack can be treated as initial state
15605            group.stack.len() > 1
15606        });
15607
15608        if !state.groups.is_empty() {
15609            self.add_selections_state = Some(state);
15610        }
15611    }
15612
15613    pub fn insert_snippet_at_selections(
15614        &mut self,
15615        action: &InsertSnippet,
15616        window: &mut Window,
15617        cx: &mut Context<Self>,
15618    ) {
15619        self.try_insert_snippet_at_selections(action, window, cx)
15620            .log_err();
15621    }
15622
15623    fn try_insert_snippet_at_selections(
15624        &mut self,
15625        action: &InsertSnippet,
15626        window: &mut Window,
15627        cx: &mut Context<Self>,
15628    ) -> Result<()> {
15629        let insertion_ranges = self
15630            .selections
15631            .all::<MultiBufferOffset>(&self.display_snapshot(cx))
15632            .into_iter()
15633            .map(|selection| selection.range())
15634            .collect_vec();
15635
15636        let snippet = if let Some(snippet_body) = &action.snippet {
15637            if action.language.is_none() && action.name.is_none() {
15638                Snippet::parse(snippet_body)?
15639            } else {
15640                bail!("`snippet` is mutually exclusive with `language` and `name`")
15641            }
15642        } else if let Some(name) = &action.name {
15643            let project = self.project().context("no project")?;
15644            let snippet_store = project.read(cx).snippets().read(cx);
15645            let snippet = snippet_store
15646                .snippets_for(action.language.clone(), cx)
15647                .into_iter()
15648                .find(|snippet| snippet.name == *name)
15649                .context("snippet not found")?;
15650            Snippet::parse(&snippet.body)?
15651        } else {
15652            // todo(andrew): open modal to select snippet
15653            bail!("`name` or `snippet` is required")
15654        };
15655
15656        self.insert_snippet(&insertion_ranges, snippet, window, cx)
15657    }
15658
15659    fn select_match_ranges(
15660        &mut self,
15661        range: Range<MultiBufferOffset>,
15662        reversed: bool,
15663        replace_newest: bool,
15664        auto_scroll: Option<Autoscroll>,
15665        window: &mut Window,
15666        cx: &mut Context<Editor>,
15667    ) {
15668        self.unfold_ranges(
15669            std::slice::from_ref(&range),
15670            false,
15671            auto_scroll.is_some(),
15672            cx,
15673        );
15674        let effects = if let Some(scroll) = auto_scroll {
15675            SelectionEffects::scroll(scroll)
15676        } else {
15677            SelectionEffects::no_scroll()
15678        };
15679        self.change_selections(effects, window, cx, |s| {
15680            if replace_newest {
15681                s.delete(s.newest_anchor().id);
15682            }
15683            if reversed {
15684                s.insert_range(range.end..range.start);
15685            } else {
15686                s.insert_range(range);
15687            }
15688        });
15689    }
15690
15691    pub fn select_next_match_internal(
15692        &mut self,
15693        display_map: &DisplaySnapshot,
15694        replace_newest: bool,
15695        autoscroll: Option<Autoscroll>,
15696        window: &mut Window,
15697        cx: &mut Context<Self>,
15698    ) -> Result<()> {
15699        let buffer = display_map.buffer_snapshot();
15700        let mut selections = self.selections.all::<MultiBufferOffset>(&display_map);
15701        if let Some(mut select_next_state) = self.select_next_state.take() {
15702            let query = &select_next_state.query;
15703            if !select_next_state.done {
15704                let first_selection = selections.iter().min_by_key(|s| s.id).unwrap();
15705                let last_selection = selections.iter().max_by_key(|s| s.id).unwrap();
15706                let mut next_selected_range = None;
15707
15708                let bytes_after_last_selection =
15709                    buffer.bytes_in_range(last_selection.end..buffer.len());
15710                let bytes_before_first_selection =
15711                    buffer.bytes_in_range(MultiBufferOffset(0)..first_selection.start);
15712                let query_matches = query
15713                    .stream_find_iter(bytes_after_last_selection)
15714                    .map(|result| (last_selection.end, result))
15715                    .chain(
15716                        query
15717                            .stream_find_iter(bytes_before_first_selection)
15718                            .map(|result| (MultiBufferOffset(0), result)),
15719                    );
15720
15721                for (start_offset, query_match) in query_matches {
15722                    let query_match = query_match.unwrap(); // can only fail due to I/O
15723                    let offset_range =
15724                        start_offset + query_match.start()..start_offset + query_match.end();
15725
15726                    if !select_next_state.wordwise
15727                        || (!buffer.is_inside_word(offset_range.start, None)
15728                            && !buffer.is_inside_word(offset_range.end, None))
15729                    {
15730                        let idx = selections
15731                            .partition_point(|selection| selection.end <= offset_range.start);
15732                        let overlaps = selections
15733                            .get(idx)
15734                            .map_or(false, |selection| selection.start < offset_range.end);
15735
15736                        if !overlaps {
15737                            next_selected_range = Some(offset_range);
15738                            break;
15739                        }
15740                    }
15741                }
15742
15743                if let Some(next_selected_range) = next_selected_range {
15744                    self.select_match_ranges(
15745                        next_selected_range,
15746                        last_selection.reversed,
15747                        replace_newest,
15748                        autoscroll,
15749                        window,
15750                        cx,
15751                    );
15752                } else {
15753                    select_next_state.done = true;
15754                }
15755            }
15756
15757            self.select_next_state = Some(select_next_state);
15758        } else {
15759            let mut only_carets = true;
15760            let mut same_text_selected = true;
15761            let mut selected_text = None;
15762
15763            let mut selections_iter = selections.iter().peekable();
15764            while let Some(selection) = selections_iter.next() {
15765                if selection.start != selection.end {
15766                    only_carets = false;
15767                }
15768
15769                if same_text_selected {
15770                    if selected_text.is_none() {
15771                        selected_text =
15772                            Some(buffer.text_for_range(selection.range()).collect::<String>());
15773                    }
15774
15775                    if let Some(next_selection) = selections_iter.peek() {
15776                        if next_selection.len() == selection.len() {
15777                            let next_selected_text = buffer
15778                                .text_for_range(next_selection.range())
15779                                .collect::<String>();
15780                            if Some(next_selected_text) != selected_text {
15781                                same_text_selected = false;
15782                                selected_text = None;
15783                            }
15784                        } else {
15785                            same_text_selected = false;
15786                            selected_text = None;
15787                        }
15788                    }
15789                }
15790            }
15791
15792            if only_carets {
15793                for selection in &mut selections {
15794                    let (word_range, _) = buffer.surrounding_word(selection.start, None);
15795                    selection.start = word_range.start;
15796                    selection.end = word_range.end;
15797                    selection.goal = SelectionGoal::None;
15798                    selection.reversed = false;
15799                    self.select_match_ranges(
15800                        selection.start..selection.end,
15801                        selection.reversed,
15802                        replace_newest,
15803                        autoscroll,
15804                        window,
15805                        cx,
15806                    );
15807                }
15808
15809                if selections.len() == 1 {
15810                    let selection = selections
15811                        .last()
15812                        .expect("ensured that there's only one selection");
15813                    let query = buffer
15814                        .text_for_range(selection.start..selection.end)
15815                        .collect::<String>();
15816                    let is_empty = query.is_empty();
15817                    let select_state = SelectNextState {
15818                        query: self.build_query(&[query], cx)?,
15819                        wordwise: true,
15820                        done: is_empty,
15821                    };
15822                    self.select_next_state = Some(select_state);
15823                } else {
15824                    self.select_next_state = None;
15825                }
15826            } else if let Some(selected_text) = selected_text {
15827                self.select_next_state = Some(SelectNextState {
15828                    query: self.build_query(&[selected_text], cx)?,
15829                    wordwise: false,
15830                    done: false,
15831                });
15832                self.select_next_match_internal(
15833                    display_map,
15834                    replace_newest,
15835                    autoscroll,
15836                    window,
15837                    cx,
15838                )?;
15839            }
15840        }
15841        Ok(())
15842    }
15843
15844    pub fn select_all_matches(
15845        &mut self,
15846        _action: &SelectAllMatches,
15847        window: &mut Window,
15848        cx: &mut Context<Self>,
15849    ) -> Result<()> {
15850        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
15851
15852        let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
15853
15854        self.select_next_match_internal(&display_map, false, None, window, cx)?;
15855        let Some(select_next_state) = self.select_next_state.as_mut().filter(|state| !state.done)
15856        else {
15857            return Ok(());
15858        };
15859
15860        let mut new_selections = Vec::new();
15861
15862        let reversed = self
15863            .selections
15864            .oldest::<MultiBufferOffset>(&display_map)
15865            .reversed;
15866        let buffer = display_map.buffer_snapshot();
15867        let query_matches = select_next_state
15868            .query
15869            .stream_find_iter(buffer.bytes_in_range(MultiBufferOffset(0)..buffer.len()));
15870
15871        for query_match in query_matches.into_iter() {
15872            let query_match = query_match.context("query match for select all action")?; // can only fail due to I/O
15873            let offset_range = if reversed {
15874                MultiBufferOffset(query_match.end())..MultiBufferOffset(query_match.start())
15875            } else {
15876                MultiBufferOffset(query_match.start())..MultiBufferOffset(query_match.end())
15877            };
15878
15879            if !select_next_state.wordwise
15880                || (!buffer.is_inside_word(offset_range.start, None)
15881                    && !buffer.is_inside_word(offset_range.end, None))
15882            {
15883                new_selections.push(offset_range.start..offset_range.end);
15884            }
15885        }
15886
15887        select_next_state.done = true;
15888
15889        if new_selections.is_empty() {
15890            log::error!("bug: new_selections is empty in select_all_matches");
15891            return Ok(());
15892        }
15893
15894        self.unfold_ranges(&new_selections, false, false, cx);
15895        self.change_selections(SelectionEffects::no_scroll(), window, cx, |selections| {
15896            selections.select_ranges(new_selections)
15897        });
15898
15899        Ok(())
15900    }
15901
15902    pub fn select_next(
15903        &mut self,
15904        action: &SelectNext,
15905        window: &mut Window,
15906        cx: &mut Context<Self>,
15907    ) -> Result<()> {
15908        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
15909        let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
15910        self.select_next_match_internal(
15911            &display_map,
15912            action.replace_newest,
15913            Some(Autoscroll::newest()),
15914            window,
15915            cx,
15916        )
15917    }
15918
15919    pub fn select_previous(
15920        &mut self,
15921        action: &SelectPrevious,
15922        window: &mut Window,
15923        cx: &mut Context<Self>,
15924    ) -> Result<()> {
15925        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
15926        let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
15927        let buffer = display_map.buffer_snapshot();
15928        let mut selections = self.selections.all::<MultiBufferOffset>(&display_map);
15929        if let Some(mut select_prev_state) = self.select_prev_state.take() {
15930            let query = &select_prev_state.query;
15931            if !select_prev_state.done {
15932                let first_selection = selections.iter().min_by_key(|s| s.id).unwrap();
15933                let last_selection = selections.iter().max_by_key(|s| s.id).unwrap();
15934                let mut next_selected_range = None;
15935                // When we're iterating matches backwards, the oldest match will actually be the furthest one in the buffer.
15936                let bytes_before_last_selection =
15937                    buffer.reversed_bytes_in_range(MultiBufferOffset(0)..last_selection.start);
15938                let bytes_after_first_selection =
15939                    buffer.reversed_bytes_in_range(first_selection.end..buffer.len());
15940                let query_matches = query
15941                    .stream_find_iter(bytes_before_last_selection)
15942                    .map(|result| (last_selection.start, result))
15943                    .chain(
15944                        query
15945                            .stream_find_iter(bytes_after_first_selection)
15946                            .map(|result| (buffer.len(), result)),
15947                    );
15948                for (end_offset, query_match) in query_matches {
15949                    let query_match = query_match.unwrap(); // can only fail due to I/O
15950                    let offset_range =
15951                        end_offset - query_match.end()..end_offset - query_match.start();
15952
15953                    if !select_prev_state.wordwise
15954                        || (!buffer.is_inside_word(offset_range.start, None)
15955                            && !buffer.is_inside_word(offset_range.end, None))
15956                    {
15957                        next_selected_range = Some(offset_range);
15958                        break;
15959                    }
15960                }
15961
15962                if let Some(next_selected_range) = next_selected_range {
15963                    self.select_match_ranges(
15964                        next_selected_range,
15965                        last_selection.reversed,
15966                        action.replace_newest,
15967                        Some(Autoscroll::newest()),
15968                        window,
15969                        cx,
15970                    );
15971                } else {
15972                    select_prev_state.done = true;
15973                }
15974            }
15975
15976            self.select_prev_state = Some(select_prev_state);
15977        } else {
15978            let mut only_carets = true;
15979            let mut same_text_selected = true;
15980            let mut selected_text = None;
15981
15982            let mut selections_iter = selections.iter().peekable();
15983            while let Some(selection) = selections_iter.next() {
15984                if selection.start != selection.end {
15985                    only_carets = false;
15986                }
15987
15988                if same_text_selected {
15989                    if selected_text.is_none() {
15990                        selected_text =
15991                            Some(buffer.text_for_range(selection.range()).collect::<String>());
15992                    }
15993
15994                    if let Some(next_selection) = selections_iter.peek() {
15995                        if next_selection.len() == selection.len() {
15996                            let next_selected_text = buffer
15997                                .text_for_range(next_selection.range())
15998                                .collect::<String>();
15999                            if Some(next_selected_text) != selected_text {
16000                                same_text_selected = false;
16001                                selected_text = None;
16002                            }
16003                        } else {
16004                            same_text_selected = false;
16005                            selected_text = None;
16006                        }
16007                    }
16008                }
16009            }
16010
16011            if only_carets {
16012                for selection in &mut selections {
16013                    let (word_range, _) = buffer.surrounding_word(selection.start, None);
16014                    selection.start = word_range.start;
16015                    selection.end = word_range.end;
16016                    selection.goal = SelectionGoal::None;
16017                    selection.reversed = false;
16018                    self.select_match_ranges(
16019                        selection.start..selection.end,
16020                        selection.reversed,
16021                        action.replace_newest,
16022                        Some(Autoscroll::newest()),
16023                        window,
16024                        cx,
16025                    );
16026                }
16027                if selections.len() == 1 {
16028                    let selection = selections
16029                        .last()
16030                        .expect("ensured that there's only one selection");
16031                    let query = buffer
16032                        .text_for_range(selection.start..selection.end)
16033                        .collect::<String>();
16034                    let is_empty = query.is_empty();
16035                    let select_state = SelectNextState {
16036                        query: self.build_query(&[query.chars().rev().collect::<String>()], cx)?,
16037                        wordwise: true,
16038                        done: is_empty,
16039                    };
16040                    self.select_prev_state = Some(select_state);
16041                } else {
16042                    self.select_prev_state = None;
16043                }
16044            } else if let Some(selected_text) = selected_text {
16045                self.select_prev_state = Some(SelectNextState {
16046                    query: self
16047                        .build_query(&[selected_text.chars().rev().collect::<String>()], cx)?,
16048                    wordwise: false,
16049                    done: false,
16050                });
16051                self.select_previous(action, window, cx)?;
16052            }
16053        }
16054        Ok(())
16055    }
16056
16057    /// Builds an `AhoCorasick` automaton from the provided patterns, while
16058    /// setting the case sensitivity based on the global
16059    /// `SelectNextCaseSensitive` setting, if set, otherwise based on the
16060    /// editor's settings.
16061    fn build_query<I, P>(&self, patterns: I, cx: &Context<Self>) -> Result<AhoCorasick, BuildError>
16062    where
16063        I: IntoIterator<Item = P>,
16064        P: AsRef<[u8]>,
16065    {
16066        let case_sensitive = self
16067            .select_next_is_case_sensitive
16068            .unwrap_or_else(|| EditorSettings::get_global(cx).search.case_sensitive);
16069
16070        let mut builder = AhoCorasickBuilder::new();
16071        builder.ascii_case_insensitive(!case_sensitive);
16072        builder.build(patterns)
16073    }
16074
16075    pub fn find_next_match(
16076        &mut self,
16077        _: &FindNextMatch,
16078        window: &mut Window,
16079        cx: &mut Context<Self>,
16080    ) -> Result<()> {
16081        let selections = self.selections.disjoint_anchors_arc();
16082        match selections.first() {
16083            Some(first) if selections.len() >= 2 => {
16084                self.change_selections(Default::default(), window, cx, |s| {
16085                    s.select_ranges([first.range()]);
16086                });
16087            }
16088            _ => self.select_next(
16089                &SelectNext {
16090                    replace_newest: true,
16091                },
16092                window,
16093                cx,
16094            )?,
16095        }
16096        Ok(())
16097    }
16098
16099    pub fn find_previous_match(
16100        &mut self,
16101        _: &FindPreviousMatch,
16102        window: &mut Window,
16103        cx: &mut Context<Self>,
16104    ) -> Result<()> {
16105        let selections = self.selections.disjoint_anchors_arc();
16106        match selections.last() {
16107            Some(last) if selections.len() >= 2 => {
16108                self.change_selections(Default::default(), window, cx, |s| {
16109                    s.select_ranges([last.range()]);
16110                });
16111            }
16112            _ => self.select_previous(
16113                &SelectPrevious {
16114                    replace_newest: true,
16115                },
16116                window,
16117                cx,
16118            )?,
16119        }
16120        Ok(())
16121    }
16122
16123    pub fn toggle_comments(
16124        &mut self,
16125        action: &ToggleComments,
16126        window: &mut Window,
16127        cx: &mut Context<Self>,
16128    ) {
16129        if self.read_only(cx) {
16130            return;
16131        }
16132        self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
16133        let text_layout_details = &self.text_layout_details(window, cx);
16134        self.transact(window, cx, |this, window, cx| {
16135            let mut selections = this
16136                .selections
16137                .all::<MultiBufferPoint>(&this.display_snapshot(cx));
16138            let mut edits = Vec::new();
16139            let mut selection_edit_ranges = Vec::new();
16140            let mut last_toggled_row = None;
16141            let snapshot = this.buffer.read(cx).read(cx);
16142            let empty_str: Arc<str> = Arc::default();
16143            let mut suffixes_inserted = Vec::new();
16144            let ignore_indent = action.ignore_indent;
16145
16146            fn comment_prefix_range(
16147                snapshot: &MultiBufferSnapshot,
16148                row: MultiBufferRow,
16149                comment_prefix: &str,
16150                comment_prefix_whitespace: &str,
16151                ignore_indent: bool,
16152            ) -> Range<Point> {
16153                let indent_size = if ignore_indent {
16154                    0
16155                } else {
16156                    snapshot.indent_size_for_line(row).len
16157                };
16158
16159                let start = Point::new(row.0, indent_size);
16160
16161                let mut line_bytes = snapshot
16162                    .bytes_in_range(start..snapshot.max_point())
16163                    .flatten()
16164                    .copied();
16165
16166                // If this line currently begins with the line comment prefix, then record
16167                // the range containing the prefix.
16168                if line_bytes
16169                    .by_ref()
16170                    .take(comment_prefix.len())
16171                    .eq(comment_prefix.bytes())
16172                {
16173                    // Include any whitespace that matches the comment prefix.
16174                    let matching_whitespace_len = line_bytes
16175                        .zip(comment_prefix_whitespace.bytes())
16176                        .take_while(|(a, b)| a == b)
16177                        .count() as u32;
16178                    let end = Point::new(
16179                        start.row,
16180                        start.column + comment_prefix.len() as u32 + matching_whitespace_len,
16181                    );
16182                    start..end
16183                } else {
16184                    start..start
16185                }
16186            }
16187
16188            fn comment_suffix_range(
16189                snapshot: &MultiBufferSnapshot,
16190                row: MultiBufferRow,
16191                comment_suffix: &str,
16192                comment_suffix_has_leading_space: bool,
16193            ) -> Range<Point> {
16194                let end = Point::new(row.0, snapshot.line_len(row));
16195                let suffix_start_column = end.column.saturating_sub(comment_suffix.len() as u32);
16196
16197                let mut line_end_bytes = snapshot
16198                    .bytes_in_range(Point::new(end.row, suffix_start_column.saturating_sub(1))..end)
16199                    .flatten()
16200                    .copied();
16201
16202                let leading_space_len = if suffix_start_column > 0
16203                    && line_end_bytes.next() == Some(b' ')
16204                    && comment_suffix_has_leading_space
16205                {
16206                    1
16207                } else {
16208                    0
16209                };
16210
16211                // If this line currently begins with the line comment prefix, then record
16212                // the range containing the prefix.
16213                if line_end_bytes.by_ref().eq(comment_suffix.bytes()) {
16214                    let start = Point::new(end.row, suffix_start_column - leading_space_len);
16215                    start..end
16216                } else {
16217                    end..end
16218                }
16219            }
16220
16221            // TODO: Handle selections that cross excerpts
16222            for selection in &mut selections {
16223                let start_column = snapshot
16224                    .indent_size_for_line(MultiBufferRow(selection.start.row))
16225                    .len;
16226                let language = if let Some(language) =
16227                    snapshot.language_scope_at(Point::new(selection.start.row, start_column))
16228                {
16229                    language
16230                } else {
16231                    continue;
16232                };
16233
16234                selection_edit_ranges.clear();
16235
16236                // If multiple selections contain a given row, avoid processing that
16237                // row more than once.
16238                let mut start_row = MultiBufferRow(selection.start.row);
16239                if last_toggled_row == Some(start_row) {
16240                    start_row = start_row.next_row();
16241                }
16242                let end_row =
16243                    if selection.end.row > selection.start.row && selection.end.column == 0 {
16244                        MultiBufferRow(selection.end.row - 1)
16245                    } else {
16246                        MultiBufferRow(selection.end.row)
16247                    };
16248                last_toggled_row = Some(end_row);
16249
16250                if start_row > end_row {
16251                    continue;
16252                }
16253
16254                // If the language has line comments, toggle those.
16255                let mut full_comment_prefixes = language.line_comment_prefixes().to_vec();
16256
16257                // If ignore_indent is set, trim spaces from the right side of all full_comment_prefixes
16258                if ignore_indent {
16259                    full_comment_prefixes = full_comment_prefixes
16260                        .into_iter()
16261                        .map(|s| Arc::from(s.trim_end()))
16262                        .collect();
16263                }
16264
16265                if !full_comment_prefixes.is_empty() {
16266                    let first_prefix = full_comment_prefixes
16267                        .first()
16268                        .expect("prefixes is non-empty");
16269                    let prefix_trimmed_lengths = full_comment_prefixes
16270                        .iter()
16271                        .map(|p| p.trim_end_matches(' ').len())
16272                        .collect::<SmallVec<[usize; 4]>>();
16273
16274                    let mut all_selection_lines_are_comments = true;
16275
16276                    for row in start_row.0..=end_row.0 {
16277                        let row = MultiBufferRow(row);
16278                        if start_row < end_row && snapshot.is_line_blank(row) {
16279                            continue;
16280                        }
16281
16282                        let prefix_range = full_comment_prefixes
16283                            .iter()
16284                            .zip(prefix_trimmed_lengths.iter().copied())
16285                            .map(|(prefix, trimmed_prefix_len)| {
16286                                comment_prefix_range(
16287                                    snapshot.deref(),
16288                                    row,
16289                                    &prefix[..trimmed_prefix_len],
16290                                    &prefix[trimmed_prefix_len..],
16291                                    ignore_indent,
16292                                )
16293                            })
16294                            .max_by_key(|range| range.end.column - range.start.column)
16295                            .expect("prefixes is non-empty");
16296
16297                        if prefix_range.is_empty() {
16298                            all_selection_lines_are_comments = false;
16299                        }
16300
16301                        selection_edit_ranges.push(prefix_range);
16302                    }
16303
16304                    if all_selection_lines_are_comments {
16305                        edits.extend(
16306                            selection_edit_ranges
16307                                .iter()
16308                                .cloned()
16309                                .map(|range| (range, empty_str.clone())),
16310                        );
16311                    } else {
16312                        let min_column = selection_edit_ranges
16313                            .iter()
16314                            .map(|range| range.start.column)
16315                            .min()
16316                            .unwrap_or(0);
16317                        edits.extend(selection_edit_ranges.iter().map(|range| {
16318                            let position = Point::new(range.start.row, min_column);
16319                            (position..position, first_prefix.clone())
16320                        }));
16321                    }
16322                } else if let Some(BlockCommentConfig {
16323                    start: full_comment_prefix,
16324                    end: comment_suffix,
16325                    ..
16326                }) = language.block_comment()
16327                {
16328                    let comment_prefix = full_comment_prefix.trim_end_matches(' ');
16329                    let comment_prefix_whitespace = &full_comment_prefix[comment_prefix.len()..];
16330                    let prefix_range = comment_prefix_range(
16331                        snapshot.deref(),
16332                        start_row,
16333                        comment_prefix,
16334                        comment_prefix_whitespace,
16335                        ignore_indent,
16336                    );
16337                    let suffix_range = comment_suffix_range(
16338                        snapshot.deref(),
16339                        end_row,
16340                        comment_suffix.trim_start_matches(' '),
16341                        comment_suffix.starts_with(' '),
16342                    );
16343
16344                    if prefix_range.is_empty() || suffix_range.is_empty() {
16345                        edits.push((
16346                            prefix_range.start..prefix_range.start,
16347                            full_comment_prefix.clone(),
16348                        ));
16349                        edits.push((suffix_range.end..suffix_range.end, comment_suffix.clone()));
16350                        suffixes_inserted.push((end_row, comment_suffix.len()));
16351                    } else {
16352                        edits.push((prefix_range, empty_str.clone()));
16353                        edits.push((suffix_range, empty_str.clone()));
16354                    }
16355                } else {
16356                    continue;
16357                }
16358            }
16359
16360            drop(snapshot);
16361            this.buffer.update(cx, |buffer, cx| {
16362                buffer.edit(edits, None, cx);
16363            });
16364
16365            // Adjust selections so that they end before any comment suffixes that
16366            // were inserted.
16367            let mut suffixes_inserted = suffixes_inserted.into_iter().peekable();
16368            let mut selections = this.selections.all::<Point>(&this.display_snapshot(cx));
16369            let snapshot = this.buffer.read(cx).read(cx);
16370            for selection in &mut selections {
16371                while let Some((row, suffix_len)) = suffixes_inserted.peek().copied() {
16372                    match row.cmp(&MultiBufferRow(selection.end.row)) {
16373                        Ordering::Less => {
16374                            suffixes_inserted.next();
16375                            continue;
16376                        }
16377                        Ordering::Greater => break,
16378                        Ordering::Equal => {
16379                            if selection.end.column == snapshot.line_len(row) {
16380                                if selection.is_empty() {
16381                                    selection.start.column -= suffix_len as u32;
16382                                }
16383                                selection.end.column -= suffix_len as u32;
16384                            }
16385                            break;
16386                        }
16387                    }
16388                }
16389            }
16390
16391            drop(snapshot);
16392            this.change_selections(Default::default(), window, cx, |s| s.select(selections));
16393
16394            let selections = this.selections.all::<Point>(&this.display_snapshot(cx));
16395            let selections_on_single_row = selections.windows(2).all(|selections| {
16396                selections[0].start.row == selections[1].start.row
16397                    && selections[0].end.row == selections[1].end.row
16398                    && selections[0].start.row == selections[0].end.row
16399            });
16400            let selections_selecting = selections
16401                .iter()
16402                .any(|selection| selection.start != selection.end);
16403            let advance_downwards = action.advance_downwards
16404                && selections_on_single_row
16405                && !selections_selecting
16406                && !matches!(this.mode, EditorMode::SingleLine);
16407
16408            if advance_downwards {
16409                let snapshot = this.buffer.read(cx).snapshot(cx);
16410
16411                this.change_selections(Default::default(), window, cx, |s| {
16412                    s.move_cursors_with(&mut |display_snapshot, display_point, _| {
16413                        let mut point = display_point.to_point(display_snapshot);
16414                        point.row += 1;
16415                        point = snapshot.clip_point(point, Bias::Left);
16416                        let display_point = point.to_display_point(display_snapshot);
16417                        let goal = SelectionGoal::HorizontalPosition(
16418                            display_snapshot
16419                                .x_for_display_point(display_point, text_layout_details)
16420                                .into(),
16421                        );
16422                        (display_point, goal)
16423                    })
16424                });
16425            }
16426        });
16427    }
16428
16429    pub fn select_enclosing_symbol(
16430        &mut self,
16431        _: &SelectEnclosingSymbol,
16432        window: &mut Window,
16433        cx: &mut Context<Self>,
16434    ) {
16435        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
16436
16437        let buffer = self.buffer.read(cx).snapshot(cx);
16438        let old_selections = self
16439            .selections
16440            .all::<MultiBufferOffset>(&self.display_snapshot(cx))
16441            .into_boxed_slice();
16442
16443        fn update_selection(
16444            selection: &Selection<MultiBufferOffset>,
16445            buffer_snap: &MultiBufferSnapshot,
16446        ) -> Option<Selection<MultiBufferOffset>> {
16447            let cursor = selection.head();
16448            let (_buffer_id, symbols) = buffer_snap.symbols_containing(cursor, None)?;
16449            for symbol in symbols.iter().rev() {
16450                let start = symbol.range.start.to_offset(buffer_snap);
16451                let end = symbol.range.end.to_offset(buffer_snap);
16452                let new_range = start..end;
16453                if start < selection.start || end > selection.end {
16454                    return Some(Selection {
16455                        id: selection.id,
16456                        start: new_range.start,
16457                        end: new_range.end,
16458                        goal: SelectionGoal::None,
16459                        reversed: selection.reversed,
16460                    });
16461                }
16462            }
16463            None
16464        }
16465
16466        let mut selected_larger_symbol = false;
16467        let new_selections = old_selections
16468            .iter()
16469            .map(|selection| match update_selection(selection, &buffer) {
16470                Some(new_selection) => {
16471                    if new_selection.range() != selection.range() {
16472                        selected_larger_symbol = true;
16473                    }
16474                    new_selection
16475                }
16476                None => selection.clone(),
16477            })
16478            .collect::<Vec<_>>();
16479
16480        if selected_larger_symbol {
16481            self.change_selections(Default::default(), window, cx, |s| {
16482                s.select(new_selections);
16483            });
16484        }
16485    }
16486
16487    pub fn select_larger_syntax_node(
16488        &mut self,
16489        _: &SelectLargerSyntaxNode,
16490        window: &mut Window,
16491        cx: &mut Context<Self>,
16492    ) {
16493        let Some(visible_row_count) = self.visible_row_count() else {
16494            return;
16495        };
16496        let old_selections: Box<[_]> = self
16497            .selections
16498            .all::<MultiBufferOffset>(&self.display_snapshot(cx))
16499            .into();
16500        if old_selections.is_empty() {
16501            return;
16502        }
16503
16504        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
16505
16506        let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
16507        let buffer = self.buffer.read(cx).snapshot(cx);
16508
16509        let mut selected_larger_node = false;
16510        let mut new_selections = old_selections
16511            .iter()
16512            .map(|selection| {
16513                let old_range = selection.start..selection.end;
16514
16515                if let Some((node, _)) = buffer.syntax_ancestor(old_range.clone()) {
16516                    // manually select word at selection
16517                    if ["string_content", "inline"].contains(&node.kind()) {
16518                        let (word_range, _) = buffer.surrounding_word(old_range.start, None);
16519                        // ignore if word is already selected
16520                        if !word_range.is_empty() && old_range != word_range {
16521                            let (last_word_range, _) = buffer.surrounding_word(old_range.end, None);
16522                            // only select word if start and end point belongs to same word
16523                            if word_range == last_word_range {
16524                                selected_larger_node = true;
16525                                return Selection {
16526                                    id: selection.id,
16527                                    start: word_range.start,
16528                                    end: word_range.end,
16529                                    goal: SelectionGoal::None,
16530                                    reversed: selection.reversed,
16531                                };
16532                            }
16533                        }
16534                    }
16535                }
16536
16537                let mut new_range = old_range.clone();
16538                while let Some((node, range)) = buffer.syntax_ancestor(new_range.clone()) {
16539                    new_range = range;
16540                    if !node.is_named() {
16541                        continue;
16542                    }
16543                    if !display_map.intersects_fold(new_range.start)
16544                        && !display_map.intersects_fold(new_range.end)
16545                    {
16546                        break;
16547                    }
16548                }
16549
16550                selected_larger_node |= new_range != old_range;
16551                Selection {
16552                    id: selection.id,
16553                    start: new_range.start,
16554                    end: new_range.end,
16555                    goal: SelectionGoal::None,
16556                    reversed: selection.reversed,
16557                }
16558            })
16559            .collect::<Vec<_>>();
16560
16561        if !selected_larger_node {
16562            return; // don't put this call in the history
16563        }
16564
16565        // scroll based on transformation done to the last selection created by the user
16566        let (last_old, last_new) = old_selections
16567            .last()
16568            .zip(new_selections.last().cloned())
16569            .expect("old_selections isn't empty");
16570
16571        // revert selection
16572        let is_selection_reversed = {
16573            let should_newest_selection_be_reversed = last_old.start != last_new.start;
16574            new_selections.last_mut().expect("checked above").reversed =
16575                should_newest_selection_be_reversed;
16576            should_newest_selection_be_reversed
16577        };
16578
16579        if selected_larger_node {
16580            self.select_syntax_node_history.disable_clearing = true;
16581            self.change_selections(SelectionEffects::no_scroll(), window, cx, |s| {
16582                s.select(new_selections.clone());
16583            });
16584            self.select_syntax_node_history.disable_clearing = false;
16585        }
16586
16587        let start_row = last_new.start.to_display_point(&display_map).row().0;
16588        let end_row = last_new.end.to_display_point(&display_map).row().0;
16589        let selection_height = end_row - start_row + 1;
16590        let scroll_margin_rows = self.vertical_scroll_margin() as u32;
16591
16592        let fits_on_the_screen = visible_row_count >= selection_height + scroll_margin_rows * 2;
16593        let scroll_behavior = if fits_on_the_screen {
16594            self.request_autoscroll(Autoscroll::fit(), cx);
16595            SelectSyntaxNodeScrollBehavior::FitSelection
16596        } else if is_selection_reversed {
16597            self.scroll_cursor_top(&ScrollCursorTop, window, cx);
16598            SelectSyntaxNodeScrollBehavior::CursorTop
16599        } else {
16600            self.scroll_cursor_bottom(&ScrollCursorBottom, window, cx);
16601            SelectSyntaxNodeScrollBehavior::CursorBottom
16602        };
16603
16604        self.select_syntax_node_history.push((
16605            old_selections,
16606            scroll_behavior,
16607            is_selection_reversed,
16608        ));
16609    }
16610
16611    pub fn select_smaller_syntax_node(
16612        &mut self,
16613        _: &SelectSmallerSyntaxNode,
16614        window: &mut Window,
16615        cx: &mut Context<Self>,
16616    ) {
16617        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
16618
16619        if let Some((mut selections, scroll_behavior, is_selection_reversed)) =
16620            self.select_syntax_node_history.pop()
16621        {
16622            if let Some(selection) = selections.last_mut() {
16623                selection.reversed = is_selection_reversed;
16624            }
16625
16626            self.select_syntax_node_history.disable_clearing = true;
16627            self.change_selections(SelectionEffects::no_scroll(), window, cx, |s| {
16628                s.select(selections.to_vec());
16629            });
16630            self.select_syntax_node_history.disable_clearing = false;
16631
16632            match scroll_behavior {
16633                SelectSyntaxNodeScrollBehavior::CursorTop => {
16634                    self.scroll_cursor_top(&ScrollCursorTop, window, cx);
16635                }
16636                SelectSyntaxNodeScrollBehavior::FitSelection => {
16637                    self.request_autoscroll(Autoscroll::fit(), cx);
16638                }
16639                SelectSyntaxNodeScrollBehavior::CursorBottom => {
16640                    self.scroll_cursor_bottom(&ScrollCursorBottom, window, cx);
16641                }
16642            }
16643        }
16644    }
16645
16646    pub fn unwrap_syntax_node(
16647        &mut self,
16648        _: &UnwrapSyntaxNode,
16649        window: &mut Window,
16650        cx: &mut Context<Self>,
16651    ) {
16652        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
16653
16654        let buffer = self.buffer.read(cx).snapshot(cx);
16655        let selections = self
16656            .selections
16657            .all::<MultiBufferOffset>(&self.display_snapshot(cx))
16658            .into_iter()
16659            // subtracting the offset requires sorting
16660            .sorted_by_key(|i| i.start);
16661
16662        let full_edits = selections
16663            .into_iter()
16664            .filter_map(|selection| {
16665                let child = if selection.is_empty()
16666                    && let Some((_, ancestor_range)) =
16667                        buffer.syntax_ancestor(selection.start..selection.end)
16668                {
16669                    ancestor_range
16670                } else {
16671                    selection.range()
16672                };
16673
16674                let mut parent = child.clone();
16675                while let Some((_, ancestor_range)) = buffer.syntax_ancestor(parent.clone()) {
16676                    parent = ancestor_range;
16677                    if parent.start < child.start || parent.end > child.end {
16678                        break;
16679                    }
16680                }
16681
16682                if parent == child {
16683                    return None;
16684                }
16685                let text = buffer.text_for_range(child).collect::<String>();
16686                Some((selection.id, parent, text))
16687            })
16688            .collect::<Vec<_>>();
16689        if full_edits.is_empty() {
16690            return;
16691        }
16692
16693        self.transact(window, cx, |this, window, cx| {
16694            this.buffer.update(cx, |buffer, cx| {
16695                buffer.edit(
16696                    full_edits
16697                        .iter()
16698                        .map(|(_, p, t)| (p.clone(), t.clone()))
16699                        .collect::<Vec<_>>(),
16700                    None,
16701                    cx,
16702                );
16703            });
16704            this.change_selections(Default::default(), window, cx, |s| {
16705                let mut offset = 0;
16706                let mut selections = vec![];
16707                for (id, parent, text) in full_edits {
16708                    let start = parent.start - offset;
16709                    offset += (parent.end - parent.start) - text.len();
16710                    selections.push(Selection {
16711                        id,
16712                        start,
16713                        end: start + text.len(),
16714                        reversed: false,
16715                        goal: Default::default(),
16716                    });
16717                }
16718                s.select(selections);
16719            });
16720        });
16721    }
16722
16723    pub fn select_next_syntax_node(
16724        &mut self,
16725        _: &SelectNextSyntaxNode,
16726        window: &mut Window,
16727        cx: &mut Context<Self>,
16728    ) {
16729        let old_selections: Box<[_]> = self
16730            .selections
16731            .all::<MultiBufferOffset>(&self.display_snapshot(cx))
16732            .into();
16733        if old_selections.is_empty() {
16734            return;
16735        }
16736
16737        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
16738
16739        let buffer = self.buffer.read(cx).snapshot(cx);
16740        let mut selected_sibling = false;
16741
16742        let new_selections = old_selections
16743            .iter()
16744            .map(|selection| {
16745                let old_range = selection.start..selection.end;
16746
16747                let old_range =
16748                    old_range.start.to_offset(&buffer)..old_range.end.to_offset(&buffer);
16749                let excerpt = buffer.excerpt_containing(old_range.clone());
16750
16751                if let Some(mut excerpt) = excerpt
16752                    && let Some(node) = excerpt
16753                        .buffer()
16754                        .syntax_next_sibling(excerpt.map_range_to_buffer(old_range))
16755                {
16756                    let new_range = excerpt.map_range_from_buffer(
16757                        BufferOffset(node.byte_range().start)..BufferOffset(node.byte_range().end),
16758                    );
16759                    selected_sibling = true;
16760                    Selection {
16761                        id: selection.id,
16762                        start: new_range.start,
16763                        end: new_range.end,
16764                        goal: SelectionGoal::None,
16765                        reversed: selection.reversed,
16766                    }
16767                } else {
16768                    selection.clone()
16769                }
16770            })
16771            .collect::<Vec<_>>();
16772
16773        if selected_sibling {
16774            self.change_selections(
16775                SelectionEffects::scroll(Autoscroll::fit()),
16776                window,
16777                cx,
16778                |s| {
16779                    s.select(new_selections);
16780                },
16781            );
16782        }
16783    }
16784
16785    pub fn select_prev_syntax_node(
16786        &mut self,
16787        _: &SelectPreviousSyntaxNode,
16788        window: &mut Window,
16789        cx: &mut Context<Self>,
16790    ) {
16791        let old_selections: Box<[_]> = self
16792            .selections
16793            .all::<MultiBufferOffset>(&self.display_snapshot(cx))
16794            .into();
16795        if old_selections.is_empty() {
16796            return;
16797        }
16798
16799        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
16800
16801        let buffer = self.buffer.read(cx).snapshot(cx);
16802        let mut selected_sibling = false;
16803
16804        let new_selections = old_selections
16805            .iter()
16806            .map(|selection| {
16807                let old_range = selection.start..selection.end;
16808                let old_range =
16809                    old_range.start.to_offset(&buffer)..old_range.end.to_offset(&buffer);
16810                let excerpt = buffer.excerpt_containing(old_range.clone());
16811
16812                if let Some(mut excerpt) = excerpt
16813                    && let Some(node) = excerpt
16814                        .buffer()
16815                        .syntax_prev_sibling(excerpt.map_range_to_buffer(old_range))
16816                {
16817                    let new_range = excerpt.map_range_from_buffer(
16818                        BufferOffset(node.byte_range().start)..BufferOffset(node.byte_range().end),
16819                    );
16820                    selected_sibling = true;
16821                    Selection {
16822                        id: selection.id,
16823                        start: new_range.start,
16824                        end: new_range.end,
16825                        goal: SelectionGoal::None,
16826                        reversed: selection.reversed,
16827                    }
16828                } else {
16829                    selection.clone()
16830                }
16831            })
16832            .collect::<Vec<_>>();
16833
16834        if selected_sibling {
16835            self.change_selections(
16836                SelectionEffects::scroll(Autoscroll::fit()),
16837                window,
16838                cx,
16839                |s| {
16840                    s.select(new_selections);
16841                },
16842            );
16843        }
16844    }
16845
16846    pub fn move_to_start_of_larger_syntax_node(
16847        &mut self,
16848        _: &MoveToStartOfLargerSyntaxNode,
16849        window: &mut Window,
16850        cx: &mut Context<Self>,
16851    ) {
16852        self.move_cursors_to_syntax_nodes(window, cx, false);
16853    }
16854
16855    pub fn move_to_end_of_larger_syntax_node(
16856        &mut self,
16857        _: &MoveToEndOfLargerSyntaxNode,
16858        window: &mut Window,
16859        cx: &mut Context<Self>,
16860    ) {
16861        self.move_cursors_to_syntax_nodes(window, cx, true);
16862    }
16863
16864    fn find_syntax_node_boundary(
16865        &self,
16866        selection_pos: MultiBufferOffset,
16867        move_to_end: bool,
16868        display_map: &DisplaySnapshot,
16869        buffer: &MultiBufferSnapshot,
16870    ) -> MultiBufferOffset {
16871        let old_range = selection_pos..selection_pos;
16872        let mut new_pos = selection_pos;
16873        let mut search_range = old_range;
16874        while let Some((node, range)) = buffer.syntax_ancestor(search_range.clone()) {
16875            search_range = range.clone();
16876            if !node.is_named()
16877                || display_map.intersects_fold(range.start)
16878                || display_map.intersects_fold(range.end)
16879                // If cursor is already at the end of the syntax node, continue searching
16880                || (move_to_end && range.end == selection_pos)
16881                // If cursor is already at the start of the syntax node, continue searching
16882                || (!move_to_end && range.start == selection_pos)
16883            {
16884                continue;
16885            }
16886
16887            // If we found a string_content node, find the largest parent that is still string_content
16888            // Enables us to skip to the end of strings without taking multiple steps inside the string
16889            let (_, final_range) = if node.kind() == "string_content" {
16890                let mut current_node = node;
16891                let mut current_range = range;
16892                while let Some((parent, parent_range)) =
16893                    buffer.syntax_ancestor(current_range.clone())
16894                {
16895                    if parent.kind() == "string_content" {
16896                        current_node = parent;
16897                        current_range = parent_range;
16898                    } else {
16899                        break;
16900                    }
16901                }
16902
16903                (current_node, current_range)
16904            } else {
16905                (node, range)
16906            };
16907
16908            new_pos = if move_to_end {
16909                final_range.end
16910            } else {
16911                final_range.start
16912            };
16913
16914            break;
16915        }
16916
16917        new_pos
16918    }
16919
16920    fn move_cursors_to_syntax_nodes(
16921        &mut self,
16922        window: &mut Window,
16923        cx: &mut Context<Self>,
16924        move_to_end: bool,
16925    ) -> bool {
16926        let old_selections: Box<[_]> = self
16927            .selections
16928            .all::<MultiBufferOffset>(&self.display_snapshot(cx))
16929            .into();
16930        if old_selections.is_empty() {
16931            return false;
16932        }
16933
16934        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
16935
16936        let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
16937        let buffer = self.buffer.read(cx).snapshot(cx);
16938
16939        let mut any_cursor_moved = false;
16940        let new_selections = old_selections
16941            .iter()
16942            .map(|selection| {
16943                if !selection.is_empty() {
16944                    return selection.clone();
16945                }
16946
16947                let selection_pos = selection.head();
16948                let new_pos = self.find_syntax_node_boundary(
16949                    selection_pos,
16950                    move_to_end,
16951                    &display_map,
16952                    &buffer,
16953                );
16954
16955                any_cursor_moved |= new_pos != selection_pos;
16956
16957                Selection {
16958                    id: selection.id,
16959                    start: new_pos,
16960                    end: new_pos,
16961                    goal: SelectionGoal::None,
16962                    reversed: false,
16963                }
16964            })
16965            .collect::<Vec<_>>();
16966
16967        self.change_selections(Default::default(), window, cx, |s| {
16968            s.select(new_selections);
16969        });
16970        self.request_autoscroll(Autoscroll::newest(), cx);
16971
16972        any_cursor_moved
16973    }
16974
16975    pub fn select_to_start_of_larger_syntax_node(
16976        &mut self,
16977        _: &SelectToStartOfLargerSyntaxNode,
16978        window: &mut Window,
16979        cx: &mut Context<Self>,
16980    ) {
16981        self.select_to_syntax_nodes(window, cx, false);
16982    }
16983
16984    pub fn select_to_end_of_larger_syntax_node(
16985        &mut self,
16986        _: &SelectToEndOfLargerSyntaxNode,
16987        window: &mut Window,
16988        cx: &mut Context<Self>,
16989    ) {
16990        self.select_to_syntax_nodes(window, cx, true);
16991    }
16992
16993    fn select_to_syntax_nodes(
16994        &mut self,
16995        window: &mut Window,
16996        cx: &mut Context<Self>,
16997        move_to_end: bool,
16998    ) {
16999        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
17000
17001        let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
17002        let buffer = self.buffer.read(cx).snapshot(cx);
17003        let old_selections = self.selections.all::<MultiBufferOffset>(&display_map);
17004
17005        let new_selections = old_selections
17006            .iter()
17007            .map(|selection| {
17008                let new_pos = self.find_syntax_node_boundary(
17009                    selection.head(),
17010                    move_to_end,
17011                    &display_map,
17012                    &buffer,
17013                );
17014
17015                let mut new_selection = selection.clone();
17016                new_selection.set_head(new_pos, SelectionGoal::None);
17017                new_selection
17018            })
17019            .collect::<Vec<_>>();
17020
17021        self.change_selections(Default::default(), window, cx, |s| {
17022            s.select(new_selections);
17023        });
17024    }
17025
17026    fn refresh_runnables(&mut self, window: &mut Window, cx: &mut Context<Self>) -> Task<()> {
17027        if !EditorSettings::get_global(cx).gutter.runnables || !self.enable_runnables {
17028            self.clear_tasks();
17029            return Task::ready(());
17030        }
17031        let project = self.project().map(Entity::downgrade);
17032        let task_sources = self.lsp_task_sources(cx);
17033        let multi_buffer = self.buffer.downgrade();
17034        cx.spawn_in(window, async move |editor, cx| {
17035            cx.background_executor().timer(UPDATE_DEBOUNCE).await;
17036            let Some(project) = project.and_then(|p| p.upgrade()) else {
17037                return;
17038            };
17039            let Ok(display_snapshot) = editor.update(cx, |this, cx| {
17040                this.display_map.update(cx, |map, cx| map.snapshot(cx))
17041            }) else {
17042                return;
17043            };
17044
17045            let hide_runnables = project.update(cx, |project, _| project.is_via_collab());
17046            if hide_runnables {
17047                return;
17048            }
17049            let new_rows =
17050                cx.background_spawn({
17051                    let snapshot = display_snapshot.clone();
17052                    async move {
17053                        Self::fetch_runnable_ranges(&snapshot, Anchor::min()..Anchor::max())
17054                    }
17055                })
17056                    .await;
17057            let Ok(lsp_tasks) =
17058                cx.update(|_, cx| crate::lsp_tasks(project.clone(), &task_sources, None, cx))
17059            else {
17060                return;
17061            };
17062            let lsp_tasks = lsp_tasks.await;
17063
17064            let Ok(mut lsp_tasks_by_rows) = cx.update(|_, cx| {
17065                lsp_tasks
17066                    .into_iter()
17067                    .flat_map(|(kind, tasks)| {
17068                        tasks.into_iter().filter_map(move |(location, task)| {
17069                            Some((kind.clone(), location?, task))
17070                        })
17071                    })
17072                    .fold(HashMap::default(), |mut acc, (kind, location, task)| {
17073                        let buffer = location.target.buffer;
17074                        let buffer_snapshot = buffer.read(cx).snapshot();
17075                        let offset = display_snapshot.buffer_snapshot().excerpts().find_map(
17076                            |(excerpt_id, snapshot, _)| {
17077                                if snapshot.remote_id() == buffer_snapshot.remote_id() {
17078                                    display_snapshot
17079                                        .buffer_snapshot()
17080                                        .anchor_in_excerpt(excerpt_id, location.target.range.start)
17081                                } else {
17082                                    None
17083                                }
17084                            },
17085                        );
17086                        if let Some(offset) = offset {
17087                            let task_buffer_range =
17088                                location.target.range.to_point(&buffer_snapshot);
17089                            let context_buffer_range =
17090                                task_buffer_range.to_offset(&buffer_snapshot);
17091                            let context_range = BufferOffset(context_buffer_range.start)
17092                                ..BufferOffset(context_buffer_range.end);
17093
17094                            acc.entry((buffer_snapshot.remote_id(), task_buffer_range.start.row))
17095                                .or_insert_with(|| RunnableTasks {
17096                                    templates: Vec::new(),
17097                                    offset,
17098                                    column: task_buffer_range.start.column,
17099                                    extra_variables: HashMap::default(),
17100                                    context_range,
17101                                })
17102                                .templates
17103                                .push((kind, task.original_task().clone()));
17104                        }
17105
17106                        acc
17107                    })
17108            }) else {
17109                return;
17110            };
17111
17112            let Ok(prefer_lsp) = multi_buffer.update(cx, |buffer, cx| {
17113                buffer.language_settings(cx).tasks.prefer_lsp
17114            }) else {
17115                return;
17116            };
17117
17118            let rows = Self::runnable_rows(
17119                project,
17120                display_snapshot,
17121                prefer_lsp && !lsp_tasks_by_rows.is_empty(),
17122                new_rows,
17123                cx.clone(),
17124            )
17125            .await;
17126            editor
17127                .update(cx, |editor, _| {
17128                    editor.clear_tasks();
17129                    for (key, mut value) in rows {
17130                        if let Some(lsp_tasks) = lsp_tasks_by_rows.remove(&key) {
17131                            value.templates.extend(lsp_tasks.templates);
17132                        }
17133
17134                        editor.insert_tasks(key, value);
17135                    }
17136                    for (key, value) in lsp_tasks_by_rows {
17137                        editor.insert_tasks(key, value);
17138                    }
17139                })
17140                .ok();
17141        })
17142    }
17143    fn fetch_runnable_ranges(
17144        snapshot: &DisplaySnapshot,
17145        range: Range<Anchor>,
17146    ) -> Vec<(Range<MultiBufferOffset>, language::RunnableRange)> {
17147        snapshot.buffer_snapshot().runnable_ranges(range).collect()
17148    }
17149
17150    fn runnable_rows(
17151        project: Entity<Project>,
17152        snapshot: DisplaySnapshot,
17153        prefer_lsp: bool,
17154        runnable_ranges: Vec<(Range<MultiBufferOffset>, language::RunnableRange)>,
17155        cx: AsyncWindowContext,
17156    ) -> Task<Vec<((BufferId, BufferRow), RunnableTasks)>> {
17157        cx.spawn(async move |cx| {
17158            let mut runnable_rows = Vec::with_capacity(runnable_ranges.len());
17159            for (run_range, mut runnable) in runnable_ranges {
17160                let Some(tasks) = cx
17161                    .update(|_, cx| Self::templates_with_tags(&project, &mut runnable.runnable, cx))
17162                    .ok()
17163                else {
17164                    continue;
17165                };
17166                let mut tasks = tasks.await;
17167
17168                if prefer_lsp {
17169                    tasks.retain(|(task_kind, _)| {
17170                        !matches!(task_kind, TaskSourceKind::Language { .. })
17171                    });
17172                }
17173                if tasks.is_empty() {
17174                    continue;
17175                }
17176
17177                let point = run_range.start.to_point(&snapshot.buffer_snapshot());
17178                let Some(row) = snapshot
17179                    .buffer_snapshot()
17180                    .buffer_line_for_row(MultiBufferRow(point.row))
17181                    .map(|(_, range)| range.start.row)
17182                else {
17183                    continue;
17184                };
17185
17186                let context_range =
17187                    BufferOffset(runnable.full_range.start)..BufferOffset(runnable.full_range.end);
17188                runnable_rows.push((
17189                    (runnable.buffer_id, row),
17190                    RunnableTasks {
17191                        templates: tasks,
17192                        offset: snapshot.buffer_snapshot().anchor_before(run_range.start),
17193                        context_range,
17194                        column: point.column,
17195                        extra_variables: runnable.extra_captures,
17196                    },
17197                ));
17198            }
17199            runnable_rows
17200        })
17201    }
17202
17203    fn templates_with_tags(
17204        project: &Entity<Project>,
17205        runnable: &mut Runnable,
17206        cx: &mut App,
17207    ) -> Task<Vec<(TaskSourceKind, TaskTemplate)>> {
17208        let (inventory, worktree_id, file) = project.read_with(cx, |project, cx| {
17209            let (worktree_id, file) = project
17210                .buffer_for_id(runnable.buffer, cx)
17211                .and_then(|buffer| buffer.read(cx).file())
17212                .map(|file| (file.worktree_id(cx), file.clone()))
17213                .unzip();
17214
17215            (
17216                project.task_store().read(cx).task_inventory().cloned(),
17217                worktree_id,
17218                file,
17219            )
17220        });
17221
17222        let tags = mem::take(&mut runnable.tags);
17223        let language = runnable.language.clone();
17224        cx.spawn(async move |cx| {
17225            let mut templates_with_tags = Vec::new();
17226            if let Some(inventory) = inventory {
17227                for RunnableTag(tag) in tags {
17228                    let new_tasks = inventory.update(cx, |inventory, cx| {
17229                        inventory.list_tasks(file.clone(), Some(language.clone()), worktree_id, cx)
17230                    });
17231                    templates_with_tags.extend(new_tasks.await.into_iter().filter(
17232                        move |(_, template)| {
17233                            template.tags.iter().any(|source_tag| source_tag == &tag)
17234                        },
17235                    ));
17236                }
17237            }
17238            templates_with_tags.sort_by_key(|(kind, _)| kind.to_owned());
17239
17240            if let Some((leading_tag_source, _)) = templates_with_tags.first() {
17241                // Strongest source wins; if we have worktree tag binding, prefer that to
17242                // global and language bindings;
17243                // if we have a global binding, prefer that to language binding.
17244                let first_mismatch = templates_with_tags
17245                    .iter()
17246                    .position(|(tag_source, _)| tag_source != leading_tag_source);
17247                if let Some(index) = first_mismatch {
17248                    templates_with_tags.truncate(index);
17249                }
17250            }
17251
17252            templates_with_tags
17253        })
17254    }
17255
17256    pub fn move_to_enclosing_bracket(
17257        &mut self,
17258        _: &MoveToEnclosingBracket,
17259        window: &mut Window,
17260        cx: &mut Context<Self>,
17261    ) {
17262        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
17263        self.change_selections(Default::default(), window, cx, |s| {
17264            s.move_offsets_with(&mut |snapshot, selection| {
17265                let Some(enclosing_bracket_ranges) =
17266                    snapshot.enclosing_bracket_ranges(selection.start..selection.end)
17267                else {
17268                    return;
17269                };
17270
17271                let mut best_length = usize::MAX;
17272                let mut best_inside = false;
17273                let mut best_in_bracket_range = false;
17274                let mut best_destination = None;
17275                for (open, close) in enclosing_bracket_ranges {
17276                    let close = close.to_inclusive();
17277                    let length = *close.end() - open.start;
17278                    let inside = selection.start >= open.end && selection.end <= *close.start();
17279                    let in_bracket_range = open.to_inclusive().contains(&selection.head())
17280                        || close.contains(&selection.head());
17281
17282                    // If best is next to a bracket and current isn't, skip
17283                    if !in_bracket_range && best_in_bracket_range {
17284                        continue;
17285                    }
17286
17287                    // Prefer smaller lengths unless best is inside and current isn't
17288                    if length > best_length && (best_inside || !inside) {
17289                        continue;
17290                    }
17291
17292                    best_length = length;
17293                    best_inside = inside;
17294                    best_in_bracket_range = in_bracket_range;
17295                    best_destination = Some(
17296                        if close.contains(&selection.start) && close.contains(&selection.end) {
17297                            if inside { open.end } else { open.start }
17298                        } else if inside {
17299                            *close.start()
17300                        } else {
17301                            *close.end()
17302                        },
17303                    );
17304                }
17305
17306                if let Some(destination) = best_destination {
17307                    selection.collapse_to(destination, SelectionGoal::None);
17308                }
17309            })
17310        });
17311    }
17312
17313    pub fn undo_selection(
17314        &mut self,
17315        _: &UndoSelection,
17316        window: &mut Window,
17317        cx: &mut Context<Self>,
17318    ) {
17319        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
17320        if let Some(entry) = self.selection_history.undo_stack.pop_back() {
17321            self.selection_history.mode = SelectionHistoryMode::Undoing;
17322            self.with_selection_effects_deferred(window, cx, |this, window, cx| {
17323                this.end_selection(window, cx);
17324                this.change_selections(
17325                    SelectionEffects::scroll(Autoscroll::newest()),
17326                    window,
17327                    cx,
17328                    |s| s.select_anchors(entry.selections.to_vec()),
17329                );
17330            });
17331            self.selection_history.mode = SelectionHistoryMode::Normal;
17332
17333            self.select_next_state = entry.select_next_state;
17334            self.select_prev_state = entry.select_prev_state;
17335            self.add_selections_state = entry.add_selections_state;
17336        }
17337    }
17338
17339    pub fn redo_selection(
17340        &mut self,
17341        _: &RedoSelection,
17342        window: &mut Window,
17343        cx: &mut Context<Self>,
17344    ) {
17345        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
17346        if let Some(entry) = self.selection_history.redo_stack.pop_back() {
17347            self.selection_history.mode = SelectionHistoryMode::Redoing;
17348            self.with_selection_effects_deferred(window, cx, |this, window, cx| {
17349                this.end_selection(window, cx);
17350                this.change_selections(
17351                    SelectionEffects::scroll(Autoscroll::newest()),
17352                    window,
17353                    cx,
17354                    |s| s.select_anchors(entry.selections.to_vec()),
17355                );
17356            });
17357            self.selection_history.mode = SelectionHistoryMode::Normal;
17358
17359            self.select_next_state = entry.select_next_state;
17360            self.select_prev_state = entry.select_prev_state;
17361            self.add_selections_state = entry.add_selections_state;
17362        }
17363    }
17364
17365    pub fn expand_excerpts(
17366        &mut self,
17367        action: &ExpandExcerpts,
17368        _: &mut Window,
17369        cx: &mut Context<Self>,
17370    ) {
17371        self.expand_excerpts_for_direction(action.lines, ExpandExcerptDirection::UpAndDown, cx)
17372    }
17373
17374    pub fn expand_excerpts_down(
17375        &mut self,
17376        action: &ExpandExcerptsDown,
17377        _: &mut Window,
17378        cx: &mut Context<Self>,
17379    ) {
17380        self.expand_excerpts_for_direction(action.lines, ExpandExcerptDirection::Down, cx)
17381    }
17382
17383    pub fn expand_excerpts_up(
17384        &mut self,
17385        action: &ExpandExcerptsUp,
17386        _: &mut Window,
17387        cx: &mut Context<Self>,
17388    ) {
17389        self.expand_excerpts_for_direction(action.lines, ExpandExcerptDirection::Up, cx)
17390    }
17391
17392    pub fn expand_excerpts_for_direction(
17393        &mut self,
17394        lines: u32,
17395        direction: ExpandExcerptDirection,
17396        cx: &mut Context<Self>,
17397    ) {
17398        let selections = self.selections.disjoint_anchors_arc();
17399
17400        let lines = if lines == 0 {
17401            EditorSettings::get_global(cx).expand_excerpt_lines
17402        } else {
17403            lines
17404        };
17405
17406        let snapshot = self.buffer.read(cx).snapshot(cx);
17407        let excerpt_ids = selections
17408            .iter()
17409            .flat_map(|selection| snapshot.excerpt_ids_for_range(selection.range()))
17410            .unique()
17411            .sorted()
17412            .collect::<Vec<_>>();
17413
17414        if self.delegate_expand_excerpts {
17415            cx.emit(EditorEvent::ExpandExcerptsRequested {
17416                excerpt_ids,
17417                lines,
17418                direction,
17419            });
17420            return;
17421        }
17422
17423        self.buffer.update(cx, |buffer, cx| {
17424            buffer.expand_excerpts(excerpt_ids, lines, direction, cx)
17425        })
17426    }
17427
17428    pub fn expand_excerpt(
17429        &mut self,
17430        excerpt: ExcerptId,
17431        direction: ExpandExcerptDirection,
17432        window: &mut Window,
17433        cx: &mut Context<Self>,
17434    ) {
17435        let lines_to_expand = EditorSettings::get_global(cx).expand_excerpt_lines;
17436
17437        if self.delegate_expand_excerpts {
17438            cx.emit(EditorEvent::ExpandExcerptsRequested {
17439                excerpt_ids: vec![excerpt],
17440                lines: lines_to_expand,
17441                direction,
17442            });
17443            return;
17444        }
17445
17446        let current_scroll_position = self.scroll_position(cx);
17447        let mut scroll = None;
17448
17449        if direction == ExpandExcerptDirection::Down {
17450            let multi_buffer = self.buffer.read(cx);
17451            let snapshot = multi_buffer.snapshot(cx);
17452            if let Some(buffer_id) = snapshot.buffer_id_for_excerpt(excerpt)
17453                && let Some(buffer) = multi_buffer.buffer(buffer_id)
17454                && let Some(excerpt_range) = snapshot.context_range_for_excerpt(excerpt)
17455            {
17456                let buffer_snapshot = buffer.read(cx).snapshot();
17457                let excerpt_end_row = Point::from_anchor(&excerpt_range.end, &buffer_snapshot).row;
17458                let last_row = buffer_snapshot.max_point().row;
17459                let lines_below = last_row.saturating_sub(excerpt_end_row);
17460                if lines_below >= lines_to_expand {
17461                    scroll = Some(
17462                        current_scroll_position
17463                            + gpui::Point::new(0.0, lines_to_expand as ScrollOffset),
17464                    );
17465                }
17466            }
17467        }
17468        if direction == ExpandExcerptDirection::Up
17469            && self
17470                .buffer
17471                .read(cx)
17472                .snapshot(cx)
17473                .excerpt_before(excerpt)
17474                .is_none()
17475        {
17476            scroll = Some(current_scroll_position);
17477        }
17478
17479        self.buffer.update(cx, |buffer, cx| {
17480            buffer.expand_excerpts([excerpt], lines_to_expand, direction, cx)
17481        });
17482
17483        if let Some(new_scroll_position) = scroll {
17484            self.set_scroll_position(new_scroll_position, window, cx);
17485        }
17486    }
17487
17488    pub fn go_to_singleton_buffer_point(
17489        &mut self,
17490        point: Point,
17491        window: &mut Window,
17492        cx: &mut Context<Self>,
17493    ) {
17494        self.go_to_singleton_buffer_range(point..point, window, cx);
17495    }
17496
17497    pub fn go_to_singleton_buffer_range(
17498        &mut self,
17499        range: Range<Point>,
17500        window: &mut Window,
17501        cx: &mut Context<Self>,
17502    ) {
17503        let multibuffer = self.buffer().read(cx);
17504        let Some(buffer) = multibuffer.as_singleton() else {
17505            return;
17506        };
17507        let Some(start) = multibuffer.buffer_point_to_anchor(&buffer, range.start, cx) else {
17508            return;
17509        };
17510        let Some(end) = multibuffer.buffer_point_to_anchor(&buffer, range.end, cx) else {
17511            return;
17512        };
17513        self.change_selections(
17514            SelectionEffects::default().nav_history(true),
17515            window,
17516            cx,
17517            |s| s.select_anchor_ranges([start..end]),
17518        );
17519    }
17520
17521    pub fn go_to_diagnostic(
17522        &mut self,
17523        action: &GoToDiagnostic,
17524        window: &mut Window,
17525        cx: &mut Context<Self>,
17526    ) {
17527        if !self.diagnostics_enabled() {
17528            return;
17529        }
17530        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
17531        self.go_to_diagnostic_impl(Direction::Next, action.severity, window, cx)
17532    }
17533
17534    pub fn go_to_prev_diagnostic(
17535        &mut self,
17536        action: &GoToPreviousDiagnostic,
17537        window: &mut Window,
17538        cx: &mut Context<Self>,
17539    ) {
17540        if !self.diagnostics_enabled() {
17541            return;
17542        }
17543        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
17544        self.go_to_diagnostic_impl(Direction::Prev, action.severity, window, cx)
17545    }
17546
17547    pub fn go_to_diagnostic_impl(
17548        &mut self,
17549        direction: Direction,
17550        severity: GoToDiagnosticSeverityFilter,
17551        window: &mut Window,
17552        cx: &mut Context<Self>,
17553    ) {
17554        let buffer = self.buffer.read(cx).snapshot(cx);
17555        let selection = self
17556            .selections
17557            .newest::<MultiBufferOffset>(&self.display_snapshot(cx));
17558
17559        let mut active_group_id = None;
17560        if let ActiveDiagnostic::Group(active_group) = &self.active_diagnostics
17561            && active_group.active_range.start.to_offset(&buffer) == selection.start
17562        {
17563            active_group_id = Some(active_group.group_id);
17564        }
17565
17566        fn filtered<'a>(
17567            severity: GoToDiagnosticSeverityFilter,
17568            diagnostics: impl Iterator<Item = DiagnosticEntryRef<'a, MultiBufferOffset>>,
17569        ) -> impl Iterator<Item = DiagnosticEntryRef<'a, MultiBufferOffset>> {
17570            diagnostics
17571                .filter(move |entry| severity.matches(entry.diagnostic.severity))
17572                .filter(|entry| entry.range.start != entry.range.end)
17573                .filter(|entry| !entry.diagnostic.is_unnecessary)
17574        }
17575
17576        let before = filtered(
17577            severity,
17578            buffer
17579                .diagnostics_in_range(MultiBufferOffset(0)..selection.start)
17580                .filter(|entry| entry.range.start <= selection.start),
17581        );
17582        let after = filtered(
17583            severity,
17584            buffer
17585                .diagnostics_in_range(selection.start..buffer.len())
17586                .filter(|entry| entry.range.start >= selection.start),
17587        );
17588
17589        let mut found: Option<DiagnosticEntryRef<MultiBufferOffset>> = None;
17590        if direction == Direction::Prev {
17591            'outer: for prev_diagnostics in [before.collect::<Vec<_>>(), after.collect::<Vec<_>>()]
17592            {
17593                for diagnostic in prev_diagnostics.into_iter().rev() {
17594                    if diagnostic.range.start != selection.start
17595                        || active_group_id
17596                            .is_some_and(|active| diagnostic.diagnostic.group_id < active)
17597                    {
17598                        found = Some(diagnostic);
17599                        break 'outer;
17600                    }
17601                }
17602            }
17603        } else {
17604            for diagnostic in after.chain(before) {
17605                if diagnostic.range.start != selection.start
17606                    || active_group_id.is_some_and(|active| diagnostic.diagnostic.group_id > active)
17607                {
17608                    found = Some(diagnostic);
17609                    break;
17610                }
17611            }
17612        }
17613        let Some(next_diagnostic) = found else {
17614            return;
17615        };
17616
17617        let next_diagnostic_start = buffer.anchor_after(next_diagnostic.range.start);
17618        let Some(buffer_id) = buffer.buffer_id_for_anchor(next_diagnostic_start) else {
17619            return;
17620        };
17621        let snapshot = self.snapshot(window, cx);
17622        if snapshot.intersects_fold(next_diagnostic.range.start) {
17623            self.unfold_ranges(
17624                std::slice::from_ref(&next_diagnostic.range),
17625                true,
17626                false,
17627                cx,
17628            );
17629        }
17630        self.change_selections(Default::default(), window, cx, |s| {
17631            s.select_ranges(vec![
17632                next_diagnostic.range.start..next_diagnostic.range.start,
17633            ])
17634        });
17635        self.activate_diagnostics(buffer_id, next_diagnostic, window, cx);
17636        self.refresh_edit_prediction(false, true, window, cx);
17637    }
17638
17639    pub fn go_to_next_hunk(&mut self, _: &GoToHunk, window: &mut Window, cx: &mut Context<Self>) {
17640        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
17641        let snapshot = self.snapshot(window, cx);
17642        let selection = self.selections.newest::<Point>(&self.display_snapshot(cx));
17643        self.go_to_hunk_before_or_after_position(
17644            &snapshot,
17645            selection.head(),
17646            Direction::Next,
17647            window,
17648            cx,
17649        );
17650    }
17651
17652    pub fn go_to_hunk_before_or_after_position(
17653        &mut self,
17654        snapshot: &EditorSnapshot,
17655        position: Point,
17656        direction: Direction,
17657        window: &mut Window,
17658        cx: &mut Context<Editor>,
17659    ) {
17660        let row = if direction == Direction::Next {
17661            self.hunk_after_position(snapshot, position)
17662                .map(|hunk| hunk.row_range.start)
17663        } else {
17664            self.hunk_before_position(snapshot, position)
17665        };
17666
17667        if let Some(row) = row {
17668            let destination = Point::new(row.0, 0);
17669            let autoscroll = Autoscroll::center();
17670
17671            self.unfold_ranges(&[destination..destination], false, false, cx);
17672            self.change_selections(SelectionEffects::scroll(autoscroll), window, cx, |s| {
17673                s.select_ranges([destination..destination]);
17674            });
17675        }
17676    }
17677
17678    fn hunk_after_position(
17679        &mut self,
17680        snapshot: &EditorSnapshot,
17681        position: Point,
17682    ) -> Option<MultiBufferDiffHunk> {
17683        snapshot
17684            .buffer_snapshot()
17685            .diff_hunks_in_range(position..snapshot.buffer_snapshot().max_point())
17686            .find(|hunk| hunk.row_range.start.0 > position.row)
17687            .or_else(|| {
17688                snapshot
17689                    .buffer_snapshot()
17690                    .diff_hunks_in_range(Point::zero()..position)
17691                    .find(|hunk| hunk.row_range.end.0 < position.row)
17692            })
17693    }
17694
17695    fn go_to_prev_hunk(
17696        &mut self,
17697        _: &GoToPreviousHunk,
17698        window: &mut Window,
17699        cx: &mut Context<Self>,
17700    ) {
17701        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
17702        let snapshot = self.snapshot(window, cx);
17703        let selection = self.selections.newest::<Point>(&snapshot.display_snapshot);
17704        self.go_to_hunk_before_or_after_position(
17705            &snapshot,
17706            selection.head(),
17707            Direction::Prev,
17708            window,
17709            cx,
17710        );
17711    }
17712
17713    fn hunk_before_position(
17714        &mut self,
17715        snapshot: &EditorSnapshot,
17716        position: Point,
17717    ) -> Option<MultiBufferRow> {
17718        snapshot
17719            .buffer_snapshot()
17720            .diff_hunk_before(position)
17721            .or_else(|| snapshot.buffer_snapshot().diff_hunk_before(Point::MAX))
17722    }
17723
17724    fn go_to_next_change(
17725        &mut self,
17726        _: &GoToNextChange,
17727        window: &mut Window,
17728        cx: &mut Context<Self>,
17729    ) {
17730        if let Some(selections) = self
17731            .change_list
17732            .next_change(1, Direction::Next)
17733            .map(|s| s.to_vec())
17734        {
17735            self.change_selections(Default::default(), window, cx, |s| {
17736                let map = s.display_snapshot();
17737                s.select_display_ranges(selections.iter().map(|a| {
17738                    let point = a.to_display_point(&map);
17739                    point..point
17740                }))
17741            })
17742        }
17743    }
17744
17745    fn go_to_previous_change(
17746        &mut self,
17747        _: &GoToPreviousChange,
17748        window: &mut Window,
17749        cx: &mut Context<Self>,
17750    ) {
17751        if let Some(selections) = self
17752            .change_list
17753            .next_change(1, Direction::Prev)
17754            .map(|s| s.to_vec())
17755        {
17756            self.change_selections(Default::default(), window, cx, |s| {
17757                let map = s.display_snapshot();
17758                s.select_display_ranges(selections.iter().map(|a| {
17759                    let point = a.to_display_point(&map);
17760                    point..point
17761                }))
17762            })
17763        }
17764    }
17765
17766    pub fn go_to_next_document_highlight(
17767        &mut self,
17768        _: &GoToNextDocumentHighlight,
17769        window: &mut Window,
17770        cx: &mut Context<Self>,
17771    ) {
17772        self.go_to_document_highlight_before_or_after_position(Direction::Next, window, cx);
17773    }
17774
17775    pub fn go_to_prev_document_highlight(
17776        &mut self,
17777        _: &GoToPreviousDocumentHighlight,
17778        window: &mut Window,
17779        cx: &mut Context<Self>,
17780    ) {
17781        self.go_to_document_highlight_before_or_after_position(Direction::Prev, window, cx);
17782    }
17783
17784    pub fn go_to_document_highlight_before_or_after_position(
17785        &mut self,
17786        direction: Direction,
17787        window: &mut Window,
17788        cx: &mut Context<Editor>,
17789    ) {
17790        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
17791        let snapshot = self.snapshot(window, cx);
17792        let buffer = &snapshot.buffer_snapshot();
17793        let position = self
17794            .selections
17795            .newest::<Point>(&snapshot.display_snapshot)
17796            .head();
17797        let anchor_position = buffer.anchor_after(position);
17798
17799        // Get all document highlights (both read and write)
17800        let mut all_highlights = Vec::new();
17801
17802        if let Some((_, read_highlights)) = self
17803            .background_highlights
17804            .get(&HighlightKey::DocumentHighlightRead)
17805        {
17806            all_highlights.extend(read_highlights.iter());
17807        }
17808
17809        if let Some((_, write_highlights)) = self
17810            .background_highlights
17811            .get(&HighlightKey::DocumentHighlightWrite)
17812        {
17813            all_highlights.extend(write_highlights.iter());
17814        }
17815
17816        if all_highlights.is_empty() {
17817            return;
17818        }
17819
17820        // Sort highlights by position
17821        all_highlights.sort_by(|a, b| a.start.cmp(&b.start, buffer));
17822
17823        let target_highlight = match direction {
17824            Direction::Next => {
17825                // Find the first highlight after the current position
17826                all_highlights
17827                    .iter()
17828                    .find(|highlight| highlight.start.cmp(&anchor_position, buffer).is_gt())
17829            }
17830            Direction::Prev => {
17831                // Find the last highlight before the current position
17832                all_highlights
17833                    .iter()
17834                    .rev()
17835                    .find(|highlight| highlight.end.cmp(&anchor_position, buffer).is_lt())
17836            }
17837        };
17838
17839        if let Some(highlight) = target_highlight {
17840            let destination = highlight.start.to_point(buffer);
17841            let autoscroll = Autoscroll::center();
17842
17843            self.unfold_ranges(&[destination..destination], false, false, cx);
17844            self.change_selections(SelectionEffects::scroll(autoscroll), window, cx, |s| {
17845                s.select_ranges([destination..destination]);
17846            });
17847        }
17848    }
17849
17850    fn go_to_line<T: 'static>(
17851        &mut self,
17852        position: Anchor,
17853        highlight_color: Option<Hsla>,
17854        window: &mut Window,
17855        cx: &mut Context<Self>,
17856    ) {
17857        let snapshot = self.snapshot(window, cx).display_snapshot;
17858        let position = position.to_point(&snapshot.buffer_snapshot());
17859        let start = snapshot
17860            .buffer_snapshot()
17861            .clip_point(Point::new(position.row, 0), Bias::Left);
17862        let end = start + Point::new(1, 0);
17863        let start = snapshot.buffer_snapshot().anchor_before(start);
17864        let end = snapshot.buffer_snapshot().anchor_before(end);
17865
17866        self.highlight_rows::<T>(
17867            start..end,
17868            highlight_color
17869                .unwrap_or_else(|| cx.theme().colors().editor_highlighted_line_background),
17870            Default::default(),
17871            cx,
17872        );
17873
17874        if self.buffer.read(cx).is_singleton() {
17875            self.request_autoscroll(Autoscroll::center().for_anchor(start), cx);
17876        }
17877    }
17878
17879    pub fn go_to_definition(
17880        &mut self,
17881        _: &GoToDefinition,
17882        window: &mut Window,
17883        cx: &mut Context<Self>,
17884    ) -> Task<Result<Navigated>> {
17885        let definition =
17886            self.go_to_definition_of_kind(GotoDefinitionKind::Symbol, false, window, cx);
17887        let fallback_strategy = EditorSettings::get_global(cx).go_to_definition_fallback;
17888        cx.spawn_in(window, async move |editor, cx| {
17889            if definition.await? == Navigated::Yes {
17890                return Ok(Navigated::Yes);
17891            }
17892            match fallback_strategy {
17893                GoToDefinitionFallback::None => Ok(Navigated::No),
17894                GoToDefinitionFallback::FindAllReferences => {
17895                    match editor.update_in(cx, |editor, window, cx| {
17896                        editor.find_all_references(&FindAllReferences::default(), window, cx)
17897                    })? {
17898                        Some(references) => references.await,
17899                        None => Ok(Navigated::No),
17900                    }
17901                }
17902            }
17903        })
17904    }
17905
17906    pub fn go_to_declaration(
17907        &mut self,
17908        _: &GoToDeclaration,
17909        window: &mut Window,
17910        cx: &mut Context<Self>,
17911    ) -> Task<Result<Navigated>> {
17912        self.go_to_definition_of_kind(GotoDefinitionKind::Declaration, false, window, cx)
17913    }
17914
17915    pub fn go_to_declaration_split(
17916        &mut self,
17917        _: &GoToDeclaration,
17918        window: &mut Window,
17919        cx: &mut Context<Self>,
17920    ) -> Task<Result<Navigated>> {
17921        self.go_to_definition_of_kind(GotoDefinitionKind::Declaration, true, window, cx)
17922    }
17923
17924    pub fn go_to_implementation(
17925        &mut self,
17926        _: &GoToImplementation,
17927        window: &mut Window,
17928        cx: &mut Context<Self>,
17929    ) -> Task<Result<Navigated>> {
17930        self.go_to_definition_of_kind(GotoDefinitionKind::Implementation, false, window, cx)
17931    }
17932
17933    pub fn go_to_implementation_split(
17934        &mut self,
17935        _: &GoToImplementationSplit,
17936        window: &mut Window,
17937        cx: &mut Context<Self>,
17938    ) -> Task<Result<Navigated>> {
17939        self.go_to_definition_of_kind(GotoDefinitionKind::Implementation, true, window, cx)
17940    }
17941
17942    pub fn go_to_type_definition(
17943        &mut self,
17944        _: &GoToTypeDefinition,
17945        window: &mut Window,
17946        cx: &mut Context<Self>,
17947    ) -> Task<Result<Navigated>> {
17948        self.go_to_definition_of_kind(GotoDefinitionKind::Type, false, window, cx)
17949    }
17950
17951    pub fn go_to_definition_split(
17952        &mut self,
17953        _: &GoToDefinitionSplit,
17954        window: &mut Window,
17955        cx: &mut Context<Self>,
17956    ) -> Task<Result<Navigated>> {
17957        self.go_to_definition_of_kind(GotoDefinitionKind::Symbol, true, window, cx)
17958    }
17959
17960    pub fn go_to_type_definition_split(
17961        &mut self,
17962        _: &GoToTypeDefinitionSplit,
17963        window: &mut Window,
17964        cx: &mut Context<Self>,
17965    ) -> Task<Result<Navigated>> {
17966        self.go_to_definition_of_kind(GotoDefinitionKind::Type, true, window, cx)
17967    }
17968
17969    fn go_to_definition_of_kind(
17970        &mut self,
17971        kind: GotoDefinitionKind,
17972        split: bool,
17973        window: &mut Window,
17974        cx: &mut Context<Self>,
17975    ) -> Task<Result<Navigated>> {
17976        let Some(provider) = self.semantics_provider.clone() else {
17977            return Task::ready(Ok(Navigated::No));
17978        };
17979        let head = self
17980            .selections
17981            .newest::<MultiBufferOffset>(&self.display_snapshot(cx))
17982            .head();
17983        let buffer = self.buffer.read(cx);
17984        let Some((buffer, head)) = buffer.text_anchor_for_position(head, cx) else {
17985            return Task::ready(Ok(Navigated::No));
17986        };
17987        let Some(definitions) = provider.definitions(&buffer, head, kind, cx) else {
17988            return Task::ready(Ok(Navigated::No));
17989        };
17990
17991        let nav_entry = self.navigation_entry(self.selections.newest_anchor().head(), cx);
17992
17993        cx.spawn_in(window, async move |editor, cx| {
17994            let Some(definitions) = definitions.await? else {
17995                return Ok(Navigated::No);
17996            };
17997            let navigated = editor
17998                .update_in(cx, |editor, window, cx| {
17999                    editor.navigate_to_hover_links(
18000                        Some(kind),
18001                        definitions
18002                            .into_iter()
18003                            .filter(|location| {
18004                                hover_links::exclude_link_to_position(&buffer, &head, location, cx)
18005                            })
18006                            .map(HoverLink::Text)
18007                            .collect::<Vec<_>>(),
18008                        nav_entry,
18009                        split,
18010                        window,
18011                        cx,
18012                    )
18013                })?
18014                .await?;
18015            anyhow::Ok(navigated)
18016        })
18017    }
18018
18019    pub fn open_url(&mut self, _: &OpenUrl, window: &mut Window, cx: &mut Context<Self>) {
18020        let selection = self.selections.newest_anchor();
18021        let head = selection.head();
18022        let tail = selection.tail();
18023
18024        let Some((buffer, start_position)) =
18025            self.buffer.read(cx).text_anchor_for_position(head, cx)
18026        else {
18027            return;
18028        };
18029
18030        let end_position = if head != tail {
18031            let Some((_, pos)) = self.buffer.read(cx).text_anchor_for_position(tail, cx) else {
18032                return;
18033            };
18034            Some(pos)
18035        } else {
18036            None
18037        };
18038
18039        let url_finder = cx.spawn_in(window, async move |_editor, cx| {
18040            let url = if let Some(end_pos) = end_position {
18041                find_url_from_range(&buffer, start_position..end_pos, cx.clone())
18042            } else {
18043                find_url(&buffer, start_position, cx.clone()).map(|(_, url)| url)
18044            };
18045
18046            if let Some(url) = url {
18047                cx.update(|window, cx| {
18048                    if parse_zed_link(&url, cx).is_some() {
18049                        window.dispatch_action(Box::new(zed_actions::OpenZedUrl { url }), cx);
18050                    } else {
18051                        cx.open_url(&url);
18052                    }
18053                })?;
18054            }
18055
18056            anyhow::Ok(())
18057        });
18058
18059        url_finder.detach();
18060    }
18061
18062    pub fn open_selected_filename(
18063        &mut self,
18064        _: &OpenSelectedFilename,
18065        window: &mut Window,
18066        cx: &mut Context<Self>,
18067    ) {
18068        let Some(workspace) = self.workspace() else {
18069            return;
18070        };
18071
18072        let position = self.selections.newest_anchor().head();
18073
18074        let Some((buffer, buffer_position)) =
18075            self.buffer.read(cx).text_anchor_for_position(position, cx)
18076        else {
18077            return;
18078        };
18079
18080        let project = self.project.clone();
18081
18082        cx.spawn_in(window, async move |_, cx| {
18083            let result = find_file(&buffer, project, buffer_position, cx).await;
18084
18085            if let Some((_, path)) = result {
18086                workspace
18087                    .update_in(cx, |workspace, window, cx| {
18088                        workspace.open_resolved_path(path, window, cx)
18089                    })?
18090                    .await?;
18091            }
18092            anyhow::Ok(())
18093        })
18094        .detach();
18095    }
18096
18097    pub(crate) fn navigate_to_hover_links(
18098        &mut self,
18099        kind: Option<GotoDefinitionKind>,
18100        definitions: Vec<HoverLink>,
18101        origin: Option<NavigationEntry>,
18102        split: bool,
18103        window: &mut Window,
18104        cx: &mut Context<Editor>,
18105    ) -> Task<Result<Navigated>> {
18106        // Separate out url and file links, we can only handle one of them at most or an arbitrary number of locations
18107        let mut first_url_or_file = None;
18108        let definitions: Vec<_> = definitions
18109            .into_iter()
18110            .filter_map(|def| match def {
18111                HoverLink::Text(link) => Some(Task::ready(anyhow::Ok(Some(link.target)))),
18112                HoverLink::InlayHint(lsp_location, server_id) => {
18113                    let computation =
18114                        self.compute_target_location(lsp_location, server_id, window, cx);
18115                    Some(cx.background_spawn(computation))
18116                }
18117                HoverLink::Url(url) => {
18118                    first_url_or_file = Some(Either::Left(url));
18119                    None
18120                }
18121                HoverLink::File(path) => {
18122                    first_url_or_file = Some(Either::Right(path));
18123                    None
18124                }
18125            })
18126            .collect();
18127
18128        let workspace = self.workspace();
18129
18130        cx.spawn_in(window, async move |editor, cx| {
18131            let locations: Vec<Location> = future::join_all(definitions)
18132                .await
18133                .into_iter()
18134                .filter_map(|location| location.transpose())
18135                .collect::<Result<_>>()
18136                .context("location tasks")?;
18137            let mut locations = cx.update(|_, cx| {
18138                locations
18139                    .into_iter()
18140                    .map(|location| {
18141                        let buffer = location.buffer.read(cx);
18142                        (location.buffer, location.range.to_point(buffer))
18143                    })
18144                    .into_group_map()
18145            })?;
18146            let mut num_locations = 0;
18147            for ranges in locations.values_mut() {
18148                ranges.sort_by_key(|range| (range.start, Reverse(range.end)));
18149                ranges.dedup();
18150                num_locations += ranges.len();
18151            }
18152
18153            if num_locations > 1 {
18154                let tab_kind = match kind {
18155                    Some(GotoDefinitionKind::Implementation) => "Implementations",
18156                    Some(GotoDefinitionKind::Symbol) | None => "Definitions",
18157                    Some(GotoDefinitionKind::Declaration) => "Declarations",
18158                    Some(GotoDefinitionKind::Type) => "Types",
18159                };
18160                let title = editor
18161                    .update_in(cx, |_, _, cx| {
18162                        let target = locations
18163                            .iter()
18164                            .flat_map(|(k, v)| iter::repeat(k.clone()).zip(v))
18165                            .map(|(buffer, location)| {
18166                                buffer
18167                                    .read(cx)
18168                                    .text_for_range(location.clone())
18169                                    .collect::<String>()
18170                            })
18171                            .filter(|text| !text.contains('\n'))
18172                            .unique()
18173                            .take(3)
18174                            .join(", ");
18175                        if target.is_empty() {
18176                            tab_kind.to_owned()
18177                        } else {
18178                            format!("{tab_kind} for {target}")
18179                        }
18180                    })
18181                    .context("buffer title")?;
18182
18183                let Some(workspace) = workspace else {
18184                    return Ok(Navigated::No);
18185                };
18186
18187                let opened = workspace
18188                    .update_in(cx, |workspace, window, cx| {
18189                        let allow_preview = PreviewTabsSettings::get_global(cx)
18190                            .enable_preview_multibuffer_from_code_navigation;
18191                        if let Some((target_editor, target_pane)) =
18192                            Self::open_locations_in_multibuffer(
18193                                workspace,
18194                                locations,
18195                                title,
18196                                split,
18197                                allow_preview,
18198                                MultibufferSelectionMode::First,
18199                                window,
18200                                cx,
18201                            )
18202                        {
18203                            // We create our own nav history instead of using
18204                            // `target_editor.nav_history` because `nav_history`
18205                            // seems to be populated asynchronously when an item
18206                            // is added to a pane
18207                            let mut nav_history = target_pane
18208                                .update(cx, |pane, _| pane.nav_history_for_item(&target_editor));
18209                            target_editor.update(cx, |editor, cx| {
18210                                let nav_data = editor
18211                                    .navigation_data(editor.selections.newest_anchor().head(), cx);
18212                                let target =
18213                                    Some(nav_history.navigation_entry(Some(
18214                                        Arc::new(nav_data) as Arc<dyn Any + Send + Sync>
18215                                    )));
18216                                nav_history.push_tag(origin, target);
18217                            })
18218                        }
18219                    })
18220                    .is_ok();
18221
18222                anyhow::Ok(Navigated::from_bool(opened))
18223            } else if num_locations == 0 {
18224                // If there is one url or file, open it directly
18225                match first_url_or_file {
18226                    Some(Either::Left(url)) => {
18227                        cx.update(|window, cx| {
18228                            if parse_zed_link(&url, cx).is_some() {
18229                                window
18230                                    .dispatch_action(Box::new(zed_actions::OpenZedUrl { url }), cx);
18231                            } else {
18232                                cx.open_url(&url);
18233                            }
18234                        })?;
18235                        Ok(Navigated::Yes)
18236                    }
18237                    Some(Either::Right(path)) => {
18238                        // TODO(andrew): respect preview tab settings
18239                        //               `enable_keep_preview_on_code_navigation` and
18240                        //               `enable_preview_file_from_code_navigation`
18241                        let Some(workspace) = workspace else {
18242                            return Ok(Navigated::No);
18243                        };
18244                        workspace
18245                            .update_in(cx, |workspace, window, cx| {
18246                                workspace.open_resolved_path(path, window, cx)
18247                            })?
18248                            .await?;
18249                        Ok(Navigated::Yes)
18250                    }
18251                    None => Ok(Navigated::No),
18252                }
18253            } else {
18254                let (target_buffer, target_ranges) = locations.into_iter().next().unwrap();
18255                let target_range = target_ranges.first().unwrap().clone();
18256
18257                editor.update_in(cx, |editor, window, cx| {
18258                    let range = editor.range_for_match(&target_range);
18259                    let range = collapse_multiline_range(range);
18260
18261                    if !split
18262                        && Some(&target_buffer) == editor.buffer.read(cx).as_singleton().as_ref()
18263                    {
18264                        editor.go_to_singleton_buffer_range(range, window, cx);
18265
18266                        let target =
18267                            editor.navigation_entry(editor.selections.newest_anchor().head(), cx);
18268                        if let Some(mut nav_history) = editor.nav_history.clone() {
18269                            nav_history.push_tag(origin, target);
18270                        }
18271                    } else {
18272                        let Some(workspace) = workspace else {
18273                            return Navigated::No;
18274                        };
18275                        let pane = workspace.read(cx).active_pane().clone();
18276                        window.defer(cx, move |window, cx| {
18277                            let (target_editor, target_pane): (Entity<Self>, Entity<Pane>) =
18278                                workspace.update(cx, |workspace, cx| {
18279                                    let pane = if split {
18280                                        workspace.adjacent_pane(window, cx)
18281                                    } else {
18282                                        workspace.active_pane().clone()
18283                                    };
18284
18285                                    let preview_tabs_settings = PreviewTabsSettings::get_global(cx);
18286                                    let keep_old_preview = preview_tabs_settings
18287                                        .enable_keep_preview_on_code_navigation;
18288                                    let allow_new_preview = preview_tabs_settings
18289                                        .enable_preview_file_from_code_navigation;
18290
18291                                    let editor = workspace.open_project_item(
18292                                        pane.clone(),
18293                                        target_buffer.clone(),
18294                                        true,
18295                                        true,
18296                                        keep_old_preview,
18297                                        allow_new_preview,
18298                                        window,
18299                                        cx,
18300                                    );
18301                                    (editor, pane)
18302                                });
18303                            // We create our own nav history instead of using
18304                            // `target_editor.nav_history` because `nav_history`
18305                            // seems to be populated asynchronously when an item
18306                            // is added to a pane
18307                            let mut nav_history = target_pane
18308                                .update(cx, |pane, _| pane.nav_history_for_item(&target_editor));
18309                            target_editor.update(cx, |target_editor, cx| {
18310                                // When selecting a definition in a different buffer, disable the nav history
18311                                // to avoid creating a history entry at the previous cursor location.
18312                                pane.update(cx, |pane, _| pane.disable_history());
18313                                target_editor.go_to_singleton_buffer_range(range, window, cx);
18314
18315                                let nav_data = target_editor.navigation_data(
18316                                    target_editor.selections.newest_anchor().head(),
18317                                    cx,
18318                                );
18319                                let target =
18320                                    Some(nav_history.navigation_entry(Some(
18321                                        Arc::new(nav_data) as Arc<dyn Any + Send + Sync>
18322                                    )));
18323                                nav_history.push_tag(origin, target);
18324                                pane.update(cx, |pane, _| pane.enable_history());
18325                            });
18326                        });
18327                    }
18328                    Navigated::Yes
18329                })
18330            }
18331        })
18332    }
18333
18334    fn compute_target_location(
18335        &self,
18336        lsp_location: lsp::Location,
18337        server_id: LanguageServerId,
18338        window: &mut Window,
18339        cx: &mut Context<Self>,
18340    ) -> Task<anyhow::Result<Option<Location>>> {
18341        let Some(project) = self.project.clone() else {
18342            return Task::ready(Ok(None));
18343        };
18344
18345        cx.spawn_in(window, async move |editor, cx| {
18346            let location_task = editor.update(cx, |_, cx| {
18347                project.update(cx, |project, cx| {
18348                    project.open_local_buffer_via_lsp(lsp_location.uri.clone(), server_id, cx)
18349                })
18350            })?;
18351            let location = Some({
18352                let target_buffer_handle = location_task.await.context("open local buffer")?;
18353                let range = target_buffer_handle.read_with(cx, |target_buffer, _| {
18354                    let target_start = target_buffer
18355                        .clip_point_utf16(point_from_lsp(lsp_location.range.start), Bias::Left);
18356                    let target_end = target_buffer
18357                        .clip_point_utf16(point_from_lsp(lsp_location.range.end), Bias::Left);
18358                    target_buffer.anchor_after(target_start)
18359                        ..target_buffer.anchor_before(target_end)
18360                });
18361                Location {
18362                    buffer: target_buffer_handle,
18363                    range,
18364                }
18365            });
18366            Ok(location)
18367        })
18368    }
18369
18370    fn go_to_next_reference(
18371        &mut self,
18372        _: &GoToNextReference,
18373        window: &mut Window,
18374        cx: &mut Context<Self>,
18375    ) {
18376        let task = self.go_to_reference_before_or_after_position(Direction::Next, 1, window, cx);
18377        if let Some(task) = task {
18378            task.detach();
18379        };
18380    }
18381
18382    fn go_to_prev_reference(
18383        &mut self,
18384        _: &GoToPreviousReference,
18385        window: &mut Window,
18386        cx: &mut Context<Self>,
18387    ) {
18388        let task = self.go_to_reference_before_or_after_position(Direction::Prev, 1, window, cx);
18389        if let Some(task) = task {
18390            task.detach();
18391        };
18392    }
18393
18394    pub fn go_to_reference_before_or_after_position(
18395        &mut self,
18396        direction: Direction,
18397        count: usize,
18398        window: &mut Window,
18399        cx: &mut Context<Self>,
18400    ) -> Option<Task<Result<()>>> {
18401        let selection = self.selections.newest_anchor();
18402        let head = selection.head();
18403
18404        let multi_buffer = self.buffer.read(cx);
18405
18406        let (buffer, text_head) = multi_buffer.text_anchor_for_position(head, cx)?;
18407        let workspace = self.workspace()?;
18408        let project = workspace.read(cx).project().clone();
18409        let references =
18410            project.update(cx, |project, cx| project.references(&buffer, text_head, cx));
18411        Some(cx.spawn_in(window, async move |editor, cx| -> Result<()> {
18412            let Some(locations) = references.await? else {
18413                return Ok(());
18414            };
18415
18416            if locations.is_empty() {
18417                // totally normal - the cursor may be on something which is not
18418                // a symbol (e.g. a keyword)
18419                log::info!("no references found under cursor");
18420                return Ok(());
18421            }
18422
18423            let multi_buffer = editor.read_with(cx, |editor, _| editor.buffer().clone())?;
18424
18425            let (locations, current_location_index) =
18426                multi_buffer.update(cx, |multi_buffer, cx| {
18427                    let mut locations = locations
18428                        .into_iter()
18429                        .filter_map(|loc| {
18430                            let start = multi_buffer.buffer_anchor_to_anchor(
18431                                &loc.buffer,
18432                                loc.range.start,
18433                                cx,
18434                            )?;
18435                            let end = multi_buffer.buffer_anchor_to_anchor(
18436                                &loc.buffer,
18437                                loc.range.end,
18438                                cx,
18439                            )?;
18440                            Some(start..end)
18441                        })
18442                        .collect::<Vec<_>>();
18443
18444                    let multi_buffer_snapshot = multi_buffer.snapshot(cx);
18445                    // There is an O(n) implementation, but given this list will be
18446                    // small (usually <100 items), the extra O(log(n)) factor isn't
18447                    // worth the (surprisingly large amount of) extra complexity.
18448                    locations
18449                        .sort_unstable_by(|l, r| l.start.cmp(&r.start, &multi_buffer_snapshot));
18450
18451                    let head_offset = head.to_offset(&multi_buffer_snapshot);
18452
18453                    let current_location_index = locations.iter().position(|loc| {
18454                        loc.start.to_offset(&multi_buffer_snapshot) <= head_offset
18455                            && loc.end.to_offset(&multi_buffer_snapshot) >= head_offset
18456                    });
18457
18458                    (locations, current_location_index)
18459                });
18460
18461            let Some(current_location_index) = current_location_index else {
18462                // This indicates something has gone wrong, because we already
18463                // handle the "no references" case above
18464                log::error!(
18465                    "failed to find current reference under cursor. Total references: {}",
18466                    locations.len()
18467                );
18468                return Ok(());
18469            };
18470
18471            let destination_location_index = match direction {
18472                Direction::Next => (current_location_index + count) % locations.len(),
18473                Direction::Prev => {
18474                    (current_location_index + locations.len() - count % locations.len())
18475                        % locations.len()
18476                }
18477            };
18478
18479            // TODO(cameron): is this needed?
18480            // the thinking is to avoid "jumping to the current location" (avoid
18481            // polluting "jumplist" in vim terms)
18482            if current_location_index == destination_location_index {
18483                return Ok(());
18484            }
18485
18486            let Range { start, end } = locations[destination_location_index];
18487
18488            editor.update_in(cx, |editor, window, cx| {
18489                let effects = SelectionEffects::default();
18490
18491                editor.unfold_ranges(&[start..end], false, false, cx);
18492                editor.change_selections(effects, window, cx, |s| {
18493                    s.select_ranges([start..start]);
18494                });
18495            })?;
18496
18497            Ok(())
18498        }))
18499    }
18500
18501    pub fn find_all_references(
18502        &mut self,
18503        action: &FindAllReferences,
18504        window: &mut Window,
18505        cx: &mut Context<Self>,
18506    ) -> Option<Task<Result<Navigated>>> {
18507        let always_open_multibuffer = action.always_open_multibuffer;
18508        let selection = self.selections.newest_anchor();
18509        let multi_buffer = self.buffer.read(cx);
18510        let multi_buffer_snapshot = multi_buffer.snapshot(cx);
18511        let selection_offset = selection.map(|anchor| anchor.to_offset(&multi_buffer_snapshot));
18512        let selection_point = selection.map(|anchor| anchor.to_point(&multi_buffer_snapshot));
18513        let head = selection_offset.head();
18514
18515        let head_anchor = multi_buffer_snapshot.anchor_at(
18516            head,
18517            if head < selection_offset.tail() {
18518                Bias::Right
18519            } else {
18520                Bias::Left
18521            },
18522        );
18523
18524        match self
18525            .find_all_references_task_sources
18526            .binary_search_by(|anchor| anchor.cmp(&head_anchor, &multi_buffer_snapshot))
18527        {
18528            Ok(_) => {
18529                log::info!(
18530                    "Ignoring repeated FindAllReferences invocation with the position of already running task"
18531                );
18532                return None;
18533            }
18534            Err(i) => {
18535                self.find_all_references_task_sources.insert(i, head_anchor);
18536            }
18537        }
18538
18539        let (buffer, head) = multi_buffer.text_anchor_for_position(head, cx)?;
18540        let workspace = self.workspace()?;
18541        let project = workspace.read(cx).project().clone();
18542        let references = project.update(cx, |project, cx| project.references(&buffer, head, cx));
18543        Some(cx.spawn_in(window, async move |editor, cx| {
18544            let _cleanup = cx.on_drop(&editor, move |editor, _| {
18545                if let Ok(i) = editor
18546                    .find_all_references_task_sources
18547                    .binary_search_by(|anchor| anchor.cmp(&head_anchor, &multi_buffer_snapshot))
18548                {
18549                    editor.find_all_references_task_sources.remove(i);
18550                }
18551            });
18552
18553            let Some(locations) = references.await? else {
18554                return anyhow::Ok(Navigated::No);
18555            };
18556            let mut locations = cx.update(|_, cx| {
18557                locations
18558                    .into_iter()
18559                    .map(|location| {
18560                        let buffer = location.buffer.read(cx);
18561                        (location.buffer, location.range.to_point(buffer))
18562                    })
18563                    // if special-casing the single-match case, remove ranges
18564                    // that intersect current selection
18565                    .filter(|(location_buffer, location)| {
18566                        if always_open_multibuffer || &buffer != location_buffer {
18567                            return true;
18568                        }
18569
18570                        !location.contains_inclusive(&selection_point.range())
18571                    })
18572                    .into_group_map()
18573            })?;
18574            if locations.is_empty() {
18575                return anyhow::Ok(Navigated::No);
18576            }
18577            for ranges in locations.values_mut() {
18578                ranges.sort_by_key(|range| (range.start, Reverse(range.end)));
18579                ranges.dedup();
18580            }
18581            let mut num_locations = 0;
18582            for ranges in locations.values_mut() {
18583                ranges.sort_by_key(|range| (range.start, Reverse(range.end)));
18584                ranges.dedup();
18585                num_locations += ranges.len();
18586            }
18587
18588            if num_locations == 1 && !always_open_multibuffer {
18589                let (target_buffer, target_ranges) = locations.into_iter().next().unwrap();
18590                let target_range = target_ranges.first().unwrap().clone();
18591
18592                return editor.update_in(cx, |editor, window, cx| {
18593                    let range = target_range.to_point(target_buffer.read(cx));
18594                    let range = editor.range_for_match(&range);
18595                    let range = range.start..range.start;
18596
18597                    if Some(&target_buffer) == editor.buffer.read(cx).as_singleton().as_ref() {
18598                        editor.go_to_singleton_buffer_range(range, window, cx);
18599                    } else {
18600                        let pane = workspace.read(cx).active_pane().clone();
18601                        window.defer(cx, move |window, cx| {
18602                            let target_editor: Entity<Self> =
18603                                workspace.update(cx, |workspace, cx| {
18604                                    let pane = workspace.active_pane().clone();
18605
18606                                    let preview_tabs_settings = PreviewTabsSettings::get_global(cx);
18607                                    let keep_old_preview = preview_tabs_settings
18608                                        .enable_keep_preview_on_code_navigation;
18609                                    let allow_new_preview = preview_tabs_settings
18610                                        .enable_preview_file_from_code_navigation;
18611
18612                                    workspace.open_project_item(
18613                                        pane,
18614                                        target_buffer.clone(),
18615                                        true,
18616                                        true,
18617                                        keep_old_preview,
18618                                        allow_new_preview,
18619                                        window,
18620                                        cx,
18621                                    )
18622                                });
18623                            target_editor.update(cx, |target_editor, cx| {
18624                                // When selecting a definition in a different buffer, disable the nav history
18625                                // to avoid creating a history entry at the previous cursor location.
18626                                pane.update(cx, |pane, _| pane.disable_history());
18627                                target_editor.go_to_singleton_buffer_range(range, window, cx);
18628                                pane.update(cx, |pane, _| pane.enable_history());
18629                            });
18630                        });
18631                    }
18632                    Navigated::No
18633                });
18634            }
18635
18636            workspace.update_in(cx, |workspace, window, cx| {
18637                let target = locations
18638                    .iter()
18639                    .flat_map(|(k, v)| iter::repeat(k.clone()).zip(v))
18640                    .map(|(buffer, location)| {
18641                        buffer
18642                            .read(cx)
18643                            .text_for_range(location.clone())
18644                            .collect::<String>()
18645                    })
18646                    .filter(|text| !text.contains('\n'))
18647                    .unique()
18648                    .take(3)
18649                    .join(", ");
18650                let title = if target.is_empty() {
18651                    "References".to_owned()
18652                } else {
18653                    format!("References to {target}")
18654                };
18655                let allow_preview = PreviewTabsSettings::get_global(cx)
18656                    .enable_preview_multibuffer_from_code_navigation;
18657                Self::open_locations_in_multibuffer(
18658                    workspace,
18659                    locations,
18660                    title,
18661                    false,
18662                    allow_preview,
18663                    MultibufferSelectionMode::First,
18664                    window,
18665                    cx,
18666                );
18667                Navigated::Yes
18668            })
18669        }))
18670    }
18671
18672    /// Opens a multibuffer with the given project locations in it.
18673    pub fn open_locations_in_multibuffer(
18674        workspace: &mut Workspace,
18675        locations: std::collections::HashMap<Entity<Buffer>, Vec<Range<Point>>>,
18676        title: String,
18677        split: bool,
18678        allow_preview: bool,
18679        multibuffer_selection_mode: MultibufferSelectionMode,
18680        window: &mut Window,
18681        cx: &mut Context<Workspace>,
18682    ) -> Option<(Entity<Editor>, Entity<Pane>)> {
18683        if locations.is_empty() {
18684            log::error!("bug: open_locations_in_multibuffer called with empty list of locations");
18685            return None;
18686        }
18687
18688        let capability = workspace.project().read(cx).capability();
18689        let mut ranges = <Vec<Range<Anchor>>>::new();
18690
18691        // a key to find existing multibuffer editors with the same set of locations
18692        // to prevent us from opening more and more multibuffer tabs for searches and the like
18693        let mut key = (title.clone(), vec![]);
18694        let excerpt_buffer = cx.new(|cx| {
18695            let key = &mut key.1;
18696            let mut multibuffer = MultiBuffer::new(capability);
18697            for (buffer, mut ranges_for_buffer) in locations {
18698                ranges_for_buffer.sort_by_key(|range| (range.start, Reverse(range.end)));
18699                key.push((buffer.read(cx).remote_id(), ranges_for_buffer.clone()));
18700                let (new_ranges, _) = multibuffer.set_excerpts_for_path(
18701                    PathKey::for_buffer(&buffer, cx),
18702                    buffer.clone(),
18703                    ranges_for_buffer,
18704                    multibuffer_context_lines(cx),
18705                    cx,
18706                );
18707                ranges.extend(new_ranges)
18708            }
18709
18710            multibuffer.with_title(title)
18711        });
18712        let existing = workspace.active_pane().update(cx, |pane, cx| {
18713            pane.items()
18714                .filter_map(|item| item.downcast::<Editor>())
18715                .find(|editor| {
18716                    editor
18717                        .read(cx)
18718                        .lookup_key
18719                        .as_ref()
18720                        .and_then(|it| {
18721                            it.downcast_ref::<(String, Vec<(BufferId, Vec<Range<Point>>)>)>()
18722                        })
18723                        .is_some_and(|it| *it == key)
18724                })
18725        });
18726        let was_existing = existing.is_some();
18727        let editor = existing.unwrap_or_else(|| {
18728            cx.new(|cx| {
18729                let mut editor = Editor::for_multibuffer(
18730                    excerpt_buffer,
18731                    Some(workspace.project().clone()),
18732                    window,
18733                    cx,
18734                );
18735                editor.lookup_key = Some(Box::new(key));
18736                editor
18737            })
18738        });
18739        editor.update(cx, |editor, cx| match multibuffer_selection_mode {
18740            MultibufferSelectionMode::First => {
18741                if let Some(first_range) = ranges.first() {
18742                    editor.change_selections(
18743                        SelectionEffects::no_scroll(),
18744                        window,
18745                        cx,
18746                        |selections| {
18747                            selections.clear_disjoint();
18748                            selections.select_anchor_ranges(std::iter::once(first_range.clone()));
18749                        },
18750                    );
18751                }
18752                editor.highlight_background(
18753                    HighlightKey::Editor,
18754                    &ranges,
18755                    |_, theme| theme.colors().editor_highlighted_line_background,
18756                    cx,
18757                );
18758            }
18759            MultibufferSelectionMode::All => {
18760                editor.change_selections(SelectionEffects::no_scroll(), window, cx, |selections| {
18761                    selections.clear_disjoint();
18762                    selections.select_anchor_ranges(ranges);
18763                });
18764            }
18765        });
18766
18767        let item = Box::new(editor.clone());
18768
18769        let pane = if split {
18770            workspace.adjacent_pane(window, cx)
18771        } else {
18772            workspace.active_pane().clone()
18773        };
18774        let activate_pane = split;
18775
18776        let mut destination_index = None;
18777        pane.update(cx, |pane, cx| {
18778            if allow_preview && !was_existing {
18779                destination_index = pane.replace_preview_item_id(item.item_id(), window, cx);
18780            }
18781            if was_existing && !allow_preview {
18782                pane.unpreview_item_if_preview(item.item_id());
18783            }
18784            pane.add_item(item, activate_pane, true, destination_index, window, cx);
18785        });
18786
18787        Some((editor, pane))
18788    }
18789
18790    pub fn rename(
18791        &mut self,
18792        _: &Rename,
18793        window: &mut Window,
18794        cx: &mut Context<Self>,
18795    ) -> Option<Task<Result<()>>> {
18796        use language::ToOffset as _;
18797
18798        let provider = self.semantics_provider.clone()?;
18799        let selection = self.selections.newest_anchor().clone();
18800        let (cursor_buffer, cursor_buffer_position) = self
18801            .buffer
18802            .read(cx)
18803            .text_anchor_for_position(selection.head(), cx)?;
18804        let (tail_buffer, cursor_buffer_position_end) = self
18805            .buffer
18806            .read(cx)
18807            .text_anchor_for_position(selection.tail(), cx)?;
18808        if tail_buffer != cursor_buffer {
18809            return None;
18810        }
18811
18812        let snapshot = cursor_buffer.read(cx).snapshot();
18813        let cursor_buffer_offset = cursor_buffer_position.to_offset(&snapshot);
18814        let cursor_buffer_offset_end = cursor_buffer_position_end.to_offset(&snapshot);
18815        let prepare_rename = provider
18816            .range_for_rename(&cursor_buffer, cursor_buffer_position, cx)
18817            .unwrap_or_else(|| Task::ready(Ok(None)));
18818        drop(snapshot);
18819
18820        Some(cx.spawn_in(window, async move |this, cx| {
18821            let rename_range = if let Some(range) = prepare_rename.await? {
18822                Some(range)
18823            } else {
18824                this.update(cx, |this, cx| {
18825                    let buffer = this.buffer.read(cx).snapshot(cx);
18826                    let mut buffer_highlights = this
18827                        .document_highlights_for_position(selection.head(), &buffer)
18828                        .filter(|highlight| {
18829                            highlight.start.excerpt_id == selection.head().excerpt_id
18830                                && highlight.end.excerpt_id == selection.head().excerpt_id
18831                        });
18832                    buffer_highlights
18833                        .next()
18834                        .map(|highlight| highlight.start.text_anchor..highlight.end.text_anchor)
18835                })?
18836            };
18837            if let Some(rename_range) = rename_range {
18838                this.update_in(cx, |this, window, cx| {
18839                    let snapshot = cursor_buffer.read(cx).snapshot();
18840                    let rename_buffer_range = rename_range.to_offset(&snapshot);
18841                    let cursor_offset_in_rename_range =
18842                        cursor_buffer_offset.saturating_sub(rename_buffer_range.start);
18843                    let cursor_offset_in_rename_range_end =
18844                        cursor_buffer_offset_end.saturating_sub(rename_buffer_range.start);
18845
18846                    this.take_rename(false, window, cx);
18847                    let buffer = this.buffer.read(cx).read(cx);
18848                    let cursor_offset = selection.head().to_offset(&buffer);
18849                    let rename_start =
18850                        cursor_offset.saturating_sub_usize(cursor_offset_in_rename_range);
18851                    let rename_end = rename_start + rename_buffer_range.len();
18852                    let range = buffer.anchor_before(rename_start)..buffer.anchor_after(rename_end);
18853                    let mut old_highlight_id = None;
18854                    let old_name: Arc<str> = buffer
18855                        .chunks(rename_start..rename_end, true)
18856                        .map(|chunk| {
18857                            if old_highlight_id.is_none() {
18858                                old_highlight_id = chunk.syntax_highlight_id;
18859                            }
18860                            chunk.text
18861                        })
18862                        .collect::<String>()
18863                        .into();
18864
18865                    drop(buffer);
18866
18867                    // Position the selection in the rename editor so that it matches the current selection.
18868                    this.show_local_selections = false;
18869                    let rename_editor = cx.new(|cx| {
18870                        let mut editor = Editor::single_line(window, cx);
18871                        editor.buffer.update(cx, |buffer, cx| {
18872                            buffer.edit(
18873                                [(MultiBufferOffset(0)..MultiBufferOffset(0), old_name.clone())],
18874                                None,
18875                                cx,
18876                            )
18877                        });
18878                        let cursor_offset_in_rename_range =
18879                            MultiBufferOffset(cursor_offset_in_rename_range);
18880                        let cursor_offset_in_rename_range_end =
18881                            MultiBufferOffset(cursor_offset_in_rename_range_end);
18882                        let rename_selection_range = match cursor_offset_in_rename_range
18883                            .cmp(&cursor_offset_in_rename_range_end)
18884                        {
18885                            Ordering::Equal => {
18886                                editor.select_all(&SelectAll, window, cx);
18887                                return editor;
18888                            }
18889                            Ordering::Less => {
18890                                cursor_offset_in_rename_range..cursor_offset_in_rename_range_end
18891                            }
18892                            Ordering::Greater => {
18893                                cursor_offset_in_rename_range_end..cursor_offset_in_rename_range
18894                            }
18895                        };
18896                        if rename_selection_range.end.0 > old_name.len() {
18897                            editor.select_all(&SelectAll, window, cx);
18898                        } else {
18899                            editor.change_selections(Default::default(), window, cx, |s| {
18900                                s.select_ranges([rename_selection_range]);
18901                            });
18902                        }
18903                        editor
18904                    });
18905                    cx.subscribe(&rename_editor, |_, _, e: &EditorEvent, cx| {
18906                        if e == &EditorEvent::Focused {
18907                            cx.emit(EditorEvent::FocusedIn)
18908                        }
18909                    })
18910                    .detach();
18911
18912                    let write_highlights =
18913                        this.clear_background_highlights(HighlightKey::DocumentHighlightWrite, cx);
18914                    let read_highlights =
18915                        this.clear_background_highlights(HighlightKey::DocumentHighlightRead, cx);
18916                    let ranges = write_highlights
18917                        .iter()
18918                        .flat_map(|(_, ranges)| ranges.iter())
18919                        .chain(read_highlights.iter().flat_map(|(_, ranges)| ranges.iter()))
18920                        .cloned()
18921                        .collect();
18922
18923                    this.highlight_text(
18924                        HighlightKey::Rename,
18925                        ranges,
18926                        HighlightStyle {
18927                            fade_out: Some(0.6),
18928                            ..Default::default()
18929                        },
18930                        cx,
18931                    );
18932                    let rename_focus_handle = rename_editor.focus_handle(cx);
18933                    window.focus(&rename_focus_handle, cx);
18934                    let block_id = this.insert_blocks(
18935                        [BlockProperties {
18936                            style: BlockStyle::Flex,
18937                            placement: BlockPlacement::Below(range.start),
18938                            height: Some(1),
18939                            render: Arc::new({
18940                                let rename_editor = rename_editor.clone();
18941                                move |cx: &mut BlockContext| {
18942                                    let mut text_style = cx.editor_style.text.clone();
18943                                    if let Some(highlight_style) = old_highlight_id
18944                                        .and_then(|h| h.style(&cx.editor_style.syntax))
18945                                    {
18946                                        text_style = text_style.highlight(highlight_style);
18947                                    }
18948                                    div()
18949                                        .block_mouse_except_scroll()
18950                                        .pl(cx.anchor_x)
18951                                        .child(EditorElement::new(
18952                                            &rename_editor,
18953                                            EditorStyle {
18954                                                background: cx.theme().system().transparent,
18955                                                local_player: cx.editor_style.local_player,
18956                                                text: text_style,
18957                                                scrollbar_width: cx.editor_style.scrollbar_width,
18958                                                syntax: cx.editor_style.syntax.clone(),
18959                                                status: cx.editor_style.status.clone(),
18960                                                inlay_hints_style: HighlightStyle {
18961                                                    font_weight: Some(FontWeight::BOLD),
18962                                                    ..make_inlay_hints_style(cx.app)
18963                                                },
18964                                                edit_prediction_styles: make_suggestion_styles(
18965                                                    cx.app,
18966                                                ),
18967                                                ..EditorStyle::default()
18968                                            },
18969                                        ))
18970                                        .into_any_element()
18971                                }
18972                            }),
18973                            priority: 0,
18974                        }],
18975                        Some(Autoscroll::fit()),
18976                        cx,
18977                    )[0];
18978                    this.pending_rename = Some(RenameState {
18979                        range,
18980                        old_name,
18981                        editor: rename_editor,
18982                        block_id,
18983                    });
18984                })?;
18985            }
18986
18987            Ok(())
18988        }))
18989    }
18990
18991    pub fn confirm_rename(
18992        &mut self,
18993        _: &ConfirmRename,
18994        window: &mut Window,
18995        cx: &mut Context<Self>,
18996    ) -> Option<Task<Result<()>>> {
18997        let rename = self.take_rename(false, window, cx)?;
18998        let workspace = self.workspace()?.downgrade();
18999        let (buffer, start) = self
19000            .buffer
19001            .read(cx)
19002            .text_anchor_for_position(rename.range.start, cx)?;
19003        let (end_buffer, _) = self
19004            .buffer
19005            .read(cx)
19006            .text_anchor_for_position(rename.range.end, cx)?;
19007        if buffer != end_buffer {
19008            return None;
19009        }
19010
19011        let old_name = rename.old_name;
19012        let new_name = rename.editor.read(cx).text(cx);
19013
19014        let rename = self.semantics_provider.as_ref()?.perform_rename(
19015            &buffer,
19016            start,
19017            new_name.clone(),
19018            cx,
19019        )?;
19020
19021        Some(cx.spawn_in(window, async move |editor, cx| {
19022            let project_transaction = rename.await?;
19023            Self::open_project_transaction(
19024                &editor,
19025                workspace,
19026                project_transaction,
19027                format!("Rename: {}{}", old_name, new_name),
19028                cx,
19029            )
19030            .await?;
19031
19032            editor.update(cx, |editor, cx| {
19033                editor.refresh_document_highlights(cx);
19034            })?;
19035            Ok(())
19036        }))
19037    }
19038
19039    fn take_rename(
19040        &mut self,
19041        moving_cursor: bool,
19042        window: &mut Window,
19043        cx: &mut Context<Self>,
19044    ) -> Option<RenameState> {
19045        let rename = self.pending_rename.take()?;
19046        if rename.editor.focus_handle(cx).is_focused(window) {
19047            window.focus(&self.focus_handle, cx);
19048        }
19049
19050        self.remove_blocks(
19051            [rename.block_id].into_iter().collect(),
19052            Some(Autoscroll::fit()),
19053            cx,
19054        );
19055        self.clear_highlights(HighlightKey::Rename, cx);
19056        self.show_local_selections = true;
19057
19058        if moving_cursor {
19059            let cursor_in_rename_editor = rename.editor.update(cx, |editor, cx| {
19060                editor
19061                    .selections
19062                    .newest::<MultiBufferOffset>(&editor.display_snapshot(cx))
19063                    .head()
19064            });
19065
19066            // Update the selection to match the position of the selection inside
19067            // the rename editor.
19068            let snapshot = self.buffer.read(cx).read(cx);
19069            let rename_range = rename.range.to_offset(&snapshot);
19070            let cursor_in_editor = snapshot
19071                .clip_offset(rename_range.start + cursor_in_rename_editor, Bias::Left)
19072                .min(rename_range.end);
19073            drop(snapshot);
19074
19075            self.change_selections(SelectionEffects::no_scroll(), window, cx, |s| {
19076                s.select_ranges(vec![cursor_in_editor..cursor_in_editor])
19077            });
19078        } else {
19079            self.refresh_document_highlights(cx);
19080        }
19081
19082        Some(rename)
19083    }
19084
19085    pub fn pending_rename(&self) -> Option<&RenameState> {
19086        self.pending_rename.as_ref()
19087    }
19088
19089    fn format(
19090        &mut self,
19091        _: &Format,
19092        window: &mut Window,
19093        cx: &mut Context<Self>,
19094    ) -> Option<Task<Result<()>>> {
19095        self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
19096
19097        let project = match &self.project {
19098            Some(project) => project.clone(),
19099            None => return None,
19100        };
19101
19102        Some(self.perform_format(
19103            project,
19104            FormatTrigger::Manual,
19105            FormatTarget::Buffers(self.buffer.read(cx).all_buffers()),
19106            window,
19107            cx,
19108        ))
19109    }
19110
19111    fn format_selections(
19112        &mut self,
19113        _: &FormatSelections,
19114        window: &mut Window,
19115        cx: &mut Context<Self>,
19116    ) -> Option<Task<Result<()>>> {
19117        self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
19118
19119        let project = match &self.project {
19120            Some(project) => project.clone(),
19121            None => return None,
19122        };
19123
19124        let ranges = self
19125            .selections
19126            .all_adjusted(&self.display_snapshot(cx))
19127            .into_iter()
19128            .map(|selection| selection.range())
19129            .collect_vec();
19130
19131        Some(self.perform_format(
19132            project,
19133            FormatTrigger::Manual,
19134            FormatTarget::Ranges(ranges),
19135            window,
19136            cx,
19137        ))
19138    }
19139
19140    fn perform_format(
19141        &mut self,
19142        project: Entity<Project>,
19143        trigger: FormatTrigger,
19144        target: FormatTarget,
19145        window: &mut Window,
19146        cx: &mut Context<Self>,
19147    ) -> Task<Result<()>> {
19148        let buffer = self.buffer.clone();
19149        let (buffers, target) = match target {
19150            FormatTarget::Buffers(buffers) => (buffers, LspFormatTarget::Buffers),
19151            FormatTarget::Ranges(selection_ranges) => {
19152                let multi_buffer = buffer.read(cx);
19153                let snapshot = multi_buffer.read(cx);
19154                let mut buffers = HashSet::default();
19155                let mut buffer_id_to_ranges: BTreeMap<BufferId, Vec<Range<text::Anchor>>> =
19156                    BTreeMap::new();
19157                for selection_range in selection_ranges {
19158                    for (buffer, buffer_range, _) in
19159                        snapshot.range_to_buffer_ranges(selection_range.start..=selection_range.end)
19160                    {
19161                        let buffer_id = buffer.remote_id();
19162                        let start = buffer.anchor_before(buffer_range.start);
19163                        let end = buffer.anchor_after(buffer_range.end);
19164                        buffers.insert(multi_buffer.buffer(buffer_id).unwrap());
19165                        buffer_id_to_ranges
19166                            .entry(buffer_id)
19167                            .and_modify(|buffer_ranges| buffer_ranges.push(start..end))
19168                            .or_insert_with(|| vec![start..end]);
19169                    }
19170                }
19171                (buffers, LspFormatTarget::Ranges(buffer_id_to_ranges))
19172            }
19173        };
19174
19175        let transaction_id_prev = buffer.read(cx).last_transaction_id(cx);
19176        let selections_prev = transaction_id_prev
19177            .and_then(|transaction_id_prev| {
19178                // default to selections as they were after the last edit, if we have them,
19179                // instead of how they are now.
19180                // This will make it so that editing, moving somewhere else, formatting, then undoing the format
19181                // will take you back to where you made the last edit, instead of staying where you scrolled
19182                self.selection_history
19183                    .transaction(transaction_id_prev)
19184                    .map(|t| t.0.clone())
19185            })
19186            .unwrap_or_else(|| self.selections.disjoint_anchors_arc());
19187
19188        let mut timeout = cx.background_executor().timer(FORMAT_TIMEOUT).fuse();
19189        let format = project.update(cx, |project, cx| {
19190            project.format(buffers, target, true, trigger, cx)
19191        });
19192
19193        cx.spawn_in(window, async move |editor, cx| {
19194            let transaction = futures::select_biased! {
19195                transaction = format.log_err().fuse() => transaction,
19196                () = timeout => {
19197                    log::warn!("timed out waiting for formatting");
19198                    None
19199                }
19200            };
19201
19202            buffer.update(cx, |buffer, cx| {
19203                if let Some(transaction) = transaction
19204                    && !buffer.is_singleton()
19205                {
19206                    buffer.push_transaction(&transaction.0, cx);
19207                }
19208                cx.notify();
19209            });
19210
19211            if let Some(transaction_id_now) =
19212                buffer.read_with(cx, |b, cx| b.last_transaction_id(cx))
19213            {
19214                let has_new_transaction = transaction_id_prev != Some(transaction_id_now);
19215                if has_new_transaction {
19216                    editor
19217                        .update(cx, |editor, _| {
19218                            editor
19219                                .selection_history
19220                                .insert_transaction(transaction_id_now, selections_prev);
19221                        })
19222                        .ok();
19223                }
19224            }
19225
19226            Ok(())
19227        })
19228    }
19229
19230    fn organize_imports(
19231        &mut self,
19232        _: &OrganizeImports,
19233        window: &mut Window,
19234        cx: &mut Context<Self>,
19235    ) -> Option<Task<Result<()>>> {
19236        self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
19237        let project = match &self.project {
19238            Some(project) => project.clone(),
19239            None => return None,
19240        };
19241        Some(self.perform_code_action_kind(
19242            project,
19243            CodeActionKind::SOURCE_ORGANIZE_IMPORTS,
19244            window,
19245            cx,
19246        ))
19247    }
19248
19249    fn perform_code_action_kind(
19250        &mut self,
19251        project: Entity<Project>,
19252        kind: CodeActionKind,
19253        window: &mut Window,
19254        cx: &mut Context<Self>,
19255    ) -> Task<Result<()>> {
19256        let buffer = self.buffer.clone();
19257        let buffers = buffer.read(cx).all_buffers();
19258        let mut timeout = cx.background_executor().timer(CODE_ACTION_TIMEOUT).fuse();
19259        let apply_action = project.update(cx, |project, cx| {
19260            project.apply_code_action_kind(buffers, kind, true, cx)
19261        });
19262        cx.spawn_in(window, async move |_, cx| {
19263            let transaction = futures::select_biased! {
19264                () = timeout => {
19265                    log::warn!("timed out waiting for executing code action");
19266                    None
19267                }
19268                transaction = apply_action.log_err().fuse() => transaction,
19269            };
19270            buffer.update(cx, |buffer, cx| {
19271                // check if we need this
19272                if let Some(transaction) = transaction
19273                    && !buffer.is_singleton()
19274                {
19275                    buffer.push_transaction(&transaction.0, cx);
19276                }
19277                cx.notify();
19278            });
19279            Ok(())
19280        })
19281    }
19282
19283    pub fn restart_language_server(
19284        &mut self,
19285        _: &RestartLanguageServer,
19286        _: &mut Window,
19287        cx: &mut Context<Self>,
19288    ) {
19289        if let Some(project) = self.project.clone() {
19290            self.buffer.update(cx, |multi_buffer, cx| {
19291                project.update(cx, |project, cx| {
19292                    project.restart_language_servers_for_buffers(
19293                        multi_buffer.all_buffers().into_iter().collect(),
19294                        HashSet::default(),
19295                        cx,
19296                    );
19297                });
19298            })
19299        }
19300    }
19301
19302    pub fn stop_language_server(
19303        &mut self,
19304        _: &StopLanguageServer,
19305        _: &mut Window,
19306        cx: &mut Context<Self>,
19307    ) {
19308        if let Some(project) = self.project.clone() {
19309            self.buffer.update(cx, |multi_buffer, cx| {
19310                project.update(cx, |project, cx| {
19311                    project.stop_language_servers_for_buffers(
19312                        multi_buffer.all_buffers().into_iter().collect(),
19313                        HashSet::default(),
19314                        cx,
19315                    );
19316                });
19317            });
19318        }
19319    }
19320
19321    fn cancel_language_server_work(
19322        workspace: &mut Workspace,
19323        _: &actions::CancelLanguageServerWork,
19324        _: &mut Window,
19325        cx: &mut Context<Workspace>,
19326    ) {
19327        let project = workspace.project();
19328        let buffers = workspace
19329            .active_item(cx)
19330            .and_then(|item| item.act_as::<Editor>(cx))
19331            .map_or(HashSet::default(), |editor| {
19332                editor.read(cx).buffer.read(cx).all_buffers()
19333            });
19334        project.update(cx, |project, cx| {
19335            project.cancel_language_server_work_for_buffers(buffers, cx);
19336        });
19337    }
19338
19339    fn show_character_palette(
19340        &mut self,
19341        _: &ShowCharacterPalette,
19342        window: &mut Window,
19343        _: &mut Context<Self>,
19344    ) {
19345        window.show_character_palette();
19346    }
19347
19348    fn refresh_active_diagnostics(&mut self, cx: &mut Context<Editor>) {
19349        if !self.diagnostics_enabled() {
19350            return;
19351        }
19352
19353        if let ActiveDiagnostic::Group(active_diagnostics) = &mut self.active_diagnostics {
19354            let buffer = self.buffer.read(cx).snapshot(cx);
19355            let primary_range_start = active_diagnostics.active_range.start.to_offset(&buffer);
19356            let primary_range_end = active_diagnostics.active_range.end.to_offset(&buffer);
19357            let is_valid = buffer
19358                .diagnostics_in_range::<MultiBufferOffset>(primary_range_start..primary_range_end)
19359                .any(|entry| {
19360                    entry.diagnostic.is_primary
19361                        && !entry.range.is_empty()
19362                        && entry.range.start == primary_range_start
19363                        && entry.diagnostic.message == active_diagnostics.active_message
19364                });
19365
19366            if !is_valid {
19367                self.dismiss_diagnostics(cx);
19368            }
19369        }
19370    }
19371
19372    pub fn active_diagnostic_group(&self) -> Option<&ActiveDiagnosticGroup> {
19373        match &self.active_diagnostics {
19374            ActiveDiagnostic::Group(group) => Some(group),
19375            _ => None,
19376        }
19377    }
19378
19379    pub fn set_all_diagnostics_active(&mut self, cx: &mut Context<Self>) {
19380        if !self.diagnostics_enabled() {
19381            return;
19382        }
19383        self.dismiss_diagnostics(cx);
19384        self.active_diagnostics = ActiveDiagnostic::All;
19385    }
19386
19387    fn activate_diagnostics(
19388        &mut self,
19389        buffer_id: BufferId,
19390        diagnostic: DiagnosticEntryRef<'_, MultiBufferOffset>,
19391        window: &mut Window,
19392        cx: &mut Context<Self>,
19393    ) {
19394        if !self.diagnostics_enabled() || matches!(self.active_diagnostics, ActiveDiagnostic::All) {
19395            return;
19396        }
19397        self.dismiss_diagnostics(cx);
19398        let snapshot = self.snapshot(window, cx);
19399        let buffer = self.buffer.read(cx).snapshot(cx);
19400        let Some(renderer) = GlobalDiagnosticRenderer::global(cx) else {
19401            return;
19402        };
19403
19404        let diagnostic_group = buffer
19405            .diagnostic_group(buffer_id, diagnostic.diagnostic.group_id)
19406            .collect::<Vec<_>>();
19407
19408        let language_registry = self
19409            .project()
19410            .map(|project| project.read(cx).languages().clone());
19411
19412        let blocks = renderer.render_group(
19413            diagnostic_group,
19414            buffer_id,
19415            snapshot,
19416            cx.weak_entity(),
19417            language_registry,
19418            cx,
19419        );
19420
19421        let blocks = self.display_map.update(cx, |display_map, cx| {
19422            display_map.insert_blocks(blocks, cx).into_iter().collect()
19423        });
19424        self.active_diagnostics = ActiveDiagnostic::Group(ActiveDiagnosticGroup {
19425            active_range: buffer.anchor_before(diagnostic.range.start)
19426                ..buffer.anchor_after(diagnostic.range.end),
19427            active_message: diagnostic.diagnostic.message.clone(),
19428            group_id: diagnostic.diagnostic.group_id,
19429            blocks,
19430        });
19431        cx.notify();
19432    }
19433
19434    fn dismiss_diagnostics(&mut self, cx: &mut Context<Self>) {
19435        if matches!(self.active_diagnostics, ActiveDiagnostic::All) {
19436            return;
19437        };
19438
19439        let prev = mem::replace(&mut self.active_diagnostics, ActiveDiagnostic::None);
19440        if let ActiveDiagnostic::Group(group) = prev {
19441            self.display_map.update(cx, |display_map, cx| {
19442                display_map.remove_blocks(group.blocks, cx);
19443            });
19444            cx.notify();
19445        }
19446    }
19447
19448    /// Disable inline diagnostics rendering for this editor.
19449    pub fn disable_inline_diagnostics(&mut self) {
19450        self.inline_diagnostics_enabled = false;
19451        self.inline_diagnostics_update = Task::ready(());
19452        self.inline_diagnostics.clear();
19453    }
19454
19455    pub fn disable_diagnostics(&mut self, cx: &mut Context<Self>) {
19456        self.diagnostics_enabled = false;
19457        self.dismiss_diagnostics(cx);
19458        self.inline_diagnostics_update = Task::ready(());
19459        self.inline_diagnostics.clear();
19460    }
19461
19462    pub fn disable_word_completions(&mut self) {
19463        self.word_completions_enabled = false;
19464    }
19465
19466    pub fn diagnostics_enabled(&self) -> bool {
19467        self.diagnostics_enabled && self.mode.is_full()
19468    }
19469
19470    pub fn inline_diagnostics_enabled(&self) -> bool {
19471        self.inline_diagnostics_enabled && self.diagnostics_enabled()
19472    }
19473
19474    pub fn show_inline_diagnostics(&self) -> bool {
19475        self.show_inline_diagnostics
19476    }
19477
19478    pub fn toggle_inline_diagnostics(
19479        &mut self,
19480        _: &ToggleInlineDiagnostics,
19481        window: &mut Window,
19482        cx: &mut Context<Editor>,
19483    ) {
19484        self.show_inline_diagnostics = !self.show_inline_diagnostics;
19485        self.refresh_inline_diagnostics(false, window, cx);
19486    }
19487
19488    pub fn set_max_diagnostics_severity(&mut self, severity: DiagnosticSeverity, cx: &mut App) {
19489        self.diagnostics_max_severity = severity;
19490        self.display_map.update(cx, |display_map, _| {
19491            display_map.diagnostics_max_severity = self.diagnostics_max_severity;
19492        });
19493    }
19494
19495    pub fn toggle_diagnostics(
19496        &mut self,
19497        _: &ToggleDiagnostics,
19498        window: &mut Window,
19499        cx: &mut Context<Editor>,
19500    ) {
19501        if !self.diagnostics_enabled() {
19502            return;
19503        }
19504
19505        let new_severity = if self.diagnostics_max_severity == DiagnosticSeverity::Off {
19506            EditorSettings::get_global(cx)
19507                .diagnostics_max_severity
19508                .filter(|severity| severity != &DiagnosticSeverity::Off)
19509                .unwrap_or(DiagnosticSeverity::Hint)
19510        } else {
19511            DiagnosticSeverity::Off
19512        };
19513        self.set_max_diagnostics_severity(new_severity, cx);
19514        if self.diagnostics_max_severity == DiagnosticSeverity::Off {
19515            self.active_diagnostics = ActiveDiagnostic::None;
19516            self.inline_diagnostics_update = Task::ready(());
19517            self.inline_diagnostics.clear();
19518        } else {
19519            self.refresh_inline_diagnostics(false, window, cx);
19520        }
19521
19522        cx.notify();
19523    }
19524
19525    pub fn toggle_minimap(
19526        &mut self,
19527        _: &ToggleMinimap,
19528        window: &mut Window,
19529        cx: &mut Context<Editor>,
19530    ) {
19531        if self.supports_minimap(cx) {
19532            self.set_minimap_visibility(self.minimap_visibility.toggle_visibility(), window, cx);
19533        }
19534    }
19535
19536    fn refresh_inline_diagnostics(
19537        &mut self,
19538        debounce: bool,
19539        window: &mut Window,
19540        cx: &mut Context<Self>,
19541    ) {
19542        let max_severity = ProjectSettings::get_global(cx)
19543            .diagnostics
19544            .inline
19545            .max_severity
19546            .unwrap_or(self.diagnostics_max_severity);
19547
19548        if !self.inline_diagnostics_enabled()
19549            || !self.diagnostics_enabled()
19550            || !self.show_inline_diagnostics
19551            || max_severity == DiagnosticSeverity::Off
19552        {
19553            self.inline_diagnostics_update = Task::ready(());
19554            self.inline_diagnostics.clear();
19555            return;
19556        }
19557
19558        let debounce_ms = ProjectSettings::get_global(cx)
19559            .diagnostics
19560            .inline
19561            .update_debounce_ms;
19562        let debounce = if debounce && debounce_ms > 0 {
19563            Some(Duration::from_millis(debounce_ms))
19564        } else {
19565            None
19566        };
19567        self.inline_diagnostics_update = cx.spawn_in(window, async move |editor, cx| {
19568            if let Some(debounce) = debounce {
19569                cx.background_executor().timer(debounce).await;
19570            }
19571            let Some(snapshot) = editor.upgrade().map(|editor| {
19572                editor.update(cx, |editor, cx| editor.buffer().read(cx).snapshot(cx))
19573            }) else {
19574                return;
19575            };
19576
19577            let new_inline_diagnostics = cx
19578                .background_spawn(async move {
19579                    let mut inline_diagnostics = Vec::<(Anchor, InlineDiagnostic)>::new();
19580                    for diagnostic_entry in
19581                        snapshot.diagnostics_in_range(MultiBufferOffset(0)..snapshot.len())
19582                    {
19583                        let message = diagnostic_entry
19584                            .diagnostic
19585                            .message
19586                            .split_once('\n')
19587                            .map(|(line, _)| line)
19588                            .map(SharedString::new)
19589                            .unwrap_or_else(|| {
19590                                SharedString::new(&*diagnostic_entry.diagnostic.message)
19591                            });
19592                        let start_anchor = snapshot.anchor_before(diagnostic_entry.range.start);
19593                        let (Ok(i) | Err(i)) = inline_diagnostics
19594                            .binary_search_by(|(probe, _)| probe.cmp(&start_anchor, &snapshot));
19595                        inline_diagnostics.insert(
19596                            i,
19597                            (
19598                                start_anchor,
19599                                InlineDiagnostic {
19600                                    message,
19601                                    group_id: diagnostic_entry.diagnostic.group_id,
19602                                    start: diagnostic_entry.range.start.to_point(&snapshot),
19603                                    is_primary: diagnostic_entry.diagnostic.is_primary,
19604                                    severity: diagnostic_entry.diagnostic.severity,
19605                                },
19606                            ),
19607                        );
19608                    }
19609                    inline_diagnostics
19610                })
19611                .await;
19612
19613            editor
19614                .update(cx, |editor, cx| {
19615                    editor.inline_diagnostics = new_inline_diagnostics;
19616                    cx.notify();
19617                })
19618                .ok();
19619        });
19620    }
19621
19622    fn pull_diagnostics(
19623        &mut self,
19624        buffer_id: BufferId,
19625        _window: &Window,
19626        cx: &mut Context<Self>,
19627    ) -> Option<()> {
19628        // `ActiveDiagnostic::All` is a special mode where editor's diagnostics are managed by the external view,
19629        // skip any LSP updates for it.
19630
19631        if self.active_diagnostics == ActiveDiagnostic::All
19632            || !self.mode().is_full()
19633            || !self.diagnostics_enabled()
19634        {
19635            return None;
19636        }
19637        let pull_diagnostics_settings = ProjectSettings::get_global(cx)
19638            .diagnostics
19639            .lsp_pull_diagnostics;
19640        if !pull_diagnostics_settings.enabled {
19641            return None;
19642        }
19643        let debounce = Duration::from_millis(pull_diagnostics_settings.debounce_ms);
19644        let project = self.project()?.downgrade();
19645        let buffer = self.buffer().read(cx).buffer(buffer_id)?;
19646
19647        self.pull_diagnostics_task = cx.spawn(async move |_, cx| {
19648            cx.background_executor().timer(debounce).await;
19649            if let Ok(task) = project.update(cx, |project, cx| {
19650                project.lsp_store().update(cx, |lsp_store, cx| {
19651                    lsp_store.pull_diagnostics_for_buffer(buffer, cx)
19652                })
19653            }) {
19654                task.await.log_err();
19655            }
19656            project
19657                .update(cx, |project, cx| {
19658                    project.lsp_store().update(cx, |lsp_store, cx| {
19659                        lsp_store.pull_document_diagnostics_for_buffer_edit(buffer_id, cx);
19660                    })
19661                })
19662                .log_err();
19663        });
19664
19665        Some(())
19666    }
19667
19668    pub fn set_selections_from_remote(
19669        &mut self,
19670        selections: Vec<Selection<Anchor>>,
19671        pending_selection: Option<Selection<Anchor>>,
19672        window: &mut Window,
19673        cx: &mut Context<Self>,
19674    ) {
19675        let old_cursor_position = self.selections.newest_anchor().head();
19676        self.selections
19677            .change_with(&self.display_snapshot(cx), |s| {
19678                s.select_anchors(selections);
19679                if let Some(pending_selection) = pending_selection {
19680                    s.set_pending(pending_selection, SelectMode::Character);
19681                } else {
19682                    s.clear_pending();
19683                }
19684            });
19685        self.selections_did_change(
19686            false,
19687            &old_cursor_position,
19688            SelectionEffects::default(),
19689            window,
19690            cx,
19691        );
19692    }
19693
19694    pub fn transact(
19695        &mut self,
19696        window: &mut Window,
19697        cx: &mut Context<Self>,
19698        update: impl FnOnce(&mut Self, &mut Window, &mut Context<Self>),
19699    ) -> Option<TransactionId> {
19700        self.with_selection_effects_deferred(window, cx, |this, window, cx| {
19701            this.start_transaction_at(Instant::now(), window, cx);
19702            update(this, window, cx);
19703            this.end_transaction_at(Instant::now(), cx)
19704        })
19705    }
19706
19707    pub fn start_transaction_at(
19708        &mut self,
19709        now: Instant,
19710        window: &mut Window,
19711        cx: &mut Context<Self>,
19712    ) -> Option<TransactionId> {
19713        self.end_selection(window, cx);
19714        if let Some(tx_id) = self
19715            .buffer
19716            .update(cx, |buffer, cx| buffer.start_transaction_at(now, cx))
19717        {
19718            self.selection_history
19719                .insert_transaction(tx_id, self.selections.disjoint_anchors_arc());
19720            cx.emit(EditorEvent::TransactionBegun {
19721                transaction_id: tx_id,
19722            });
19723            Some(tx_id)
19724        } else {
19725            None
19726        }
19727    }
19728
19729    pub fn end_transaction_at(
19730        &mut self,
19731        now: Instant,
19732        cx: &mut Context<Self>,
19733    ) -> Option<TransactionId> {
19734        if let Some(transaction_id) = self
19735            .buffer
19736            .update(cx, |buffer, cx| buffer.end_transaction_at(now, cx))
19737        {
19738            if let Some((_, end_selections)) =
19739                self.selection_history.transaction_mut(transaction_id)
19740            {
19741                *end_selections = Some(self.selections.disjoint_anchors_arc());
19742            } else {
19743                log::error!("unexpectedly ended a transaction that wasn't started by this editor");
19744            }
19745
19746            cx.emit(EditorEvent::Edited { transaction_id });
19747            Some(transaction_id)
19748        } else {
19749            None
19750        }
19751    }
19752
19753    pub fn modify_transaction_selection_history(
19754        &mut self,
19755        transaction_id: TransactionId,
19756        modify: impl FnOnce(&mut (Arc<[Selection<Anchor>]>, Option<Arc<[Selection<Anchor>]>>)),
19757    ) -> bool {
19758        self.selection_history
19759            .transaction_mut(transaction_id)
19760            .map(modify)
19761            .is_some()
19762    }
19763
19764    pub fn set_mark(&mut self, _: &actions::SetMark, window: &mut Window, cx: &mut Context<Self>) {
19765        if self.selection_mark_mode {
19766            self.change_selections(SelectionEffects::no_scroll(), window, cx, |s| {
19767                s.move_with(&mut |_, sel| {
19768                    sel.collapse_to(sel.head(), SelectionGoal::None);
19769                });
19770            })
19771        }
19772        self.selection_mark_mode = true;
19773        cx.notify();
19774    }
19775
19776    pub fn swap_selection_ends(
19777        &mut self,
19778        _: &actions::SwapSelectionEnds,
19779        window: &mut Window,
19780        cx: &mut Context<Self>,
19781    ) {
19782        self.change_selections(SelectionEffects::no_scroll(), window, cx, |s| {
19783            s.move_with(&mut |_, sel| {
19784                if sel.start != sel.end {
19785                    sel.reversed = !sel.reversed
19786                }
19787            });
19788        });
19789        self.request_autoscroll(Autoscroll::newest(), cx);
19790        cx.notify();
19791    }
19792
19793    pub fn toggle_focus(
19794        workspace: &mut Workspace,
19795        _: &actions::ToggleFocus,
19796        window: &mut Window,
19797        cx: &mut Context<Workspace>,
19798    ) {
19799        let Some(item) = workspace.recent_active_item_by_type::<Self>(cx) else {
19800            return;
19801        };
19802        workspace.activate_item(&item, true, true, window, cx);
19803    }
19804
19805    pub fn toggle_fold(
19806        &mut self,
19807        _: &actions::ToggleFold,
19808        window: &mut Window,
19809        cx: &mut Context<Self>,
19810    ) {
19811        if self.buffer_kind(cx) == ItemBufferKind::Singleton {
19812            let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
19813            let selection = self.selections.newest::<Point>(&display_map);
19814
19815            let range = if selection.is_empty() {
19816                let point = selection.head().to_display_point(&display_map);
19817                let start = DisplayPoint::new(point.row(), 0).to_point(&display_map);
19818                let end = DisplayPoint::new(point.row(), display_map.line_len(point.row()))
19819                    .to_point(&display_map);
19820                start..end
19821            } else {
19822                selection.range()
19823            };
19824            if display_map.folds_in_range(range).next().is_some() {
19825                self.unfold_lines(&Default::default(), window, cx)
19826            } else {
19827                self.fold(&Default::default(), window, cx)
19828            }
19829        } else {
19830            let multi_buffer_snapshot = self.buffer.read(cx).snapshot(cx);
19831            let buffer_ids: HashSet<_> = self
19832                .selections
19833                .disjoint_anchor_ranges()
19834                .flat_map(|range| multi_buffer_snapshot.buffer_ids_for_range(range))
19835                .collect();
19836
19837            let should_unfold = buffer_ids
19838                .iter()
19839                .any(|buffer_id| self.is_buffer_folded(*buffer_id, cx));
19840
19841            for buffer_id in buffer_ids {
19842                if should_unfold {
19843                    self.unfold_buffer(buffer_id, cx);
19844                } else {
19845                    self.fold_buffer(buffer_id, cx);
19846                }
19847            }
19848        }
19849    }
19850
19851    pub fn toggle_fold_recursive(
19852        &mut self,
19853        _: &actions::ToggleFoldRecursive,
19854        window: &mut Window,
19855        cx: &mut Context<Self>,
19856    ) {
19857        let selection = self.selections.newest::<Point>(&self.display_snapshot(cx));
19858
19859        let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
19860        let range = if selection.is_empty() {
19861            let point = selection.head().to_display_point(&display_map);
19862            let start = DisplayPoint::new(point.row(), 0).to_point(&display_map);
19863            let end = DisplayPoint::new(point.row(), display_map.line_len(point.row()))
19864                .to_point(&display_map);
19865            start..end
19866        } else {
19867            selection.range()
19868        };
19869        if display_map.folds_in_range(range).next().is_some() {
19870            self.unfold_recursive(&Default::default(), window, cx)
19871        } else {
19872            self.fold_recursive(&Default::default(), window, cx)
19873        }
19874    }
19875
19876    pub fn fold(&mut self, _: &actions::Fold, window: &mut Window, cx: &mut Context<Self>) {
19877        if self.buffer_kind(cx) == ItemBufferKind::Singleton {
19878            let mut to_fold = Vec::new();
19879            let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
19880            let selections = self.selections.all_adjusted(&display_map);
19881
19882            for selection in selections {
19883                let range = selection.range().sorted();
19884                let buffer_start_row = range.start.row;
19885
19886                if range.start.row != range.end.row {
19887                    let mut found = false;
19888                    let mut row = range.start.row;
19889                    while row <= range.end.row {
19890                        if let Some(crease) = display_map.crease_for_buffer_row(MultiBufferRow(row))
19891                        {
19892                            found = true;
19893                            row = crease.range().end.row + 1;
19894                            to_fold.push(crease);
19895                        } else {
19896                            row += 1
19897                        }
19898                    }
19899                    if found {
19900                        continue;
19901                    }
19902                }
19903
19904                for row in (0..=range.start.row).rev() {
19905                    if let Some(crease) = display_map.crease_for_buffer_row(MultiBufferRow(row))
19906                        && crease.range().end.row >= buffer_start_row
19907                    {
19908                        to_fold.push(crease);
19909                        if row <= range.start.row {
19910                            break;
19911                        }
19912                    }
19913                }
19914            }
19915
19916            self.fold_creases(to_fold, true, window, cx);
19917        } else {
19918            let multi_buffer_snapshot = self.buffer.read(cx).snapshot(cx);
19919            let buffer_ids = self
19920                .selections
19921                .disjoint_anchor_ranges()
19922                .flat_map(|range| multi_buffer_snapshot.buffer_ids_for_range(range))
19923                .collect::<HashSet<_>>();
19924            for buffer_id in buffer_ids {
19925                self.fold_buffer(buffer_id, cx);
19926            }
19927        }
19928    }
19929
19930    pub fn toggle_fold_all(
19931        &mut self,
19932        _: &actions::ToggleFoldAll,
19933        window: &mut Window,
19934        cx: &mut Context<Self>,
19935    ) {
19936        let has_folds = if self.buffer.read(cx).is_singleton() {
19937            let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
19938            let has_folds = display_map
19939                .folds_in_range(MultiBufferOffset(0)..display_map.buffer_snapshot().len())
19940                .next()
19941                .is_some();
19942            has_folds
19943        } else {
19944            let buffer_ids = self.buffer.read(cx).excerpt_buffer_ids();
19945            let has_folds = buffer_ids
19946                .iter()
19947                .any(|buffer_id| self.is_buffer_folded(*buffer_id, cx));
19948            has_folds
19949        };
19950
19951        if has_folds {
19952            self.unfold_all(&actions::UnfoldAll, window, cx);
19953        } else {
19954            self.fold_all(&actions::FoldAll, window, cx);
19955        }
19956    }
19957
19958    fn fold_at_level(
19959        &mut self,
19960        fold_at: &FoldAtLevel,
19961        window: &mut Window,
19962        cx: &mut Context<Self>,
19963    ) {
19964        if !self.buffer.read(cx).is_singleton() {
19965            return;
19966        }
19967
19968        let fold_at_level = fold_at.0;
19969        let snapshot = self.buffer.read(cx).snapshot(cx);
19970        let mut to_fold = Vec::new();
19971        let mut stack = vec![(0, snapshot.max_row().0, 1)];
19972
19973        let row_ranges_to_keep: Vec<Range<u32>> = self
19974            .selections
19975            .all::<Point>(&self.display_snapshot(cx))
19976            .into_iter()
19977            .map(|sel| sel.start.row..sel.end.row)
19978            .collect();
19979
19980        while let Some((mut start_row, end_row, current_level)) = stack.pop() {
19981            while start_row < end_row {
19982                match self
19983                    .snapshot(window, cx)
19984                    .crease_for_buffer_row(MultiBufferRow(start_row))
19985                {
19986                    Some(crease) => {
19987                        let nested_start_row = crease.range().start.row + 1;
19988                        let nested_end_row = crease.range().end.row;
19989
19990                        if current_level < fold_at_level {
19991                            stack.push((nested_start_row, nested_end_row, current_level + 1));
19992                        } else if current_level == fold_at_level {
19993                            // Fold iff there is no selection completely contained within the fold region
19994                            if !row_ranges_to_keep.iter().any(|selection| {
19995                                selection.end >= nested_start_row
19996                                    && selection.start <= nested_end_row
19997                            }) {
19998                                to_fold.push(crease);
19999                            }
20000                        }
20001
20002                        start_row = nested_end_row + 1;
20003                    }
20004                    None => start_row += 1,
20005                }
20006            }
20007        }
20008
20009        self.fold_creases(to_fold, true, window, cx);
20010    }
20011
20012    pub fn fold_at_level_1(
20013        &mut self,
20014        _: &actions::FoldAtLevel1,
20015        window: &mut Window,
20016        cx: &mut Context<Self>,
20017    ) {
20018        self.fold_at_level(&actions::FoldAtLevel(1), window, cx);
20019    }
20020
20021    pub fn fold_at_level_2(
20022        &mut self,
20023        _: &actions::FoldAtLevel2,
20024        window: &mut Window,
20025        cx: &mut Context<Self>,
20026    ) {
20027        self.fold_at_level(&actions::FoldAtLevel(2), window, cx);
20028    }
20029
20030    pub fn fold_at_level_3(
20031        &mut self,
20032        _: &actions::FoldAtLevel3,
20033        window: &mut Window,
20034        cx: &mut Context<Self>,
20035    ) {
20036        self.fold_at_level(&actions::FoldAtLevel(3), window, cx);
20037    }
20038
20039    pub fn fold_at_level_4(
20040        &mut self,
20041        _: &actions::FoldAtLevel4,
20042        window: &mut Window,
20043        cx: &mut Context<Self>,
20044    ) {
20045        self.fold_at_level(&actions::FoldAtLevel(4), window, cx);
20046    }
20047
20048    pub fn fold_at_level_5(
20049        &mut self,
20050        _: &actions::FoldAtLevel5,
20051        window: &mut Window,
20052        cx: &mut Context<Self>,
20053    ) {
20054        self.fold_at_level(&actions::FoldAtLevel(5), window, cx);
20055    }
20056
20057    pub fn fold_at_level_6(
20058        &mut self,
20059        _: &actions::FoldAtLevel6,
20060        window: &mut Window,
20061        cx: &mut Context<Self>,
20062    ) {
20063        self.fold_at_level(&actions::FoldAtLevel(6), window, cx);
20064    }
20065
20066    pub fn fold_at_level_7(
20067        &mut self,
20068        _: &actions::FoldAtLevel7,
20069        window: &mut Window,
20070        cx: &mut Context<Self>,
20071    ) {
20072        self.fold_at_level(&actions::FoldAtLevel(7), window, cx);
20073    }
20074
20075    pub fn fold_at_level_8(
20076        &mut self,
20077        _: &actions::FoldAtLevel8,
20078        window: &mut Window,
20079        cx: &mut Context<Self>,
20080    ) {
20081        self.fold_at_level(&actions::FoldAtLevel(8), window, cx);
20082    }
20083
20084    pub fn fold_at_level_9(
20085        &mut self,
20086        _: &actions::FoldAtLevel9,
20087        window: &mut Window,
20088        cx: &mut Context<Self>,
20089    ) {
20090        self.fold_at_level(&actions::FoldAtLevel(9), window, cx);
20091    }
20092
20093    pub fn fold_all(&mut self, _: &actions::FoldAll, window: &mut Window, cx: &mut Context<Self>) {
20094        if self.buffer.read(cx).is_singleton() {
20095            let mut fold_ranges = Vec::new();
20096            let snapshot = self.buffer.read(cx).snapshot(cx);
20097
20098            for row in 0..snapshot.max_row().0 {
20099                if let Some(foldable_range) = self
20100                    .snapshot(window, cx)
20101                    .crease_for_buffer_row(MultiBufferRow(row))
20102                {
20103                    fold_ranges.push(foldable_range);
20104                }
20105            }
20106
20107            self.fold_creases(fold_ranges, true, window, cx);
20108        } else {
20109            self.toggle_fold_multiple_buffers = cx.spawn_in(window, async move |editor, cx| {
20110                editor
20111                    .update_in(cx, |editor, _, cx| {
20112                        for buffer_id in editor.buffer.read(cx).excerpt_buffer_ids() {
20113                            editor.fold_buffer(buffer_id, cx);
20114                        }
20115                    })
20116                    .ok();
20117            });
20118        }
20119    }
20120
20121    pub fn fold_function_bodies(
20122        &mut self,
20123        _: &actions::FoldFunctionBodies,
20124        window: &mut Window,
20125        cx: &mut Context<Self>,
20126    ) {
20127        let snapshot = self.buffer.read(cx).snapshot(cx);
20128
20129        let ranges = snapshot
20130            .text_object_ranges(
20131                MultiBufferOffset(0)..snapshot.len(),
20132                TreeSitterOptions::default(),
20133            )
20134            .filter_map(|(range, obj)| (obj == TextObject::InsideFunction).then_some(range))
20135            .collect::<Vec<_>>();
20136
20137        let creases = ranges
20138            .into_iter()
20139            .map(|range| Crease::simple(range, self.display_map.read(cx).fold_placeholder.clone()))
20140            .collect();
20141
20142        self.fold_creases(creases, true, window, cx);
20143    }
20144
20145    pub fn fold_recursive(
20146        &mut self,
20147        _: &actions::FoldRecursive,
20148        window: &mut Window,
20149        cx: &mut Context<Self>,
20150    ) {
20151        let mut to_fold = Vec::new();
20152        let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
20153        let selections = self.selections.all_adjusted(&display_map);
20154
20155        for selection in selections {
20156            let range = selection.range().sorted();
20157            let buffer_start_row = range.start.row;
20158
20159            if range.start.row != range.end.row {
20160                let mut found = false;
20161                for row in range.start.row..=range.end.row {
20162                    if let Some(crease) = display_map.crease_for_buffer_row(MultiBufferRow(row)) {
20163                        found = true;
20164                        to_fold.push(crease);
20165                    }
20166                }
20167                if found {
20168                    continue;
20169                }
20170            }
20171
20172            for row in (0..=range.start.row).rev() {
20173                if let Some(crease) = display_map.crease_for_buffer_row(MultiBufferRow(row)) {
20174                    if crease.range().end.row >= buffer_start_row {
20175                        to_fold.push(crease);
20176                    } else {
20177                        break;
20178                    }
20179                }
20180            }
20181        }
20182
20183        self.fold_creases(to_fold, true, window, cx);
20184    }
20185
20186    pub fn fold_at(
20187        &mut self,
20188        buffer_row: MultiBufferRow,
20189        window: &mut Window,
20190        cx: &mut Context<Self>,
20191    ) {
20192        let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
20193
20194        if let Some(crease) = display_map.crease_for_buffer_row(buffer_row) {
20195            let autoscroll = self
20196                .selections
20197                .all::<Point>(&display_map)
20198                .iter()
20199                .any(|selection| crease.range().overlaps(&selection.range()));
20200
20201            self.fold_creases(vec![crease], autoscroll, window, cx);
20202        }
20203    }
20204
20205    pub fn unfold_lines(&mut self, _: &UnfoldLines, _window: &mut Window, cx: &mut Context<Self>) {
20206        if self.buffer_kind(cx) == ItemBufferKind::Singleton {
20207            let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
20208            let buffer = display_map.buffer_snapshot();
20209            let selections = self.selections.all::<Point>(&display_map);
20210            let ranges = selections
20211                .iter()
20212                .map(|s| {
20213                    let range = s.display_range(&display_map).sorted();
20214                    let mut start = range.start.to_point(&display_map);
20215                    let mut end = range.end.to_point(&display_map);
20216                    start.column = 0;
20217                    end.column = buffer.line_len(MultiBufferRow(end.row));
20218                    start..end
20219                })
20220                .collect::<Vec<_>>();
20221
20222            self.unfold_ranges(&ranges, true, true, cx);
20223        } else {
20224            let multi_buffer_snapshot = self.buffer.read(cx).snapshot(cx);
20225            let buffer_ids = self
20226                .selections
20227                .disjoint_anchor_ranges()
20228                .flat_map(|range| multi_buffer_snapshot.buffer_ids_for_range(range))
20229                .collect::<HashSet<_>>();
20230            for buffer_id in buffer_ids {
20231                self.unfold_buffer(buffer_id, cx);
20232            }
20233        }
20234    }
20235
20236    pub fn unfold_recursive(
20237        &mut self,
20238        _: &UnfoldRecursive,
20239        _window: &mut Window,
20240        cx: &mut Context<Self>,
20241    ) {
20242        let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
20243        let selections = self.selections.all::<Point>(&display_map);
20244        let ranges = selections
20245            .iter()
20246            .map(|s| {
20247                let mut range = s.display_range(&display_map).sorted();
20248                *range.start.column_mut() = 0;
20249                *range.end.column_mut() = display_map.line_len(range.end.row());
20250                let start = range.start.to_point(&display_map);
20251                let end = range.end.to_point(&display_map);
20252                start..end
20253            })
20254            .collect::<Vec<_>>();
20255
20256        self.unfold_ranges(&ranges, true, true, cx);
20257    }
20258
20259    pub fn unfold_at(
20260        &mut self,
20261        buffer_row: MultiBufferRow,
20262        _window: &mut Window,
20263        cx: &mut Context<Self>,
20264    ) {
20265        let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
20266
20267        let intersection_range = Point::new(buffer_row.0, 0)
20268            ..Point::new(
20269                buffer_row.0,
20270                display_map.buffer_snapshot().line_len(buffer_row),
20271            );
20272
20273        let autoscroll = self
20274            .selections
20275            .all::<Point>(&display_map)
20276            .iter()
20277            .any(|selection| RangeExt::overlaps(&selection.range(), &intersection_range));
20278
20279        self.unfold_ranges(&[intersection_range], true, autoscroll, cx);
20280    }
20281
20282    pub fn unfold_all(
20283        &mut self,
20284        _: &actions::UnfoldAll,
20285        _window: &mut Window,
20286        cx: &mut Context<Self>,
20287    ) {
20288        if self.buffer.read(cx).is_singleton() {
20289            let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
20290            self.unfold_ranges(
20291                &[MultiBufferOffset(0)..display_map.buffer_snapshot().len()],
20292                true,
20293                true,
20294                cx,
20295            );
20296        } else {
20297            self.toggle_fold_multiple_buffers = cx.spawn(async move |editor, cx| {
20298                editor
20299                    .update(cx, |editor, cx| {
20300                        for buffer_id in editor.buffer.read(cx).excerpt_buffer_ids() {
20301                            editor.unfold_buffer(buffer_id, cx);
20302                        }
20303                    })
20304                    .ok();
20305            });
20306        }
20307    }
20308
20309    pub fn fold_selected_ranges(
20310        &mut self,
20311        _: &FoldSelectedRanges,
20312        window: &mut Window,
20313        cx: &mut Context<Self>,
20314    ) {
20315        let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
20316        let selections = self.selections.all_adjusted(&display_map);
20317        let ranges = selections
20318            .into_iter()
20319            .map(|s| Crease::simple(s.range(), display_map.fold_placeholder.clone()))
20320            .collect::<Vec<_>>();
20321        self.fold_creases(ranges, true, window, cx);
20322    }
20323
20324    pub fn fold_ranges<T: ToOffset + Clone>(
20325        &mut self,
20326        ranges: Vec<Range<T>>,
20327        auto_scroll: bool,
20328        window: &mut Window,
20329        cx: &mut Context<Self>,
20330    ) {
20331        let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
20332        let ranges = ranges
20333            .into_iter()
20334            .map(|r| Crease::simple(r, display_map.fold_placeholder.clone()))
20335            .collect::<Vec<_>>();
20336        self.fold_creases(ranges, auto_scroll, window, cx);
20337    }
20338
20339    pub fn fold_creases<T: ToOffset + Clone>(
20340        &mut self,
20341        creases: Vec<Crease<T>>,
20342        auto_scroll: bool,
20343        _window: &mut Window,
20344        cx: &mut Context<Self>,
20345    ) {
20346        if creases.is_empty() {
20347            return;
20348        }
20349
20350        self.display_map.update(cx, |map, cx| map.fold(creases, cx));
20351
20352        if auto_scroll {
20353            self.request_autoscroll(Autoscroll::fit(), cx);
20354        }
20355
20356        cx.notify();
20357
20358        self.scrollbar_marker_state.dirty = true;
20359        self.folds_did_change(cx);
20360    }
20361
20362    /// Removes any folds whose ranges intersect any of the given ranges.
20363    pub fn unfold_ranges<T: ToOffset + Clone>(
20364        &mut self,
20365        ranges: &[Range<T>],
20366        inclusive: bool,
20367        auto_scroll: bool,
20368        cx: &mut Context<Self>,
20369    ) {
20370        self.remove_folds_with(ranges, auto_scroll, cx, |map, cx| {
20371            map.unfold_intersecting(ranges.iter().cloned(), inclusive, cx);
20372        });
20373        self.folds_did_change(cx);
20374    }
20375
20376    pub fn fold_buffer(&mut self, buffer_id: BufferId, cx: &mut Context<Self>) {
20377        self.fold_buffers([buffer_id], cx);
20378    }
20379
20380    pub fn fold_buffers(
20381        &mut self,
20382        buffer_ids: impl IntoIterator<Item = BufferId>,
20383        cx: &mut Context<Self>,
20384    ) {
20385        if self.buffer().read(cx).is_singleton() {
20386            return;
20387        }
20388
20389        let ids_to_fold: Vec<BufferId> = buffer_ids
20390            .into_iter()
20391            .filter(|id| !self.is_buffer_folded(*id, cx))
20392            .collect();
20393
20394        if ids_to_fold.is_empty() {
20395            return;
20396        }
20397
20398        let mut all_folded_excerpt_ids = Vec::new();
20399        for buffer_id in &ids_to_fold {
20400            let folded_excerpts = self.buffer().read(cx).excerpts_for_buffer(*buffer_id, cx);
20401            all_folded_excerpt_ids.extend(folded_excerpts.into_iter().map(|(id, _)| id));
20402        }
20403
20404        self.display_map.update(cx, |display_map, cx| {
20405            display_map.fold_buffers(ids_to_fold.clone(), cx)
20406        });
20407
20408        let snapshot = self.display_snapshot(cx);
20409        self.selections.change_with(&snapshot, |selections| {
20410            for buffer_id in ids_to_fold {
20411                selections.remove_selections_from_buffer(buffer_id);
20412            }
20413        });
20414
20415        cx.emit(EditorEvent::BufferFoldToggled {
20416            ids: all_folded_excerpt_ids,
20417            folded: true,
20418        });
20419        cx.notify();
20420    }
20421
20422    pub fn unfold_buffer(&mut self, buffer_id: BufferId, cx: &mut Context<Self>) {
20423        if self.buffer().read(cx).is_singleton() || !self.is_buffer_folded(buffer_id, cx) {
20424            return;
20425        }
20426        let unfolded_excerpts = self.buffer().read(cx).excerpts_for_buffer(buffer_id, cx);
20427        self.display_map.update(cx, |display_map, cx| {
20428            display_map.unfold_buffers([buffer_id], cx);
20429        });
20430        cx.emit(EditorEvent::BufferFoldToggled {
20431            ids: unfolded_excerpts.iter().map(|&(id, _)| id).collect(),
20432            folded: false,
20433        });
20434        cx.notify();
20435    }
20436
20437    pub fn is_buffer_folded(&self, buffer: BufferId, cx: &App) -> bool {
20438        self.display_map.read(cx).is_buffer_folded(buffer)
20439    }
20440
20441    pub fn has_any_buffer_folded(&self, cx: &App) -> bool {
20442        if self.buffer().read(cx).is_singleton() {
20443            return false;
20444        }
20445        !self.folded_buffers(cx).is_empty()
20446    }
20447
20448    pub fn folded_buffers<'a>(&self, cx: &'a App) -> &'a HashSet<BufferId> {
20449        self.display_map.read(cx).folded_buffers()
20450    }
20451
20452    pub fn disable_header_for_buffer(&mut self, buffer_id: BufferId, cx: &mut Context<Self>) {
20453        self.display_map.update(cx, |display_map, cx| {
20454            display_map.disable_header_for_buffer(buffer_id, cx);
20455        });
20456        cx.notify();
20457    }
20458
20459    /// Removes any folds with the given ranges.
20460    pub fn remove_folds_with_type<T: ToOffset + Clone>(
20461        &mut self,
20462        ranges: &[Range<T>],
20463        type_id: TypeId,
20464        auto_scroll: bool,
20465        cx: &mut Context<Self>,
20466    ) {
20467        self.remove_folds_with(ranges, auto_scroll, cx, |map, cx| {
20468            map.remove_folds_with_type(ranges.iter().cloned(), type_id, cx)
20469        });
20470        self.folds_did_change(cx);
20471    }
20472
20473    fn remove_folds_with<T: ToOffset + Clone>(
20474        &mut self,
20475        ranges: &[Range<T>],
20476        auto_scroll: bool,
20477        cx: &mut Context<Self>,
20478        update: impl FnOnce(&mut DisplayMap, &mut Context<DisplayMap>),
20479    ) {
20480        if ranges.is_empty() {
20481            return;
20482        }
20483
20484        let mut buffers_affected = HashSet::default();
20485        let multi_buffer = self.buffer().read(cx);
20486        for range in ranges {
20487            if let Some((_, buffer, _)) = multi_buffer.excerpt_containing(range.start.clone(), cx) {
20488                buffers_affected.insert(buffer.read(cx).remote_id());
20489            };
20490        }
20491
20492        self.display_map.update(cx, update);
20493
20494        if auto_scroll {
20495            self.request_autoscroll(Autoscroll::fit(), cx);
20496        }
20497
20498        cx.notify();
20499        self.scrollbar_marker_state.dirty = true;
20500        self.active_indent_guides_state.dirty = true;
20501    }
20502
20503    pub fn update_renderer_widths(
20504        &mut self,
20505        widths: impl IntoIterator<Item = (ChunkRendererId, Pixels)>,
20506        cx: &mut Context<Self>,
20507    ) -> bool {
20508        self.display_map
20509            .update(cx, |map, cx| map.update_fold_widths(widths, cx))
20510    }
20511
20512    pub fn default_fold_placeholder(&self, cx: &App) -> FoldPlaceholder {
20513        self.display_map.read(cx).fold_placeholder.clone()
20514    }
20515
20516    pub fn set_expand_all_diff_hunks(&mut self, cx: &mut App) {
20517        self.buffer.update(cx, |buffer, cx| {
20518            buffer.set_all_diff_hunks_expanded(cx);
20519        });
20520    }
20521
20522    pub fn expand_all_diff_hunks(
20523        &mut self,
20524        _: &ExpandAllDiffHunks,
20525        _window: &mut Window,
20526        cx: &mut Context<Self>,
20527    ) {
20528        self.buffer.update(cx, |buffer, cx| {
20529            buffer.expand_diff_hunks(vec![Anchor::min()..Anchor::max()], cx)
20530        });
20531    }
20532
20533    pub fn collapse_all_diff_hunks(
20534        &mut self,
20535        _: &CollapseAllDiffHunks,
20536        _window: &mut Window,
20537        cx: &mut Context<Self>,
20538    ) {
20539        self.buffer.update(cx, |buffer, cx| {
20540            buffer.collapse_diff_hunks(vec![Anchor::min()..Anchor::max()], cx)
20541        });
20542    }
20543
20544    pub fn toggle_selected_diff_hunks(
20545        &mut self,
20546        _: &ToggleSelectedDiffHunks,
20547        _window: &mut Window,
20548        cx: &mut Context<Self>,
20549    ) {
20550        let ranges: Vec<_> = self
20551            .selections
20552            .disjoint_anchors()
20553            .iter()
20554            .map(|s| s.range())
20555            .collect();
20556        self.toggle_diff_hunks_in_ranges(ranges, cx);
20557    }
20558
20559    pub fn diff_hunks_in_ranges<'a>(
20560        &'a self,
20561        ranges: &'a [Range<Anchor>],
20562        buffer: &'a MultiBufferSnapshot,
20563    ) -> impl 'a + Iterator<Item = MultiBufferDiffHunk> {
20564        ranges.iter().flat_map(move |range| {
20565            let end_excerpt_id = range.end.excerpt_id;
20566            let range = range.to_point(buffer);
20567            let mut peek_end = range.end;
20568            if range.end.row < buffer.max_row().0 {
20569                peek_end = Point::new(range.end.row + 1, 0);
20570            }
20571            buffer
20572                .diff_hunks_in_range(range.start..peek_end)
20573                .filter(move |hunk| hunk.excerpt_id.cmp(&end_excerpt_id, buffer).is_le())
20574        })
20575    }
20576
20577    pub fn has_stageable_diff_hunks_in_ranges(
20578        &self,
20579        ranges: &[Range<Anchor>],
20580        snapshot: &MultiBufferSnapshot,
20581    ) -> bool {
20582        let mut hunks = self.diff_hunks_in_ranges(ranges, snapshot);
20583        hunks.any(|hunk| hunk.status().has_secondary_hunk())
20584    }
20585
20586    pub fn toggle_staged_selected_diff_hunks(
20587        &mut self,
20588        _: &::git::ToggleStaged,
20589        _: &mut Window,
20590        cx: &mut Context<Self>,
20591    ) {
20592        let snapshot = self.buffer.read(cx).snapshot(cx);
20593        let ranges: Vec<_> = self
20594            .selections
20595            .disjoint_anchors()
20596            .iter()
20597            .map(|s| s.range())
20598            .collect();
20599        let stage = self.has_stageable_diff_hunks_in_ranges(&ranges, &snapshot);
20600        self.stage_or_unstage_diff_hunks(stage, ranges, cx);
20601    }
20602
20603    pub fn set_render_diff_hunk_controls(
20604        &mut self,
20605        render_diff_hunk_controls: RenderDiffHunkControlsFn,
20606        cx: &mut Context<Self>,
20607    ) {
20608        self.render_diff_hunk_controls = render_diff_hunk_controls;
20609        cx.notify();
20610    }
20611
20612    pub fn stage_and_next(
20613        &mut self,
20614        _: &::git::StageAndNext,
20615        window: &mut Window,
20616        cx: &mut Context<Self>,
20617    ) {
20618        self.do_stage_or_unstage_and_next(true, window, cx);
20619    }
20620
20621    pub fn unstage_and_next(
20622        &mut self,
20623        _: &::git::UnstageAndNext,
20624        window: &mut Window,
20625        cx: &mut Context<Self>,
20626    ) {
20627        self.do_stage_or_unstage_and_next(false, window, cx);
20628    }
20629
20630    pub fn stage_or_unstage_diff_hunks(
20631        &mut self,
20632        stage: bool,
20633        ranges: Vec<Range<Anchor>>,
20634        cx: &mut Context<Self>,
20635    ) {
20636        if self.delegate_stage_and_restore {
20637            let snapshot = self.buffer.read(cx).snapshot(cx);
20638            let hunks: Vec<_> = self.diff_hunks_in_ranges(&ranges, &snapshot).collect();
20639            if !hunks.is_empty() {
20640                cx.emit(EditorEvent::StageOrUnstageRequested { stage, hunks });
20641            }
20642            return;
20643        }
20644        let task = self.save_buffers_for_ranges_if_needed(&ranges, cx);
20645        cx.spawn(async move |this, cx| {
20646            task.await?;
20647            this.update(cx, |this, cx| {
20648                let snapshot = this.buffer.read(cx).snapshot(cx);
20649                let chunk_by = this
20650                    .diff_hunks_in_ranges(&ranges, &snapshot)
20651                    .chunk_by(|hunk| hunk.buffer_id);
20652                for (buffer_id, hunks) in &chunk_by {
20653                    this.do_stage_or_unstage(stage, buffer_id, hunks, cx);
20654                }
20655            })
20656        })
20657        .detach_and_log_err(cx);
20658    }
20659
20660    fn save_buffers_for_ranges_if_needed(
20661        &mut self,
20662        ranges: &[Range<Anchor>],
20663        cx: &mut Context<Editor>,
20664    ) -> Task<Result<()>> {
20665        let multibuffer = self.buffer.read(cx);
20666        let snapshot = multibuffer.read(cx);
20667        let buffer_ids: HashSet<_> = ranges
20668            .iter()
20669            .flat_map(|range| snapshot.buffer_ids_for_range(range.clone()))
20670            .collect();
20671        drop(snapshot);
20672
20673        let mut buffers = HashSet::default();
20674        for buffer_id in buffer_ids {
20675            if let Some(buffer_entity) = multibuffer.buffer(buffer_id) {
20676                let buffer = buffer_entity.read(cx);
20677                if buffer.file().is_some_and(|file| file.disk_state().exists()) && buffer.is_dirty()
20678                {
20679                    buffers.insert(buffer_entity);
20680                }
20681            }
20682        }
20683
20684        if let Some(project) = &self.project {
20685            project.update(cx, |project, cx| project.save_buffers(buffers, cx))
20686        } else {
20687            Task::ready(Ok(()))
20688        }
20689    }
20690
20691    fn do_stage_or_unstage_and_next(
20692        &mut self,
20693        stage: bool,
20694        window: &mut Window,
20695        cx: &mut Context<Self>,
20696    ) {
20697        let ranges = self.selections.disjoint_anchor_ranges().collect::<Vec<_>>();
20698
20699        if ranges.iter().any(|range| range.start != range.end) {
20700            self.stage_or_unstage_diff_hunks(stage, ranges, cx);
20701            return;
20702        }
20703
20704        self.stage_or_unstage_diff_hunks(stage, ranges, cx);
20705        let snapshot = self.snapshot(window, cx);
20706        let position = self
20707            .selections
20708            .newest::<Point>(&snapshot.display_snapshot)
20709            .head();
20710        let mut row = snapshot
20711            .buffer_snapshot()
20712            .diff_hunks_in_range(position..snapshot.buffer_snapshot().max_point())
20713            .find(|hunk| hunk.row_range.start.0 > position.row)
20714            .map(|hunk| hunk.row_range.start);
20715
20716        let all_diff_hunks_expanded = self.buffer().read(cx).all_diff_hunks_expanded();
20717        // Outside of the project diff editor, wrap around to the beginning.
20718        if !all_diff_hunks_expanded {
20719            row = row.or_else(|| {
20720                snapshot
20721                    .buffer_snapshot()
20722                    .diff_hunks_in_range(Point::zero()..position)
20723                    .find(|hunk| hunk.row_range.end.0 < position.row)
20724                    .map(|hunk| hunk.row_range.start)
20725            });
20726        }
20727
20728        if let Some(row) = row {
20729            let destination = Point::new(row.0, 0);
20730            let autoscroll = Autoscroll::center();
20731
20732            self.unfold_ranges(&[destination..destination], false, false, cx);
20733            self.change_selections(SelectionEffects::scroll(autoscroll), window, cx, |s| {
20734                s.select_ranges([destination..destination]);
20735            });
20736        }
20737    }
20738
20739    pub(crate) fn do_stage_or_unstage(
20740        &self,
20741        stage: bool,
20742        buffer_id: BufferId,
20743        hunks: impl Iterator<Item = MultiBufferDiffHunk>,
20744        cx: &mut App,
20745    ) -> Option<()> {
20746        let project = self.project()?;
20747        let buffer = project.read(cx).buffer_for_id(buffer_id, cx)?;
20748        let diff = self.buffer.read(cx).diff_for(buffer_id)?;
20749        let buffer_snapshot = buffer.read(cx).snapshot();
20750        let file_exists = buffer_snapshot
20751            .file()
20752            .is_some_and(|file| file.disk_state().exists());
20753        diff.update(cx, |diff, cx| {
20754            diff.stage_or_unstage_hunks(
20755                stage,
20756                &hunks
20757                    .map(|hunk| buffer_diff::DiffHunk {
20758                        buffer_range: hunk.buffer_range,
20759                        // We don't need to pass in word diffs here because they're only used for rendering and
20760                        // this function changes internal state
20761                        base_word_diffs: Vec::default(),
20762                        buffer_word_diffs: Vec::default(),
20763                        diff_base_byte_range: hunk.diff_base_byte_range.start.0
20764                            ..hunk.diff_base_byte_range.end.0,
20765                        secondary_status: hunk.status.secondary,
20766                        range: Point::zero()..Point::zero(), // unused
20767                    })
20768                    .collect::<Vec<_>>(),
20769                &buffer_snapshot,
20770                file_exists,
20771                cx,
20772            )
20773        });
20774        None
20775    }
20776
20777    pub fn expand_selected_diff_hunks(&mut self, cx: &mut Context<Self>) {
20778        let ranges: Vec<_> = self
20779            .selections
20780            .disjoint_anchors()
20781            .iter()
20782            .map(|s| s.range())
20783            .collect();
20784        self.buffer
20785            .update(cx, |buffer, cx| buffer.expand_diff_hunks(ranges, cx))
20786    }
20787
20788    pub fn clear_expanded_diff_hunks(&mut self, cx: &mut Context<Self>) -> bool {
20789        self.buffer.update(cx, |buffer, cx| {
20790            let ranges = vec![Anchor::min()..Anchor::max()];
20791            if !buffer.all_diff_hunks_expanded()
20792                && buffer.has_expanded_diff_hunks_in_ranges(&ranges, cx)
20793            {
20794                buffer.collapse_diff_hunks(ranges, cx);
20795                true
20796            } else {
20797                false
20798            }
20799        })
20800    }
20801
20802    fn has_any_expanded_diff_hunks(&self, cx: &App) -> bool {
20803        if self.buffer.read(cx).all_diff_hunks_expanded() {
20804            return true;
20805        }
20806        let ranges = vec![Anchor::min()..Anchor::max()];
20807        self.buffer
20808            .read(cx)
20809            .has_expanded_diff_hunks_in_ranges(&ranges, cx)
20810    }
20811
20812    fn toggle_diff_hunks_in_ranges(
20813        &mut self,
20814        ranges: Vec<Range<Anchor>>,
20815        cx: &mut Context<Editor>,
20816    ) {
20817        self.buffer.update(cx, |buffer, cx| {
20818            let expand = !buffer.has_expanded_diff_hunks_in_ranges(&ranges, cx);
20819            buffer.expand_or_collapse_diff_hunks(ranges, expand, cx);
20820        })
20821    }
20822
20823    fn toggle_single_diff_hunk(&mut self, range: Range<Anchor>, cx: &mut Context<Self>) {
20824        self.buffer.update(cx, |buffer, cx| {
20825            buffer.toggle_single_diff_hunk(range, cx);
20826        })
20827    }
20828
20829    pub(crate) fn apply_all_diff_hunks(
20830        &mut self,
20831        _: &ApplyAllDiffHunks,
20832        window: &mut Window,
20833        cx: &mut Context<Self>,
20834    ) {
20835        self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
20836
20837        let buffers = self.buffer.read(cx).all_buffers();
20838        for branch_buffer in buffers {
20839            branch_buffer.update(cx, |branch_buffer, cx| {
20840                branch_buffer.merge_into_base(Vec::new(), cx);
20841            });
20842        }
20843
20844        if let Some(project) = self.project.clone() {
20845            self.save(
20846                SaveOptions {
20847                    format: true,
20848                    autosave: false,
20849                },
20850                project,
20851                window,
20852                cx,
20853            )
20854            .detach_and_log_err(cx);
20855        }
20856    }
20857
20858    pub(crate) fn apply_selected_diff_hunks(
20859        &mut self,
20860        _: &ApplyDiffHunk,
20861        window: &mut Window,
20862        cx: &mut Context<Self>,
20863    ) {
20864        self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
20865        let snapshot = self.snapshot(window, cx);
20866        let hunks = snapshot.hunks_for_ranges(
20867            self.selections
20868                .all(&snapshot.display_snapshot)
20869                .into_iter()
20870                .map(|selection| selection.range()),
20871        );
20872        let mut ranges_by_buffer = HashMap::default();
20873        self.transact(window, cx, |editor, _window, cx| {
20874            for hunk in hunks {
20875                if let Some(buffer) = editor.buffer.read(cx).buffer(hunk.buffer_id) {
20876                    ranges_by_buffer
20877                        .entry(buffer.clone())
20878                        .or_insert_with(Vec::new)
20879                        .push(hunk.buffer_range.to_offset(buffer.read(cx)));
20880                }
20881            }
20882
20883            for (buffer, ranges) in ranges_by_buffer {
20884                buffer.update(cx, |buffer, cx| {
20885                    buffer.merge_into_base(ranges, cx);
20886                });
20887            }
20888        });
20889
20890        if let Some(project) = self.project.clone() {
20891            self.save(
20892                SaveOptions {
20893                    format: true,
20894                    autosave: false,
20895                },
20896                project,
20897                window,
20898                cx,
20899            )
20900            .detach_and_log_err(cx);
20901        }
20902    }
20903
20904    pub fn set_gutter_hovered(&mut self, hovered: bool, cx: &mut Context<Self>) {
20905        if hovered != self.gutter_hovered {
20906            self.gutter_hovered = hovered;
20907            cx.notify();
20908        }
20909    }
20910
20911    pub fn insert_blocks(
20912        &mut self,
20913        blocks: impl IntoIterator<Item = BlockProperties<Anchor>>,
20914        autoscroll: Option<Autoscroll>,
20915        cx: &mut Context<Self>,
20916    ) -> Vec<CustomBlockId> {
20917        let blocks = self
20918            .display_map
20919            .update(cx, |display_map, cx| display_map.insert_blocks(blocks, cx));
20920        if let Some(autoscroll) = autoscroll {
20921            self.request_autoscroll(autoscroll, cx);
20922        }
20923        cx.notify();
20924        blocks
20925    }
20926
20927    pub fn resize_blocks(
20928        &mut self,
20929        heights: HashMap<CustomBlockId, u32>,
20930        autoscroll: Option<Autoscroll>,
20931        cx: &mut Context<Self>,
20932    ) {
20933        self.display_map
20934            .update(cx, |display_map, cx| display_map.resize_blocks(heights, cx));
20935        if let Some(autoscroll) = autoscroll {
20936            self.request_autoscroll(autoscroll, cx);
20937        }
20938        cx.notify();
20939    }
20940
20941    pub fn replace_blocks(
20942        &mut self,
20943        renderers: HashMap<CustomBlockId, RenderBlock>,
20944        autoscroll: Option<Autoscroll>,
20945        cx: &mut Context<Self>,
20946    ) {
20947        self.display_map
20948            .update(cx, |display_map, _cx| display_map.replace_blocks(renderers));
20949        if let Some(autoscroll) = autoscroll {
20950            self.request_autoscroll(autoscroll, cx);
20951        }
20952        cx.notify();
20953    }
20954
20955    pub fn remove_blocks(
20956        &mut self,
20957        block_ids: HashSet<CustomBlockId>,
20958        autoscroll: Option<Autoscroll>,
20959        cx: &mut Context<Self>,
20960    ) {
20961        self.display_map.update(cx, |display_map, cx| {
20962            display_map.remove_blocks(block_ids, cx)
20963        });
20964        if let Some(autoscroll) = autoscroll {
20965            self.request_autoscroll(autoscroll, cx);
20966        }
20967        cx.notify();
20968    }
20969
20970    pub fn row_for_block(
20971        &self,
20972        block_id: CustomBlockId,
20973        cx: &mut Context<Self>,
20974    ) -> Option<DisplayRow> {
20975        self.display_map
20976            .update(cx, |map, cx| map.row_for_block(block_id, cx))
20977    }
20978
20979    pub(crate) fn set_focused_block(&mut self, focused_block: FocusedBlock) {
20980        self.focused_block = Some(focused_block);
20981    }
20982
20983    pub(crate) fn take_focused_block(&mut self) -> Option<FocusedBlock> {
20984        self.focused_block.take()
20985    }
20986
20987    pub fn insert_creases(
20988        &mut self,
20989        creases: impl IntoIterator<Item = Crease<Anchor>>,
20990        cx: &mut Context<Self>,
20991    ) -> Vec<CreaseId> {
20992        self.display_map
20993            .update(cx, |map, cx| map.insert_creases(creases, cx))
20994    }
20995
20996    pub fn remove_creases(
20997        &mut self,
20998        ids: impl IntoIterator<Item = CreaseId>,
20999        cx: &mut Context<Self>,
21000    ) -> Vec<(CreaseId, Range<Anchor>)> {
21001        self.display_map
21002            .update(cx, |map, cx| map.remove_creases(ids, cx))
21003    }
21004
21005    pub fn longest_row(&self, cx: &mut App) -> DisplayRow {
21006        self.display_map
21007            .update(cx, |map, cx| map.snapshot(cx))
21008            .longest_row()
21009    }
21010
21011    pub fn max_point(&self, cx: &mut App) -> DisplayPoint {
21012        self.display_map
21013            .update(cx, |map, cx| map.snapshot(cx))
21014            .max_point()
21015    }
21016
21017    pub fn text(&self, cx: &App) -> String {
21018        self.buffer.read(cx).read(cx).text()
21019    }
21020
21021    pub fn is_empty(&self, cx: &App) -> bool {
21022        self.buffer.read(cx).read(cx).is_empty()
21023    }
21024
21025    pub fn text_option(&self, cx: &App) -> Option<String> {
21026        let text = self.text(cx);
21027        let text = text.trim();
21028
21029        if text.is_empty() {
21030            return None;
21031        }
21032
21033        Some(text.to_string())
21034    }
21035
21036    pub fn set_text(
21037        &mut self,
21038        text: impl Into<Arc<str>>,
21039        window: &mut Window,
21040        cx: &mut Context<Self>,
21041    ) {
21042        self.transact(window, cx, |this, _, cx| {
21043            this.buffer
21044                .read(cx)
21045                .as_singleton()
21046                .expect("you can only call set_text on editors for singleton buffers")
21047                .update(cx, |buffer, cx| buffer.set_text(text, cx));
21048        });
21049    }
21050
21051    pub fn display_text(&self, cx: &mut App) -> String {
21052        self.display_map
21053            .update(cx, |map, cx| map.snapshot(cx))
21054            .text()
21055    }
21056
21057    fn create_minimap(
21058        &self,
21059        minimap_settings: MinimapSettings,
21060        window: &mut Window,
21061        cx: &mut Context<Self>,
21062    ) -> Option<Entity<Self>> {
21063        (minimap_settings.minimap_enabled() && self.buffer_kind(cx) == ItemBufferKind::Singleton)
21064            .then(|| self.initialize_new_minimap(minimap_settings, window, cx))
21065    }
21066
21067    fn initialize_new_minimap(
21068        &self,
21069        minimap_settings: MinimapSettings,
21070        window: &mut Window,
21071        cx: &mut Context<Self>,
21072    ) -> Entity<Self> {
21073        const MINIMAP_FONT_WEIGHT: gpui::FontWeight = gpui::FontWeight::BLACK;
21074        const MINIMAP_FONT_FAMILY: SharedString = SharedString::new_static(".ZedMono");
21075
21076        let mut minimap = Editor::new_internal(
21077            EditorMode::Minimap {
21078                parent: cx.weak_entity(),
21079            },
21080            self.buffer.clone(),
21081            None,
21082            Some(self.display_map.clone()),
21083            window,
21084            cx,
21085        );
21086        let my_snapshot = self.display_map.update(cx, |map, cx| map.snapshot(cx));
21087        let minimap_snapshot = minimap.display_map.update(cx, |map, cx| map.snapshot(cx));
21088        minimap.scroll_manager.clone_state(
21089            &self.scroll_manager,
21090            &my_snapshot,
21091            &minimap_snapshot,
21092            cx,
21093        );
21094        minimap.set_text_style_refinement(TextStyleRefinement {
21095            font_size: Some(MINIMAP_FONT_SIZE),
21096            font_weight: Some(MINIMAP_FONT_WEIGHT),
21097            font_family: Some(MINIMAP_FONT_FAMILY),
21098            ..Default::default()
21099        });
21100        minimap.update_minimap_configuration(minimap_settings, cx);
21101        cx.new(|_| minimap)
21102    }
21103
21104    fn update_minimap_configuration(&mut self, minimap_settings: MinimapSettings, cx: &App) {
21105        let current_line_highlight = minimap_settings
21106            .current_line_highlight
21107            .unwrap_or_else(|| EditorSettings::get_global(cx).current_line_highlight);
21108        self.set_current_line_highlight(Some(current_line_highlight));
21109    }
21110
21111    pub fn minimap(&self) -> Option<&Entity<Self>> {
21112        self.minimap
21113            .as_ref()
21114            .filter(|_| self.minimap_visibility.visible())
21115    }
21116
21117    pub fn wrap_guides(&self, cx: &App) -> SmallVec<[(usize, bool); 2]> {
21118        let mut wrap_guides = smallvec![];
21119
21120        if self.show_wrap_guides == Some(false) {
21121            return wrap_guides;
21122        }
21123
21124        let settings = self.buffer.read(cx).language_settings(cx);
21125        if settings.show_wrap_guides {
21126            match self.soft_wrap_mode(cx) {
21127                SoftWrap::Column(soft_wrap) => {
21128                    wrap_guides.push((soft_wrap as usize, true));
21129                }
21130                SoftWrap::Bounded(soft_wrap) => {
21131                    wrap_guides.push((soft_wrap as usize, true));
21132                }
21133                SoftWrap::GitDiff | SoftWrap::None | SoftWrap::EditorWidth => {}
21134            }
21135            wrap_guides.extend(settings.wrap_guides.iter().map(|guide| (*guide, false)))
21136        }
21137
21138        wrap_guides
21139    }
21140
21141    pub fn soft_wrap_mode(&self, cx: &App) -> SoftWrap {
21142        let settings = self.buffer.read(cx).language_settings(cx);
21143        let mode = self.soft_wrap_mode_override.unwrap_or(settings.soft_wrap);
21144        match mode {
21145            language_settings::SoftWrap::PreferLine | language_settings::SoftWrap::None => {
21146                SoftWrap::None
21147            }
21148            language_settings::SoftWrap::EditorWidth => SoftWrap::EditorWidth,
21149            language_settings::SoftWrap::PreferredLineLength => {
21150                SoftWrap::Column(settings.preferred_line_length)
21151            }
21152            language_settings::SoftWrap::Bounded => {
21153                SoftWrap::Bounded(settings.preferred_line_length)
21154            }
21155        }
21156    }
21157
21158    pub fn set_soft_wrap_mode(
21159        &mut self,
21160        mode: language_settings::SoftWrap,
21161        cx: &mut Context<Self>,
21162    ) {
21163        self.soft_wrap_mode_override = Some(mode);
21164        cx.notify();
21165    }
21166
21167    pub fn set_hard_wrap(&mut self, hard_wrap: Option<usize>, cx: &mut Context<Self>) {
21168        self.hard_wrap = hard_wrap;
21169        cx.notify();
21170    }
21171
21172    pub fn set_text_style_refinement(&mut self, style: TextStyleRefinement) {
21173        self.text_style_refinement = Some(style);
21174    }
21175
21176    /// called by the Element so we know what style we were most recently rendered with.
21177    pub fn set_style(&mut self, style: EditorStyle, window: &mut Window, cx: &mut Context<Self>) {
21178        // We intentionally do not inform the display map about the minimap style
21179        // so that wrapping is not recalculated and stays consistent for the editor
21180        // and its linked minimap.
21181        if !self.mode.is_minimap() {
21182            let font = style.text.font();
21183            let font_size = style.text.font_size.to_pixels(window.rem_size());
21184            let display_map = self
21185                .placeholder_display_map
21186                .as_ref()
21187                .filter(|_| self.is_empty(cx))
21188                .unwrap_or(&self.display_map);
21189
21190            display_map.update(cx, |map, cx| map.set_font(font, font_size, cx));
21191        }
21192        self.style = Some(style);
21193    }
21194
21195    pub fn style(&mut self, cx: &App) -> &EditorStyle {
21196        if self.style.is_none() {
21197            self.style = Some(self.create_style(cx));
21198        }
21199        self.style.as_ref().unwrap()
21200    }
21201
21202    // Called by the element. This method is not designed to be called outside of the editor
21203    // element's layout code because it does not notify when rewrapping is computed synchronously.
21204    pub(crate) fn set_wrap_width(&self, width: Option<Pixels>, cx: &mut App) -> bool {
21205        if self.is_empty(cx) {
21206            self.placeholder_display_map
21207                .as_ref()
21208                .map_or(false, |display_map| {
21209                    display_map.update(cx, |map, cx| map.set_wrap_width(width, cx))
21210                })
21211        } else {
21212            self.display_map
21213                .update(cx, |map, cx| map.set_wrap_width(width, cx))
21214        }
21215    }
21216
21217    pub fn set_soft_wrap(&mut self) {
21218        self.soft_wrap_mode_override = Some(language_settings::SoftWrap::EditorWidth)
21219    }
21220
21221    pub fn toggle_soft_wrap(&mut self, _: &ToggleSoftWrap, _: &mut Window, cx: &mut Context<Self>) {
21222        if self.soft_wrap_mode_override.is_some() {
21223            self.soft_wrap_mode_override.take();
21224        } else {
21225            let soft_wrap = match self.soft_wrap_mode(cx) {
21226                SoftWrap::GitDiff => return,
21227                SoftWrap::None => language_settings::SoftWrap::EditorWidth,
21228                SoftWrap::EditorWidth | SoftWrap::Column(_) | SoftWrap::Bounded(_) => {
21229                    language_settings::SoftWrap::None
21230                }
21231            };
21232            self.soft_wrap_mode_override = Some(soft_wrap);
21233        }
21234        cx.notify();
21235    }
21236
21237    pub fn toggle_tab_bar(&mut self, _: &ToggleTabBar, _: &mut Window, cx: &mut Context<Self>) {
21238        let Some(workspace) = self.workspace() else {
21239            return;
21240        };
21241        let fs = workspace.read(cx).app_state().fs.clone();
21242        let current_show = TabBarSettings::get_global(cx).show;
21243        update_settings_file(fs, cx, move |setting, _| {
21244            setting.tab_bar.get_or_insert_default().show = Some(!current_show);
21245        });
21246    }
21247
21248    pub fn toggle_indent_guides(
21249        &mut self,
21250        _: &ToggleIndentGuides,
21251        _: &mut Window,
21252        cx: &mut Context<Self>,
21253    ) {
21254        let currently_enabled = self.should_show_indent_guides().unwrap_or_else(|| {
21255            self.buffer
21256                .read(cx)
21257                .language_settings(cx)
21258                .indent_guides
21259                .enabled
21260        });
21261        self.show_indent_guides = Some(!currently_enabled);
21262        cx.notify();
21263    }
21264
21265    fn should_show_indent_guides(&self) -> Option<bool> {
21266        self.show_indent_guides
21267    }
21268
21269    pub fn disable_indent_guides_for_buffer(
21270        &mut self,
21271        buffer_id: BufferId,
21272        cx: &mut Context<Self>,
21273    ) {
21274        self.buffers_with_disabled_indent_guides.insert(buffer_id);
21275        cx.notify();
21276    }
21277
21278    pub fn has_indent_guides_disabled_for_buffer(&self, buffer_id: BufferId) -> bool {
21279        self.buffers_with_disabled_indent_guides
21280            .contains(&buffer_id)
21281    }
21282
21283    pub fn toggle_line_numbers(
21284        &mut self,
21285        _: &ToggleLineNumbers,
21286        _: &mut Window,
21287        cx: &mut Context<Self>,
21288    ) {
21289        let mut editor_settings = EditorSettings::get_global(cx).clone();
21290        editor_settings.gutter.line_numbers = !editor_settings.gutter.line_numbers;
21291        EditorSettings::override_global(editor_settings, cx);
21292    }
21293
21294    pub fn line_numbers_enabled(&self, cx: &App) -> bool {
21295        if let Some(show_line_numbers) = self.show_line_numbers {
21296            return show_line_numbers;
21297        }
21298        EditorSettings::get_global(cx).gutter.line_numbers
21299    }
21300
21301    pub fn relative_line_numbers(&self, cx: &App) -> RelativeLineNumbers {
21302        match (
21303            self.use_relative_line_numbers,
21304            EditorSettings::get_global(cx).relative_line_numbers,
21305        ) {
21306            (None, setting) => setting,
21307            (Some(false), _) => RelativeLineNumbers::Disabled,
21308            (Some(true), RelativeLineNumbers::Wrapped) => RelativeLineNumbers::Wrapped,
21309            (Some(true), _) => RelativeLineNumbers::Enabled,
21310        }
21311    }
21312
21313    pub fn toggle_relative_line_numbers(
21314        &mut self,
21315        _: &ToggleRelativeLineNumbers,
21316        _: &mut Window,
21317        cx: &mut Context<Self>,
21318    ) {
21319        let is_relative = self.relative_line_numbers(cx);
21320        self.set_relative_line_number(Some(!is_relative.enabled()), cx)
21321    }
21322
21323    pub fn set_relative_line_number(&mut self, is_relative: Option<bool>, cx: &mut Context<Self>) {
21324        self.use_relative_line_numbers = is_relative;
21325        cx.notify();
21326    }
21327
21328    pub fn set_show_gutter(&mut self, show_gutter: bool, cx: &mut Context<Self>) {
21329        self.show_gutter = show_gutter;
21330        cx.notify();
21331    }
21332
21333    pub fn set_show_scrollbars(&mut self, show: bool, cx: &mut Context<Self>) {
21334        self.show_scrollbars = ScrollbarAxes {
21335            horizontal: show,
21336            vertical: show,
21337        };
21338        cx.notify();
21339    }
21340
21341    pub fn set_show_vertical_scrollbar(&mut self, show: bool, cx: &mut Context<Self>) {
21342        self.show_scrollbars.vertical = show;
21343        cx.notify();
21344    }
21345
21346    pub fn set_show_horizontal_scrollbar(&mut self, show: bool, cx: &mut Context<Self>) {
21347        self.show_scrollbars.horizontal = show;
21348        cx.notify();
21349    }
21350
21351    pub fn set_minimap_visibility(
21352        &mut self,
21353        minimap_visibility: MinimapVisibility,
21354        window: &mut Window,
21355        cx: &mut Context<Self>,
21356    ) {
21357        if self.minimap_visibility != minimap_visibility {
21358            if minimap_visibility.visible() && self.minimap.is_none() {
21359                let minimap_settings = EditorSettings::get_global(cx).minimap;
21360                self.minimap =
21361                    self.create_minimap(minimap_settings.with_show_override(), window, cx);
21362            }
21363            self.minimap_visibility = minimap_visibility;
21364            cx.notify();
21365        }
21366    }
21367
21368    pub fn disable_scrollbars_and_minimap(&mut self, window: &mut Window, cx: &mut Context<Self>) {
21369        self.set_show_scrollbars(false, cx);
21370        self.set_minimap_visibility(MinimapVisibility::Disabled, window, cx);
21371    }
21372
21373    pub fn hide_minimap_by_default(&mut self, window: &mut Window, cx: &mut Context<Self>) {
21374        self.set_minimap_visibility(self.minimap_visibility.hidden(), window, cx);
21375    }
21376
21377    /// Normally the text in full mode and auto height editors is padded on the
21378    /// left side by roughly half a character width for improved hit testing.
21379    ///
21380    /// Use this method to disable this for cases where this is not wanted (e.g.
21381    /// if you want to align the editor text with some other text above or below)
21382    /// or if you want to add this padding to single-line editors.
21383    pub fn set_offset_content(&mut self, offset_content: bool, cx: &mut Context<Self>) {
21384        self.offset_content = offset_content;
21385        cx.notify();
21386    }
21387
21388    pub fn set_show_line_numbers(&mut self, show_line_numbers: bool, cx: &mut Context<Self>) {
21389        self.show_line_numbers = Some(show_line_numbers);
21390        cx.notify();
21391    }
21392
21393    pub fn disable_expand_excerpt_buttons(&mut self, cx: &mut Context<Self>) {
21394        self.disable_expand_excerpt_buttons = true;
21395        cx.notify();
21396    }
21397
21398    pub fn set_number_deleted_lines(&mut self, number: bool, cx: &mut Context<Self>) {
21399        self.number_deleted_lines = number;
21400        cx.notify();
21401    }
21402
21403    pub fn set_delegate_expand_excerpts(&mut self, delegate: bool) {
21404        self.delegate_expand_excerpts = delegate;
21405    }
21406
21407    pub fn set_delegate_stage_and_restore(&mut self, delegate: bool) {
21408        self.delegate_stage_and_restore = delegate;
21409    }
21410
21411    pub fn set_delegate_open_excerpts(&mut self, delegate: bool) {
21412        self.delegate_open_excerpts = delegate;
21413    }
21414
21415    pub fn set_on_local_selections_changed(
21416        &mut self,
21417        callback: Option<Box<dyn Fn(Point, &mut Window, &mut Context<Self>) + 'static>>,
21418    ) {
21419        self.on_local_selections_changed = callback;
21420    }
21421
21422    pub fn set_suppress_selection_callback(&mut self, suppress: bool) {
21423        self.suppress_selection_callback = suppress;
21424    }
21425
21426    pub fn set_show_git_diff_gutter(&mut self, show_git_diff_gutter: bool, cx: &mut Context<Self>) {
21427        self.show_git_diff_gutter = Some(show_git_diff_gutter);
21428        cx.notify();
21429    }
21430
21431    pub fn set_show_code_actions(&mut self, show_code_actions: bool, cx: &mut Context<Self>) {
21432        self.show_code_actions = Some(show_code_actions);
21433        cx.notify();
21434    }
21435
21436    pub fn set_show_runnables(&mut self, show_runnables: bool, cx: &mut Context<Self>) {
21437        self.show_runnables = Some(show_runnables);
21438        cx.notify();
21439    }
21440
21441    pub fn set_show_breakpoints(&mut self, show_breakpoints: bool, cx: &mut Context<Self>) {
21442        self.show_breakpoints = Some(show_breakpoints);
21443        cx.notify();
21444    }
21445
21446    pub fn set_show_diff_review_button(&mut self, show: bool, cx: &mut Context<Self>) {
21447        self.show_diff_review_button = show;
21448        cx.notify();
21449    }
21450
21451    pub fn show_diff_review_button(&self) -> bool {
21452        self.show_diff_review_button
21453    }
21454
21455    pub fn render_diff_review_button(
21456        &self,
21457        display_row: DisplayRow,
21458        width: Pixels,
21459        cx: &mut Context<Self>,
21460    ) -> impl IntoElement {
21461        let text_color = cx.theme().colors().text;
21462        let icon_color = cx.theme().colors().icon_accent;
21463
21464        h_flex()
21465            .id("diff_review_button")
21466            .cursor_pointer()
21467            .w(width - px(1.))
21468            .h(relative(0.9))
21469            .justify_center()
21470            .rounded_sm()
21471            .border_1()
21472            .border_color(text_color.opacity(0.1))
21473            .bg(text_color.opacity(0.15))
21474            .hover(|s| {
21475                s.bg(icon_color.opacity(0.4))
21476                    .border_color(icon_color.opacity(0.5))
21477            })
21478            .child(Icon::new(IconName::Plus).size(IconSize::Small))
21479            .tooltip(Tooltip::text("Add Review (drag to select multiple lines)"))
21480            .on_mouse_down(
21481                gpui::MouseButton::Left,
21482                cx.listener(move |editor, _event: &gpui::MouseDownEvent, window, cx| {
21483                    editor.start_diff_review_drag(display_row, window, cx);
21484                }),
21485            )
21486    }
21487
21488    pub fn start_diff_review_drag(
21489        &mut self,
21490        display_row: DisplayRow,
21491        window: &mut Window,
21492        cx: &mut Context<Self>,
21493    ) {
21494        let snapshot = self.snapshot(window, cx);
21495        let point = snapshot
21496            .display_snapshot
21497            .display_point_to_point(DisplayPoint::new(display_row, 0), Bias::Left);
21498        let anchor = snapshot.buffer_snapshot().anchor_before(point);
21499        self.diff_review_drag_state = Some(DiffReviewDragState {
21500            start_anchor: anchor,
21501            current_anchor: anchor,
21502        });
21503        cx.notify();
21504    }
21505
21506    pub fn update_diff_review_drag(
21507        &mut self,
21508        display_row: DisplayRow,
21509        window: &mut Window,
21510        cx: &mut Context<Self>,
21511    ) {
21512        if self.diff_review_drag_state.is_none() {
21513            return;
21514        }
21515        let snapshot = self.snapshot(window, cx);
21516        let point = snapshot
21517            .display_snapshot
21518            .display_point_to_point(display_row.as_display_point(), Bias::Left);
21519        let anchor = snapshot.buffer_snapshot().anchor_before(point);
21520        if let Some(drag_state) = &mut self.diff_review_drag_state {
21521            drag_state.current_anchor = anchor;
21522            cx.notify();
21523        }
21524    }
21525
21526    pub fn end_diff_review_drag(&mut self, window: &mut Window, cx: &mut Context<Self>) {
21527        if let Some(drag_state) = self.diff_review_drag_state.take() {
21528            let snapshot = self.snapshot(window, cx);
21529            let range = drag_state.row_range(&snapshot.display_snapshot);
21530            self.show_diff_review_overlay(*range.start()..*range.end(), window, cx);
21531        }
21532        cx.notify();
21533    }
21534
21535    pub fn cancel_diff_review_drag(&mut self, cx: &mut Context<Self>) {
21536        self.diff_review_drag_state = None;
21537        cx.notify();
21538    }
21539
21540    /// Calculates the appropriate block height for the diff review overlay.
21541    /// Height is in lines: 2 for input row, 1 for header when comments exist,
21542    /// and 2 lines per comment when expanded.
21543    fn calculate_overlay_height(
21544        &self,
21545        hunk_key: &DiffHunkKey,
21546        comments_expanded: bool,
21547        snapshot: &MultiBufferSnapshot,
21548    ) -> u32 {
21549        let comment_count = self.hunk_comment_count(hunk_key, snapshot);
21550        let base_height: u32 = 2; // Input row with avatar and buttons
21551
21552        if comment_count == 0 {
21553            base_height
21554        } else if comments_expanded {
21555            // Header (1 line) + 2 lines per comment
21556            base_height + 1 + (comment_count as u32 * 2)
21557        } else {
21558            // Just header when collapsed
21559            base_height + 1
21560        }
21561    }
21562
21563    pub fn show_diff_review_overlay(
21564        &mut self,
21565        display_range: Range<DisplayRow>,
21566        window: &mut Window,
21567        cx: &mut Context<Self>,
21568    ) {
21569        let Range { start, end } = display_range.sorted();
21570
21571        let buffer_snapshot = self.buffer.read(cx).snapshot(cx);
21572        let editor_snapshot = self.snapshot(window, cx);
21573
21574        // Convert display rows to multibuffer points
21575        let start_point = editor_snapshot
21576            .display_snapshot
21577            .display_point_to_point(start.as_display_point(), Bias::Left);
21578        let end_point = editor_snapshot
21579            .display_snapshot
21580            .display_point_to_point(end.as_display_point(), Bias::Left);
21581        let end_multi_buffer_row = MultiBufferRow(end_point.row);
21582
21583        // Create anchor range for the selected lines (start of first line to end of last line)
21584        let line_end = Point::new(
21585            end_point.row,
21586            buffer_snapshot.line_len(end_multi_buffer_row),
21587        );
21588        let anchor_range =
21589            buffer_snapshot.anchor_after(start_point)..buffer_snapshot.anchor_before(line_end);
21590
21591        // Compute the hunk key for this display row
21592        let file_path = buffer_snapshot
21593            .file_at(start_point)
21594            .map(|file: &Arc<dyn language::File>| file.path().clone())
21595            .unwrap_or_else(|| Arc::from(util::rel_path::RelPath::empty()));
21596        let hunk_start_anchor = buffer_snapshot.anchor_before(start_point);
21597        let new_hunk_key = DiffHunkKey {
21598            file_path,
21599            hunk_start_anchor,
21600        };
21601
21602        // Check if we already have an overlay for this hunk
21603        if let Some(existing_overlay) = self.diff_review_overlays.iter().find(|overlay| {
21604            Self::hunk_keys_match(&overlay.hunk_key, &new_hunk_key, &buffer_snapshot)
21605        }) {
21606            // Just focus the existing overlay's prompt editor
21607            let focus_handle = existing_overlay.prompt_editor.focus_handle(cx);
21608            window.focus(&focus_handle, cx);
21609            return;
21610        }
21611
21612        // Dismiss overlays that have no comments for their hunks
21613        self.dismiss_overlays_without_comments(cx);
21614
21615        // Get the current user's avatar URI from the project's user_store
21616        let user_avatar_uri = self.project.as_ref().and_then(|project| {
21617            let user_store = project.read(cx).user_store();
21618            user_store
21619                .read(cx)
21620                .current_user()
21621                .map(|user| user.avatar_uri.clone())
21622        });
21623
21624        // Create anchor at the end of the last row so the block appears immediately below it
21625        // Use multibuffer coordinates for anchor creation
21626        let line_len = buffer_snapshot.line_len(end_multi_buffer_row);
21627        let anchor = buffer_snapshot.anchor_after(Point::new(end_multi_buffer_row.0, line_len));
21628
21629        // Use the hunk key we already computed
21630        let hunk_key = new_hunk_key;
21631
21632        // Create the prompt editor for the review input
21633        let prompt_editor = cx.new(|cx| {
21634            let mut editor = Editor::single_line(window, cx);
21635            editor.set_placeholder_text("Add a review comment...", window, cx);
21636            editor
21637        });
21638
21639        // Register the Newline action on the prompt editor to submit the review
21640        let parent_editor = cx.entity().downgrade();
21641        let subscription = prompt_editor.update(cx, |prompt_editor, _cx| {
21642            prompt_editor.register_action({
21643                let parent_editor = parent_editor.clone();
21644                move |_: &crate::actions::Newline, window, cx| {
21645                    if let Some(editor) = parent_editor.upgrade() {
21646                        editor.update(cx, |editor, cx| {
21647                            editor.submit_diff_review_comment(window, cx);
21648                        });
21649                    }
21650                }
21651            })
21652        });
21653
21654        // Calculate initial height based on existing comments for this hunk
21655        let initial_height = self.calculate_overlay_height(&hunk_key, true, &buffer_snapshot);
21656
21657        // Create the overlay block
21658        let prompt_editor_for_render = prompt_editor.clone();
21659        let hunk_key_for_render = hunk_key.clone();
21660        let editor_handle = cx.entity().downgrade();
21661        let block = BlockProperties {
21662            style: BlockStyle::Sticky,
21663            placement: BlockPlacement::Below(anchor),
21664            height: Some(initial_height),
21665            render: Arc::new(move |cx| {
21666                Self::render_diff_review_overlay(
21667                    &prompt_editor_for_render,
21668                    &hunk_key_for_render,
21669                    &editor_handle,
21670                    cx,
21671                )
21672            }),
21673            priority: 0,
21674        };
21675
21676        let block_ids = self.insert_blocks([block], None, cx);
21677        let Some(block_id) = block_ids.into_iter().next() else {
21678            log::error!("Failed to insert diff review overlay block");
21679            return;
21680        };
21681
21682        self.diff_review_overlays.push(DiffReviewOverlay {
21683            anchor_range,
21684            block_id,
21685            prompt_editor: prompt_editor.clone(),
21686            hunk_key,
21687            comments_expanded: true,
21688            inline_edit_editors: HashMap::default(),
21689            inline_edit_subscriptions: HashMap::default(),
21690            user_avatar_uri,
21691            _subscription: subscription,
21692        });
21693
21694        // Focus the prompt editor
21695        let focus_handle = prompt_editor.focus_handle(cx);
21696        window.focus(&focus_handle, cx);
21697
21698        cx.notify();
21699    }
21700
21701    /// Dismisses all diff review overlays.
21702    pub fn dismiss_all_diff_review_overlays(&mut self, cx: &mut Context<Self>) {
21703        if self.diff_review_overlays.is_empty() {
21704            return;
21705        }
21706        let block_ids: HashSet<_> = self
21707            .diff_review_overlays
21708            .drain(..)
21709            .map(|overlay| overlay.block_id)
21710            .collect();
21711        self.remove_blocks(block_ids, None, cx);
21712        cx.notify();
21713    }
21714
21715    /// Dismisses overlays that have no comments stored for their hunks.
21716    /// Keeps overlays that have at least one comment.
21717    fn dismiss_overlays_without_comments(&mut self, cx: &mut Context<Self>) {
21718        let snapshot = self.buffer.read(cx).snapshot(cx);
21719
21720        // First, compute which overlays have comments (to avoid borrow issues with retain)
21721        let overlays_with_comments: Vec<bool> = self
21722            .diff_review_overlays
21723            .iter()
21724            .map(|overlay| self.hunk_comment_count(&overlay.hunk_key, &snapshot) > 0)
21725            .collect();
21726
21727        // Now collect block IDs to remove and retain overlays
21728        let mut block_ids_to_remove = HashSet::default();
21729        let mut index = 0;
21730        self.diff_review_overlays.retain(|overlay| {
21731            let has_comments = overlays_with_comments[index];
21732            index += 1;
21733            if !has_comments {
21734                block_ids_to_remove.insert(overlay.block_id);
21735            }
21736            has_comments
21737        });
21738
21739        if !block_ids_to_remove.is_empty() {
21740            self.remove_blocks(block_ids_to_remove, None, cx);
21741            cx.notify();
21742        }
21743    }
21744
21745    /// Refreshes the diff review overlay block to update its height and render function.
21746    /// Uses resize_blocks and replace_blocks to avoid visual flicker from remove+insert.
21747    fn refresh_diff_review_overlay_height(
21748        &mut self,
21749        hunk_key: &DiffHunkKey,
21750        _window: &mut Window,
21751        cx: &mut Context<Self>,
21752    ) {
21753        // Extract all needed data from overlay first to avoid borrow conflicts
21754        let snapshot = self.buffer.read(cx).snapshot(cx);
21755        let (comments_expanded, block_id, prompt_editor) = {
21756            let Some(overlay) = self
21757                .diff_review_overlays
21758                .iter()
21759                .find(|overlay| Self::hunk_keys_match(&overlay.hunk_key, hunk_key, &snapshot))
21760            else {
21761                return;
21762            };
21763
21764            (
21765                overlay.comments_expanded,
21766                overlay.block_id,
21767                overlay.prompt_editor.clone(),
21768            )
21769        };
21770
21771        // Calculate new height
21772        let snapshot = self.buffer.read(cx).snapshot(cx);
21773        let new_height = self.calculate_overlay_height(hunk_key, comments_expanded, &snapshot);
21774
21775        // Update the block height using resize_blocks (avoids flicker)
21776        let mut heights = HashMap::default();
21777        heights.insert(block_id, new_height);
21778        self.resize_blocks(heights, None, cx);
21779
21780        // Update the render function using replace_blocks (avoids flicker)
21781        let hunk_key_for_render = hunk_key.clone();
21782        let editor_handle = cx.entity().downgrade();
21783        let render: Arc<dyn Fn(&mut BlockContext) -> AnyElement + Send + Sync> =
21784            Arc::new(move |cx| {
21785                Self::render_diff_review_overlay(
21786                    &prompt_editor,
21787                    &hunk_key_for_render,
21788                    &editor_handle,
21789                    cx,
21790                )
21791            });
21792
21793        let mut renderers = HashMap::default();
21794        renderers.insert(block_id, render);
21795        self.replace_blocks(renderers, None, cx);
21796    }
21797
21798    /// Action handler for SubmitDiffReviewComment.
21799    pub fn submit_diff_review_comment_action(
21800        &mut self,
21801        _: &SubmitDiffReviewComment,
21802        window: &mut Window,
21803        cx: &mut Context<Self>,
21804    ) {
21805        self.submit_diff_review_comment(window, cx);
21806    }
21807
21808    /// Stores the diff review comment locally.
21809    /// Comments are stored per-hunk and can later be batch-submitted to the Agent panel.
21810    pub fn submit_diff_review_comment(&mut self, window: &mut Window, cx: &mut Context<Self>) {
21811        // Find the overlay that currently has focus
21812        let overlay_index = self
21813            .diff_review_overlays
21814            .iter()
21815            .position(|overlay| overlay.prompt_editor.focus_handle(cx).is_focused(window));
21816        let Some(overlay_index) = overlay_index else {
21817            return;
21818        };
21819        let overlay = &self.diff_review_overlays[overlay_index];
21820
21821        let comment_text = overlay.prompt_editor.read(cx).text(cx).trim().to_string();
21822        if comment_text.is_empty() {
21823            return;
21824        }
21825
21826        let anchor_range = overlay.anchor_range.clone();
21827        let hunk_key = overlay.hunk_key.clone();
21828
21829        self.add_review_comment(hunk_key.clone(), comment_text, anchor_range, cx);
21830
21831        // Clear the prompt editor but keep the overlay open
21832        if let Some(overlay) = self.diff_review_overlays.get(overlay_index) {
21833            overlay.prompt_editor.update(cx, |editor, cx| {
21834                editor.clear(window, cx);
21835            });
21836        }
21837
21838        // Refresh the overlay to update the block height for the new comment
21839        self.refresh_diff_review_overlay_height(&hunk_key, window, cx);
21840
21841        cx.notify();
21842    }
21843
21844    /// Returns the prompt editor for the diff review overlay, if one is active.
21845    /// This is primarily used for testing.
21846    pub fn diff_review_prompt_editor(&self) -> Option<&Entity<Editor>> {
21847        self.diff_review_overlays
21848            .first()
21849            .map(|overlay| &overlay.prompt_editor)
21850    }
21851
21852    /// Returns the line range for the first diff review overlay, if one is active.
21853    /// Returns (start_row, end_row) as physical line numbers in the underlying file.
21854    pub fn diff_review_line_range(&self, cx: &App) -> Option<(u32, u32)> {
21855        let overlay = self.diff_review_overlays.first()?;
21856        let snapshot = self.buffer.read(cx).snapshot(cx);
21857        let start_point = overlay.anchor_range.start.to_point(&snapshot);
21858        let end_point = overlay.anchor_range.end.to_point(&snapshot);
21859        let start_row = snapshot
21860            .point_to_buffer_point(start_point)
21861            .map(|(_, p, _)| p.row)
21862            .unwrap_or(start_point.row);
21863        let end_row = snapshot
21864            .point_to_buffer_point(end_point)
21865            .map(|(_, p, _)| p.row)
21866            .unwrap_or(end_point.row);
21867        Some((start_row, end_row))
21868    }
21869
21870    /// Sets whether the comments section is expanded in the diff review overlay.
21871    /// This is primarily used for testing.
21872    pub fn set_diff_review_comments_expanded(&mut self, expanded: bool, cx: &mut Context<Self>) {
21873        for overlay in &mut self.diff_review_overlays {
21874            overlay.comments_expanded = expanded;
21875        }
21876        cx.notify();
21877    }
21878
21879    /// Compares two DiffHunkKeys for equality by resolving their anchors.
21880    fn hunk_keys_match(a: &DiffHunkKey, b: &DiffHunkKey, snapshot: &MultiBufferSnapshot) -> bool {
21881        a.file_path == b.file_path
21882            && a.hunk_start_anchor.to_point(snapshot) == b.hunk_start_anchor.to_point(snapshot)
21883    }
21884
21885    /// Returns comments for a specific hunk, ordered by creation time.
21886    pub fn comments_for_hunk<'a>(
21887        &'a self,
21888        key: &DiffHunkKey,
21889        snapshot: &MultiBufferSnapshot,
21890    ) -> &'a [StoredReviewComment] {
21891        let key_point = key.hunk_start_anchor.to_point(snapshot);
21892        self.stored_review_comments
21893            .iter()
21894            .find(|(k, _)| {
21895                k.file_path == key.file_path && k.hunk_start_anchor.to_point(snapshot) == key_point
21896            })
21897            .map(|(_, comments)| comments.as_slice())
21898            .unwrap_or(&[])
21899    }
21900
21901    /// Returns the total count of stored review comments across all hunks.
21902    pub fn total_review_comment_count(&self) -> usize {
21903        self.stored_review_comments
21904            .iter()
21905            .map(|(_, v)| v.len())
21906            .sum()
21907    }
21908
21909    /// Returns the count of comments for a specific hunk.
21910    pub fn hunk_comment_count(&self, key: &DiffHunkKey, snapshot: &MultiBufferSnapshot) -> usize {
21911        let key_point = key.hunk_start_anchor.to_point(snapshot);
21912        self.stored_review_comments
21913            .iter()
21914            .find(|(k, _)| {
21915                k.file_path == key.file_path && k.hunk_start_anchor.to_point(snapshot) == key_point
21916            })
21917            .map(|(_, v)| v.len())
21918            .unwrap_or(0)
21919    }
21920
21921    /// Adds a new review comment to a specific hunk.
21922    pub fn add_review_comment(
21923        &mut self,
21924        hunk_key: DiffHunkKey,
21925        comment: String,
21926        anchor_range: Range<Anchor>,
21927        cx: &mut Context<Self>,
21928    ) -> usize {
21929        let id = self.next_review_comment_id;
21930        self.next_review_comment_id += 1;
21931
21932        let stored_comment = StoredReviewComment::new(id, comment, anchor_range);
21933
21934        let snapshot = self.buffer.read(cx).snapshot(cx);
21935        let key_point = hunk_key.hunk_start_anchor.to_point(&snapshot);
21936
21937        // Find existing entry for this hunk or add a new one
21938        if let Some((_, comments)) = self.stored_review_comments.iter_mut().find(|(k, _)| {
21939            k.file_path == hunk_key.file_path
21940                && k.hunk_start_anchor.to_point(&snapshot) == key_point
21941        }) {
21942            comments.push(stored_comment);
21943        } else {
21944            self.stored_review_comments
21945                .push((hunk_key, vec![stored_comment]));
21946        }
21947
21948        cx.emit(EditorEvent::ReviewCommentsChanged {
21949            total_count: self.total_review_comment_count(),
21950        });
21951        cx.notify();
21952        id
21953    }
21954
21955    /// Removes a review comment by ID from any hunk.
21956    pub fn remove_review_comment(&mut self, id: usize, cx: &mut Context<Self>) -> bool {
21957        for (_, comments) in self.stored_review_comments.iter_mut() {
21958            if let Some(index) = comments.iter().position(|c| c.id == id) {
21959                comments.remove(index);
21960                cx.emit(EditorEvent::ReviewCommentsChanged {
21961                    total_count: self.total_review_comment_count(),
21962                });
21963                cx.notify();
21964                return true;
21965            }
21966        }
21967        false
21968    }
21969
21970    /// Updates a review comment's text by ID.
21971    pub fn update_review_comment(
21972        &mut self,
21973        id: usize,
21974        new_comment: String,
21975        cx: &mut Context<Self>,
21976    ) -> bool {
21977        for (_, comments) in self.stored_review_comments.iter_mut() {
21978            if let Some(comment) = comments.iter_mut().find(|c| c.id == id) {
21979                comment.comment = new_comment;
21980                comment.is_editing = false;
21981                cx.emit(EditorEvent::ReviewCommentsChanged {
21982                    total_count: self.total_review_comment_count(),
21983                });
21984                cx.notify();
21985                return true;
21986            }
21987        }
21988        false
21989    }
21990
21991    /// Sets a comment's editing state.
21992    pub fn set_comment_editing(&mut self, id: usize, is_editing: bool, cx: &mut Context<Self>) {
21993        for (_, comments) in self.stored_review_comments.iter_mut() {
21994            if let Some(comment) = comments.iter_mut().find(|c| c.id == id) {
21995                comment.is_editing = is_editing;
21996                cx.notify();
21997                return;
21998            }
21999        }
22000    }
22001
22002    /// Takes all stored comments from all hunks, clearing the storage.
22003    /// Returns a Vec of (hunk_key, comments) pairs.
22004    pub fn take_all_review_comments(
22005        &mut self,
22006        cx: &mut Context<Self>,
22007    ) -> Vec<(DiffHunkKey, Vec<StoredReviewComment>)> {
22008        // Dismiss all overlays when taking comments (e.g., when sending to agent)
22009        self.dismiss_all_diff_review_overlays(cx);
22010        let comments = std::mem::take(&mut self.stored_review_comments);
22011        // Reset the ID counter since all comments have been taken
22012        self.next_review_comment_id = 0;
22013        cx.emit(EditorEvent::ReviewCommentsChanged { total_count: 0 });
22014        cx.notify();
22015        comments
22016    }
22017
22018    /// Removes review comments whose anchors are no longer valid or whose
22019    /// associated diff hunks no longer exist.
22020    ///
22021    /// This should be called when the buffer changes to prevent orphaned comments
22022    /// from accumulating.
22023    pub fn cleanup_orphaned_review_comments(&mut self, cx: &mut Context<Self>) {
22024        let snapshot = self.buffer.read(cx).snapshot(cx);
22025        let original_count = self.total_review_comment_count();
22026
22027        // Remove comments with invalid hunk anchors
22028        self.stored_review_comments
22029            .retain(|(hunk_key, _)| hunk_key.hunk_start_anchor.is_valid(&snapshot));
22030
22031        // Also clean up individual comments with invalid anchor ranges
22032        for (_, comments) in &mut self.stored_review_comments {
22033            comments.retain(|comment| {
22034                comment.range.start.is_valid(&snapshot) && comment.range.end.is_valid(&snapshot)
22035            });
22036        }
22037
22038        // Remove empty hunk entries
22039        self.stored_review_comments
22040            .retain(|(_, comments)| !comments.is_empty());
22041
22042        let new_count = self.total_review_comment_count();
22043        if new_count != original_count {
22044            cx.emit(EditorEvent::ReviewCommentsChanged {
22045                total_count: new_count,
22046            });
22047            cx.notify();
22048        }
22049    }
22050
22051    /// Toggles the expanded state of the comments section in the overlay.
22052    pub fn toggle_review_comments_expanded(
22053        &mut self,
22054        _: &ToggleReviewCommentsExpanded,
22055        window: &mut Window,
22056        cx: &mut Context<Self>,
22057    ) {
22058        // Find the overlay that currently has focus, or use the first one
22059        let overlay_info = self.diff_review_overlays.iter_mut().find_map(|overlay| {
22060            if overlay.prompt_editor.focus_handle(cx).is_focused(window) {
22061                overlay.comments_expanded = !overlay.comments_expanded;
22062                Some(overlay.hunk_key.clone())
22063            } else {
22064                None
22065            }
22066        });
22067
22068        // If no focused overlay found, toggle the first one
22069        let hunk_key = overlay_info.or_else(|| {
22070            self.diff_review_overlays.first_mut().map(|overlay| {
22071                overlay.comments_expanded = !overlay.comments_expanded;
22072                overlay.hunk_key.clone()
22073            })
22074        });
22075
22076        if let Some(hunk_key) = hunk_key {
22077            self.refresh_diff_review_overlay_height(&hunk_key, window, cx);
22078            cx.notify();
22079        }
22080    }
22081
22082    /// Handles the EditReviewComment action - sets a comment into editing mode.
22083    pub fn edit_review_comment(
22084        &mut self,
22085        action: &EditReviewComment,
22086        window: &mut Window,
22087        cx: &mut Context<Self>,
22088    ) {
22089        let comment_id = action.id;
22090
22091        // Set the comment to editing mode
22092        self.set_comment_editing(comment_id, true, cx);
22093
22094        // Find the overlay that contains this comment and create an inline editor if needed
22095        // First, find which hunk this comment belongs to
22096        let hunk_key = self
22097            .stored_review_comments
22098            .iter()
22099            .find_map(|(key, comments)| {
22100                if comments.iter().any(|c| c.id == comment_id) {
22101                    Some(key.clone())
22102                } else {
22103                    None
22104                }
22105            });
22106
22107        let snapshot = self.buffer.read(cx).snapshot(cx);
22108        if let Some(hunk_key) = hunk_key {
22109            if let Some(overlay) = self
22110                .diff_review_overlays
22111                .iter_mut()
22112                .find(|overlay| Self::hunk_keys_match(&overlay.hunk_key, &hunk_key, &snapshot))
22113            {
22114                if let std::collections::hash_map::Entry::Vacant(entry) =
22115                    overlay.inline_edit_editors.entry(comment_id)
22116                {
22117                    // Find the comment text
22118                    let comment_text = self
22119                        .stored_review_comments
22120                        .iter()
22121                        .flat_map(|(_, comments)| comments)
22122                        .find(|c| c.id == comment_id)
22123                        .map(|c| c.comment.clone())
22124                        .unwrap_or_default();
22125
22126                    // Create inline editor
22127                    let parent_editor = cx.entity().downgrade();
22128                    let inline_editor = cx.new(|cx| {
22129                        let mut editor = Editor::single_line(window, cx);
22130                        editor.set_text(&*comment_text, window, cx);
22131                        // Select all text for easy replacement
22132                        editor.select_all(&crate::actions::SelectAll, window, cx);
22133                        editor
22134                    });
22135
22136                    // Register the Newline action to confirm the edit
22137                    let subscription = inline_editor.update(cx, |inline_editor, _cx| {
22138                        inline_editor.register_action({
22139                            let parent_editor = parent_editor.clone();
22140                            move |_: &crate::actions::Newline, window, cx| {
22141                                if let Some(editor) = parent_editor.upgrade() {
22142                                    editor.update(cx, |editor, cx| {
22143                                        editor.confirm_edit_review_comment(comment_id, window, cx);
22144                                    });
22145                                }
22146                            }
22147                        })
22148                    });
22149
22150                    // Store the subscription to keep the action handler alive
22151                    overlay
22152                        .inline_edit_subscriptions
22153                        .insert(comment_id, subscription);
22154
22155                    // Focus the inline editor
22156                    let focus_handle = inline_editor.focus_handle(cx);
22157                    window.focus(&focus_handle, cx);
22158
22159                    entry.insert(inline_editor);
22160                }
22161            }
22162        }
22163
22164        cx.notify();
22165    }
22166
22167    /// Confirms an inline edit of a review comment.
22168    pub fn confirm_edit_review_comment(
22169        &mut self,
22170        comment_id: usize,
22171        _window: &mut Window,
22172        cx: &mut Context<Self>,
22173    ) {
22174        // Get the new text from the inline editor
22175        // Find the overlay containing this comment's inline editor
22176        let snapshot = self.buffer.read(cx).snapshot(cx);
22177        let hunk_key = self
22178            .stored_review_comments
22179            .iter()
22180            .find_map(|(key, comments)| {
22181                if comments.iter().any(|c| c.id == comment_id) {
22182                    Some(key.clone())
22183                } else {
22184                    None
22185                }
22186            });
22187
22188        let new_text = hunk_key
22189            .as_ref()
22190            .and_then(|hunk_key| {
22191                self.diff_review_overlays
22192                    .iter()
22193                    .find(|overlay| Self::hunk_keys_match(&overlay.hunk_key, hunk_key, &snapshot))
22194            })
22195            .as_ref()
22196            .and_then(|overlay| overlay.inline_edit_editors.get(&comment_id))
22197            .map(|editor| editor.read(cx).text(cx).trim().to_string());
22198
22199        if let Some(new_text) = new_text {
22200            if !new_text.is_empty() {
22201                self.update_review_comment(comment_id, new_text, cx);
22202            }
22203        }
22204
22205        // Remove the inline editor and its subscription
22206        if let Some(hunk_key) = hunk_key {
22207            if let Some(overlay) = self
22208                .diff_review_overlays
22209                .iter_mut()
22210                .find(|overlay| Self::hunk_keys_match(&overlay.hunk_key, &hunk_key, &snapshot))
22211            {
22212                overlay.inline_edit_editors.remove(&comment_id);
22213                overlay.inline_edit_subscriptions.remove(&comment_id);
22214            }
22215        }
22216
22217        // Clear editing state
22218        self.set_comment_editing(comment_id, false, cx);
22219    }
22220
22221    /// Cancels an inline edit of a review comment.
22222    pub fn cancel_edit_review_comment(
22223        &mut self,
22224        comment_id: usize,
22225        _window: &mut Window,
22226        cx: &mut Context<Self>,
22227    ) {
22228        // Find which hunk this comment belongs to
22229        let hunk_key = self
22230            .stored_review_comments
22231            .iter()
22232            .find_map(|(key, comments)| {
22233                if comments.iter().any(|c| c.id == comment_id) {
22234                    Some(key.clone())
22235                } else {
22236                    None
22237                }
22238            });
22239
22240        // Remove the inline editor and its subscription
22241        if let Some(hunk_key) = hunk_key {
22242            let snapshot = self.buffer.read(cx).snapshot(cx);
22243            if let Some(overlay) = self
22244                .diff_review_overlays
22245                .iter_mut()
22246                .find(|overlay| Self::hunk_keys_match(&overlay.hunk_key, &hunk_key, &snapshot))
22247            {
22248                overlay.inline_edit_editors.remove(&comment_id);
22249                overlay.inline_edit_subscriptions.remove(&comment_id);
22250            }
22251        }
22252
22253        // Clear editing state
22254        self.set_comment_editing(comment_id, false, cx);
22255    }
22256
22257    /// Action handler for ConfirmEditReviewComment.
22258    pub fn confirm_edit_review_comment_action(
22259        &mut self,
22260        action: &ConfirmEditReviewComment,
22261        window: &mut Window,
22262        cx: &mut Context<Self>,
22263    ) {
22264        self.confirm_edit_review_comment(action.id, window, cx);
22265    }
22266
22267    /// Action handler for CancelEditReviewComment.
22268    pub fn cancel_edit_review_comment_action(
22269        &mut self,
22270        action: &CancelEditReviewComment,
22271        window: &mut Window,
22272        cx: &mut Context<Self>,
22273    ) {
22274        self.cancel_edit_review_comment(action.id, window, cx);
22275    }
22276
22277    /// Handles the DeleteReviewComment action - removes a comment.
22278    pub fn delete_review_comment(
22279        &mut self,
22280        action: &DeleteReviewComment,
22281        window: &mut Window,
22282        cx: &mut Context<Self>,
22283    ) {
22284        // Get the hunk key before removing the comment
22285        // Find the hunk key from the comment itself
22286        let comment_id = action.id;
22287        let hunk_key = self
22288            .stored_review_comments
22289            .iter()
22290            .find_map(|(key, comments)| {
22291                if comments.iter().any(|c| c.id == comment_id) {
22292                    Some(key.clone())
22293                } else {
22294                    None
22295                }
22296            });
22297
22298        // Also get it from the overlay for refresh purposes
22299        let overlay_hunk_key = self
22300            .diff_review_overlays
22301            .first()
22302            .map(|o| o.hunk_key.clone());
22303
22304        self.remove_review_comment(action.id, cx);
22305
22306        // Refresh the overlay height after removing a comment
22307        if let Some(hunk_key) = hunk_key.or(overlay_hunk_key) {
22308            self.refresh_diff_review_overlay_height(&hunk_key, window, cx);
22309        }
22310    }
22311
22312    fn render_diff_review_overlay(
22313        prompt_editor: &Entity<Editor>,
22314        hunk_key: &DiffHunkKey,
22315        editor_handle: &WeakEntity<Editor>,
22316        cx: &mut BlockContext,
22317    ) -> AnyElement {
22318        fn format_line_ranges(ranges: &[(u32, u32)]) -> Option<String> {
22319            if ranges.is_empty() {
22320                return None;
22321            }
22322            let formatted: Vec<String> = ranges
22323                .iter()
22324                .map(|(start, end)| {
22325                    let start_line = start + 1;
22326                    let end_line = end + 1;
22327                    if start_line == end_line {
22328                        format!("Line {start_line}")
22329                    } else {
22330                        format!("Lines {start_line}-{end_line}")
22331                    }
22332                })
22333                .collect();
22334            // Don't show label for single line in single excerpt
22335            if ranges.len() == 1 && ranges[0].0 == ranges[0].1 {
22336                return None;
22337            }
22338            Some(formatted.join(""))
22339        }
22340
22341        let theme = cx.theme();
22342        let colors = theme.colors();
22343
22344        let (comments, comments_expanded, inline_editors, user_avatar_uri, line_ranges) =
22345            editor_handle
22346                .upgrade()
22347                .map(|editor| {
22348                    let editor = editor.read(cx);
22349                    let snapshot = editor.buffer().read(cx).snapshot(cx);
22350                    let comments = editor.comments_for_hunk(hunk_key, &snapshot).to_vec();
22351                    let (expanded, editors, avatar_uri, line_ranges) = editor
22352                        .diff_review_overlays
22353                        .iter()
22354                        .find(|overlay| {
22355                            Editor::hunk_keys_match(&overlay.hunk_key, hunk_key, &snapshot)
22356                        })
22357                        .map(|o| {
22358                            let start_point = o.anchor_range.start.to_point(&snapshot);
22359                            let end_point = o.anchor_range.end.to_point(&snapshot);
22360                            // Get line ranges per excerpt to detect discontinuities
22361                            let buffer_ranges =
22362                                snapshot.range_to_buffer_ranges(start_point..end_point);
22363                            let ranges: Vec<(u32, u32)> = buffer_ranges
22364                                .iter()
22365                                .map(|(buffer, range, _)| {
22366                                    let start = buffer.offset_to_point(range.start.0).row;
22367                                    let end = buffer.offset_to_point(range.end.0).row;
22368                                    (start, end)
22369                                })
22370                                .collect();
22371                            (
22372                                o.comments_expanded,
22373                                o.inline_edit_editors.clone(),
22374                                o.user_avatar_uri.clone(),
22375                                if ranges.is_empty() {
22376                                    None
22377                                } else {
22378                                    Some(ranges)
22379                                },
22380                            )
22381                        })
22382                        .unwrap_or((true, HashMap::default(), None, None));
22383                    (comments, expanded, editors, avatar_uri, line_ranges)
22384                })
22385                .unwrap_or((Vec::new(), true, HashMap::default(), None, None));
22386
22387        let comment_count = comments.len();
22388        let avatar_size = px(20.);
22389        let action_icon_size = IconSize::XSmall;
22390
22391        v_flex()
22392            .w_full()
22393            .bg(colors.editor_background)
22394            .border_b_1()
22395            .border_color(colors.border)
22396            .px_2()
22397            .pb_2()
22398            .gap_2()
22399            // Line range indicator (only shown for multi-line selections or multiple excerpts)
22400            .when_some(line_ranges, |el, ranges| {
22401                let label = format_line_ranges(&ranges);
22402                if let Some(label) = label {
22403                    el.child(
22404                        h_flex()
22405                            .w_full()
22406                            .px_2()
22407                            .child(Label::new(label).size(LabelSize::Small).color(Color::Muted)),
22408                    )
22409                } else {
22410                    el
22411                }
22412            })
22413            // Top row: editable input with user's avatar
22414            .child(
22415                h_flex()
22416                    .w_full()
22417                    .items_center()
22418                    .gap_2()
22419                    .px_2()
22420                    .py_1p5()
22421                    .rounded_md()
22422                    .bg(colors.surface_background)
22423                    .child(
22424                        div()
22425                            .size(avatar_size)
22426                            .flex_shrink_0()
22427                            .rounded_full()
22428                            .overflow_hidden()
22429                            .child(if let Some(ref avatar_uri) = user_avatar_uri {
22430                                Avatar::new(avatar_uri.clone())
22431                                    .size(avatar_size)
22432                                    .into_any_element()
22433                            } else {
22434                                Icon::new(IconName::Person)
22435                                    .size(IconSize::Small)
22436                                    .color(ui::Color::Muted)
22437                                    .into_any_element()
22438                            }),
22439                    )
22440                    .child(
22441                        div()
22442                            .flex_1()
22443                            .border_1()
22444                            .border_color(colors.border)
22445                            .rounded_md()
22446                            .bg(colors.editor_background)
22447                            .px_2()
22448                            .py_1()
22449                            .child(prompt_editor.clone()),
22450                    )
22451                    .child(
22452                        h_flex()
22453                            .flex_shrink_0()
22454                            .gap_1()
22455                            .child(
22456                                IconButton::new("diff-review-close", IconName::Close)
22457                                    .icon_color(ui::Color::Muted)
22458                                    .icon_size(action_icon_size)
22459                                    .tooltip(Tooltip::text("Close"))
22460                                    .on_click(|_, window, cx| {
22461                                        window
22462                                            .dispatch_action(Box::new(crate::actions::Cancel), cx);
22463                                    }),
22464                            )
22465                            .child(
22466                                IconButton::new("diff-review-add", IconName::Return)
22467                                    .icon_color(ui::Color::Muted)
22468                                    .icon_size(action_icon_size)
22469                                    .tooltip(Tooltip::text("Add comment"))
22470                                    .on_click(|_, window, cx| {
22471                                        window.dispatch_action(
22472                                            Box::new(crate::actions::SubmitDiffReviewComment),
22473                                            cx,
22474                                        );
22475                                    }),
22476                            ),
22477                    ),
22478            )
22479            // Expandable comments section (only shown when there are comments)
22480            .when(comment_count > 0, |el| {
22481                el.child(Self::render_comments_section(
22482                    comments,
22483                    comments_expanded,
22484                    inline_editors,
22485                    user_avatar_uri,
22486                    avatar_size,
22487                    action_icon_size,
22488                    colors,
22489                ))
22490            })
22491            .into_any_element()
22492    }
22493
22494    fn render_comments_section(
22495        comments: Vec<StoredReviewComment>,
22496        expanded: bool,
22497        inline_editors: HashMap<usize, Entity<Editor>>,
22498        user_avatar_uri: Option<SharedUri>,
22499        avatar_size: Pixels,
22500        action_icon_size: IconSize,
22501        colors: &theme::ThemeColors,
22502    ) -> impl IntoElement {
22503        let comment_count = comments.len();
22504
22505        v_flex()
22506            .w_full()
22507            .gap_1()
22508            // Header with expand/collapse toggle
22509            .child(
22510                h_flex()
22511                    .id("review-comments-header")
22512                    .w_full()
22513                    .items_center()
22514                    .gap_1()
22515                    .px_2()
22516                    .py_1()
22517                    .cursor_pointer()
22518                    .rounded_md()
22519                    .hover(|style| style.bg(colors.ghost_element_hover))
22520                    .on_click(|_, window: &mut Window, cx| {
22521                        window.dispatch_action(
22522                            Box::new(crate::actions::ToggleReviewCommentsExpanded),
22523                            cx,
22524                        );
22525                    })
22526                    .child(
22527                        Icon::new(if expanded {
22528                            IconName::ChevronDown
22529                        } else {
22530                            IconName::ChevronRight
22531                        })
22532                        .size(IconSize::Small)
22533                        .color(ui::Color::Muted),
22534                    )
22535                    .child(
22536                        Label::new(format!(
22537                            "{} Comment{}",
22538                            comment_count,
22539                            if comment_count == 1 { "" } else { "s" }
22540                        ))
22541                        .size(LabelSize::Small)
22542                        .color(Color::Muted),
22543                    ),
22544            )
22545            // Comments list (when expanded)
22546            .when(expanded, |el| {
22547                el.children(comments.into_iter().map(|comment| {
22548                    let inline_editor = inline_editors.get(&comment.id).cloned();
22549                    Self::render_comment_row(
22550                        comment,
22551                        inline_editor,
22552                        user_avatar_uri.clone(),
22553                        avatar_size,
22554                        action_icon_size,
22555                        colors,
22556                    )
22557                }))
22558            })
22559    }
22560
22561    fn render_comment_row(
22562        comment: StoredReviewComment,
22563        inline_editor: Option<Entity<Editor>>,
22564        user_avatar_uri: Option<SharedUri>,
22565        avatar_size: Pixels,
22566        action_icon_size: IconSize,
22567        colors: &theme::ThemeColors,
22568    ) -> impl IntoElement {
22569        let comment_id = comment.id;
22570        let is_editing = inline_editor.is_some();
22571
22572        h_flex()
22573            .w_full()
22574            .items_center()
22575            .gap_2()
22576            .px_2()
22577            .py_1p5()
22578            .rounded_md()
22579            .bg(colors.surface_background)
22580            .child(
22581                div()
22582                    .size(avatar_size)
22583                    .flex_shrink_0()
22584                    .rounded_full()
22585                    .overflow_hidden()
22586                    .child(if let Some(ref avatar_uri) = user_avatar_uri {
22587                        Avatar::new(avatar_uri.clone())
22588                            .size(avatar_size)
22589                            .into_any_element()
22590                    } else {
22591                        Icon::new(IconName::Person)
22592                            .size(IconSize::Small)
22593                            .color(ui::Color::Muted)
22594                            .into_any_element()
22595                    }),
22596            )
22597            .child(if let Some(editor) = inline_editor {
22598                // Inline edit mode: show an editable text field
22599                div()
22600                    .flex_1()
22601                    .border_1()
22602                    .border_color(colors.border)
22603                    .rounded_md()
22604                    .bg(colors.editor_background)
22605                    .px_2()
22606                    .py_1()
22607                    .child(editor)
22608                    .into_any_element()
22609            } else {
22610                // Display mode: show the comment text
22611                div()
22612                    .flex_1()
22613                    .text_sm()
22614                    .text_color(colors.text)
22615                    .child(comment.comment)
22616                    .into_any_element()
22617            })
22618            .child(if is_editing {
22619                // Editing mode: show close and confirm buttons
22620                h_flex()
22621                    .gap_1()
22622                    .child(
22623                        IconButton::new(
22624                            format!("diff-review-cancel-edit-{comment_id}"),
22625                            IconName::Close,
22626                        )
22627                        .icon_color(ui::Color::Muted)
22628                        .icon_size(action_icon_size)
22629                        .tooltip(Tooltip::text("Cancel"))
22630                        .on_click(move |_, window, cx| {
22631                            window.dispatch_action(
22632                                Box::new(crate::actions::CancelEditReviewComment {
22633                                    id: comment_id,
22634                                }),
22635                                cx,
22636                            );
22637                        }),
22638                    )
22639                    .child(
22640                        IconButton::new(
22641                            format!("diff-review-confirm-edit-{comment_id}"),
22642                            IconName::Return,
22643                        )
22644                        .icon_color(ui::Color::Muted)
22645                        .icon_size(action_icon_size)
22646                        .tooltip(Tooltip::text("Confirm"))
22647                        .on_click(move |_, window, cx| {
22648                            window.dispatch_action(
22649                                Box::new(crate::actions::ConfirmEditReviewComment {
22650                                    id: comment_id,
22651                                }),
22652                                cx,
22653                            );
22654                        }),
22655                    )
22656                    .into_any_element()
22657            } else {
22658                // Display mode: no action buttons for now (edit/delete not yet implemented)
22659                gpui::Empty.into_any_element()
22660            })
22661    }
22662
22663    pub fn set_masked(&mut self, masked: bool, cx: &mut Context<Self>) {
22664        if self.display_map.read(cx).masked != masked {
22665            self.display_map.update(cx, |map, _| map.masked = masked);
22666        }
22667        cx.notify()
22668    }
22669
22670    pub fn set_show_wrap_guides(&mut self, show_wrap_guides: bool, cx: &mut Context<Self>) {
22671        self.show_wrap_guides = Some(show_wrap_guides);
22672        cx.notify();
22673    }
22674
22675    pub fn set_show_indent_guides(&mut self, show_indent_guides: bool, cx: &mut Context<Self>) {
22676        self.show_indent_guides = Some(show_indent_guides);
22677        cx.notify();
22678    }
22679
22680    pub fn working_directory(&self, cx: &App) -> Option<PathBuf> {
22681        if let Some(buffer) = self.buffer().read(cx).as_singleton() {
22682            if let Some(file) = buffer.read(cx).file().and_then(|f| f.as_local())
22683                && let Some(dir) = file.abs_path(cx).parent()
22684            {
22685                return Some(dir.to_owned());
22686            }
22687        }
22688
22689        None
22690    }
22691
22692    fn target_file<'a>(&self, cx: &'a App) -> Option<&'a dyn language::LocalFile> {
22693        self.active_excerpt(cx)?
22694            .1
22695            .read(cx)
22696            .file()
22697            .and_then(|f| f.as_local())
22698    }
22699
22700    pub fn target_file_abs_path(&self, cx: &mut Context<Self>) -> Option<PathBuf> {
22701        self.active_excerpt(cx).and_then(|(_, buffer, _)| {
22702            let buffer = buffer.read(cx);
22703            if let Some(project_path) = buffer.project_path(cx) {
22704                let project = self.project()?.read(cx);
22705                project.absolute_path(&project_path, cx)
22706            } else {
22707                buffer
22708                    .file()
22709                    .and_then(|file| file.as_local().map(|file| file.abs_path(cx)))
22710            }
22711        })
22712    }
22713
22714    pub fn reveal_in_finder(
22715        &mut self,
22716        _: &RevealInFileManager,
22717        _window: &mut Window,
22718        cx: &mut Context<Self>,
22719    ) {
22720        if let Some(path) = self.target_file_abs_path(cx) {
22721            if let Some(project) = self.project() {
22722                project.update(cx, |project, cx| project.reveal_path(&path, cx));
22723            } else {
22724                cx.reveal_path(&path);
22725            }
22726        }
22727    }
22728
22729    pub fn copy_path(
22730        &mut self,
22731        _: &zed_actions::workspace::CopyPath,
22732        _window: &mut Window,
22733        cx: &mut Context<Self>,
22734    ) {
22735        if let Some(path) = self.target_file_abs_path(cx)
22736            && let Some(path) = path.to_str()
22737        {
22738            cx.write_to_clipboard(ClipboardItem::new_string(path.to_string()));
22739        } else {
22740            cx.propagate();
22741        }
22742    }
22743
22744    pub fn copy_relative_path(
22745        &mut self,
22746        _: &zed_actions::workspace::CopyRelativePath,
22747        _window: &mut Window,
22748        cx: &mut Context<Self>,
22749    ) {
22750        if let Some(path) = self.active_excerpt(cx).and_then(|(_, buffer, _)| {
22751            let project = self.project()?.read(cx);
22752            let path = buffer.read(cx).file()?.path();
22753            let path = path.display(project.path_style(cx));
22754            Some(path)
22755        }) {
22756            cx.write_to_clipboard(ClipboardItem::new_string(path.to_string()));
22757        } else {
22758            cx.propagate();
22759        }
22760    }
22761
22762    /// Returns the project path for the editor's buffer, if any buffer is
22763    /// opened in the editor.
22764    pub fn project_path(&self, cx: &App) -> Option<ProjectPath> {
22765        if let Some(buffer) = self.buffer.read(cx).as_singleton() {
22766            buffer.read(cx).project_path(cx)
22767        } else {
22768            None
22769        }
22770    }
22771
22772    // Returns true if the editor handled a go-to-line request
22773    pub fn go_to_active_debug_line(&mut self, window: &mut Window, cx: &mut Context<Self>) -> bool {
22774        maybe!({
22775            let breakpoint_store = self.breakpoint_store.as_ref()?;
22776
22777            let Some(active_stack_frame) = breakpoint_store.read(cx).active_position().cloned()
22778            else {
22779                self.clear_row_highlights::<ActiveDebugLine>();
22780                return None;
22781            };
22782
22783            let position = active_stack_frame.position;
22784            let buffer_id = position.buffer_id?;
22785            let snapshot = self
22786                .project
22787                .as_ref()?
22788                .read(cx)
22789                .buffer_for_id(buffer_id, cx)?
22790                .read(cx)
22791                .snapshot();
22792
22793            let mut handled = false;
22794            for (id, ExcerptRange { context, .. }) in
22795                self.buffer.read(cx).excerpts_for_buffer(buffer_id, cx)
22796            {
22797                if context.start.cmp(&position, &snapshot).is_ge()
22798                    || context.end.cmp(&position, &snapshot).is_lt()
22799                {
22800                    continue;
22801                }
22802                let snapshot = self.buffer.read(cx).snapshot(cx);
22803                let multibuffer_anchor = snapshot.anchor_in_excerpt(id, position)?;
22804
22805                handled = true;
22806                self.clear_row_highlights::<ActiveDebugLine>();
22807
22808                self.go_to_line::<ActiveDebugLine>(
22809                    multibuffer_anchor,
22810                    Some(cx.theme().colors().editor_debugger_active_line_background),
22811                    window,
22812                    cx,
22813                );
22814
22815                cx.notify();
22816            }
22817
22818            handled.then_some(())
22819        })
22820        .is_some()
22821    }
22822
22823    pub fn copy_file_name_without_extension(
22824        &mut self,
22825        _: &CopyFileNameWithoutExtension,
22826        _: &mut Window,
22827        cx: &mut Context<Self>,
22828    ) {
22829        if let Some(file_stem) = self.active_excerpt(cx).and_then(|(_, buffer, _)| {
22830            let file = buffer.read(cx).file()?;
22831            file.path().file_stem()
22832        }) {
22833            cx.write_to_clipboard(ClipboardItem::new_string(file_stem.to_string()));
22834        }
22835    }
22836
22837    pub fn copy_file_name(&mut self, _: &CopyFileName, _: &mut Window, cx: &mut Context<Self>) {
22838        if let Some(file_name) = self.active_excerpt(cx).and_then(|(_, buffer, _)| {
22839            let file = buffer.read(cx).file()?;
22840            Some(file.file_name(cx))
22841        }) {
22842            cx.write_to_clipboard(ClipboardItem::new_string(file_name.to_string()));
22843        }
22844    }
22845
22846    pub fn toggle_git_blame(
22847        &mut self,
22848        _: &::git::Blame,
22849        window: &mut Window,
22850        cx: &mut Context<Self>,
22851    ) {
22852        self.show_git_blame_gutter = !self.show_git_blame_gutter;
22853
22854        if self.show_git_blame_gutter && !self.has_blame_entries(cx) {
22855            self.start_git_blame(true, window, cx);
22856        }
22857
22858        cx.notify();
22859    }
22860
22861    pub fn toggle_git_blame_inline(
22862        &mut self,
22863        _: &ToggleGitBlameInline,
22864        window: &mut Window,
22865        cx: &mut Context<Self>,
22866    ) {
22867        self.toggle_git_blame_inline_internal(true, window, cx);
22868        cx.notify();
22869    }
22870
22871    pub fn open_git_blame_commit(
22872        &mut self,
22873        _: &OpenGitBlameCommit,
22874        window: &mut Window,
22875        cx: &mut Context<Self>,
22876    ) {
22877        self.open_git_blame_commit_internal(window, cx);
22878    }
22879
22880    fn open_git_blame_commit_internal(
22881        &mut self,
22882        window: &mut Window,
22883        cx: &mut Context<Self>,
22884    ) -> Option<()> {
22885        let blame = self.blame.as_ref()?;
22886        let snapshot = self.snapshot(window, cx);
22887        let cursor = self
22888            .selections
22889            .newest::<Point>(&snapshot.display_snapshot)
22890            .head();
22891        let (buffer, point, _) = snapshot.buffer_snapshot().point_to_buffer_point(cursor)?;
22892        let (_, blame_entry) = blame
22893            .update(cx, |blame, cx| {
22894                blame
22895                    .blame_for_rows(
22896                        &[RowInfo {
22897                            buffer_id: Some(buffer.remote_id()),
22898                            buffer_row: Some(point.row),
22899                            ..Default::default()
22900                        }],
22901                        cx,
22902                    )
22903                    .next()
22904            })
22905            .flatten()?;
22906        let renderer = cx.global::<GlobalBlameRenderer>().0.clone();
22907        let repo = blame.read(cx).repository(cx, buffer.remote_id())?;
22908        let workspace = self.workspace()?.downgrade();
22909        renderer.open_blame_commit(blame_entry, repo, workspace, window, cx);
22910        None
22911    }
22912
22913    pub fn git_blame_inline_enabled(&self) -> bool {
22914        self.git_blame_inline_enabled
22915    }
22916
22917    pub fn toggle_selection_menu(
22918        &mut self,
22919        _: &ToggleSelectionMenu,
22920        _: &mut Window,
22921        cx: &mut Context<Self>,
22922    ) {
22923        self.show_selection_menu = self
22924            .show_selection_menu
22925            .map(|show_selections_menu| !show_selections_menu)
22926            .or_else(|| Some(!EditorSettings::get_global(cx).toolbar.selections_menu));
22927
22928        cx.notify();
22929    }
22930
22931    pub fn selection_menu_enabled(&self, cx: &App) -> bool {
22932        self.show_selection_menu
22933            .unwrap_or_else(|| EditorSettings::get_global(cx).toolbar.selections_menu)
22934    }
22935
22936    fn start_git_blame(
22937        &mut self,
22938        user_triggered: bool,
22939        window: &mut Window,
22940        cx: &mut Context<Self>,
22941    ) {
22942        if let Some(project) = self.project() {
22943            if let Some(buffer) = self.buffer().read(cx).as_singleton()
22944                && buffer.read(cx).file().is_none()
22945            {
22946                return;
22947            }
22948
22949            let focused = self.focus_handle(cx).contains_focused(window, cx);
22950
22951            let project = project.clone();
22952            let blame = cx
22953                .new(|cx| GitBlame::new(self.buffer.clone(), project, user_triggered, focused, cx));
22954            self.blame_subscription =
22955                Some(cx.observe_in(&blame, window, |_, _, _, cx| cx.notify()));
22956            self.blame = Some(blame);
22957        }
22958    }
22959
22960    fn toggle_git_blame_inline_internal(
22961        &mut self,
22962        user_triggered: bool,
22963        window: &mut Window,
22964        cx: &mut Context<Self>,
22965    ) {
22966        if self.git_blame_inline_enabled {
22967            self.git_blame_inline_enabled = false;
22968            self.show_git_blame_inline = false;
22969            self.show_git_blame_inline_delay_task.take();
22970        } else {
22971            self.git_blame_inline_enabled = true;
22972            self.start_git_blame_inline(user_triggered, window, cx);
22973        }
22974
22975        cx.notify();
22976    }
22977
22978    fn start_git_blame_inline(
22979        &mut self,
22980        user_triggered: bool,
22981        window: &mut Window,
22982        cx: &mut Context<Self>,
22983    ) {
22984        self.start_git_blame(user_triggered, window, cx);
22985
22986        if ProjectSettings::get_global(cx)
22987            .git
22988            .inline_blame_delay()
22989            .is_some()
22990        {
22991            self.start_inline_blame_timer(window, cx);
22992        } else {
22993            self.show_git_blame_inline = true
22994        }
22995    }
22996
22997    pub fn blame(&self) -> Option<&Entity<GitBlame>> {
22998        self.blame.as_ref()
22999    }
23000
23001    pub fn show_git_blame_gutter(&self) -> bool {
23002        self.show_git_blame_gutter
23003    }
23004
23005    pub fn render_git_blame_gutter(&self, cx: &App) -> bool {
23006        !self.mode().is_minimap() && self.show_git_blame_gutter && self.has_blame_entries(cx)
23007    }
23008
23009    pub fn render_git_blame_inline(&self, window: &Window, cx: &App) -> bool {
23010        self.show_git_blame_inline
23011            && (self.focus_handle.is_focused(window) || self.inline_blame_popover.is_some())
23012            && !self.newest_selection_head_on_empty_line(cx)
23013            && self.has_blame_entries(cx)
23014    }
23015
23016    fn has_blame_entries(&self, cx: &App) -> bool {
23017        self.blame()
23018            .is_some_and(|blame| blame.read(cx).has_generated_entries())
23019    }
23020
23021    fn newest_selection_head_on_empty_line(&self, cx: &App) -> bool {
23022        let cursor_anchor = self.selections.newest_anchor().head();
23023
23024        let snapshot = self.buffer.read(cx).snapshot(cx);
23025        let buffer_row = MultiBufferRow(cursor_anchor.to_point(&snapshot).row);
23026
23027        snapshot.line_len(buffer_row) == 0
23028    }
23029
23030    fn get_permalink_to_line(&self, cx: &mut Context<Self>) -> Task<Result<url::Url>> {
23031        let buffer_and_selection = maybe!({
23032            let selection = self.selections.newest::<Point>(&self.display_snapshot(cx));
23033            let selection_range = selection.range();
23034
23035            let multi_buffer = self.buffer().read(cx);
23036            let multi_buffer_snapshot = multi_buffer.snapshot(cx);
23037            let buffer_ranges = multi_buffer_snapshot
23038                .range_to_buffer_ranges(selection_range.start..=selection_range.end);
23039
23040            let (buffer, range, _) = if selection.reversed {
23041                buffer_ranges.first()
23042            } else {
23043                buffer_ranges.last()
23044            }?;
23045
23046            let buffer_range = range.to_point(buffer);
23047
23048            let Some(buffer_diff) = multi_buffer.diff_for(buffer.remote_id()) else {
23049                return Some((
23050                    multi_buffer.buffer(buffer.remote_id()).unwrap(),
23051                    buffer_range.start.row..buffer_range.end.row,
23052                ));
23053            };
23054
23055            let buffer_diff_snapshot = buffer_diff.read(cx).snapshot(cx);
23056            let start =
23057                buffer_diff_snapshot.buffer_point_to_base_text_point(buffer_range.start, buffer);
23058            let end =
23059                buffer_diff_snapshot.buffer_point_to_base_text_point(buffer_range.end, buffer);
23060
23061            Some((
23062                multi_buffer.buffer(buffer.remote_id()).unwrap(),
23063                start.row..end.row,
23064            ))
23065        });
23066
23067        let Some((buffer, selection)) = buffer_and_selection else {
23068            return Task::ready(Err(anyhow!("failed to determine buffer and selection")));
23069        };
23070
23071        let Some(project) = self.project() else {
23072            return Task::ready(Err(anyhow!("editor does not have project")));
23073        };
23074
23075        project.update(cx, |project, cx| {
23076            project.get_permalink_to_line(&buffer, selection, cx)
23077        })
23078    }
23079
23080    pub fn copy_permalink_to_line(
23081        &mut self,
23082        _: &CopyPermalinkToLine,
23083        window: &mut Window,
23084        cx: &mut Context<Self>,
23085    ) {
23086        let permalink_task = self.get_permalink_to_line(cx);
23087        let workspace = self.workspace();
23088
23089        cx.spawn_in(window, async move |_, cx| match permalink_task.await {
23090            Ok(permalink) => {
23091                cx.update(|_, cx| {
23092                    cx.write_to_clipboard(ClipboardItem::new_string(permalink.to_string()));
23093                })
23094                .ok();
23095            }
23096            Err(err) => {
23097                let message = format!("Failed to copy permalink: {err}");
23098
23099                anyhow::Result::<()>::Err(err).log_err();
23100
23101                if let Some(workspace) = workspace {
23102                    workspace
23103                        .update_in(cx, |workspace, _, cx| {
23104                            struct CopyPermalinkToLine;
23105
23106                            workspace.show_toast(
23107                                Toast::new(
23108                                    NotificationId::unique::<CopyPermalinkToLine>(),
23109                                    message,
23110                                ),
23111                                cx,
23112                            )
23113                        })
23114                        .ok();
23115                }
23116            }
23117        })
23118        .detach();
23119    }
23120
23121    pub fn copy_file_location(
23122        &mut self,
23123        _: &CopyFileLocation,
23124        _: &mut Window,
23125        cx: &mut Context<Self>,
23126    ) {
23127        let selection = self
23128            .selections
23129            .newest::<Point>(&self.display_snapshot(cx))
23130            .start
23131            .row
23132            + 1;
23133        if let Some(file_location) = self.active_excerpt(cx).and_then(|(_, buffer, _)| {
23134            let project = self.project()?.read(cx);
23135            let file = buffer.read(cx).file()?;
23136            let path = file.path().display(project.path_style(cx));
23137
23138            Some(format!("{path}:{selection}"))
23139        }) {
23140            cx.write_to_clipboard(ClipboardItem::new_string(file_location));
23141        }
23142    }
23143
23144    pub fn open_permalink_to_line(
23145        &mut self,
23146        _: &OpenPermalinkToLine,
23147        window: &mut Window,
23148        cx: &mut Context<Self>,
23149    ) {
23150        let permalink_task = self.get_permalink_to_line(cx);
23151        let workspace = self.workspace();
23152
23153        cx.spawn_in(window, async move |_, cx| match permalink_task.await {
23154            Ok(permalink) => {
23155                cx.update(|_, cx| {
23156                    cx.open_url(permalink.as_ref());
23157                })
23158                .ok();
23159            }
23160            Err(err) => {
23161                let message = format!("Failed to open permalink: {err}");
23162
23163                anyhow::Result::<()>::Err(err).log_err();
23164
23165                if let Some(workspace) = workspace {
23166                    workspace.update(cx, |workspace, cx| {
23167                        struct OpenPermalinkToLine;
23168
23169                        workspace.show_toast(
23170                            Toast::new(NotificationId::unique::<OpenPermalinkToLine>(), message),
23171                            cx,
23172                        )
23173                    });
23174                }
23175            }
23176        })
23177        .detach();
23178    }
23179
23180    pub fn insert_uuid_v4(
23181        &mut self,
23182        _: &InsertUuidV4,
23183        window: &mut Window,
23184        cx: &mut Context<Self>,
23185    ) {
23186        self.insert_uuid(UuidVersion::V4, window, cx);
23187    }
23188
23189    pub fn insert_uuid_v7(
23190        &mut self,
23191        _: &InsertUuidV7,
23192        window: &mut Window,
23193        cx: &mut Context<Self>,
23194    ) {
23195        self.insert_uuid(UuidVersion::V7, window, cx);
23196    }
23197
23198    fn insert_uuid(&mut self, version: UuidVersion, window: &mut Window, cx: &mut Context<Self>) {
23199        self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
23200        self.transact(window, cx, |this, window, cx| {
23201            let edits = this
23202                .selections
23203                .all::<Point>(&this.display_snapshot(cx))
23204                .into_iter()
23205                .map(|selection| {
23206                    let uuid = match version {
23207                        UuidVersion::V4 => uuid::Uuid::new_v4(),
23208                        UuidVersion::V7 => uuid::Uuid::now_v7(),
23209                    };
23210
23211                    (selection.range(), uuid.to_string())
23212                });
23213            this.edit(edits, cx);
23214            this.refresh_edit_prediction(true, false, window, cx);
23215        });
23216    }
23217
23218    pub fn open_selections_in_multibuffer(
23219        &mut self,
23220        _: &OpenSelectionsInMultibuffer,
23221        window: &mut Window,
23222        cx: &mut Context<Self>,
23223    ) {
23224        let multibuffer = self.buffer.read(cx);
23225
23226        let Some(buffer) = multibuffer.as_singleton() else {
23227            return;
23228        };
23229
23230        let Some(workspace) = self.workspace() else {
23231            return;
23232        };
23233
23234        let title = multibuffer.title(cx).to_string();
23235
23236        let locations = self
23237            .selections
23238            .all_anchors(&self.display_snapshot(cx))
23239            .iter()
23240            .map(|selection| {
23241                (
23242                    buffer.clone(),
23243                    (selection.start.text_anchor..selection.end.text_anchor)
23244                        .to_point(buffer.read(cx)),
23245                )
23246            })
23247            .into_group_map();
23248
23249        cx.spawn_in(window, async move |_, cx| {
23250            workspace.update_in(cx, |workspace, window, cx| {
23251                Self::open_locations_in_multibuffer(
23252                    workspace,
23253                    locations,
23254                    format!("Selections for '{title}'"),
23255                    false,
23256                    false,
23257                    MultibufferSelectionMode::All,
23258                    window,
23259                    cx,
23260                );
23261            })
23262        })
23263        .detach();
23264    }
23265
23266    /// Adds a row highlight for the given range. If a row has multiple highlights, the
23267    /// last highlight added will be used.
23268    ///
23269    /// If the range ends at the beginning of a line, then that line will not be highlighted.
23270    pub fn highlight_rows<T: 'static>(
23271        &mut self,
23272        range: Range<Anchor>,
23273        color: Hsla,
23274        options: RowHighlightOptions,
23275        cx: &mut Context<Self>,
23276    ) {
23277        let snapshot = self.buffer().read(cx).snapshot(cx);
23278        let row_highlights = self.highlighted_rows.entry(TypeId::of::<T>()).or_default();
23279        let ix = row_highlights.binary_search_by(|highlight| {
23280            Ordering::Equal
23281                .then_with(|| highlight.range.start.cmp(&range.start, &snapshot))
23282                .then_with(|| highlight.range.end.cmp(&range.end, &snapshot))
23283        });
23284
23285        if let Err(mut ix) = ix {
23286            let index = post_inc(&mut self.highlight_order);
23287
23288            // If this range intersects with the preceding highlight, then merge it with
23289            // the preceding highlight. Otherwise insert a new highlight.
23290            let mut merged = false;
23291            if ix > 0 {
23292                let prev_highlight = &mut row_highlights[ix - 1];
23293                if prev_highlight
23294                    .range
23295                    .end
23296                    .cmp(&range.start, &snapshot)
23297                    .is_ge()
23298                {
23299                    ix -= 1;
23300                    if prev_highlight.range.end.cmp(&range.end, &snapshot).is_lt() {
23301                        prev_highlight.range.end = range.end;
23302                    }
23303                    merged = true;
23304                    prev_highlight.index = index;
23305                    prev_highlight.color = color;
23306                    prev_highlight.options = options;
23307                }
23308            }
23309
23310            if !merged {
23311                row_highlights.insert(
23312                    ix,
23313                    RowHighlight {
23314                        range,
23315                        index,
23316                        color,
23317                        options,
23318                        type_id: TypeId::of::<T>(),
23319                    },
23320                );
23321            }
23322
23323            // If any of the following highlights intersect with this one, merge them.
23324            while let Some(next_highlight) = row_highlights.get(ix + 1) {
23325                let highlight = &row_highlights[ix];
23326                if next_highlight
23327                    .range
23328                    .start
23329                    .cmp(&highlight.range.end, &snapshot)
23330                    .is_le()
23331                {
23332                    if next_highlight
23333                        .range
23334                        .end
23335                        .cmp(&highlight.range.end, &snapshot)
23336                        .is_gt()
23337                    {
23338                        row_highlights[ix].range.end = next_highlight.range.end;
23339                    }
23340                    row_highlights.remove(ix + 1);
23341                } else {
23342                    break;
23343                }
23344            }
23345        }
23346    }
23347
23348    /// Remove any highlighted row ranges of the given type that intersect the
23349    /// given ranges.
23350    pub fn remove_highlighted_rows<T: 'static>(
23351        &mut self,
23352        ranges_to_remove: Vec<Range<Anchor>>,
23353        cx: &mut Context<Self>,
23354    ) {
23355        let snapshot = self.buffer().read(cx).snapshot(cx);
23356        let row_highlights = self.highlighted_rows.entry(TypeId::of::<T>()).or_default();
23357        let mut ranges_to_remove = ranges_to_remove.iter().peekable();
23358        row_highlights.retain(|highlight| {
23359            while let Some(range_to_remove) = ranges_to_remove.peek() {
23360                match range_to_remove.end.cmp(&highlight.range.start, &snapshot) {
23361                    Ordering::Less | Ordering::Equal => {
23362                        ranges_to_remove.next();
23363                    }
23364                    Ordering::Greater => {
23365                        match range_to_remove.start.cmp(&highlight.range.end, &snapshot) {
23366                            Ordering::Less | Ordering::Equal => {
23367                                return false;
23368                            }
23369                            Ordering::Greater => break,
23370                        }
23371                    }
23372                }
23373            }
23374
23375            true
23376        })
23377    }
23378
23379    /// Clear all anchor ranges for a certain highlight context type, so no corresponding rows will be highlighted.
23380    pub fn clear_row_highlights<T: 'static>(&mut self) {
23381        self.highlighted_rows.remove(&TypeId::of::<T>());
23382    }
23383
23384    /// For a highlight given context type, gets all anchor ranges that will be used for row highlighting.
23385    pub fn highlighted_rows<T: 'static>(&self) -> impl '_ + Iterator<Item = (Range<Anchor>, Hsla)> {
23386        self.highlighted_rows
23387            .get(&TypeId::of::<T>())
23388            .map_or(&[] as &[_], |vec| vec.as_slice())
23389            .iter()
23390            .map(|highlight| (highlight.range.clone(), highlight.color))
23391    }
23392
23393    /// Merges all anchor ranges for all context types ever set, picking the last highlight added in case of a row conflict.
23394    /// Returns a map of display rows that are highlighted and their corresponding highlight color.
23395    /// Allows to ignore certain kinds of highlights.
23396    pub fn highlighted_display_rows(
23397        &self,
23398        window: &mut Window,
23399        cx: &mut App,
23400    ) -> BTreeMap<DisplayRow, LineHighlight> {
23401        let snapshot = self.snapshot(window, cx);
23402        let mut used_highlight_orders = HashMap::default();
23403        self.highlighted_rows
23404            .iter()
23405            .flat_map(|(_, highlighted_rows)| highlighted_rows.iter())
23406            .fold(
23407                BTreeMap::<DisplayRow, LineHighlight>::new(),
23408                |mut unique_rows, highlight| {
23409                    let start = highlight.range.start.to_display_point(&snapshot);
23410                    let end = highlight.range.end.to_display_point(&snapshot);
23411                    let start_row = start.row().0;
23412                    let end_row = if !highlight.range.end.text_anchor.is_max() && end.column() == 0
23413                    {
23414                        end.row().0.saturating_sub(1)
23415                    } else {
23416                        end.row().0
23417                    };
23418                    for row in start_row..=end_row {
23419                        let used_index =
23420                            used_highlight_orders.entry(row).or_insert(highlight.index);
23421                        if highlight.index >= *used_index {
23422                            *used_index = highlight.index;
23423                            unique_rows.insert(
23424                                DisplayRow(row),
23425                                LineHighlight {
23426                                    include_gutter: highlight.options.include_gutter,
23427                                    border: None,
23428                                    background: highlight.color.into(),
23429                                    type_id: Some(highlight.type_id),
23430                                },
23431                            );
23432                        }
23433                    }
23434                    unique_rows
23435                },
23436            )
23437    }
23438
23439    pub fn highlighted_display_row_for_autoscroll(
23440        &self,
23441        snapshot: &DisplaySnapshot,
23442    ) -> Option<DisplayRow> {
23443        self.highlighted_rows
23444            .values()
23445            .flat_map(|highlighted_rows| highlighted_rows.iter())
23446            .filter_map(|highlight| {
23447                if highlight.options.autoscroll {
23448                    Some(highlight.range.start.to_display_point(snapshot).row())
23449                } else {
23450                    None
23451                }
23452            })
23453            .min()
23454    }
23455
23456    pub fn set_search_within_ranges(&mut self, ranges: &[Range<Anchor>], cx: &mut Context<Self>) {
23457        self.highlight_background(
23458            HighlightKey::SearchWithinRange,
23459            ranges,
23460            |_, colors| colors.colors().editor_document_highlight_read_background,
23461            cx,
23462        )
23463    }
23464
23465    pub fn set_breadcrumb_header(&mut self, new_header: String) {
23466        self.breadcrumb_header = Some(new_header);
23467    }
23468
23469    pub fn clear_search_within_ranges(&mut self, cx: &mut Context<Self>) {
23470        self.clear_background_highlights(HighlightKey::SearchWithinRange, cx);
23471    }
23472
23473    pub fn highlight_background(
23474        &mut self,
23475        key: HighlightKey,
23476        ranges: &[Range<Anchor>],
23477        color_fetcher: impl Fn(&usize, &Theme) -> Hsla + Send + Sync + 'static,
23478        cx: &mut Context<Self>,
23479    ) {
23480        self.background_highlights
23481            .insert(key, (Arc::new(color_fetcher), Arc::from(ranges)));
23482        self.scrollbar_marker_state.dirty = true;
23483        cx.notify();
23484    }
23485
23486    pub fn highlight_background_key(
23487        &mut self,
23488        key: HighlightKey,
23489        ranges: &[Range<Anchor>],
23490        color_fetcher: impl Fn(&usize, &Theme) -> Hsla + Send + Sync + 'static,
23491        cx: &mut Context<Self>,
23492    ) {
23493        self.background_highlights
23494            .insert(key, (Arc::new(color_fetcher), Arc::from(ranges)));
23495        self.scrollbar_marker_state.dirty = true;
23496        cx.notify();
23497    }
23498
23499    pub fn clear_background_highlights(
23500        &mut self,
23501        key: HighlightKey,
23502        cx: &mut Context<Self>,
23503    ) -> Option<BackgroundHighlight> {
23504        let text_highlights = self.background_highlights.remove(&key)?;
23505        if !text_highlights.1.is_empty() {
23506            self.scrollbar_marker_state.dirty = true;
23507            cx.notify();
23508        }
23509        Some(text_highlights)
23510    }
23511
23512    pub fn highlight_gutter<T: 'static>(
23513        &mut self,
23514        ranges: impl Into<Vec<Range<Anchor>>>,
23515        color_fetcher: fn(&App) -> Hsla,
23516        cx: &mut Context<Self>,
23517    ) {
23518        self.gutter_highlights
23519            .insert(TypeId::of::<T>(), (color_fetcher, ranges.into()));
23520        cx.notify();
23521    }
23522
23523    pub fn clear_gutter_highlights<T: 'static>(
23524        &mut self,
23525        cx: &mut Context<Self>,
23526    ) -> Option<GutterHighlight> {
23527        cx.notify();
23528        self.gutter_highlights.remove(&TypeId::of::<T>())
23529    }
23530
23531    pub fn insert_gutter_highlight<T: 'static>(
23532        &mut self,
23533        range: Range<Anchor>,
23534        color_fetcher: fn(&App) -> Hsla,
23535        cx: &mut Context<Self>,
23536    ) {
23537        let snapshot = self.buffer().read(cx).snapshot(cx);
23538        let mut highlights = self
23539            .gutter_highlights
23540            .remove(&TypeId::of::<T>())
23541            .map(|(_, highlights)| highlights)
23542            .unwrap_or_default();
23543        let ix = highlights.binary_search_by(|highlight| {
23544            Ordering::Equal
23545                .then_with(|| highlight.start.cmp(&range.start, &snapshot))
23546                .then_with(|| highlight.end.cmp(&range.end, &snapshot))
23547        });
23548        if let Err(ix) = ix {
23549            highlights.insert(ix, range);
23550        }
23551        self.gutter_highlights
23552            .insert(TypeId::of::<T>(), (color_fetcher, highlights));
23553    }
23554
23555    pub fn remove_gutter_highlights<T: 'static>(
23556        &mut self,
23557        ranges_to_remove: Vec<Range<Anchor>>,
23558        cx: &mut Context<Self>,
23559    ) {
23560        let snapshot = self.buffer().read(cx).snapshot(cx);
23561        let Some((color_fetcher, mut gutter_highlights)) =
23562            self.gutter_highlights.remove(&TypeId::of::<T>())
23563        else {
23564            return;
23565        };
23566        let mut ranges_to_remove = ranges_to_remove.iter().peekable();
23567        gutter_highlights.retain(|highlight| {
23568            while let Some(range_to_remove) = ranges_to_remove.peek() {
23569                match range_to_remove.end.cmp(&highlight.start, &snapshot) {
23570                    Ordering::Less | Ordering::Equal => {
23571                        ranges_to_remove.next();
23572                    }
23573                    Ordering::Greater => {
23574                        match range_to_remove.start.cmp(&highlight.end, &snapshot) {
23575                            Ordering::Less | Ordering::Equal => {
23576                                return false;
23577                            }
23578                            Ordering::Greater => break,
23579                        }
23580                    }
23581                }
23582            }
23583
23584            true
23585        });
23586        self.gutter_highlights
23587            .insert(TypeId::of::<T>(), (color_fetcher, gutter_highlights));
23588    }
23589
23590    #[cfg(feature = "test-support")]
23591    pub fn all_text_highlights(
23592        &self,
23593        window: &mut Window,
23594        cx: &mut Context<Self>,
23595    ) -> Vec<(HighlightStyle, Vec<Range<DisplayPoint>>)> {
23596        let snapshot = self.snapshot(window, cx);
23597        self.display_map.update(cx, |display_map, _| {
23598            display_map
23599                .all_text_highlights()
23600                .map(|(_, highlight)| {
23601                    let (style, ranges) = highlight.as_ref();
23602                    (
23603                        *style,
23604                        ranges
23605                            .iter()
23606                            .map(|range| range.clone().to_display_points(&snapshot))
23607                            .collect(),
23608                    )
23609                })
23610                .collect()
23611        })
23612    }
23613
23614    #[cfg(feature = "test-support")]
23615    pub fn all_text_background_highlights(
23616        &self,
23617        window: &mut Window,
23618        cx: &mut Context<Self>,
23619    ) -> Vec<(Range<DisplayPoint>, Hsla)> {
23620        let snapshot = self.snapshot(window, cx);
23621        let buffer = &snapshot.buffer_snapshot();
23622        let start = buffer.anchor_before(MultiBufferOffset(0));
23623        let end = buffer.anchor_after(buffer.len());
23624        self.sorted_background_highlights_in_range(start..end, &snapshot, cx.theme())
23625    }
23626
23627    #[cfg(any(test, feature = "test-support"))]
23628    pub fn sorted_background_highlights_in_range(
23629        &self,
23630        search_range: Range<Anchor>,
23631        display_snapshot: &DisplaySnapshot,
23632        theme: &Theme,
23633    ) -> Vec<(Range<DisplayPoint>, Hsla)> {
23634        let mut res = self.background_highlights_in_range(search_range, display_snapshot, theme);
23635        res.sort_by(|a, b| {
23636            a.0.start
23637                .cmp(&b.0.start)
23638                .then_with(|| a.0.end.cmp(&b.0.end))
23639                .then_with(|| a.1.cmp(&b.1))
23640        });
23641        res
23642    }
23643
23644    #[cfg(feature = "test-support")]
23645    pub fn search_background_highlights(&mut self, cx: &mut Context<Self>) -> Vec<Range<Point>> {
23646        let snapshot = self.buffer().read(cx).snapshot(cx);
23647
23648        let highlights = self
23649            .background_highlights
23650            .get(&HighlightKey::BufferSearchHighlights);
23651
23652        if let Some((_color, ranges)) = highlights {
23653            ranges
23654                .iter()
23655                .map(|range| range.start.to_point(&snapshot)..range.end.to_point(&snapshot))
23656                .collect_vec()
23657        } else {
23658            vec![]
23659        }
23660    }
23661
23662    fn document_highlights_for_position<'a>(
23663        &'a self,
23664        position: Anchor,
23665        buffer: &'a MultiBufferSnapshot,
23666    ) -> impl 'a + Iterator<Item = &'a Range<Anchor>> {
23667        let read_highlights = self
23668            .background_highlights
23669            .get(&HighlightKey::DocumentHighlightRead)
23670            .map(|h| &h.1);
23671        let write_highlights = self
23672            .background_highlights
23673            .get(&HighlightKey::DocumentHighlightWrite)
23674            .map(|h| &h.1);
23675        let left_position = position.bias_left(buffer);
23676        let right_position = position.bias_right(buffer);
23677        read_highlights
23678            .into_iter()
23679            .chain(write_highlights)
23680            .flat_map(move |ranges| {
23681                let start_ix = match ranges.binary_search_by(|probe| {
23682                    let cmp = probe.end.cmp(&left_position, buffer);
23683                    if cmp.is_ge() {
23684                        Ordering::Greater
23685                    } else {
23686                        Ordering::Less
23687                    }
23688                }) {
23689                    Ok(i) | Err(i) => i,
23690                };
23691
23692                ranges[start_ix..]
23693                    .iter()
23694                    .take_while(move |range| range.start.cmp(&right_position, buffer).is_le())
23695            })
23696    }
23697
23698    pub fn has_background_highlights(&self, key: HighlightKey) -> bool {
23699        self.background_highlights
23700            .get(&key)
23701            .is_some_and(|(_, highlights)| !highlights.is_empty())
23702    }
23703
23704    /// Returns all background highlights for a given range.
23705    ///
23706    /// The order of highlights is not deterministic, do sort the ranges if needed for the logic.
23707    pub fn background_highlights_in_range(
23708        &self,
23709        search_range: Range<Anchor>,
23710        display_snapshot: &DisplaySnapshot,
23711        theme: &Theme,
23712    ) -> Vec<(Range<DisplayPoint>, Hsla)> {
23713        let mut results = Vec::new();
23714        for (color_fetcher, ranges) in self.background_highlights.values() {
23715            let start_ix = match ranges.binary_search_by(|probe| {
23716                let cmp = probe
23717                    .end
23718                    .cmp(&search_range.start, &display_snapshot.buffer_snapshot());
23719                if cmp.is_gt() {
23720                    Ordering::Greater
23721                } else {
23722                    Ordering::Less
23723                }
23724            }) {
23725                Ok(i) | Err(i) => i,
23726            };
23727            for (index, range) in ranges[start_ix..].iter().enumerate() {
23728                if range
23729                    .start
23730                    .cmp(&search_range.end, &display_snapshot.buffer_snapshot())
23731                    .is_ge()
23732                {
23733                    break;
23734                }
23735
23736                let color = color_fetcher(&(start_ix + index), theme);
23737                let start = range.start.to_display_point(display_snapshot);
23738                let end = range.end.to_display_point(display_snapshot);
23739                results.push((start..end, color))
23740            }
23741        }
23742        results
23743    }
23744
23745    pub fn gutter_highlights_in_range(
23746        &self,
23747        search_range: Range<Anchor>,
23748        display_snapshot: &DisplaySnapshot,
23749        cx: &App,
23750    ) -> Vec<(Range<DisplayPoint>, Hsla)> {
23751        let mut results = Vec::new();
23752        for (color_fetcher, ranges) in self.gutter_highlights.values() {
23753            let color = color_fetcher(cx);
23754            let start_ix = match ranges.binary_search_by(|probe| {
23755                let cmp = probe
23756                    .end
23757                    .cmp(&search_range.start, &display_snapshot.buffer_snapshot());
23758                if cmp.is_gt() {
23759                    Ordering::Greater
23760                } else {
23761                    Ordering::Less
23762                }
23763            }) {
23764                Ok(i) | Err(i) => i,
23765            };
23766            for range in &ranges[start_ix..] {
23767                if range
23768                    .start
23769                    .cmp(&search_range.end, &display_snapshot.buffer_snapshot())
23770                    .is_ge()
23771                {
23772                    break;
23773                }
23774
23775                let start = range.start.to_display_point(display_snapshot);
23776                let end = range.end.to_display_point(display_snapshot);
23777                results.push((start..end, color))
23778            }
23779        }
23780        results
23781    }
23782
23783    /// Get the text ranges corresponding to the redaction query
23784    pub fn redacted_ranges(
23785        &self,
23786        search_range: Range<Anchor>,
23787        display_snapshot: &DisplaySnapshot,
23788        cx: &App,
23789    ) -> Vec<Range<DisplayPoint>> {
23790        display_snapshot
23791            .buffer_snapshot()
23792            .redacted_ranges(search_range, |file| {
23793                if let Some(file) = file {
23794                    file.is_private()
23795                        && EditorSettings::get(
23796                            Some(SettingsLocation {
23797                                worktree_id: file.worktree_id(cx),
23798                                path: file.path().as_ref(),
23799                            }),
23800                            cx,
23801                        )
23802                        .redact_private_values
23803                } else {
23804                    false
23805                }
23806            })
23807            .map(|range| {
23808                range.start.to_display_point(display_snapshot)
23809                    ..range.end.to_display_point(display_snapshot)
23810            })
23811            .collect()
23812    }
23813
23814    pub fn highlight_text_key(
23815        &mut self,
23816        key: HighlightKey,
23817        ranges: Vec<Range<Anchor>>,
23818        style: HighlightStyle,
23819        merge: bool,
23820        cx: &mut Context<Self>,
23821    ) {
23822        self.display_map.update(cx, |map, cx| {
23823            map.highlight_text(key, ranges, style, merge, cx);
23824        });
23825        cx.notify();
23826    }
23827
23828    pub fn highlight_text(
23829        &mut self,
23830        key: HighlightKey,
23831        ranges: Vec<Range<Anchor>>,
23832        style: HighlightStyle,
23833        cx: &mut Context<Self>,
23834    ) {
23835        self.display_map.update(cx, |map, cx| {
23836            map.highlight_text(key, ranges, style, false, cx)
23837        });
23838        cx.notify();
23839    }
23840
23841    pub fn text_highlights<'a>(
23842        &'a self,
23843        key: HighlightKey,
23844        cx: &'a App,
23845    ) -> Option<(HighlightStyle, &'a [Range<Anchor>])> {
23846        self.display_map.read(cx).text_highlights(key)
23847    }
23848
23849    pub fn clear_highlights(&mut self, key: HighlightKey, cx: &mut Context<Self>) {
23850        let cleared = self
23851            .display_map
23852            .update(cx, |map, _| map.clear_highlights(key));
23853        if cleared {
23854            cx.notify();
23855        }
23856    }
23857
23858    pub fn clear_highlights_with(
23859        &mut self,
23860        f: &mut dyn FnMut(&HighlightKey) -> bool,
23861        cx: &mut Context<Self>,
23862    ) {
23863        let cleared = self
23864            .display_map
23865            .update(cx, |map, _| map.clear_highlights_with(f));
23866        if cleared {
23867            cx.notify();
23868        }
23869    }
23870
23871    pub fn show_local_cursors(&self, window: &mut Window, cx: &mut App) -> bool {
23872        (self.read_only(cx) || self.blink_manager.read(cx).visible())
23873            && self.focus_handle.is_focused(window)
23874    }
23875
23876    pub fn set_show_cursor_when_unfocused(&mut self, is_enabled: bool, cx: &mut Context<Self>) {
23877        self.show_cursor_when_unfocused = is_enabled;
23878        cx.notify();
23879    }
23880
23881    fn on_buffer_changed(&mut self, _: Entity<MultiBuffer>, cx: &mut Context<Self>) {
23882        cx.notify();
23883    }
23884
23885    fn on_debug_session_event(
23886        &mut self,
23887        _session: Entity<Session>,
23888        event: &SessionEvent,
23889        cx: &mut Context<Self>,
23890    ) {
23891        if let SessionEvent::InvalidateInlineValue = event {
23892            self.refresh_inline_values(cx);
23893        }
23894    }
23895
23896    pub fn refresh_inline_values(&mut self, cx: &mut Context<Self>) {
23897        let Some(project) = self.project.clone() else {
23898            return;
23899        };
23900
23901        if !self.inline_value_cache.enabled {
23902            let inlays = std::mem::take(&mut self.inline_value_cache.inlays);
23903            self.splice_inlays(&inlays, Vec::new(), cx);
23904            return;
23905        }
23906
23907        let current_execution_position = self
23908            .highlighted_rows
23909            .get(&TypeId::of::<ActiveDebugLine>())
23910            .and_then(|lines| lines.last().map(|line| line.range.end));
23911
23912        self.inline_value_cache.refresh_task = cx.spawn(async move |editor, cx| {
23913            let inline_values = editor
23914                .update(cx, |editor, cx| {
23915                    let Some(current_execution_position) = current_execution_position else {
23916                        return Some(Task::ready(Ok(Vec::new())));
23917                    };
23918
23919                    let buffer = editor.buffer.read_with(cx, |buffer, cx| {
23920                        let snapshot = buffer.snapshot(cx);
23921
23922                        let excerpt = snapshot.excerpt_containing(
23923                            current_execution_position..current_execution_position,
23924                        )?;
23925
23926                        editor.buffer.read(cx).buffer(excerpt.buffer_id())
23927                    })?;
23928
23929                    let range =
23930                        buffer.read(cx).anchor_before(0)..current_execution_position.text_anchor;
23931
23932                    project.inline_values(buffer, range, cx)
23933                })
23934                .ok()
23935                .flatten()?
23936                .await
23937                .context("refreshing debugger inlays")
23938                .log_err()?;
23939
23940            let mut buffer_inline_values: HashMap<BufferId, Vec<InlayHint>> = HashMap::default();
23941
23942            for (buffer_id, inline_value) in inline_values
23943                .into_iter()
23944                .filter_map(|hint| Some((hint.position.buffer_id?, hint)))
23945            {
23946                buffer_inline_values
23947                    .entry(buffer_id)
23948                    .or_default()
23949                    .push(inline_value);
23950            }
23951
23952            editor
23953                .update(cx, |editor, cx| {
23954                    let snapshot = editor.buffer.read(cx).snapshot(cx);
23955                    let mut new_inlays = Vec::default();
23956
23957                    for (excerpt_id, buffer_snapshot, _) in snapshot.excerpts() {
23958                        let buffer_id = buffer_snapshot.remote_id();
23959                        buffer_inline_values
23960                            .get(&buffer_id)
23961                            .into_iter()
23962                            .flatten()
23963                            .for_each(|hint| {
23964                                let inlay = Inlay::debugger(
23965                                    post_inc(&mut editor.next_inlay_id),
23966                                    Anchor::in_buffer(excerpt_id, hint.position),
23967                                    hint.text(),
23968                                );
23969                                if !inlay.text().chars().contains(&'\n') {
23970                                    new_inlays.push(inlay);
23971                                }
23972                            });
23973                    }
23974
23975                    let mut inlay_ids = new_inlays.iter().map(|inlay| inlay.id).collect();
23976                    std::mem::swap(&mut editor.inline_value_cache.inlays, &mut inlay_ids);
23977
23978                    editor.splice_inlays(&inlay_ids, new_inlays, cx);
23979                })
23980                .ok()?;
23981            Some(())
23982        });
23983    }
23984
23985    fn on_buffer_event(
23986        &mut self,
23987        multibuffer: &Entity<MultiBuffer>,
23988        event: &multi_buffer::Event,
23989        window: &mut Window,
23990        cx: &mut Context<Self>,
23991    ) {
23992        match event {
23993            multi_buffer::Event::Edited { edited_buffer } => {
23994                self.scrollbar_marker_state.dirty = true;
23995                self.active_indent_guides_state.dirty = true;
23996                self.refresh_active_diagnostics(cx);
23997                self.refresh_code_actions(window, cx);
23998                self.refresh_single_line_folds(window, cx);
23999                let snapshot = self.snapshot(window, cx);
24000                self.refresh_matching_bracket_highlights(&snapshot, cx);
24001                self.refresh_outline_symbols_at_cursor(cx);
24002                self.refresh_sticky_headers(&snapshot, cx);
24003                if self.has_active_edit_prediction() {
24004                    self.update_visible_edit_prediction(window, cx);
24005                }
24006
24007                // Clean up orphaned review comments after edits
24008                self.cleanup_orphaned_review_comments(cx);
24009
24010                if let Some(buffer) = edited_buffer {
24011                    if buffer.read(cx).file().is_none() {
24012                        cx.emit(EditorEvent::TitleChanged);
24013                    }
24014
24015                    if self.project.is_some() {
24016                        let buffer_id = buffer.read(cx).remote_id();
24017                        self.register_buffer(buffer_id, cx);
24018                        self.update_lsp_data(Some(buffer_id), window, cx);
24019                        self.refresh_inlay_hints(
24020                            InlayHintRefreshReason::BufferEdited(buffer_id),
24021                            cx,
24022                        );
24023                    }
24024                }
24025
24026                cx.emit(EditorEvent::BufferEdited);
24027                cx.emit(SearchEvent::MatchesInvalidated);
24028
24029                let Some(project) = &self.project else { return };
24030                let (telemetry, is_via_ssh) = {
24031                    let project = project.read(cx);
24032                    let telemetry = project.client().telemetry().clone();
24033                    let is_via_ssh = project.is_via_remote_server();
24034                    (telemetry, is_via_ssh)
24035                };
24036                telemetry.log_edit_event("editor", is_via_ssh);
24037            }
24038            multi_buffer::Event::ExcerptsAdded {
24039                buffer,
24040                predecessor,
24041                excerpts,
24042            } => {
24043                self.tasks_update_task = Some(self.refresh_runnables(window, cx));
24044                let buffer_id = buffer.read(cx).remote_id();
24045                if self.buffer.read(cx).diff_for(buffer_id).is_none()
24046                    && let Some(project) = &self.project
24047                {
24048                    update_uncommitted_diff_for_buffer(
24049                        cx.entity(),
24050                        project,
24051                        [buffer.clone()],
24052                        self.buffer.clone(),
24053                        cx,
24054                    )
24055                    .detach();
24056                }
24057                self.semantic_token_state
24058                    .invalidate_buffer(&buffer.read(cx).remote_id());
24059                self.update_lsp_data(Some(buffer_id), window, cx);
24060                self.refresh_inlay_hints(InlayHintRefreshReason::NewLinesShown, cx);
24061                self.colorize_brackets(false, cx);
24062                self.refresh_selected_text_highlights(true, window, cx);
24063                cx.emit(EditorEvent::ExcerptsAdded {
24064                    buffer: buffer.clone(),
24065                    predecessor: *predecessor,
24066                    excerpts: excerpts.clone(),
24067                });
24068            }
24069            multi_buffer::Event::ExcerptsRemoved {
24070                ids,
24071                removed_buffer_ids,
24072            } => {
24073                if let Some(inlay_hints) = &mut self.inlay_hints {
24074                    inlay_hints.remove_inlay_chunk_data(removed_buffer_ids);
24075                }
24076                self.refresh_inlay_hints(InlayHintRefreshReason::ExcerptsRemoved(ids.clone()), cx);
24077                for buffer_id in removed_buffer_ids {
24078                    self.registered_buffers.remove(buffer_id);
24079                    self.tasks
24080                        .retain(|(task_buffer_id, _), _| task_buffer_id != buffer_id);
24081                    self.semantic_token_state.invalidate_buffer(buffer_id);
24082                    self.display_map.update(cx, |display_map, cx| {
24083                        display_map.invalidate_semantic_highlights(*buffer_id);
24084                        display_map.clear_lsp_folding_ranges(*buffer_id, cx);
24085                    });
24086                }
24087                jsx_tag_auto_close::refresh_enabled_in_any_buffer(self, multibuffer, cx);
24088                cx.emit(EditorEvent::ExcerptsRemoved {
24089                    ids: ids.clone(),
24090                    removed_buffer_ids: removed_buffer_ids.clone(),
24091                });
24092            }
24093            multi_buffer::Event::ExcerptsEdited {
24094                excerpt_ids,
24095                buffer_ids,
24096            } => {
24097                self.display_map.update(cx, |map, cx| {
24098                    map.unfold_buffers(buffer_ids.iter().copied(), cx)
24099                });
24100                cx.emit(EditorEvent::ExcerptsEdited {
24101                    ids: excerpt_ids.clone(),
24102                });
24103            }
24104            multi_buffer::Event::ExcerptsExpanded { ids } => {
24105                self.refresh_inlay_hints(InlayHintRefreshReason::NewLinesShown, cx);
24106                self.refresh_document_highlights(cx);
24107                let snapshot = multibuffer.read(cx).snapshot(cx);
24108                for id in ids {
24109                    self.fetched_tree_sitter_chunks.remove(id);
24110                    if let Some(buffer) = snapshot.buffer_for_excerpt(*id) {
24111                        self.semantic_token_state
24112                            .invalidate_buffer(&buffer.remote_id());
24113                    }
24114                }
24115                self.colorize_brackets(false, cx);
24116                self.update_lsp_data(None, window, cx);
24117                cx.emit(EditorEvent::ExcerptsExpanded { ids: ids.clone() })
24118            }
24119            multi_buffer::Event::Reparsed(buffer_id) => {
24120                self.tasks_update_task = Some(self.refresh_runnables(window, cx));
24121                self.refresh_selected_text_highlights(true, window, cx);
24122                self.colorize_brackets(true, cx);
24123                jsx_tag_auto_close::refresh_enabled_in_any_buffer(self, multibuffer, cx);
24124
24125                cx.emit(EditorEvent::Reparsed(*buffer_id));
24126            }
24127            multi_buffer::Event::DiffHunksToggled => {
24128                self.tasks_update_task = Some(self.refresh_runnables(window, cx));
24129            }
24130            multi_buffer::Event::LanguageChanged(buffer_id, is_fresh_language) => {
24131                if !is_fresh_language {
24132                    self.registered_buffers.remove(&buffer_id);
24133                }
24134                jsx_tag_auto_close::refresh_enabled_in_any_buffer(self, multibuffer, cx);
24135                cx.emit(EditorEvent::Reparsed(*buffer_id));
24136                cx.notify();
24137            }
24138            multi_buffer::Event::DirtyChanged => cx.emit(EditorEvent::DirtyChanged),
24139            multi_buffer::Event::Saved => cx.emit(EditorEvent::Saved),
24140            multi_buffer::Event::FileHandleChanged
24141            | multi_buffer::Event::Reloaded
24142            | multi_buffer::Event::BufferDiffChanged => cx.emit(EditorEvent::TitleChanged),
24143            multi_buffer::Event::DiagnosticsUpdated => {
24144                self.update_diagnostics_state(window, cx);
24145            }
24146            _ => {}
24147        };
24148    }
24149
24150    fn update_diagnostics_state(&mut self, window: &mut Window, cx: &mut Context<'_, Editor>) {
24151        if !self.diagnostics_enabled() {
24152            return;
24153        }
24154        self.refresh_active_diagnostics(cx);
24155        self.refresh_inline_diagnostics(true, window, cx);
24156        self.scrollbar_marker_state.dirty = true;
24157        cx.notify();
24158    }
24159
24160    pub fn start_temporary_diff_override(&mut self) {
24161        self.load_diff_task.take();
24162        self.temporary_diff_override = true;
24163    }
24164
24165    pub fn end_temporary_diff_override(&mut self, cx: &mut Context<Self>) {
24166        self.temporary_diff_override = false;
24167        self.set_render_diff_hunk_controls(Arc::new(render_diff_hunk_controls), cx);
24168        self.buffer.update(cx, |buffer, cx| {
24169            buffer.set_all_diff_hunks_collapsed(cx);
24170        });
24171
24172        if let Some(project) = self.project.clone() {
24173            self.load_diff_task = Some(
24174                update_uncommitted_diff_for_buffer(
24175                    cx.entity(),
24176                    &project,
24177                    self.buffer.read(cx).all_buffers(),
24178                    self.buffer.clone(),
24179                    cx,
24180                )
24181                .shared(),
24182            );
24183        }
24184    }
24185
24186    fn on_display_map_changed(
24187        &mut self,
24188        _: Entity<DisplayMap>,
24189        _: &mut Window,
24190        cx: &mut Context<Self>,
24191    ) {
24192        cx.notify();
24193    }
24194
24195    fn fetch_accent_data(&self, cx: &App) -> Option<AccentData> {
24196        if !self.mode.is_full() {
24197            return None;
24198        }
24199
24200        let theme_settings = theme::ThemeSettings::get_global(cx);
24201        let theme = cx.theme();
24202        let accent_colors = theme.accents().clone();
24203
24204        let accent_overrides = theme_settings
24205            .theme_overrides
24206            .get(theme.name.as_ref())
24207            .map(|theme_style| &theme_style.accents)
24208            .into_iter()
24209            .flatten()
24210            .chain(
24211                theme_settings
24212                    .experimental_theme_overrides
24213                    .as_ref()
24214                    .map(|overrides| &overrides.accents)
24215                    .into_iter()
24216                    .flatten(),
24217            )
24218            .flat_map(|accent| accent.0.clone().map(SharedString::from))
24219            .collect();
24220
24221        Some(AccentData {
24222            colors: accent_colors,
24223            overrides: accent_overrides,
24224        })
24225    }
24226
24227    fn fetch_applicable_language_settings(
24228        &self,
24229        cx: &App,
24230    ) -> HashMap<Option<LanguageName>, LanguageSettings> {
24231        if !self.mode.is_full() {
24232            return HashMap::default();
24233        }
24234
24235        self.buffer().read(cx).all_buffers().into_iter().fold(
24236            HashMap::default(),
24237            |mut acc, buffer| {
24238                let buffer = buffer.read(cx);
24239                let language = buffer.language().map(|language| language.name());
24240                if let hash_map::Entry::Vacant(v) = acc.entry(language.clone()) {
24241                    let file = buffer.file();
24242                    v.insert(language_settings(language, file, cx).into_owned());
24243                }
24244                acc
24245            },
24246        )
24247    }
24248
24249    fn settings_changed(&mut self, window: &mut Window, cx: &mut Context<Self>) {
24250        let new_language_settings = self.fetch_applicable_language_settings(cx);
24251        let language_settings_changed = new_language_settings != self.applicable_language_settings;
24252        self.applicable_language_settings = new_language_settings;
24253
24254        let new_accents = self.fetch_accent_data(cx);
24255        let accents_changed = new_accents != self.accent_data;
24256        self.accent_data = new_accents;
24257
24258        if self.diagnostics_enabled() {
24259            let new_severity = EditorSettings::get_global(cx)
24260                .diagnostics_max_severity
24261                .unwrap_or(DiagnosticSeverity::Hint);
24262            self.set_max_diagnostics_severity(new_severity, cx);
24263        }
24264        self.tasks_update_task = Some(self.refresh_runnables(window, cx));
24265        self.update_edit_prediction_settings(cx);
24266        self.refresh_edit_prediction(true, false, window, cx);
24267        self.refresh_inline_values(cx);
24268
24269        let old_cursor_shape = self.cursor_shape;
24270        let old_show_breadcrumbs = self.show_breadcrumbs;
24271
24272        {
24273            let editor_settings = EditorSettings::get_global(cx);
24274            self.scroll_manager.vertical_scroll_margin = editor_settings.vertical_scroll_margin;
24275            self.show_breadcrumbs = editor_settings.toolbar.breadcrumbs;
24276            self.cursor_shape = editor_settings.cursor_shape.unwrap_or_default();
24277            self.hide_mouse_mode = editor_settings.hide_mouse.unwrap_or_default();
24278        }
24279
24280        if old_cursor_shape != self.cursor_shape {
24281            cx.emit(EditorEvent::CursorShapeChanged);
24282        }
24283
24284        if old_show_breadcrumbs != self.show_breadcrumbs {
24285            cx.emit(EditorEvent::BreadcrumbsChanged);
24286        }
24287
24288        let (restore_unsaved_buffers, show_inline_diagnostics, inline_blame_enabled) = {
24289            let project_settings = ProjectSettings::get_global(cx);
24290            (
24291                project_settings.session.restore_unsaved_buffers,
24292                project_settings.diagnostics.inline.enabled,
24293                project_settings.git.inline_blame.enabled,
24294            )
24295        };
24296        self.buffer_serialization = self
24297            .should_serialize_buffer()
24298            .then(|| BufferSerialization::new(restore_unsaved_buffers));
24299
24300        if self.mode.is_full() {
24301            if self.show_inline_diagnostics != show_inline_diagnostics {
24302                self.show_inline_diagnostics = show_inline_diagnostics;
24303                self.refresh_inline_diagnostics(false, window, cx);
24304            }
24305
24306            if self.git_blame_inline_enabled != inline_blame_enabled {
24307                self.toggle_git_blame_inline_internal(false, window, cx);
24308            }
24309
24310            let minimap_settings = EditorSettings::get_global(cx).minimap;
24311            if self.minimap_visibility != MinimapVisibility::Disabled {
24312                if self.minimap_visibility.settings_visibility()
24313                    != minimap_settings.minimap_enabled()
24314                {
24315                    self.set_minimap_visibility(
24316                        MinimapVisibility::for_mode(self.mode(), cx),
24317                        window,
24318                        cx,
24319                    );
24320                } else if let Some(minimap_entity) = self.minimap.as_ref() {
24321                    minimap_entity.update(cx, |minimap_editor, cx| {
24322                        minimap_editor.update_minimap_configuration(minimap_settings, cx)
24323                    })
24324                }
24325            }
24326
24327            if language_settings_changed || accents_changed {
24328                self.colorize_brackets(true, cx);
24329            }
24330
24331            if language_settings_changed {
24332                self.clear_disabled_lsp_folding_ranges(window, cx);
24333                self.refresh_document_symbols(None, cx);
24334            }
24335
24336            if let Some(inlay_splice) = self.colors.as_mut().and_then(|colors| {
24337                colors.render_mode_updated(EditorSettings::get_global(cx).lsp_document_colors)
24338            }) {
24339                if !inlay_splice.is_empty() {
24340                    self.splice_inlays(&inlay_splice.to_remove, inlay_splice.to_insert, cx);
24341                }
24342                self.refresh_document_colors(None, window, cx);
24343            }
24344
24345            self.refresh_inlay_hints(
24346                InlayHintRefreshReason::SettingsChange(inlay_hint_settings(
24347                    self.selections.newest_anchor().head(),
24348                    &self.buffer.read(cx).snapshot(cx),
24349                    cx,
24350                )),
24351                cx,
24352            );
24353
24354            let new_semantic_token_rules = ProjectSettings::get_global(cx)
24355                .global_lsp_settings
24356                .semantic_token_rules
24357                .clone();
24358            let semantic_token_rules_changed = self
24359                .semantic_token_state
24360                .update_rules(new_semantic_token_rules);
24361            if language_settings_changed || semantic_token_rules_changed {
24362                self.invalidate_semantic_tokens(None);
24363                self.refresh_semantic_tokens(None, None, cx);
24364            }
24365        }
24366
24367        cx.notify();
24368    }
24369
24370    fn theme_changed(&mut self, _: &mut Window, cx: &mut Context<Self>) {
24371        if !self.mode.is_full() {
24372            return;
24373        }
24374
24375        let new_accents = self.fetch_accent_data(cx);
24376        if new_accents != self.accent_data {
24377            self.accent_data = new_accents;
24378            self.colorize_brackets(true, cx);
24379        }
24380
24381        self.invalidate_semantic_tokens(None);
24382        self.refresh_semantic_tokens(None, None, cx);
24383    }
24384
24385    pub fn set_searchable(&mut self, searchable: bool) {
24386        self.searchable = searchable;
24387    }
24388
24389    pub fn searchable(&self) -> bool {
24390        self.searchable
24391    }
24392
24393    pub fn open_excerpts_in_split(
24394        &mut self,
24395        _: &OpenExcerptsSplit,
24396        window: &mut Window,
24397        cx: &mut Context<Self>,
24398    ) {
24399        self.open_excerpts_common(None, true, window, cx)
24400    }
24401
24402    pub fn open_excerpts(&mut self, _: &OpenExcerpts, window: &mut Window, cx: &mut Context<Self>) {
24403        self.open_excerpts_common(None, false, window, cx)
24404    }
24405
24406    pub(crate) fn open_excerpts_common(
24407        &mut self,
24408        jump_data: Option<JumpData>,
24409        split: bool,
24410        window: &mut Window,
24411        cx: &mut Context<Self>,
24412    ) {
24413        if self.buffer.read(cx).is_singleton() {
24414            cx.propagate();
24415            return;
24416        }
24417
24418        let mut new_selections_by_buffer = HashMap::default();
24419        match &jump_data {
24420            Some(JumpData::MultiBufferPoint {
24421                excerpt_id,
24422                position,
24423                anchor,
24424                line_offset_from_top,
24425            }) => {
24426                let multi_buffer_snapshot = self.buffer.read(cx).snapshot(cx);
24427                if let Some(buffer) = multi_buffer_snapshot
24428                    .buffer_id_for_excerpt(*excerpt_id)
24429                    .and_then(|buffer_id| self.buffer.read(cx).buffer(buffer_id))
24430                {
24431                    let buffer_snapshot = buffer.read(cx).snapshot();
24432                    let jump_to_point = if buffer_snapshot.can_resolve(anchor) {
24433                        language::ToPoint::to_point(anchor, &buffer_snapshot)
24434                    } else {
24435                        buffer_snapshot.clip_point(*position, Bias::Left)
24436                    };
24437                    let jump_to_offset = buffer_snapshot.point_to_offset(jump_to_point);
24438                    new_selections_by_buffer.insert(
24439                        buffer,
24440                        (
24441                            vec![BufferOffset(jump_to_offset)..BufferOffset(jump_to_offset)],
24442                            Some(*line_offset_from_top),
24443                        ),
24444                    );
24445                }
24446            }
24447            Some(JumpData::MultiBufferRow {
24448                row,
24449                line_offset_from_top,
24450            }) => {
24451                let point = MultiBufferPoint::new(row.0, 0);
24452                if let Some((buffer, buffer_point, _)) =
24453                    self.buffer.read(cx).point_to_buffer_point(point, cx)
24454                {
24455                    let buffer_offset = buffer.read(cx).point_to_offset(buffer_point);
24456                    new_selections_by_buffer
24457                        .entry(buffer)
24458                        .or_insert((Vec::new(), Some(*line_offset_from_top)))
24459                        .0
24460                        .push(BufferOffset(buffer_offset)..BufferOffset(buffer_offset))
24461                }
24462            }
24463            None => {
24464                let selections = self
24465                    .selections
24466                    .all::<MultiBufferOffset>(&self.display_snapshot(cx));
24467                let multi_buffer = self.buffer.read(cx);
24468                for selection in selections {
24469                    for (snapshot, range, _, anchor) in multi_buffer
24470                        .snapshot(cx)
24471                        .range_to_buffer_ranges_with_deleted_hunks(selection.range())
24472                    {
24473                        if let Some(anchor) = anchor {
24474                            let Some(buffer_handle) = multi_buffer.buffer_for_anchor(anchor, cx)
24475                            else {
24476                                continue;
24477                            };
24478                            let offset = text::ToOffset::to_offset(
24479                                &anchor.text_anchor,
24480                                &buffer_handle.read(cx).snapshot(),
24481                            );
24482                            let range = BufferOffset(offset)..BufferOffset(offset);
24483                            new_selections_by_buffer
24484                                .entry(buffer_handle)
24485                                .or_insert((Vec::new(), None))
24486                                .0
24487                                .push(range)
24488                        } else {
24489                            let Some(buffer_handle) = multi_buffer.buffer(snapshot.remote_id())
24490                            else {
24491                                continue;
24492                            };
24493                            new_selections_by_buffer
24494                                .entry(buffer_handle)
24495                                .or_insert((Vec::new(), None))
24496                                .0
24497                                .push(range)
24498                        }
24499                    }
24500                }
24501            }
24502        }
24503
24504        if self.delegate_open_excerpts {
24505            let selections_by_buffer: HashMap<_, _> = new_selections_by_buffer
24506                .into_iter()
24507                .map(|(buffer, value)| (buffer.read(cx).remote_id(), value))
24508                .collect();
24509            if !selections_by_buffer.is_empty() {
24510                cx.emit(EditorEvent::OpenExcerptsRequested {
24511                    selections_by_buffer,
24512                    split,
24513                });
24514            }
24515            return;
24516        }
24517
24518        let Some(workspace) = self.workspace() else {
24519            cx.propagate();
24520            return;
24521        };
24522
24523        new_selections_by_buffer
24524            .retain(|buffer, _| buffer.read(cx).file().is_none_or(|file| file.can_open()));
24525
24526        if new_selections_by_buffer.is_empty() {
24527            return;
24528        }
24529
24530        Self::open_buffers_in_workspace(
24531            workspace.downgrade(),
24532            new_selections_by_buffer,
24533            split,
24534            window,
24535            cx,
24536        );
24537    }
24538
24539    pub(crate) fn open_buffers_in_workspace(
24540        workspace: WeakEntity<Workspace>,
24541        new_selections_by_buffer: HashMap<
24542            Entity<language::Buffer>,
24543            (Vec<Range<BufferOffset>>, Option<u32>),
24544        >,
24545        split: bool,
24546        window: &mut Window,
24547        cx: &mut App,
24548    ) {
24549        // We defer the pane interaction because we ourselves are a workspace item
24550        // and activating a new item causes the pane to call a method on us reentrantly,
24551        // which panics if we're on the stack.
24552        window.defer(cx, move |window, cx| {
24553            workspace
24554                .update(cx, |workspace, cx| {
24555                    let pane = if split {
24556                        workspace.adjacent_pane(window, cx)
24557                    } else {
24558                        workspace.active_pane().clone()
24559                    };
24560
24561                    for (buffer, (ranges, scroll_offset)) in new_selections_by_buffer {
24562                        let buffer_read = buffer.read(cx);
24563                        let (has_file, is_project_file) = if let Some(file) = buffer_read.file() {
24564                            (true, project::File::from_dyn(Some(file)).is_some())
24565                        } else {
24566                            (false, false)
24567                        };
24568
24569                        // If project file is none workspace.open_project_item will fail to open the excerpt
24570                        // in a pre existing workspace item if one exists, because Buffer entity_id will be None
24571                        // so we check if there's a tab match in that case first
24572                        let editor = (!has_file || !is_project_file)
24573                            .then(|| {
24574                                // Handle file-less buffers separately: those are not really the project items, so won't have a project path or entity id,
24575                                // so `workspace.open_project_item` will never find them, always opening a new editor.
24576                                // Instead, we try to activate the existing editor in the pane first.
24577                                let (editor, pane_item_index, pane_item_id) =
24578                                    pane.read(cx).items().enumerate().find_map(|(i, item)| {
24579                                        let editor = item.downcast::<Editor>()?;
24580                                        let singleton_buffer =
24581                                            editor.read(cx).buffer().read(cx).as_singleton()?;
24582                                        if singleton_buffer == buffer {
24583                                            Some((editor, i, item.item_id()))
24584                                        } else {
24585                                            None
24586                                        }
24587                                    })?;
24588                                pane.update(cx, |pane, cx| {
24589                                    pane.activate_item(pane_item_index, true, true, window, cx);
24590                                    if !PreviewTabsSettings::get_global(cx)
24591                                        .enable_preview_from_multibuffer
24592                                    {
24593                                        pane.unpreview_item_if_preview(pane_item_id);
24594                                    }
24595                                });
24596                                Some(editor)
24597                            })
24598                            .flatten()
24599                            .unwrap_or_else(|| {
24600                                let keep_old_preview = PreviewTabsSettings::get_global(cx)
24601                                    .enable_keep_preview_on_code_navigation;
24602                                let allow_new_preview = PreviewTabsSettings::get_global(cx)
24603                                    .enable_preview_from_multibuffer;
24604                                workspace.open_project_item::<Self>(
24605                                    pane.clone(),
24606                                    buffer,
24607                                    true,
24608                                    true,
24609                                    keep_old_preview,
24610                                    allow_new_preview,
24611                                    window,
24612                                    cx,
24613                                )
24614                            });
24615
24616                        editor.update(cx, |editor, cx| {
24617                            if has_file && !is_project_file {
24618                                editor.set_read_only(true);
24619                            }
24620                            let autoscroll = match scroll_offset {
24621                                Some(scroll_offset) => {
24622                                    Autoscroll::top_relative(scroll_offset as usize)
24623                                }
24624                                None => Autoscroll::newest(),
24625                            };
24626                            let nav_history = editor.nav_history.take();
24627                            let multibuffer_snapshot = editor.buffer().read(cx).snapshot(cx);
24628                            let Some((excerpt_id, _, buffer_snapshot)) =
24629                                multibuffer_snapshot.as_singleton()
24630                            else {
24631                                return;
24632                            };
24633                            editor.change_selections(
24634                                SelectionEffects::scroll(autoscroll),
24635                                window,
24636                                cx,
24637                                |s| {
24638                                    s.select_ranges(ranges.into_iter().map(|range| {
24639                                        let range = buffer_snapshot.anchor_before(range.start)
24640                                            ..buffer_snapshot.anchor_after(range.end);
24641                                        multibuffer_snapshot
24642                                            .anchor_range_in_excerpt(excerpt_id, range)
24643                                            .unwrap()
24644                                    }));
24645                                },
24646                            );
24647                            editor.nav_history = nav_history;
24648                        });
24649                    }
24650                })
24651                .ok();
24652        });
24653    }
24654
24655    fn marked_text_ranges(&self, cx: &App) -> Option<Vec<Range<MultiBufferOffsetUtf16>>> {
24656        let snapshot = self.buffer.read(cx).read(cx);
24657        let (_, ranges) = self.text_highlights(HighlightKey::InputComposition, cx)?;
24658        Some(
24659            ranges
24660                .iter()
24661                .map(move |range| {
24662                    range.start.to_offset_utf16(&snapshot)..range.end.to_offset_utf16(&snapshot)
24663                })
24664                .collect(),
24665        )
24666    }
24667
24668    fn selection_replacement_ranges(
24669        &self,
24670        range: Range<MultiBufferOffsetUtf16>,
24671        cx: &mut App,
24672    ) -> Vec<Range<MultiBufferOffsetUtf16>> {
24673        let selections = self
24674            .selections
24675            .all::<MultiBufferOffsetUtf16>(&self.display_snapshot(cx));
24676        let newest_selection = selections
24677            .iter()
24678            .max_by_key(|selection| selection.id)
24679            .unwrap();
24680        let start_delta = range.start.0.0 as isize - newest_selection.start.0.0 as isize;
24681        let end_delta = range.end.0.0 as isize - newest_selection.end.0.0 as isize;
24682        let snapshot = self.buffer.read(cx).read(cx);
24683        selections
24684            .into_iter()
24685            .map(|mut selection| {
24686                selection.start.0.0 =
24687                    (selection.start.0.0 as isize).saturating_add(start_delta) as usize;
24688                selection.end.0.0 = (selection.end.0.0 as isize).saturating_add(end_delta) as usize;
24689                snapshot.clip_offset_utf16(selection.start, Bias::Left)
24690                    ..snapshot.clip_offset_utf16(selection.end, Bias::Right)
24691            })
24692            .collect()
24693    }
24694
24695    fn report_editor_event(
24696        &self,
24697        reported_event: ReportEditorEvent,
24698        file_extension: Option<String>,
24699        cx: &App,
24700    ) {
24701        if cfg!(any(test, feature = "test-support")) {
24702            return;
24703        }
24704
24705        let Some(project) = &self.project else { return };
24706
24707        // If None, we are in a file without an extension
24708        let file = self
24709            .buffer
24710            .read(cx)
24711            .as_singleton()
24712            .and_then(|b| b.read(cx).file());
24713        let file_extension = file_extension.or(file
24714            .as_ref()
24715            .and_then(|file| Path::new(file.file_name(cx)).extension())
24716            .and_then(|e| e.to_str())
24717            .map(|a| a.to_string()));
24718
24719        let vim_mode = vim_mode_setting::VimModeSetting::try_get(cx)
24720            .map(|vim_mode| vim_mode.0)
24721            .unwrap_or(false);
24722
24723        let edit_predictions_provider = all_language_settings(file, cx).edit_predictions.provider;
24724        let copilot_enabled = edit_predictions_provider
24725            == language::language_settings::EditPredictionProvider::Copilot;
24726        let copilot_enabled_for_language = self
24727            .buffer
24728            .read(cx)
24729            .language_settings(cx)
24730            .show_edit_predictions;
24731
24732        let project = project.read(cx);
24733        let event_type = reported_event.event_type();
24734
24735        if let ReportEditorEvent::Saved { auto_saved } = reported_event {
24736            telemetry::event!(
24737                event_type,
24738                type = if auto_saved {"autosave"} else {"manual"},
24739                file_extension,
24740                vim_mode,
24741                copilot_enabled,
24742                copilot_enabled_for_language,
24743                edit_predictions_provider,
24744                is_via_ssh = project.is_via_remote_server(),
24745            );
24746        } else {
24747            telemetry::event!(
24748                event_type,
24749                file_extension,
24750                vim_mode,
24751                copilot_enabled,
24752                copilot_enabled_for_language,
24753                edit_predictions_provider,
24754                is_via_ssh = project.is_via_remote_server(),
24755            );
24756        };
24757    }
24758
24759    /// Copy the highlighted chunks to the clipboard as JSON. The format is an array of lines,
24760    /// with each line being an array of {text, highlight} objects.
24761    fn copy_highlight_json(
24762        &mut self,
24763        _: &CopyHighlightJson,
24764        window: &mut Window,
24765        cx: &mut Context<Self>,
24766    ) {
24767        #[derive(Serialize)]
24768        struct Chunk<'a> {
24769            text: String,
24770            highlight: Option<&'a str>,
24771        }
24772
24773        let snapshot = self.buffer.read(cx).snapshot(cx);
24774        let range = self
24775            .selected_text_range(false, window, cx)
24776            .and_then(|selection| {
24777                if selection.range.is_empty() {
24778                    None
24779                } else {
24780                    Some(
24781                        snapshot.offset_utf16_to_offset(MultiBufferOffsetUtf16(OffsetUtf16(
24782                            selection.range.start,
24783                        )))
24784                            ..snapshot.offset_utf16_to_offset(MultiBufferOffsetUtf16(OffsetUtf16(
24785                                selection.range.end,
24786                            ))),
24787                    )
24788                }
24789            })
24790            .unwrap_or_else(|| MultiBufferOffset(0)..snapshot.len());
24791
24792        let chunks = snapshot.chunks(range, true);
24793        let mut lines = Vec::new();
24794        let mut line: VecDeque<Chunk> = VecDeque::new();
24795
24796        let Some(style) = self.style.as_ref() else {
24797            return;
24798        };
24799
24800        for chunk in chunks {
24801            let highlight = chunk
24802                .syntax_highlight_id
24803                .and_then(|id| id.name(&style.syntax));
24804            let mut chunk_lines = chunk.text.split('\n').peekable();
24805            while let Some(text) = chunk_lines.next() {
24806                let mut merged_with_last_token = false;
24807                if let Some(last_token) = line.back_mut()
24808                    && last_token.highlight == highlight
24809                {
24810                    last_token.text.push_str(text);
24811                    merged_with_last_token = true;
24812                }
24813
24814                if !merged_with_last_token {
24815                    line.push_back(Chunk {
24816                        text: text.into(),
24817                        highlight,
24818                    });
24819                }
24820
24821                if chunk_lines.peek().is_some() {
24822                    if line.len() > 1 && line.front().unwrap().text.is_empty() {
24823                        line.pop_front();
24824                    }
24825                    if line.len() > 1 && line.back().unwrap().text.is_empty() {
24826                        line.pop_back();
24827                    }
24828
24829                    lines.push(mem::take(&mut line));
24830                }
24831            }
24832        }
24833
24834        let Some(lines) = serde_json::to_string_pretty(&lines).log_err() else {
24835            return;
24836        };
24837        cx.write_to_clipboard(ClipboardItem::new_string(lines));
24838    }
24839
24840    pub fn open_context_menu(
24841        &mut self,
24842        _: &OpenContextMenu,
24843        window: &mut Window,
24844        cx: &mut Context<Self>,
24845    ) {
24846        self.request_autoscroll(Autoscroll::newest(), cx);
24847        let position = self
24848            .selections
24849            .newest_display(&self.display_snapshot(cx))
24850            .start;
24851        mouse_context_menu::deploy_context_menu(self, None, position, window, cx);
24852    }
24853
24854    pub fn replay_insert_event(
24855        &mut self,
24856        text: &str,
24857        relative_utf16_range: Option<Range<isize>>,
24858        window: &mut Window,
24859        cx: &mut Context<Self>,
24860    ) {
24861        if !self.input_enabled {
24862            cx.emit(EditorEvent::InputIgnored { text: text.into() });
24863            return;
24864        }
24865        if let Some(relative_utf16_range) = relative_utf16_range {
24866            let selections = self
24867                .selections
24868                .all::<MultiBufferOffsetUtf16>(&self.display_snapshot(cx));
24869            self.change_selections(SelectionEffects::no_scroll(), window, cx, |s| {
24870                let new_ranges = selections.into_iter().map(|range| {
24871                    let start = MultiBufferOffsetUtf16(OffsetUtf16(
24872                        range
24873                            .head()
24874                            .0
24875                            .0
24876                            .saturating_add_signed(relative_utf16_range.start),
24877                    ));
24878                    let end = MultiBufferOffsetUtf16(OffsetUtf16(
24879                        range
24880                            .head()
24881                            .0
24882                            .0
24883                            .saturating_add_signed(relative_utf16_range.end),
24884                    ));
24885                    start..end
24886                });
24887                s.select_ranges(new_ranges);
24888            });
24889        }
24890
24891        self.handle_input(text, window, cx);
24892    }
24893
24894    pub fn is_focused(&self, window: &Window) -> bool {
24895        self.focus_handle.is_focused(window)
24896    }
24897
24898    fn handle_focus(&mut self, window: &mut Window, cx: &mut Context<Self>) {
24899        cx.emit(EditorEvent::Focused);
24900
24901        if let Some(descendant) = self
24902            .last_focused_descendant
24903            .take()
24904            .and_then(|descendant| descendant.upgrade())
24905        {
24906            window.focus(&descendant, cx);
24907        } else {
24908            if let Some(blame) = self.blame.as_ref() {
24909                blame.update(cx, GitBlame::focus)
24910            }
24911
24912            self.blink_manager.update(cx, BlinkManager::enable);
24913            self.show_cursor_names(window, cx);
24914            self.buffer.update(cx, |buffer, cx| {
24915                buffer.finalize_last_transaction(cx);
24916                if self.leader_id.is_none() {
24917                    buffer.set_active_selections(
24918                        &self.selections.disjoint_anchors_arc(),
24919                        self.selections.line_mode(),
24920                        self.cursor_shape,
24921                        cx,
24922                    );
24923                }
24924            });
24925
24926            if let Some(position_map) = self.last_position_map.clone() {
24927                EditorElement::mouse_moved(
24928                    self,
24929                    &MouseMoveEvent {
24930                        position: window.mouse_position(),
24931                        pressed_button: None,
24932                        modifiers: window.modifiers(),
24933                    },
24934                    &position_map,
24935                    None,
24936                    window,
24937                    cx,
24938                );
24939            }
24940        }
24941    }
24942
24943    fn handle_focus_in(&mut self, _: &mut Window, cx: &mut Context<Self>) {
24944        cx.emit(EditorEvent::FocusedIn)
24945    }
24946
24947    fn handle_focus_out(
24948        &mut self,
24949        event: FocusOutEvent,
24950        _window: &mut Window,
24951        cx: &mut Context<Self>,
24952    ) {
24953        if event.blurred != self.focus_handle {
24954            self.last_focused_descendant = Some(event.blurred);
24955        }
24956        self.selection_drag_state = SelectionDragState::None;
24957        self.refresh_inlay_hints(InlayHintRefreshReason::ModifiersChanged(false), cx);
24958    }
24959
24960    pub fn handle_blur(&mut self, window: &mut Window, cx: &mut Context<Self>) {
24961        self.blink_manager.update(cx, BlinkManager::disable);
24962        self.buffer
24963            .update(cx, |buffer, cx| buffer.remove_active_selections(cx));
24964
24965        if let Some(blame) = self.blame.as_ref() {
24966            blame.update(cx, GitBlame::blur)
24967        }
24968        if !self.hover_state.focused(window, cx) {
24969            hide_hover(self, cx);
24970        }
24971        if !self
24972            .context_menu
24973            .borrow()
24974            .as_ref()
24975            .is_some_and(|context_menu| context_menu.focused(window, cx))
24976        {
24977            self.hide_context_menu(window, cx);
24978        }
24979        self.take_active_edit_prediction(cx);
24980        cx.emit(EditorEvent::Blurred);
24981        cx.notify();
24982    }
24983
24984    pub fn observe_pending_input(&mut self, window: &mut Window, cx: &mut Context<Self>) {
24985        let mut pending: String = window
24986            .pending_input_keystrokes()
24987            .into_iter()
24988            .flatten()
24989            .filter_map(|keystroke| keystroke.key_char.clone())
24990            .collect();
24991
24992        if !self.input_enabled || self.read_only || !self.focus_handle.is_focused(window) {
24993            pending = "".to_string();
24994        }
24995
24996        let existing_pending = self
24997            .text_highlights(HighlightKey::PendingInput, cx)
24998            .map(|(_, ranges)| ranges.to_vec());
24999        if existing_pending.is_none() && pending.is_empty() {
25000            return;
25001        }
25002        let transaction =
25003            self.transact(window, cx, |this, window, cx| {
25004                let selections = this
25005                    .selections
25006                    .all::<MultiBufferOffset>(&this.display_snapshot(cx));
25007                let edits = selections
25008                    .iter()
25009                    .map(|selection| (selection.end..selection.end, pending.clone()));
25010                this.edit(edits, cx);
25011                this.change_selections(SelectionEffects::no_scroll(), window, cx, |s| {
25012                    s.select_ranges(selections.into_iter().enumerate().map(|(ix, sel)| {
25013                        sel.start + ix * pending.len()..sel.end + ix * pending.len()
25014                    }));
25015                });
25016                if let Some(existing_ranges) = existing_pending {
25017                    let edits = existing_ranges.iter().map(|range| (range.clone(), ""));
25018                    this.edit(edits, cx);
25019                }
25020            });
25021
25022        let snapshot = self.snapshot(window, cx);
25023        let ranges = self
25024            .selections
25025            .all::<MultiBufferOffset>(&snapshot.display_snapshot)
25026            .into_iter()
25027            .map(|selection| {
25028                snapshot.buffer_snapshot().anchor_after(selection.end)
25029                    ..snapshot
25030                        .buffer_snapshot()
25031                        .anchor_before(selection.end + pending.len())
25032            })
25033            .collect();
25034
25035        if pending.is_empty() {
25036            self.clear_highlights(HighlightKey::PendingInput, cx);
25037        } else {
25038            self.highlight_text(
25039                HighlightKey::PendingInput,
25040                ranges,
25041                HighlightStyle {
25042                    underline: Some(UnderlineStyle {
25043                        thickness: px(1.),
25044                        color: None,
25045                        wavy: false,
25046                    }),
25047                    ..Default::default()
25048                },
25049                cx,
25050            );
25051        }
25052
25053        self.ime_transaction = self.ime_transaction.or(transaction);
25054        if let Some(transaction) = self.ime_transaction {
25055            self.buffer.update(cx, |buffer, cx| {
25056                buffer.group_until_transaction(transaction, cx);
25057            });
25058        }
25059
25060        if self
25061            .text_highlights(HighlightKey::PendingInput, cx)
25062            .is_none()
25063        {
25064            self.ime_transaction.take();
25065        }
25066    }
25067
25068    pub fn register_action_renderer(
25069        &mut self,
25070        listener: impl Fn(&Editor, &mut Window, &mut Context<Editor>) + 'static,
25071    ) -> Subscription {
25072        let id = self.next_editor_action_id.post_inc();
25073        self.editor_actions
25074            .borrow_mut()
25075            .insert(id, Box::new(listener));
25076
25077        let editor_actions = self.editor_actions.clone();
25078        Subscription::new(move || {
25079            editor_actions.borrow_mut().remove(&id);
25080        })
25081    }
25082
25083    pub fn register_action<A: Action>(
25084        &mut self,
25085        listener: impl Fn(&A, &mut Window, &mut App) + 'static,
25086    ) -> Subscription {
25087        let id = self.next_editor_action_id.post_inc();
25088        let listener = Arc::new(listener);
25089        self.editor_actions.borrow_mut().insert(
25090            id,
25091            Box::new(move |_, window, _| {
25092                let listener = listener.clone();
25093                window.on_action(TypeId::of::<A>(), move |action, phase, window, cx| {
25094                    let action = action.downcast_ref().unwrap();
25095                    if phase == DispatchPhase::Bubble {
25096                        listener(action, window, cx)
25097                    }
25098                })
25099            }),
25100        );
25101
25102        let editor_actions = self.editor_actions.clone();
25103        Subscription::new(move || {
25104            editor_actions.borrow_mut().remove(&id);
25105        })
25106    }
25107
25108    pub fn file_header_size(&self) -> u32 {
25109        FILE_HEADER_HEIGHT
25110    }
25111
25112    pub fn restore(
25113        &mut self,
25114        revert_changes: HashMap<BufferId, Vec<(Range<text::Anchor>, Rope)>>,
25115        window: &mut Window,
25116        cx: &mut Context<Self>,
25117    ) {
25118        self.buffer().update(cx, |multi_buffer, cx| {
25119            for (buffer_id, changes) in revert_changes {
25120                if let Some(buffer) = multi_buffer.buffer(buffer_id) {
25121                    buffer.update(cx, |buffer, cx| {
25122                        buffer.edit(
25123                            changes
25124                                .into_iter()
25125                                .map(|(range, text)| (range, text.to_string())),
25126                            None,
25127                            cx,
25128                        );
25129                    });
25130                }
25131            }
25132        });
25133        self.change_selections(SelectionEffects::no_scroll(), window, cx, |selections| {
25134            selections.refresh()
25135        });
25136    }
25137
25138    pub fn to_pixel_point(
25139        &mut self,
25140        source: Anchor,
25141        editor_snapshot: &EditorSnapshot,
25142        window: &mut Window,
25143        cx: &mut App,
25144    ) -> Option<gpui::Point<Pixels>> {
25145        let source_point = source.to_display_point(editor_snapshot);
25146        self.display_to_pixel_point(source_point, editor_snapshot, window, cx)
25147    }
25148
25149    pub fn display_to_pixel_point(
25150        &mut self,
25151        source: DisplayPoint,
25152        editor_snapshot: &EditorSnapshot,
25153        window: &mut Window,
25154        cx: &mut App,
25155    ) -> Option<gpui::Point<Pixels>> {
25156        let line_height = self.style(cx).text.line_height_in_pixels(window.rem_size());
25157        let text_layout_details = self.text_layout_details(window, cx);
25158        let scroll_top = text_layout_details
25159            .scroll_anchor
25160            .scroll_position(editor_snapshot)
25161            .y;
25162
25163        if source.row().as_f64() < scroll_top.floor() {
25164            return None;
25165        }
25166        let source_x = editor_snapshot.x_for_display_point(source, &text_layout_details);
25167        let source_y = line_height * (source.row().as_f64() - scroll_top) as f32;
25168        Some(gpui::Point::new(source_x, source_y))
25169    }
25170
25171    pub fn has_visible_completions_menu(&self) -> bool {
25172        !self.edit_prediction_preview_is_active()
25173            && self.context_menu.borrow().as_ref().is_some_and(|menu| {
25174                menu.visible() && matches!(menu, CodeContextMenu::Completions(_))
25175            })
25176    }
25177
25178    pub fn register_addon<T: Addon>(&mut self, instance: T) {
25179        if self.mode.is_minimap() {
25180            return;
25181        }
25182        self.addons
25183            .insert(std::any::TypeId::of::<T>(), Box::new(instance));
25184    }
25185
25186    pub fn unregister_addon<T: Addon>(&mut self) {
25187        self.addons.remove(&std::any::TypeId::of::<T>());
25188    }
25189
25190    pub fn addon<T: Addon>(&self) -> Option<&T> {
25191        let type_id = std::any::TypeId::of::<T>();
25192        self.addons
25193            .get(&type_id)
25194            .and_then(|item| item.to_any().downcast_ref::<T>())
25195    }
25196
25197    pub fn addon_mut<T: Addon>(&mut self) -> Option<&mut T> {
25198        let type_id = std::any::TypeId::of::<T>();
25199        self.addons
25200            .get_mut(&type_id)
25201            .and_then(|item| item.to_any_mut()?.downcast_mut::<T>())
25202    }
25203
25204    fn character_dimensions(&self, window: &mut Window, cx: &mut App) -> CharacterDimensions {
25205        let text_layout_details = self.text_layout_details(window, cx);
25206        let style = &text_layout_details.editor_style;
25207        let font_id = window.text_system().resolve_font(&style.text.font());
25208        let font_size = style.text.font_size.to_pixels(window.rem_size());
25209        let line_height = style.text.line_height_in_pixels(window.rem_size());
25210        let em_width = window.text_system().em_width(font_id, font_size).unwrap();
25211        let em_advance = window.text_system().em_advance(font_id, font_size).unwrap();
25212
25213        CharacterDimensions {
25214            em_width,
25215            em_advance,
25216            line_height,
25217        }
25218    }
25219
25220    pub fn wait_for_diff_to_load(&self) -> Option<Shared<Task<()>>> {
25221        self.load_diff_task.clone()
25222    }
25223
25224    fn read_metadata_from_db(
25225        &mut self,
25226        item_id: u64,
25227        workspace_id: WorkspaceId,
25228        window: &mut Window,
25229        cx: &mut Context<Editor>,
25230    ) {
25231        if self.buffer_kind(cx) == ItemBufferKind::Singleton
25232            && !self.mode.is_minimap()
25233            && WorkspaceSettings::get(None, cx).restore_on_startup
25234                != RestoreOnStartupBehavior::EmptyTab
25235        {
25236            let buffer_snapshot = OnceCell::new();
25237
25238            if let Some(folds) = DB.get_editor_folds(item_id, workspace_id).log_err()
25239                && !folds.is_empty()
25240            {
25241                let snapshot = buffer_snapshot.get_or_init(|| self.buffer.read(cx).snapshot(cx));
25242                let snapshot_len = snapshot.len().0;
25243
25244                // Helper: search for fingerprint in buffer, return offset if found
25245                let find_fingerprint = |fingerprint: &str, search_start: usize| -> Option<usize> {
25246                    // Ensure we start at a character boundary (defensive)
25247                    let search_start = snapshot
25248                        .clip_offset(MultiBufferOffset(search_start), Bias::Left)
25249                        .0;
25250                    let search_end = snapshot_len.saturating_sub(fingerprint.len());
25251
25252                    let mut byte_offset = search_start;
25253                    for ch in snapshot.chars_at(MultiBufferOffset(search_start)) {
25254                        if byte_offset > search_end {
25255                            break;
25256                        }
25257                        if snapshot.contains_str_at(MultiBufferOffset(byte_offset), fingerprint) {
25258                            return Some(byte_offset);
25259                        }
25260                        byte_offset += ch.len_utf8();
25261                    }
25262                    None
25263                };
25264
25265                // Track search position to handle duplicate fingerprints correctly.
25266                // Folds are stored in document order, so we advance after each match.
25267                let mut search_start = 0usize;
25268
25269                let valid_folds: Vec<_> = folds
25270                    .into_iter()
25271                    .filter_map(|(stored_start, stored_end, start_fp, end_fp)| {
25272                        // Skip folds without fingerprints (old data before migration)
25273                        let sfp = start_fp?;
25274                        let efp = end_fp?;
25275                        let efp_len = efp.len();
25276
25277                        // Fast path: check if fingerprints match at stored offsets
25278                        // Note: end_fp is content BEFORE fold end, so check at (stored_end - efp_len)
25279                        let start_matches = stored_start < snapshot_len
25280                            && snapshot.contains_str_at(MultiBufferOffset(stored_start), &sfp);
25281                        let efp_check_pos = stored_end.saturating_sub(efp_len);
25282                        let end_matches = efp_check_pos >= stored_start
25283                            && stored_end <= snapshot_len
25284                            && snapshot.contains_str_at(MultiBufferOffset(efp_check_pos), &efp);
25285
25286                        let (new_start, new_end) = if start_matches && end_matches {
25287                            // Offsets unchanged, use stored values
25288                            (stored_start, stored_end)
25289                        } else if sfp == efp {
25290                            // Short fold: identical fingerprints can only match once per search
25291                            // Use stored fold length to compute new_end
25292                            let new_start = find_fingerprint(&sfp, search_start)?;
25293                            let fold_len = stored_end - stored_start;
25294                            let new_end = new_start + fold_len;
25295                            (new_start, new_end)
25296                        } else {
25297                            // Slow path: search for fingerprints in buffer
25298                            let new_start = find_fingerprint(&sfp, search_start)?;
25299                            // Search for end_fp after start, then add efp_len to get actual fold end
25300                            let efp_pos = find_fingerprint(&efp, new_start + sfp.len())?;
25301                            let new_end = efp_pos + efp_len;
25302                            (new_start, new_end)
25303                        };
25304
25305                        // Advance search position for next fold
25306                        search_start = new_end;
25307
25308                        // Validate fold makes sense (end must be after start)
25309                        if new_end <= new_start {
25310                            return None;
25311                        }
25312
25313                        Some(
25314                            snapshot.clip_offset(MultiBufferOffset(new_start), Bias::Left)
25315                                ..snapshot.clip_offset(MultiBufferOffset(new_end), Bias::Right),
25316                        )
25317                    })
25318                    .collect();
25319
25320                if !valid_folds.is_empty() {
25321                    self.fold_ranges(valid_folds, false, window, cx);
25322
25323                    // Migrate folds to current entity_id before workspace cleanup runs.
25324                    // Entity IDs change between sessions, but workspace cleanup deletes
25325                    // old editor rows (cascading to folds) based on current entity IDs.
25326                    let new_editor_id = cx.entity().entity_id().as_u64() as ItemId;
25327                    if new_editor_id != item_id {
25328                        cx.spawn(async move |_, _| {
25329                            DB.migrate_editor_folds(item_id, new_editor_id, workspace_id)
25330                                .await
25331                                .log_err();
25332                        })
25333                        .detach();
25334                    }
25335                }
25336            }
25337
25338            if let Some(selections) = DB.get_editor_selections(item_id, workspace_id).log_err()
25339                && !selections.is_empty()
25340            {
25341                let snapshot = buffer_snapshot.get_or_init(|| self.buffer.read(cx).snapshot(cx));
25342                // skip adding the initial selection to selection history
25343                self.selection_history.mode = SelectionHistoryMode::Skipping;
25344                self.change_selections(SelectionEffects::no_scroll(), window, cx, |s| {
25345                    s.select_ranges(selections.into_iter().map(|(start, end)| {
25346                        snapshot.clip_offset(MultiBufferOffset(start), Bias::Left)
25347                            ..snapshot.clip_offset(MultiBufferOffset(end), Bias::Right)
25348                    }));
25349                });
25350                self.selection_history.mode = SelectionHistoryMode::Normal;
25351            };
25352        }
25353
25354        self.read_scroll_position_from_db(item_id, workspace_id, window, cx);
25355    }
25356
25357    fn update_lsp_data(
25358        &mut self,
25359        for_buffer: Option<BufferId>,
25360        window: &mut Window,
25361        cx: &mut Context<'_, Self>,
25362    ) {
25363        if !self.enable_lsp_data {
25364            return;
25365        }
25366
25367        if let Some(buffer_id) = for_buffer {
25368            self.pull_diagnostics(buffer_id, window, cx);
25369        }
25370        self.refresh_semantic_tokens(for_buffer, None, cx);
25371        self.refresh_document_colors(for_buffer, window, cx);
25372        self.refresh_folding_ranges(for_buffer, window, cx);
25373        self.refresh_document_symbols(for_buffer, cx);
25374    }
25375
25376    fn register_visible_buffers(&mut self, cx: &mut Context<Self>) {
25377        if !self.mode().is_full() {
25378            return;
25379        }
25380        for (_, (visible_buffer, _, _)) in self.visible_excerpts(true, cx) {
25381            self.register_buffer(visible_buffer.read(cx).remote_id(), cx);
25382        }
25383    }
25384
25385    fn register_buffer(&mut self, buffer_id: BufferId, cx: &mut Context<Self>) {
25386        if !self.mode().is_full() {
25387            return;
25388        }
25389
25390        if !self.registered_buffers.contains_key(&buffer_id)
25391            && let Some(project) = self.project.as_ref()
25392        {
25393            if let Some(buffer) = self.buffer.read(cx).buffer(buffer_id) {
25394                project.update(cx, |project, cx| {
25395                    self.registered_buffers.insert(
25396                        buffer_id,
25397                        project.register_buffer_with_language_servers(&buffer, cx),
25398                    );
25399                });
25400            } else {
25401                self.registered_buffers.remove(&buffer_id);
25402            }
25403        }
25404    }
25405
25406    fn create_style(&self, cx: &App) -> EditorStyle {
25407        let settings = ThemeSettings::get_global(cx);
25408
25409        let mut text_style = match self.mode {
25410            EditorMode::SingleLine | EditorMode::AutoHeight { .. } => TextStyle {
25411                color: cx.theme().colors().editor_foreground,
25412                font_family: settings.ui_font.family.clone(),
25413                font_features: settings.ui_font.features.clone(),
25414                font_fallbacks: settings.ui_font.fallbacks.clone(),
25415                font_size: rems(0.875).into(),
25416                font_weight: settings.ui_font.weight,
25417                line_height: relative(settings.buffer_line_height.value()),
25418                ..Default::default()
25419            },
25420            EditorMode::Full { .. } | EditorMode::Minimap { .. } => TextStyle {
25421                color: cx.theme().colors().editor_foreground,
25422                font_family: settings.buffer_font.family.clone(),
25423                font_features: settings.buffer_font.features.clone(),
25424                font_fallbacks: settings.buffer_font.fallbacks.clone(),
25425                font_size: settings.buffer_font_size(cx).into(),
25426                font_weight: settings.buffer_font.weight,
25427                line_height: relative(settings.buffer_line_height.value()),
25428                ..Default::default()
25429            },
25430        };
25431        if let Some(text_style_refinement) = &self.text_style_refinement {
25432            text_style.refine(text_style_refinement)
25433        }
25434
25435        let background = match self.mode {
25436            EditorMode::SingleLine => cx.theme().system().transparent,
25437            EditorMode::AutoHeight { .. } => cx.theme().system().transparent,
25438            EditorMode::Full { .. } => cx.theme().colors().editor_background,
25439            EditorMode::Minimap { .. } => cx.theme().colors().editor_background.opacity(0.7),
25440        };
25441
25442        EditorStyle {
25443            background,
25444            border: cx.theme().colors().border,
25445            local_player: cx.theme().players().local(),
25446            text: text_style,
25447            scrollbar_width: EditorElement::SCROLLBAR_WIDTH,
25448            syntax: cx.theme().syntax().clone(),
25449            status: cx.theme().status().clone(),
25450            inlay_hints_style: make_inlay_hints_style(cx),
25451            edit_prediction_styles: make_suggestion_styles(cx),
25452            unnecessary_code_fade: settings.unnecessary_code_fade,
25453            show_underlines: self.diagnostics_enabled(),
25454        }
25455    }
25456
25457    fn breadcrumbs_inner(&self, cx: &App) -> Option<Vec<BreadcrumbText>> {
25458        let multibuffer = self.buffer().read(cx);
25459        let is_singleton = multibuffer.is_singleton();
25460        let (buffer_id, symbols) = self.outline_symbols_at_cursor.as_ref()?;
25461        let buffer = multibuffer.buffer(*buffer_id)?;
25462
25463        let buffer = buffer.read(cx);
25464        let settings = ThemeSettings::get_global(cx);
25465        // In a multi-buffer layout, we don't want to include the filename in the breadcrumbs
25466        let mut breadcrumbs = if is_singleton {
25467            let text = self.breadcrumb_header.clone().unwrap_or_else(|| {
25468                buffer
25469                    .snapshot()
25470                    .resolve_file_path(
25471                        self.project
25472                            .as_ref()
25473                            .map(|project| project.read(cx).visible_worktrees(cx).count() > 1)
25474                            .unwrap_or_default(),
25475                        cx,
25476                    )
25477                    .unwrap_or_else(|| {
25478                        if multibuffer.is_singleton() {
25479                            multibuffer.title(cx).to_string()
25480                        } else {
25481                            "untitled".to_string()
25482                        }
25483                    })
25484            });
25485            vec![BreadcrumbText {
25486                text,
25487                highlights: None,
25488                font: Some(settings.buffer_font.clone()),
25489            }]
25490        } else {
25491            vec![]
25492        };
25493
25494        breadcrumbs.extend(symbols.iter().map(|symbol| BreadcrumbText {
25495            text: symbol.text.clone(),
25496            highlights: Some(symbol.highlight_ranges.clone()),
25497            font: Some(settings.buffer_font.clone()),
25498        }));
25499        Some(breadcrumbs)
25500    }
25501
25502    fn disable_lsp_data(&mut self) {
25503        self.enable_lsp_data = false;
25504    }
25505
25506    fn disable_runnables(&mut self) {
25507        self.enable_runnables = false;
25508    }
25509}
25510
25511fn edit_for_markdown_paste<'a>(
25512    buffer: &MultiBufferSnapshot,
25513    range: Range<MultiBufferOffset>,
25514    to_insert: &'a str,
25515    url: Option<url::Url>,
25516) -> (Range<MultiBufferOffset>, Cow<'a, str>) {
25517    if url.is_none() {
25518        return (range, Cow::Borrowed(to_insert));
25519    };
25520
25521    let old_text = buffer.text_for_range(range.clone()).collect::<String>();
25522
25523    let new_text = if range.is_empty() || url::Url::parse(&old_text).is_ok() {
25524        Cow::Borrowed(to_insert)
25525    } else {
25526        Cow::Owned(format!("[{old_text}]({to_insert})"))
25527    };
25528    (range, new_text)
25529}
25530
25531fn process_completion_for_edit(
25532    completion: &Completion,
25533    intent: CompletionIntent,
25534    buffer: &Entity<Buffer>,
25535    cursor_position: &text::Anchor,
25536    cx: &mut Context<Editor>,
25537) -> CompletionEdit {
25538    let buffer = buffer.read(cx);
25539    let buffer_snapshot = buffer.snapshot();
25540    let (snippet, new_text) = if completion.is_snippet() {
25541        let mut snippet_source = completion.new_text.clone();
25542        // Workaround for typescript language server issues so that methods don't expand within
25543        // strings and functions with type expressions. The previous point is used because the query
25544        // for function identifier doesn't match when the cursor is immediately after. See PR #30312
25545        let previous_point = text::ToPoint::to_point(cursor_position, &buffer_snapshot);
25546        let previous_point = if previous_point.column > 0 {
25547            cursor_position.to_previous_offset(&buffer_snapshot)
25548        } else {
25549            cursor_position.to_offset(&buffer_snapshot)
25550        };
25551        if let Some(scope) = buffer_snapshot.language_scope_at(previous_point)
25552            && scope.prefers_label_for_snippet_in_completion()
25553            && let Some(label) = completion.label()
25554            && matches!(
25555                completion.kind(),
25556                Some(CompletionItemKind::FUNCTION) | Some(CompletionItemKind::METHOD)
25557            )
25558        {
25559            snippet_source = label;
25560        }
25561        match Snippet::parse(&snippet_source).log_err() {
25562            Some(parsed_snippet) => (Some(parsed_snippet.clone()), parsed_snippet.text),
25563            None => (None, completion.new_text.clone()),
25564        }
25565    } else {
25566        (None, completion.new_text.clone())
25567    };
25568
25569    let mut range_to_replace = {
25570        let replace_range = &completion.replace_range;
25571        if let CompletionSource::Lsp {
25572            insert_range: Some(insert_range),
25573            ..
25574        } = &completion.source
25575        {
25576            debug_assert_eq!(
25577                insert_range.start, replace_range.start,
25578                "insert_range and replace_range should start at the same position"
25579            );
25580            debug_assert!(
25581                insert_range
25582                    .start
25583                    .cmp(cursor_position, &buffer_snapshot)
25584                    .is_le(),
25585                "insert_range should start before or at cursor position"
25586            );
25587            debug_assert!(
25588                replace_range
25589                    .start
25590                    .cmp(cursor_position, &buffer_snapshot)
25591                    .is_le(),
25592                "replace_range should start before or at cursor position"
25593            );
25594
25595            let should_replace = match intent {
25596                CompletionIntent::CompleteWithInsert => false,
25597                CompletionIntent::CompleteWithReplace => true,
25598                CompletionIntent::Complete | CompletionIntent::Compose => {
25599                    let insert_mode =
25600                        language_settings(buffer.language().map(|l| l.name()), buffer.file(), cx)
25601                            .completions
25602                            .lsp_insert_mode;
25603                    match insert_mode {
25604                        LspInsertMode::Insert => false,
25605                        LspInsertMode::Replace => true,
25606                        LspInsertMode::ReplaceSubsequence => {
25607                            let mut text_to_replace = buffer.chars_for_range(
25608                                buffer.anchor_before(replace_range.start)
25609                                    ..buffer.anchor_after(replace_range.end),
25610                            );
25611                            let mut current_needle = text_to_replace.next();
25612                            for haystack_ch in completion.label.text.chars() {
25613                                if let Some(needle_ch) = current_needle
25614                                    && haystack_ch.eq_ignore_ascii_case(&needle_ch)
25615                                {
25616                                    current_needle = text_to_replace.next();
25617                                }
25618                            }
25619                            current_needle.is_none()
25620                        }
25621                        LspInsertMode::ReplaceSuffix => {
25622                            if replace_range
25623                                .end
25624                                .cmp(cursor_position, &buffer_snapshot)
25625                                .is_gt()
25626                            {
25627                                let range_after_cursor = *cursor_position..replace_range.end;
25628                                let text_after_cursor = buffer
25629                                    .text_for_range(
25630                                        buffer.anchor_before(range_after_cursor.start)
25631                                            ..buffer.anchor_after(range_after_cursor.end),
25632                                    )
25633                                    .collect::<String>()
25634                                    .to_ascii_lowercase();
25635                                completion
25636                                    .label
25637                                    .text
25638                                    .to_ascii_lowercase()
25639                                    .ends_with(&text_after_cursor)
25640                            } else {
25641                                true
25642                            }
25643                        }
25644                    }
25645                }
25646            };
25647
25648            if should_replace {
25649                replace_range.clone()
25650            } else {
25651                insert_range.clone()
25652            }
25653        } else {
25654            replace_range.clone()
25655        }
25656    };
25657
25658    if range_to_replace
25659        .end
25660        .cmp(cursor_position, &buffer_snapshot)
25661        .is_lt()
25662    {
25663        range_to_replace.end = *cursor_position;
25664    }
25665
25666    let replace_range = range_to_replace.to_offset(buffer);
25667    CompletionEdit {
25668        new_text,
25669        replace_range: BufferOffset(replace_range.start)..BufferOffset(replace_range.end),
25670        snippet,
25671    }
25672}
25673
25674struct CompletionEdit {
25675    new_text: String,
25676    replace_range: Range<BufferOffset>,
25677    snippet: Option<Snippet>,
25678}
25679
25680fn comment_delimiter_for_newline(
25681    start_point: &Point,
25682    buffer: &MultiBufferSnapshot,
25683    language: &LanguageScope,
25684) -> Option<Arc<str>> {
25685    let delimiters = language.line_comment_prefixes();
25686    let max_len_of_delimiter = delimiters.iter().map(|delimiter| delimiter.len()).max()?;
25687    let (snapshot, range) = buffer.buffer_line_for_row(MultiBufferRow(start_point.row))?;
25688
25689    let num_of_whitespaces = snapshot
25690        .chars_for_range(range.clone())
25691        .take_while(|c| c.is_whitespace())
25692        .count();
25693    let comment_candidate = snapshot
25694        .chars_for_range(range.clone())
25695        .skip(num_of_whitespaces)
25696        .take(max_len_of_delimiter)
25697        .collect::<String>();
25698    let (delimiter, trimmed_len) = delimiters
25699        .iter()
25700        .filter_map(|delimiter| {
25701            let prefix = delimiter.trim_end();
25702            if comment_candidate.starts_with(prefix) {
25703                Some((delimiter, prefix.len()))
25704            } else {
25705                None
25706            }
25707        })
25708        .max_by_key(|(_, len)| *len)?;
25709
25710    if let Some(BlockCommentConfig {
25711        start: block_start, ..
25712    }) = language.block_comment()
25713    {
25714        let block_start_trimmed = block_start.trim_end();
25715        if block_start_trimmed.starts_with(delimiter.trim_end()) {
25716            let line_content = snapshot
25717                .chars_for_range(range)
25718                .skip(num_of_whitespaces)
25719                .take(block_start_trimmed.len())
25720                .collect::<String>();
25721
25722            if line_content.starts_with(block_start_trimmed) {
25723                return None;
25724            }
25725        }
25726    }
25727
25728    let cursor_is_placed_after_comment_marker =
25729        num_of_whitespaces + trimmed_len <= start_point.column as usize;
25730    if cursor_is_placed_after_comment_marker {
25731        Some(delimiter.clone())
25732    } else {
25733        None
25734    }
25735}
25736
25737fn documentation_delimiter_for_newline(
25738    start_point: &Point,
25739    buffer: &MultiBufferSnapshot,
25740    language: &LanguageScope,
25741    newline_config: &mut NewlineConfig,
25742) -> Option<Arc<str>> {
25743    let BlockCommentConfig {
25744        start: start_tag,
25745        end: end_tag,
25746        prefix: delimiter,
25747        tab_size: len,
25748    } = language.documentation_comment()?;
25749    let is_within_block_comment = buffer
25750        .language_scope_at(*start_point)
25751        .is_some_and(|scope| scope.override_name() == Some("comment"));
25752    if !is_within_block_comment {
25753        return None;
25754    }
25755
25756    let (snapshot, range) = buffer.buffer_line_for_row(MultiBufferRow(start_point.row))?;
25757
25758    let num_of_whitespaces = snapshot
25759        .chars_for_range(range.clone())
25760        .take_while(|c| c.is_whitespace())
25761        .count();
25762
25763    // 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.
25764    let column = start_point.column;
25765    let cursor_is_after_start_tag = {
25766        let start_tag_len = start_tag.len();
25767        let start_tag_line = snapshot
25768            .chars_for_range(range.clone())
25769            .skip(num_of_whitespaces)
25770            .take(start_tag_len)
25771            .collect::<String>();
25772        if start_tag_line.starts_with(start_tag.as_ref()) {
25773            num_of_whitespaces + start_tag_len <= column as usize
25774        } else {
25775            false
25776        }
25777    };
25778
25779    let cursor_is_after_delimiter = {
25780        let delimiter_trim = delimiter.trim_end();
25781        let delimiter_line = snapshot
25782            .chars_for_range(range.clone())
25783            .skip(num_of_whitespaces)
25784            .take(delimiter_trim.len())
25785            .collect::<String>();
25786        if delimiter_line.starts_with(delimiter_trim) {
25787            num_of_whitespaces + delimiter_trim.len() <= column as usize
25788        } else {
25789            false
25790        }
25791    };
25792
25793    let mut needs_extra_line = false;
25794    let mut extra_line_additional_indent = IndentSize::spaces(0);
25795
25796    let cursor_is_before_end_tag_if_exists = {
25797        let mut char_position = 0u32;
25798        let mut end_tag_offset = None;
25799
25800        'outer: for chunk in snapshot.text_for_range(range) {
25801            if let Some(byte_pos) = chunk.find(&**end_tag) {
25802                let chars_before_match = chunk[..byte_pos].chars().count() as u32;
25803                end_tag_offset = Some(char_position + chars_before_match);
25804                break 'outer;
25805            }
25806            char_position += chunk.chars().count() as u32;
25807        }
25808
25809        if let Some(end_tag_offset) = end_tag_offset {
25810            let cursor_is_before_end_tag = column <= end_tag_offset;
25811            if cursor_is_after_start_tag {
25812                if cursor_is_before_end_tag {
25813                    needs_extra_line = true;
25814                }
25815                let cursor_is_at_start_of_end_tag = column == end_tag_offset;
25816                if cursor_is_at_start_of_end_tag {
25817                    extra_line_additional_indent.len = *len;
25818                }
25819            }
25820            cursor_is_before_end_tag
25821        } else {
25822            true
25823        }
25824    };
25825
25826    if (cursor_is_after_start_tag || cursor_is_after_delimiter)
25827        && cursor_is_before_end_tag_if_exists
25828    {
25829        let additional_indent = if cursor_is_after_start_tag {
25830            IndentSize::spaces(*len)
25831        } else {
25832            IndentSize::spaces(0)
25833        };
25834
25835        *newline_config = NewlineConfig::Newline {
25836            additional_indent,
25837            extra_line_additional_indent: if needs_extra_line {
25838                Some(extra_line_additional_indent)
25839            } else {
25840                None
25841            },
25842            prevent_auto_indent: true,
25843        };
25844        Some(delimiter.clone())
25845    } else {
25846        None
25847    }
25848}
25849
25850const ORDERED_LIST_MAX_MARKER_LEN: usize = 16;
25851
25852fn list_delimiter_for_newline(
25853    start_point: &Point,
25854    buffer: &MultiBufferSnapshot,
25855    language: &LanguageScope,
25856    newline_config: &mut NewlineConfig,
25857) -> Option<Arc<str>> {
25858    let (snapshot, range) = buffer.buffer_line_for_row(MultiBufferRow(start_point.row))?;
25859
25860    let num_of_whitespaces = snapshot
25861        .chars_for_range(range.clone())
25862        .take_while(|c| c.is_whitespace())
25863        .count();
25864
25865    let task_list_entries: Vec<_> = language
25866        .task_list()
25867        .into_iter()
25868        .flat_map(|config| {
25869            config
25870                .prefixes
25871                .iter()
25872                .map(|prefix| (prefix.as_ref(), config.continuation.as_ref()))
25873        })
25874        .collect();
25875    let unordered_list_entries: Vec<_> = language
25876        .unordered_list()
25877        .iter()
25878        .map(|marker| (marker.as_ref(), marker.as_ref()))
25879        .collect();
25880
25881    let all_entries: Vec<_> = task_list_entries
25882        .into_iter()
25883        .chain(unordered_list_entries)
25884        .collect();
25885
25886    if let Some(max_prefix_len) = all_entries.iter().map(|(p, _)| p.len()).max() {
25887        let candidate: String = snapshot
25888            .chars_for_range(range.clone())
25889            .skip(num_of_whitespaces)
25890            .take(max_prefix_len)
25891            .collect();
25892
25893        if let Some((prefix, continuation)) = all_entries
25894            .iter()
25895            .filter(|(prefix, _)| candidate.starts_with(*prefix))
25896            .max_by_key(|(prefix, _)| prefix.len())
25897        {
25898            let end_of_prefix = num_of_whitespaces + prefix.len();
25899            let cursor_is_after_prefix = end_of_prefix <= start_point.column as usize;
25900            let has_content_after_marker = snapshot
25901                .chars_for_range(range)
25902                .skip(end_of_prefix)
25903                .any(|c| !c.is_whitespace());
25904
25905            if has_content_after_marker && cursor_is_after_prefix {
25906                return Some((*continuation).into());
25907            }
25908
25909            if start_point.column as usize == end_of_prefix {
25910                if num_of_whitespaces == 0 {
25911                    *newline_config = NewlineConfig::ClearCurrentLine;
25912                } else {
25913                    *newline_config = NewlineConfig::UnindentCurrentLine {
25914                        continuation: (*continuation).into(),
25915                    };
25916                }
25917            }
25918
25919            return None;
25920        }
25921    }
25922
25923    let candidate: String = snapshot
25924        .chars_for_range(range.clone())
25925        .skip(num_of_whitespaces)
25926        .take(ORDERED_LIST_MAX_MARKER_LEN)
25927        .collect();
25928
25929    for ordered_config in language.ordered_list() {
25930        let regex = match Regex::new(&ordered_config.pattern) {
25931            Ok(r) => r,
25932            Err(_) => continue,
25933        };
25934
25935        if let Some(captures) = regex.captures(&candidate) {
25936            let full_match = captures.get(0)?;
25937            let marker_len = full_match.len();
25938            let end_of_prefix = num_of_whitespaces + marker_len;
25939            let cursor_is_after_prefix = end_of_prefix <= start_point.column as usize;
25940
25941            let has_content_after_marker = snapshot
25942                .chars_for_range(range)
25943                .skip(end_of_prefix)
25944                .any(|c| !c.is_whitespace());
25945
25946            if has_content_after_marker && cursor_is_after_prefix {
25947                let number: u32 = captures.get(1)?.as_str().parse().ok()?;
25948                let continuation = ordered_config
25949                    .format
25950                    .replace("{1}", &(number + 1).to_string());
25951                return Some(continuation.into());
25952            }
25953
25954            if start_point.column as usize == end_of_prefix {
25955                let continuation = ordered_config.format.replace("{1}", "1");
25956                if num_of_whitespaces == 0 {
25957                    *newline_config = NewlineConfig::ClearCurrentLine;
25958                } else {
25959                    *newline_config = NewlineConfig::UnindentCurrentLine {
25960                        continuation: continuation.into(),
25961                    };
25962                }
25963            }
25964
25965            return None;
25966        }
25967    }
25968
25969    None
25970}
25971
25972fn is_list_prefix_row(
25973    row: MultiBufferRow,
25974    buffer: &MultiBufferSnapshot,
25975    language: &LanguageScope,
25976) -> bool {
25977    let Some((snapshot, range)) = buffer.buffer_line_for_row(row) else {
25978        return false;
25979    };
25980
25981    let num_of_whitespaces = snapshot
25982        .chars_for_range(range.clone())
25983        .take_while(|c| c.is_whitespace())
25984        .count();
25985
25986    let task_list_prefixes: Vec<_> = language
25987        .task_list()
25988        .into_iter()
25989        .flat_map(|config| {
25990            config
25991                .prefixes
25992                .iter()
25993                .map(|p| p.as_ref())
25994                .collect::<Vec<_>>()
25995        })
25996        .collect();
25997    let unordered_list_markers: Vec<_> = language
25998        .unordered_list()
25999        .iter()
26000        .map(|marker| marker.as_ref())
26001        .collect();
26002    let all_prefixes: Vec<_> = task_list_prefixes
26003        .into_iter()
26004        .chain(unordered_list_markers)
26005        .collect();
26006    if let Some(max_prefix_len) = all_prefixes.iter().map(|p| p.len()).max() {
26007        let candidate: String = snapshot
26008            .chars_for_range(range.clone())
26009            .skip(num_of_whitespaces)
26010            .take(max_prefix_len)
26011            .collect();
26012        if all_prefixes
26013            .iter()
26014            .any(|prefix| candidate.starts_with(*prefix))
26015        {
26016            return true;
26017        }
26018    }
26019
26020    let ordered_list_candidate: String = snapshot
26021        .chars_for_range(range)
26022        .skip(num_of_whitespaces)
26023        .take(ORDERED_LIST_MAX_MARKER_LEN)
26024        .collect();
26025    for ordered_config in language.ordered_list() {
26026        let regex = match Regex::new(&ordered_config.pattern) {
26027            Ok(r) => r,
26028            Err(_) => continue,
26029        };
26030        if let Some(captures) = regex.captures(&ordered_list_candidate) {
26031            return captures.get(0).is_some();
26032        }
26033    }
26034
26035    false
26036}
26037
26038#[derive(Debug)]
26039enum NewlineConfig {
26040    /// Insert newline with optional additional indent and optional extra blank line
26041    Newline {
26042        additional_indent: IndentSize,
26043        extra_line_additional_indent: Option<IndentSize>,
26044        prevent_auto_indent: bool,
26045    },
26046    /// Clear the current line
26047    ClearCurrentLine,
26048    /// Unindent the current line and add continuation
26049    UnindentCurrentLine { continuation: Arc<str> },
26050}
26051
26052impl NewlineConfig {
26053    fn has_extra_line(&self) -> bool {
26054        matches!(
26055            self,
26056            Self::Newline {
26057                extra_line_additional_indent: Some(_),
26058                ..
26059            }
26060        )
26061    }
26062
26063    fn insert_extra_newline_brackets(
26064        buffer: &MultiBufferSnapshot,
26065        range: Range<MultiBufferOffset>,
26066        language: &language::LanguageScope,
26067    ) -> bool {
26068        let leading_whitespace_len = buffer
26069            .reversed_chars_at(range.start)
26070            .take_while(|c| c.is_whitespace() && *c != '\n')
26071            .map(|c| c.len_utf8())
26072            .sum::<usize>();
26073        let trailing_whitespace_len = buffer
26074            .chars_at(range.end)
26075            .take_while(|c| c.is_whitespace() && *c != '\n')
26076            .map(|c| c.len_utf8())
26077            .sum::<usize>();
26078        let range = range.start - leading_whitespace_len..range.end + trailing_whitespace_len;
26079
26080        language.brackets().any(|(pair, enabled)| {
26081            let pair_start = pair.start.trim_end();
26082            let pair_end = pair.end.trim_start();
26083
26084            enabled
26085                && pair.newline
26086                && buffer.contains_str_at(range.end, pair_end)
26087                && buffer.contains_str_at(
26088                    range.start.saturating_sub_usize(pair_start.len()),
26089                    pair_start,
26090                )
26091        })
26092    }
26093
26094    fn insert_extra_newline_tree_sitter(
26095        buffer: &MultiBufferSnapshot,
26096        range: Range<MultiBufferOffset>,
26097    ) -> bool {
26098        let (buffer, range) = match buffer
26099            .range_to_buffer_ranges(range.start..=range.end)
26100            .as_slice()
26101        {
26102            [(buffer, range, _)] => (*buffer, range.clone()),
26103            _ => return false,
26104        };
26105        let pair = {
26106            let mut result: Option<BracketMatch<usize>> = None;
26107
26108            for pair in buffer
26109                .all_bracket_ranges(range.start.0..range.end.0)
26110                .filter(move |pair| {
26111                    pair.open_range.start <= range.start.0 && pair.close_range.end >= range.end.0
26112                })
26113            {
26114                let len = pair.close_range.end - pair.open_range.start;
26115
26116                if let Some(existing) = &result {
26117                    let existing_len = existing.close_range.end - existing.open_range.start;
26118                    if len > existing_len {
26119                        continue;
26120                    }
26121                }
26122
26123                result = Some(pair);
26124            }
26125
26126            result
26127        };
26128        let Some(pair) = pair else {
26129            return false;
26130        };
26131        pair.newline_only
26132            && buffer
26133                .chars_for_range(pair.open_range.end..range.start.0)
26134                .chain(buffer.chars_for_range(range.end.0..pair.close_range.start))
26135                .all(|c| c.is_whitespace() && c != '\n')
26136    }
26137}
26138
26139fn update_uncommitted_diff_for_buffer(
26140    editor: Entity<Editor>,
26141    project: &Entity<Project>,
26142    buffers: impl IntoIterator<Item = Entity<Buffer>>,
26143    buffer: Entity<MultiBuffer>,
26144    cx: &mut App,
26145) -> Task<()> {
26146    let mut tasks = Vec::new();
26147    project.update(cx, |project, cx| {
26148        for buffer in buffers {
26149            if project::File::from_dyn(buffer.read(cx).file()).is_some() {
26150                tasks.push(project.open_uncommitted_diff(buffer.clone(), cx))
26151            }
26152        }
26153    });
26154    cx.spawn(async move |cx| {
26155        let diffs = future::join_all(tasks).await;
26156        if editor.read_with(cx, |editor, _cx| editor.temporary_diff_override) {
26157            return;
26158        }
26159
26160        buffer.update(cx, |buffer, cx| {
26161            for diff in diffs.into_iter().flatten() {
26162                buffer.add_diff(diff, cx);
26163            }
26164        });
26165    })
26166}
26167
26168fn char_len_with_expanded_tabs(offset: usize, text: &str, tab_size: NonZeroU32) -> usize {
26169    let tab_size = tab_size.get() as usize;
26170    let mut width = offset;
26171
26172    for ch in text.chars() {
26173        width += if ch == '\t' {
26174            tab_size - (width % tab_size)
26175        } else {
26176            1
26177        };
26178    }
26179
26180    width - offset
26181}
26182
26183#[cfg(test)]
26184mod tests {
26185    use super::*;
26186
26187    #[test]
26188    fn test_string_size_with_expanded_tabs() {
26189        let nz = |val| NonZeroU32::new(val).unwrap();
26190        assert_eq!(char_len_with_expanded_tabs(0, "", nz(4)), 0);
26191        assert_eq!(char_len_with_expanded_tabs(0, "hello", nz(4)), 5);
26192        assert_eq!(char_len_with_expanded_tabs(0, "\thello", nz(4)), 9);
26193        assert_eq!(char_len_with_expanded_tabs(0, "abc\tab", nz(4)), 6);
26194        assert_eq!(char_len_with_expanded_tabs(0, "hello\t", nz(4)), 8);
26195        assert_eq!(char_len_with_expanded_tabs(0, "\t\t", nz(8)), 16);
26196        assert_eq!(char_len_with_expanded_tabs(0, "x\t", nz(8)), 8);
26197        assert_eq!(char_len_with_expanded_tabs(7, "x\t", nz(8)), 9);
26198    }
26199}
26200
26201/// Tokenizes a string into runs of text that should stick together, or that is whitespace.
26202struct WordBreakingTokenizer<'a> {
26203    input: &'a str,
26204}
26205
26206impl<'a> WordBreakingTokenizer<'a> {
26207    fn new(input: &'a str) -> Self {
26208        Self { input }
26209    }
26210}
26211
26212fn is_char_ideographic(ch: char) -> bool {
26213    use unicode_script::Script::*;
26214    use unicode_script::UnicodeScript;
26215    matches!(ch.script(), Han | Tangut | Yi)
26216}
26217
26218fn is_grapheme_ideographic(text: &str) -> bool {
26219    text.chars().any(is_char_ideographic)
26220}
26221
26222fn is_grapheme_whitespace(text: &str) -> bool {
26223    text.chars().any(|x| x.is_whitespace())
26224}
26225
26226fn should_stay_with_preceding_ideograph(text: &str) -> bool {
26227    text.chars()
26228        .next()
26229        .is_some_and(|ch| matches!(ch, '。' | '、' | ',' | '?' | '!' | ':' | ';' | '…'))
26230}
26231
26232#[derive(PartialEq, Eq, Debug, Clone, Copy)]
26233enum WordBreakToken<'a> {
26234    Word { token: &'a str, grapheme_len: usize },
26235    InlineWhitespace { token: &'a str, grapheme_len: usize },
26236    Newline,
26237}
26238
26239impl<'a> Iterator for WordBreakingTokenizer<'a> {
26240    /// Yields a span, the count of graphemes in the token, and whether it was
26241    /// whitespace. Note that it also breaks at word boundaries.
26242    type Item = WordBreakToken<'a>;
26243
26244    fn next(&mut self) -> Option<Self::Item> {
26245        use unicode_segmentation::UnicodeSegmentation;
26246        if self.input.is_empty() {
26247            return None;
26248        }
26249
26250        let mut iter = self.input.graphemes(true).peekable();
26251        let mut offset = 0;
26252        let mut grapheme_len = 0;
26253        if let Some(first_grapheme) = iter.next() {
26254            let is_newline = first_grapheme == "\n";
26255            let is_whitespace = is_grapheme_whitespace(first_grapheme);
26256            offset += first_grapheme.len();
26257            grapheme_len += 1;
26258            if is_grapheme_ideographic(first_grapheme) && !is_whitespace {
26259                if let Some(grapheme) = iter.peek().copied()
26260                    && should_stay_with_preceding_ideograph(grapheme)
26261                {
26262                    offset += grapheme.len();
26263                    grapheme_len += 1;
26264                }
26265            } else {
26266                let mut words = self.input[offset..].split_word_bound_indices().peekable();
26267                let mut next_word_bound = words.peek().copied();
26268                if next_word_bound.is_some_and(|(i, _)| i == 0) {
26269                    next_word_bound = words.next();
26270                }
26271                while let Some(grapheme) = iter.peek().copied() {
26272                    if next_word_bound.is_some_and(|(i, _)| i == offset) {
26273                        break;
26274                    };
26275                    if is_grapheme_whitespace(grapheme) != is_whitespace
26276                        || (grapheme == "\n") != is_newline
26277                    {
26278                        break;
26279                    };
26280                    offset += grapheme.len();
26281                    grapheme_len += 1;
26282                    iter.next();
26283                }
26284            }
26285            let token = &self.input[..offset];
26286            self.input = &self.input[offset..];
26287            if token == "\n" {
26288                Some(WordBreakToken::Newline)
26289            } else if is_whitespace {
26290                Some(WordBreakToken::InlineWhitespace {
26291                    token,
26292                    grapheme_len,
26293                })
26294            } else {
26295                Some(WordBreakToken::Word {
26296                    token,
26297                    grapheme_len,
26298                })
26299            }
26300        } else {
26301            None
26302        }
26303    }
26304}
26305
26306#[test]
26307fn test_word_breaking_tokenizer() {
26308    let tests: &[(&str, &[WordBreakToken<'static>])] = &[
26309        ("", &[]),
26310        ("  ", &[whitespace("  ", 2)]),
26311        ("Ʒ", &[word("Ʒ", 1)]),
26312        ("Ǽ", &[word("Ǽ", 1)]),
26313        ("", &[word("", 1)]),
26314        ("⋑⋑", &[word("⋑⋑", 2)]),
26315        (
26316            "原理,进而",
26317            &[word("", 1), word("理,", 2), word("", 1), word("", 1)],
26318        ),
26319        (
26320            "hello world",
26321            &[word("hello", 5), whitespace(" ", 1), word("world", 5)],
26322        ),
26323        (
26324            "hello, world",
26325            &[word("hello,", 6), whitespace(" ", 1), word("world", 5)],
26326        ),
26327        (
26328            "  hello world",
26329            &[
26330                whitespace("  ", 2),
26331                word("hello", 5),
26332                whitespace(" ", 1),
26333                word("world", 5),
26334            ],
26335        ),
26336        (
26337            "这是什么 \n 钢笔",
26338            &[
26339                word("", 1),
26340                word("", 1),
26341                word("", 1),
26342                word("", 1),
26343                whitespace(" ", 1),
26344                newline(),
26345                whitespace(" ", 1),
26346                word("", 1),
26347                word("", 1),
26348            ],
26349        ),
26350        (" mutton", &[whitespace("", 1), word("mutton", 6)]),
26351    ];
26352
26353    fn word(token: &'static str, grapheme_len: usize) -> WordBreakToken<'static> {
26354        WordBreakToken::Word {
26355            token,
26356            grapheme_len,
26357        }
26358    }
26359
26360    fn whitespace(token: &'static str, grapheme_len: usize) -> WordBreakToken<'static> {
26361        WordBreakToken::InlineWhitespace {
26362            token,
26363            grapheme_len,
26364        }
26365    }
26366
26367    fn newline() -> WordBreakToken<'static> {
26368        WordBreakToken::Newline
26369    }
26370
26371    for (input, result) in tests {
26372        assert_eq!(
26373            WordBreakingTokenizer::new(input)
26374                .collect::<Vec<_>>()
26375                .as_slice(),
26376            *result,
26377        );
26378    }
26379}
26380
26381fn wrap_with_prefix(
26382    first_line_prefix: String,
26383    subsequent_lines_prefix: String,
26384    unwrapped_text: String,
26385    wrap_column: usize,
26386    tab_size: NonZeroU32,
26387    preserve_existing_whitespace: bool,
26388) -> String {
26389    let first_line_prefix_len = char_len_with_expanded_tabs(0, &first_line_prefix, tab_size);
26390    let subsequent_lines_prefix_len =
26391        char_len_with_expanded_tabs(0, &subsequent_lines_prefix, tab_size);
26392    let mut wrapped_text = String::new();
26393    let mut current_line = first_line_prefix;
26394    let mut is_first_line = true;
26395
26396    let tokenizer = WordBreakingTokenizer::new(&unwrapped_text);
26397    let mut current_line_len = first_line_prefix_len;
26398    let mut in_whitespace = false;
26399    for token in tokenizer {
26400        let have_preceding_whitespace = in_whitespace;
26401        match token {
26402            WordBreakToken::Word {
26403                token,
26404                grapheme_len,
26405            } => {
26406                in_whitespace = false;
26407                let current_prefix_len = if is_first_line {
26408                    first_line_prefix_len
26409                } else {
26410                    subsequent_lines_prefix_len
26411                };
26412                if current_line_len + grapheme_len > wrap_column
26413                    && current_line_len != current_prefix_len
26414                {
26415                    wrapped_text.push_str(current_line.trim_end());
26416                    wrapped_text.push('\n');
26417                    is_first_line = false;
26418                    current_line = subsequent_lines_prefix.clone();
26419                    current_line_len = subsequent_lines_prefix_len;
26420                }
26421                current_line.push_str(token);
26422                current_line_len += grapheme_len;
26423            }
26424            WordBreakToken::InlineWhitespace {
26425                mut token,
26426                mut grapheme_len,
26427            } => {
26428                in_whitespace = true;
26429                if have_preceding_whitespace && !preserve_existing_whitespace {
26430                    continue;
26431                }
26432                if !preserve_existing_whitespace {
26433                    // Keep a single whitespace grapheme as-is
26434                    if let Some(first) =
26435                        unicode_segmentation::UnicodeSegmentation::graphemes(token, true).next()
26436                    {
26437                        token = first;
26438                    } else {
26439                        token = " ";
26440                    }
26441                    grapheme_len = 1;
26442                }
26443                let current_prefix_len = if is_first_line {
26444                    first_line_prefix_len
26445                } else {
26446                    subsequent_lines_prefix_len
26447                };
26448                if current_line_len + grapheme_len > wrap_column {
26449                    wrapped_text.push_str(current_line.trim_end());
26450                    wrapped_text.push('\n');
26451                    is_first_line = false;
26452                    current_line = subsequent_lines_prefix.clone();
26453                    current_line_len = subsequent_lines_prefix_len;
26454                } else if current_line_len != current_prefix_len || preserve_existing_whitespace {
26455                    current_line.push_str(token);
26456                    current_line_len += grapheme_len;
26457                }
26458            }
26459            WordBreakToken::Newline => {
26460                in_whitespace = true;
26461                let current_prefix_len = if is_first_line {
26462                    first_line_prefix_len
26463                } else {
26464                    subsequent_lines_prefix_len
26465                };
26466                if preserve_existing_whitespace {
26467                    wrapped_text.push_str(current_line.trim_end());
26468                    wrapped_text.push('\n');
26469                    is_first_line = false;
26470                    current_line = subsequent_lines_prefix.clone();
26471                    current_line_len = subsequent_lines_prefix_len;
26472                } else if have_preceding_whitespace {
26473                    continue;
26474                } else if current_line_len + 1 > wrap_column
26475                    && current_line_len != current_prefix_len
26476                {
26477                    wrapped_text.push_str(current_line.trim_end());
26478                    wrapped_text.push('\n');
26479                    is_first_line = false;
26480                    current_line = subsequent_lines_prefix.clone();
26481                    current_line_len = subsequent_lines_prefix_len;
26482                } else if current_line_len != current_prefix_len {
26483                    current_line.push(' ');
26484                    current_line_len += 1;
26485                }
26486            }
26487        }
26488    }
26489
26490    if !current_line.is_empty() {
26491        wrapped_text.push_str(&current_line);
26492    }
26493    wrapped_text
26494}
26495
26496#[test]
26497fn test_wrap_with_prefix() {
26498    assert_eq!(
26499        wrap_with_prefix(
26500            "# ".to_string(),
26501            "# ".to_string(),
26502            "abcdefg".to_string(),
26503            4,
26504            NonZeroU32::new(4).unwrap(),
26505            false,
26506        ),
26507        "# abcdefg"
26508    );
26509    assert_eq!(
26510        wrap_with_prefix(
26511            "".to_string(),
26512            "".to_string(),
26513            "\thello world".to_string(),
26514            8,
26515            NonZeroU32::new(4).unwrap(),
26516            false,
26517        ),
26518        "hello\nworld"
26519    );
26520    assert_eq!(
26521        wrap_with_prefix(
26522            "// ".to_string(),
26523            "// ".to_string(),
26524            "xx \nyy zz aa bb cc".to_string(),
26525            12,
26526            NonZeroU32::new(4).unwrap(),
26527            false,
26528        ),
26529        "// xx yy zz\n// aa bb cc"
26530    );
26531    assert_eq!(
26532        wrap_with_prefix(
26533            String::new(),
26534            String::new(),
26535            "这是什么 \n 钢笔".to_string(),
26536            3,
26537            NonZeroU32::new(4).unwrap(),
26538            false,
26539        ),
26540        "这是什\n么 钢\n"
26541    );
26542    assert_eq!(
26543        wrap_with_prefix(
26544            String::new(),
26545            String::new(),
26546            format!("foo{}bar", '\u{2009}'), // thin space
26547            80,
26548            NonZeroU32::new(4).unwrap(),
26549            false,
26550        ),
26551        format!("foo{}bar", '\u{2009}')
26552    );
26553}
26554
26555pub trait CollaborationHub {
26556    fn collaborators<'a>(&self, cx: &'a App) -> &'a HashMap<PeerId, Collaborator>;
26557    fn user_participant_indices<'a>(&self, cx: &'a App) -> &'a HashMap<u64, ParticipantIndex>;
26558    fn user_names(&self, cx: &App) -> HashMap<u64, SharedString>;
26559}
26560
26561impl CollaborationHub for Entity<Project> {
26562    fn collaborators<'a>(&self, cx: &'a App) -> &'a HashMap<PeerId, Collaborator> {
26563        self.read(cx).collaborators()
26564    }
26565
26566    fn user_participant_indices<'a>(&self, cx: &'a App) -> &'a HashMap<u64, ParticipantIndex> {
26567        self.read(cx).user_store().read(cx).participant_indices()
26568    }
26569
26570    fn user_names(&self, cx: &App) -> HashMap<u64, SharedString> {
26571        let this = self.read(cx);
26572        let user_ids = this.collaborators().values().map(|c| c.user_id);
26573        this.user_store().read(cx).participant_names(user_ids, cx)
26574    }
26575}
26576
26577pub trait SemanticsProvider {
26578    fn hover(
26579        &self,
26580        buffer: &Entity<Buffer>,
26581        position: text::Anchor,
26582        cx: &mut App,
26583    ) -> Option<Task<Option<Vec<project::Hover>>>>;
26584
26585    fn inline_values(
26586        &self,
26587        buffer_handle: Entity<Buffer>,
26588        range: Range<text::Anchor>,
26589        cx: &mut App,
26590    ) -> Option<Task<anyhow::Result<Vec<InlayHint>>>>;
26591
26592    fn applicable_inlay_chunks(
26593        &self,
26594        buffer: &Entity<Buffer>,
26595        ranges: &[Range<text::Anchor>],
26596        cx: &mut App,
26597    ) -> Vec<Range<BufferRow>>;
26598
26599    fn invalidate_inlay_hints(&self, for_buffers: &HashSet<BufferId>, cx: &mut App);
26600
26601    fn inlay_hints(
26602        &self,
26603        invalidate: InvalidationStrategy,
26604        buffer: Entity<Buffer>,
26605        ranges: Vec<Range<text::Anchor>>,
26606        known_chunks: Option<(clock::Global, HashSet<Range<BufferRow>>)>,
26607        cx: &mut App,
26608    ) -> Option<HashMap<Range<BufferRow>, Task<Result<CacheInlayHints>>>>;
26609
26610    fn semantic_tokens(
26611        &self,
26612        buffer: Entity<Buffer>,
26613        refresh: Option<RefreshForServer>,
26614        cx: &mut App,
26615    ) -> Shared<Task<std::result::Result<BufferSemanticTokens, Arc<anyhow::Error>>>>;
26616
26617    fn supports_inlay_hints(&self, buffer: &Entity<Buffer>, cx: &mut App) -> bool;
26618
26619    fn supports_semantic_tokens(&self, buffer: &Entity<Buffer>, cx: &mut App) -> bool;
26620
26621    fn document_highlights(
26622        &self,
26623        buffer: &Entity<Buffer>,
26624        position: text::Anchor,
26625        cx: &mut App,
26626    ) -> Option<Task<Result<Vec<DocumentHighlight>>>>;
26627
26628    fn definitions(
26629        &self,
26630        buffer: &Entity<Buffer>,
26631        position: text::Anchor,
26632        kind: GotoDefinitionKind,
26633        cx: &mut App,
26634    ) -> Option<Task<Result<Option<Vec<LocationLink>>>>>;
26635
26636    fn range_for_rename(
26637        &self,
26638        buffer: &Entity<Buffer>,
26639        position: text::Anchor,
26640        cx: &mut App,
26641    ) -> Option<Task<Result<Option<Range<text::Anchor>>>>>;
26642
26643    fn perform_rename(
26644        &self,
26645        buffer: &Entity<Buffer>,
26646        position: text::Anchor,
26647        new_name: String,
26648        cx: &mut App,
26649    ) -> Option<Task<Result<ProjectTransaction>>>;
26650}
26651
26652pub trait CompletionProvider {
26653    fn completions(
26654        &self,
26655        excerpt_id: ExcerptId,
26656        buffer: &Entity<Buffer>,
26657        buffer_position: text::Anchor,
26658        trigger: CompletionContext,
26659        window: &mut Window,
26660        cx: &mut Context<Editor>,
26661    ) -> Task<Result<Vec<CompletionResponse>>>;
26662
26663    fn resolve_completions(
26664        &self,
26665        _buffer: Entity<Buffer>,
26666        _completion_indices: Vec<usize>,
26667        _completions: Rc<RefCell<Box<[Completion]>>>,
26668        _cx: &mut Context<Editor>,
26669    ) -> Task<Result<bool>> {
26670        Task::ready(Ok(false))
26671    }
26672
26673    fn apply_additional_edits_for_completion(
26674        &self,
26675        _buffer: Entity<Buffer>,
26676        _completions: Rc<RefCell<Box<[Completion]>>>,
26677        _completion_index: usize,
26678        _push_to_history: bool,
26679        _cx: &mut Context<Editor>,
26680    ) -> Task<Result<Option<language::Transaction>>> {
26681        Task::ready(Ok(None))
26682    }
26683
26684    fn is_completion_trigger(
26685        &self,
26686        buffer: &Entity<Buffer>,
26687        position: language::Anchor,
26688        text: &str,
26689        trigger_in_words: bool,
26690        cx: &mut Context<Editor>,
26691    ) -> bool;
26692
26693    fn selection_changed(&self, _mat: Option<&StringMatch>, _window: &mut Window, _cx: &mut App) {}
26694
26695    fn sort_completions(&self) -> bool {
26696        true
26697    }
26698
26699    fn filter_completions(&self) -> bool {
26700        true
26701    }
26702
26703    fn show_snippets(&self) -> bool {
26704        false
26705    }
26706}
26707
26708pub trait CodeActionProvider {
26709    fn id(&self) -> Arc<str>;
26710
26711    fn code_actions(
26712        &self,
26713        buffer: &Entity<Buffer>,
26714        range: Range<text::Anchor>,
26715        window: &mut Window,
26716        cx: &mut App,
26717    ) -> Task<Result<Vec<CodeAction>>>;
26718
26719    fn apply_code_action(
26720        &self,
26721        buffer_handle: Entity<Buffer>,
26722        action: CodeAction,
26723        excerpt_id: ExcerptId,
26724        push_to_history: bool,
26725        window: &mut Window,
26726        cx: &mut App,
26727    ) -> Task<Result<ProjectTransaction>>;
26728}
26729
26730impl CodeActionProvider for Entity<Project> {
26731    fn id(&self) -> Arc<str> {
26732        "project".into()
26733    }
26734
26735    fn code_actions(
26736        &self,
26737        buffer: &Entity<Buffer>,
26738        range: Range<text::Anchor>,
26739        _window: &mut Window,
26740        cx: &mut App,
26741    ) -> Task<Result<Vec<CodeAction>>> {
26742        self.update(cx, |project, cx| {
26743            let code_lens_actions = project.code_lens_actions(buffer, range.clone(), cx);
26744            let code_actions = project.code_actions(buffer, range, None, cx);
26745            cx.background_spawn(async move {
26746                let (code_lens_actions, code_actions) = join(code_lens_actions, code_actions).await;
26747                Ok(code_lens_actions
26748                    .context("code lens fetch")?
26749                    .into_iter()
26750                    .flatten()
26751                    .chain(
26752                        code_actions
26753                            .context("code action fetch")?
26754                            .into_iter()
26755                            .flatten(),
26756                    )
26757                    .collect())
26758            })
26759        })
26760    }
26761
26762    fn apply_code_action(
26763        &self,
26764        buffer_handle: Entity<Buffer>,
26765        action: CodeAction,
26766        _excerpt_id: ExcerptId,
26767        push_to_history: bool,
26768        _window: &mut Window,
26769        cx: &mut App,
26770    ) -> Task<Result<ProjectTransaction>> {
26771        self.update(cx, |project, cx| {
26772            project.apply_code_action(buffer_handle, action, push_to_history, cx)
26773        })
26774    }
26775}
26776
26777fn snippet_completions(
26778    project: &Project,
26779    buffer: &Entity<Buffer>,
26780    buffer_anchor: text::Anchor,
26781    classifier: CharClassifier,
26782    cx: &mut App,
26783) -> Task<Result<CompletionResponse>> {
26784    let languages = buffer.read(cx).languages_at(buffer_anchor);
26785    let snippet_store = project.snippets().read(cx);
26786
26787    let scopes: Vec<_> = languages
26788        .iter()
26789        .filter_map(|language| {
26790            let language_name = language.lsp_id();
26791            let snippets = snippet_store.snippets_for(Some(language_name), cx);
26792
26793            if snippets.is_empty() {
26794                None
26795            } else {
26796                Some((language.default_scope(), snippets))
26797            }
26798        })
26799        .collect();
26800
26801    if scopes.is_empty() {
26802        return Task::ready(Ok(CompletionResponse {
26803            completions: vec![],
26804            display_options: CompletionDisplayOptions::default(),
26805            is_incomplete: false,
26806        }));
26807    }
26808
26809    let snapshot = buffer.read(cx).text_snapshot();
26810    let executor = cx.background_executor().clone();
26811
26812    cx.background_spawn(async move {
26813        let is_word_char = |c| classifier.is_word(c);
26814
26815        let mut is_incomplete = false;
26816        let mut completions: Vec<Completion> = Vec::new();
26817
26818        const MAX_PREFIX_LEN: usize = 128;
26819        let buffer_offset = text::ToOffset::to_offset(&buffer_anchor, &snapshot);
26820        let window_start = buffer_offset.saturating_sub(MAX_PREFIX_LEN);
26821        let window_start = snapshot.clip_offset(window_start, Bias::Left);
26822
26823        let max_buffer_window: String = snapshot
26824            .text_for_range(window_start..buffer_offset)
26825            .collect();
26826
26827        if max_buffer_window.is_empty() {
26828            return Ok(CompletionResponse {
26829                completions: vec![],
26830                display_options: CompletionDisplayOptions::default(),
26831                is_incomplete: true,
26832            });
26833        }
26834
26835        for (_scope, snippets) in scopes.into_iter() {
26836            // Sort snippets by word count to match longer snippet prefixes first.
26837            let mut sorted_snippet_candidates = snippets
26838                .iter()
26839                .enumerate()
26840                .flat_map(|(snippet_ix, snippet)| {
26841                    snippet
26842                        .prefix
26843                        .iter()
26844                        .enumerate()
26845                        .map(move |(prefix_ix, prefix)| {
26846                            let word_count =
26847                                snippet_candidate_suffixes(prefix, is_word_char).count();
26848                            ((snippet_ix, prefix_ix), prefix, word_count)
26849                        })
26850                })
26851                .collect_vec();
26852            sorted_snippet_candidates
26853                .sort_unstable_by_key(|(_, _, word_count)| Reverse(*word_count));
26854
26855            // Each prefix may be matched multiple times; the completion menu must filter out duplicates.
26856
26857            let buffer_windows = snippet_candidate_suffixes(&max_buffer_window, is_word_char)
26858                .take(
26859                    sorted_snippet_candidates
26860                        .first()
26861                        .map(|(_, _, word_count)| *word_count)
26862                        .unwrap_or_default(),
26863                )
26864                .collect_vec();
26865
26866            const MAX_RESULTS: usize = 100;
26867            // Each match also remembers how many characters from the buffer it consumed
26868            let mut matches: Vec<(StringMatch, usize)> = vec![];
26869
26870            let mut snippet_list_cutoff_index = 0;
26871            for (buffer_index, buffer_window) in buffer_windows.iter().enumerate().rev() {
26872                let word_count = buffer_index + 1;
26873                // Increase `snippet_list_cutoff_index` until we have all of the
26874                // snippets with sufficiently many words.
26875                while sorted_snippet_candidates
26876                    .get(snippet_list_cutoff_index)
26877                    .is_some_and(|(_ix, _prefix, snippet_word_count)| {
26878                        *snippet_word_count >= word_count
26879                    })
26880                {
26881                    snippet_list_cutoff_index += 1;
26882                }
26883
26884                // Take only the candidates with at least `word_count` many words
26885                let snippet_candidates_at_word_len =
26886                    &sorted_snippet_candidates[..snippet_list_cutoff_index];
26887
26888                let candidates = snippet_candidates_at_word_len
26889                    .iter()
26890                    .map(|(_snippet_ix, prefix, _snippet_word_count)| prefix)
26891                    .enumerate() // index in `sorted_snippet_candidates`
26892                    // First char must match
26893                    .filter(|(_ix, prefix)| {
26894                        itertools::equal(
26895                            prefix
26896                                .chars()
26897                                .next()
26898                                .into_iter()
26899                                .flat_map(|c| c.to_lowercase()),
26900                            buffer_window
26901                                .chars()
26902                                .next()
26903                                .into_iter()
26904                                .flat_map(|c| c.to_lowercase()),
26905                        )
26906                    })
26907                    .map(|(ix, prefix)| StringMatchCandidate::new(ix, prefix))
26908                    .collect::<Vec<StringMatchCandidate>>();
26909
26910                matches.extend(
26911                    fuzzy::match_strings(
26912                        &candidates,
26913                        &buffer_window,
26914                        buffer_window.chars().any(|c| c.is_uppercase()),
26915                        true,
26916                        MAX_RESULTS - matches.len(), // always prioritize longer snippets
26917                        &Default::default(),
26918                        executor.clone(),
26919                    )
26920                    .await
26921                    .into_iter()
26922                    .map(|string_match| (string_match, buffer_window.len())),
26923                );
26924
26925                if matches.len() >= MAX_RESULTS {
26926                    break;
26927                }
26928            }
26929
26930            let to_lsp = |point: &text::Anchor| {
26931                let end = text::ToPointUtf16::to_point_utf16(point, &snapshot);
26932                point_to_lsp(end)
26933            };
26934            let lsp_end = to_lsp(&buffer_anchor);
26935
26936            if matches.len() >= MAX_RESULTS {
26937                is_incomplete = true;
26938            }
26939
26940            completions.extend(matches.iter().map(|(string_match, buffer_window_len)| {
26941                let ((snippet_index, prefix_index), matching_prefix, _snippet_word_count) =
26942                    sorted_snippet_candidates[string_match.candidate_id];
26943                let snippet = &snippets[snippet_index];
26944                let start = buffer_offset - buffer_window_len;
26945                let start = snapshot.anchor_before(start);
26946                let range = start..buffer_anchor;
26947                let lsp_start = to_lsp(&start);
26948                let lsp_range = lsp::Range {
26949                    start: lsp_start,
26950                    end: lsp_end,
26951                };
26952                Completion {
26953                    replace_range: range,
26954                    new_text: snippet.body.clone(),
26955                    source: CompletionSource::Lsp {
26956                        insert_range: None,
26957                        server_id: LanguageServerId(usize::MAX),
26958                        resolved: true,
26959                        lsp_completion: Box::new(lsp::CompletionItem {
26960                            label: snippet.prefix.first().unwrap().clone(),
26961                            kind: Some(CompletionItemKind::SNIPPET),
26962                            label_details: snippet.description.as_ref().map(|description| {
26963                                lsp::CompletionItemLabelDetails {
26964                                    detail: Some(description.clone()),
26965                                    description: None,
26966                                }
26967                            }),
26968                            insert_text_format: Some(InsertTextFormat::SNIPPET),
26969                            text_edit: Some(lsp::CompletionTextEdit::InsertAndReplace(
26970                                lsp::InsertReplaceEdit {
26971                                    new_text: snippet.body.clone(),
26972                                    insert: lsp_range,
26973                                    replace: lsp_range,
26974                                },
26975                            )),
26976                            filter_text: Some(snippet.body.clone()),
26977                            sort_text: Some(char::MAX.to_string()),
26978                            ..lsp::CompletionItem::default()
26979                        }),
26980                        lsp_defaults: None,
26981                    },
26982                    label: CodeLabel {
26983                        text: matching_prefix.clone(),
26984                        runs: Vec::new(),
26985                        filter_range: 0..matching_prefix.len(),
26986                    },
26987                    icon_path: None,
26988                    documentation: Some(CompletionDocumentation::SingleLineAndMultiLinePlainText {
26989                        single_line: snippet.name.clone().into(),
26990                        plain_text: snippet
26991                            .description
26992                            .clone()
26993                            .map(|description| description.into()),
26994                    }),
26995                    insert_text_mode: None,
26996                    confirm: None,
26997                    match_start: Some(start),
26998                    snippet_deduplication_key: Some((snippet_index, prefix_index)),
26999                }
27000            }));
27001        }
27002
27003        Ok(CompletionResponse {
27004            completions,
27005            display_options: CompletionDisplayOptions::default(),
27006            is_incomplete,
27007        })
27008    })
27009}
27010
27011impl CompletionProvider for Entity<Project> {
27012    fn completions(
27013        &self,
27014        _excerpt_id: ExcerptId,
27015        buffer: &Entity<Buffer>,
27016        buffer_position: text::Anchor,
27017        options: CompletionContext,
27018        _window: &mut Window,
27019        cx: &mut Context<Editor>,
27020    ) -> Task<Result<Vec<CompletionResponse>>> {
27021        self.update(cx, |project, cx| {
27022            let task = project.completions(buffer, buffer_position, options, cx);
27023            cx.background_spawn(task)
27024        })
27025    }
27026
27027    fn resolve_completions(
27028        &self,
27029        buffer: Entity<Buffer>,
27030        completion_indices: Vec<usize>,
27031        completions: Rc<RefCell<Box<[Completion]>>>,
27032        cx: &mut Context<Editor>,
27033    ) -> Task<Result<bool>> {
27034        self.update(cx, |project, cx| {
27035            project.lsp_store().update(cx, |lsp_store, cx| {
27036                lsp_store.resolve_completions(buffer, completion_indices, completions, cx)
27037            })
27038        })
27039    }
27040
27041    fn apply_additional_edits_for_completion(
27042        &self,
27043        buffer: Entity<Buffer>,
27044        completions: Rc<RefCell<Box<[Completion]>>>,
27045        completion_index: usize,
27046        push_to_history: bool,
27047        cx: &mut Context<Editor>,
27048    ) -> Task<Result<Option<language::Transaction>>> {
27049        self.update(cx, |project, cx| {
27050            project.lsp_store().update(cx, |lsp_store, cx| {
27051                lsp_store.apply_additional_edits_for_completion(
27052                    buffer,
27053                    completions,
27054                    completion_index,
27055                    push_to_history,
27056                    cx,
27057                )
27058            })
27059        })
27060    }
27061
27062    fn is_completion_trigger(
27063        &self,
27064        buffer: &Entity<Buffer>,
27065        position: language::Anchor,
27066        text: &str,
27067        trigger_in_words: bool,
27068        cx: &mut Context<Editor>,
27069    ) -> bool {
27070        let mut chars = text.chars();
27071        let char = if let Some(char) = chars.next() {
27072            char
27073        } else {
27074            return false;
27075        };
27076        if chars.next().is_some() {
27077            return false;
27078        }
27079
27080        let buffer = buffer.read(cx);
27081        let snapshot = buffer.snapshot();
27082        let classifier = snapshot
27083            .char_classifier_at(position)
27084            .scope_context(Some(CharScopeContext::Completion));
27085        if trigger_in_words && classifier.is_word(char) {
27086            return true;
27087        }
27088
27089        buffer.completion_triggers().contains(text)
27090    }
27091
27092    fn show_snippets(&self) -> bool {
27093        true
27094    }
27095}
27096
27097impl SemanticsProvider for Entity<Project> {
27098    fn hover(
27099        &self,
27100        buffer: &Entity<Buffer>,
27101        position: text::Anchor,
27102        cx: &mut App,
27103    ) -> Option<Task<Option<Vec<project::Hover>>>> {
27104        Some(self.update(cx, |project, cx| project.hover(buffer, position, cx)))
27105    }
27106
27107    fn document_highlights(
27108        &self,
27109        buffer: &Entity<Buffer>,
27110        position: text::Anchor,
27111        cx: &mut App,
27112    ) -> Option<Task<Result<Vec<DocumentHighlight>>>> {
27113        Some(self.update(cx, |project, cx| {
27114            project.document_highlights(buffer, position, cx)
27115        }))
27116    }
27117
27118    fn definitions(
27119        &self,
27120        buffer: &Entity<Buffer>,
27121        position: text::Anchor,
27122        kind: GotoDefinitionKind,
27123        cx: &mut App,
27124    ) -> Option<Task<Result<Option<Vec<LocationLink>>>>> {
27125        Some(self.update(cx, |project, cx| match kind {
27126            GotoDefinitionKind::Symbol => project.definitions(buffer, position, cx),
27127            GotoDefinitionKind::Declaration => project.declarations(buffer, position, cx),
27128            GotoDefinitionKind::Type => project.type_definitions(buffer, position, cx),
27129            GotoDefinitionKind::Implementation => project.implementations(buffer, position, cx),
27130        }))
27131    }
27132
27133    fn supports_inlay_hints(&self, buffer: &Entity<Buffer>, cx: &mut App) -> bool {
27134        self.update(cx, |project, cx| {
27135            if project
27136                .active_debug_session(cx)
27137                .is_some_and(|(session, _)| session.read(cx).any_stopped_thread())
27138            {
27139                return true;
27140            }
27141
27142            buffer.update(cx, |buffer, cx| {
27143                project.any_language_server_supports_inlay_hints(buffer, cx)
27144            })
27145        })
27146    }
27147
27148    fn supports_semantic_tokens(&self, buffer: &Entity<Buffer>, cx: &mut App) -> bool {
27149        self.update(cx, |project, cx| {
27150            buffer.update(cx, |buffer, cx| {
27151                project.any_language_server_supports_semantic_tokens(buffer, cx)
27152            })
27153        })
27154    }
27155
27156    fn inline_values(
27157        &self,
27158        buffer_handle: Entity<Buffer>,
27159        range: Range<text::Anchor>,
27160        cx: &mut App,
27161    ) -> Option<Task<anyhow::Result<Vec<InlayHint>>>> {
27162        self.update(cx, |project, cx| {
27163            let (session, active_stack_frame) = project.active_debug_session(cx)?;
27164
27165            Some(project.inline_values(session, active_stack_frame, buffer_handle, range, cx))
27166        })
27167    }
27168
27169    fn applicable_inlay_chunks(
27170        &self,
27171        buffer: &Entity<Buffer>,
27172        ranges: &[Range<text::Anchor>],
27173        cx: &mut App,
27174    ) -> Vec<Range<BufferRow>> {
27175        self.read(cx).lsp_store().update(cx, |lsp_store, cx| {
27176            lsp_store.applicable_inlay_chunks(buffer, ranges, cx)
27177        })
27178    }
27179
27180    fn invalidate_inlay_hints(&self, for_buffers: &HashSet<BufferId>, cx: &mut App) {
27181        self.read(cx).lsp_store().update(cx, |lsp_store, _| {
27182            lsp_store.invalidate_inlay_hints(for_buffers)
27183        });
27184    }
27185
27186    fn inlay_hints(
27187        &self,
27188        invalidate: InvalidationStrategy,
27189        buffer: Entity<Buffer>,
27190        ranges: Vec<Range<text::Anchor>>,
27191        known_chunks: Option<(clock::Global, HashSet<Range<BufferRow>>)>,
27192        cx: &mut App,
27193    ) -> Option<HashMap<Range<BufferRow>, Task<Result<CacheInlayHints>>>> {
27194        Some(self.read(cx).lsp_store().update(cx, |lsp_store, cx| {
27195            lsp_store.inlay_hints(invalidate, buffer, ranges, known_chunks, cx)
27196        }))
27197    }
27198
27199    fn semantic_tokens(
27200        &self,
27201        buffer: Entity<Buffer>,
27202        refresh: Option<RefreshForServer>,
27203        cx: &mut App,
27204    ) -> Shared<Task<std::result::Result<BufferSemanticTokens, Arc<anyhow::Error>>>> {
27205        self.read(cx).lsp_store().update(cx, |lsp_store, cx| {
27206            lsp_store.semantic_tokens(buffer, refresh, cx)
27207        })
27208    }
27209
27210    fn range_for_rename(
27211        &self,
27212        buffer: &Entity<Buffer>,
27213        position: text::Anchor,
27214        cx: &mut App,
27215    ) -> Option<Task<Result<Option<Range<text::Anchor>>>>> {
27216        Some(self.update(cx, |project, cx| {
27217            let buffer = buffer.clone();
27218            let task = project.prepare_rename(buffer.clone(), position, cx);
27219            cx.spawn(async move |_, cx| {
27220                Ok(match task.await? {
27221                    PrepareRenameResponse::Success(range) => Some(range),
27222                    PrepareRenameResponse::InvalidPosition => None,
27223                    PrepareRenameResponse::OnlyUnpreparedRenameSupported => {
27224                        // Fallback on using TreeSitter info to determine identifier range
27225                        buffer.read_with(cx, |buffer, _| {
27226                            let snapshot = buffer.snapshot();
27227                            let (range, kind) = snapshot.surrounding_word(position, None);
27228                            if kind != Some(CharKind::Word) {
27229                                return None;
27230                            }
27231                            Some(
27232                                snapshot.anchor_before(range.start)
27233                                    ..snapshot.anchor_after(range.end),
27234                            )
27235                        })
27236                    }
27237                })
27238            })
27239        }))
27240    }
27241
27242    fn perform_rename(
27243        &self,
27244        buffer: &Entity<Buffer>,
27245        position: text::Anchor,
27246        new_name: String,
27247        cx: &mut App,
27248    ) -> Option<Task<Result<ProjectTransaction>>> {
27249        Some(self.update(cx, |project, cx| {
27250            project.perform_rename(buffer.clone(), position, new_name, cx)
27251        }))
27252    }
27253}
27254
27255fn consume_contiguous_rows(
27256    contiguous_row_selections: &mut Vec<Selection<Point>>,
27257    selection: &Selection<Point>,
27258    display_map: &DisplaySnapshot,
27259    selections: &mut Peekable<std::slice::Iter<Selection<Point>>>,
27260) -> (MultiBufferRow, MultiBufferRow) {
27261    contiguous_row_selections.push(selection.clone());
27262    let start_row = starting_row(selection, display_map);
27263    let mut end_row = ending_row(selection, display_map);
27264
27265    while let Some(next_selection) = selections.peek() {
27266        if next_selection.start.row <= end_row.0 {
27267            end_row = ending_row(next_selection, display_map);
27268            contiguous_row_selections.push(selections.next().unwrap().clone());
27269        } else {
27270            break;
27271        }
27272    }
27273    (start_row, end_row)
27274}
27275
27276fn starting_row(selection: &Selection<Point>, display_map: &DisplaySnapshot) -> MultiBufferRow {
27277    if selection.start.column > 0 {
27278        MultiBufferRow(display_map.prev_line_boundary(selection.start).0.row)
27279    } else {
27280        MultiBufferRow(selection.start.row)
27281    }
27282}
27283
27284fn ending_row(next_selection: &Selection<Point>, display_map: &DisplaySnapshot) -> MultiBufferRow {
27285    if next_selection.end.column > 0 || next_selection.is_empty() {
27286        MultiBufferRow(display_map.next_line_boundary(next_selection.end).0.row + 1)
27287    } else {
27288        MultiBufferRow(next_selection.end.row)
27289    }
27290}
27291
27292impl EditorSnapshot {
27293    pub fn remote_selections_in_range<'a>(
27294        &'a self,
27295        range: &'a Range<Anchor>,
27296        collaboration_hub: &dyn CollaborationHub,
27297        cx: &'a App,
27298    ) -> impl 'a + Iterator<Item = RemoteSelection> {
27299        let participant_names = collaboration_hub.user_names(cx);
27300        let participant_indices = collaboration_hub.user_participant_indices(cx);
27301        let collaborators_by_peer_id = collaboration_hub.collaborators(cx);
27302        let collaborators_by_replica_id = collaborators_by_peer_id
27303            .values()
27304            .map(|collaborator| (collaborator.replica_id, collaborator))
27305            .collect::<HashMap<_, _>>();
27306        self.buffer_snapshot()
27307            .selections_in_range(range, false)
27308            .filter_map(move |(replica_id, line_mode, cursor_shape, selection)| {
27309                if replica_id == ReplicaId::AGENT {
27310                    Some(RemoteSelection {
27311                        replica_id,
27312                        selection,
27313                        cursor_shape,
27314                        line_mode,
27315                        collaborator_id: CollaboratorId::Agent,
27316                        user_name: Some("Agent".into()),
27317                        color: cx.theme().players().agent(),
27318                    })
27319                } else {
27320                    let collaborator = collaborators_by_replica_id.get(&replica_id)?;
27321                    let participant_index = participant_indices.get(&collaborator.user_id).copied();
27322                    let user_name = participant_names.get(&collaborator.user_id).cloned();
27323                    Some(RemoteSelection {
27324                        replica_id,
27325                        selection,
27326                        cursor_shape,
27327                        line_mode,
27328                        collaborator_id: CollaboratorId::PeerId(collaborator.peer_id),
27329                        user_name,
27330                        color: if let Some(index) = participant_index {
27331                            cx.theme().players().color_for_participant(index.0)
27332                        } else {
27333                            cx.theme().players().absent()
27334                        },
27335                    })
27336                }
27337            })
27338    }
27339
27340    pub fn hunks_for_ranges(
27341        &self,
27342        ranges: impl IntoIterator<Item = Range<Point>>,
27343    ) -> Vec<MultiBufferDiffHunk> {
27344        let mut hunks = Vec::new();
27345        let mut processed_buffer_rows: HashMap<BufferId, HashSet<Range<text::Anchor>>> =
27346            HashMap::default();
27347        for query_range in ranges {
27348            let query_rows =
27349                MultiBufferRow(query_range.start.row)..MultiBufferRow(query_range.end.row + 1);
27350            for hunk in self.buffer_snapshot().diff_hunks_in_range(
27351                Point::new(query_rows.start.0, 0)..Point::new(query_rows.end.0, 0),
27352            ) {
27353                // Include deleted hunks that are adjacent to the query range, because
27354                // otherwise they would be missed.
27355                let mut intersects_range = hunk.row_range.overlaps(&query_rows);
27356                if hunk.status().is_deleted() {
27357                    intersects_range |= hunk.row_range.start == query_rows.end;
27358                    intersects_range |= hunk.row_range.end == query_rows.start;
27359                }
27360                if intersects_range {
27361                    if !processed_buffer_rows
27362                        .entry(hunk.buffer_id)
27363                        .or_default()
27364                        .insert(hunk.buffer_range.start..hunk.buffer_range.end)
27365                    {
27366                        continue;
27367                    }
27368                    hunks.push(hunk);
27369                }
27370            }
27371        }
27372
27373        hunks
27374    }
27375
27376    fn display_diff_hunks_for_rows<'a>(
27377        &'a self,
27378        display_rows: Range<DisplayRow>,
27379        folded_buffers: &'a HashSet<BufferId>,
27380    ) -> impl 'a + Iterator<Item = DisplayDiffHunk> {
27381        let buffer_start = DisplayPoint::new(display_rows.start, 0).to_point(self);
27382        let buffer_end = DisplayPoint::new(display_rows.end, 0).to_point(self);
27383
27384        self.buffer_snapshot()
27385            .diff_hunks_in_range(buffer_start..buffer_end)
27386            .filter_map(|hunk| {
27387                if folded_buffers.contains(&hunk.buffer_id)
27388                    || (hunk.row_range.is_empty() && self.buffer.all_diff_hunks_expanded())
27389                {
27390                    return None;
27391                }
27392
27393                let hunk_start_point = Point::new(hunk.row_range.start.0, 0);
27394                let hunk_end_point = if hunk.row_range.end > hunk.row_range.start {
27395                    let last_row = MultiBufferRow(hunk.row_range.end.0 - 1);
27396                    let line_len = self.buffer_snapshot().line_len(last_row);
27397                    Point::new(last_row.0, line_len)
27398                } else {
27399                    Point::new(hunk.row_range.end.0, 0)
27400                };
27401
27402                let hunk_display_start = self.point_to_display_point(hunk_start_point, Bias::Left);
27403                let hunk_display_end = self.point_to_display_point(hunk_end_point, Bias::Right);
27404
27405                let display_hunk = if hunk_display_start.column() != 0 {
27406                    DisplayDiffHunk::Folded {
27407                        display_row: hunk_display_start.row(),
27408                    }
27409                } else {
27410                    let mut end_row = hunk_display_end.row();
27411                    if hunk.row_range.end > hunk.row_range.start || hunk_display_end.column() > 0 {
27412                        end_row.0 += 1;
27413                    }
27414                    let is_created_file = hunk.is_created_file();
27415
27416                    DisplayDiffHunk::Unfolded {
27417                        status: hunk.status(),
27418                        diff_base_byte_range: hunk.diff_base_byte_range.start.0
27419                            ..hunk.diff_base_byte_range.end.0,
27420                        word_diffs: hunk.word_diffs,
27421                        display_row_range: hunk_display_start.row()..end_row,
27422                        multi_buffer_range: Anchor::range_in_buffer(
27423                            hunk.excerpt_id,
27424                            hunk.buffer_range,
27425                        ),
27426                        is_created_file,
27427                    }
27428                };
27429
27430                Some(display_hunk)
27431            })
27432    }
27433
27434    pub fn language_at<T: ToOffset>(&self, position: T) -> Option<&Arc<Language>> {
27435        self.display_snapshot
27436            .buffer_snapshot()
27437            .language_at(position)
27438    }
27439
27440    pub fn is_focused(&self) -> bool {
27441        self.is_focused
27442    }
27443
27444    pub fn placeholder_text(&self) -> Option<String> {
27445        self.placeholder_display_snapshot
27446            .as_ref()
27447            .map(|display_map| display_map.text())
27448    }
27449
27450    pub fn scroll_position(&self) -> gpui::Point<ScrollOffset> {
27451        self.scroll_anchor.scroll_position(&self.display_snapshot)
27452    }
27453
27454    pub fn gutter_dimensions(
27455        &self,
27456        font_id: FontId,
27457        font_size: Pixels,
27458        style: &EditorStyle,
27459        window: &mut Window,
27460        cx: &App,
27461    ) -> GutterDimensions {
27462        if self.show_gutter
27463            && let Some(ch_width) = cx.text_system().ch_width(font_id, font_size).log_err()
27464            && let Some(ch_advance) = cx.text_system().ch_advance(font_id, font_size).log_err()
27465        {
27466            let show_git_gutter = self.show_git_diff_gutter.unwrap_or_else(|| {
27467                matches!(
27468                    ProjectSettings::get_global(cx).git.git_gutter,
27469                    GitGutterSetting::TrackedFiles
27470                )
27471            });
27472            let gutter_settings = EditorSettings::get_global(cx).gutter;
27473            let show_line_numbers = self
27474                .show_line_numbers
27475                .unwrap_or(gutter_settings.line_numbers);
27476            let line_gutter_width = if show_line_numbers {
27477                // Avoid flicker-like gutter resizes when the line number gains another digit by
27478                // only resizing the gutter on files with > 10**min_line_number_digits lines.
27479                let min_width_for_number_on_gutter =
27480                    ch_advance * gutter_settings.min_line_number_digits as f32;
27481                self.max_line_number_width(style, window)
27482                    .max(min_width_for_number_on_gutter)
27483            } else {
27484                0.0.into()
27485            };
27486
27487            let show_runnables = self.show_runnables.unwrap_or(gutter_settings.runnables);
27488            let show_breakpoints = self.show_breakpoints.unwrap_or(gutter_settings.breakpoints);
27489
27490            let git_blame_entries_width =
27491                self.git_blame_gutter_max_author_length
27492                    .map(|max_author_length| {
27493                        let renderer = cx.global::<GlobalBlameRenderer>().0.clone();
27494                        const MAX_RELATIVE_TIMESTAMP: &str = "60 minutes ago";
27495
27496                        /// The number of characters to dedicate to gaps and margins.
27497                        const SPACING_WIDTH: usize = 4;
27498
27499                        let max_char_count = max_author_length.min(renderer.max_author_length())
27500                            + ::git::SHORT_SHA_LENGTH
27501                            + MAX_RELATIVE_TIMESTAMP.len()
27502                            + SPACING_WIDTH;
27503
27504                        ch_advance * max_char_count
27505                    });
27506
27507            let is_singleton = self.buffer_snapshot().is_singleton();
27508
27509            let mut left_padding = git_blame_entries_width.unwrap_or(Pixels::ZERO);
27510            left_padding += if !is_singleton {
27511                ch_width * 4.0
27512            } else if show_runnables || show_breakpoints {
27513                ch_width * 3.0
27514            } else if show_git_gutter && show_line_numbers {
27515                ch_width * 2.0
27516            } else if show_git_gutter || show_line_numbers {
27517                ch_width
27518            } else {
27519                px(0.)
27520            };
27521
27522            let shows_folds = is_singleton && gutter_settings.folds;
27523
27524            let right_padding = if shows_folds && show_line_numbers {
27525                ch_width * 4.0
27526            } else if shows_folds || (!is_singleton && show_line_numbers) {
27527                ch_width * 3.0
27528            } else if show_line_numbers {
27529                ch_width
27530            } else {
27531                px(0.)
27532            };
27533
27534            GutterDimensions {
27535                left_padding,
27536                right_padding,
27537                width: line_gutter_width + left_padding + right_padding,
27538                margin: GutterDimensions::default_gutter_margin(font_id, font_size, cx),
27539                git_blame_entries_width,
27540            }
27541        } else if self.offset_content {
27542            GutterDimensions::default_with_margin(font_id, font_size, cx)
27543        } else {
27544            GutterDimensions::default()
27545        }
27546    }
27547
27548    pub fn render_crease_toggle(
27549        &self,
27550        buffer_row: MultiBufferRow,
27551        row_contains_cursor: bool,
27552        editor: Entity<Editor>,
27553        window: &mut Window,
27554        cx: &mut App,
27555    ) -> Option<AnyElement> {
27556        let folded = self.is_line_folded(buffer_row);
27557        let mut is_foldable = false;
27558
27559        if let Some(crease) = self
27560            .crease_snapshot
27561            .query_row(buffer_row, self.buffer_snapshot())
27562        {
27563            is_foldable = true;
27564            match crease {
27565                Crease::Inline { render_toggle, .. } | Crease::Block { render_toggle, .. } => {
27566                    if let Some(render_toggle) = render_toggle {
27567                        let toggle_callback =
27568                            Arc::new(move |folded, window: &mut Window, cx: &mut App| {
27569                                if folded {
27570                                    editor.update(cx, |editor, cx| {
27571                                        editor.fold_at(buffer_row, window, cx)
27572                                    });
27573                                } else {
27574                                    editor.update(cx, |editor, cx| {
27575                                        editor.unfold_at(buffer_row, window, cx)
27576                                    });
27577                                }
27578                            });
27579                        return Some((render_toggle)(
27580                            buffer_row,
27581                            folded,
27582                            toggle_callback,
27583                            window,
27584                            cx,
27585                        ));
27586                    }
27587                }
27588            }
27589        }
27590
27591        is_foldable |= !self.use_lsp_folding_ranges && self.starts_indent(buffer_row);
27592
27593        if folded || (is_foldable && (row_contains_cursor || self.gutter_hovered)) {
27594            Some(
27595                Disclosure::new(("gutter_crease", buffer_row.0), !folded)
27596                    .toggle_state(folded)
27597                    .on_click(window.listener_for(&editor, move |this, _e, window, cx| {
27598                        if folded {
27599                            this.unfold_at(buffer_row, window, cx);
27600                        } else {
27601                            this.fold_at(buffer_row, window, cx);
27602                        }
27603                    }))
27604                    .into_any_element(),
27605            )
27606        } else {
27607            None
27608        }
27609    }
27610
27611    pub fn render_crease_trailer(
27612        &self,
27613        buffer_row: MultiBufferRow,
27614        window: &mut Window,
27615        cx: &mut App,
27616    ) -> Option<AnyElement> {
27617        let folded = self.is_line_folded(buffer_row);
27618        if let Crease::Inline { render_trailer, .. } = self
27619            .crease_snapshot
27620            .query_row(buffer_row, self.buffer_snapshot())?
27621        {
27622            let render_trailer = render_trailer.as_ref()?;
27623            Some(render_trailer(buffer_row, folded, window, cx))
27624        } else {
27625            None
27626        }
27627    }
27628
27629    pub fn max_line_number_width(&self, style: &EditorStyle, window: &mut Window) -> Pixels {
27630        let digit_count = self.widest_line_number().ilog10() + 1;
27631        column_pixels(style, digit_count as usize, window)
27632    }
27633
27634    /// Returns the line delta from `base` to `line` in the multibuffer, ignoring wrapped lines.
27635    ///
27636    /// This is positive if `base` is before `line`.
27637    fn relative_line_delta(
27638        &self,
27639        current_selection_head: DisplayRow,
27640        first_visible_row: DisplayRow,
27641        consider_wrapped_lines: bool,
27642    ) -> i64 {
27643        let current_selection_head = current_selection_head.as_display_point().to_point(self);
27644        let first_visible_row = first_visible_row.as_display_point().to_point(self);
27645
27646        if consider_wrapped_lines {
27647            let wrap_snapshot = self.wrap_snapshot();
27648            let base_wrap_row = wrap_snapshot
27649                .make_wrap_point(current_selection_head, Bias::Left)
27650                .row();
27651            let wrap_row = wrap_snapshot
27652                .make_wrap_point(first_visible_row, Bias::Left)
27653                .row();
27654
27655            wrap_row.0 as i64 - base_wrap_row.0 as i64
27656        } else {
27657            let fold_snapshot = self.fold_snapshot();
27658            let base_fold_row = fold_snapshot
27659                .to_fold_point(self.to_inlay_point(current_selection_head), Bias::Left)
27660                .row();
27661            let fold_row = fold_snapshot
27662                .to_fold_point(self.to_inlay_point(first_visible_row), Bias::Left)
27663                .row();
27664
27665            fold_row as i64 - base_fold_row as i64
27666        }
27667    }
27668
27669    /// Returns the unsigned relative line number to display for each row in `rows`.
27670    ///
27671    /// Wrapped rows are excluded from the hashmap if `count_relative_lines` is `false`.
27672    pub fn calculate_relative_line_numbers(
27673        &self,
27674        rows: &Range<DisplayRow>,
27675        current_selection_head: DisplayRow,
27676        count_wrapped_lines: bool,
27677    ) -> HashMap<DisplayRow, u32> {
27678        let initial_offset =
27679            self.relative_line_delta(current_selection_head, rows.start, count_wrapped_lines);
27680
27681        self.row_infos(rows.start)
27682            .take(rows.len())
27683            .enumerate()
27684            .map(|(i, row_info)| (DisplayRow(rows.start.0 + i as u32), row_info))
27685            .filter(|(_row, row_info)| {
27686                row_info.buffer_row.is_some()
27687                    || (count_wrapped_lines && row_info.wrapped_buffer_row.is_some())
27688            })
27689            .enumerate()
27690            .filter_map(|(i, (row, row_info))| {
27691                // We want to ensure here that the current line has absolute
27692                // numbering, even if we are in a soft-wrapped line. With the
27693                // exception that if we are in a deleted line, we should number this
27694                // relative with 0, as otherwise it would have no line number at all
27695                let relative_line_number = (initial_offset + i as i64).unsigned_abs() as u32;
27696
27697                (relative_line_number != 0
27698                    || row_info
27699                        .diff_status
27700                        .is_some_and(|status| status.is_deleted()))
27701                .then_some((row, relative_line_number))
27702            })
27703            .collect()
27704    }
27705}
27706
27707pub fn column_pixels(style: &EditorStyle, column: usize, window: &Window) -> Pixels {
27708    let font_size = style.text.font_size.to_pixels(window.rem_size());
27709    let layout = window.text_system().shape_line(
27710        SharedString::from(" ".repeat(column)),
27711        font_size,
27712        &[TextRun {
27713            len: column,
27714            font: style.text.font(),
27715            color: Hsla::default(),
27716            ..Default::default()
27717        }],
27718        None,
27719    );
27720
27721    layout.width
27722}
27723
27724impl Deref for EditorSnapshot {
27725    type Target = DisplaySnapshot;
27726
27727    fn deref(&self) -> &Self::Target {
27728        &self.display_snapshot
27729    }
27730}
27731
27732#[derive(Clone, Debug, PartialEq, Eq)]
27733pub enum EditorEvent {
27734    /// Emitted when the stored review comments change (added, removed, or updated).
27735    ReviewCommentsChanged {
27736        /// The new total count of review comments.
27737        total_count: usize,
27738    },
27739    InputIgnored {
27740        text: Arc<str>,
27741    },
27742    InputHandled {
27743        utf16_range_to_replace: Option<Range<isize>>,
27744        text: Arc<str>,
27745    },
27746    ExcerptsAdded {
27747        buffer: Entity<Buffer>,
27748        predecessor: ExcerptId,
27749        excerpts: Vec<(ExcerptId, ExcerptRange<language::Anchor>)>,
27750    },
27751    ExcerptsRemoved {
27752        ids: Vec<ExcerptId>,
27753        removed_buffer_ids: Vec<BufferId>,
27754    },
27755    BufferFoldToggled {
27756        ids: Vec<ExcerptId>,
27757        folded: bool,
27758    },
27759    ExcerptsEdited {
27760        ids: Vec<ExcerptId>,
27761    },
27762    ExcerptsExpanded {
27763        ids: Vec<ExcerptId>,
27764    },
27765    ExpandExcerptsRequested {
27766        excerpt_ids: Vec<ExcerptId>,
27767        lines: u32,
27768        direction: ExpandExcerptDirection,
27769    },
27770    StageOrUnstageRequested {
27771        stage: bool,
27772        hunks: Vec<MultiBufferDiffHunk>,
27773    },
27774    OpenExcerptsRequested {
27775        selections_by_buffer: HashMap<BufferId, (Vec<Range<BufferOffset>>, Option<u32>)>,
27776        split: bool,
27777    },
27778    RestoreRequested {
27779        hunks: Vec<MultiBufferDiffHunk>,
27780    },
27781    BufferEdited,
27782    Edited {
27783        transaction_id: clock::Lamport,
27784    },
27785    Reparsed(BufferId),
27786    Focused,
27787    FocusedIn,
27788    Blurred,
27789    DirtyChanged,
27790    Saved,
27791    TitleChanged,
27792    SelectionsChanged {
27793        local: bool,
27794    },
27795    ScrollPositionChanged {
27796        local: bool,
27797        autoscroll: bool,
27798    },
27799    TransactionUndone {
27800        transaction_id: clock::Lamport,
27801    },
27802    TransactionBegun {
27803        transaction_id: clock::Lamport,
27804    },
27805    CursorShapeChanged,
27806    BreadcrumbsChanged,
27807    OutlineSymbolsChanged,
27808    PushedToNavHistory {
27809        anchor: Anchor,
27810        is_deactivate: bool,
27811    },
27812}
27813
27814impl EventEmitter<EditorEvent> for Editor {}
27815
27816impl Focusable for Editor {
27817    fn focus_handle(&self, _cx: &App) -> FocusHandle {
27818        self.focus_handle.clone()
27819    }
27820}
27821
27822impl Render for Editor {
27823    fn render(&mut self, _: &mut Window, cx: &mut Context<Self>) -> impl IntoElement {
27824        EditorElement::new(&cx.entity(), self.create_style(cx))
27825    }
27826}
27827
27828impl EntityInputHandler for Editor {
27829    fn text_for_range(
27830        &mut self,
27831        range_utf16: Range<usize>,
27832        adjusted_range: &mut Option<Range<usize>>,
27833        _: &mut Window,
27834        cx: &mut Context<Self>,
27835    ) -> Option<String> {
27836        let snapshot = self.buffer.read(cx).read(cx);
27837        let start = snapshot.clip_offset_utf16(
27838            MultiBufferOffsetUtf16(OffsetUtf16(range_utf16.start)),
27839            Bias::Left,
27840        );
27841        let end = snapshot.clip_offset_utf16(
27842            MultiBufferOffsetUtf16(OffsetUtf16(range_utf16.end)),
27843            Bias::Right,
27844        );
27845        if (start.0.0..end.0.0) != range_utf16 {
27846            adjusted_range.replace(start.0.0..end.0.0);
27847        }
27848        Some(snapshot.text_for_range(start..end).collect())
27849    }
27850
27851    fn selected_text_range(
27852        &mut self,
27853        ignore_disabled_input: bool,
27854        _: &mut Window,
27855        cx: &mut Context<Self>,
27856    ) -> Option<UTF16Selection> {
27857        // Prevent the IME menu from appearing when holding down an alphabetic key
27858        // while input is disabled.
27859        if !ignore_disabled_input && !self.input_enabled {
27860            return None;
27861        }
27862
27863        let selection = self
27864            .selections
27865            .newest::<MultiBufferOffsetUtf16>(&self.display_snapshot(cx));
27866        let range = selection.range();
27867
27868        Some(UTF16Selection {
27869            range: range.start.0.0..range.end.0.0,
27870            reversed: selection.reversed,
27871        })
27872    }
27873
27874    fn marked_text_range(&self, _: &mut Window, cx: &mut Context<Self>) -> Option<Range<usize>> {
27875        let snapshot = self.buffer.read(cx).read(cx);
27876        let range = self
27877            .text_highlights(HighlightKey::InputComposition, cx)?
27878            .1
27879            .first()?;
27880        Some(range.start.to_offset_utf16(&snapshot).0.0..range.end.to_offset_utf16(&snapshot).0.0)
27881    }
27882
27883    fn unmark_text(&mut self, _: &mut Window, cx: &mut Context<Self>) {
27884        self.clear_highlights(HighlightKey::InputComposition, cx);
27885        self.ime_transaction.take();
27886    }
27887
27888    fn replace_text_in_range(
27889        &mut self,
27890        range_utf16: Option<Range<usize>>,
27891        text: &str,
27892        window: &mut Window,
27893        cx: &mut Context<Self>,
27894    ) {
27895        if !self.input_enabled {
27896            cx.emit(EditorEvent::InputIgnored { text: text.into() });
27897            return;
27898        }
27899
27900        self.transact(window, cx, |this, window, cx| {
27901            let new_selected_ranges = if let Some(range_utf16) = range_utf16 {
27902                let range_utf16 = MultiBufferOffsetUtf16(OffsetUtf16(range_utf16.start))
27903                    ..MultiBufferOffsetUtf16(OffsetUtf16(range_utf16.end));
27904                Some(this.selection_replacement_ranges(range_utf16, cx))
27905            } else {
27906                this.marked_text_ranges(cx)
27907            };
27908
27909            let range_to_replace = new_selected_ranges.as_ref().and_then(|ranges_to_replace| {
27910                let newest_selection_id = this.selections.newest_anchor().id;
27911                this.selections
27912                    .all::<MultiBufferOffsetUtf16>(&this.display_snapshot(cx))
27913                    .iter()
27914                    .zip(ranges_to_replace.iter())
27915                    .find_map(|(selection, range)| {
27916                        if selection.id == newest_selection_id {
27917                            Some(
27918                                (range.start.0.0 as isize - selection.head().0.0 as isize)
27919                                    ..(range.end.0.0 as isize - selection.head().0.0 as isize),
27920                            )
27921                        } else {
27922                            None
27923                        }
27924                    })
27925            });
27926
27927            cx.emit(EditorEvent::InputHandled {
27928                utf16_range_to_replace: range_to_replace,
27929                text: text.into(),
27930            });
27931
27932            if let Some(new_selected_ranges) = new_selected_ranges {
27933                this.change_selections(SelectionEffects::no_scroll(), window, cx, |selections| {
27934                    selections.select_ranges(new_selected_ranges)
27935                });
27936                this.backspace(&Default::default(), window, cx);
27937            }
27938
27939            this.handle_input(text, window, cx);
27940        });
27941
27942        if let Some(transaction) = self.ime_transaction {
27943            self.buffer.update(cx, |buffer, cx| {
27944                buffer.group_until_transaction(transaction, cx);
27945            });
27946        }
27947
27948        self.unmark_text(window, cx);
27949    }
27950
27951    fn replace_and_mark_text_in_range(
27952        &mut self,
27953        range_utf16: Option<Range<usize>>,
27954        text: &str,
27955        new_selected_range_utf16: Option<Range<usize>>,
27956        window: &mut Window,
27957        cx: &mut Context<Self>,
27958    ) {
27959        if !self.input_enabled {
27960            return;
27961        }
27962
27963        let transaction = self.transact(window, cx, |this, window, cx| {
27964            let ranges_to_replace = if let Some(mut marked_ranges) = this.marked_text_ranges(cx) {
27965                let snapshot = this.buffer.read(cx).read(cx);
27966                if let Some(relative_range_utf16) = range_utf16.as_ref() {
27967                    for marked_range in &mut marked_ranges {
27968                        marked_range.end = marked_range.start + relative_range_utf16.end;
27969                        marked_range.start += relative_range_utf16.start;
27970                        marked_range.start =
27971                            snapshot.clip_offset_utf16(marked_range.start, Bias::Left);
27972                        marked_range.end =
27973                            snapshot.clip_offset_utf16(marked_range.end, Bias::Right);
27974                    }
27975                }
27976                Some(marked_ranges)
27977            } else if let Some(range_utf16) = range_utf16 {
27978                let range_utf16 = MultiBufferOffsetUtf16(OffsetUtf16(range_utf16.start))
27979                    ..MultiBufferOffsetUtf16(OffsetUtf16(range_utf16.end));
27980                Some(this.selection_replacement_ranges(range_utf16, cx))
27981            } else {
27982                None
27983            };
27984
27985            let range_to_replace = ranges_to_replace.as_ref().and_then(|ranges_to_replace| {
27986                let newest_selection_id = this.selections.newest_anchor().id;
27987                this.selections
27988                    .all::<MultiBufferOffsetUtf16>(&this.display_snapshot(cx))
27989                    .iter()
27990                    .zip(ranges_to_replace.iter())
27991                    .find_map(|(selection, range)| {
27992                        if selection.id == newest_selection_id {
27993                            Some(
27994                                (range.start.0.0 as isize - selection.head().0.0 as isize)
27995                                    ..(range.end.0.0 as isize - selection.head().0.0 as isize),
27996                            )
27997                        } else {
27998                            None
27999                        }
28000                    })
28001            });
28002
28003            cx.emit(EditorEvent::InputHandled {
28004                utf16_range_to_replace: range_to_replace,
28005                text: text.into(),
28006            });
28007
28008            if let Some(ranges) = ranges_to_replace {
28009                this.change_selections(SelectionEffects::no_scroll(), window, cx, |s| {
28010                    s.select_ranges(ranges)
28011                });
28012            }
28013
28014            let marked_ranges = {
28015                let snapshot = this.buffer.read(cx).read(cx);
28016                this.selections
28017                    .disjoint_anchors_arc()
28018                    .iter()
28019                    .map(|selection| {
28020                        selection.start.bias_left(&snapshot)..selection.end.bias_right(&snapshot)
28021                    })
28022                    .collect::<Vec<_>>()
28023            };
28024
28025            if text.is_empty() {
28026                this.unmark_text(window, cx);
28027            } else {
28028                this.highlight_text(
28029                    HighlightKey::InputComposition,
28030                    marked_ranges.clone(),
28031                    HighlightStyle {
28032                        underline: Some(UnderlineStyle {
28033                            thickness: px(1.),
28034                            color: None,
28035                            wavy: false,
28036                        }),
28037                        ..Default::default()
28038                    },
28039                    cx,
28040                );
28041            }
28042
28043            // Disable auto-closing when composing text (i.e. typing a `"` on a Brazilian keyboard)
28044            let use_autoclose = this.use_autoclose;
28045            let use_auto_surround = this.use_auto_surround;
28046            this.set_use_autoclose(false);
28047            this.set_use_auto_surround(false);
28048            this.handle_input(text, window, cx);
28049            this.set_use_autoclose(use_autoclose);
28050            this.set_use_auto_surround(use_auto_surround);
28051
28052            if let Some(new_selected_range) = new_selected_range_utf16 {
28053                let snapshot = this.buffer.read(cx).read(cx);
28054                let new_selected_ranges = marked_ranges
28055                    .into_iter()
28056                    .map(|marked_range| {
28057                        let insertion_start = marked_range.start.to_offset_utf16(&snapshot).0;
28058                        let new_start = MultiBufferOffsetUtf16(OffsetUtf16(
28059                            insertion_start.0 + new_selected_range.start,
28060                        ));
28061                        let new_end = MultiBufferOffsetUtf16(OffsetUtf16(
28062                            insertion_start.0 + new_selected_range.end,
28063                        ));
28064                        snapshot.clip_offset_utf16(new_start, Bias::Left)
28065                            ..snapshot.clip_offset_utf16(new_end, Bias::Right)
28066                    })
28067                    .collect::<Vec<_>>();
28068
28069                drop(snapshot);
28070                this.change_selections(SelectionEffects::no_scroll(), window, cx, |selections| {
28071                    selections.select_ranges(new_selected_ranges)
28072                });
28073            }
28074        });
28075
28076        self.ime_transaction = self.ime_transaction.or(transaction);
28077        if let Some(transaction) = self.ime_transaction {
28078            self.buffer.update(cx, |buffer, cx| {
28079                buffer.group_until_transaction(transaction, cx);
28080            });
28081        }
28082
28083        if self
28084            .text_highlights(HighlightKey::InputComposition, cx)
28085            .is_none()
28086        {
28087            self.ime_transaction.take();
28088        }
28089    }
28090
28091    fn bounds_for_range(
28092        &mut self,
28093        range_utf16: Range<usize>,
28094        element_bounds: gpui::Bounds<Pixels>,
28095        window: &mut Window,
28096        cx: &mut Context<Self>,
28097    ) -> Option<gpui::Bounds<Pixels>> {
28098        let text_layout_details = self.text_layout_details(window, cx);
28099        let CharacterDimensions {
28100            em_width,
28101            em_advance,
28102            line_height,
28103        } = self.character_dimensions(window, cx);
28104
28105        let snapshot = self.snapshot(window, cx);
28106        let scroll_position = snapshot.scroll_position();
28107        let scroll_left = scroll_position.x * ScrollOffset::from(em_advance);
28108
28109        let start =
28110            MultiBufferOffsetUtf16(OffsetUtf16(range_utf16.start)).to_display_point(&snapshot);
28111        let x = Pixels::from(
28112            ScrollOffset::from(
28113                snapshot.x_for_display_point(start, &text_layout_details)
28114                    + self.gutter_dimensions.full_width(),
28115            ) - scroll_left,
28116        );
28117        let y = line_height * (start.row().as_f64() - scroll_position.y) as f32;
28118
28119        Some(Bounds {
28120            origin: element_bounds.origin + point(x, y),
28121            size: size(em_width, line_height),
28122        })
28123    }
28124
28125    fn character_index_for_point(
28126        &mut self,
28127        point: gpui::Point<Pixels>,
28128        _window: &mut Window,
28129        _cx: &mut Context<Self>,
28130    ) -> Option<usize> {
28131        let position_map = self.last_position_map.as_ref()?;
28132        if !position_map.text_hitbox.contains(&point) {
28133            return None;
28134        }
28135        let display_point = position_map.point_for_position(point).previous_valid;
28136        let anchor = position_map
28137            .snapshot
28138            .display_point_to_anchor(display_point, Bias::Left);
28139        let utf16_offset = anchor.to_offset_utf16(&position_map.snapshot.buffer_snapshot());
28140        Some(utf16_offset.0.0)
28141    }
28142
28143    fn accepts_text_input(&self, _window: &mut Window, _cx: &mut Context<Self>) -> bool {
28144        self.input_enabled
28145    }
28146}
28147
28148trait SelectionExt {
28149    fn display_range(&self, map: &DisplaySnapshot) -> Range<DisplayPoint>;
28150    fn spanned_rows(
28151        &self,
28152        include_end_if_at_line_start: bool,
28153        map: &DisplaySnapshot,
28154    ) -> Range<MultiBufferRow>;
28155}
28156
28157impl<T: ToPoint + ToOffset> SelectionExt for Selection<T> {
28158    fn display_range(&self, map: &DisplaySnapshot) -> Range<DisplayPoint> {
28159        let start = self
28160            .start
28161            .to_point(map.buffer_snapshot())
28162            .to_display_point(map);
28163        let end = self
28164            .end
28165            .to_point(map.buffer_snapshot())
28166            .to_display_point(map);
28167        if self.reversed {
28168            end..start
28169        } else {
28170            start..end
28171        }
28172    }
28173
28174    fn spanned_rows(
28175        &self,
28176        include_end_if_at_line_start: bool,
28177        map: &DisplaySnapshot,
28178    ) -> Range<MultiBufferRow> {
28179        let start = self.start.to_point(map.buffer_snapshot());
28180        let mut end = self.end.to_point(map.buffer_snapshot());
28181        if !include_end_if_at_line_start && start.row != end.row && end.column == 0 {
28182            end.row -= 1;
28183        }
28184
28185        let buffer_start = map.prev_line_boundary(start).0;
28186        let buffer_end = map.next_line_boundary(end).0;
28187        MultiBufferRow(buffer_start.row)..MultiBufferRow(buffer_end.row + 1)
28188    }
28189}
28190
28191impl<T: InvalidationRegion> InvalidationStack<T> {
28192    fn invalidate<S>(&mut self, selections: &[Selection<S>], buffer: &MultiBufferSnapshot)
28193    where
28194        S: Clone + ToOffset,
28195    {
28196        while let Some(region) = self.last() {
28197            let all_selections_inside_invalidation_ranges =
28198                if selections.len() == region.ranges().len() {
28199                    selections
28200                        .iter()
28201                        .zip(region.ranges().iter().map(|r| r.to_offset(buffer)))
28202                        .all(|(selection, invalidation_range)| {
28203                            let head = selection.head().to_offset(buffer);
28204                            invalidation_range.start <= head && invalidation_range.end >= head
28205                        })
28206                } else {
28207                    false
28208                };
28209
28210            if all_selections_inside_invalidation_ranges {
28211                break;
28212            } else {
28213                self.pop();
28214            }
28215        }
28216    }
28217}
28218
28219#[derive(Clone)]
28220struct ErasedEditorImpl(Entity<Editor>);
28221
28222impl ui_input::ErasedEditor for ErasedEditorImpl {
28223    fn text(&self, cx: &App) -> String {
28224        self.0.read(cx).text(cx)
28225    }
28226
28227    fn set_text(&self, text: &str, window: &mut Window, cx: &mut App) {
28228        self.0.update(cx, |this, cx| {
28229            this.set_text(text, window, cx);
28230        })
28231    }
28232
28233    fn clear(&self, window: &mut Window, cx: &mut App) {
28234        self.0.update(cx, |this, cx| this.clear(window, cx));
28235    }
28236
28237    fn set_placeholder_text(&self, text: &str, window: &mut Window, cx: &mut App) {
28238        self.0.update(cx, |this, cx| {
28239            this.set_placeholder_text(text, window, cx);
28240        });
28241    }
28242
28243    fn focus_handle(&self, cx: &App) -> FocusHandle {
28244        self.0.read(cx).focus_handle(cx)
28245    }
28246
28247    fn render(&self, _: &mut Window, cx: &App) -> AnyElement {
28248        let settings = ThemeSettings::get_global(cx);
28249        let theme_color = cx.theme().colors();
28250
28251        let text_style = TextStyle {
28252            font_family: settings.ui_font.family.clone(),
28253            font_features: settings.ui_font.features.clone(),
28254            font_size: rems(0.875).into(),
28255            font_weight: settings.buffer_font.weight,
28256            font_style: FontStyle::Normal,
28257            line_height: relative(1.2),
28258            color: theme_color.text,
28259            ..Default::default()
28260        };
28261        let editor_style = EditorStyle {
28262            background: theme_color.ghost_element_background,
28263            local_player: cx.theme().players().local(),
28264            syntax: cx.theme().syntax().clone(),
28265            text: text_style,
28266            ..Default::default()
28267        };
28268        EditorElement::new(&self.0, editor_style).into_any()
28269    }
28270
28271    fn as_any(&self) -> &dyn Any {
28272        &self.0
28273    }
28274
28275    fn move_selection_to_end(&self, window: &mut Window, cx: &mut App) {
28276        self.0.update(cx, |editor, cx| {
28277            let editor_offset = editor.buffer().read(cx).len(cx);
28278            editor.change_selections(
28279                SelectionEffects::scroll(Autoscroll::Next),
28280                window,
28281                cx,
28282                |s| s.select_ranges(Some(editor_offset..editor_offset)),
28283            );
28284        });
28285    }
28286
28287    fn subscribe(
28288        &self,
28289        mut callback: Box<dyn FnMut(ui_input::ErasedEditorEvent, &mut Window, &mut App) + 'static>,
28290        window: &mut Window,
28291        cx: &mut App,
28292    ) -> Subscription {
28293        window.subscribe(&self.0, cx, move |_, event: &EditorEvent, window, cx| {
28294            let event = match event {
28295                EditorEvent::BufferEdited => ui_input::ErasedEditorEvent::BufferEdited,
28296                EditorEvent::Blurred => ui_input::ErasedEditorEvent::Blurred,
28297                _ => return,
28298            };
28299            (callback)(event, window, cx);
28300        })
28301    }
28302
28303    fn set_masked(&self, masked: bool, _window: &mut Window, cx: &mut App) {
28304        self.0.update(cx, |editor, cx| {
28305            editor.set_masked(masked, cx);
28306        });
28307    }
28308}
28309impl<T> Default for InvalidationStack<T> {
28310    fn default() -> Self {
28311        Self(Default::default())
28312    }
28313}
28314
28315impl<T> Deref for InvalidationStack<T> {
28316    type Target = Vec<T>;
28317
28318    fn deref(&self) -> &Self::Target {
28319        &self.0
28320    }
28321}
28322
28323impl<T> DerefMut for InvalidationStack<T> {
28324    fn deref_mut(&mut self) -> &mut Self::Target {
28325        &mut self.0
28326    }
28327}
28328
28329impl InvalidationRegion for SnippetState {
28330    fn ranges(&self) -> &[Range<Anchor>] {
28331        &self.ranges[self.active_index]
28332    }
28333}
28334
28335fn edit_prediction_edit_text(
28336    current_snapshot: &BufferSnapshot,
28337    edits: &[(Range<Anchor>, impl AsRef<str>)],
28338    edit_preview: &EditPreview,
28339    include_deletions: bool,
28340    cx: &App,
28341) -> HighlightedText {
28342    let edits = edits
28343        .iter()
28344        .map(|(anchor, text)| (anchor.start.text_anchor..anchor.end.text_anchor, text))
28345        .collect::<Vec<_>>();
28346
28347    edit_preview.highlight_edits(current_snapshot, &edits, include_deletions, cx)
28348}
28349
28350fn edit_prediction_fallback_text(edits: &[(Range<Anchor>, Arc<str>)], cx: &App) -> HighlightedText {
28351    // Fallback for providers that don't provide edit_preview (like Copilot/Supermaven)
28352    // Just show the raw edit text with basic styling
28353    let mut text = String::new();
28354    let mut highlights = Vec::new();
28355
28356    let insertion_highlight_style = HighlightStyle {
28357        color: Some(cx.theme().colors().text),
28358        ..Default::default()
28359    };
28360
28361    for (_, edit_text) in edits {
28362        let start_offset = text.len();
28363        text.push_str(edit_text);
28364        let end_offset = text.len();
28365
28366        if start_offset < end_offset {
28367            highlights.push((start_offset..end_offset, insertion_highlight_style));
28368        }
28369    }
28370
28371    HighlightedText {
28372        text: text.into(),
28373        highlights,
28374    }
28375}
28376
28377pub fn diagnostic_style(severity: lsp::DiagnosticSeverity, colors: &StatusColors) -> Hsla {
28378    match severity {
28379        lsp::DiagnosticSeverity::ERROR => colors.error,
28380        lsp::DiagnosticSeverity::WARNING => colors.warning,
28381        lsp::DiagnosticSeverity::INFORMATION => colors.info,
28382        lsp::DiagnosticSeverity::HINT => colors.info,
28383        _ => colors.ignored,
28384    }
28385}
28386
28387pub fn styled_runs_for_code_label<'a>(
28388    label: &'a CodeLabel,
28389    syntax_theme: &'a theme::SyntaxTheme,
28390    local_player: &'a theme::PlayerColor,
28391) -> impl 'a + Iterator<Item = (Range<usize>, HighlightStyle)> {
28392    let fade_out = HighlightStyle {
28393        fade_out: Some(0.35),
28394        ..Default::default()
28395    };
28396
28397    let mut prev_end = label.filter_range.end;
28398    label
28399        .runs
28400        .iter()
28401        .enumerate()
28402        .flat_map(move |(ix, (range, highlight_id))| {
28403            let style = if *highlight_id == language::HighlightId::TABSTOP_INSERT_ID {
28404                HighlightStyle {
28405                    color: Some(local_player.cursor),
28406                    ..Default::default()
28407                }
28408            } else if *highlight_id == language::HighlightId::TABSTOP_REPLACE_ID {
28409                HighlightStyle {
28410                    background_color: Some(local_player.selection),
28411                    ..Default::default()
28412                }
28413            } else if let Some(style) = highlight_id.style(syntax_theme) {
28414                style
28415            } else {
28416                return Default::default();
28417            };
28418            let muted_style = style.highlight(fade_out);
28419
28420            let mut runs = SmallVec::<[(Range<usize>, HighlightStyle); 3]>::new();
28421            if range.start >= label.filter_range.end {
28422                if range.start > prev_end {
28423                    runs.push((prev_end..range.start, fade_out));
28424                }
28425                runs.push((range.clone(), muted_style));
28426            } else if range.end <= label.filter_range.end {
28427                runs.push((range.clone(), style));
28428            } else {
28429                runs.push((range.start..label.filter_range.end, style));
28430                runs.push((label.filter_range.end..range.end, muted_style));
28431            }
28432            prev_end = cmp::max(prev_end, range.end);
28433
28434            if ix + 1 == label.runs.len() && label.text.len() > prev_end {
28435                runs.push((prev_end..label.text.len(), fade_out));
28436            }
28437
28438            runs
28439        })
28440}
28441
28442pub(crate) fn split_words(text: &str) -> impl std::iter::Iterator<Item = &str> + '_ {
28443    let mut prev_index = 0;
28444    let mut prev_codepoint: Option<char> = None;
28445    text.char_indices()
28446        .chain([(text.len(), '\0')])
28447        .filter_map(move |(index, codepoint)| {
28448            let prev_codepoint = prev_codepoint.replace(codepoint)?;
28449            let is_boundary = index == text.len()
28450                || !prev_codepoint.is_uppercase() && codepoint.is_uppercase()
28451                || !prev_codepoint.is_alphanumeric() && codepoint.is_alphanumeric();
28452            if is_boundary {
28453                let chunk = &text[prev_index..index];
28454                prev_index = index;
28455                Some(chunk)
28456            } else {
28457                None
28458            }
28459        })
28460}
28461
28462/// Given a string of text immediately before the cursor, iterates over possible
28463/// strings a snippet could match to. More precisely: returns an iterator over
28464/// suffixes of `text` created by splitting at word boundaries (before & after
28465/// every non-word character).
28466///
28467/// Shorter suffixes are returned first.
28468pub(crate) fn snippet_candidate_suffixes(
28469    text: &str,
28470    is_word_char: impl Fn(char) -> bool,
28471) -> impl std::iter::Iterator<Item = &str> {
28472    let mut prev_index = text.len();
28473    let mut prev_codepoint = None;
28474    text.char_indices()
28475        .rev()
28476        .chain([(0, '\0')])
28477        .filter_map(move |(index, codepoint)| {
28478            let prev_index = std::mem::replace(&mut prev_index, index);
28479            let prev_codepoint = prev_codepoint.replace(codepoint)?;
28480            if is_word_char(prev_codepoint) && is_word_char(codepoint) {
28481                None
28482            } else {
28483                let chunk = &text[prev_index..]; // go to end of string
28484                Some(chunk)
28485            }
28486        })
28487}
28488
28489pub trait RangeToAnchorExt: Sized {
28490    fn to_anchors(self, snapshot: &MultiBufferSnapshot) -> Range<Anchor>;
28491
28492    fn to_display_points(self, snapshot: &EditorSnapshot) -> Range<DisplayPoint> {
28493        let anchor_range = self.to_anchors(&snapshot.buffer_snapshot());
28494        anchor_range.start.to_display_point(snapshot)..anchor_range.end.to_display_point(snapshot)
28495    }
28496}
28497
28498impl<T: ToOffset> RangeToAnchorExt for Range<T> {
28499    fn to_anchors(self, snapshot: &MultiBufferSnapshot) -> Range<Anchor> {
28500        let start_offset = self.start.to_offset(snapshot);
28501        let end_offset = self.end.to_offset(snapshot);
28502        if start_offset == end_offset {
28503            snapshot.anchor_before(start_offset)..snapshot.anchor_before(end_offset)
28504        } else {
28505            snapshot.anchor_after(self.start)..snapshot.anchor_before(self.end)
28506        }
28507    }
28508}
28509
28510pub trait RowExt {
28511    fn as_f64(&self) -> f64;
28512
28513    fn next_row(&self) -> Self;
28514
28515    fn previous_row(&self) -> Self;
28516
28517    fn minus(&self, other: Self) -> u32;
28518}
28519
28520impl RowExt for DisplayRow {
28521    fn as_f64(&self) -> f64 {
28522        self.0 as _
28523    }
28524
28525    fn next_row(&self) -> Self {
28526        Self(self.0 + 1)
28527    }
28528
28529    fn previous_row(&self) -> Self {
28530        Self(self.0.saturating_sub(1))
28531    }
28532
28533    fn minus(&self, other: Self) -> u32 {
28534        self.0 - other.0
28535    }
28536}
28537
28538impl RowExt for MultiBufferRow {
28539    fn as_f64(&self) -> f64 {
28540        self.0 as _
28541    }
28542
28543    fn next_row(&self) -> Self {
28544        Self(self.0 + 1)
28545    }
28546
28547    fn previous_row(&self) -> Self {
28548        Self(self.0.saturating_sub(1))
28549    }
28550
28551    fn minus(&self, other: Self) -> u32 {
28552        self.0 - other.0
28553    }
28554}
28555
28556trait RowRangeExt {
28557    type Row;
28558
28559    fn len(&self) -> usize;
28560
28561    fn iter_rows(&self) -> impl DoubleEndedIterator<Item = Self::Row>;
28562}
28563
28564impl RowRangeExt for Range<MultiBufferRow> {
28565    type Row = MultiBufferRow;
28566
28567    fn len(&self) -> usize {
28568        (self.end.0 - self.start.0) as usize
28569    }
28570
28571    fn iter_rows(&self) -> impl DoubleEndedIterator<Item = MultiBufferRow> {
28572        (self.start.0..self.end.0).map(MultiBufferRow)
28573    }
28574}
28575
28576impl RowRangeExt for Range<DisplayRow> {
28577    type Row = DisplayRow;
28578
28579    fn len(&self) -> usize {
28580        (self.end.0 - self.start.0) as usize
28581    }
28582
28583    fn iter_rows(&self) -> impl DoubleEndedIterator<Item = DisplayRow> {
28584        (self.start.0..self.end.0).map(DisplayRow)
28585    }
28586}
28587
28588/// If select range has more than one line, we
28589/// just point the cursor to range.start.
28590fn collapse_multiline_range(range: Range<Point>) -> Range<Point> {
28591    if range.start.row == range.end.row {
28592        range
28593    } else {
28594        range.start..range.start
28595    }
28596}
28597pub struct KillRing(ClipboardItem);
28598impl Global for KillRing {}
28599
28600const UPDATE_DEBOUNCE: Duration = Duration::from_millis(50);
28601
28602enum BreakpointPromptEditAction {
28603    Log,
28604    Condition,
28605    HitCondition,
28606}
28607
28608struct BreakpointPromptEditor {
28609    pub(crate) prompt: Entity<Editor>,
28610    editor: WeakEntity<Editor>,
28611    breakpoint_anchor: Anchor,
28612    breakpoint: Breakpoint,
28613    edit_action: BreakpointPromptEditAction,
28614    block_ids: HashSet<CustomBlockId>,
28615    editor_margins: Arc<Mutex<EditorMargins>>,
28616    _subscriptions: Vec<Subscription>,
28617}
28618
28619impl BreakpointPromptEditor {
28620    const MAX_LINES: u8 = 4;
28621
28622    fn new(
28623        editor: WeakEntity<Editor>,
28624        breakpoint_anchor: Anchor,
28625        breakpoint: Breakpoint,
28626        edit_action: BreakpointPromptEditAction,
28627        window: &mut Window,
28628        cx: &mut Context<Self>,
28629    ) -> Self {
28630        let base_text = match edit_action {
28631            BreakpointPromptEditAction::Log => breakpoint.message.as_ref(),
28632            BreakpointPromptEditAction::Condition => breakpoint.condition.as_ref(),
28633            BreakpointPromptEditAction::HitCondition => breakpoint.hit_condition.as_ref(),
28634        }
28635        .map(|msg| msg.to_string())
28636        .unwrap_or_default();
28637
28638        let buffer = cx.new(|cx| Buffer::local(base_text, cx));
28639        let buffer = cx.new(|cx| MultiBuffer::singleton(buffer, cx));
28640
28641        let prompt = cx.new(|cx| {
28642            let mut prompt = Editor::new(
28643                EditorMode::AutoHeight {
28644                    min_lines: 1,
28645                    max_lines: Some(Self::MAX_LINES as usize),
28646                },
28647                buffer,
28648                None,
28649                window,
28650                cx,
28651            );
28652            prompt.set_soft_wrap_mode(language::language_settings::SoftWrap::EditorWidth, cx);
28653            prompt.set_show_cursor_when_unfocused(false, cx);
28654            prompt.set_placeholder_text(
28655                match edit_action {
28656                    BreakpointPromptEditAction::Log => "Message to log when a breakpoint is hit. Expressions within {} are interpolated.",
28657                    BreakpointPromptEditAction::Condition => "Condition when a breakpoint is hit. Expressions within {} are interpolated.",
28658                    BreakpointPromptEditAction::HitCondition => "How many breakpoint hits to ignore",
28659                },
28660                window,
28661                cx,
28662            );
28663
28664            prompt
28665        });
28666
28667        Self {
28668            prompt,
28669            editor,
28670            breakpoint_anchor,
28671            breakpoint,
28672            edit_action,
28673            editor_margins: Arc::new(Mutex::new(EditorMargins::default())),
28674            block_ids: Default::default(),
28675            _subscriptions: vec![],
28676        }
28677    }
28678
28679    pub(crate) fn add_block_ids(&mut self, block_ids: Vec<CustomBlockId>) {
28680        self.block_ids.extend(block_ids)
28681    }
28682
28683    fn confirm(&mut self, _: &menu::Confirm, window: &mut Window, cx: &mut Context<Self>) {
28684        if let Some(editor) = self.editor.upgrade() {
28685            let message = self
28686                .prompt
28687                .read(cx)
28688                .buffer
28689                .read(cx)
28690                .as_singleton()
28691                .expect("A multi buffer in breakpoint prompt isn't possible")
28692                .read(cx)
28693                .as_rope()
28694                .to_string();
28695
28696            editor.update(cx, |editor, cx| {
28697                editor.edit_breakpoint_at_anchor(
28698                    self.breakpoint_anchor,
28699                    self.breakpoint.clone(),
28700                    match self.edit_action {
28701                        BreakpointPromptEditAction::Log => {
28702                            BreakpointEditAction::EditLogMessage(message.into())
28703                        }
28704                        BreakpointPromptEditAction::Condition => {
28705                            BreakpointEditAction::EditCondition(message.into())
28706                        }
28707                        BreakpointPromptEditAction::HitCondition => {
28708                            BreakpointEditAction::EditHitCondition(message.into())
28709                        }
28710                    },
28711                    cx,
28712                );
28713
28714                editor.remove_blocks(self.block_ids.clone(), None, cx);
28715                cx.focus_self(window);
28716            });
28717        }
28718    }
28719
28720    fn cancel(&mut self, _: &menu::Cancel, window: &mut Window, cx: &mut Context<Self>) {
28721        self.editor
28722            .update(cx, |editor, cx| {
28723                editor.remove_blocks(self.block_ids.clone(), None, cx);
28724                window.focus(&editor.focus_handle, cx);
28725            })
28726            .log_err();
28727    }
28728
28729    fn render_prompt_editor(&self, cx: &mut Context<Self>) -> impl IntoElement {
28730        let settings = ThemeSettings::get_global(cx);
28731        let text_style = TextStyle {
28732            color: if self.prompt.read(cx).read_only(cx) {
28733                cx.theme().colors().text_disabled
28734            } else {
28735                cx.theme().colors().text
28736            },
28737            font_family: settings.buffer_font.family.clone(),
28738            font_fallbacks: settings.buffer_font.fallbacks.clone(),
28739            font_size: settings.buffer_font_size(cx).into(),
28740            font_weight: settings.buffer_font.weight,
28741            line_height: relative(settings.buffer_line_height.value()),
28742            ..Default::default()
28743        };
28744        EditorElement::new(
28745            &self.prompt,
28746            EditorStyle {
28747                background: cx.theme().colors().editor_background,
28748                local_player: cx.theme().players().local(),
28749                text: text_style,
28750                ..Default::default()
28751            },
28752        )
28753    }
28754}
28755
28756impl Render for BreakpointPromptEditor {
28757    fn render(&mut self, window: &mut Window, cx: &mut Context<Self>) -> impl IntoElement {
28758        let editor_margins = *self.editor_margins.lock();
28759        let gutter_dimensions = editor_margins.gutter;
28760        h_flex()
28761            .key_context("Editor")
28762            .bg(cx.theme().colors().editor_background)
28763            .border_y_1()
28764            .border_color(cx.theme().status().info_border)
28765            .size_full()
28766            .py(window.line_height() / 2.5)
28767            .on_action(cx.listener(Self::confirm))
28768            .on_action(cx.listener(Self::cancel))
28769            .child(h_flex().w(gutter_dimensions.full_width() + (gutter_dimensions.margin / 2.0)))
28770            .child(div().flex_1().child(self.render_prompt_editor(cx)))
28771    }
28772}
28773
28774impl Focusable for BreakpointPromptEditor {
28775    fn focus_handle(&self, cx: &App) -> FocusHandle {
28776        self.prompt.focus_handle(cx)
28777    }
28778}
28779
28780fn all_edits_insertions_or_deletions(
28781    edits: &Vec<(Range<Anchor>, Arc<str>)>,
28782    snapshot: &MultiBufferSnapshot,
28783) -> bool {
28784    let mut all_insertions = true;
28785    let mut all_deletions = true;
28786
28787    for (range, new_text) in edits.iter() {
28788        let range_is_empty = range.to_offset(snapshot).is_empty();
28789        let text_is_empty = new_text.is_empty();
28790
28791        if range_is_empty != text_is_empty {
28792            if range_is_empty {
28793                all_deletions = false;
28794            } else {
28795                all_insertions = false;
28796            }
28797        } else {
28798            return false;
28799        }
28800
28801        if !all_insertions && !all_deletions {
28802            return false;
28803        }
28804    }
28805    all_insertions || all_deletions
28806}
28807
28808struct MissingEditPredictionKeybindingTooltip;
28809
28810impl Render for MissingEditPredictionKeybindingTooltip {
28811    fn render(&mut self, _: &mut Window, cx: &mut Context<Self>) -> impl IntoElement {
28812        ui::tooltip_container(cx, |container, cx| {
28813            container
28814                .flex_shrink_0()
28815                .max_w_80()
28816                .min_h(rems_from_px(124.))
28817                .justify_between()
28818                .child(
28819                    v_flex()
28820                        .flex_1()
28821                        .text_ui_sm(cx)
28822                        .child(Label::new("Conflict with Accept Keybinding"))
28823                        .child("Your keymap currently overrides the default accept keybinding. To continue, assign one keybinding for the `editor::AcceptEditPrediction` action.")
28824                )
28825                .child(
28826                    h_flex()
28827                        .pb_1()
28828                        .gap_1()
28829                        .items_end()
28830                        .w_full()
28831                        .child(Button::new("open-keymap", "Assign Keybinding").size(ButtonSize::Compact).on_click(|_ev, window, cx| {
28832                            window.dispatch_action(zed_actions::OpenKeymapFile.boxed_clone(), cx)
28833                        }))
28834                        .child(Button::new("see-docs", "See Docs").size(ButtonSize::Compact).on_click(|_ev, _window, cx| {
28835                            cx.open_url("https://zed.dev/docs/completions#edit-predictions-missing-keybinding");
28836                        })),
28837                )
28838        })
28839    }
28840}
28841
28842#[derive(Debug, Clone, Copy, PartialEq)]
28843pub struct LineHighlight {
28844    pub background: Background,
28845    pub border: Option<gpui::Hsla>,
28846    pub include_gutter: bool,
28847    pub type_id: Option<TypeId>,
28848}
28849
28850struct LineManipulationResult {
28851    pub new_text: String,
28852    pub line_count_before: usize,
28853    pub line_count_after: usize,
28854}
28855
28856fn render_diff_hunk_controls(
28857    row: u32,
28858    status: &DiffHunkStatus,
28859    hunk_range: Range<Anchor>,
28860    is_created_file: bool,
28861    line_height: Pixels,
28862    editor: &Entity<Editor>,
28863    _window: &mut Window,
28864    cx: &mut App,
28865) -> AnyElement {
28866    h_flex()
28867        .h(line_height)
28868        .mr_1()
28869        .gap_1()
28870        .px_0p5()
28871        .pb_1()
28872        .border_x_1()
28873        .border_b_1()
28874        .border_color(cx.theme().colors().border_variant)
28875        .rounded_b_lg()
28876        .bg(cx.theme().colors().editor_background)
28877        .gap_1()
28878        .block_mouse_except_scroll()
28879        .shadow_md()
28880        .child(if status.has_secondary_hunk() {
28881            Button::new(("stage", row as u64), "Stage")
28882                .alpha(if status.is_pending() { 0.66 } else { 1.0 })
28883                .tooltip({
28884                    let focus_handle = editor.focus_handle(cx);
28885                    move |_window, cx| {
28886                        Tooltip::for_action_in(
28887                            "Stage Hunk",
28888                            &::git::ToggleStaged,
28889                            &focus_handle,
28890                            cx,
28891                        )
28892                    }
28893                })
28894                .on_click({
28895                    let editor = editor.clone();
28896                    move |_event, _window, cx| {
28897                        editor.update(cx, |editor, cx| {
28898                            editor.stage_or_unstage_diff_hunks(
28899                                true,
28900                                vec![hunk_range.start..hunk_range.start],
28901                                cx,
28902                            );
28903                        });
28904                    }
28905                })
28906        } else {
28907            Button::new(("unstage", row as u64), "Unstage")
28908                .alpha(if status.is_pending() { 0.66 } else { 1.0 })
28909                .tooltip({
28910                    let focus_handle = editor.focus_handle(cx);
28911                    move |_window, cx| {
28912                        Tooltip::for_action_in(
28913                            "Unstage Hunk",
28914                            &::git::ToggleStaged,
28915                            &focus_handle,
28916                            cx,
28917                        )
28918                    }
28919                })
28920                .on_click({
28921                    let editor = editor.clone();
28922                    move |_event, _window, cx| {
28923                        editor.update(cx, |editor, cx| {
28924                            editor.stage_or_unstage_diff_hunks(
28925                                false,
28926                                vec![hunk_range.start..hunk_range.start],
28927                                cx,
28928                            );
28929                        });
28930                    }
28931                })
28932        })
28933        .child(
28934            Button::new(("restore", row as u64), "Restore")
28935                .tooltip({
28936                    let focus_handle = editor.focus_handle(cx);
28937                    move |_window, cx| {
28938                        Tooltip::for_action_in("Restore Hunk", &::git::Restore, &focus_handle, cx)
28939                    }
28940                })
28941                .on_click({
28942                    let editor = editor.clone();
28943                    move |_event, window, cx| {
28944                        editor.update(cx, |editor, cx| {
28945                            let snapshot = editor.snapshot(window, cx);
28946                            let point = hunk_range.start.to_point(&snapshot.buffer_snapshot());
28947                            editor.restore_hunks_in_ranges(vec![point..point], window, cx);
28948                        });
28949                    }
28950                })
28951                .disabled(is_created_file),
28952        )
28953        .when(
28954            !editor.read(cx).buffer().read(cx).all_diff_hunks_expanded(),
28955            |el| {
28956                el.child(
28957                    IconButton::new(("next-hunk", row as u64), IconName::ArrowDown)
28958                        .shape(IconButtonShape::Square)
28959                        .icon_size(IconSize::Small)
28960                        // .disabled(!has_multiple_hunks)
28961                        .tooltip({
28962                            let focus_handle = editor.focus_handle(cx);
28963                            move |_window, cx| {
28964                                Tooltip::for_action_in("Next Hunk", &GoToHunk, &focus_handle, cx)
28965                            }
28966                        })
28967                        .on_click({
28968                            let editor = editor.clone();
28969                            move |_event, window, cx| {
28970                                editor.update(cx, |editor, cx| {
28971                                    let snapshot = editor.snapshot(window, cx);
28972                                    let position =
28973                                        hunk_range.end.to_point(&snapshot.buffer_snapshot());
28974                                    editor.go_to_hunk_before_or_after_position(
28975                                        &snapshot,
28976                                        position,
28977                                        Direction::Next,
28978                                        window,
28979                                        cx,
28980                                    );
28981                                    editor.expand_selected_diff_hunks(cx);
28982                                });
28983                            }
28984                        }),
28985                )
28986                .child(
28987                    IconButton::new(("prev-hunk", row as u64), IconName::ArrowUp)
28988                        .shape(IconButtonShape::Square)
28989                        .icon_size(IconSize::Small)
28990                        // .disabled(!has_multiple_hunks)
28991                        .tooltip({
28992                            let focus_handle = editor.focus_handle(cx);
28993                            move |_window, cx| {
28994                                Tooltip::for_action_in(
28995                                    "Previous Hunk",
28996                                    &GoToPreviousHunk,
28997                                    &focus_handle,
28998                                    cx,
28999                                )
29000                            }
29001                        })
29002                        .on_click({
29003                            let editor = editor.clone();
29004                            move |_event, window, cx| {
29005                                editor.update(cx, |editor, cx| {
29006                                    let snapshot = editor.snapshot(window, cx);
29007                                    let point =
29008                                        hunk_range.start.to_point(&snapshot.buffer_snapshot());
29009                                    editor.go_to_hunk_before_or_after_position(
29010                                        &snapshot,
29011                                        point,
29012                                        Direction::Prev,
29013                                        window,
29014                                        cx,
29015                                    );
29016                                    editor.expand_selected_diff_hunks(cx);
29017                                });
29018                            }
29019                        }),
29020                )
29021            },
29022        )
29023        .into_any_element()
29024}
29025
29026pub fn multibuffer_context_lines(cx: &App) -> u32 {
29027    EditorSettings::try_get(cx)
29028        .map(|settings| settings.excerpt_context_lines)
29029        .unwrap_or(2)
29030        .min(32)
29031}