editor.rs

    1#![allow(rustdoc::private_intra_doc_links)]
    2//! This is the place where everything editor-related is stored (data-wise) and displayed (ui-wise).
    3//! The main point of interest in this crate is [`Editor`] type, which is used in every other Zed part as a user input element.
    4//! It comes in different flavors: single line, multiline and a fixed height one.
    5//!
    6//! Editor contains of multiple large submodules:
    7//! * [`element`] — the place where all rendering happens
    8//! * [`display_map`] - chunks up text in the editor into the logical blocks, establishes coordinates and mapping between each of them.
    9//!   Contains all metadata related to text transformations (folds, fake inlay text insertions, soft wraps, tab markup, etc.).
   10//!
   11//! All other submodules and structs are mostly concerned with holding editor data about the way it displays current buffer region(s).
   12//!
   13//! If you're looking to improve Vim mode, you should check out Vim crate that wraps Editor and overrides its behavior.
   14pub mod actions;
   15pub mod blink_manager;
   16mod bracket_colorization;
   17mod clangd_ext;
   18pub mod code_context_menus;
   19pub mod display_map;
   20mod document_colors;
   21mod document_symbols;
   22mod editor_settings;
   23mod element;
   24mod folding_ranges;
   25mod git;
   26mod highlight_matching_bracket;
   27mod hover_links;
   28pub mod hover_popover;
   29mod indent_guides;
   30mod inlays;
   31pub mod items;
   32mod jsx_tag_auto_close;
   33mod linked_editing_ranges;
   34mod lsp_ext;
   35mod mouse_context_menu;
   36pub mod movement;
   37mod persistence;
   38mod rust_analyzer_ext;
   39pub mod scroll;
   40mod selections_collection;
   41pub mod semantic_tokens;
   42mod split;
   43pub mod split_editor_view;
   44pub mod tasks;
   45
   46#[cfg(test)]
   47mod code_completion_tests;
   48#[cfg(test)]
   49mod edit_prediction_tests;
   50#[cfg(test)]
   51mod editor_tests;
   52mod signature_help;
   53#[cfg(any(test, feature = "test-support"))]
   54pub mod test;
   55
   56pub(crate) use actions::*;
   57pub use display_map::{
   58    ChunkRenderer, ChunkRendererContext, DisplayPoint, FoldPlaceholder, HighlightKey,
   59    SemanticTokenHighlight,
   60};
   61pub use edit_prediction_types::Direction;
   62pub use editor_settings::{
   63    CompletionDetailAlignment, CurrentLineHighlight, DiffViewStyle, DocumentColorsRenderMode,
   64    EditorSettings, HideMouseMode, ScrollBeyondLastLine, ScrollbarAxes, SearchSettings,
   65    ShowMinimap,
   66};
   67pub use element::{
   68    CursorLayout, EditorElement, HighlightedRange, HighlightedRangeLine, PointForPosition,
   69    render_breadcrumb_text,
   70};
   71pub use git::blame::BlameRenderer;
   72pub use hover_popover::hover_markdown_style;
   73pub use inlays::Inlay;
   74pub use items::MAX_TAB_TITLE_LEN;
   75pub use linked_editing_ranges::LinkedEdits;
   76pub use lsp::CompletionContext;
   77pub use lsp_ext::lsp_tasks;
   78pub use multi_buffer::{
   79    Anchor, AnchorRangeExt, BufferOffset, ExcerptId, ExcerptRange, MBTextSummary, MultiBuffer,
   80    MultiBufferOffset, MultiBufferOffsetUtf16, MultiBufferSnapshot, PathKey, RowInfo, ToOffset,
   81    ToPoint,
   82};
   83pub use split::{SplittableEditor, ToggleSplitDiff};
   84pub use split_editor_view::SplitEditorView;
   85pub use text::Bias;
   86
   87use ::git::{Restore, blame::BlameEntry, commit::ParsedCommitMessage, status::FileStatus};
   88use aho_corasick::{AhoCorasick, AhoCorasickBuilder, BuildError};
   89use anyhow::{Context as _, Result, anyhow, bail};
   90use blink_manager::BlinkManager;
   91use buffer_diff::DiffHunkStatus;
   92use client::{Collaborator, ParticipantIndex, parse_zed_link};
   93use clock::ReplicaId;
   94use code_context_menus::{
   95    AvailableCodeAction, CodeActionContents, CodeActionsItem, CodeActionsMenu, CodeContextMenu,
   96    CompletionsMenu, ContextMenuOrigin,
   97};
   98use collections::{BTreeMap, HashMap, HashSet, VecDeque};
   99use convert_case::{Case, Casing};
  100use dap::TelemetrySpawnLocation;
  101use display_map::*;
  102use document_colors::LspColorData;
  103use edit_prediction_types::{
  104    EditPredictionDelegate, EditPredictionDelegateHandle, EditPredictionDiscardReason,
  105    EditPredictionGranularity, SuggestionDisplayType,
  106};
  107use editor_settings::{GoToDefinitionFallback, Minimap as MinimapSettings};
  108use element::{AcceptEditPredictionBinding, LineWithInvisibles, PositionMap, layout_line};
  109use futures::{
  110    FutureExt,
  111    future::{self, Shared, join},
  112};
  113use fuzzy::{StringMatch, StringMatchCandidate};
  114use git::blame::{GitBlame, GlobalBlameRenderer};
  115use gpui::{
  116    Action, Animation, AnimationExt, AnyElement, App, AppContext, AsyncWindowContext,
  117    AvailableSpace, Background, Bounds, ClickEvent, ClipboardEntry, ClipboardItem, Context,
  118    DispatchPhase, Edges, Entity, EntityId, EntityInputHandler, EventEmitter, FocusHandle,
  119    FocusOutEvent, Focusable, FontId, FontStyle, FontWeight, Global, HighlightStyle, Hsla,
  120    KeyContext, Modifiers, MouseButton, MouseDownEvent, MouseMoveEvent, PaintQuad, ParentElement,
  121    Pixels, PressureStage, Render, ScrollHandle, SharedString, SharedUri, Size, Stateful, Styled,
  122    Subscription, Task, TextRun, TextStyle, TextStyleRefinement, UTF16Selection, UnderlineStyle,
  123    UniformListScrollHandle, WeakEntity, WeakFocusHandle, Window, div, point, prelude::*,
  124    pulsating_between, px, relative, size,
  125};
  126use hover_links::{HoverLink, HoveredLinkState, find_file};
  127use hover_popover::{HoverState, hide_hover};
  128use indent_guides::ActiveIndentGuidesState;
  129use inlays::{InlaySplice, inlay_hints::InlayHintRefreshReason};
  130use itertools::{Either, Itertools};
  131use language::{
  132    AutoindentMode, BlockCommentConfig, BracketMatch, BracketPair, Buffer, BufferRow,
  133    BufferSnapshot, Capability, CharClassifier, CharKind, CharScopeContext, CodeLabel, CursorShape,
  134    DiagnosticEntryRef, DiffOptions, EditPredictionsMode, EditPreview, HighlightedText, IndentKind,
  135    IndentSize, Language, LanguageName, LanguageRegistry, LanguageScope, LocalFile, OffsetRangeExt,
  136    OutlineItem, Point, Runnable, Selection, SelectionGoal, TextObject, TransactionId,
  137    TreeSitterOptions, WordsQuery,
  138    language_settings::{
  139        self, LanguageSettings, LspInsertMode, RewrapBehavior, WordsCompletionMode,
  140        all_language_settings, language_settings,
  141    },
  142    point_from_lsp, point_to_lsp, text_diff_with_options,
  143};
  144use linked_editing_ranges::refresh_linked_ranges;
  145use lsp::{
  146    CodeActionKind, CompletionItemKind, CompletionTriggerKind, InsertTextFormat, InsertTextMode,
  147    LanguageServerId,
  148};
  149use markdown::Markdown;
  150use mouse_context_menu::MouseContextMenu;
  151use movement::TextLayoutDetails;
  152use multi_buffer::{
  153    ExcerptInfo, ExpandExcerptDirection, MultiBufferDiffHunk, MultiBufferPoint, MultiBufferRow,
  154};
  155use parking_lot::Mutex;
  156use persistence::DB;
  157use project::{
  158    BreakpointWithPosition, CodeAction, Completion, CompletionDisplayOptions, CompletionIntent,
  159    CompletionResponse, CompletionSource, DisableAiSettings, DocumentHighlight, InlayHint, InlayId,
  160    InvalidationStrategy, Location, LocationLink, LspAction, PrepareRenameResponse, Project,
  161    ProjectItem, ProjectPath, ProjectTransaction, TaskSourceKind,
  162    debugger::{
  163        breakpoint_store::{
  164            Breakpoint, BreakpointEditAction, BreakpointSessionState, BreakpointState,
  165            BreakpointStore, BreakpointStoreEvent,
  166        },
  167        session::{Session, SessionEvent},
  168    },
  169    git_store::GitStoreEvent,
  170    lsp_store::{
  171        BufferSemanticTokens, CacheInlayHints, CompletionDocumentation, FormatTrigger,
  172        LspFormatTarget, OpenLspBufferHandle, RefreshForServer,
  173    },
  174    project_settings::{DiagnosticSeverity, GoToDiagnosticSeverityFilter, ProjectSettings},
  175};
  176use rand::seq::SliceRandom;
  177use regex::Regex;
  178use rpc::{ErrorCode, ErrorExt, proto::PeerId};
  179use scroll::{Autoscroll, OngoingScroll, ScrollAnchor, ScrollManager, SharedScrollAnchor};
  180use selections_collection::{MutableSelectionsCollection, SelectionsCollection};
  181use serde::{Deserialize, Serialize};
  182use settings::{
  183    GitGutterSetting, RelativeLineNumbers, Settings, SettingsLocation, SettingsStore,
  184    update_settings_file,
  185};
  186use smallvec::{SmallVec, smallvec};
  187use snippet::Snippet;
  188use std::{
  189    any::{Any, TypeId},
  190    borrow::Cow,
  191    cell::{OnceCell, RefCell},
  192    cmp::{self, Ordering, Reverse},
  193    collections::hash_map,
  194    iter::{self, Peekable},
  195    mem,
  196    num::NonZeroU32,
  197    ops::{ControlFlow, Deref, DerefMut, Not, Range, RangeInclusive},
  198    path::{Path, PathBuf},
  199    rc::Rc,
  200    sync::Arc,
  201    time::{Duration, Instant},
  202};
  203use task::{ResolvedTask, RunnableTag, TaskTemplate, TaskVariables};
  204use text::{BufferId, FromAnchor, OffsetUtf16, Rope, ToOffset as _, ToPoint as _};
  205use theme::{
  206    AccentColors, ActiveTheme, GlobalTheme, PlayerColor, StatusColors, SyntaxTheme, Theme,
  207    ThemeSettings, observe_buffer_font_size_adjustment,
  208};
  209use ui::{
  210    Avatar, ButtonSize, ButtonStyle, ContextMenu, Disclosure, IconButton, IconButtonShape,
  211    IconName, IconSize, Indicator, Key, Tooltip, h_flex, prelude::*, scrollbars::ScrollbarAutoHide,
  212};
  213use ui_input::ErasedEditor;
  214use util::{RangeExt, ResultExt, TryFutureExt, maybe, post_inc};
  215use workspace::{
  216    CollaboratorId, Item as WorkspaceItem, ItemId, ItemNavHistory, NavigationEntry, OpenInTerminal,
  217    OpenTerminal, Pane, RestoreOnStartupBehavior, SERIALIZATION_THROTTLE_TIME, SplitDirection,
  218    TabBarSettings, Toast, ViewId, Workspace, WorkspaceId, WorkspaceSettings,
  219    item::{BreadcrumbText, ItemBufferKind, ItemHandle, PreviewTabsSettings, SaveOptions},
  220    notifications::{DetachAndPromptErr, NotificationId, NotifyTaskExt},
  221    searchable::SearchEvent,
  222};
  223use zed_actions::editor::{MoveDown, MoveUp};
  224
  225use crate::{
  226    code_context_menus::CompletionsMenuSource,
  227    editor_settings::MultiCursorModifier,
  228    hover_links::{find_url, find_url_from_range},
  229    inlays::{
  230        InlineValueCache,
  231        inlay_hints::{LspInlayHintData, inlay_hint_settings},
  232    },
  233    scroll::{ScrollOffset, ScrollPixelOffset},
  234    selections_collection::resolve_selections_wrapping_blocks,
  235    semantic_tokens::SemanticTokenState,
  236    signature_help::{SignatureHelpHiddenBy, SignatureHelpState},
  237};
  238
  239pub const FILE_HEADER_HEIGHT: u32 = 2;
  240pub const BUFFER_HEADER_PADDING: Rems = rems(0.25);
  241pub const MULTI_BUFFER_EXCERPT_HEADER_HEIGHT: u32 = 1;
  242const CURSOR_BLINK_INTERVAL: Duration = Duration::from_millis(500);
  243const MAX_LINE_LEN: usize = 1024;
  244const MIN_NAVIGATION_HISTORY_ROW_DELTA: i64 = 10;
  245const MAX_SELECTION_HISTORY_LEN: usize = 1024;
  246pub(crate) const CURSORS_VISIBLE_FOR: Duration = Duration::from_millis(2000);
  247#[doc(hidden)]
  248pub const CODE_ACTIONS_DEBOUNCE_TIMEOUT: Duration = Duration::from_millis(250);
  249pub const SELECTION_HIGHLIGHT_DEBOUNCE_TIMEOUT: Duration = Duration::from_millis(100);
  250
  251pub(crate) const CODE_ACTION_TIMEOUT: Duration = Duration::from_secs(5);
  252pub(crate) const FORMAT_TIMEOUT: Duration = Duration::from_secs(5);
  253pub(crate) const SCROLL_CENTER_TOP_BOTTOM_DEBOUNCE_TIMEOUT: Duration = Duration::from_secs(1);
  254pub const LSP_REQUEST_DEBOUNCE_TIMEOUT: Duration = Duration::from_millis(50);
  255
  256pub(crate) const EDIT_PREDICTION_KEY_CONTEXT: &str = "edit_prediction";
  257pub(crate) const EDIT_PREDICTION_CONFLICT_KEY_CONTEXT: &str = "edit_prediction_conflict";
  258pub(crate) const MINIMAP_FONT_SIZE: AbsoluteLength = AbsoluteLength::Pixels(px(2.));
  259
  260pub type RenderDiffHunkControlsFn = Arc<
  261    dyn Fn(
  262        u32,
  263        &DiffHunkStatus,
  264        Range<Anchor>,
  265        bool,
  266        Pixels,
  267        &Entity<Editor>,
  268        &mut Window,
  269        &mut App,
  270    ) -> AnyElement,
  271>;
  272
  273enum ReportEditorEvent {
  274    Saved { auto_saved: bool },
  275    EditorOpened,
  276    Closed,
  277}
  278
  279impl ReportEditorEvent {
  280    pub fn event_type(&self) -> &'static str {
  281        match self {
  282            Self::Saved { .. } => "Editor Saved",
  283            Self::EditorOpened => "Editor Opened",
  284            Self::Closed => "Editor Closed",
  285        }
  286    }
  287}
  288
  289pub enum ActiveDebugLine {}
  290pub enum DebugStackFrameLine {}
  291
  292pub enum ConflictsOuter {}
  293pub enum ConflictsOurs {}
  294pub enum ConflictsTheirs {}
  295pub enum ConflictsOursMarker {}
  296pub enum ConflictsTheirsMarker {}
  297
  298pub struct HunkAddedColor;
  299pub struct HunkRemovedColor;
  300
  301#[derive(Debug, Copy, Clone, PartialEq, Eq)]
  302pub enum Navigated {
  303    Yes,
  304    No,
  305}
  306
  307impl Navigated {
  308    pub fn from_bool(yes: bool) -> Navigated {
  309        if yes { Navigated::Yes } else { Navigated::No }
  310    }
  311}
  312
  313#[derive(Debug, Clone, PartialEq, Eq)]
  314enum DisplayDiffHunk {
  315    Folded {
  316        display_row: DisplayRow,
  317    },
  318    Unfolded {
  319        is_created_file: bool,
  320        diff_base_byte_range: Range<usize>,
  321        display_row_range: Range<DisplayRow>,
  322        multi_buffer_range: Range<Anchor>,
  323        status: DiffHunkStatus,
  324        word_diffs: Vec<Range<MultiBufferOffset>>,
  325    },
  326}
  327
  328pub enum HideMouseCursorOrigin {
  329    TypingAction,
  330    MovementAction,
  331}
  332
  333pub fn init(cx: &mut App) {
  334    cx.set_global(GlobalBlameRenderer(Arc::new(())));
  335    cx.set_global(breadcrumbs::RenderBreadcrumbText(render_breadcrumb_text));
  336
  337    workspace::register_project_item::<Editor>(cx);
  338    workspace::FollowableViewRegistry::register::<Editor>(cx);
  339    workspace::register_serializable_item::<Editor>(cx);
  340
  341    cx.observe_new(
  342        |workspace: &mut Workspace, _: Option<&mut Window>, _cx: &mut Context<Workspace>| {
  343            workspace.register_action(Editor::new_file);
  344            workspace.register_action(Editor::new_file_split);
  345            workspace.register_action(Editor::new_file_vertical);
  346            workspace.register_action(Editor::new_file_horizontal);
  347            workspace.register_action(Editor::cancel_language_server_work);
  348            workspace.register_action(Editor::toggle_focus);
  349        },
  350    )
  351    .detach();
  352
  353    cx.on_action(move |_: &workspace::NewFile, cx| {
  354        let app_state = workspace::AppState::global(cx);
  355        if let Some(app_state) = app_state.upgrade() {
  356            workspace::open_new(
  357                Default::default(),
  358                app_state,
  359                cx,
  360                |workspace, window, cx| {
  361                    Editor::new_file(workspace, &Default::default(), window, cx)
  362                },
  363            )
  364            .detach_and_log_err(cx);
  365        }
  366    })
  367    .on_action(move |_: &workspace::NewWindow, cx| {
  368        let app_state = workspace::AppState::global(cx);
  369        if let Some(app_state) = app_state.upgrade() {
  370            workspace::open_new(
  371                Default::default(),
  372                app_state,
  373                cx,
  374                |workspace, window, cx| {
  375                    cx.activate(true);
  376                    Editor::new_file(workspace, &Default::default(), window, cx)
  377                },
  378            )
  379            .detach_and_log_err(cx);
  380        }
  381    });
  382    _ = ui_input::ERASED_EDITOR_FACTORY.set(|window, cx| {
  383        Arc::new(ErasedEditorImpl(
  384            cx.new(|cx| Editor::single_line(window, cx)),
  385        )) as Arc<dyn ErasedEditor>
  386    });
  387    _ = multi_buffer::EXCERPT_CONTEXT_LINES.set(multibuffer_context_lines);
  388}
  389
  390pub fn set_blame_renderer(renderer: impl BlameRenderer + 'static, cx: &mut App) {
  391    cx.set_global(GlobalBlameRenderer(Arc::new(renderer)));
  392}
  393
  394pub trait DiagnosticRenderer {
  395    fn render_group(
  396        &self,
  397        diagnostic_group: Vec<DiagnosticEntryRef<'_, Point>>,
  398        buffer_id: BufferId,
  399        snapshot: EditorSnapshot,
  400        editor: WeakEntity<Editor>,
  401        language_registry: Option<Arc<LanguageRegistry>>,
  402        cx: &mut App,
  403    ) -> Vec<BlockProperties<Anchor>>;
  404
  405    fn render_hover(
  406        &self,
  407        diagnostic_group: Vec<DiagnosticEntryRef<'_, Point>>,
  408        range: Range<Point>,
  409        buffer_id: BufferId,
  410        language_registry: Option<Arc<LanguageRegistry>>,
  411        cx: &mut App,
  412    ) -> Option<Entity<markdown::Markdown>>;
  413
  414    fn open_link(
  415        &self,
  416        editor: &mut Editor,
  417        link: SharedString,
  418        window: &mut Window,
  419        cx: &mut Context<Editor>,
  420    );
  421}
  422
  423pub(crate) struct GlobalDiagnosticRenderer(pub Arc<dyn DiagnosticRenderer>);
  424
  425impl GlobalDiagnosticRenderer {
  426    fn global(cx: &App) -> Option<Arc<dyn DiagnosticRenderer>> {
  427        cx.try_global::<Self>().map(|g| g.0.clone())
  428    }
  429}
  430
  431impl gpui::Global for GlobalDiagnosticRenderer {}
  432pub fn set_diagnostic_renderer(renderer: impl DiagnosticRenderer + 'static, cx: &mut App) {
  433    cx.set_global(GlobalDiagnosticRenderer(Arc::new(renderer)));
  434}
  435
  436pub struct SearchWithinRange;
  437
  438trait InvalidationRegion {
  439    fn ranges(&self) -> &[Range<Anchor>];
  440}
  441
  442#[derive(Clone, Debug, PartialEq)]
  443pub enum SelectPhase {
  444    Begin {
  445        position: DisplayPoint,
  446        add: bool,
  447        click_count: usize,
  448    },
  449    BeginColumnar {
  450        position: DisplayPoint,
  451        reset: bool,
  452        mode: ColumnarMode,
  453        goal_column: u32,
  454    },
  455    Extend {
  456        position: DisplayPoint,
  457        click_count: usize,
  458    },
  459    Update {
  460        position: DisplayPoint,
  461        goal_column: u32,
  462        scroll_delta: gpui::Point<f32>,
  463    },
  464    End,
  465}
  466
  467#[derive(Clone, Debug, PartialEq)]
  468pub enum ColumnarMode {
  469    FromMouse,
  470    FromSelection,
  471}
  472
  473#[derive(Clone, Debug)]
  474pub enum SelectMode {
  475    Character,
  476    Word(Range<Anchor>),
  477    Line(Range<Anchor>),
  478    All,
  479}
  480
  481#[derive(Copy, Clone, Default, PartialEq, Eq, Debug)]
  482pub enum SizingBehavior {
  483    /// The editor will layout itself using `size_full` and will include the vertical
  484    /// scroll margin as requested by user settings.
  485    #[default]
  486    Default,
  487    /// The editor will layout itself using `size_full`, but will not have any
  488    /// vertical overscroll.
  489    ExcludeOverscrollMargin,
  490    /// The editor will request a vertical size according to its content and will be
  491    /// layouted without a vertical scroll margin.
  492    SizeByContent,
  493}
  494
  495#[derive(Clone, PartialEq, Eq, Debug)]
  496pub enum EditorMode {
  497    SingleLine,
  498    AutoHeight {
  499        min_lines: usize,
  500        max_lines: Option<usize>,
  501    },
  502    Full {
  503        /// When set to `true`, the editor will scale its UI elements with the buffer font size.
  504        scale_ui_elements_with_buffer_font_size: bool,
  505        /// When set to `true`, the editor will render a background for the active line.
  506        show_active_line_background: bool,
  507        /// Determines the sizing behavior for this editor
  508        sizing_behavior: SizingBehavior,
  509    },
  510    Minimap {
  511        parent: WeakEntity<Editor>,
  512    },
  513}
  514
  515impl EditorMode {
  516    pub fn full() -> Self {
  517        Self::Full {
  518            scale_ui_elements_with_buffer_font_size: true,
  519            show_active_line_background: true,
  520            sizing_behavior: SizingBehavior::Default,
  521        }
  522    }
  523
  524    #[inline]
  525    pub fn is_full(&self) -> bool {
  526        matches!(self, Self::Full { .. })
  527    }
  528
  529    #[inline]
  530    pub fn is_single_line(&self) -> bool {
  531        matches!(self, Self::SingleLine { .. })
  532    }
  533
  534    #[inline]
  535    fn is_minimap(&self) -> bool {
  536        matches!(self, Self::Minimap { .. })
  537    }
  538}
  539
  540#[derive(Copy, Clone, Debug)]
  541pub enum SoftWrap {
  542    /// Prefer not to wrap at all.
  543    ///
  544    /// Note: this is currently internal, as actually limited by [`crate::MAX_LINE_LEN`] until it wraps.
  545    /// The mode is used inside git diff hunks, where it's seems currently more useful to not wrap as much as possible.
  546    GitDiff,
  547    /// Prefer a single line generally, unless an overly long line is encountered.
  548    None,
  549    /// Soft wrap lines that exceed the editor width.
  550    EditorWidth,
  551    /// Soft wrap lines at the preferred line length.
  552    Column(u32),
  553    /// Soft wrap line at the preferred line length or the editor width (whichever is smaller).
  554    Bounded(u32),
  555}
  556
  557#[derive(Clone)]
  558pub struct EditorStyle {
  559    pub background: Hsla,
  560    pub border: Hsla,
  561    pub local_player: PlayerColor,
  562    pub text: TextStyle,
  563    pub scrollbar_width: Pixels,
  564    pub syntax: Arc<SyntaxTheme>,
  565    pub status: StatusColors,
  566    pub inlay_hints_style: HighlightStyle,
  567    pub edit_prediction_styles: EditPredictionStyles,
  568    pub unnecessary_code_fade: f32,
  569    pub show_underlines: bool,
  570}
  571
  572impl Default for EditorStyle {
  573    fn default() -> Self {
  574        Self {
  575            background: Hsla::default(),
  576            border: Hsla::default(),
  577            local_player: PlayerColor::default(),
  578            text: TextStyle::default(),
  579            scrollbar_width: Pixels::default(),
  580            syntax: Default::default(),
  581            // HACK: Status colors don't have a real default.
  582            // We should look into removing the status colors from the editor
  583            // style and retrieve them directly from the theme.
  584            status: StatusColors::dark(),
  585            inlay_hints_style: HighlightStyle::default(),
  586            edit_prediction_styles: EditPredictionStyles {
  587                insertion: HighlightStyle::default(),
  588                whitespace: HighlightStyle::default(),
  589            },
  590            unnecessary_code_fade: Default::default(),
  591            show_underlines: true,
  592        }
  593    }
  594}
  595
  596pub fn make_inlay_hints_style(cx: &App) -> HighlightStyle {
  597    let show_background = language_settings::language_settings(None, None, cx)
  598        .inlay_hints
  599        .show_background;
  600
  601    let mut style = cx.theme().syntax().get("hint");
  602
  603    if style.color.is_none() {
  604        style.color = Some(cx.theme().status().hint);
  605    }
  606
  607    if !show_background {
  608        style.background_color = None;
  609        return style;
  610    }
  611
  612    if style.background_color.is_none() {
  613        style.background_color = Some(cx.theme().status().hint_background);
  614    }
  615
  616    style
  617}
  618
  619pub fn make_suggestion_styles(cx: &App) -> EditPredictionStyles {
  620    EditPredictionStyles {
  621        insertion: HighlightStyle {
  622            color: Some(cx.theme().status().predictive),
  623            ..HighlightStyle::default()
  624        },
  625        whitespace: HighlightStyle {
  626            background_color: Some(cx.theme().status().created_background),
  627            ..HighlightStyle::default()
  628        },
  629    }
  630}
  631
  632type CompletionId = usize;
  633
  634pub(crate) enum EditDisplayMode {
  635    TabAccept,
  636    DiffPopover,
  637    Inline,
  638}
  639
  640enum EditPrediction {
  641    Edit {
  642        edits: Vec<(Range<Anchor>, Arc<str>)>,
  643        /// Predicted cursor position as (anchor, offset_from_anchor).
  644        /// The anchor is in multibuffer coordinates; after applying edits,
  645        /// resolve the anchor and add the offset to get the final cursor position.
  646        cursor_position: Option<(Anchor, usize)>,
  647        edit_preview: Option<EditPreview>,
  648        display_mode: EditDisplayMode,
  649        snapshot: BufferSnapshot,
  650    },
  651    /// Move to a specific location in the active editor
  652    MoveWithin {
  653        target: Anchor,
  654        snapshot: BufferSnapshot,
  655    },
  656    /// Move to a specific location in a different editor (not the active one)
  657    MoveOutside {
  658        target: language::Anchor,
  659        snapshot: BufferSnapshot,
  660    },
  661}
  662
  663struct EditPredictionState {
  664    inlay_ids: Vec<InlayId>,
  665    completion: EditPrediction,
  666    completion_id: Option<SharedString>,
  667    invalidation_range: Option<Range<Anchor>>,
  668}
  669
  670enum EditPredictionSettings {
  671    Disabled,
  672    Enabled {
  673        show_in_menu: bool,
  674        preview_requires_modifier: bool,
  675    },
  676}
  677
  678#[derive(Debug, Clone)]
  679struct InlineDiagnostic {
  680    message: SharedString,
  681    group_id: usize,
  682    is_primary: bool,
  683    start: Point,
  684    severity: lsp::DiagnosticSeverity,
  685}
  686
  687pub enum MenuEditPredictionsPolicy {
  688    Never,
  689    ByProvider,
  690}
  691
  692pub enum EditPredictionPreview {
  693    /// Modifier is not pressed
  694    Inactive { released_too_fast: bool },
  695    /// Modifier pressed
  696    Active {
  697        since: Instant,
  698        previous_scroll_position: Option<SharedScrollAnchor>,
  699    },
  700}
  701
  702impl EditPredictionPreview {
  703    pub fn released_too_fast(&self) -> bool {
  704        match self {
  705            EditPredictionPreview::Inactive { released_too_fast } => *released_too_fast,
  706            EditPredictionPreview::Active { .. } => false,
  707        }
  708    }
  709
  710    pub fn set_previous_scroll_position(&mut self, scroll_position: Option<SharedScrollAnchor>) {
  711        if let EditPredictionPreview::Active {
  712            previous_scroll_position,
  713            ..
  714        } = self
  715        {
  716            *previous_scroll_position = scroll_position;
  717        }
  718    }
  719}
  720
  721pub struct ContextMenuOptions {
  722    pub min_entries_visible: usize,
  723    pub max_entries_visible: usize,
  724    pub placement: Option<ContextMenuPlacement>,
  725}
  726
  727#[derive(Debug, Clone, PartialEq, Eq)]
  728pub enum ContextMenuPlacement {
  729    Above,
  730    Below,
  731}
  732
  733#[derive(Copy, Clone, Eq, PartialEq, PartialOrd, Ord, Debug, Default)]
  734struct EditorActionId(usize);
  735
  736impl EditorActionId {
  737    pub fn post_inc(&mut self) -> Self {
  738        let answer = self.0;
  739
  740        *self = Self(answer + 1);
  741
  742        Self(answer)
  743    }
  744}
  745
  746// type GetFieldEditorTheme = dyn Fn(&theme::Theme) -> theme::FieldEditor;
  747// type OverrideTextStyle = dyn Fn(&EditorStyle) -> Option<HighlightStyle>;
  748
  749type BackgroundHighlight = (
  750    Arc<dyn Fn(&usize, &Theme) -> Hsla + Send + Sync>,
  751    Arc<[Range<Anchor>]>,
  752);
  753type GutterHighlight = (fn(&App) -> Hsla, Vec<Range<Anchor>>);
  754
  755#[derive(Default)]
  756struct ScrollbarMarkerState {
  757    scrollbar_size: Size<Pixels>,
  758    dirty: bool,
  759    markers: Arc<[PaintQuad]>,
  760    pending_refresh: Option<Task<Result<()>>>,
  761}
  762
  763impl ScrollbarMarkerState {
  764    fn should_refresh(&self, scrollbar_size: Size<Pixels>) -> bool {
  765        self.pending_refresh.is_none() && (self.scrollbar_size != scrollbar_size || self.dirty)
  766    }
  767}
  768
  769#[derive(Clone, Copy, PartialEq, Eq)]
  770pub enum MinimapVisibility {
  771    Disabled,
  772    Enabled {
  773        /// The configuration currently present in the users settings.
  774        setting_configuration: bool,
  775        /// Whether to override the currently set visibility from the users setting.
  776        toggle_override: bool,
  777    },
  778}
  779
  780impl MinimapVisibility {
  781    fn for_mode(mode: &EditorMode, cx: &App) -> Self {
  782        if mode.is_full() {
  783            Self::Enabled {
  784                setting_configuration: EditorSettings::get_global(cx).minimap.minimap_enabled(),
  785                toggle_override: false,
  786            }
  787        } else {
  788            Self::Disabled
  789        }
  790    }
  791
  792    fn hidden(&self) -> Self {
  793        match *self {
  794            Self::Enabled {
  795                setting_configuration,
  796                ..
  797            } => Self::Enabled {
  798                setting_configuration,
  799                toggle_override: setting_configuration,
  800            },
  801            Self::Disabled => Self::Disabled,
  802        }
  803    }
  804
  805    fn disabled(&self) -> bool {
  806        matches!(*self, Self::Disabled)
  807    }
  808
  809    fn settings_visibility(&self) -> bool {
  810        match *self {
  811            Self::Enabled {
  812                setting_configuration,
  813                ..
  814            } => setting_configuration,
  815            _ => false,
  816        }
  817    }
  818
  819    fn visible(&self) -> bool {
  820        match *self {
  821            Self::Enabled {
  822                setting_configuration,
  823                toggle_override,
  824            } => setting_configuration ^ toggle_override,
  825            _ => false,
  826        }
  827    }
  828
  829    fn toggle_visibility(&self) -> Self {
  830        match *self {
  831            Self::Enabled {
  832                toggle_override,
  833                setting_configuration,
  834            } => Self::Enabled {
  835                setting_configuration,
  836                toggle_override: !toggle_override,
  837            },
  838            Self::Disabled => Self::Disabled,
  839        }
  840    }
  841}
  842
  843#[derive(Debug, Clone, Copy, PartialEq, Eq)]
  844pub enum BufferSerialization {
  845    All,
  846    NonDirtyBuffers,
  847}
  848
  849impl BufferSerialization {
  850    fn new(restore_unsaved_buffers: bool) -> Self {
  851        if restore_unsaved_buffers {
  852            Self::All
  853        } else {
  854            Self::NonDirtyBuffers
  855        }
  856    }
  857}
  858
  859#[derive(Clone, Debug)]
  860struct RunnableTasks {
  861    templates: Vec<(TaskSourceKind, TaskTemplate)>,
  862    offset: multi_buffer::Anchor,
  863    // We need the column at which the task context evaluation should take place (when we're spawning it via gutter).
  864    column: u32,
  865    // Values of all named captures, including those starting with '_'
  866    extra_variables: HashMap<String, String>,
  867    // Full range of the tagged region. We use it to determine which `extra_variables` to grab for context resolution in e.g. a modal.
  868    context_range: Range<BufferOffset>,
  869}
  870
  871impl RunnableTasks {
  872    fn resolve<'a>(
  873        &'a self,
  874        cx: &'a task::TaskContext,
  875    ) -> impl Iterator<Item = (TaskSourceKind, ResolvedTask)> + 'a {
  876        self.templates.iter().filter_map(|(kind, template)| {
  877            template
  878                .resolve_task(&kind.to_id_base(), cx)
  879                .map(|task| (kind.clone(), task))
  880        })
  881    }
  882}
  883
  884#[derive(Clone)]
  885pub struct ResolvedTasks {
  886    templates: SmallVec<[(TaskSourceKind, ResolvedTask); 1]>,
  887    position: Anchor,
  888}
  889
  890/// Addons allow storing per-editor state in other crates (e.g. Vim)
  891pub trait Addon: 'static {
  892    fn extend_key_context(&self, _: &mut KeyContext, _: &App) {}
  893
  894    fn render_buffer_header_controls(
  895        &self,
  896        _: &ExcerptInfo,
  897        _: &Window,
  898        _: &App,
  899    ) -> Option<AnyElement> {
  900        None
  901    }
  902
  903    fn override_status_for_buffer_id(&self, _: BufferId, _: &App) -> Option<FileStatus> {
  904        None
  905    }
  906
  907    fn to_any(&self) -> &dyn std::any::Any;
  908
  909    fn to_any_mut(&mut self) -> Option<&mut dyn std::any::Any> {
  910        None
  911    }
  912}
  913
  914struct ChangeLocation {
  915    current: Option<Vec<Anchor>>,
  916    original: Vec<Anchor>,
  917}
  918impl ChangeLocation {
  919    fn locations(&self) -> &[Anchor] {
  920        self.current.as_ref().unwrap_or(&self.original)
  921    }
  922}
  923
  924/// A set of caret positions, registered when the editor was edited.
  925pub struct ChangeList {
  926    changes: Vec<ChangeLocation>,
  927    /// Currently "selected" change.
  928    position: Option<usize>,
  929}
  930
  931impl ChangeList {
  932    pub fn new() -> Self {
  933        Self {
  934            changes: Vec::new(),
  935            position: None,
  936        }
  937    }
  938
  939    /// Moves to the next change in the list (based on the direction given) and returns the caret positions for the next change.
  940    /// If reaches the end of the list in the direction, returns the corresponding change until called for a different direction.
  941    pub fn next_change(&mut self, count: usize, direction: Direction) -> Option<&[Anchor]> {
  942        if self.changes.is_empty() {
  943            return None;
  944        }
  945
  946        let prev = self.position.unwrap_or(self.changes.len());
  947        let next = if direction == Direction::Prev {
  948            prev.saturating_sub(count)
  949        } else {
  950            (prev + count).min(self.changes.len() - 1)
  951        };
  952        self.position = Some(next);
  953        self.changes.get(next).map(|change| change.locations())
  954    }
  955
  956    /// Adds a new change to the list, resetting the change list position.
  957    pub fn push_to_change_list(&mut self, group: bool, new_positions: Vec<Anchor>) {
  958        self.position.take();
  959        if let Some(last) = self.changes.last_mut()
  960            && group
  961        {
  962            last.current = Some(new_positions)
  963        } else {
  964            self.changes.push(ChangeLocation {
  965                original: new_positions,
  966                current: None,
  967            });
  968        }
  969    }
  970
  971    pub fn last(&self) -> Option<&[Anchor]> {
  972        self.changes.last().map(|change| change.locations())
  973    }
  974
  975    pub fn last_before_grouping(&self) -> Option<&[Anchor]> {
  976        self.changes.last().map(|change| change.original.as_slice())
  977    }
  978
  979    pub fn invert_last_group(&mut self) {
  980        if let Some(last) = self.changes.last_mut()
  981            && let Some(current) = last.current.as_mut()
  982        {
  983            mem::swap(&mut last.original, current);
  984        }
  985    }
  986}
  987
  988#[derive(Clone)]
  989struct InlineBlamePopoverState {
  990    scroll_handle: ScrollHandle,
  991    commit_message: Option<ParsedCommitMessage>,
  992    markdown: Entity<Markdown>,
  993}
  994
  995struct InlineBlamePopover {
  996    position: gpui::Point<Pixels>,
  997    hide_task: Option<Task<()>>,
  998    popover_bounds: Option<Bounds<Pixels>>,
  999    popover_state: InlineBlamePopoverState,
 1000    keyboard_grace: bool,
 1001}
 1002
 1003enum SelectionDragState {
 1004    /// State when no drag related activity is detected.
 1005    None,
 1006    /// State when the mouse is down on a selection that is about to be dragged.
 1007    ReadyToDrag {
 1008        selection: Selection<Anchor>,
 1009        click_position: gpui::Point<Pixels>,
 1010        mouse_down_time: Instant,
 1011    },
 1012    /// State when the mouse is dragging the selection in the editor.
 1013    Dragging {
 1014        selection: Selection<Anchor>,
 1015        drop_cursor: Selection<Anchor>,
 1016        hide_drop_cursor: bool,
 1017    },
 1018}
 1019
 1020enum ColumnarSelectionState {
 1021    FromMouse {
 1022        selection_tail: Anchor,
 1023        display_point: Option<DisplayPoint>,
 1024    },
 1025    FromSelection {
 1026        selection_tail: Anchor,
 1027    },
 1028}
 1029
 1030/// Represents a breakpoint indicator that shows up when hovering over lines in the gutter that don't have
 1031/// a breakpoint on them.
 1032#[derive(Clone, Copy, Debug, PartialEq, Eq)]
 1033struct PhantomBreakpointIndicator {
 1034    display_row: DisplayRow,
 1035    /// There's a small debounce between hovering over the line and showing the indicator.
 1036    /// We don't want to show the indicator when moving the mouse from editor to e.g. project panel.
 1037    is_active: bool,
 1038    collides_with_existing_breakpoint: bool,
 1039}
 1040
 1041/// Represents a diff review button indicator that shows up when hovering over lines in the gutter
 1042/// in diff view mode.
 1043#[derive(Clone, Copy, Debug, PartialEq, Eq)]
 1044pub(crate) struct PhantomDiffReviewIndicator {
 1045    /// The starting anchor of the selection (or the only row if not dragging).
 1046    pub start: Anchor,
 1047    /// The ending anchor of the selection. Equal to start_anchor for single-line selection.
 1048    pub end: Anchor,
 1049    /// There's a small debounce between hovering over the line and showing the indicator.
 1050    /// We don't want to show the indicator when moving the mouse from editor to e.g. project panel.
 1051    pub is_active: bool,
 1052}
 1053
 1054#[derive(Clone, Debug)]
 1055pub(crate) struct DiffReviewDragState {
 1056    pub start_anchor: Anchor,
 1057    pub current_anchor: Anchor,
 1058}
 1059
 1060impl DiffReviewDragState {
 1061    pub fn row_range(&self, snapshot: &DisplaySnapshot) -> std::ops::RangeInclusive<DisplayRow> {
 1062        let start = self.start_anchor.to_display_point(snapshot).row();
 1063        let current = self.current_anchor.to_display_point(snapshot).row();
 1064
 1065        (start..=current).sorted()
 1066    }
 1067}
 1068
 1069/// Identifies a specific hunk in the diff buffer.
 1070/// Used as a key to group comments by their location.
 1071#[derive(Clone, Debug)]
 1072pub struct DiffHunkKey {
 1073    /// The file path (relative to worktree) this hunk belongs to.
 1074    pub file_path: Arc<util::rel_path::RelPath>,
 1075    /// An anchor at the start of the hunk. This tracks position as the buffer changes.
 1076    pub hunk_start_anchor: Anchor,
 1077}
 1078
 1079/// A review comment stored locally before being sent to the Agent panel.
 1080#[derive(Clone)]
 1081pub struct StoredReviewComment {
 1082    /// Unique identifier for this comment (for edit/delete operations).
 1083    pub id: usize,
 1084    /// The comment text entered by the user.
 1085    pub comment: String,
 1086    /// Anchors for the code range being reviewed.
 1087    pub range: Range<Anchor>,
 1088    /// Timestamp when the comment was created (for chronological ordering).
 1089    pub created_at: Instant,
 1090    /// Whether this comment is currently being edited inline.
 1091    pub is_editing: bool,
 1092}
 1093
 1094impl StoredReviewComment {
 1095    pub fn new(id: usize, comment: String, anchor_range: Range<Anchor>) -> Self {
 1096        Self {
 1097            id,
 1098            comment,
 1099            range: anchor_range,
 1100            created_at: Instant::now(),
 1101            is_editing: false,
 1102        }
 1103    }
 1104}
 1105
 1106/// Represents an active diff review overlay that appears when clicking the "Add Review" button.
 1107pub(crate) struct DiffReviewOverlay {
 1108    pub anchor_range: Range<Anchor>,
 1109    /// The block ID for the overlay.
 1110    pub block_id: CustomBlockId,
 1111    /// The editor entity for the review input.
 1112    pub prompt_editor: Entity<Editor>,
 1113    /// The hunk key this overlay belongs to.
 1114    pub hunk_key: DiffHunkKey,
 1115    /// Whether the comments section is expanded.
 1116    pub comments_expanded: bool,
 1117    /// Editors for comments currently being edited inline.
 1118    /// Key: comment ID, Value: Editor entity for inline editing.
 1119    pub inline_edit_editors: HashMap<usize, Entity<Editor>>,
 1120    /// Subscriptions for inline edit editors' action handlers.
 1121    /// Key: comment ID, Value: Subscription keeping the Newline action handler alive.
 1122    pub inline_edit_subscriptions: HashMap<usize, Subscription>,
 1123    /// The current user's avatar URI for display in comment rows.
 1124    pub user_avatar_uri: Option<SharedUri>,
 1125    /// Subscription to keep the action handler alive.
 1126    _subscription: Subscription,
 1127}
 1128
 1129/// Zed's primary implementation of text input, allowing users to edit a [`MultiBuffer`].
 1130///
 1131/// See the [module level documentation](self) for more information.
 1132pub struct Editor {
 1133    focus_handle: FocusHandle,
 1134    last_focused_descendant: Option<WeakFocusHandle>,
 1135    /// The text buffer being edited
 1136    buffer: Entity<MultiBuffer>,
 1137    /// Map of how text in the buffer should be displayed.
 1138    /// Handles soft wraps, folds, fake inlay text insertions, etc.
 1139    pub display_map: Entity<DisplayMap>,
 1140    placeholder_display_map: Option<Entity<DisplayMap>>,
 1141    pub selections: SelectionsCollection,
 1142    pub scroll_manager: ScrollManager,
 1143    /// When inline assist editors are linked, they all render cursors because
 1144    /// typing enters text into each of them, even the ones that aren't focused.
 1145    pub(crate) show_cursor_when_unfocused: bool,
 1146    columnar_selection_state: Option<ColumnarSelectionState>,
 1147    add_selections_state: Option<AddSelectionsState>,
 1148    select_next_state: Option<SelectNextState>,
 1149    select_prev_state: Option<SelectNextState>,
 1150    selection_history: SelectionHistory,
 1151    defer_selection_effects: bool,
 1152    deferred_selection_effects_state: Option<DeferredSelectionEffectsState>,
 1153    autoclose_regions: Vec<AutocloseRegion>,
 1154    snippet_stack: InvalidationStack<SnippetState>,
 1155    select_syntax_node_history: SelectSyntaxNodeHistory,
 1156    ime_transaction: Option<TransactionId>,
 1157    pub diagnostics_max_severity: DiagnosticSeverity,
 1158    active_diagnostics: ActiveDiagnostic,
 1159    show_inline_diagnostics: bool,
 1160    inline_diagnostics_update: Task<()>,
 1161    inline_diagnostics_enabled: bool,
 1162    diagnostics_enabled: bool,
 1163    word_completions_enabled: bool,
 1164    inline_diagnostics: Vec<(Anchor, InlineDiagnostic)>,
 1165    soft_wrap_mode_override: Option<language_settings::SoftWrap>,
 1166    hard_wrap: Option<usize>,
 1167    project: Option<Entity<Project>>,
 1168    semantics_provider: Option<Rc<dyn SemanticsProvider>>,
 1169    completion_provider: Option<Rc<dyn CompletionProvider>>,
 1170    collaboration_hub: Option<Box<dyn CollaborationHub>>,
 1171    blink_manager: Entity<BlinkManager>,
 1172    show_cursor_names: bool,
 1173    hovered_cursors: HashMap<HoveredCursor, Task<()>>,
 1174    pub show_local_selections: bool,
 1175    mode: EditorMode,
 1176    show_breadcrumbs: bool,
 1177    show_gutter: bool,
 1178    show_scrollbars: ScrollbarAxes,
 1179    minimap_visibility: MinimapVisibility,
 1180    offset_content: bool,
 1181    disable_expand_excerpt_buttons: bool,
 1182    delegate_expand_excerpts: bool,
 1183    delegate_stage_and_restore: bool,
 1184    delegate_open_excerpts: bool,
 1185    enable_lsp_data: bool,
 1186    enable_runnables: bool,
 1187    show_line_numbers: Option<bool>,
 1188    use_relative_line_numbers: Option<bool>,
 1189    show_git_diff_gutter: Option<bool>,
 1190    show_code_actions: Option<bool>,
 1191    show_runnables: Option<bool>,
 1192    show_breakpoints: Option<bool>,
 1193    show_diff_review_button: bool,
 1194    show_wrap_guides: Option<bool>,
 1195    show_indent_guides: Option<bool>,
 1196    buffers_with_disabled_indent_guides: HashSet<BufferId>,
 1197    highlight_order: usize,
 1198    highlighted_rows: HashMap<TypeId, Vec<RowHighlight>>,
 1199    background_highlights: HashMap<HighlightKey, BackgroundHighlight>,
 1200    gutter_highlights: HashMap<TypeId, GutterHighlight>,
 1201    scrollbar_marker_state: ScrollbarMarkerState,
 1202    active_indent_guides_state: ActiveIndentGuidesState,
 1203    nav_history: Option<ItemNavHistory>,
 1204    context_menu: RefCell<Option<CodeContextMenu>>,
 1205    context_menu_options: Option<ContextMenuOptions>,
 1206    mouse_context_menu: Option<MouseContextMenu>,
 1207    completion_tasks: Vec<(CompletionId, Task<()>)>,
 1208    inline_blame_popover: Option<InlineBlamePopover>,
 1209    inline_blame_popover_show_task: Option<Task<()>>,
 1210    signature_help_state: SignatureHelpState,
 1211    auto_signature_help: Option<bool>,
 1212    find_all_references_task_sources: Vec<Anchor>,
 1213    next_completion_id: CompletionId,
 1214    available_code_actions: Option<(Location, Rc<[AvailableCodeAction]>)>,
 1215    code_actions_task: Option<Task<Result<()>>>,
 1216    quick_selection_highlight_task: Option<(Range<Anchor>, Task<()>)>,
 1217    debounced_selection_highlight_task: Option<(Range<Anchor>, Task<()>)>,
 1218    debounced_selection_highlight_complete: bool,
 1219    document_highlights_task: Option<Task<()>>,
 1220    linked_editing_range_task: Option<Task<Option<()>>>,
 1221    linked_edit_ranges: linked_editing_ranges::LinkedEditingRanges,
 1222    pending_rename: Option<RenameState>,
 1223    searchable: bool,
 1224    cursor_shape: CursorShape,
 1225    /// Whether the cursor is offset one character to the left when something is
 1226    /// selected (needed for vim visual mode)
 1227    cursor_offset_on_selection: bool,
 1228    current_line_highlight: Option<CurrentLineHighlight>,
 1229    /// Whether to collapse search match ranges to just their start position.
 1230    /// When true, navigating to a match positions the cursor at the match
 1231    /// without selecting the matched text.
 1232    collapse_matches: bool,
 1233    autoindent_mode: Option<AutoindentMode>,
 1234    workspace: Option<(WeakEntity<Workspace>, Option<WorkspaceId>)>,
 1235    input_enabled: bool,
 1236    use_modal_editing: bool,
 1237    read_only: bool,
 1238    leader_id: Option<CollaboratorId>,
 1239    remote_id: Option<ViewId>,
 1240    pub hover_state: HoverState,
 1241    pending_mouse_down: Option<Rc<RefCell<Option<MouseDownEvent>>>>,
 1242    prev_pressure_stage: Option<PressureStage>,
 1243    gutter_hovered: bool,
 1244    hovered_link_state: Option<HoveredLinkState>,
 1245    edit_prediction_provider: Option<RegisteredEditPredictionDelegate>,
 1246    code_action_providers: Vec<Rc<dyn CodeActionProvider>>,
 1247    active_edit_prediction: Option<EditPredictionState>,
 1248    /// Used to prevent flickering as the user types while the menu is open
 1249    stale_edit_prediction_in_menu: Option<EditPredictionState>,
 1250    edit_prediction_settings: EditPredictionSettings,
 1251    edit_predictions_hidden_for_vim_mode: bool,
 1252    show_edit_predictions_override: Option<bool>,
 1253    show_completions_on_input_override: Option<bool>,
 1254    menu_edit_predictions_policy: MenuEditPredictionsPolicy,
 1255    edit_prediction_preview: EditPredictionPreview,
 1256    edit_prediction_indent_conflict: bool,
 1257    edit_prediction_requires_modifier_in_indent_conflict: bool,
 1258    next_inlay_id: usize,
 1259    next_color_inlay_id: usize,
 1260    _subscriptions: Vec<Subscription>,
 1261    pixel_position_of_newest_cursor: Option<gpui::Point<Pixels>>,
 1262    gutter_dimensions: GutterDimensions,
 1263    style: Option<EditorStyle>,
 1264    text_style_refinement: Option<TextStyleRefinement>,
 1265    next_editor_action_id: EditorActionId,
 1266    editor_actions: Rc<
 1267        RefCell<BTreeMap<EditorActionId, Box<dyn Fn(&Editor, &mut Window, &mut Context<Self>)>>>,
 1268    >,
 1269    use_autoclose: bool,
 1270    use_auto_surround: bool,
 1271    auto_replace_emoji_shortcode: bool,
 1272    jsx_tag_auto_close_enabled_in_any_buffer: bool,
 1273    show_git_blame_gutter: bool,
 1274    show_git_blame_inline: bool,
 1275    show_git_blame_inline_delay_task: Option<Task<()>>,
 1276    git_blame_inline_enabled: bool,
 1277    render_diff_hunk_controls: RenderDiffHunkControlsFn,
 1278    buffer_serialization: Option<BufferSerialization>,
 1279    show_selection_menu: Option<bool>,
 1280    blame: Option<Entity<GitBlame>>,
 1281    blame_subscription: Option<Subscription>,
 1282    custom_context_menu: Option<
 1283        Box<
 1284            dyn 'static
 1285                + Fn(
 1286                    &mut Self,
 1287                    DisplayPoint,
 1288                    &mut Window,
 1289                    &mut Context<Self>,
 1290                ) -> Option<Entity<ui::ContextMenu>>,
 1291        >,
 1292    >,
 1293    last_bounds: Option<Bounds<Pixels>>,
 1294    last_position_map: Option<Rc<PositionMap>>,
 1295    expect_bounds_change: Option<Bounds<Pixels>>,
 1296    tasks: BTreeMap<(BufferId, BufferRow), RunnableTasks>,
 1297    tasks_update_task: Option<Task<()>>,
 1298    breakpoint_store: Option<Entity<BreakpointStore>>,
 1299    gutter_breakpoint_indicator: (Option<PhantomBreakpointIndicator>, Option<Task<()>>),
 1300    pub(crate) gutter_diff_review_indicator: (Option<PhantomDiffReviewIndicator>, Option<Task<()>>),
 1301    pub(crate) diff_review_drag_state: Option<DiffReviewDragState>,
 1302    /// Active diff review overlays. Multiple overlays can be open simultaneously
 1303    /// when hunks have comments stored.
 1304    pub(crate) diff_review_overlays: Vec<DiffReviewOverlay>,
 1305    /// Stored review comments grouped by hunk.
 1306    /// Uses a Vec instead of HashMap because DiffHunkKey contains an Anchor
 1307    /// which doesn't implement Hash/Eq in a way suitable for HashMap keys.
 1308    stored_review_comments: Vec<(DiffHunkKey, Vec<StoredReviewComment>)>,
 1309    /// Counter for generating unique comment IDs.
 1310    next_review_comment_id: usize,
 1311    hovered_diff_hunk_row: Option<DisplayRow>,
 1312    pull_diagnostics_task: Task<()>,
 1313    in_project_search: bool,
 1314    previous_search_ranges: Option<Arc<[Range<Anchor>]>>,
 1315    breadcrumb_header: Option<String>,
 1316    focused_block: Option<FocusedBlock>,
 1317    next_scroll_position: NextScrollCursorCenterTopBottom,
 1318    addons: HashMap<TypeId, Box<dyn Addon>>,
 1319    registered_buffers: HashMap<BufferId, OpenLspBufferHandle>,
 1320    load_diff_task: Option<Shared<Task<()>>>,
 1321    /// Whether we are temporarily displaying a diff other than git's
 1322    temporary_diff_override: bool,
 1323    selection_mark_mode: bool,
 1324    toggle_fold_multiple_buffers: Task<()>,
 1325    _scroll_cursor_center_top_bottom_task: Task<()>,
 1326    serialize_selections: Task<()>,
 1327    serialize_folds: Task<()>,
 1328    mouse_cursor_hidden: bool,
 1329    minimap: Option<Entity<Self>>,
 1330    hide_mouse_mode: HideMouseMode,
 1331    pub change_list: ChangeList,
 1332    inline_value_cache: InlineValueCache,
 1333    number_deleted_lines: bool,
 1334
 1335    selection_drag_state: SelectionDragState,
 1336    colors: Option<LspColorData>,
 1337    post_scroll_update: Task<()>,
 1338    refresh_colors_task: Task<()>,
 1339    use_document_folding_ranges: bool,
 1340    refresh_folding_ranges_task: Task<()>,
 1341    inlay_hints: Option<LspInlayHintData>,
 1342    folding_newlines: Task<()>,
 1343    select_next_is_case_sensitive: Option<bool>,
 1344    pub lookup_key: Option<Box<dyn Any + Send + Sync>>,
 1345    on_local_selections_changed:
 1346        Option<Box<dyn Fn(Point, &mut Window, &mut Context<Self>) + 'static>>,
 1347    suppress_selection_callback: bool,
 1348    applicable_language_settings: HashMap<Option<LanguageName>, LanguageSettings>,
 1349    accent_data: Option<AccentData>,
 1350    bracket_fetched_tree_sitter_chunks: HashMap<ExcerptId, HashSet<Range<BufferRow>>>,
 1351    semantic_token_state: SemanticTokenState,
 1352    pub(crate) refresh_matching_bracket_highlights_task: Task<()>,
 1353    refresh_document_symbols_task: Shared<Task<()>>,
 1354    lsp_document_symbols: HashMap<BufferId, Vec<OutlineItem<text::Anchor>>>,
 1355    refresh_outline_symbols_at_cursor_at_cursor_task: Task<()>,
 1356    outline_symbols_at_cursor: Option<(BufferId, Vec<OutlineItem<Anchor>>)>,
 1357    sticky_headers_task: Task<()>,
 1358    sticky_headers: Option<Vec<OutlineItem<Anchor>>>,
 1359    pub(crate) colorize_brackets_task: Task<()>,
 1360}
 1361
 1362#[derive(Debug, PartialEq)]
 1363struct AccentData {
 1364    colors: AccentColors,
 1365    overrides: Vec<SharedString>,
 1366}
 1367
 1368fn debounce_value(debounce_ms: u64) -> Option<Duration> {
 1369    if debounce_ms > 0 {
 1370        Some(Duration::from_millis(debounce_ms))
 1371    } else {
 1372        None
 1373    }
 1374}
 1375
 1376#[derive(Copy, Clone, Debug, PartialEq, Eq, Default)]
 1377enum NextScrollCursorCenterTopBottom {
 1378    #[default]
 1379    Center,
 1380    Top,
 1381    Bottom,
 1382}
 1383
 1384impl NextScrollCursorCenterTopBottom {
 1385    fn next(&self) -> Self {
 1386        match self {
 1387            Self::Center => Self::Top,
 1388            Self::Top => Self::Bottom,
 1389            Self::Bottom => Self::Center,
 1390        }
 1391    }
 1392}
 1393
 1394#[derive(Clone)]
 1395pub struct EditorSnapshot {
 1396    pub mode: EditorMode,
 1397    show_gutter: bool,
 1398    offset_content: bool,
 1399    show_line_numbers: Option<bool>,
 1400    number_deleted_lines: bool,
 1401    show_git_diff_gutter: Option<bool>,
 1402    show_code_actions: Option<bool>,
 1403    show_runnables: Option<bool>,
 1404    show_breakpoints: Option<bool>,
 1405    git_blame_gutter_max_author_length: Option<usize>,
 1406    pub display_snapshot: DisplaySnapshot,
 1407    pub placeholder_display_snapshot: Option<DisplaySnapshot>,
 1408    is_focused: bool,
 1409    scroll_anchor: SharedScrollAnchor,
 1410    ongoing_scroll: OngoingScroll,
 1411    current_line_highlight: CurrentLineHighlight,
 1412    gutter_hovered: bool,
 1413    semantic_tokens_enabled: bool,
 1414}
 1415
 1416#[derive(Default, Debug, Clone, Copy)]
 1417pub struct GutterDimensions {
 1418    pub left_padding: Pixels,
 1419    pub right_padding: Pixels,
 1420    pub width: Pixels,
 1421    pub margin: Pixels,
 1422    pub git_blame_entries_width: Option<Pixels>,
 1423}
 1424
 1425impl GutterDimensions {
 1426    fn default_with_margin(font_id: FontId, font_size: Pixels, cx: &App) -> Self {
 1427        Self {
 1428            margin: Self::default_gutter_margin(font_id, font_size, cx),
 1429            ..Default::default()
 1430        }
 1431    }
 1432
 1433    fn default_gutter_margin(font_id: FontId, font_size: Pixels, cx: &App) -> Pixels {
 1434        -cx.text_system().descent(font_id, font_size)
 1435    }
 1436    /// The full width of the space taken up by the gutter.
 1437    pub fn full_width(&self) -> Pixels {
 1438        self.margin + self.width
 1439    }
 1440
 1441    /// The width of the space reserved for the fold indicators,
 1442    /// use alongside 'justify_end' and `gutter_width` to
 1443    /// right align content with the line numbers
 1444    pub fn fold_area_width(&self) -> Pixels {
 1445        self.margin + self.right_padding
 1446    }
 1447}
 1448
 1449struct CharacterDimensions {
 1450    em_width: Pixels,
 1451    em_advance: Pixels,
 1452    line_height: Pixels,
 1453}
 1454
 1455#[derive(Debug)]
 1456pub struct RemoteSelection {
 1457    pub replica_id: ReplicaId,
 1458    pub selection: Selection<Anchor>,
 1459    pub cursor_shape: CursorShape,
 1460    pub collaborator_id: CollaboratorId,
 1461    pub line_mode: bool,
 1462    pub user_name: Option<SharedString>,
 1463    pub color: PlayerColor,
 1464}
 1465
 1466#[derive(Clone, Debug)]
 1467struct SelectionHistoryEntry {
 1468    selections: Arc<[Selection<Anchor>]>,
 1469    select_next_state: Option<SelectNextState>,
 1470    select_prev_state: Option<SelectNextState>,
 1471    add_selections_state: Option<AddSelectionsState>,
 1472}
 1473
 1474#[derive(Copy, Clone, Default, Debug, PartialEq, Eq)]
 1475enum SelectionHistoryMode {
 1476    #[default]
 1477    Normal,
 1478    Undoing,
 1479    Redoing,
 1480    Skipping,
 1481}
 1482
 1483#[derive(Clone, PartialEq, Eq, Hash)]
 1484struct HoveredCursor {
 1485    replica_id: ReplicaId,
 1486    selection_id: usize,
 1487}
 1488
 1489#[derive(Debug)]
 1490/// SelectionEffects controls the side-effects of updating the selection.
 1491///
 1492/// The default behaviour does "what you mostly want":
 1493/// - it pushes to the nav history if the cursor moved by >10 lines
 1494/// - it re-triggers completion requests
 1495/// - it scrolls to fit
 1496///
 1497/// You might want to modify these behaviours. For example when doing a "jump"
 1498/// like go to definition, we always want to add to nav history; but when scrolling
 1499/// in vim mode we never do.
 1500///
 1501/// Similarly, you might want to disable scrolling if you don't want the viewport to
 1502/// move.
 1503#[derive(Clone)]
 1504pub struct SelectionEffects {
 1505    nav_history: Option<bool>,
 1506    completions: bool,
 1507    scroll: Option<Autoscroll>,
 1508}
 1509
 1510impl Default for SelectionEffects {
 1511    fn default() -> Self {
 1512        Self {
 1513            nav_history: None,
 1514            completions: true,
 1515            scroll: Some(Autoscroll::fit()),
 1516        }
 1517    }
 1518}
 1519impl SelectionEffects {
 1520    pub fn scroll(scroll: Autoscroll) -> Self {
 1521        Self {
 1522            scroll: Some(scroll),
 1523            ..Default::default()
 1524        }
 1525    }
 1526
 1527    pub fn no_scroll() -> Self {
 1528        Self {
 1529            scroll: None,
 1530            ..Default::default()
 1531        }
 1532    }
 1533
 1534    pub fn completions(self, completions: bool) -> Self {
 1535        Self {
 1536            completions,
 1537            ..self
 1538        }
 1539    }
 1540
 1541    pub fn nav_history(self, nav_history: bool) -> Self {
 1542        Self {
 1543            nav_history: Some(nav_history),
 1544            ..self
 1545        }
 1546    }
 1547}
 1548
 1549struct DeferredSelectionEffectsState {
 1550    changed: bool,
 1551    effects: SelectionEffects,
 1552    old_cursor_position: Anchor,
 1553    history_entry: SelectionHistoryEntry,
 1554}
 1555
 1556#[derive(Default)]
 1557struct SelectionHistory {
 1558    #[allow(clippy::type_complexity)]
 1559    selections_by_transaction:
 1560        HashMap<TransactionId, (Arc<[Selection<Anchor>]>, Option<Arc<[Selection<Anchor>]>>)>,
 1561    mode: SelectionHistoryMode,
 1562    undo_stack: VecDeque<SelectionHistoryEntry>,
 1563    redo_stack: VecDeque<SelectionHistoryEntry>,
 1564}
 1565
 1566impl SelectionHistory {
 1567    #[track_caller]
 1568    fn insert_transaction(
 1569        &mut self,
 1570        transaction_id: TransactionId,
 1571        selections: Arc<[Selection<Anchor>]>,
 1572    ) {
 1573        if selections.is_empty() {
 1574            log::error!(
 1575                "SelectionHistory::insert_transaction called with empty selections. Caller: {}",
 1576                std::panic::Location::caller()
 1577            );
 1578            return;
 1579        }
 1580        self.selections_by_transaction
 1581            .insert(transaction_id, (selections, None));
 1582    }
 1583
 1584    #[allow(clippy::type_complexity)]
 1585    fn transaction(
 1586        &self,
 1587        transaction_id: TransactionId,
 1588    ) -> Option<&(Arc<[Selection<Anchor>]>, Option<Arc<[Selection<Anchor>]>>)> {
 1589        self.selections_by_transaction.get(&transaction_id)
 1590    }
 1591
 1592    #[allow(clippy::type_complexity)]
 1593    fn transaction_mut(
 1594        &mut self,
 1595        transaction_id: TransactionId,
 1596    ) -> Option<&mut (Arc<[Selection<Anchor>]>, Option<Arc<[Selection<Anchor>]>>)> {
 1597        self.selections_by_transaction.get_mut(&transaction_id)
 1598    }
 1599
 1600    fn push(&mut self, entry: SelectionHistoryEntry) {
 1601        if !entry.selections.is_empty() {
 1602            match self.mode {
 1603                SelectionHistoryMode::Normal => {
 1604                    self.push_undo(entry);
 1605                    self.redo_stack.clear();
 1606                }
 1607                SelectionHistoryMode::Undoing => self.push_redo(entry),
 1608                SelectionHistoryMode::Redoing => self.push_undo(entry),
 1609                SelectionHistoryMode::Skipping => {}
 1610            }
 1611        }
 1612    }
 1613
 1614    fn push_undo(&mut self, entry: SelectionHistoryEntry) {
 1615        if self
 1616            .undo_stack
 1617            .back()
 1618            .is_none_or(|e| e.selections != entry.selections)
 1619        {
 1620            self.undo_stack.push_back(entry);
 1621            if self.undo_stack.len() > MAX_SELECTION_HISTORY_LEN {
 1622                self.undo_stack.pop_front();
 1623            }
 1624        }
 1625    }
 1626
 1627    fn push_redo(&mut self, entry: SelectionHistoryEntry) {
 1628        if self
 1629            .redo_stack
 1630            .back()
 1631            .is_none_or(|e| e.selections != entry.selections)
 1632        {
 1633            self.redo_stack.push_back(entry);
 1634            if self.redo_stack.len() > MAX_SELECTION_HISTORY_LEN {
 1635                self.redo_stack.pop_front();
 1636            }
 1637        }
 1638    }
 1639}
 1640
 1641#[derive(Clone, Copy)]
 1642pub struct RowHighlightOptions {
 1643    pub autoscroll: bool,
 1644    pub include_gutter: bool,
 1645}
 1646
 1647impl Default for RowHighlightOptions {
 1648    fn default() -> Self {
 1649        Self {
 1650            autoscroll: Default::default(),
 1651            include_gutter: true,
 1652        }
 1653    }
 1654}
 1655
 1656struct RowHighlight {
 1657    index: usize,
 1658    range: Range<Anchor>,
 1659    color: Hsla,
 1660    options: RowHighlightOptions,
 1661    type_id: TypeId,
 1662}
 1663
 1664#[derive(Clone, Debug)]
 1665struct AddSelectionsState {
 1666    groups: Vec<AddSelectionsGroup>,
 1667}
 1668
 1669#[derive(Clone, Debug)]
 1670struct AddSelectionsGroup {
 1671    above: bool,
 1672    stack: Vec<usize>,
 1673}
 1674
 1675#[derive(Clone)]
 1676struct SelectNextState {
 1677    query: AhoCorasick,
 1678    wordwise: bool,
 1679    done: bool,
 1680}
 1681
 1682impl std::fmt::Debug for SelectNextState {
 1683    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
 1684        f.debug_struct(std::any::type_name::<Self>())
 1685            .field("wordwise", &self.wordwise)
 1686            .field("done", &self.done)
 1687            .finish()
 1688    }
 1689}
 1690
 1691#[derive(Debug)]
 1692struct AutocloseRegion {
 1693    selection_id: usize,
 1694    range: Range<Anchor>,
 1695    pair: BracketPair,
 1696}
 1697
 1698#[derive(Debug)]
 1699struct SnippetState {
 1700    ranges: Vec<Vec<Range<Anchor>>>,
 1701    active_index: usize,
 1702    choices: Vec<Option<Vec<String>>>,
 1703}
 1704
 1705#[doc(hidden)]
 1706pub struct RenameState {
 1707    pub range: Range<Anchor>,
 1708    pub old_name: Arc<str>,
 1709    pub editor: Entity<Editor>,
 1710    block_id: CustomBlockId,
 1711}
 1712
 1713struct InvalidationStack<T>(Vec<T>);
 1714
 1715struct RegisteredEditPredictionDelegate {
 1716    provider: Arc<dyn EditPredictionDelegateHandle>,
 1717    _subscription: Subscription,
 1718}
 1719
 1720#[derive(Debug, PartialEq, Eq)]
 1721pub struct ActiveDiagnosticGroup {
 1722    pub active_range: Range<Anchor>,
 1723    pub active_message: String,
 1724    pub group_id: usize,
 1725    pub blocks: HashSet<CustomBlockId>,
 1726}
 1727
 1728#[derive(Debug, PartialEq, Eq)]
 1729
 1730pub(crate) enum ActiveDiagnostic {
 1731    None,
 1732    All,
 1733    Group(ActiveDiagnosticGroup),
 1734}
 1735
 1736#[derive(Serialize, Deserialize, Clone, Debug)]
 1737pub struct ClipboardSelection {
 1738    /// The number of bytes in this selection.
 1739    pub len: usize,
 1740    /// Whether this was a full-line selection.
 1741    pub is_entire_line: bool,
 1742    /// The indentation of the first line when this content was originally copied.
 1743    pub first_line_indent: u32,
 1744    #[serde(default)]
 1745    pub file_path: Option<PathBuf>,
 1746    #[serde(default)]
 1747    pub line_range: Option<RangeInclusive<u32>>,
 1748}
 1749
 1750impl ClipboardSelection {
 1751    pub fn for_buffer(
 1752        len: usize,
 1753        is_entire_line: bool,
 1754        range: Range<Point>,
 1755        buffer: &MultiBufferSnapshot,
 1756        project: Option<&Entity<Project>>,
 1757        cx: &App,
 1758    ) -> Self {
 1759        let first_line_indent = buffer
 1760            .indent_size_for_line(MultiBufferRow(range.start.row))
 1761            .len;
 1762
 1763        let file_path = util::maybe!({
 1764            let project = project?.read(cx);
 1765            let file = buffer.file_at(range.start)?;
 1766            let project_path = ProjectPath {
 1767                worktree_id: file.worktree_id(cx),
 1768                path: file.path().clone(),
 1769            };
 1770            project.absolute_path(&project_path, cx)
 1771        });
 1772
 1773        let line_range = file_path.as_ref().and_then(|_| {
 1774            let (_, start_point, start_excerpt_id) = buffer.point_to_buffer_point(range.start)?;
 1775            let (_, end_point, end_excerpt_id) = buffer.point_to_buffer_point(range.end)?;
 1776            if start_excerpt_id == end_excerpt_id {
 1777                Some(start_point.row..=end_point.row)
 1778            } else {
 1779                None
 1780            }
 1781        });
 1782
 1783        Self {
 1784            len,
 1785            is_entire_line,
 1786            first_line_indent,
 1787            file_path,
 1788            line_range,
 1789        }
 1790    }
 1791}
 1792
 1793// selections, scroll behavior, was newest selection reversed
 1794type SelectSyntaxNodeHistoryState = (
 1795    Box<[Selection<Anchor>]>,
 1796    SelectSyntaxNodeScrollBehavior,
 1797    bool,
 1798);
 1799
 1800#[derive(Default)]
 1801struct SelectSyntaxNodeHistory {
 1802    stack: Vec<SelectSyntaxNodeHistoryState>,
 1803    // disable temporarily to allow changing selections without losing the stack
 1804    pub disable_clearing: bool,
 1805}
 1806
 1807impl SelectSyntaxNodeHistory {
 1808    pub fn try_clear(&mut self) {
 1809        if !self.disable_clearing {
 1810            self.stack.clear();
 1811        }
 1812    }
 1813
 1814    pub fn push(&mut self, selection: SelectSyntaxNodeHistoryState) {
 1815        self.stack.push(selection);
 1816    }
 1817
 1818    pub fn pop(&mut self) -> Option<SelectSyntaxNodeHistoryState> {
 1819        self.stack.pop()
 1820    }
 1821}
 1822
 1823enum SelectSyntaxNodeScrollBehavior {
 1824    CursorTop,
 1825    FitSelection,
 1826    CursorBottom,
 1827}
 1828
 1829#[derive(Debug, Clone, Copy)]
 1830pub(crate) struct NavigationData {
 1831    cursor_anchor: Anchor,
 1832    cursor_position: Point,
 1833    scroll_anchor: ScrollAnchor,
 1834    scroll_top_row: u32,
 1835}
 1836
 1837#[derive(Debug, Clone, Copy, PartialEq, Eq)]
 1838pub enum GotoDefinitionKind {
 1839    Symbol,
 1840    Declaration,
 1841    Type,
 1842    Implementation,
 1843}
 1844
 1845pub enum FormatTarget {
 1846    Buffers(HashSet<Entity<Buffer>>),
 1847    Ranges(Vec<Range<MultiBufferPoint>>),
 1848}
 1849
 1850pub(crate) struct FocusedBlock {
 1851    id: BlockId,
 1852    focus_handle: WeakFocusHandle,
 1853}
 1854
 1855#[derive(Clone, Debug)]
 1856pub enum JumpData {
 1857    MultiBufferRow {
 1858        row: MultiBufferRow,
 1859        line_offset_from_top: u32,
 1860    },
 1861    MultiBufferPoint {
 1862        excerpt_id: ExcerptId,
 1863        position: Point,
 1864        anchor: text::Anchor,
 1865        line_offset_from_top: u32,
 1866    },
 1867}
 1868
 1869pub enum MultibufferSelectionMode {
 1870    First,
 1871    All,
 1872}
 1873
 1874#[derive(Clone, Copy, Debug, Default)]
 1875pub struct RewrapOptions {
 1876    pub override_language_settings: bool,
 1877    pub preserve_existing_whitespace: bool,
 1878}
 1879
 1880impl Editor {
 1881    pub fn single_line(window: &mut Window, cx: &mut Context<Self>) -> Self {
 1882        let buffer = cx.new(|cx| Buffer::local("", cx));
 1883        let buffer = cx.new(|cx| MultiBuffer::singleton(buffer, cx));
 1884        Self::new(EditorMode::SingleLine, buffer, None, window, cx)
 1885    }
 1886
 1887    pub fn multi_line(window: &mut Window, cx: &mut Context<Self>) -> Self {
 1888        let buffer = cx.new(|cx| Buffer::local("", cx));
 1889        let buffer = cx.new(|cx| MultiBuffer::singleton(buffer, cx));
 1890        Self::new(EditorMode::full(), buffer, None, window, cx)
 1891    }
 1892
 1893    pub fn auto_height(
 1894        min_lines: usize,
 1895        max_lines: usize,
 1896        window: &mut Window,
 1897        cx: &mut Context<Self>,
 1898    ) -> Self {
 1899        let buffer = cx.new(|cx| Buffer::local("", cx));
 1900        let buffer = cx.new(|cx| MultiBuffer::singleton(buffer, cx));
 1901        Self::new(
 1902            EditorMode::AutoHeight {
 1903                min_lines,
 1904                max_lines: Some(max_lines),
 1905            },
 1906            buffer,
 1907            None,
 1908            window,
 1909            cx,
 1910        )
 1911    }
 1912
 1913    /// Creates a new auto-height editor with a minimum number of lines but no maximum.
 1914    /// The editor grows as tall as needed to fit its content.
 1915    pub fn auto_height_unbounded(
 1916        min_lines: usize,
 1917        window: &mut Window,
 1918        cx: &mut Context<Self>,
 1919    ) -> Self {
 1920        let buffer = cx.new(|cx| Buffer::local("", cx));
 1921        let buffer = cx.new(|cx| MultiBuffer::singleton(buffer, cx));
 1922        Self::new(
 1923            EditorMode::AutoHeight {
 1924                min_lines,
 1925                max_lines: None,
 1926            },
 1927            buffer,
 1928            None,
 1929            window,
 1930            cx,
 1931        )
 1932    }
 1933
 1934    pub fn for_buffer(
 1935        buffer: Entity<Buffer>,
 1936        project: Option<Entity<Project>>,
 1937        window: &mut Window,
 1938        cx: &mut Context<Self>,
 1939    ) -> Self {
 1940        let buffer = cx.new(|cx| MultiBuffer::singleton(buffer, cx));
 1941        Self::new(EditorMode::full(), buffer, project, window, cx)
 1942    }
 1943
 1944    pub fn for_multibuffer(
 1945        buffer: Entity<MultiBuffer>,
 1946        project: Option<Entity<Project>>,
 1947        window: &mut Window,
 1948        cx: &mut Context<Self>,
 1949    ) -> Self {
 1950        Self::new(EditorMode::full(), buffer, project, window, cx)
 1951    }
 1952
 1953    pub fn clone(&self, window: &mut Window, cx: &mut Context<Self>) -> Self {
 1954        let mut clone = Self::new(
 1955            self.mode.clone(),
 1956            self.buffer.clone(),
 1957            self.project.clone(),
 1958            window,
 1959            cx,
 1960        );
 1961        let my_snapshot = self.display_map.update(cx, |display_map, cx| {
 1962            let snapshot = display_map.snapshot(cx);
 1963            clone.display_map.update(cx, |display_map, cx| {
 1964                display_map.set_state(&snapshot, cx);
 1965            });
 1966            snapshot
 1967        });
 1968        let clone_snapshot = clone.display_map.update(cx, |map, cx| map.snapshot(cx));
 1969        clone.folds_did_change(cx);
 1970        clone.selections.clone_state(&self.selections);
 1971        clone
 1972            .scroll_manager
 1973            .clone_state(&self.scroll_manager, &my_snapshot, &clone_snapshot, cx);
 1974        clone.searchable = self.searchable;
 1975        clone.read_only = self.read_only;
 1976        clone.buffers_with_disabled_indent_guides =
 1977            self.buffers_with_disabled_indent_guides.clone();
 1978        clone
 1979    }
 1980
 1981    pub fn new(
 1982        mode: EditorMode,
 1983        buffer: Entity<MultiBuffer>,
 1984        project: Option<Entity<Project>>,
 1985        window: &mut Window,
 1986        cx: &mut Context<Self>,
 1987    ) -> Self {
 1988        Editor::new_internal(mode, buffer, project, None, window, cx)
 1989    }
 1990
 1991    pub fn refresh_sticky_headers(
 1992        &mut self,
 1993        display_snapshot: &DisplaySnapshot,
 1994        cx: &mut Context<Editor>,
 1995    ) {
 1996        if !self.mode.is_full() {
 1997            return;
 1998        }
 1999        let multi_buffer = display_snapshot.buffer_snapshot();
 2000        let scroll_anchor = self
 2001            .scroll_manager
 2002            .native_anchor(display_snapshot, cx)
 2003            .anchor;
 2004        let Some((excerpt_id, _, buffer)) = multi_buffer.as_singleton() else {
 2005            return;
 2006        };
 2007        let buffer = buffer.clone();
 2008
 2009        let buffer_visible_start = scroll_anchor.text_anchor.to_point(&buffer);
 2010        let max_row = buffer.max_point().row;
 2011        let start_row = buffer_visible_start.row.min(max_row);
 2012        let end_row = (buffer_visible_start.row + 10).min(max_row);
 2013
 2014        let syntax = self.style(cx).syntax.clone();
 2015        let background_task = cx.background_spawn(async move {
 2016            buffer
 2017                .outline_items_containing(
 2018                    Point::new(start_row, 0)..Point::new(end_row, 0),
 2019                    true,
 2020                    Some(syntax.as_ref()),
 2021                )
 2022                .into_iter()
 2023                .map(|outline_item| OutlineItem {
 2024                    depth: outline_item.depth,
 2025                    range: Anchor::range_in_buffer(excerpt_id, outline_item.range),
 2026                    source_range_for_text: Anchor::range_in_buffer(
 2027                        excerpt_id,
 2028                        outline_item.source_range_for_text,
 2029                    ),
 2030                    text: outline_item.text,
 2031                    highlight_ranges: outline_item.highlight_ranges,
 2032                    name_ranges: outline_item.name_ranges,
 2033                    body_range: outline_item
 2034                        .body_range
 2035                        .map(|range| Anchor::range_in_buffer(excerpt_id, range)),
 2036                    annotation_range: outline_item
 2037                        .annotation_range
 2038                        .map(|range| Anchor::range_in_buffer(excerpt_id, range)),
 2039                })
 2040                .collect()
 2041        });
 2042        self.sticky_headers_task = cx.spawn(async move |this, cx| {
 2043            let sticky_headers = background_task.await;
 2044            this.update(cx, |this, cx| {
 2045                this.sticky_headers = Some(sticky_headers);
 2046                cx.notify();
 2047            })
 2048            .ok();
 2049        });
 2050    }
 2051
 2052    fn new_internal(
 2053        mode: EditorMode,
 2054        multi_buffer: Entity<MultiBuffer>,
 2055        project: Option<Entity<Project>>,
 2056        display_map: Option<Entity<DisplayMap>>,
 2057        window: &mut Window,
 2058        cx: &mut Context<Self>,
 2059    ) -> Self {
 2060        debug_assert!(
 2061            display_map.is_none() || mode.is_minimap(),
 2062            "Providing a display map for a new editor is only intended for the minimap and might have unintended side effects otherwise!"
 2063        );
 2064
 2065        let full_mode = mode.is_full();
 2066        let is_minimap = mode.is_minimap();
 2067        let diagnostics_max_severity = if full_mode {
 2068            EditorSettings::get_global(cx)
 2069                .diagnostics_max_severity
 2070                .unwrap_or(DiagnosticSeverity::Hint)
 2071        } else {
 2072            DiagnosticSeverity::Off
 2073        };
 2074        let style = window.text_style();
 2075        let font_size = style.font_size.to_pixels(window.rem_size());
 2076        let editor = cx.entity().downgrade();
 2077        let fold_placeholder = FoldPlaceholder {
 2078            constrain_width: false,
 2079            render: Arc::new(move |fold_id, fold_range, cx| {
 2080                let editor = editor.clone();
 2081                FoldPlaceholder::fold_element(fold_id, cx)
 2082                    .cursor_pointer()
 2083                    .child("")
 2084                    .on_mouse_down(MouseButton::Left, |_, _, cx| cx.stop_propagation())
 2085                    .on_click(move |_, _window, cx| {
 2086                        editor
 2087                            .update(cx, |editor, cx| {
 2088                                editor.unfold_ranges(
 2089                                    &[fold_range.start..fold_range.end],
 2090                                    true,
 2091                                    false,
 2092                                    cx,
 2093                                );
 2094                                cx.stop_propagation();
 2095                            })
 2096                            .ok();
 2097                    })
 2098                    .into_any()
 2099            }),
 2100            merge_adjacent: true,
 2101            ..FoldPlaceholder::default()
 2102        };
 2103        let display_map = display_map.unwrap_or_else(|| {
 2104            cx.new(|cx| {
 2105                DisplayMap::new(
 2106                    multi_buffer.clone(),
 2107                    style.font(),
 2108                    font_size,
 2109                    None,
 2110                    FILE_HEADER_HEIGHT,
 2111                    MULTI_BUFFER_EXCERPT_HEADER_HEIGHT,
 2112                    fold_placeholder,
 2113                    diagnostics_max_severity,
 2114                    cx,
 2115                )
 2116            })
 2117        });
 2118
 2119        let selections = SelectionsCollection::new();
 2120
 2121        let blink_manager = cx.new(|cx| {
 2122            let mut blink_manager = BlinkManager::new(
 2123                CURSOR_BLINK_INTERVAL,
 2124                |cx| EditorSettings::get_global(cx).cursor_blink,
 2125                cx,
 2126            );
 2127            if is_minimap {
 2128                blink_manager.disable(cx);
 2129            }
 2130            blink_manager
 2131        });
 2132
 2133        let soft_wrap_mode_override =
 2134            matches!(mode, EditorMode::SingleLine).then(|| language_settings::SoftWrap::None);
 2135
 2136        let mut project_subscriptions = Vec::new();
 2137        if full_mode && let Some(project) = project.as_ref() {
 2138            project_subscriptions.push(cx.subscribe_in(
 2139                project,
 2140                window,
 2141                |editor, _, event, window, cx| match event {
 2142                    project::Event::RefreshCodeLens => {
 2143                        // we always query lens with actions, without storing them, always refreshing them
 2144                    }
 2145                    project::Event::RefreshInlayHints {
 2146                        server_id,
 2147                        request_id,
 2148                    } => {
 2149                        editor.refresh_inlay_hints(
 2150                            InlayHintRefreshReason::RefreshRequested {
 2151                                server_id: *server_id,
 2152                                request_id: *request_id,
 2153                            },
 2154                            cx,
 2155                        );
 2156                    }
 2157                    project::Event::RefreshSemanticTokens {
 2158                        server_id,
 2159                        request_id,
 2160                    } => {
 2161                        editor.refresh_semantic_tokens(
 2162                            None,
 2163                            Some(RefreshForServer {
 2164                                server_id: *server_id,
 2165                                request_id: *request_id,
 2166                            }),
 2167                            cx,
 2168                        );
 2169                    }
 2170                    project::Event::LanguageServerRemoved(_) => {
 2171                        editor.registered_buffers.clear();
 2172                        editor.register_visible_buffers(cx);
 2173                        editor.invalidate_semantic_tokens(None);
 2174                        editor.update_lsp_data(None, window, cx);
 2175                        editor.refresh_inlay_hints(InlayHintRefreshReason::ServerRemoved, cx);
 2176                        if editor.tasks_update_task.is_none() {
 2177                            editor.tasks_update_task = Some(editor.refresh_runnables(window, cx));
 2178                        }
 2179                    }
 2180                    project::Event::LanguageServerAdded(..) => {
 2181                        if editor.tasks_update_task.is_none() {
 2182                            editor.tasks_update_task = Some(editor.refresh_runnables(window, cx));
 2183                        }
 2184                    }
 2185                    project::Event::SnippetEdit(id, snippet_edits) => {
 2186                        // todo(lw): Non singletons
 2187                        if let Some(buffer) = editor.buffer.read(cx).as_singleton() {
 2188                            let snapshot = buffer.read(cx).snapshot();
 2189                            let focus_handle = editor.focus_handle(cx);
 2190                            if snapshot.remote_id() == *id && focus_handle.is_focused(window) {
 2191                                for (range, snippet) in snippet_edits {
 2192                                    let buffer_range =
 2193                                        language::range_from_lsp(*range).to_offset(&snapshot);
 2194                                    editor
 2195                                        .insert_snippet(
 2196                                            &[MultiBufferOffset(buffer_range.start)
 2197                                                ..MultiBufferOffset(buffer_range.end)],
 2198                                            snippet.clone(),
 2199                                            window,
 2200                                            cx,
 2201                                        )
 2202                                        .ok();
 2203                                }
 2204                            }
 2205                        }
 2206                    }
 2207                    project::Event::LanguageServerBufferRegistered { buffer_id, .. } => {
 2208                        let buffer_id = *buffer_id;
 2209                        if editor.buffer().read(cx).buffer(buffer_id).is_some() {
 2210                            editor.register_buffer(buffer_id, cx);
 2211                            editor.update_lsp_data(Some(buffer_id), window, cx);
 2212                            editor.refresh_inlay_hints(InlayHintRefreshReason::NewLinesShown, cx);
 2213                            refresh_linked_ranges(editor, window, cx);
 2214                            editor.refresh_code_actions(window, cx);
 2215                            editor.refresh_document_highlights(cx);
 2216                        }
 2217                    }
 2218
 2219                    project::Event::EntryRenamed(transaction, project_path, abs_path) => {
 2220                        let Some(workspace) = editor.workspace() else {
 2221                            return;
 2222                        };
 2223                        let Some(active_editor) = workspace.read(cx).active_item_as::<Self>(cx)
 2224                        else {
 2225                            return;
 2226                        };
 2227
 2228                        if active_editor.entity_id() == cx.entity_id() {
 2229                            let entity_id = cx.entity_id();
 2230                            workspace.update(cx, |this, cx| {
 2231                                this.panes_mut()
 2232                                    .iter_mut()
 2233                                    .filter(|pane| pane.entity_id() != entity_id)
 2234                                    .for_each(|p| {
 2235                                        p.update(cx, |pane, _| {
 2236                                            pane.nav_history_mut().rename_item(
 2237                                                entity_id,
 2238                                                project_path.clone(),
 2239                                                abs_path.clone().into(),
 2240                                            );
 2241                                        })
 2242                                    });
 2243                            });
 2244
 2245                            Self::open_transaction_for_hidden_buffers(
 2246                                workspace,
 2247                                transaction.clone(),
 2248                                "Rename".to_string(),
 2249                                window,
 2250                                cx,
 2251                            );
 2252                        }
 2253                    }
 2254
 2255                    project::Event::WorkspaceEditApplied(transaction) => {
 2256                        let Some(workspace) = editor.workspace() else {
 2257                            return;
 2258                        };
 2259                        let Some(active_editor) = workspace.read(cx).active_item_as::<Self>(cx)
 2260                        else {
 2261                            return;
 2262                        };
 2263
 2264                        if active_editor.entity_id() == cx.entity_id() {
 2265                            Self::open_transaction_for_hidden_buffers(
 2266                                workspace,
 2267                                transaction.clone(),
 2268                                "LSP Edit".to_string(),
 2269                                window,
 2270                                cx,
 2271                            );
 2272                        }
 2273                    }
 2274
 2275                    _ => {}
 2276                },
 2277            ));
 2278            if let Some(task_inventory) = project
 2279                .read(cx)
 2280                .task_store()
 2281                .read(cx)
 2282                .task_inventory()
 2283                .cloned()
 2284            {
 2285                project_subscriptions.push(cx.observe_in(
 2286                    &task_inventory,
 2287                    window,
 2288                    |editor, _, window, cx| {
 2289                        editor.tasks_update_task = Some(editor.refresh_runnables(window, cx));
 2290                    },
 2291                ));
 2292            };
 2293
 2294            project_subscriptions.push(cx.subscribe_in(
 2295                &project.read(cx).breakpoint_store(),
 2296                window,
 2297                |editor, _, event, window, cx| match event {
 2298                    BreakpointStoreEvent::ClearDebugLines => {
 2299                        editor.clear_row_highlights::<ActiveDebugLine>();
 2300                        editor.refresh_inline_values(cx);
 2301                    }
 2302                    BreakpointStoreEvent::SetDebugLine => {
 2303                        if editor.go_to_active_debug_line(window, cx) {
 2304                            cx.stop_propagation();
 2305                        }
 2306
 2307                        editor.refresh_inline_values(cx);
 2308                    }
 2309                    _ => {}
 2310                },
 2311            ));
 2312            let git_store = project.read(cx).git_store().clone();
 2313            let project = project.clone();
 2314            project_subscriptions.push(cx.subscribe(&git_store, move |this, _, event, cx| {
 2315                if let GitStoreEvent::RepositoryAdded = event {
 2316                    this.load_diff_task = Some(
 2317                        update_uncommitted_diff_for_buffer(
 2318                            cx.entity(),
 2319                            &project,
 2320                            this.buffer.read(cx).all_buffers(),
 2321                            this.buffer.clone(),
 2322                            cx,
 2323                        )
 2324                        .shared(),
 2325                    );
 2326                }
 2327            }));
 2328        }
 2329
 2330        let buffer_snapshot = multi_buffer.read(cx).snapshot(cx);
 2331
 2332        let inlay_hint_settings =
 2333            inlay_hint_settings(selections.newest_anchor().head(), &buffer_snapshot, cx);
 2334        let focus_handle = cx.focus_handle();
 2335        if !is_minimap {
 2336            cx.on_focus(&focus_handle, window, Self::handle_focus)
 2337                .detach();
 2338            cx.on_focus_in(&focus_handle, window, Self::handle_focus_in)
 2339                .detach();
 2340            cx.on_focus_out(&focus_handle, window, Self::handle_focus_out)
 2341                .detach();
 2342            cx.on_blur(&focus_handle, window, Self::handle_blur)
 2343                .detach();
 2344            cx.observe_pending_input(window, Self::observe_pending_input)
 2345                .detach();
 2346        }
 2347
 2348        let show_indent_guides =
 2349            if matches!(mode, EditorMode::SingleLine | EditorMode::Minimap { .. }) {
 2350                Some(false)
 2351            } else {
 2352                None
 2353            };
 2354
 2355        let breakpoint_store = match (&mode, project.as_ref()) {
 2356            (EditorMode::Full { .. }, Some(project)) => Some(project.read(cx).breakpoint_store()),
 2357            _ => None,
 2358        };
 2359
 2360        let mut code_action_providers = Vec::new();
 2361        let mut load_uncommitted_diff = None;
 2362        if let Some(project) = project.clone() {
 2363            load_uncommitted_diff = Some(
 2364                update_uncommitted_diff_for_buffer(
 2365                    cx.entity(),
 2366                    &project,
 2367                    multi_buffer.read(cx).all_buffers(),
 2368                    multi_buffer.clone(),
 2369                    cx,
 2370                )
 2371                .shared(),
 2372            );
 2373            code_action_providers.push(Rc::new(project) as Rc<_>);
 2374        }
 2375
 2376        let mut editor = Self {
 2377            focus_handle,
 2378            show_cursor_when_unfocused: false,
 2379            last_focused_descendant: None,
 2380            buffer: multi_buffer.clone(),
 2381            display_map: display_map.clone(),
 2382            placeholder_display_map: None,
 2383            selections,
 2384            scroll_manager: ScrollManager::new(cx),
 2385            columnar_selection_state: None,
 2386            add_selections_state: None,
 2387            select_next_state: None,
 2388            select_prev_state: None,
 2389            selection_history: SelectionHistory::default(),
 2390            defer_selection_effects: false,
 2391            deferred_selection_effects_state: None,
 2392            autoclose_regions: Vec::new(),
 2393            snippet_stack: InvalidationStack::default(),
 2394            select_syntax_node_history: SelectSyntaxNodeHistory::default(),
 2395            ime_transaction: None,
 2396            active_diagnostics: ActiveDiagnostic::None,
 2397            show_inline_diagnostics: ProjectSettings::get_global(cx).diagnostics.inline.enabled,
 2398            inline_diagnostics_update: Task::ready(()),
 2399            inline_diagnostics: Vec::new(),
 2400            soft_wrap_mode_override,
 2401            diagnostics_max_severity,
 2402            hard_wrap: None,
 2403            completion_provider: project.clone().map(|project| Rc::new(project) as _),
 2404            semantics_provider: project
 2405                .as_ref()
 2406                .map(|project| Rc::new(project.downgrade()) as _),
 2407            collaboration_hub: project.clone().map(|project| Box::new(project) as _),
 2408            project,
 2409            blink_manager: blink_manager.clone(),
 2410            show_local_selections: true,
 2411            show_scrollbars: ScrollbarAxes {
 2412                horizontal: full_mode,
 2413                vertical: full_mode,
 2414            },
 2415            minimap_visibility: MinimapVisibility::for_mode(&mode, cx),
 2416            offset_content: !matches!(mode, EditorMode::SingleLine),
 2417            show_breadcrumbs: EditorSettings::get_global(cx).toolbar.breadcrumbs,
 2418            show_gutter: full_mode,
 2419            show_line_numbers: (!full_mode).then_some(false),
 2420            use_relative_line_numbers: None,
 2421            disable_expand_excerpt_buttons: !full_mode,
 2422            delegate_expand_excerpts: false,
 2423            delegate_stage_and_restore: false,
 2424            delegate_open_excerpts: false,
 2425            enable_lsp_data: true,
 2426            enable_runnables: true,
 2427            show_git_diff_gutter: None,
 2428            show_code_actions: None,
 2429            show_runnables: None,
 2430            show_breakpoints: None,
 2431            show_diff_review_button: false,
 2432            show_wrap_guides: None,
 2433            show_indent_guides,
 2434            buffers_with_disabled_indent_guides: HashSet::default(),
 2435            highlight_order: 0,
 2436            highlighted_rows: HashMap::default(),
 2437            background_highlights: HashMap::default(),
 2438            gutter_highlights: HashMap::default(),
 2439            scrollbar_marker_state: ScrollbarMarkerState::default(),
 2440            active_indent_guides_state: ActiveIndentGuidesState::default(),
 2441            nav_history: None,
 2442            context_menu: RefCell::new(None),
 2443            context_menu_options: None,
 2444            mouse_context_menu: None,
 2445            completion_tasks: Vec::new(),
 2446            inline_blame_popover: None,
 2447            inline_blame_popover_show_task: None,
 2448            signature_help_state: SignatureHelpState::default(),
 2449            auto_signature_help: None,
 2450            find_all_references_task_sources: Vec::new(),
 2451            next_completion_id: 0,
 2452            next_inlay_id: 0,
 2453            code_action_providers,
 2454            available_code_actions: None,
 2455            code_actions_task: None,
 2456            quick_selection_highlight_task: None,
 2457            debounced_selection_highlight_task: None,
 2458            debounced_selection_highlight_complete: false,
 2459            document_highlights_task: None,
 2460            linked_editing_range_task: None,
 2461            pending_rename: None,
 2462            searchable: !is_minimap,
 2463            cursor_shape: EditorSettings::get_global(cx)
 2464                .cursor_shape
 2465                .unwrap_or_default(),
 2466            cursor_offset_on_selection: false,
 2467            current_line_highlight: None,
 2468            autoindent_mode: Some(AutoindentMode::EachLine),
 2469            collapse_matches: false,
 2470            workspace: None,
 2471            input_enabled: !is_minimap,
 2472            use_modal_editing: full_mode,
 2473            read_only: is_minimap,
 2474            use_autoclose: true,
 2475            use_auto_surround: true,
 2476            auto_replace_emoji_shortcode: false,
 2477            jsx_tag_auto_close_enabled_in_any_buffer: false,
 2478            leader_id: None,
 2479            remote_id: None,
 2480            hover_state: HoverState::default(),
 2481            pending_mouse_down: None,
 2482            prev_pressure_stage: None,
 2483            hovered_link_state: None,
 2484            edit_prediction_provider: None,
 2485            active_edit_prediction: None,
 2486            stale_edit_prediction_in_menu: None,
 2487            edit_prediction_preview: EditPredictionPreview::Inactive {
 2488                released_too_fast: false,
 2489            },
 2490            inline_diagnostics_enabled: full_mode,
 2491            diagnostics_enabled: full_mode,
 2492            word_completions_enabled: full_mode,
 2493            inline_value_cache: InlineValueCache::new(inlay_hint_settings.show_value_hints),
 2494            gutter_hovered: false,
 2495            pixel_position_of_newest_cursor: None,
 2496            last_bounds: None,
 2497            last_position_map: None,
 2498            expect_bounds_change: None,
 2499            gutter_dimensions: GutterDimensions::default(),
 2500            style: None,
 2501            show_cursor_names: false,
 2502            hovered_cursors: HashMap::default(),
 2503            next_editor_action_id: EditorActionId::default(),
 2504            editor_actions: Rc::default(),
 2505            edit_predictions_hidden_for_vim_mode: false,
 2506            show_edit_predictions_override: None,
 2507            show_completions_on_input_override: None,
 2508            menu_edit_predictions_policy: MenuEditPredictionsPolicy::ByProvider,
 2509            edit_prediction_settings: EditPredictionSettings::Disabled,
 2510            edit_prediction_indent_conflict: false,
 2511            edit_prediction_requires_modifier_in_indent_conflict: true,
 2512            custom_context_menu: None,
 2513            show_git_blame_gutter: false,
 2514            show_git_blame_inline: false,
 2515            show_selection_menu: None,
 2516            show_git_blame_inline_delay_task: None,
 2517            git_blame_inline_enabled: full_mode
 2518                && ProjectSettings::get_global(cx).git.inline_blame.enabled,
 2519            render_diff_hunk_controls: Arc::new(render_diff_hunk_controls),
 2520            buffer_serialization: is_minimap.not().then(|| {
 2521                BufferSerialization::new(
 2522                    ProjectSettings::get_global(cx)
 2523                        .session
 2524                        .restore_unsaved_buffers,
 2525                )
 2526            }),
 2527            blame: None,
 2528            blame_subscription: None,
 2529            tasks: BTreeMap::default(),
 2530
 2531            breakpoint_store,
 2532            gutter_breakpoint_indicator: (None, None),
 2533            gutter_diff_review_indicator: (None, None),
 2534            diff_review_drag_state: None,
 2535            diff_review_overlays: Vec::new(),
 2536            stored_review_comments: Vec::new(),
 2537            next_review_comment_id: 0,
 2538            hovered_diff_hunk_row: None,
 2539            _subscriptions: (!is_minimap)
 2540                .then(|| {
 2541                    vec![
 2542                        cx.observe(&multi_buffer, Self::on_buffer_changed),
 2543                        cx.subscribe_in(&multi_buffer, window, Self::on_buffer_event),
 2544                        cx.observe_in(&display_map, window, Self::on_display_map_changed),
 2545                        cx.observe(&blink_manager, |_, _, cx| cx.notify()),
 2546                        cx.observe_global_in::<SettingsStore>(window, Self::settings_changed),
 2547                        cx.observe_global_in::<GlobalTheme>(window, Self::theme_changed),
 2548                        observe_buffer_font_size_adjustment(cx, |_, cx| cx.notify()),
 2549                        cx.observe_window_activation(window, |editor, window, cx| {
 2550                            let active = window.is_window_active();
 2551                            editor.blink_manager.update(cx, |blink_manager, cx| {
 2552                                if active {
 2553                                    blink_manager.enable(cx);
 2554                                } else {
 2555                                    blink_manager.disable(cx);
 2556                                }
 2557                            });
 2558                            if active {
 2559                                editor.show_mouse_cursor(cx);
 2560                            }
 2561                        }),
 2562                    ]
 2563                })
 2564                .unwrap_or_default(),
 2565            tasks_update_task: None,
 2566            pull_diagnostics_task: Task::ready(()),
 2567            colors: None,
 2568            refresh_colors_task: Task::ready(()),
 2569            use_document_folding_ranges: false,
 2570            refresh_folding_ranges_task: Task::ready(()),
 2571            inlay_hints: None,
 2572            next_color_inlay_id: 0,
 2573            post_scroll_update: Task::ready(()),
 2574            linked_edit_ranges: Default::default(),
 2575            in_project_search: false,
 2576            previous_search_ranges: None,
 2577            breadcrumb_header: None,
 2578            focused_block: None,
 2579            next_scroll_position: NextScrollCursorCenterTopBottom::default(),
 2580            addons: HashMap::default(),
 2581            registered_buffers: HashMap::default(),
 2582            _scroll_cursor_center_top_bottom_task: Task::ready(()),
 2583            selection_mark_mode: false,
 2584            toggle_fold_multiple_buffers: Task::ready(()),
 2585            serialize_selections: Task::ready(()),
 2586            serialize_folds: Task::ready(()),
 2587            text_style_refinement: None,
 2588            load_diff_task: load_uncommitted_diff,
 2589            temporary_diff_override: false,
 2590            mouse_cursor_hidden: false,
 2591            minimap: None,
 2592            hide_mouse_mode: EditorSettings::get_global(cx)
 2593                .hide_mouse
 2594                .unwrap_or_default(),
 2595            change_list: ChangeList::new(),
 2596            mode,
 2597            selection_drag_state: SelectionDragState::None,
 2598            folding_newlines: Task::ready(()),
 2599            lookup_key: None,
 2600            select_next_is_case_sensitive: None,
 2601            on_local_selections_changed: None,
 2602            suppress_selection_callback: false,
 2603            applicable_language_settings: HashMap::default(),
 2604            semantic_token_state: SemanticTokenState::new(cx, full_mode),
 2605            accent_data: None,
 2606            bracket_fetched_tree_sitter_chunks: HashMap::default(),
 2607            number_deleted_lines: false,
 2608            refresh_matching_bracket_highlights_task: Task::ready(()),
 2609            refresh_document_symbols_task: Task::ready(()).shared(),
 2610            lsp_document_symbols: HashMap::default(),
 2611            refresh_outline_symbols_at_cursor_at_cursor_task: Task::ready(()),
 2612            outline_symbols_at_cursor: None,
 2613            sticky_headers_task: Task::ready(()),
 2614            sticky_headers: None,
 2615            colorize_brackets_task: Task::ready(()),
 2616        };
 2617
 2618        if is_minimap {
 2619            return editor;
 2620        }
 2621
 2622        editor.applicable_language_settings = editor.fetch_applicable_language_settings(cx);
 2623        editor.accent_data = editor.fetch_accent_data(cx);
 2624
 2625        if let Some(breakpoints) = editor.breakpoint_store.as_ref() {
 2626            editor
 2627                ._subscriptions
 2628                .push(cx.observe(breakpoints, |_, _, cx| {
 2629                    cx.notify();
 2630                }));
 2631        }
 2632        editor.tasks_update_task = Some(editor.refresh_runnables(window, cx));
 2633        editor._subscriptions.extend(project_subscriptions);
 2634
 2635        editor._subscriptions.push(cx.subscribe_in(
 2636            &cx.entity(),
 2637            window,
 2638            |editor, _, e: &EditorEvent, window, cx| match e {
 2639                EditorEvent::ScrollPositionChanged { local, .. } => {
 2640                    if *local {
 2641                        editor.hide_signature_help(cx, SignatureHelpHiddenBy::Escape);
 2642                        editor.inline_blame_popover.take();
 2643                        let snapshot = editor.snapshot(window, cx);
 2644                        let new_anchor = editor
 2645                            .scroll_manager
 2646                            .native_anchor(&snapshot.display_snapshot, cx);
 2647                        editor.update_restoration_data(cx, move |data| {
 2648                            data.scroll_position = (
 2649                                new_anchor.top_row(snapshot.buffer_snapshot()),
 2650                                new_anchor.offset,
 2651                            );
 2652                        });
 2653
 2654                        editor.post_scroll_update = cx.spawn_in(window, async move |editor, cx| {
 2655                            cx.background_executor()
 2656                                .timer(Duration::from_millis(50))
 2657                                .await;
 2658                            editor
 2659                                .update_in(cx, |editor, window, cx| {
 2660                                    editor.register_visible_buffers(cx);
 2661                                    editor.colorize_brackets(false, cx);
 2662                                    editor.refresh_inlay_hints(
 2663                                        InlayHintRefreshReason::NewLinesShown,
 2664                                        cx,
 2665                                    );
 2666                                    if !editor.buffer().read(cx).is_singleton() {
 2667                                        editor.update_lsp_data(None, window, cx);
 2668                                    }
 2669                                })
 2670                                .ok();
 2671                        });
 2672                    }
 2673                    editor.refresh_sticky_headers(&editor.snapshot(window, cx), cx);
 2674                }
 2675                EditorEvent::Edited { .. } => {
 2676                    let vim_mode = vim_mode_setting::VimModeSetting::try_get(cx)
 2677                        .map(|vim_mode| vim_mode.0)
 2678                        .unwrap_or(false);
 2679                    if !vim_mode {
 2680                        let display_map = editor.display_snapshot(cx);
 2681                        let selections = editor.selections.all_adjusted_display(&display_map);
 2682                        let pop_state = editor
 2683                            .change_list
 2684                            .last()
 2685                            .map(|previous| {
 2686                                previous.len() == selections.len()
 2687                                    && previous.iter().enumerate().all(|(ix, p)| {
 2688                                        p.to_display_point(&display_map).row()
 2689                                            == selections[ix].head().row()
 2690                                    })
 2691                            })
 2692                            .unwrap_or(false);
 2693                        let new_positions = selections
 2694                            .into_iter()
 2695                            .map(|s| display_map.display_point_to_anchor(s.head(), Bias::Left))
 2696                            .collect();
 2697                        editor
 2698                            .change_list
 2699                            .push_to_change_list(pop_state, new_positions);
 2700                    }
 2701                }
 2702                _ => (),
 2703            },
 2704        ));
 2705
 2706        if let Some(dap_store) = editor
 2707            .project
 2708            .as_ref()
 2709            .map(|project| project.read(cx).dap_store())
 2710        {
 2711            let weak_editor = cx.weak_entity();
 2712
 2713            editor
 2714                ._subscriptions
 2715                .push(
 2716                    cx.observe_new::<project::debugger::session::Session>(move |_, _, cx| {
 2717                        let session_entity = cx.entity();
 2718                        weak_editor
 2719                            .update(cx, |editor, cx| {
 2720                                editor._subscriptions.push(
 2721                                    cx.subscribe(&session_entity, Self::on_debug_session_event),
 2722                                );
 2723                            })
 2724                            .ok();
 2725                    }),
 2726                );
 2727
 2728            for session in dap_store.read(cx).sessions().cloned().collect::<Vec<_>>() {
 2729                editor
 2730                    ._subscriptions
 2731                    .push(cx.subscribe(&session, Self::on_debug_session_event));
 2732            }
 2733        }
 2734
 2735        // skip adding the initial selection to selection history
 2736        editor.selection_history.mode = SelectionHistoryMode::Skipping;
 2737        editor.end_selection(window, cx);
 2738        editor.selection_history.mode = SelectionHistoryMode::Normal;
 2739
 2740        editor.scroll_manager.show_scrollbars(window, cx);
 2741        jsx_tag_auto_close::refresh_enabled_in_any_buffer(&mut editor, &multi_buffer, cx);
 2742
 2743        if full_mode {
 2744            let should_auto_hide_scrollbars = cx.should_auto_hide_scrollbars();
 2745            cx.set_global(ScrollbarAutoHide(should_auto_hide_scrollbars));
 2746
 2747            if editor.git_blame_inline_enabled {
 2748                editor.start_git_blame_inline(false, window, cx);
 2749            }
 2750
 2751            editor.go_to_active_debug_line(window, cx);
 2752
 2753            editor.minimap =
 2754                editor.create_minimap(EditorSettings::get_global(cx).minimap, window, cx);
 2755            editor.colors = Some(LspColorData::new(cx));
 2756            editor.use_document_folding_ranges = true;
 2757            editor.inlay_hints = Some(LspInlayHintData::new(inlay_hint_settings));
 2758
 2759            if let Some(buffer) = multi_buffer.read(cx).as_singleton() {
 2760                editor.register_buffer(buffer.read(cx).remote_id(), cx);
 2761            }
 2762            editor.report_editor_event(ReportEditorEvent::EditorOpened, None, cx);
 2763        }
 2764
 2765        editor
 2766    }
 2767
 2768    pub fn display_snapshot(&self, cx: &mut App) -> DisplaySnapshot {
 2769        self.display_map.update(cx, |map, cx| map.snapshot(cx))
 2770    }
 2771
 2772    pub fn deploy_mouse_context_menu(
 2773        &mut self,
 2774        position: gpui::Point<Pixels>,
 2775        context_menu: Entity<ContextMenu>,
 2776        window: &mut Window,
 2777        cx: &mut Context<Self>,
 2778    ) {
 2779        self.mouse_context_menu = Some(MouseContextMenu::new(
 2780            self,
 2781            crate::mouse_context_menu::MenuPosition::PinnedToScreen(position),
 2782            context_menu,
 2783            window,
 2784            cx,
 2785        ));
 2786    }
 2787
 2788    pub fn mouse_menu_is_focused(&self, window: &Window, cx: &App) -> bool {
 2789        self.mouse_context_menu
 2790            .as_ref()
 2791            .is_some_and(|menu| menu.context_menu.focus_handle(cx).is_focused(window))
 2792    }
 2793
 2794    pub fn is_range_selected(&mut self, range: &Range<Anchor>, cx: &mut Context<Self>) -> bool {
 2795        if self
 2796            .selections
 2797            .pending_anchor()
 2798            .is_some_and(|pending_selection| {
 2799                let snapshot = self.buffer().read(cx).snapshot(cx);
 2800                pending_selection.range().includes(range, &snapshot)
 2801            })
 2802        {
 2803            return true;
 2804        }
 2805
 2806        self.selections
 2807            .disjoint_in_range::<MultiBufferOffset>(range.clone(), &self.display_snapshot(cx))
 2808            .into_iter()
 2809            .any(|selection| {
 2810                // This is needed to cover a corner case, if we just check for an existing
 2811                // selection in the fold range, having a cursor at the start of the fold
 2812                // marks it as selected. Non-empty selections don't cause this.
 2813                let length = selection.end - selection.start;
 2814                length > 0
 2815            })
 2816    }
 2817
 2818    pub fn key_context(&self, window: &mut Window, cx: &mut App) -> KeyContext {
 2819        self.key_context_internal(self.has_active_edit_prediction(), window, cx)
 2820    }
 2821
 2822    fn key_context_internal(
 2823        &self,
 2824        has_active_edit_prediction: bool,
 2825        window: &mut Window,
 2826        cx: &mut App,
 2827    ) -> KeyContext {
 2828        let mut key_context = KeyContext::new_with_defaults();
 2829        key_context.add("Editor");
 2830        let mode = match self.mode {
 2831            EditorMode::SingleLine => "single_line",
 2832            EditorMode::AutoHeight { .. } => "auto_height",
 2833            EditorMode::Minimap { .. } => "minimap",
 2834            EditorMode::Full { .. } => "full",
 2835        };
 2836
 2837        if EditorSettings::jupyter_enabled(cx) {
 2838            key_context.add("jupyter");
 2839        }
 2840
 2841        key_context.set("mode", mode);
 2842        if self.pending_rename.is_some() {
 2843            key_context.add("renaming");
 2844        }
 2845
 2846        if let Some(snippet_stack) = self.snippet_stack.last() {
 2847            key_context.add("in_snippet");
 2848
 2849            if snippet_stack.active_index > 0 {
 2850                key_context.add("has_previous_tabstop");
 2851            }
 2852
 2853            if snippet_stack.active_index < snippet_stack.ranges.len().saturating_sub(1) {
 2854                key_context.add("has_next_tabstop");
 2855            }
 2856        }
 2857
 2858        match self.context_menu.borrow().as_ref() {
 2859            Some(CodeContextMenu::Completions(menu)) => {
 2860                if menu.visible() {
 2861                    key_context.add("menu");
 2862                    key_context.add("showing_completions");
 2863                }
 2864            }
 2865            Some(CodeContextMenu::CodeActions(menu)) => {
 2866                if menu.visible() {
 2867                    key_context.add("menu");
 2868                    key_context.add("showing_code_actions")
 2869                }
 2870            }
 2871            None => {}
 2872        }
 2873
 2874        if self.signature_help_state.has_multiple_signatures() {
 2875            key_context.add("showing_signature_help");
 2876        }
 2877
 2878        // Disable vim contexts when a sub-editor (e.g. rename/inline assistant) is focused.
 2879        if !self.focus_handle(cx).contains_focused(window, cx)
 2880            || (self.is_focused(window) || self.mouse_menu_is_focused(window, cx))
 2881        {
 2882            for addon in self.addons.values() {
 2883                addon.extend_key_context(&mut key_context, cx)
 2884            }
 2885        }
 2886
 2887        if let Some(singleton_buffer) = self.buffer.read(cx).as_singleton() {
 2888            if let Some(extension) = singleton_buffer.read(cx).file().and_then(|file| {
 2889                Some(
 2890                    file.full_path(cx)
 2891                        .extension()?
 2892                        .to_string_lossy()
 2893                        .to_lowercase(),
 2894                )
 2895            }) {
 2896                key_context.set("extension", extension);
 2897            }
 2898        } else {
 2899            key_context.add("multibuffer");
 2900        }
 2901
 2902        if has_active_edit_prediction {
 2903            if self.edit_prediction_in_conflict() {
 2904                key_context.add(EDIT_PREDICTION_CONFLICT_KEY_CONTEXT);
 2905            } else {
 2906                key_context.add(EDIT_PREDICTION_KEY_CONTEXT);
 2907                key_context.add("copilot_suggestion");
 2908            }
 2909        }
 2910
 2911        if self.selection_mark_mode {
 2912            key_context.add("selection_mode");
 2913        }
 2914
 2915        let disjoint = self.selections.disjoint_anchors();
 2916        let snapshot = self.snapshot(window, cx);
 2917        let snapshot = snapshot.buffer_snapshot();
 2918        if self.mode == EditorMode::SingleLine
 2919            && let [selection] = disjoint
 2920            && selection.start == selection.end
 2921            && selection.end.to_offset(snapshot) == snapshot.len()
 2922        {
 2923            key_context.add("end_of_input");
 2924        }
 2925
 2926        if self.has_any_expanded_diff_hunks(cx) {
 2927            key_context.add("diffs_expanded");
 2928        }
 2929
 2930        key_context
 2931    }
 2932
 2933    pub fn last_bounds(&self) -> Option<&Bounds<Pixels>> {
 2934        self.last_bounds.as_ref()
 2935    }
 2936
 2937    fn show_mouse_cursor(&mut self, cx: &mut Context<Self>) {
 2938        if self.mouse_cursor_hidden {
 2939            self.mouse_cursor_hidden = false;
 2940            cx.notify();
 2941        }
 2942    }
 2943
 2944    pub fn hide_mouse_cursor(&mut self, origin: HideMouseCursorOrigin, cx: &mut Context<Self>) {
 2945        let hide_mouse_cursor = match origin {
 2946            HideMouseCursorOrigin::TypingAction => {
 2947                matches!(
 2948                    self.hide_mouse_mode,
 2949                    HideMouseMode::OnTyping | HideMouseMode::OnTypingAndMovement
 2950                )
 2951            }
 2952            HideMouseCursorOrigin::MovementAction => {
 2953                matches!(self.hide_mouse_mode, HideMouseMode::OnTypingAndMovement)
 2954            }
 2955        };
 2956        if self.mouse_cursor_hidden != hide_mouse_cursor {
 2957            self.mouse_cursor_hidden = hide_mouse_cursor;
 2958            cx.notify();
 2959        }
 2960    }
 2961
 2962    pub fn edit_prediction_in_conflict(&self) -> bool {
 2963        if !self.show_edit_predictions_in_menu() {
 2964            return false;
 2965        }
 2966
 2967        let showing_completions = self
 2968            .context_menu
 2969            .borrow()
 2970            .as_ref()
 2971            .is_some_and(|context| matches!(context, CodeContextMenu::Completions(_)));
 2972
 2973        showing_completions
 2974            || self.edit_prediction_requires_modifier()
 2975            // Require modifier key when the cursor is on leading whitespace, to allow `tab`
 2976            // bindings to insert tab characters.
 2977            || (self.edit_prediction_requires_modifier_in_indent_conflict && self.edit_prediction_indent_conflict)
 2978    }
 2979
 2980    pub fn accept_edit_prediction_keybind(
 2981        &self,
 2982        granularity: EditPredictionGranularity,
 2983        window: &mut Window,
 2984        cx: &mut App,
 2985    ) -> AcceptEditPredictionBinding {
 2986        let key_context = self.key_context_internal(true, window, cx);
 2987        let in_conflict = self.edit_prediction_in_conflict();
 2988
 2989        let bindings =
 2990            match granularity {
 2991                EditPredictionGranularity::Word => window
 2992                    .bindings_for_action_in_context(&AcceptNextWordEditPrediction, key_context),
 2993                EditPredictionGranularity::Line => window
 2994                    .bindings_for_action_in_context(&AcceptNextLineEditPrediction, key_context),
 2995                EditPredictionGranularity::Full => {
 2996                    window.bindings_for_action_in_context(&AcceptEditPrediction, key_context)
 2997                }
 2998            };
 2999
 3000        AcceptEditPredictionBinding(bindings.into_iter().rev().find(|binding| {
 3001            !in_conflict
 3002                || binding
 3003                    .keystrokes()
 3004                    .first()
 3005                    .is_some_and(|keystroke| keystroke.modifiers().modified())
 3006        }))
 3007    }
 3008
 3009    pub fn new_file(
 3010        workspace: &mut Workspace,
 3011        _: &workspace::NewFile,
 3012        window: &mut Window,
 3013        cx: &mut Context<Workspace>,
 3014    ) {
 3015        Self::new_in_workspace(workspace, window, cx).detach_and_prompt_err(
 3016            "Failed to create buffer",
 3017            window,
 3018            cx,
 3019            |e, _, _| match e.error_code() {
 3020                ErrorCode::RemoteUpgradeRequired => Some(format!(
 3021                "The remote instance of Zed does not support this yet. It must be upgraded to {}",
 3022                e.error_tag("required").unwrap_or("the latest version")
 3023            )),
 3024                _ => None,
 3025            },
 3026        );
 3027    }
 3028
 3029    pub fn new_in_workspace(
 3030        workspace: &mut Workspace,
 3031        window: &mut Window,
 3032        cx: &mut Context<Workspace>,
 3033    ) -> Task<Result<Entity<Editor>>> {
 3034        let project = workspace.project().clone();
 3035        let create = project.update(cx, |project, cx| project.create_buffer(None, true, cx));
 3036
 3037        cx.spawn_in(window, async move |workspace, cx| {
 3038            let buffer = create.await?;
 3039            workspace.update_in(cx, |workspace, window, cx| {
 3040                let editor =
 3041                    cx.new(|cx| Editor::for_buffer(buffer, Some(project.clone()), window, cx));
 3042                workspace.add_item_to_active_pane(Box::new(editor.clone()), None, true, window, cx);
 3043                editor
 3044            })
 3045        })
 3046    }
 3047
 3048    fn new_file_vertical(
 3049        workspace: &mut Workspace,
 3050        _: &workspace::NewFileSplitVertical,
 3051        window: &mut Window,
 3052        cx: &mut Context<Workspace>,
 3053    ) {
 3054        Self::new_file_in_direction(workspace, SplitDirection::vertical(cx), window, cx)
 3055    }
 3056
 3057    fn new_file_horizontal(
 3058        workspace: &mut Workspace,
 3059        _: &workspace::NewFileSplitHorizontal,
 3060        window: &mut Window,
 3061        cx: &mut Context<Workspace>,
 3062    ) {
 3063        Self::new_file_in_direction(workspace, SplitDirection::horizontal(cx), window, cx)
 3064    }
 3065
 3066    fn new_file_split(
 3067        workspace: &mut Workspace,
 3068        action: &workspace::NewFileSplit,
 3069        window: &mut Window,
 3070        cx: &mut Context<Workspace>,
 3071    ) {
 3072        Self::new_file_in_direction(workspace, action.0, window, cx)
 3073    }
 3074
 3075    fn new_file_in_direction(
 3076        workspace: &mut Workspace,
 3077        direction: SplitDirection,
 3078        window: &mut Window,
 3079        cx: &mut Context<Workspace>,
 3080    ) {
 3081        let project = workspace.project().clone();
 3082        let create = project.update(cx, |project, cx| project.create_buffer(None, true, cx));
 3083
 3084        cx.spawn_in(window, async move |workspace, cx| {
 3085            let buffer = create.await?;
 3086            workspace.update_in(cx, move |workspace, window, cx| {
 3087                workspace.split_item(
 3088                    direction,
 3089                    Box::new(
 3090                        cx.new(|cx| Editor::for_buffer(buffer, Some(project.clone()), window, cx)),
 3091                    ),
 3092                    window,
 3093                    cx,
 3094                )
 3095            })?;
 3096            anyhow::Ok(())
 3097        })
 3098        .detach_and_prompt_err("Failed to create buffer", window, cx, |e, _, _| {
 3099            match e.error_code() {
 3100                ErrorCode::RemoteUpgradeRequired => Some(format!(
 3101                "The remote instance of Zed does not support this yet. It must be upgraded to {}",
 3102                e.error_tag("required").unwrap_or("the latest version")
 3103            )),
 3104                _ => None,
 3105            }
 3106        });
 3107    }
 3108
 3109    pub fn leader_id(&self) -> Option<CollaboratorId> {
 3110        self.leader_id
 3111    }
 3112
 3113    pub fn buffer(&self) -> &Entity<MultiBuffer> {
 3114        &self.buffer
 3115    }
 3116
 3117    pub fn project(&self) -> Option<&Entity<Project>> {
 3118        self.project.as_ref()
 3119    }
 3120
 3121    pub fn workspace(&self) -> Option<Entity<Workspace>> {
 3122        self.workspace.as_ref()?.0.upgrade()
 3123    }
 3124
 3125    /// Detaches a task and shows an error notification in the workspace if available,
 3126    /// otherwise just logs the error.
 3127    pub fn detach_and_notify_err<R, E>(
 3128        &self,
 3129        task: Task<Result<R, E>>,
 3130        window: &mut Window,
 3131        cx: &mut App,
 3132    ) where
 3133        E: std::fmt::Debug + std::fmt::Display + 'static,
 3134        R: 'static,
 3135    {
 3136        if let Some(workspace) = self.workspace() {
 3137            task.detach_and_notify_err(workspace.downgrade(), window, cx);
 3138        } else {
 3139            task.detach_and_log_err(cx);
 3140        }
 3141    }
 3142
 3143    /// Returns the workspace serialization ID if this editor should be serialized.
 3144    fn workspace_serialization_id(&self, _cx: &App) -> Option<WorkspaceId> {
 3145        self.workspace
 3146            .as_ref()
 3147            .filter(|_| self.should_serialize_buffer())
 3148            .and_then(|workspace| workspace.1)
 3149    }
 3150
 3151    pub fn title<'a>(&self, cx: &'a App) -> Cow<'a, str> {
 3152        self.buffer().read(cx).title(cx)
 3153    }
 3154
 3155    pub fn snapshot(&self, window: &Window, cx: &mut App) -> EditorSnapshot {
 3156        let git_blame_gutter_max_author_length = self
 3157            .render_git_blame_gutter(cx)
 3158            .then(|| {
 3159                if let Some(blame) = self.blame.as_ref() {
 3160                    let max_author_length =
 3161                        blame.update(cx, |blame, cx| blame.max_author_length(cx));
 3162                    Some(max_author_length)
 3163                } else {
 3164                    None
 3165                }
 3166            })
 3167            .flatten();
 3168
 3169        let display_snapshot = self.display_map.update(cx, |map, cx| map.snapshot(cx));
 3170
 3171        EditorSnapshot {
 3172            mode: self.mode.clone(),
 3173            show_gutter: self.show_gutter,
 3174            offset_content: self.offset_content,
 3175            show_line_numbers: self.show_line_numbers,
 3176            number_deleted_lines: self.number_deleted_lines,
 3177            show_git_diff_gutter: self.show_git_diff_gutter,
 3178            semantic_tokens_enabled: self.semantic_token_state.enabled(),
 3179            show_code_actions: self.show_code_actions,
 3180            show_runnables: self.show_runnables,
 3181            show_breakpoints: self.show_breakpoints,
 3182            git_blame_gutter_max_author_length,
 3183            scroll_anchor: self.scroll_manager.shared_scroll_anchor(cx),
 3184            display_snapshot,
 3185            placeholder_display_snapshot: self
 3186                .placeholder_display_map
 3187                .as_ref()
 3188                .map(|display_map| display_map.update(cx, |map, cx| map.snapshot(cx))),
 3189            ongoing_scroll: self.scroll_manager.ongoing_scroll(),
 3190            is_focused: self.focus_handle.is_focused(window),
 3191            current_line_highlight: self
 3192                .current_line_highlight
 3193                .unwrap_or_else(|| EditorSettings::get_global(cx).current_line_highlight),
 3194            gutter_hovered: self.gutter_hovered,
 3195        }
 3196    }
 3197
 3198    pub fn language_at<T: ToOffset>(&self, point: T, cx: &App) -> Option<Arc<Language>> {
 3199        self.buffer.read(cx).language_at(point, cx)
 3200    }
 3201
 3202    pub fn file_at<T: ToOffset>(&self, point: T, cx: &App) -> Option<Arc<dyn language::File>> {
 3203        self.buffer.read(cx).read(cx).file_at(point).cloned()
 3204    }
 3205
 3206    pub fn active_excerpt(
 3207        &self,
 3208        cx: &App,
 3209    ) -> Option<(ExcerptId, Entity<Buffer>, Range<text::Anchor>)> {
 3210        self.buffer
 3211            .read(cx)
 3212            .excerpt_containing(self.selections.newest_anchor().head(), cx)
 3213    }
 3214
 3215    pub fn mode(&self) -> &EditorMode {
 3216        &self.mode
 3217    }
 3218
 3219    pub fn set_mode(&mut self, mode: EditorMode) {
 3220        self.mode = mode;
 3221    }
 3222
 3223    pub fn collaboration_hub(&self) -> Option<&dyn CollaborationHub> {
 3224        self.collaboration_hub.as_deref()
 3225    }
 3226
 3227    pub fn set_collaboration_hub(&mut self, hub: Box<dyn CollaborationHub>) {
 3228        self.collaboration_hub = Some(hub);
 3229    }
 3230
 3231    pub fn set_in_project_search(&mut self, in_project_search: bool) {
 3232        self.in_project_search = in_project_search;
 3233    }
 3234
 3235    pub fn set_custom_context_menu(
 3236        &mut self,
 3237        f: impl 'static
 3238        + Fn(
 3239            &mut Self,
 3240            DisplayPoint,
 3241            &mut Window,
 3242            &mut Context<Self>,
 3243        ) -> Option<Entity<ui::ContextMenu>>,
 3244    ) {
 3245        self.custom_context_menu = Some(Box::new(f))
 3246    }
 3247
 3248    pub fn set_completion_provider(&mut self, provider: Option<Rc<dyn CompletionProvider>>) {
 3249        self.completion_provider = provider;
 3250    }
 3251
 3252    #[cfg(any(test, feature = "test-support"))]
 3253    pub fn completion_provider(&self) -> Option<Rc<dyn CompletionProvider>> {
 3254        self.completion_provider.clone()
 3255    }
 3256
 3257    pub fn semantics_provider(&self) -> Option<Rc<dyn SemanticsProvider>> {
 3258        self.semantics_provider.clone()
 3259    }
 3260
 3261    pub fn set_semantics_provider(&mut self, provider: Option<Rc<dyn SemanticsProvider>>) {
 3262        self.semantics_provider = provider;
 3263    }
 3264
 3265    pub fn set_edit_prediction_provider<T>(
 3266        &mut self,
 3267        provider: Option<Entity<T>>,
 3268        window: &mut Window,
 3269        cx: &mut Context<Self>,
 3270    ) where
 3271        T: EditPredictionDelegate,
 3272    {
 3273        self.edit_prediction_provider = provider.map(|provider| RegisteredEditPredictionDelegate {
 3274            _subscription: cx.observe_in(&provider, window, |this, _, window, cx| {
 3275                if this.focus_handle.is_focused(window) {
 3276                    this.update_visible_edit_prediction(window, cx);
 3277                }
 3278            }),
 3279            provider: Arc::new(provider),
 3280        });
 3281        self.update_edit_prediction_settings(cx);
 3282        self.refresh_edit_prediction(false, false, window, cx);
 3283    }
 3284
 3285    pub fn placeholder_text(&self, cx: &mut App) -> Option<String> {
 3286        self.placeholder_display_map
 3287            .as_ref()
 3288            .map(|display_map| display_map.update(cx, |map, cx| map.snapshot(cx)).text())
 3289    }
 3290
 3291    pub fn set_placeholder_text(
 3292        &mut self,
 3293        placeholder_text: &str,
 3294        window: &mut Window,
 3295        cx: &mut Context<Self>,
 3296    ) {
 3297        let multibuffer = cx
 3298            .new(|cx| MultiBuffer::singleton(cx.new(|cx| Buffer::local(placeholder_text, cx)), cx));
 3299
 3300        let style = window.text_style();
 3301
 3302        self.placeholder_display_map = Some(cx.new(|cx| {
 3303            DisplayMap::new(
 3304                multibuffer,
 3305                style.font(),
 3306                style.font_size.to_pixels(window.rem_size()),
 3307                None,
 3308                FILE_HEADER_HEIGHT,
 3309                MULTI_BUFFER_EXCERPT_HEADER_HEIGHT,
 3310                Default::default(),
 3311                DiagnosticSeverity::Off,
 3312                cx,
 3313            )
 3314        }));
 3315        cx.notify();
 3316    }
 3317
 3318    pub fn set_cursor_shape(&mut self, cursor_shape: CursorShape, cx: &mut Context<Self>) {
 3319        self.cursor_shape = cursor_shape;
 3320
 3321        // Disrupt blink for immediate user feedback that the cursor shape has changed
 3322        self.blink_manager.update(cx, BlinkManager::show_cursor);
 3323
 3324        cx.notify();
 3325    }
 3326
 3327    pub fn cursor_shape(&self) -> CursorShape {
 3328        self.cursor_shape
 3329    }
 3330
 3331    pub fn set_cursor_offset_on_selection(&mut self, set_cursor_offset_on_selection: bool) {
 3332        self.cursor_offset_on_selection = set_cursor_offset_on_selection;
 3333    }
 3334
 3335    pub fn set_current_line_highlight(
 3336        &mut self,
 3337        current_line_highlight: Option<CurrentLineHighlight>,
 3338    ) {
 3339        self.current_line_highlight = current_line_highlight;
 3340    }
 3341
 3342    pub fn set_collapse_matches(&mut self, collapse_matches: bool) {
 3343        self.collapse_matches = collapse_matches;
 3344    }
 3345
 3346    pub fn range_for_match<T: std::marker::Copy>(&self, range: &Range<T>) -> Range<T> {
 3347        if self.collapse_matches {
 3348            return range.start..range.start;
 3349        }
 3350        range.clone()
 3351    }
 3352
 3353    pub fn clip_at_line_ends(&mut self, cx: &mut Context<Self>) -> bool {
 3354        self.display_map.read(cx).clip_at_line_ends
 3355    }
 3356
 3357    pub fn set_clip_at_line_ends(&mut self, clip: bool, cx: &mut Context<Self>) {
 3358        if self.display_map.read(cx).clip_at_line_ends != clip {
 3359            self.display_map
 3360                .update(cx, |map, _| map.clip_at_line_ends = clip);
 3361        }
 3362    }
 3363
 3364    pub fn set_input_enabled(&mut self, input_enabled: bool) {
 3365        self.input_enabled = input_enabled;
 3366    }
 3367
 3368    pub fn set_edit_predictions_hidden_for_vim_mode(
 3369        &mut self,
 3370        hidden: bool,
 3371        window: &mut Window,
 3372        cx: &mut Context<Self>,
 3373    ) {
 3374        if hidden != self.edit_predictions_hidden_for_vim_mode {
 3375            self.edit_predictions_hidden_for_vim_mode = hidden;
 3376            if hidden {
 3377                self.update_visible_edit_prediction(window, cx);
 3378            } else {
 3379                self.refresh_edit_prediction(true, false, window, cx);
 3380            }
 3381        }
 3382    }
 3383
 3384    pub fn set_menu_edit_predictions_policy(&mut self, value: MenuEditPredictionsPolicy) {
 3385        self.menu_edit_predictions_policy = value;
 3386    }
 3387
 3388    pub fn set_autoindent(&mut self, autoindent: bool) {
 3389        if autoindent {
 3390            self.autoindent_mode = Some(AutoindentMode::EachLine);
 3391        } else {
 3392            self.autoindent_mode = None;
 3393        }
 3394    }
 3395
 3396    pub fn capability(&self, cx: &App) -> Capability {
 3397        if self.read_only {
 3398            Capability::ReadOnly
 3399        } else {
 3400            self.buffer.read(cx).capability()
 3401        }
 3402    }
 3403
 3404    pub fn read_only(&self, cx: &App) -> bool {
 3405        self.read_only || self.buffer.read(cx).read_only()
 3406    }
 3407
 3408    pub fn set_read_only(&mut self, read_only: bool) {
 3409        self.read_only = read_only;
 3410    }
 3411
 3412    pub fn set_use_autoclose(&mut self, autoclose: bool) {
 3413        self.use_autoclose = autoclose;
 3414    }
 3415
 3416    pub fn set_use_auto_surround(&mut self, auto_surround: bool) {
 3417        self.use_auto_surround = auto_surround;
 3418    }
 3419
 3420    pub fn set_auto_replace_emoji_shortcode(&mut self, auto_replace: bool) {
 3421        self.auto_replace_emoji_shortcode = auto_replace;
 3422    }
 3423
 3424    pub fn set_should_serialize(&mut self, should_serialize: bool, cx: &App) {
 3425        self.buffer_serialization = should_serialize.then(|| {
 3426            BufferSerialization::new(
 3427                ProjectSettings::get_global(cx)
 3428                    .session
 3429                    .restore_unsaved_buffers,
 3430            )
 3431        })
 3432    }
 3433
 3434    fn should_serialize_buffer(&self) -> bool {
 3435        self.buffer_serialization.is_some()
 3436    }
 3437
 3438    pub fn toggle_edit_predictions(
 3439        &mut self,
 3440        _: &ToggleEditPrediction,
 3441        window: &mut Window,
 3442        cx: &mut Context<Self>,
 3443    ) {
 3444        if self.show_edit_predictions_override.is_some() {
 3445            self.set_show_edit_predictions(None, window, cx);
 3446        } else {
 3447            let show_edit_predictions = !self.edit_predictions_enabled();
 3448            self.set_show_edit_predictions(Some(show_edit_predictions), window, cx);
 3449        }
 3450    }
 3451
 3452    pub fn set_show_completions_on_input(&mut self, show_completions_on_input: Option<bool>) {
 3453        self.show_completions_on_input_override = show_completions_on_input;
 3454    }
 3455
 3456    pub fn set_show_edit_predictions(
 3457        &mut self,
 3458        show_edit_predictions: Option<bool>,
 3459        window: &mut Window,
 3460        cx: &mut Context<Self>,
 3461    ) {
 3462        self.show_edit_predictions_override = show_edit_predictions;
 3463        self.update_edit_prediction_settings(cx);
 3464
 3465        if let Some(false) = show_edit_predictions {
 3466            self.discard_edit_prediction(EditPredictionDiscardReason::Ignored, cx);
 3467        } else {
 3468            self.refresh_edit_prediction(false, true, window, cx);
 3469        }
 3470    }
 3471
 3472    fn edit_predictions_disabled_in_scope(
 3473        &self,
 3474        buffer: &Entity<Buffer>,
 3475        buffer_position: language::Anchor,
 3476        cx: &App,
 3477    ) -> bool {
 3478        let snapshot = buffer.read(cx).snapshot();
 3479        let settings = snapshot.settings_at(buffer_position, cx);
 3480
 3481        let Some(scope) = snapshot.language_scope_at(buffer_position) else {
 3482            return false;
 3483        };
 3484
 3485        scope.override_name().is_some_and(|scope_name| {
 3486            settings
 3487                .edit_predictions_disabled_in
 3488                .iter()
 3489                .any(|s| s == scope_name)
 3490        })
 3491    }
 3492
 3493    pub fn set_use_modal_editing(&mut self, to: bool) {
 3494        self.use_modal_editing = to;
 3495    }
 3496
 3497    pub fn use_modal_editing(&self) -> bool {
 3498        self.use_modal_editing
 3499    }
 3500
 3501    fn selections_did_change(
 3502        &mut self,
 3503        local: bool,
 3504        old_cursor_position: &Anchor,
 3505        effects: SelectionEffects,
 3506        window: &mut Window,
 3507        cx: &mut Context<Self>,
 3508    ) {
 3509        window.invalidate_character_coordinates();
 3510
 3511        // Copy selections to primary selection buffer
 3512        #[cfg(any(target_os = "linux", target_os = "freebsd"))]
 3513        if local {
 3514            let selections = self
 3515                .selections
 3516                .all::<MultiBufferOffset>(&self.display_snapshot(cx));
 3517            let buffer_handle = self.buffer.read(cx).read(cx);
 3518
 3519            let mut text = String::new();
 3520            for (index, selection) in selections.iter().enumerate() {
 3521                let text_for_selection = buffer_handle
 3522                    .text_for_range(selection.start..selection.end)
 3523                    .collect::<String>();
 3524
 3525                text.push_str(&text_for_selection);
 3526                if index != selections.len() - 1 {
 3527                    text.push('\n');
 3528                }
 3529            }
 3530
 3531            if !text.is_empty() {
 3532                cx.write_to_primary(ClipboardItem::new_string(text));
 3533            }
 3534        }
 3535
 3536        let selection_anchors = self.selections.disjoint_anchors_arc();
 3537
 3538        if self.focus_handle.is_focused(window) && self.leader_id.is_none() {
 3539            self.buffer.update(cx, |buffer, cx| {
 3540                buffer.set_active_selections(
 3541                    &selection_anchors,
 3542                    self.selections.line_mode(),
 3543                    self.cursor_shape,
 3544                    cx,
 3545                )
 3546            });
 3547        }
 3548        let display_map = self
 3549            .display_map
 3550            .update(cx, |display_map, cx| display_map.snapshot(cx));
 3551        let buffer = display_map.buffer_snapshot();
 3552        if self.selections.count() == 1 {
 3553            self.add_selections_state = None;
 3554        }
 3555        self.select_next_state = None;
 3556        self.select_prev_state = None;
 3557        self.select_syntax_node_history.try_clear();
 3558        self.invalidate_autoclose_regions(&selection_anchors, buffer);
 3559        self.snippet_stack.invalidate(&selection_anchors, buffer);
 3560        self.take_rename(false, window, cx);
 3561
 3562        let newest_selection = self.selections.newest_anchor();
 3563        let new_cursor_position = newest_selection.head();
 3564        let selection_start = newest_selection.start;
 3565
 3566        if effects.nav_history.is_none() || effects.nav_history == Some(true) {
 3567            self.push_to_nav_history(
 3568                *old_cursor_position,
 3569                Some(new_cursor_position.to_point(buffer)),
 3570                false,
 3571                effects.nav_history == Some(true),
 3572                cx,
 3573            );
 3574        }
 3575
 3576        if local {
 3577            if let Some(buffer_id) = new_cursor_position.text_anchor.buffer_id {
 3578                self.register_buffer(buffer_id, cx);
 3579            }
 3580
 3581            let mut context_menu = self.context_menu.borrow_mut();
 3582            let completion_menu = match context_menu.as_ref() {
 3583                Some(CodeContextMenu::Completions(menu)) => Some(menu),
 3584                Some(CodeContextMenu::CodeActions(_)) => {
 3585                    *context_menu = None;
 3586                    None
 3587                }
 3588                None => None,
 3589            };
 3590            let completion_position = completion_menu.map(|menu| menu.initial_position);
 3591            drop(context_menu);
 3592
 3593            if effects.completions
 3594                && let Some(completion_position) = completion_position
 3595            {
 3596                let start_offset = selection_start.to_offset(buffer);
 3597                let position_matches = start_offset == completion_position.to_offset(buffer);
 3598                let continue_showing = if let Some((snap, ..)) =
 3599                    buffer.point_to_buffer_offset(completion_position)
 3600                    && !snap.capability.editable()
 3601                {
 3602                    false
 3603                } else if position_matches {
 3604                    if self.snippet_stack.is_empty() {
 3605                        buffer.char_kind_before(start_offset, Some(CharScopeContext::Completion))
 3606                            == Some(CharKind::Word)
 3607                    } else {
 3608                        // Snippet choices can be shown even when the cursor is in whitespace.
 3609                        // Dismissing the menu with actions like backspace is handled by
 3610                        // invalidation regions.
 3611                        true
 3612                    }
 3613                } else {
 3614                    false
 3615                };
 3616
 3617                if continue_showing {
 3618                    self.open_or_update_completions_menu(None, None, false, window, cx);
 3619                } else {
 3620                    self.hide_context_menu(window, cx);
 3621                }
 3622            }
 3623
 3624            hide_hover(self, cx);
 3625
 3626            if old_cursor_position.to_display_point(&display_map).row()
 3627                != new_cursor_position.to_display_point(&display_map).row()
 3628            {
 3629                self.available_code_actions.take();
 3630            }
 3631            self.refresh_code_actions(window, cx);
 3632            self.refresh_document_highlights(cx);
 3633            refresh_linked_ranges(self, window, cx);
 3634
 3635            self.refresh_selected_text_highlights(&display_map, false, window, cx);
 3636            self.refresh_matching_bracket_highlights(&display_map, cx);
 3637            self.refresh_outline_symbols_at_cursor(cx);
 3638            self.update_visible_edit_prediction(window, cx);
 3639            self.edit_prediction_requires_modifier_in_indent_conflict = true;
 3640            self.inline_blame_popover.take();
 3641            if self.git_blame_inline_enabled {
 3642                self.start_inline_blame_timer(window, cx);
 3643            }
 3644        }
 3645
 3646        self.blink_manager.update(cx, BlinkManager::pause_blinking);
 3647
 3648        if local && !self.suppress_selection_callback {
 3649            if let Some(callback) = self.on_local_selections_changed.as_ref() {
 3650                let cursor_position = self.selections.newest::<Point>(&display_map).head();
 3651                callback(cursor_position, window, cx);
 3652            }
 3653        }
 3654
 3655        cx.emit(EditorEvent::SelectionsChanged { local });
 3656
 3657        let selections = &self.selections.disjoint_anchors_arc();
 3658        if selections.len() == 1 {
 3659            cx.emit(SearchEvent::ActiveMatchChanged)
 3660        }
 3661        if local && let Some((_, _, buffer_snapshot)) = buffer.as_singleton() {
 3662            let inmemory_selections = selections
 3663                .iter()
 3664                .map(|s| {
 3665                    text::ToPoint::to_point(&s.range().start.text_anchor, buffer_snapshot)
 3666                        ..text::ToPoint::to_point(&s.range().end.text_anchor, buffer_snapshot)
 3667                })
 3668                .collect();
 3669            self.update_restoration_data(cx, |data| {
 3670                data.selections = inmemory_selections;
 3671            });
 3672
 3673            if WorkspaceSettings::get(None, cx).restore_on_startup
 3674                != RestoreOnStartupBehavior::EmptyTab
 3675                && let Some(workspace_id) = self.workspace_serialization_id(cx)
 3676            {
 3677                let snapshot = self.buffer().read(cx).snapshot(cx);
 3678                let selections = selections.clone();
 3679                let background_executor = cx.background_executor().clone();
 3680                let editor_id = cx.entity().entity_id().as_u64() as ItemId;
 3681                self.serialize_selections = cx.background_spawn(async move {
 3682                    background_executor.timer(SERIALIZATION_THROTTLE_TIME).await;
 3683                    let db_selections = selections
 3684                        .iter()
 3685                        .map(|selection| {
 3686                            (
 3687                                selection.start.to_offset(&snapshot).0,
 3688                                selection.end.to_offset(&snapshot).0,
 3689                            )
 3690                        })
 3691                        .collect();
 3692
 3693                    DB.save_editor_selections(editor_id, workspace_id, db_selections)
 3694                        .await
 3695                        .with_context(|| {
 3696                            format!(
 3697                                "persisting editor selections for editor {editor_id}, \
 3698                                workspace {workspace_id:?}"
 3699                            )
 3700                        })
 3701                        .log_err();
 3702                });
 3703            }
 3704        }
 3705
 3706        cx.notify();
 3707    }
 3708
 3709    fn folds_did_change(&mut self, cx: &mut Context<Self>) {
 3710        use text::ToOffset as _;
 3711        use text::ToPoint as _;
 3712
 3713        if self.mode.is_minimap()
 3714            || WorkspaceSettings::get(None, cx).restore_on_startup
 3715                == RestoreOnStartupBehavior::EmptyTab
 3716        {
 3717            return;
 3718        }
 3719
 3720        if !self.buffer().read(cx).is_singleton() {
 3721            return;
 3722        }
 3723
 3724        let display_snapshot = self
 3725            .display_map
 3726            .update(cx, |display_map, cx| display_map.snapshot(cx));
 3727        let Some((.., snapshot)) = display_snapshot.buffer_snapshot().as_singleton() else {
 3728            return;
 3729        };
 3730        let inmemory_folds = display_snapshot
 3731            .folds_in_range(MultiBufferOffset(0)..display_snapshot.buffer_snapshot().len())
 3732            .map(|fold| {
 3733                fold.range.start.text_anchor.to_point(&snapshot)
 3734                    ..fold.range.end.text_anchor.to_point(&snapshot)
 3735            })
 3736            .collect();
 3737        self.update_restoration_data(cx, |data| {
 3738            data.folds = inmemory_folds;
 3739        });
 3740
 3741        let Some(workspace_id) = self.workspace_serialization_id(cx) else {
 3742            return;
 3743        };
 3744
 3745        // Get file path for path-based fold storage (survives tab close)
 3746        let Some(file_path) = self.buffer().read(cx).as_singleton().and_then(|buffer| {
 3747            project::File::from_dyn(buffer.read(cx).file())
 3748                .map(|file| Arc::<Path>::from(file.abs_path(cx)))
 3749        }) else {
 3750            return;
 3751        };
 3752
 3753        let background_executor = cx.background_executor().clone();
 3754        const FINGERPRINT_LEN: usize = 32;
 3755        let db_folds = display_snapshot
 3756            .folds_in_range(MultiBufferOffset(0)..display_snapshot.buffer_snapshot().len())
 3757            .map(|fold| {
 3758                let start = fold.range.start.text_anchor.to_offset(&snapshot);
 3759                let end = fold.range.end.text_anchor.to_offset(&snapshot);
 3760
 3761                // Extract fingerprints - content at fold boundaries for validation on restore
 3762                // Both fingerprints must be INSIDE the fold to avoid capturing surrounding
 3763                // content that might change independently.
 3764                // start_fp: first min(32, fold_len) bytes of fold content
 3765                // end_fp: last min(32, fold_len) bytes of fold content
 3766                // Clip to character boundaries to handle multibyte UTF-8 characters.
 3767                let fold_len = end - start;
 3768                let start_fp_end = snapshot
 3769                    .clip_offset(start + std::cmp::min(FINGERPRINT_LEN, fold_len), Bias::Left);
 3770                let start_fp: String = snapshot.text_for_range(start..start_fp_end).collect();
 3771                let end_fp_start = snapshot
 3772                    .clip_offset(end.saturating_sub(FINGERPRINT_LEN).max(start), Bias::Right);
 3773                let end_fp: String = snapshot.text_for_range(end_fp_start..end).collect();
 3774
 3775                (start, end, start_fp, end_fp)
 3776            })
 3777            .collect::<Vec<_>>();
 3778        self.serialize_folds = cx.background_spawn(async move {
 3779            background_executor.timer(SERIALIZATION_THROTTLE_TIME).await;
 3780            if db_folds.is_empty() {
 3781                // No folds - delete any persisted folds for this file
 3782                DB.delete_file_folds(workspace_id, file_path)
 3783                    .await
 3784                    .with_context(|| format!("deleting file folds for workspace {workspace_id:?}"))
 3785                    .log_err();
 3786            } else {
 3787                DB.save_file_folds(workspace_id, file_path, db_folds)
 3788                    .await
 3789                    .with_context(|| {
 3790                        format!("persisting file folds for workspace {workspace_id:?}")
 3791                    })
 3792                    .log_err();
 3793            }
 3794        });
 3795    }
 3796
 3797    pub fn sync_selections(
 3798        &mut self,
 3799        other: Entity<Editor>,
 3800        cx: &mut Context<Self>,
 3801    ) -> gpui::Subscription {
 3802        let other_selections = other.read(cx).selections.disjoint_anchors().to_vec();
 3803        if !other_selections.is_empty() {
 3804            self.selections
 3805                .change_with(&self.display_snapshot(cx), |selections| {
 3806                    selections.select_anchors(other_selections);
 3807                });
 3808        }
 3809
 3810        let other_subscription = cx.subscribe(&other, |this, other, other_evt, cx| {
 3811            if let EditorEvent::SelectionsChanged { local: true } = other_evt {
 3812                let other_selections = other.read(cx).selections.disjoint_anchors().to_vec();
 3813                if other_selections.is_empty() {
 3814                    return;
 3815                }
 3816                let snapshot = this.display_snapshot(cx);
 3817                this.selections.change_with(&snapshot, |selections| {
 3818                    selections.select_anchors(other_selections);
 3819                });
 3820            }
 3821        });
 3822
 3823        let this_subscription = cx.subscribe_self::<EditorEvent>(move |this, this_evt, cx| {
 3824            if let EditorEvent::SelectionsChanged { local: true } = this_evt {
 3825                let these_selections = this.selections.disjoint_anchors().to_vec();
 3826                if these_selections.is_empty() {
 3827                    return;
 3828                }
 3829                other.update(cx, |other_editor, cx| {
 3830                    let snapshot = other_editor.display_snapshot(cx);
 3831                    other_editor
 3832                        .selections
 3833                        .change_with(&snapshot, |selections| {
 3834                            selections.select_anchors(these_selections);
 3835                        })
 3836                });
 3837            }
 3838        });
 3839
 3840        Subscription::join(other_subscription, this_subscription)
 3841    }
 3842
 3843    fn unfold_buffers_with_selections(&mut self, cx: &mut Context<Self>) {
 3844        if self.buffer().read(cx).is_singleton() {
 3845            return;
 3846        }
 3847        let snapshot = self.buffer.read(cx).snapshot(cx);
 3848        let buffer_ids: HashSet<BufferId> = self
 3849            .selections
 3850            .disjoint_anchor_ranges()
 3851            .flat_map(|range| snapshot.buffer_ids_for_range(range))
 3852            .collect();
 3853        for buffer_id in buffer_ids {
 3854            self.unfold_buffer(buffer_id, cx);
 3855        }
 3856    }
 3857
 3858    /// Changes selections using the provided mutation function. Changes to `self.selections` occur
 3859    /// immediately, but when run within `transact` or `with_selection_effects_deferred` other
 3860    /// effects of selection change occur at the end of the transaction.
 3861    pub fn change_selections<R>(
 3862        &mut self,
 3863        effects: SelectionEffects,
 3864        window: &mut Window,
 3865        cx: &mut Context<Self>,
 3866        change: impl FnOnce(&mut MutableSelectionsCollection<'_, '_>) -> R,
 3867    ) -> R {
 3868        let snapshot = self.display_snapshot(cx);
 3869        if let Some(state) = &mut self.deferred_selection_effects_state {
 3870            state.effects.scroll = effects.scroll.or(state.effects.scroll);
 3871            state.effects.completions = effects.completions;
 3872            state.effects.nav_history = effects.nav_history.or(state.effects.nav_history);
 3873            let (changed, result) = self.selections.change_with(&snapshot, change);
 3874            state.changed |= changed;
 3875            return result;
 3876        }
 3877        let mut state = DeferredSelectionEffectsState {
 3878            changed: false,
 3879            effects,
 3880            old_cursor_position: self.selections.newest_anchor().head(),
 3881            history_entry: SelectionHistoryEntry {
 3882                selections: self.selections.disjoint_anchors_arc(),
 3883                select_next_state: self.select_next_state.clone(),
 3884                select_prev_state: self.select_prev_state.clone(),
 3885                add_selections_state: self.add_selections_state.clone(),
 3886            },
 3887        };
 3888        let (changed, result) = self.selections.change_with(&snapshot, change);
 3889        state.changed = state.changed || changed;
 3890        if self.defer_selection_effects {
 3891            self.deferred_selection_effects_state = Some(state);
 3892        } else {
 3893            self.apply_selection_effects(state, window, cx);
 3894        }
 3895        result
 3896    }
 3897
 3898    /// Defers the effects of selection change, so that the effects of multiple calls to
 3899    /// `change_selections` are applied at the end. This way these intermediate states aren't added
 3900    /// to selection history and the state of popovers based on selection position aren't
 3901    /// erroneously updated.
 3902    pub fn with_selection_effects_deferred<R>(
 3903        &mut self,
 3904        window: &mut Window,
 3905        cx: &mut Context<Self>,
 3906        update: impl FnOnce(&mut Self, &mut Window, &mut Context<Self>) -> R,
 3907    ) -> R {
 3908        let already_deferred = self.defer_selection_effects;
 3909        self.defer_selection_effects = true;
 3910        let result = update(self, window, cx);
 3911        if !already_deferred {
 3912            self.defer_selection_effects = false;
 3913            if let Some(state) = self.deferred_selection_effects_state.take() {
 3914                self.apply_selection_effects(state, window, cx);
 3915            }
 3916        }
 3917        result
 3918    }
 3919
 3920    fn apply_selection_effects(
 3921        &mut self,
 3922        state: DeferredSelectionEffectsState,
 3923        window: &mut Window,
 3924        cx: &mut Context<Self>,
 3925    ) {
 3926        if state.changed {
 3927            self.selection_history.push(state.history_entry);
 3928
 3929            if let Some(autoscroll) = state.effects.scroll {
 3930                self.request_autoscroll(autoscroll, cx);
 3931            }
 3932
 3933            let old_cursor_position = &state.old_cursor_position;
 3934
 3935            self.selections_did_change(true, old_cursor_position, state.effects, window, cx);
 3936
 3937            if self.should_open_signature_help_automatically(old_cursor_position, cx) {
 3938                self.show_signature_help_auto(window, cx);
 3939            }
 3940        }
 3941    }
 3942
 3943    pub fn edit<I, S, T>(&mut self, edits: I, cx: &mut Context<Self>)
 3944    where
 3945        I: IntoIterator<Item = (Range<S>, T)>,
 3946        S: ToOffset,
 3947        T: Into<Arc<str>>,
 3948    {
 3949        if self.read_only(cx) {
 3950            return;
 3951        }
 3952
 3953        self.buffer
 3954            .update(cx, |buffer, cx| buffer.edit(edits, None, cx));
 3955    }
 3956
 3957    pub fn edit_with_autoindent<I, S, T>(&mut self, edits: I, cx: &mut Context<Self>)
 3958    where
 3959        I: IntoIterator<Item = (Range<S>, T)>,
 3960        S: ToOffset,
 3961        T: Into<Arc<str>>,
 3962    {
 3963        if self.read_only(cx) {
 3964            return;
 3965        }
 3966
 3967        self.buffer.update(cx, |buffer, cx| {
 3968            buffer.edit(edits, self.autoindent_mode.clone(), cx)
 3969        });
 3970    }
 3971
 3972    pub fn edit_with_block_indent<I, S, T>(
 3973        &mut self,
 3974        edits: I,
 3975        original_indent_columns: Vec<Option<u32>>,
 3976        cx: &mut Context<Self>,
 3977    ) where
 3978        I: IntoIterator<Item = (Range<S>, T)>,
 3979        S: ToOffset,
 3980        T: Into<Arc<str>>,
 3981    {
 3982        if self.read_only(cx) {
 3983            return;
 3984        }
 3985
 3986        self.buffer.update(cx, |buffer, cx| {
 3987            buffer.edit(
 3988                edits,
 3989                Some(AutoindentMode::Block {
 3990                    original_indent_columns,
 3991                }),
 3992                cx,
 3993            )
 3994        });
 3995    }
 3996
 3997    fn select(&mut self, phase: SelectPhase, window: &mut Window, cx: &mut Context<Self>) {
 3998        self.hide_context_menu(window, cx);
 3999
 4000        match phase {
 4001            SelectPhase::Begin {
 4002                position,
 4003                add,
 4004                click_count,
 4005            } => self.begin_selection(position, add, click_count, window, cx),
 4006            SelectPhase::BeginColumnar {
 4007                position,
 4008                goal_column,
 4009                reset,
 4010                mode,
 4011            } => self.begin_columnar_selection(position, goal_column, reset, mode, window, cx),
 4012            SelectPhase::Extend {
 4013                position,
 4014                click_count,
 4015            } => self.extend_selection(position, click_count, window, cx),
 4016            SelectPhase::Update {
 4017                position,
 4018                goal_column,
 4019                scroll_delta,
 4020            } => self.update_selection(position, goal_column, scroll_delta, window, cx),
 4021            SelectPhase::End => self.end_selection(window, cx),
 4022        }
 4023    }
 4024
 4025    fn extend_selection(
 4026        &mut self,
 4027        position: DisplayPoint,
 4028        click_count: usize,
 4029        window: &mut Window,
 4030        cx: &mut Context<Self>,
 4031    ) {
 4032        let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
 4033        let tail = self
 4034            .selections
 4035            .newest::<MultiBufferOffset>(&display_map)
 4036            .tail();
 4037        let click_count = click_count.max(match self.selections.select_mode() {
 4038            SelectMode::Character => 1,
 4039            SelectMode::Word(_) => 2,
 4040            SelectMode::Line(_) => 3,
 4041            SelectMode::All => 4,
 4042        });
 4043        self.begin_selection(position, false, click_count, window, cx);
 4044
 4045        let tail_anchor = display_map.buffer_snapshot().anchor_before(tail);
 4046
 4047        let current_selection = match self.selections.select_mode() {
 4048            SelectMode::Character | SelectMode::All => tail_anchor..tail_anchor,
 4049            SelectMode::Word(range) | SelectMode::Line(range) => range.clone(),
 4050        };
 4051
 4052        let mut pending_selection = self
 4053            .selections
 4054            .pending_anchor()
 4055            .cloned()
 4056            .expect("extend_selection not called with pending selection");
 4057
 4058        if pending_selection
 4059            .start
 4060            .cmp(&current_selection.start, display_map.buffer_snapshot())
 4061            == Ordering::Greater
 4062        {
 4063            pending_selection.start = current_selection.start;
 4064        }
 4065        if pending_selection
 4066            .end
 4067            .cmp(&current_selection.end, display_map.buffer_snapshot())
 4068            == Ordering::Less
 4069        {
 4070            pending_selection.end = current_selection.end;
 4071            pending_selection.reversed = true;
 4072        }
 4073
 4074        let mut pending_mode = self.selections.pending_mode().unwrap();
 4075        match &mut pending_mode {
 4076            SelectMode::Word(range) | SelectMode::Line(range) => *range = current_selection,
 4077            _ => {}
 4078        }
 4079
 4080        let effects = if EditorSettings::get_global(cx).autoscroll_on_clicks {
 4081            SelectionEffects::scroll(Autoscroll::fit())
 4082        } else {
 4083            SelectionEffects::no_scroll()
 4084        };
 4085
 4086        self.change_selections(effects, window, cx, |s| {
 4087            s.set_pending(pending_selection.clone(), pending_mode);
 4088            s.set_is_extending(true);
 4089        });
 4090    }
 4091
 4092    fn begin_selection(
 4093        &mut self,
 4094        position: DisplayPoint,
 4095        add: bool,
 4096        click_count: usize,
 4097        window: &mut Window,
 4098        cx: &mut Context<Self>,
 4099    ) {
 4100        if !self.focus_handle.is_focused(window) {
 4101            self.last_focused_descendant = None;
 4102            window.focus(&self.focus_handle, cx);
 4103        }
 4104
 4105        let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
 4106        let buffer = display_map.buffer_snapshot();
 4107        let position = display_map.clip_point(position, Bias::Left);
 4108
 4109        let start;
 4110        let end;
 4111        let mode;
 4112        let mut auto_scroll;
 4113        match click_count {
 4114            1 => {
 4115                start = buffer.anchor_before(position.to_point(&display_map));
 4116                end = start;
 4117                mode = SelectMode::Character;
 4118                auto_scroll = true;
 4119            }
 4120            2 => {
 4121                let position = display_map
 4122                    .clip_point(position, Bias::Left)
 4123                    .to_offset(&display_map, Bias::Left);
 4124                let (range, _) = buffer.surrounding_word(position, None);
 4125                start = buffer.anchor_before(range.start);
 4126                end = buffer.anchor_before(range.end);
 4127                mode = SelectMode::Word(start..end);
 4128                auto_scroll = true;
 4129            }
 4130            3 => {
 4131                let position = display_map
 4132                    .clip_point(position, Bias::Left)
 4133                    .to_point(&display_map);
 4134                let line_start = display_map.prev_line_boundary(position).0;
 4135                let next_line_start = buffer.clip_point(
 4136                    display_map.next_line_boundary(position).0 + Point::new(1, 0),
 4137                    Bias::Left,
 4138                );
 4139                start = buffer.anchor_before(line_start);
 4140                end = buffer.anchor_before(next_line_start);
 4141                mode = SelectMode::Line(start..end);
 4142                auto_scroll = true;
 4143            }
 4144            _ => {
 4145                start = buffer.anchor_before(MultiBufferOffset(0));
 4146                end = buffer.anchor_before(buffer.len());
 4147                mode = SelectMode::All;
 4148                auto_scroll = false;
 4149            }
 4150        }
 4151        auto_scroll &= EditorSettings::get_global(cx).autoscroll_on_clicks;
 4152
 4153        let point_to_delete: Option<usize> = {
 4154            let selected_points: Vec<Selection<Point>> =
 4155                self.selections.disjoint_in_range(start..end, &display_map);
 4156
 4157            if !add || click_count > 1 {
 4158                None
 4159            } else if !selected_points.is_empty() {
 4160                Some(selected_points[0].id)
 4161            } else {
 4162                let clicked_point_already_selected =
 4163                    self.selections.disjoint_anchors().iter().find(|selection| {
 4164                        selection.start.to_point(buffer) == start.to_point(buffer)
 4165                            || selection.end.to_point(buffer) == end.to_point(buffer)
 4166                    });
 4167
 4168                clicked_point_already_selected.map(|selection| selection.id)
 4169            }
 4170        };
 4171
 4172        let selections_count = self.selections.count();
 4173        let effects = if auto_scroll {
 4174            SelectionEffects::default()
 4175        } else {
 4176            SelectionEffects::no_scroll()
 4177        };
 4178
 4179        self.change_selections(effects, window, cx, |s| {
 4180            if let Some(point_to_delete) = point_to_delete {
 4181                s.delete(point_to_delete);
 4182
 4183                if selections_count == 1 {
 4184                    s.set_pending_anchor_range(start..end, mode);
 4185                }
 4186            } else {
 4187                if !add {
 4188                    s.clear_disjoint();
 4189                }
 4190
 4191                s.set_pending_anchor_range(start..end, mode);
 4192            }
 4193        });
 4194    }
 4195
 4196    fn begin_columnar_selection(
 4197        &mut self,
 4198        position: DisplayPoint,
 4199        goal_column: u32,
 4200        reset: bool,
 4201        mode: ColumnarMode,
 4202        window: &mut Window,
 4203        cx: &mut Context<Self>,
 4204    ) {
 4205        if !self.focus_handle.is_focused(window) {
 4206            self.last_focused_descendant = None;
 4207            window.focus(&self.focus_handle, cx);
 4208        }
 4209
 4210        let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
 4211
 4212        if reset {
 4213            let pointer_position = display_map
 4214                .buffer_snapshot()
 4215                .anchor_before(position.to_point(&display_map));
 4216
 4217            self.change_selections(
 4218                SelectionEffects::scroll(Autoscroll::newest()),
 4219                window,
 4220                cx,
 4221                |s| {
 4222                    s.clear_disjoint();
 4223                    s.set_pending_anchor_range(
 4224                        pointer_position..pointer_position,
 4225                        SelectMode::Character,
 4226                    );
 4227                },
 4228            );
 4229        };
 4230
 4231        let tail = self.selections.newest::<Point>(&display_map).tail();
 4232        let selection_anchor = display_map.buffer_snapshot().anchor_before(tail);
 4233        self.columnar_selection_state = match mode {
 4234            ColumnarMode::FromMouse => Some(ColumnarSelectionState::FromMouse {
 4235                selection_tail: selection_anchor,
 4236                display_point: if reset {
 4237                    if position.column() != goal_column {
 4238                        Some(DisplayPoint::new(position.row(), goal_column))
 4239                    } else {
 4240                        None
 4241                    }
 4242                } else {
 4243                    None
 4244                },
 4245            }),
 4246            ColumnarMode::FromSelection => Some(ColumnarSelectionState::FromSelection {
 4247                selection_tail: selection_anchor,
 4248            }),
 4249        };
 4250
 4251        if !reset {
 4252            self.select_columns(position, goal_column, &display_map, window, cx);
 4253        }
 4254    }
 4255
 4256    fn update_selection(
 4257        &mut self,
 4258        position: DisplayPoint,
 4259        goal_column: u32,
 4260        scroll_delta: gpui::Point<f32>,
 4261        window: &mut Window,
 4262        cx: &mut Context<Self>,
 4263    ) {
 4264        let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
 4265
 4266        if self.columnar_selection_state.is_some() {
 4267            self.select_columns(position, goal_column, &display_map, window, cx);
 4268        } else if let Some(mut pending) = self.selections.pending_anchor().cloned() {
 4269            let buffer = display_map.buffer_snapshot();
 4270            let head;
 4271            let tail;
 4272            let mode = self.selections.pending_mode().unwrap();
 4273            match &mode {
 4274                SelectMode::Character => {
 4275                    head = position.to_point(&display_map);
 4276                    tail = pending.tail().to_point(buffer);
 4277                }
 4278                SelectMode::Word(original_range) => {
 4279                    let offset = display_map
 4280                        .clip_point(position, Bias::Left)
 4281                        .to_offset(&display_map, Bias::Left);
 4282                    let original_range = original_range.to_offset(buffer);
 4283
 4284                    let head_offset = if buffer.is_inside_word(offset, None)
 4285                        || original_range.contains(&offset)
 4286                    {
 4287                        let (word_range, _) = buffer.surrounding_word(offset, None);
 4288                        if word_range.start < original_range.start {
 4289                            word_range.start
 4290                        } else {
 4291                            word_range.end
 4292                        }
 4293                    } else {
 4294                        offset
 4295                    };
 4296
 4297                    head = head_offset.to_point(buffer);
 4298                    if head_offset <= original_range.start {
 4299                        tail = original_range.end.to_point(buffer);
 4300                    } else {
 4301                        tail = original_range.start.to_point(buffer);
 4302                    }
 4303                }
 4304                SelectMode::Line(original_range) => {
 4305                    let original_range = original_range.to_point(display_map.buffer_snapshot());
 4306
 4307                    let position = display_map
 4308                        .clip_point(position, Bias::Left)
 4309                        .to_point(&display_map);
 4310                    let line_start = display_map.prev_line_boundary(position).0;
 4311                    let next_line_start = buffer.clip_point(
 4312                        display_map.next_line_boundary(position).0 + Point::new(1, 0),
 4313                        Bias::Left,
 4314                    );
 4315
 4316                    if line_start < original_range.start {
 4317                        head = line_start
 4318                    } else {
 4319                        head = next_line_start
 4320                    }
 4321
 4322                    if head <= original_range.start {
 4323                        tail = original_range.end;
 4324                    } else {
 4325                        tail = original_range.start;
 4326                    }
 4327                }
 4328                SelectMode::All => {
 4329                    return;
 4330                }
 4331            };
 4332
 4333            if head < tail {
 4334                pending.start = buffer.anchor_before(head);
 4335                pending.end = buffer.anchor_before(tail);
 4336                pending.reversed = true;
 4337            } else {
 4338                pending.start = buffer.anchor_before(tail);
 4339                pending.end = buffer.anchor_before(head);
 4340                pending.reversed = false;
 4341            }
 4342
 4343            self.change_selections(SelectionEffects::no_scroll(), window, cx, |s| {
 4344                s.set_pending(pending.clone(), mode);
 4345            });
 4346        } else {
 4347            log::error!("update_selection dispatched with no pending selection");
 4348            return;
 4349        }
 4350
 4351        self.apply_scroll_delta(scroll_delta, window, cx);
 4352        cx.notify();
 4353    }
 4354
 4355    fn end_selection(&mut self, window: &mut Window, cx: &mut Context<Self>) {
 4356        self.columnar_selection_state.take();
 4357        if let Some(pending_mode) = self.selections.pending_mode() {
 4358            let selections = self
 4359                .selections
 4360                .all::<MultiBufferOffset>(&self.display_snapshot(cx));
 4361            self.change_selections(SelectionEffects::no_scroll(), window, cx, |s| {
 4362                s.select(selections);
 4363                s.clear_pending();
 4364                if s.is_extending() {
 4365                    s.set_is_extending(false);
 4366                } else {
 4367                    s.set_select_mode(pending_mode);
 4368                }
 4369            });
 4370        }
 4371    }
 4372
 4373    fn select_columns(
 4374        &mut self,
 4375        head: DisplayPoint,
 4376        goal_column: u32,
 4377        display_map: &DisplaySnapshot,
 4378        window: &mut Window,
 4379        cx: &mut Context<Self>,
 4380    ) {
 4381        let Some(columnar_state) = self.columnar_selection_state.as_ref() else {
 4382            return;
 4383        };
 4384
 4385        let tail = match columnar_state {
 4386            ColumnarSelectionState::FromMouse {
 4387                selection_tail,
 4388                display_point,
 4389            } => display_point.unwrap_or_else(|| selection_tail.to_display_point(display_map)),
 4390            ColumnarSelectionState::FromSelection { selection_tail } => {
 4391                selection_tail.to_display_point(display_map)
 4392            }
 4393        };
 4394
 4395        let start_row = cmp::min(tail.row(), head.row());
 4396        let end_row = cmp::max(tail.row(), head.row());
 4397        let start_column = cmp::min(tail.column(), goal_column);
 4398        let end_column = cmp::max(tail.column(), goal_column);
 4399        let reversed = start_column < tail.column();
 4400
 4401        let selection_ranges = (start_row.0..=end_row.0)
 4402            .map(DisplayRow)
 4403            .filter_map(|row| {
 4404                if (matches!(columnar_state, ColumnarSelectionState::FromMouse { .. })
 4405                    || start_column <= display_map.line_len(row))
 4406                    && !display_map.is_block_line(row)
 4407                {
 4408                    let start = display_map
 4409                        .clip_point(DisplayPoint::new(row, start_column), Bias::Left)
 4410                        .to_point(display_map);
 4411                    let end = display_map
 4412                        .clip_point(DisplayPoint::new(row, end_column), Bias::Right)
 4413                        .to_point(display_map);
 4414                    if reversed {
 4415                        Some(end..start)
 4416                    } else {
 4417                        Some(start..end)
 4418                    }
 4419                } else {
 4420                    None
 4421                }
 4422            })
 4423            .collect::<Vec<_>>();
 4424        if selection_ranges.is_empty() {
 4425            return;
 4426        }
 4427
 4428        let ranges = match columnar_state {
 4429            ColumnarSelectionState::FromMouse { .. } => {
 4430                let mut non_empty_ranges = selection_ranges
 4431                    .iter()
 4432                    .filter(|selection_range| selection_range.start != selection_range.end)
 4433                    .peekable();
 4434                if non_empty_ranges.peek().is_some() {
 4435                    non_empty_ranges.cloned().collect()
 4436                } else {
 4437                    selection_ranges
 4438                }
 4439            }
 4440            _ => selection_ranges,
 4441        };
 4442
 4443        self.change_selections(SelectionEffects::no_scroll(), window, cx, |s| {
 4444            s.select_ranges(ranges);
 4445        });
 4446        cx.notify();
 4447    }
 4448
 4449    pub fn has_non_empty_selection(&self, snapshot: &DisplaySnapshot) -> bool {
 4450        self.selections
 4451            .all_adjusted(snapshot)
 4452            .iter()
 4453            .any(|selection| !selection.is_empty())
 4454    }
 4455
 4456    pub fn has_pending_nonempty_selection(&self) -> bool {
 4457        let pending_nonempty_selection = match self.selections.pending_anchor() {
 4458            Some(Selection { start, end, .. }) => start != end,
 4459            None => false,
 4460        };
 4461
 4462        pending_nonempty_selection
 4463            || (self.columnar_selection_state.is_some()
 4464                && self.selections.disjoint_anchors().len() > 1)
 4465    }
 4466
 4467    pub fn has_pending_selection(&self) -> bool {
 4468        self.selections.pending_anchor().is_some() || self.columnar_selection_state.is_some()
 4469    }
 4470
 4471    pub fn cancel(&mut self, _: &Cancel, window: &mut Window, cx: &mut Context<Self>) {
 4472        self.selection_mark_mode = false;
 4473        self.selection_drag_state = SelectionDragState::None;
 4474
 4475        if self.dismiss_menus_and_popups(true, window, cx) {
 4476            cx.notify();
 4477            return;
 4478        }
 4479        if self.clear_expanded_diff_hunks(cx) {
 4480            cx.notify();
 4481            return;
 4482        }
 4483        if self.show_git_blame_gutter {
 4484            self.show_git_blame_gutter = false;
 4485            cx.notify();
 4486            return;
 4487        }
 4488
 4489        if self.mode.is_full()
 4490            && self.change_selections(Default::default(), window, cx, |s| s.try_cancel())
 4491        {
 4492            cx.notify();
 4493            return;
 4494        }
 4495
 4496        cx.propagate();
 4497    }
 4498
 4499    pub fn dismiss_menus_and_popups(
 4500        &mut self,
 4501        is_user_requested: bool,
 4502        window: &mut Window,
 4503        cx: &mut Context<Self>,
 4504    ) -> bool {
 4505        let mut dismissed = false;
 4506
 4507        dismissed |= self.take_rename(false, window, cx).is_some();
 4508        dismissed |= self.hide_blame_popover(true, cx);
 4509        dismissed |= hide_hover(self, cx);
 4510        dismissed |= self.hide_signature_help(cx, SignatureHelpHiddenBy::Escape);
 4511        dismissed |= self.hide_context_menu(window, cx).is_some();
 4512        dismissed |= self.mouse_context_menu.take().is_some();
 4513        dismissed |= is_user_requested
 4514            && self.discard_edit_prediction(EditPredictionDiscardReason::Rejected, cx);
 4515        dismissed |= self.snippet_stack.pop().is_some();
 4516        if self.diff_review_drag_state.is_some() {
 4517            self.cancel_diff_review_drag(cx);
 4518            dismissed = true;
 4519        }
 4520        if !self.diff_review_overlays.is_empty() {
 4521            self.dismiss_all_diff_review_overlays(cx);
 4522            dismissed = true;
 4523        }
 4524
 4525        if self.mode.is_full() && matches!(self.active_diagnostics, ActiveDiagnostic::Group(_)) {
 4526            self.dismiss_diagnostics(cx);
 4527            dismissed = true;
 4528        }
 4529
 4530        dismissed
 4531    }
 4532
 4533    fn linked_editing_ranges_for(
 4534        &self,
 4535        selection: Range<text::Anchor>,
 4536        cx: &App,
 4537    ) -> Option<HashMap<Entity<Buffer>, Vec<Range<text::Anchor>>>> {
 4538        if self.linked_edit_ranges.is_empty() {
 4539            return None;
 4540        }
 4541        let ((base_range, linked_ranges), buffer_snapshot, buffer) =
 4542            selection.end.buffer_id.and_then(|end_buffer_id| {
 4543                if selection.start.buffer_id != Some(end_buffer_id) {
 4544                    return None;
 4545                }
 4546                let buffer = self.buffer.read(cx).buffer(end_buffer_id)?;
 4547                let snapshot = buffer.read(cx).snapshot();
 4548                self.linked_edit_ranges
 4549                    .get(end_buffer_id, selection.start..selection.end, &snapshot)
 4550                    .map(|ranges| (ranges, snapshot, buffer))
 4551            })?;
 4552        use text::ToOffset as TO;
 4553        // find offset from the start of current range to current cursor position
 4554        let start_byte_offset = TO::to_offset(&base_range.start, &buffer_snapshot);
 4555
 4556        let start_offset = TO::to_offset(&selection.start, &buffer_snapshot);
 4557        let start_difference = start_offset - start_byte_offset;
 4558        let end_offset = TO::to_offset(&selection.end, &buffer_snapshot);
 4559        let end_difference = end_offset - start_byte_offset;
 4560
 4561        // Current range has associated linked ranges.
 4562        let mut linked_edits = HashMap::<_, Vec<_>>::default();
 4563        for range in linked_ranges.iter() {
 4564            let start_offset = TO::to_offset(&range.start, &buffer_snapshot);
 4565            let end_offset = start_offset + end_difference;
 4566            let start_offset = start_offset + start_difference;
 4567            if start_offset > buffer_snapshot.len() || end_offset > buffer_snapshot.len() {
 4568                continue;
 4569            }
 4570            if self.selections.disjoint_anchor_ranges().any(|s| {
 4571                if s.start.text_anchor.buffer_id != selection.start.buffer_id
 4572                    || s.end.text_anchor.buffer_id != selection.end.buffer_id
 4573                {
 4574                    return false;
 4575                }
 4576                TO::to_offset(&s.start.text_anchor, &buffer_snapshot) <= end_offset
 4577                    && TO::to_offset(&s.end.text_anchor, &buffer_snapshot) >= start_offset
 4578            }) {
 4579                continue;
 4580            }
 4581            let start = buffer_snapshot.anchor_after(start_offset);
 4582            let end = buffer_snapshot.anchor_after(end_offset);
 4583            linked_edits
 4584                .entry(buffer.clone())
 4585                .or_default()
 4586                .push(start..end);
 4587        }
 4588        Some(linked_edits)
 4589    }
 4590
 4591    pub fn handle_input(&mut self, text: &str, window: &mut Window, cx: &mut Context<Self>) {
 4592        let text: Arc<str> = text.into();
 4593
 4594        if self.read_only(cx) {
 4595            return;
 4596        }
 4597
 4598        self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
 4599
 4600        self.unfold_buffers_with_selections(cx);
 4601
 4602        let selections = self.selections.all_adjusted(&self.display_snapshot(cx));
 4603        let mut bracket_inserted = false;
 4604        let mut edits = Vec::new();
 4605        let mut linked_edits = LinkedEdits::new();
 4606        let mut new_selections = Vec::with_capacity(selections.len());
 4607        let mut new_autoclose_regions = Vec::new();
 4608        let snapshot = self.buffer.read(cx).read(cx);
 4609        let mut clear_linked_edit_ranges = false;
 4610        let mut all_selections_read_only = true;
 4611        let mut has_adjacent_edits = false;
 4612        let mut in_adjacent_group = false;
 4613
 4614        let mut regions = self
 4615            .selections_with_autoclose_regions(selections, &snapshot)
 4616            .peekable();
 4617
 4618        while let Some((selection, autoclose_region)) = regions.next() {
 4619            if snapshot
 4620                .point_to_buffer_point(selection.head())
 4621                .is_none_or(|(snapshot, ..)| !snapshot.capability.editable())
 4622            {
 4623                continue;
 4624            }
 4625            if snapshot
 4626                .point_to_buffer_point(selection.tail())
 4627                .is_none_or(|(snapshot, ..)| !snapshot.capability.editable())
 4628            {
 4629                // note, ideally we'd clip the tail to the closest writeable region towards the head
 4630                continue;
 4631            }
 4632            all_selections_read_only = false;
 4633
 4634            if let Some(scope) = snapshot.language_scope_at(selection.head()) {
 4635                // Determine if the inserted text matches the opening or closing
 4636                // bracket of any of this language's bracket pairs.
 4637                let mut bracket_pair = None;
 4638                let mut is_bracket_pair_start = false;
 4639                let mut is_bracket_pair_end = false;
 4640                if !text.is_empty() {
 4641                    let mut bracket_pair_matching_end = None;
 4642                    // `text` can be empty when a user is using IME (e.g. Chinese Wubi Simplified)
 4643                    //  and they are removing the character that triggered IME popup.
 4644                    for (pair, enabled) in scope.brackets() {
 4645                        if !pair.close && !pair.surround {
 4646                            continue;
 4647                        }
 4648
 4649                        if enabled && pair.start.ends_with(text.as_ref()) {
 4650                            let prefix_len = pair.start.len() - text.len();
 4651                            let preceding_text_matches_prefix = prefix_len == 0
 4652                                || (selection.start.column >= (prefix_len as u32)
 4653                                    && snapshot.contains_str_at(
 4654                                        Point::new(
 4655                                            selection.start.row,
 4656                                            selection.start.column - (prefix_len as u32),
 4657                                        ),
 4658                                        &pair.start[..prefix_len],
 4659                                    ));
 4660                            if preceding_text_matches_prefix {
 4661                                bracket_pair = Some(pair.clone());
 4662                                is_bracket_pair_start = true;
 4663                                break;
 4664                            }
 4665                        }
 4666                        if pair.end.as_str() == text.as_ref() && bracket_pair_matching_end.is_none()
 4667                        {
 4668                            // take first bracket pair matching end, but don't break in case a later bracket
 4669                            // pair matches start
 4670                            bracket_pair_matching_end = Some(pair.clone());
 4671                        }
 4672                    }
 4673                    if let Some(end) = bracket_pair_matching_end
 4674                        && bracket_pair.is_none()
 4675                    {
 4676                        bracket_pair = Some(end);
 4677                        is_bracket_pair_end = true;
 4678                    }
 4679                }
 4680
 4681                if let Some(bracket_pair) = bracket_pair {
 4682                    let snapshot_settings = snapshot.language_settings_at(selection.start, cx);
 4683                    let autoclose = self.use_autoclose && snapshot_settings.use_autoclose;
 4684                    let auto_surround =
 4685                        self.use_auto_surround && snapshot_settings.use_auto_surround;
 4686                    if selection.is_empty() {
 4687                        if is_bracket_pair_start {
 4688                            // If the inserted text is a suffix of an opening bracket and the
 4689                            // selection is preceded by the rest of the opening bracket, then
 4690                            // insert the closing bracket.
 4691                            let following_text_allows_autoclose = snapshot
 4692                                .chars_at(selection.start)
 4693                                .next()
 4694                                .is_none_or(|c| scope.should_autoclose_before(c));
 4695
 4696                            let preceding_text_allows_autoclose = selection.start.column == 0
 4697                                || snapshot
 4698                                    .reversed_chars_at(selection.start)
 4699                                    .next()
 4700                                    .is_none_or(|c| {
 4701                                        bracket_pair.start != bracket_pair.end
 4702                                            || !snapshot
 4703                                                .char_classifier_at(selection.start)
 4704                                                .is_word(c)
 4705                                    });
 4706
 4707                            let is_closing_quote = if bracket_pair.end == bracket_pair.start
 4708                                && bracket_pair.start.len() == 1
 4709                            {
 4710                                let target = bracket_pair.start.chars().next().unwrap();
 4711                                let mut byte_offset = 0u32;
 4712                                let current_line_count = snapshot
 4713                                    .reversed_chars_at(selection.start)
 4714                                    .take_while(|&c| c != '\n')
 4715                                    .filter(|c| {
 4716                                        byte_offset += c.len_utf8() as u32;
 4717                                        if *c != target {
 4718                                            return false;
 4719                                        }
 4720
 4721                                        let point = Point::new(
 4722                                            selection.start.row,
 4723                                            selection.start.column.saturating_sub(byte_offset),
 4724                                        );
 4725
 4726                                        let is_enabled = snapshot
 4727                                            .language_scope_at(point)
 4728                                            .and_then(|scope| {
 4729                                                scope
 4730                                                    .brackets()
 4731                                                    .find(|(pair, _)| {
 4732                                                        pair.start == bracket_pair.start
 4733                                                    })
 4734                                                    .map(|(_, enabled)| enabled)
 4735                                            })
 4736                                            .unwrap_or(true);
 4737
 4738                                        let is_delimiter = snapshot
 4739                                            .language_scope_at(Point::new(
 4740                                                point.row,
 4741                                                point.column + 1,
 4742                                            ))
 4743                                            .and_then(|scope| {
 4744                                                scope
 4745                                                    .brackets()
 4746                                                    .find(|(pair, _)| {
 4747                                                        pair.start == bracket_pair.start
 4748                                                    })
 4749                                                    .map(|(_, enabled)| !enabled)
 4750                                            })
 4751                                            .unwrap_or(false);
 4752
 4753                                        is_enabled && !is_delimiter
 4754                                    })
 4755                                    .count();
 4756                                current_line_count % 2 == 1
 4757                            } else {
 4758                                false
 4759                            };
 4760
 4761                            if autoclose
 4762                                && bracket_pair.close
 4763                                && following_text_allows_autoclose
 4764                                && preceding_text_allows_autoclose
 4765                                && !is_closing_quote
 4766                            {
 4767                                let anchor = snapshot.anchor_before(selection.end);
 4768                                new_selections.push((selection.map(|_| anchor), text.len()));
 4769                                new_autoclose_regions.push((
 4770                                    anchor,
 4771                                    text.len(),
 4772                                    selection.id,
 4773                                    bracket_pair.clone(),
 4774                                ));
 4775                                edits.push((
 4776                                    selection.range(),
 4777                                    format!("{}{}", text, bracket_pair.end).into(),
 4778                                ));
 4779                                bracket_inserted = true;
 4780                                continue;
 4781                            }
 4782                        }
 4783
 4784                        if let Some(region) = autoclose_region {
 4785                            // If the selection is followed by an auto-inserted closing bracket,
 4786                            // then don't insert that closing bracket again; just move the selection
 4787                            // past the closing bracket.
 4788                            let should_skip = selection.end == region.range.end.to_point(&snapshot)
 4789                                && text.as_ref() == region.pair.end.as_str()
 4790                                && snapshot.contains_str_at(region.range.end, text.as_ref());
 4791                            if should_skip {
 4792                                let anchor = snapshot.anchor_after(selection.end);
 4793                                new_selections
 4794                                    .push((selection.map(|_| anchor), region.pair.end.len()));
 4795                                continue;
 4796                            }
 4797                        }
 4798
 4799                        let always_treat_brackets_as_autoclosed = snapshot
 4800                            .language_settings_at(selection.start, cx)
 4801                            .always_treat_brackets_as_autoclosed;
 4802                        if always_treat_brackets_as_autoclosed
 4803                            && is_bracket_pair_end
 4804                            && snapshot.contains_str_at(selection.end, text.as_ref())
 4805                        {
 4806                            // Otherwise, when `always_treat_brackets_as_autoclosed` is set to `true
 4807                            // and the inserted text is a closing bracket and the selection is followed
 4808                            // by the closing bracket then move the selection past the closing bracket.
 4809                            let anchor = snapshot.anchor_after(selection.end);
 4810                            new_selections.push((selection.map(|_| anchor), text.len()));
 4811                            continue;
 4812                        }
 4813                    }
 4814                    // If an opening bracket is 1 character long and is typed while
 4815                    // text is selected, then surround that text with the bracket pair.
 4816                    else if auto_surround
 4817                        && bracket_pair.surround
 4818                        && is_bracket_pair_start
 4819                        && bracket_pair.start.chars().count() == 1
 4820                    {
 4821                        edits.push((selection.start..selection.start, text.clone()));
 4822                        edits.push((
 4823                            selection.end..selection.end,
 4824                            bracket_pair.end.as_str().into(),
 4825                        ));
 4826                        bracket_inserted = true;
 4827                        new_selections.push((
 4828                            Selection {
 4829                                id: selection.id,
 4830                                start: snapshot.anchor_after(selection.start),
 4831                                end: snapshot.anchor_before(selection.end),
 4832                                reversed: selection.reversed,
 4833                                goal: selection.goal,
 4834                            },
 4835                            0,
 4836                        ));
 4837                        continue;
 4838                    }
 4839                }
 4840            }
 4841
 4842            if self.auto_replace_emoji_shortcode
 4843                && selection.is_empty()
 4844                && text.as_ref().ends_with(':')
 4845                && let Some(possible_emoji_short_code) =
 4846                    Self::find_possible_emoji_shortcode_at_position(&snapshot, selection.start)
 4847                && !possible_emoji_short_code.is_empty()
 4848                && let Some(emoji) = emojis::get_by_shortcode(&possible_emoji_short_code)
 4849            {
 4850                let emoji_shortcode_start = Point::new(
 4851                    selection.start.row,
 4852                    selection.start.column - possible_emoji_short_code.len() as u32 - 1,
 4853                );
 4854
 4855                // Remove shortcode from buffer
 4856                edits.push((
 4857                    emoji_shortcode_start..selection.start,
 4858                    "".to_string().into(),
 4859                ));
 4860                new_selections.push((
 4861                    Selection {
 4862                        id: selection.id,
 4863                        start: snapshot.anchor_after(emoji_shortcode_start),
 4864                        end: snapshot.anchor_before(selection.start),
 4865                        reversed: selection.reversed,
 4866                        goal: selection.goal,
 4867                    },
 4868                    0,
 4869                ));
 4870
 4871                // Insert emoji
 4872                let selection_start_anchor = snapshot.anchor_after(selection.start);
 4873                new_selections.push((selection.map(|_| selection_start_anchor), 0));
 4874                edits.push((selection.start..selection.end, emoji.to_string().into()));
 4875
 4876                continue;
 4877            }
 4878
 4879            let next_is_adjacent = regions
 4880                .peek()
 4881                .is_some_and(|(next, _)| selection.end == next.start);
 4882
 4883            // If not handling any auto-close operation, then just replace the selected
 4884            // text with the given input and move the selection to the end of the
 4885            // newly inserted text.
 4886            let anchor = if in_adjacent_group || next_is_adjacent {
 4887                // After edits the right bias would shift those anchor to the next visible fragment
 4888                // but we want to resolve to the previous one
 4889                snapshot.anchor_before(selection.end)
 4890            } else {
 4891                snapshot.anchor_after(selection.end)
 4892            };
 4893
 4894            if !self.linked_edit_ranges.is_empty() {
 4895                let start_anchor = snapshot.anchor_before(selection.start);
 4896
 4897                let is_word_char = text.chars().next().is_none_or(|char| {
 4898                    let classifier = snapshot
 4899                        .char_classifier_at(start_anchor.to_offset(&snapshot))
 4900                        .scope_context(Some(CharScopeContext::LinkedEdit));
 4901                    classifier.is_word(char)
 4902                });
 4903                let is_dot = text.as_ref() == ".";
 4904                let should_apply_linked_edit = is_word_char || is_dot;
 4905
 4906                if should_apply_linked_edit {
 4907                    let anchor_range = start_anchor.text_anchor..anchor.text_anchor;
 4908                    linked_edits.push(&self, anchor_range, text.clone(), cx);
 4909                } else {
 4910                    clear_linked_edit_ranges = true;
 4911                }
 4912            }
 4913
 4914            new_selections.push((selection.map(|_| anchor), 0));
 4915            edits.push((selection.start..selection.end, text.clone()));
 4916
 4917            has_adjacent_edits |= next_is_adjacent;
 4918            in_adjacent_group = next_is_adjacent;
 4919        }
 4920
 4921        if all_selections_read_only {
 4922            return;
 4923        }
 4924
 4925        drop(regions);
 4926        drop(snapshot);
 4927
 4928        self.transact(window, cx, |this, window, cx| {
 4929            if clear_linked_edit_ranges {
 4930                this.linked_edit_ranges.clear();
 4931            }
 4932            let initial_buffer_versions =
 4933                jsx_tag_auto_close::construct_initial_buffer_versions_map(this, &edits, cx);
 4934
 4935            this.buffer.update(cx, |buffer, cx| {
 4936                if has_adjacent_edits {
 4937                    buffer.edit_non_coalesce(edits, this.autoindent_mode.clone(), cx);
 4938                } else {
 4939                    buffer.edit(edits, this.autoindent_mode.clone(), cx);
 4940                }
 4941            });
 4942            linked_edits.apply(cx);
 4943            let new_anchor_selections = new_selections.iter().map(|e| &e.0);
 4944            let new_selection_deltas = new_selections.iter().map(|e| e.1);
 4945            let map = this.display_map.update(cx, |map, cx| map.snapshot(cx));
 4946            let new_selections = resolve_selections_wrapping_blocks::<MultiBufferOffset, _>(
 4947                new_anchor_selections,
 4948                &map,
 4949            )
 4950            .zip(new_selection_deltas)
 4951            .map(|(selection, delta)| Selection {
 4952                id: selection.id,
 4953                start: selection.start + delta,
 4954                end: selection.end + delta,
 4955                reversed: selection.reversed,
 4956                goal: SelectionGoal::None,
 4957            })
 4958            .collect::<Vec<_>>();
 4959
 4960            let mut i = 0;
 4961            for (position, delta, selection_id, pair) in new_autoclose_regions {
 4962                let position = position.to_offset(map.buffer_snapshot()) + delta;
 4963                let start = map.buffer_snapshot().anchor_before(position);
 4964                let end = map.buffer_snapshot().anchor_after(position);
 4965                while let Some(existing_state) = this.autoclose_regions.get(i) {
 4966                    match existing_state
 4967                        .range
 4968                        .start
 4969                        .cmp(&start, map.buffer_snapshot())
 4970                    {
 4971                        Ordering::Less => i += 1,
 4972                        Ordering::Greater => break,
 4973                        Ordering::Equal => {
 4974                            match end.cmp(&existing_state.range.end, map.buffer_snapshot()) {
 4975                                Ordering::Less => i += 1,
 4976                                Ordering::Equal => break,
 4977                                Ordering::Greater => break,
 4978                            }
 4979                        }
 4980                    }
 4981                }
 4982                this.autoclose_regions.insert(
 4983                    i,
 4984                    AutocloseRegion {
 4985                        selection_id,
 4986                        range: start..end,
 4987                        pair,
 4988                    },
 4989                );
 4990            }
 4991
 4992            let had_active_edit_prediction = this.has_active_edit_prediction();
 4993            this.change_selections(
 4994                SelectionEffects::scroll(Autoscroll::fit()).completions(false),
 4995                window,
 4996                cx,
 4997                |s| s.select(new_selections),
 4998            );
 4999
 5000            if !bracket_inserted
 5001                && let Some(on_type_format_task) =
 5002                    this.trigger_on_type_formatting(text.to_string(), window, cx)
 5003            {
 5004                on_type_format_task.detach_and_log_err(cx);
 5005            }
 5006
 5007            let editor_settings = EditorSettings::get_global(cx);
 5008            if bracket_inserted
 5009                && (editor_settings.auto_signature_help
 5010                    || editor_settings.show_signature_help_after_edits)
 5011            {
 5012                this.show_signature_help(&ShowSignatureHelp, window, cx);
 5013            }
 5014
 5015            let trigger_in_words =
 5016                this.show_edit_predictions_in_menu() || !had_active_edit_prediction;
 5017            if this.hard_wrap.is_some() {
 5018                let latest: Range<Point> = this.selections.newest(&map).range();
 5019                if latest.is_empty()
 5020                    && this
 5021                        .buffer()
 5022                        .read(cx)
 5023                        .snapshot(cx)
 5024                        .line_len(MultiBufferRow(latest.start.row))
 5025                        == latest.start.column
 5026                {
 5027                    this.rewrap_impl(
 5028                        RewrapOptions {
 5029                            override_language_settings: true,
 5030                            preserve_existing_whitespace: true,
 5031                        },
 5032                        cx,
 5033                    )
 5034                }
 5035            }
 5036            this.trigger_completion_on_input(&text, trigger_in_words, window, cx);
 5037            refresh_linked_ranges(this, window, cx);
 5038            this.refresh_edit_prediction(true, false, window, cx);
 5039            jsx_tag_auto_close::handle_from(this, initial_buffer_versions, window, cx);
 5040        });
 5041    }
 5042
 5043    fn find_possible_emoji_shortcode_at_position(
 5044        snapshot: &MultiBufferSnapshot,
 5045        position: Point,
 5046    ) -> Option<String> {
 5047        let mut chars = Vec::new();
 5048        let mut found_colon = false;
 5049        for char in snapshot.reversed_chars_at(position).take(100) {
 5050            // Found a possible emoji shortcode in the middle of the buffer
 5051            if found_colon {
 5052                if char.is_whitespace() {
 5053                    chars.reverse();
 5054                    return Some(chars.iter().collect());
 5055                }
 5056                // If the previous character is not a whitespace, we are in the middle of a word
 5057                // and we only want to complete the shortcode if the word is made up of other emojis
 5058                let mut containing_word = String::new();
 5059                for ch in snapshot
 5060                    .reversed_chars_at(position)
 5061                    .skip(chars.len() + 1)
 5062                    .take(100)
 5063                {
 5064                    if ch.is_whitespace() {
 5065                        break;
 5066                    }
 5067                    containing_word.push(ch);
 5068                }
 5069                let containing_word = containing_word.chars().rev().collect::<String>();
 5070                if util::word_consists_of_emojis(containing_word.as_str()) {
 5071                    chars.reverse();
 5072                    return Some(chars.iter().collect());
 5073                }
 5074            }
 5075
 5076            if char.is_whitespace() || !char.is_ascii() {
 5077                return None;
 5078            }
 5079            if char == ':' {
 5080                found_colon = true;
 5081            } else {
 5082                chars.push(char);
 5083            }
 5084        }
 5085        // Found a possible emoji shortcode at the beginning of the buffer
 5086        chars.reverse();
 5087        Some(chars.iter().collect())
 5088    }
 5089
 5090    pub fn newline(&mut self, _: &Newline, window: &mut Window, cx: &mut Context<Self>) {
 5091        self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
 5092        self.transact(window, cx, |this, window, cx| {
 5093            let (edits_with_flags, selection_info): (Vec<_>, Vec<_>) = {
 5094                let selections = this
 5095                    .selections
 5096                    .all::<MultiBufferOffset>(&this.display_snapshot(cx));
 5097                let multi_buffer = this.buffer.read(cx);
 5098                let buffer = multi_buffer.snapshot(cx);
 5099                selections
 5100                    .iter()
 5101                    .map(|selection| {
 5102                        let start_point = selection.start.to_point(&buffer);
 5103                        let mut existing_indent =
 5104                            buffer.indent_size_for_line(MultiBufferRow(start_point.row));
 5105                        existing_indent.len = cmp::min(existing_indent.len, start_point.column);
 5106                        let start = selection.start;
 5107                        let end = selection.end;
 5108                        let selection_is_empty = start == end;
 5109                        let language_scope = buffer.language_scope_at(start);
 5110                        let (delimiter, newline_config) = if let Some(language) = &language_scope {
 5111                            let needs_extra_newline = NewlineConfig::insert_extra_newline_brackets(
 5112                                &buffer,
 5113                                start..end,
 5114                                language,
 5115                            )
 5116                                || NewlineConfig::insert_extra_newline_tree_sitter(
 5117                                    &buffer,
 5118                                    start..end,
 5119                                );
 5120
 5121                            let mut newline_config = NewlineConfig::Newline {
 5122                                additional_indent: IndentSize::spaces(0),
 5123                                extra_line_additional_indent: if needs_extra_newline {
 5124                                    Some(IndentSize::spaces(0))
 5125                                } else {
 5126                                    None
 5127                                },
 5128                                prevent_auto_indent: false,
 5129                            };
 5130
 5131                            let comment_delimiter = maybe!({
 5132                                if !selection_is_empty {
 5133                                    return None;
 5134                                }
 5135
 5136                                if !multi_buffer.language_settings(cx).extend_comment_on_newline {
 5137                                    return None;
 5138                                }
 5139
 5140                                return comment_delimiter_for_newline(
 5141                                    &start_point,
 5142                                    &buffer,
 5143                                    language,
 5144                                );
 5145                            });
 5146
 5147                            let doc_delimiter = maybe!({
 5148                                if !selection_is_empty {
 5149                                    return None;
 5150                                }
 5151
 5152                                if !multi_buffer.language_settings(cx).extend_comment_on_newline {
 5153                                    return None;
 5154                                }
 5155
 5156                                return documentation_delimiter_for_newline(
 5157                                    &start_point,
 5158                                    &buffer,
 5159                                    language,
 5160                                    &mut newline_config,
 5161                                );
 5162                            });
 5163
 5164                            let list_delimiter = maybe!({
 5165                                if !selection_is_empty {
 5166                                    return None;
 5167                                }
 5168
 5169                                if !multi_buffer.language_settings(cx).extend_list_on_newline {
 5170                                    return None;
 5171                                }
 5172
 5173                                return list_delimiter_for_newline(
 5174                                    &start_point,
 5175                                    &buffer,
 5176                                    language,
 5177                                    &mut newline_config,
 5178                                );
 5179                            });
 5180
 5181                            (
 5182                                comment_delimiter.or(doc_delimiter).or(list_delimiter),
 5183                                newline_config,
 5184                            )
 5185                        } else {
 5186                            (
 5187                                None,
 5188                                NewlineConfig::Newline {
 5189                                    additional_indent: IndentSize::spaces(0),
 5190                                    extra_line_additional_indent: None,
 5191                                    prevent_auto_indent: false,
 5192                                },
 5193                            )
 5194                        };
 5195
 5196                        let (edit_start, new_text, prevent_auto_indent) = match &newline_config {
 5197                            NewlineConfig::ClearCurrentLine => {
 5198                                let row_start =
 5199                                    buffer.point_to_offset(Point::new(start_point.row, 0));
 5200                                (row_start, String::new(), false)
 5201                            }
 5202                            NewlineConfig::UnindentCurrentLine { continuation } => {
 5203                                let row_start =
 5204                                    buffer.point_to_offset(Point::new(start_point.row, 0));
 5205                                let tab_size = buffer.language_settings_at(start, cx).tab_size;
 5206                                let tab_size_indent = IndentSize::spaces(tab_size.get());
 5207                                let reduced_indent =
 5208                                    existing_indent.with_delta(Ordering::Less, tab_size_indent);
 5209                                let mut new_text = String::new();
 5210                                new_text.extend(reduced_indent.chars());
 5211                                new_text.push_str(continuation);
 5212                                (row_start, new_text, true)
 5213                            }
 5214                            NewlineConfig::Newline {
 5215                                additional_indent,
 5216                                extra_line_additional_indent,
 5217                                prevent_auto_indent,
 5218                            } => {
 5219                                let capacity_for_delimiter =
 5220                                    delimiter.as_deref().map(str::len).unwrap_or_default();
 5221                                let extra_line_len = extra_line_additional_indent
 5222                                    .map(|i| 1 + existing_indent.len as usize + i.len as usize)
 5223                                    .unwrap_or(0);
 5224                                let mut new_text = String::with_capacity(
 5225                                    1 + capacity_for_delimiter
 5226                                        + existing_indent.len as usize
 5227                                        + additional_indent.len as usize
 5228                                        + extra_line_len,
 5229                                );
 5230                                new_text.push('\n');
 5231                                new_text.extend(existing_indent.chars());
 5232                                new_text.extend(additional_indent.chars());
 5233                                if let Some(delimiter) = &delimiter {
 5234                                    new_text.push_str(delimiter);
 5235                                }
 5236                                if let Some(extra_indent) = extra_line_additional_indent {
 5237                                    new_text.push('\n');
 5238                                    new_text.extend(existing_indent.chars());
 5239                                    new_text.extend(extra_indent.chars());
 5240                                }
 5241                                (start, new_text, *prevent_auto_indent)
 5242                            }
 5243                        };
 5244
 5245                        let anchor = buffer.anchor_after(end);
 5246                        let new_selection = selection.map(|_| anchor);
 5247                        (
 5248                            ((edit_start..end, new_text), prevent_auto_indent),
 5249                            (newline_config.has_extra_line(), new_selection),
 5250                        )
 5251                    })
 5252                    .unzip()
 5253            };
 5254
 5255            let mut auto_indent_edits = Vec::new();
 5256            let mut edits = Vec::new();
 5257            for (edit, prevent_auto_indent) in edits_with_flags {
 5258                if prevent_auto_indent {
 5259                    edits.push(edit);
 5260                } else {
 5261                    auto_indent_edits.push(edit);
 5262                }
 5263            }
 5264            if !edits.is_empty() {
 5265                this.edit(edits, cx);
 5266            }
 5267            if !auto_indent_edits.is_empty() {
 5268                this.edit_with_autoindent(auto_indent_edits, cx);
 5269            }
 5270
 5271            let buffer = this.buffer.read(cx).snapshot(cx);
 5272            let new_selections = selection_info
 5273                .into_iter()
 5274                .map(|(extra_newline_inserted, new_selection)| {
 5275                    let mut cursor = new_selection.end.to_point(&buffer);
 5276                    if extra_newline_inserted {
 5277                        cursor.row -= 1;
 5278                        cursor.column = buffer.line_len(MultiBufferRow(cursor.row));
 5279                    }
 5280                    new_selection.map(|_| cursor)
 5281                })
 5282                .collect();
 5283
 5284            this.change_selections(Default::default(), window, cx, |s| s.select(new_selections));
 5285            this.refresh_edit_prediction(true, false, window, cx);
 5286            if let Some(task) = this.trigger_on_type_formatting("\n".to_owned(), window, cx) {
 5287                task.detach_and_log_err(cx);
 5288            }
 5289        });
 5290    }
 5291
 5292    pub fn newline_above(&mut self, _: &NewlineAbove, window: &mut Window, cx: &mut Context<Self>) {
 5293        self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
 5294
 5295        let buffer = self.buffer.read(cx);
 5296        let snapshot = buffer.snapshot(cx);
 5297
 5298        let mut edits = Vec::new();
 5299        let mut rows = Vec::new();
 5300
 5301        for (rows_inserted, selection) in self
 5302            .selections
 5303            .all_adjusted(&self.display_snapshot(cx))
 5304            .into_iter()
 5305            .enumerate()
 5306        {
 5307            let cursor = selection.head();
 5308            let row = cursor.row;
 5309
 5310            let start_of_line = snapshot.clip_point(Point::new(row, 0), Bias::Left);
 5311
 5312            let newline = "\n".to_string();
 5313            edits.push((start_of_line..start_of_line, newline));
 5314
 5315            rows.push(row + rows_inserted as u32);
 5316        }
 5317
 5318        self.transact(window, cx, |editor, window, cx| {
 5319            editor.edit(edits, cx);
 5320
 5321            editor.change_selections(Default::default(), window, cx, |s| {
 5322                let mut index = 0;
 5323                s.move_cursors_with(&mut |map, _, _| {
 5324                    let row = rows[index];
 5325                    index += 1;
 5326
 5327                    let point = Point::new(row, 0);
 5328                    let boundary = map.next_line_boundary(point).1;
 5329                    let clipped = map.clip_point(boundary, Bias::Left);
 5330
 5331                    (clipped, SelectionGoal::None)
 5332                });
 5333            });
 5334
 5335            let mut indent_edits = Vec::new();
 5336            let multibuffer_snapshot = editor.buffer.read(cx).snapshot(cx);
 5337            for row in rows {
 5338                let indents = multibuffer_snapshot.suggested_indents(row..row + 1, cx);
 5339                for (row, indent) in indents {
 5340                    if indent.len == 0 {
 5341                        continue;
 5342                    }
 5343
 5344                    let text = match indent.kind {
 5345                        IndentKind::Space => " ".repeat(indent.len as usize),
 5346                        IndentKind::Tab => "\t".repeat(indent.len as usize),
 5347                    };
 5348                    let point = Point::new(row.0, 0);
 5349                    indent_edits.push((point..point, text));
 5350                }
 5351            }
 5352            editor.edit(indent_edits, cx);
 5353            if let Some(format) = editor.trigger_on_type_formatting("\n".to_owned(), window, cx) {
 5354                format.detach_and_log_err(cx);
 5355            }
 5356        });
 5357    }
 5358
 5359    pub fn newline_below(&mut self, _: &NewlineBelow, window: &mut Window, cx: &mut Context<Self>) {
 5360        self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
 5361
 5362        let mut buffer_edits: HashMap<EntityId, (Entity<Buffer>, Vec<Point>)> = HashMap::default();
 5363        let mut rows = Vec::new();
 5364        let mut rows_inserted = 0;
 5365
 5366        for selection in self.selections.all_adjusted(&self.display_snapshot(cx)) {
 5367            let cursor = selection.head();
 5368            let row = cursor.row;
 5369
 5370            let point = Point::new(row, 0);
 5371            let Some((buffer_handle, buffer_point, _)) =
 5372                self.buffer.read(cx).point_to_buffer_point(point, cx)
 5373            else {
 5374                continue;
 5375            };
 5376
 5377            buffer_edits
 5378                .entry(buffer_handle.entity_id())
 5379                .or_insert_with(|| (buffer_handle, Vec::new()))
 5380                .1
 5381                .push(buffer_point);
 5382
 5383            rows_inserted += 1;
 5384            rows.push(row + rows_inserted);
 5385        }
 5386
 5387        self.transact(window, cx, |editor, window, cx| {
 5388            for (_, (buffer_handle, points)) in &buffer_edits {
 5389                buffer_handle.update(cx, |buffer, cx| {
 5390                    let edits: Vec<_> = points
 5391                        .iter()
 5392                        .map(|point| {
 5393                            let target = Point::new(point.row + 1, 0);
 5394                            let start_of_line = buffer.point_to_offset(target).min(buffer.len());
 5395                            (start_of_line..start_of_line, "\n")
 5396                        })
 5397                        .collect();
 5398                    buffer.edit(edits, None, cx);
 5399                });
 5400            }
 5401
 5402            editor.change_selections(Default::default(), window, cx, |s| {
 5403                let mut index = 0;
 5404                s.move_cursors_with(&mut |map, _, _| {
 5405                    let row = rows[index];
 5406                    index += 1;
 5407
 5408                    let point = Point::new(row, 0);
 5409                    let boundary = map.next_line_boundary(point).1;
 5410                    let clipped = map.clip_point(boundary, Bias::Left);
 5411
 5412                    (clipped, SelectionGoal::None)
 5413                });
 5414            });
 5415
 5416            let mut indent_edits = Vec::new();
 5417            let multibuffer_snapshot = editor.buffer.read(cx).snapshot(cx);
 5418            for row in rows {
 5419                let indents = multibuffer_snapshot.suggested_indents(row..row + 1, cx);
 5420                for (row, indent) in indents {
 5421                    if indent.len == 0 {
 5422                        continue;
 5423                    }
 5424
 5425                    let text = match indent.kind {
 5426                        IndentKind::Space => " ".repeat(indent.len as usize),
 5427                        IndentKind::Tab => "\t".repeat(indent.len as usize),
 5428                    };
 5429                    let point = Point::new(row.0, 0);
 5430                    indent_edits.push((point..point, text));
 5431                }
 5432            }
 5433            editor.edit(indent_edits, cx);
 5434            if let Some(format) = editor.trigger_on_type_formatting("\n".to_owned(), window, cx) {
 5435                format.detach_and_log_err(cx);
 5436            }
 5437        });
 5438    }
 5439
 5440    pub fn insert(&mut self, text: &str, window: &mut Window, cx: &mut Context<Self>) {
 5441        let autoindent = text.is_empty().not().then(|| AutoindentMode::Block {
 5442            original_indent_columns: Vec::new(),
 5443        });
 5444        self.replace_selections(text, autoindent, window, cx, false);
 5445    }
 5446
 5447    /// Replaces the editor's selections with the provided `text`, applying the
 5448    /// given `autoindent_mode` (`None` will skip autoindentation).
 5449    ///
 5450    /// Early returns if the editor is in read-only mode, without applying any
 5451    /// edits.
 5452    fn replace_selections(
 5453        &mut self,
 5454        text: &str,
 5455        autoindent_mode: Option<AutoindentMode>,
 5456        window: &mut Window,
 5457        cx: &mut Context<Self>,
 5458        apply_linked_edits: bool,
 5459    ) {
 5460        if self.read_only(cx) {
 5461            return;
 5462        }
 5463
 5464        let text: Arc<str> = text.into();
 5465        self.transact(window, cx, |this, window, cx| {
 5466            let old_selections = this.selections.all_adjusted(&this.display_snapshot(cx));
 5467            let linked_edits = if apply_linked_edits {
 5468                this.linked_edits_for_selections(text.clone(), cx)
 5469            } else {
 5470                LinkedEdits::new()
 5471            };
 5472
 5473            let selection_anchors = this.buffer.update(cx, |buffer, cx| {
 5474                let anchors = {
 5475                    let snapshot = buffer.read(cx);
 5476                    old_selections
 5477                        .iter()
 5478                        .map(|s| {
 5479                            let anchor = snapshot.anchor_after(s.head());
 5480                            s.map(|_| anchor)
 5481                        })
 5482                        .collect::<Vec<_>>()
 5483                };
 5484                buffer.edit(
 5485                    old_selections
 5486                        .iter()
 5487                        .map(|s| (s.start..s.end, text.clone())),
 5488                    autoindent_mode,
 5489                    cx,
 5490                );
 5491                anchors
 5492            });
 5493
 5494            linked_edits.apply(cx);
 5495
 5496            this.change_selections(Default::default(), window, cx, |s| {
 5497                s.select_anchors(selection_anchors);
 5498            });
 5499
 5500            if apply_linked_edits {
 5501                refresh_linked_ranges(this, window, cx);
 5502            }
 5503
 5504            cx.notify();
 5505        });
 5506    }
 5507
 5508    /// Collects linked edits for the current selections, pairing each linked
 5509    /// range with `text`.
 5510    pub fn linked_edits_for_selections(&self, text: Arc<str>, cx: &App) -> LinkedEdits {
 5511        let mut linked_edits = LinkedEdits::new();
 5512        if !self.linked_edit_ranges.is_empty() {
 5513            for selection in self.selections.disjoint_anchors() {
 5514                let start = selection.start.text_anchor;
 5515                let end = selection.end.text_anchor;
 5516                linked_edits.push(self, start..end, text.clone(), cx);
 5517            }
 5518        }
 5519        linked_edits
 5520    }
 5521
 5522    /// Deletes the content covered by the current selections and applies
 5523    /// linked edits.
 5524    pub fn delete_selections_with_linked_edits(
 5525        &mut self,
 5526        window: &mut Window,
 5527        cx: &mut Context<Self>,
 5528    ) {
 5529        self.replace_selections("", None, window, cx, true);
 5530    }
 5531
 5532    #[cfg(any(test, feature = "test-support"))]
 5533    pub fn set_linked_edit_ranges_for_testing(
 5534        &mut self,
 5535        ranges: Vec<(Range<Point>, Vec<Range<Point>>)>,
 5536        cx: &mut Context<Self>,
 5537    ) -> Option<()> {
 5538        let Some((buffer, _)) = self
 5539            .buffer
 5540            .read(cx)
 5541            .text_anchor_for_position(self.selections.newest_anchor().start, cx)
 5542        else {
 5543            return None;
 5544        };
 5545        let buffer = buffer.read(cx);
 5546        let buffer_id = buffer.remote_id();
 5547        let mut linked_ranges = Vec::with_capacity(ranges.len());
 5548        for (base_range, linked_ranges_points) in ranges {
 5549            let base_anchor =
 5550                buffer.anchor_before(base_range.start)..buffer.anchor_after(base_range.end);
 5551            let linked_anchors = linked_ranges_points
 5552                .into_iter()
 5553                .map(|range| buffer.anchor_before(range.start)..buffer.anchor_after(range.end))
 5554                .collect();
 5555            linked_ranges.push((base_anchor, linked_anchors));
 5556        }
 5557        let mut map = HashMap::default();
 5558        map.insert(buffer_id, linked_ranges);
 5559        self.linked_edit_ranges = linked_editing_ranges::LinkedEditingRanges(map);
 5560        Some(())
 5561    }
 5562
 5563    fn trigger_completion_on_input(
 5564        &mut self,
 5565        text: &str,
 5566        trigger_in_words: bool,
 5567        window: &mut Window,
 5568        cx: &mut Context<Self>,
 5569    ) {
 5570        let completions_source = self
 5571            .context_menu
 5572            .borrow()
 5573            .as_ref()
 5574            .and_then(|menu| match menu {
 5575                CodeContextMenu::Completions(completions_menu) => Some(completions_menu.source),
 5576                CodeContextMenu::CodeActions(_) => None,
 5577            });
 5578
 5579        match completions_source {
 5580            Some(CompletionsMenuSource::Words { .. }) => {
 5581                self.open_or_update_completions_menu(
 5582                    Some(CompletionsMenuSource::Words {
 5583                        ignore_threshold: false,
 5584                    }),
 5585                    None,
 5586                    trigger_in_words,
 5587                    window,
 5588                    cx,
 5589                );
 5590            }
 5591            _ => self.open_or_update_completions_menu(
 5592                None,
 5593                Some(text.to_owned()).filter(|x| !x.is_empty()),
 5594                true,
 5595                window,
 5596                cx,
 5597            ),
 5598        }
 5599    }
 5600
 5601    /// If any empty selections is touching the start of its innermost containing autoclose
 5602    /// region, expand it to select the brackets.
 5603    fn select_autoclose_pair(&mut self, window: &mut Window, cx: &mut Context<Self>) {
 5604        let selections = self
 5605            .selections
 5606            .all::<MultiBufferOffset>(&self.display_snapshot(cx));
 5607        let buffer = self.buffer.read(cx).read(cx);
 5608        let new_selections = self
 5609            .selections_with_autoclose_regions(selections, &buffer)
 5610            .map(|(mut selection, region)| {
 5611                if !selection.is_empty() {
 5612                    return selection;
 5613                }
 5614
 5615                if let Some(region) = region {
 5616                    let mut range = region.range.to_offset(&buffer);
 5617                    if selection.start == range.start && range.start.0 >= region.pair.start.len() {
 5618                        range.start -= region.pair.start.len();
 5619                        if buffer.contains_str_at(range.start, &region.pair.start)
 5620                            && buffer.contains_str_at(range.end, &region.pair.end)
 5621                        {
 5622                            range.end += region.pair.end.len();
 5623                            selection.start = range.start;
 5624                            selection.end = range.end;
 5625
 5626                            return selection;
 5627                        }
 5628                    }
 5629                }
 5630
 5631                let always_treat_brackets_as_autoclosed = buffer
 5632                    .language_settings_at(selection.start, cx)
 5633                    .always_treat_brackets_as_autoclosed;
 5634
 5635                if !always_treat_brackets_as_autoclosed {
 5636                    return selection;
 5637                }
 5638
 5639                if let Some(scope) = buffer.language_scope_at(selection.start) {
 5640                    for (pair, enabled) in scope.brackets() {
 5641                        if !enabled || !pair.close {
 5642                            continue;
 5643                        }
 5644
 5645                        if buffer.contains_str_at(selection.start, &pair.end) {
 5646                            let pair_start_len = pair.start.len();
 5647                            if buffer.contains_str_at(
 5648                                selection.start.saturating_sub_usize(pair_start_len),
 5649                                &pair.start,
 5650                            ) {
 5651                                selection.start -= pair_start_len;
 5652                                selection.end += pair.end.len();
 5653
 5654                                return selection;
 5655                            }
 5656                        }
 5657                    }
 5658                }
 5659
 5660                selection
 5661            })
 5662            .collect();
 5663
 5664        drop(buffer);
 5665        self.change_selections(SelectionEffects::no_scroll(), window, cx, |selections| {
 5666            selections.select(new_selections)
 5667        });
 5668    }
 5669
 5670    /// Iterate the given selections, and for each one, find the smallest surrounding
 5671    /// autoclose region. This uses the ordering of the selections and the autoclose
 5672    /// regions to avoid repeated comparisons.
 5673    fn selections_with_autoclose_regions<'a, D: ToOffset + Clone>(
 5674        &'a self,
 5675        selections: impl IntoIterator<Item = Selection<D>>,
 5676        buffer: &'a MultiBufferSnapshot,
 5677    ) -> impl Iterator<Item = (Selection<D>, Option<&'a AutocloseRegion>)> {
 5678        let mut i = 0;
 5679        let mut regions = self.autoclose_regions.as_slice();
 5680        selections.into_iter().map(move |selection| {
 5681            let range = selection.start.to_offset(buffer)..selection.end.to_offset(buffer);
 5682
 5683            let mut enclosing = None;
 5684            while let Some(pair_state) = regions.get(i) {
 5685                if pair_state.range.end.to_offset(buffer) < range.start {
 5686                    regions = &regions[i + 1..];
 5687                    i = 0;
 5688                } else if pair_state.range.start.to_offset(buffer) > range.end {
 5689                    break;
 5690                } else {
 5691                    if pair_state.selection_id == selection.id {
 5692                        enclosing = Some(pair_state);
 5693                    }
 5694                    i += 1;
 5695                }
 5696            }
 5697
 5698            (selection, enclosing)
 5699        })
 5700    }
 5701
 5702    /// Remove any autoclose regions that no longer contain their selection or have invalid anchors in ranges.
 5703    fn invalidate_autoclose_regions(
 5704        &mut self,
 5705        mut selections: &[Selection<Anchor>],
 5706        buffer: &MultiBufferSnapshot,
 5707    ) {
 5708        self.autoclose_regions.retain(|state| {
 5709            if !state.range.start.is_valid(buffer) || !state.range.end.is_valid(buffer) {
 5710                return false;
 5711            }
 5712
 5713            let mut i = 0;
 5714            while let Some(selection) = selections.get(i) {
 5715                if selection.end.cmp(&state.range.start, buffer).is_lt() {
 5716                    selections = &selections[1..];
 5717                    continue;
 5718                }
 5719                if selection.start.cmp(&state.range.end, buffer).is_gt() {
 5720                    break;
 5721                }
 5722                if selection.id == state.selection_id {
 5723                    return true;
 5724                } else {
 5725                    i += 1;
 5726                }
 5727            }
 5728            false
 5729        });
 5730    }
 5731
 5732    fn completion_query(buffer: &MultiBufferSnapshot, position: impl ToOffset) -> Option<String> {
 5733        let offset = position.to_offset(buffer);
 5734        let (word_range, kind) =
 5735            buffer.surrounding_word(offset, Some(CharScopeContext::Completion));
 5736        if offset > word_range.start && kind == Some(CharKind::Word) {
 5737            Some(
 5738                buffer
 5739                    .text_for_range(word_range.start..offset)
 5740                    .collect::<String>(),
 5741            )
 5742        } else {
 5743            None
 5744        }
 5745    }
 5746
 5747    pub fn visible_excerpts(
 5748        &self,
 5749        lsp_related_only: bool,
 5750        cx: &mut Context<Editor>,
 5751    ) -> HashMap<ExcerptId, (Entity<Buffer>, clock::Global, Range<usize>)> {
 5752        let project = self.project().cloned();
 5753        let display_snapshot = self.display_map.update(cx, |map, cx| map.snapshot(cx));
 5754        let multi_buffer = self.buffer().read(cx);
 5755        let multi_buffer_snapshot = multi_buffer.snapshot(cx);
 5756        let multi_buffer_visible_start = self
 5757            .scroll_manager
 5758            .native_anchor(&display_snapshot, cx)
 5759            .anchor
 5760            .to_point(&multi_buffer_snapshot);
 5761        let multi_buffer_visible_end = multi_buffer_snapshot.clip_point(
 5762            multi_buffer_visible_start
 5763                + Point::new(self.visible_line_count().unwrap_or(0.).ceil() as u32, 0),
 5764            Bias::Left,
 5765        );
 5766        multi_buffer_snapshot
 5767            .range_to_buffer_ranges(multi_buffer_visible_start..=multi_buffer_visible_end)
 5768            .into_iter()
 5769            .filter(|(_, excerpt_visible_range, _)| !excerpt_visible_range.is_empty())
 5770            .filter_map(|(buffer, excerpt_visible_range, excerpt_id)| {
 5771                if !lsp_related_only {
 5772                    return Some((
 5773                        excerpt_id,
 5774                        (
 5775                            multi_buffer.buffer(buffer.remote_id()).unwrap(),
 5776                            buffer.version().clone(),
 5777                            excerpt_visible_range.start.0..excerpt_visible_range.end.0,
 5778                        ),
 5779                    ));
 5780                }
 5781
 5782                let project = project.as_ref()?.read(cx);
 5783                let buffer_file = project::File::from_dyn(buffer.file())?;
 5784                let buffer_worktree = project.worktree_for_id(buffer_file.worktree_id(cx), cx)?;
 5785                let worktree_entry = buffer_worktree
 5786                    .read(cx)
 5787                    .entry_for_id(buffer_file.project_entry_id()?)?;
 5788                if worktree_entry.is_ignored {
 5789                    None
 5790                } else {
 5791                    Some((
 5792                        excerpt_id,
 5793                        (
 5794                            multi_buffer.buffer(buffer.remote_id()).unwrap(),
 5795                            buffer.version().clone(),
 5796                            excerpt_visible_range.start.0..excerpt_visible_range.end.0,
 5797                        ),
 5798                    ))
 5799                }
 5800            })
 5801            .collect()
 5802    }
 5803
 5804    pub fn text_layout_details(&self, window: &mut Window, cx: &mut App) -> TextLayoutDetails {
 5805        TextLayoutDetails {
 5806            text_system: window.text_system().clone(),
 5807            editor_style: self.style.clone().unwrap(),
 5808            rem_size: window.rem_size(),
 5809            scroll_anchor: self.scroll_manager.shared_scroll_anchor(cx),
 5810            visible_rows: self.visible_line_count(),
 5811            vertical_scroll_margin: self.scroll_manager.vertical_scroll_margin,
 5812        }
 5813    }
 5814
 5815    fn trigger_on_type_formatting(
 5816        &self,
 5817        input: String,
 5818        window: &mut Window,
 5819        cx: &mut Context<Self>,
 5820    ) -> Option<Task<Result<()>>> {
 5821        if input.chars().count() != 1 {
 5822            return None;
 5823        }
 5824
 5825        let project = self.project()?;
 5826        let position = self.selections.newest_anchor().head();
 5827        let (buffer, buffer_position) = self
 5828            .buffer
 5829            .read(cx)
 5830            .text_anchor_for_position(position, cx)?;
 5831
 5832        let settings = language_settings::language_settings(
 5833            buffer
 5834                .read(cx)
 5835                .language_at(buffer_position)
 5836                .map(|l| l.name()),
 5837            buffer.read(cx).file(),
 5838            cx,
 5839        );
 5840        if !settings.use_on_type_format {
 5841            return None;
 5842        }
 5843
 5844        // OnTypeFormatting returns a list of edits, no need to pass them between Zed instances,
 5845        // hence we do LSP request & edit on host side only — add formats to host's history.
 5846        let push_to_lsp_host_history = true;
 5847        // If this is not the host, append its history with new edits.
 5848        let push_to_client_history = project.read(cx).is_via_collab();
 5849
 5850        let on_type_formatting = project.update(cx, |project, cx| {
 5851            project.on_type_format(
 5852                buffer.clone(),
 5853                buffer_position,
 5854                input,
 5855                push_to_lsp_host_history,
 5856                cx,
 5857            )
 5858        });
 5859        Some(cx.spawn_in(window, async move |editor, cx| {
 5860            if let Some(transaction) = on_type_formatting.await? {
 5861                if push_to_client_history {
 5862                    buffer.update(cx, |buffer, _| {
 5863                        buffer.push_transaction(transaction, Instant::now());
 5864                        buffer.finalize_last_transaction();
 5865                    });
 5866                }
 5867                editor.update(cx, |editor, cx| {
 5868                    editor.refresh_document_highlights(cx);
 5869                })?;
 5870            }
 5871            Ok(())
 5872        }))
 5873    }
 5874
 5875    pub fn show_word_completions(
 5876        &mut self,
 5877        _: &ShowWordCompletions,
 5878        window: &mut Window,
 5879        cx: &mut Context<Self>,
 5880    ) {
 5881        self.open_or_update_completions_menu(
 5882            Some(CompletionsMenuSource::Words {
 5883                ignore_threshold: true,
 5884            }),
 5885            None,
 5886            false,
 5887            window,
 5888            cx,
 5889        );
 5890    }
 5891
 5892    pub fn show_completions(
 5893        &mut self,
 5894        _: &ShowCompletions,
 5895        window: &mut Window,
 5896        cx: &mut Context<Self>,
 5897    ) {
 5898        self.open_or_update_completions_menu(None, None, false, window, cx);
 5899    }
 5900
 5901    fn open_or_update_completions_menu(
 5902        &mut self,
 5903        requested_source: Option<CompletionsMenuSource>,
 5904        trigger: Option<String>,
 5905        trigger_in_words: bool,
 5906        window: &mut Window,
 5907        cx: &mut Context<Self>,
 5908    ) {
 5909        if self.pending_rename.is_some() {
 5910            return;
 5911        }
 5912
 5913        let completions_source = self
 5914            .context_menu
 5915            .borrow()
 5916            .as_ref()
 5917            .and_then(|menu| match menu {
 5918                CodeContextMenu::Completions(completions_menu) => Some(completions_menu.source),
 5919                CodeContextMenu::CodeActions(_) => None,
 5920            });
 5921
 5922        let multibuffer_snapshot = self.buffer.read(cx).read(cx);
 5923
 5924        // Typically `start` == `end`, but with snippet tabstop choices the default choice is
 5925        // inserted and selected. To handle that case, the start of the selection is used so that
 5926        // the menu starts with all choices.
 5927        let position = self
 5928            .selections
 5929            .newest_anchor()
 5930            .start
 5931            .bias_right(&multibuffer_snapshot);
 5932        if position.diff_base_anchor.is_some() {
 5933            return;
 5934        }
 5935        let buffer_position = multibuffer_snapshot.anchor_before(position);
 5936        let Some(buffer) = buffer_position
 5937            .text_anchor
 5938            .buffer_id
 5939            .and_then(|buffer_id| self.buffer.read(cx).buffer(buffer_id))
 5940        else {
 5941            return;
 5942        };
 5943        let buffer_snapshot = buffer.read(cx).snapshot();
 5944
 5945        let menu_is_open = matches!(
 5946            self.context_menu.borrow().as_ref(),
 5947            Some(CodeContextMenu::Completions(_))
 5948        );
 5949
 5950        let language = buffer_snapshot
 5951            .language_at(buffer_position.text_anchor)
 5952            .map(|language| language.name());
 5953
 5954        let language_settings = language_settings(language.clone(), buffer_snapshot.file(), cx);
 5955        let completion_settings = language_settings.completions.clone();
 5956
 5957        let show_completions_on_input = self
 5958            .show_completions_on_input_override
 5959            .unwrap_or(language_settings.show_completions_on_input);
 5960        if !menu_is_open && trigger.is_some() && !show_completions_on_input {
 5961            return;
 5962        }
 5963
 5964        let query: Option<Arc<String>> =
 5965            Self::completion_query(&multibuffer_snapshot, buffer_position)
 5966                .map(|query| query.into());
 5967
 5968        drop(multibuffer_snapshot);
 5969
 5970        // Hide the current completions menu when query is empty. Without this, cached
 5971        // completions from before the trigger char may be reused (#32774).
 5972        if query.is_none() && menu_is_open {
 5973            self.hide_context_menu(window, cx);
 5974        }
 5975
 5976        let mut ignore_word_threshold = false;
 5977        let provider = match requested_source {
 5978            Some(CompletionsMenuSource::Normal) | None => self.completion_provider.clone(),
 5979            Some(CompletionsMenuSource::Words { ignore_threshold }) => {
 5980                ignore_word_threshold = ignore_threshold;
 5981                None
 5982            }
 5983            Some(CompletionsMenuSource::SnippetChoices)
 5984            | Some(CompletionsMenuSource::SnippetsOnly) => {
 5985                log::error!("bug: SnippetChoices requested_source is not handled");
 5986                None
 5987            }
 5988        };
 5989
 5990        let sort_completions = provider
 5991            .as_ref()
 5992            .is_some_and(|provider| provider.sort_completions());
 5993
 5994        let filter_completions = provider
 5995            .as_ref()
 5996            .is_none_or(|provider| provider.filter_completions());
 5997
 5998        let was_snippets_only = matches!(
 5999            completions_source,
 6000            Some(CompletionsMenuSource::SnippetsOnly)
 6001        );
 6002
 6003        if let Some(CodeContextMenu::Completions(menu)) = self.context_menu.borrow_mut().as_mut() {
 6004            if filter_completions {
 6005                menu.filter(
 6006                    query.clone().unwrap_or_default(),
 6007                    buffer_position.text_anchor,
 6008                    &buffer,
 6009                    provider.clone(),
 6010                    window,
 6011                    cx,
 6012                );
 6013            }
 6014            // When `is_incomplete` is false, no need to re-query completions when the current query
 6015            // is a suffix of the initial query.
 6016            let was_complete = !menu.is_incomplete;
 6017            if was_complete && !was_snippets_only {
 6018                // If the new query is a suffix of the old query (typing more characters) and
 6019                // the previous result was complete, the existing completions can be filtered.
 6020                //
 6021                // Note that snippet completions are always complete.
 6022                let query_matches = match (&menu.initial_query, &query) {
 6023                    (Some(initial_query), Some(query)) => query.starts_with(initial_query.as_ref()),
 6024                    (None, _) => true,
 6025                    _ => false,
 6026                };
 6027                if query_matches {
 6028                    let position_matches = if menu.initial_position == position {
 6029                        true
 6030                    } else {
 6031                        let snapshot = self.buffer.read(cx).read(cx);
 6032                        menu.initial_position.to_offset(&snapshot) == position.to_offset(&snapshot)
 6033                    };
 6034                    if position_matches {
 6035                        return;
 6036                    }
 6037                }
 6038            }
 6039        };
 6040
 6041        let Anchor {
 6042            excerpt_id: buffer_excerpt_id,
 6043            text_anchor: buffer_position,
 6044            ..
 6045        } = buffer_position;
 6046
 6047        let (word_replace_range, word_to_exclude) = if let (word_range, Some(CharKind::Word)) =
 6048            buffer_snapshot.surrounding_word(buffer_position, None)
 6049        {
 6050            let word_to_exclude = buffer_snapshot
 6051                .text_for_range(word_range.clone())
 6052                .collect::<String>();
 6053            (
 6054                buffer_snapshot.anchor_before(word_range.start)
 6055                    ..buffer_snapshot.anchor_after(buffer_position),
 6056                Some(word_to_exclude),
 6057            )
 6058        } else {
 6059            (buffer_position..buffer_position, None)
 6060        };
 6061
 6062        let show_completion_documentation = buffer_snapshot
 6063            .settings_at(buffer_position, cx)
 6064            .show_completion_documentation;
 6065
 6066        // The document can be large, so stay in reasonable bounds when searching for words,
 6067        // otherwise completion pop-up might be slow to appear.
 6068        const WORD_LOOKUP_ROWS: u32 = 5_000;
 6069        let buffer_row = text::ToPoint::to_point(&buffer_position, &buffer_snapshot).row;
 6070        let min_word_search = buffer_snapshot.clip_point(
 6071            Point::new(buffer_row.saturating_sub(WORD_LOOKUP_ROWS), 0),
 6072            Bias::Left,
 6073        );
 6074        let max_word_search = buffer_snapshot.clip_point(
 6075            Point::new(buffer_row + WORD_LOOKUP_ROWS, 0).min(buffer_snapshot.max_point()),
 6076            Bias::Right,
 6077        );
 6078        let word_search_range = buffer_snapshot.point_to_offset(min_word_search)
 6079            ..buffer_snapshot.point_to_offset(max_word_search);
 6080
 6081        let skip_digits = query
 6082            .as_ref()
 6083            .is_none_or(|query| !query.chars().any(|c| c.is_digit(10)));
 6084
 6085        let load_provider_completions = provider.as_ref().is_some_and(|provider| {
 6086            trigger.as_ref().is_none_or(|trigger| {
 6087                provider.is_completion_trigger(
 6088                    &buffer,
 6089                    position.text_anchor,
 6090                    trigger,
 6091                    trigger_in_words,
 6092                    cx,
 6093                )
 6094            })
 6095        });
 6096
 6097        let provider_responses = if let Some(provider) = &provider
 6098            && load_provider_completions
 6099        {
 6100            let trigger_character =
 6101                trigger.filter(|trigger| buffer.read(cx).completion_triggers().contains(trigger));
 6102            let completion_context = CompletionContext {
 6103                trigger_kind: match &trigger_character {
 6104                    Some(_) => CompletionTriggerKind::TRIGGER_CHARACTER,
 6105                    None => CompletionTriggerKind::INVOKED,
 6106                },
 6107                trigger_character,
 6108            };
 6109
 6110            provider.completions(
 6111                buffer_excerpt_id,
 6112                &buffer,
 6113                buffer_position,
 6114                completion_context,
 6115                window,
 6116                cx,
 6117            )
 6118        } else {
 6119            Task::ready(Ok(Vec::new()))
 6120        };
 6121
 6122        let load_word_completions = if !self.word_completions_enabled {
 6123            false
 6124        } else if requested_source
 6125            == Some(CompletionsMenuSource::Words {
 6126                ignore_threshold: true,
 6127            })
 6128        {
 6129            true
 6130        } else {
 6131            load_provider_completions
 6132                && completion_settings.words != WordsCompletionMode::Disabled
 6133                && (ignore_word_threshold || {
 6134                    let words_min_length = completion_settings.words_min_length;
 6135                    // check whether word has at least `words_min_length` characters
 6136                    let query_chars = query.iter().flat_map(|q| q.chars());
 6137                    query_chars.take(words_min_length).count() == words_min_length
 6138                })
 6139        };
 6140
 6141        let mut words = if load_word_completions {
 6142            cx.background_spawn({
 6143                let buffer_snapshot = buffer_snapshot.clone();
 6144                async move {
 6145                    buffer_snapshot.words_in_range(WordsQuery {
 6146                        fuzzy_contents: None,
 6147                        range: word_search_range,
 6148                        skip_digits,
 6149                    })
 6150                }
 6151            })
 6152        } else {
 6153            Task::ready(BTreeMap::default())
 6154        };
 6155
 6156        let snippets = if let Some(provider) = &provider
 6157            && provider.show_snippets()
 6158            && let Some(project) = self.project()
 6159        {
 6160            let char_classifier = buffer_snapshot
 6161                .char_classifier_at(buffer_position)
 6162                .scope_context(Some(CharScopeContext::Completion));
 6163            project.update(cx, |project, cx| {
 6164                snippet_completions(project, &buffer, buffer_position, char_classifier, cx)
 6165            })
 6166        } else {
 6167            Task::ready(Ok(CompletionResponse {
 6168                completions: Vec::new(),
 6169                display_options: Default::default(),
 6170                is_incomplete: false,
 6171            }))
 6172        };
 6173
 6174        let snippet_sort_order = EditorSettings::get_global(cx).snippet_sort_order;
 6175
 6176        let id = post_inc(&mut self.next_completion_id);
 6177        let task = cx.spawn_in(window, async move |editor, cx| {
 6178            let Ok(()) = editor.update(cx, |this, _| {
 6179                this.completion_tasks.retain(|(task_id, _)| *task_id >= id);
 6180            }) else {
 6181                return;
 6182            };
 6183
 6184            // TODO: Ideally completions from different sources would be selectively re-queried, so
 6185            // that having one source with `is_incomplete: true` doesn't cause all to be re-queried.
 6186            let mut completions = Vec::new();
 6187            let mut is_incomplete = false;
 6188            let mut display_options: Option<CompletionDisplayOptions> = None;
 6189            if let Some(provider_responses) = provider_responses.await.log_err()
 6190                && !provider_responses.is_empty()
 6191            {
 6192                for response in provider_responses {
 6193                    completions.extend(response.completions);
 6194                    is_incomplete = is_incomplete || response.is_incomplete;
 6195                    match display_options.as_mut() {
 6196                        None => {
 6197                            display_options = Some(response.display_options);
 6198                        }
 6199                        Some(options) => options.merge(&response.display_options),
 6200                    }
 6201                }
 6202                if completion_settings.words == WordsCompletionMode::Fallback {
 6203                    words = Task::ready(BTreeMap::default());
 6204                }
 6205            }
 6206            let display_options = display_options.unwrap_or_default();
 6207
 6208            let mut words = words.await;
 6209            if let Some(word_to_exclude) = &word_to_exclude {
 6210                words.remove(word_to_exclude);
 6211            }
 6212            for lsp_completion in &completions {
 6213                words.remove(&lsp_completion.new_text);
 6214            }
 6215            completions.extend(words.into_iter().map(|(word, word_range)| Completion {
 6216                replace_range: word_replace_range.clone(),
 6217                new_text: word.clone(),
 6218                label: CodeLabel::plain(word, None),
 6219                match_start: None,
 6220                snippet_deduplication_key: None,
 6221                icon_path: None,
 6222                documentation: None,
 6223                source: CompletionSource::BufferWord {
 6224                    word_range,
 6225                    resolved: false,
 6226                },
 6227                insert_text_mode: Some(InsertTextMode::AS_IS),
 6228                confirm: None,
 6229            }));
 6230
 6231            completions.extend(
 6232                snippets
 6233                    .await
 6234                    .into_iter()
 6235                    .flat_map(|response| response.completions),
 6236            );
 6237
 6238            let menu = if completions.is_empty() {
 6239                None
 6240            } else {
 6241                let Ok((mut menu, matches_task)) = editor.update(cx, |editor, cx| {
 6242                    let languages = editor
 6243                        .workspace
 6244                        .as_ref()
 6245                        .and_then(|(workspace, _)| workspace.upgrade())
 6246                        .map(|workspace| workspace.read(cx).app_state().languages.clone());
 6247                    let menu = CompletionsMenu::new(
 6248                        id,
 6249                        requested_source.unwrap_or(if load_provider_completions {
 6250                            CompletionsMenuSource::Normal
 6251                        } else {
 6252                            CompletionsMenuSource::SnippetsOnly
 6253                        }),
 6254                        sort_completions,
 6255                        show_completion_documentation,
 6256                        position,
 6257                        query.clone(),
 6258                        is_incomplete,
 6259                        buffer.clone(),
 6260                        completions.into(),
 6261                        editor
 6262                            .context_menu()
 6263                            .borrow_mut()
 6264                            .as_ref()
 6265                            .map(|menu| menu.primary_scroll_handle()),
 6266                        display_options,
 6267                        snippet_sort_order,
 6268                        languages,
 6269                        language,
 6270                        cx,
 6271                    );
 6272
 6273                    let query = if filter_completions { query } else { None };
 6274                    let matches_task = menu.do_async_filtering(
 6275                        query.unwrap_or_default(),
 6276                        buffer_position,
 6277                        &buffer,
 6278                        cx,
 6279                    );
 6280                    (menu, matches_task)
 6281                }) else {
 6282                    return;
 6283                };
 6284
 6285                let matches = matches_task.await;
 6286
 6287                let Ok(()) = editor.update_in(cx, |editor, window, cx| {
 6288                    // Newer menu already set, so exit.
 6289                    if let Some(CodeContextMenu::Completions(prev_menu)) =
 6290                        editor.context_menu.borrow().as_ref()
 6291                        && prev_menu.id > id
 6292                    {
 6293                        return;
 6294                    };
 6295
 6296                    // Only valid to take prev_menu because either the new menu is immediately set
 6297                    // below, or the menu is hidden.
 6298                    if let Some(CodeContextMenu::Completions(prev_menu)) =
 6299                        editor.context_menu.borrow_mut().take()
 6300                    {
 6301                        let position_matches =
 6302                            if prev_menu.initial_position == menu.initial_position {
 6303                                true
 6304                            } else {
 6305                                let snapshot = editor.buffer.read(cx).read(cx);
 6306                                prev_menu.initial_position.to_offset(&snapshot)
 6307                                    == menu.initial_position.to_offset(&snapshot)
 6308                            };
 6309                        if position_matches {
 6310                            // Preserve markdown cache before `set_filter_results` because it will
 6311                            // try to populate the documentation cache.
 6312                            menu.preserve_markdown_cache(prev_menu);
 6313                        }
 6314                    };
 6315
 6316                    menu.set_filter_results(matches, provider, window, cx);
 6317                }) else {
 6318                    return;
 6319                };
 6320
 6321                menu.visible().then_some(menu)
 6322            };
 6323
 6324            editor
 6325                .update_in(cx, |editor, window, cx| {
 6326                    if editor.focus_handle.is_focused(window)
 6327                        && let Some(menu) = menu
 6328                    {
 6329                        *editor.context_menu.borrow_mut() =
 6330                            Some(CodeContextMenu::Completions(menu));
 6331
 6332                        crate::hover_popover::hide_hover(editor, cx);
 6333                        if editor.show_edit_predictions_in_menu() {
 6334                            editor.update_visible_edit_prediction(window, cx);
 6335                        } else {
 6336                            editor
 6337                                .discard_edit_prediction(EditPredictionDiscardReason::Ignored, cx);
 6338                        }
 6339
 6340                        cx.notify();
 6341                        return;
 6342                    }
 6343
 6344                    if editor.completion_tasks.len() <= 1 {
 6345                        // If there are no more completion tasks and the last menu was empty, we should hide it.
 6346                        let was_hidden = editor.hide_context_menu(window, cx).is_none();
 6347                        // If it was already hidden and we don't show edit predictions in the menu,
 6348                        // we should also show the edit prediction when available.
 6349                        if was_hidden && editor.show_edit_predictions_in_menu() {
 6350                            editor.update_visible_edit_prediction(window, cx);
 6351                        }
 6352                    }
 6353                })
 6354                .ok();
 6355        });
 6356
 6357        self.completion_tasks.push((id, task));
 6358    }
 6359
 6360    #[cfg(any(test, feature = "test-support"))]
 6361    pub fn current_completions(&self) -> Option<Vec<project::Completion>> {
 6362        let menu = self.context_menu.borrow();
 6363        if let CodeContextMenu::Completions(menu) = menu.as_ref()? {
 6364            let completions = menu.completions.borrow();
 6365            Some(completions.to_vec())
 6366        } else {
 6367            None
 6368        }
 6369    }
 6370
 6371    pub fn with_completions_menu_matching_id<R>(
 6372        &self,
 6373        id: CompletionId,
 6374        f: impl FnOnce(Option<&mut CompletionsMenu>) -> R,
 6375    ) -> R {
 6376        let mut context_menu = self.context_menu.borrow_mut();
 6377        let Some(CodeContextMenu::Completions(completions_menu)) = &mut *context_menu else {
 6378            return f(None);
 6379        };
 6380        if completions_menu.id != id {
 6381            return f(None);
 6382        }
 6383        f(Some(completions_menu))
 6384    }
 6385
 6386    pub fn confirm_completion(
 6387        &mut self,
 6388        action: &ConfirmCompletion,
 6389        window: &mut Window,
 6390        cx: &mut Context<Self>,
 6391    ) -> Option<Task<Result<()>>> {
 6392        self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
 6393        self.do_completion(action.item_ix, CompletionIntent::Complete, window, cx)
 6394    }
 6395
 6396    pub fn confirm_completion_insert(
 6397        &mut self,
 6398        _: &ConfirmCompletionInsert,
 6399        window: &mut Window,
 6400        cx: &mut Context<Self>,
 6401    ) -> Option<Task<Result<()>>> {
 6402        self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
 6403        self.do_completion(None, CompletionIntent::CompleteWithInsert, window, cx)
 6404    }
 6405
 6406    pub fn confirm_completion_replace(
 6407        &mut self,
 6408        _: &ConfirmCompletionReplace,
 6409        window: &mut Window,
 6410        cx: &mut Context<Self>,
 6411    ) -> Option<Task<Result<()>>> {
 6412        self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
 6413        self.do_completion(None, CompletionIntent::CompleteWithReplace, window, cx)
 6414    }
 6415
 6416    pub fn compose_completion(
 6417        &mut self,
 6418        action: &ComposeCompletion,
 6419        window: &mut Window,
 6420        cx: &mut Context<Self>,
 6421    ) -> Option<Task<Result<()>>> {
 6422        self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
 6423        self.do_completion(action.item_ix, CompletionIntent::Compose, window, cx)
 6424    }
 6425
 6426    fn do_completion(
 6427        &mut self,
 6428        item_ix: Option<usize>,
 6429        intent: CompletionIntent,
 6430        window: &mut Window,
 6431        cx: &mut Context<Editor>,
 6432    ) -> Option<Task<Result<()>>> {
 6433        use language::ToOffset as _;
 6434
 6435        let CodeContextMenu::Completions(completions_menu) = self.hide_context_menu(window, cx)?
 6436        else {
 6437            return None;
 6438        };
 6439
 6440        let candidate_id = {
 6441            let entries = completions_menu.entries.borrow();
 6442            let mat = entries.get(item_ix.unwrap_or(completions_menu.selected_item))?;
 6443            if self.show_edit_predictions_in_menu() {
 6444                self.discard_edit_prediction(EditPredictionDiscardReason::Rejected, cx);
 6445            }
 6446            mat.candidate_id
 6447        };
 6448
 6449        let completion = completions_menu
 6450            .completions
 6451            .borrow()
 6452            .get(candidate_id)?
 6453            .clone();
 6454        cx.stop_propagation();
 6455
 6456        let buffer_handle = completions_menu.buffer.clone();
 6457
 6458        let CompletionEdit {
 6459            new_text,
 6460            snippet,
 6461            replace_range,
 6462        } = process_completion_for_edit(
 6463            &completion,
 6464            intent,
 6465            &buffer_handle,
 6466            &completions_menu.initial_position.text_anchor,
 6467            cx,
 6468        );
 6469
 6470        let buffer = buffer_handle.read(cx);
 6471        let snapshot = self.buffer.read(cx).snapshot(cx);
 6472        let newest_anchor = self.selections.newest_anchor();
 6473        let replace_range_multibuffer = {
 6474            let mut excerpt = snapshot.excerpt_containing(newest_anchor.range()).unwrap();
 6475            excerpt.map_range_from_buffer(replace_range.clone())
 6476        };
 6477        if snapshot.buffer_id_for_anchor(newest_anchor.head()) != Some(buffer.remote_id()) {
 6478            return None;
 6479        }
 6480
 6481        let old_text = buffer
 6482            .text_for_range(replace_range.clone())
 6483            .collect::<String>();
 6484        let lookbehind = newest_anchor
 6485            .start
 6486            .text_anchor
 6487            .to_offset(buffer)
 6488            .saturating_sub(replace_range.start.0);
 6489        let lookahead = replace_range
 6490            .end
 6491            .0
 6492            .saturating_sub(newest_anchor.end.text_anchor.to_offset(buffer));
 6493        let prefix = &old_text[..old_text.len().saturating_sub(lookahead)];
 6494        let suffix = &old_text[lookbehind.min(old_text.len())..];
 6495
 6496        let selections = self
 6497            .selections
 6498            .all::<MultiBufferOffset>(&self.display_snapshot(cx));
 6499        let mut ranges = Vec::new();
 6500        let mut linked_edits = LinkedEdits::new();
 6501
 6502        let text: Arc<str> = new_text.clone().into();
 6503        for selection in &selections {
 6504            let range = if selection.id == newest_anchor.id {
 6505                replace_range_multibuffer.clone()
 6506            } else {
 6507                let mut range = selection.range();
 6508
 6509                // if prefix is present, don't duplicate it
 6510                if snapshot.contains_str_at(range.start.saturating_sub_usize(lookbehind), prefix) {
 6511                    range.start = range.start.saturating_sub_usize(lookbehind);
 6512
 6513                    // if suffix is also present, mimic the newest cursor and replace it
 6514                    if selection.id != newest_anchor.id
 6515                        && snapshot.contains_str_at(range.end, suffix)
 6516                    {
 6517                        range.end += lookahead;
 6518                    }
 6519                }
 6520                range
 6521            };
 6522
 6523            ranges.push(range.clone());
 6524
 6525            if !self.linked_edit_ranges.is_empty() {
 6526                let start_anchor = snapshot.anchor_before(range.start);
 6527                let end_anchor = snapshot.anchor_after(range.end);
 6528                let anchor_range = start_anchor.text_anchor..end_anchor.text_anchor;
 6529                linked_edits.push(&self, anchor_range, text.clone(), cx);
 6530            }
 6531        }
 6532
 6533        let common_prefix_len = old_text
 6534            .chars()
 6535            .zip(new_text.chars())
 6536            .take_while(|(a, b)| a == b)
 6537            .map(|(a, _)| a.len_utf8())
 6538            .sum::<usize>();
 6539
 6540        cx.emit(EditorEvent::InputHandled {
 6541            utf16_range_to_replace: None,
 6542            text: new_text[common_prefix_len..].into(),
 6543        });
 6544
 6545        self.transact(window, cx, |editor, window, cx| {
 6546            if let Some(mut snippet) = snippet {
 6547                snippet.text = new_text.to_string();
 6548                editor
 6549                    .insert_snippet(&ranges, snippet, window, cx)
 6550                    .log_err();
 6551            } else {
 6552                editor.buffer.update(cx, |multi_buffer, cx| {
 6553                    let auto_indent = match completion.insert_text_mode {
 6554                        Some(InsertTextMode::AS_IS) => None,
 6555                        _ => editor.autoindent_mode.clone(),
 6556                    };
 6557                    let edits = ranges.into_iter().map(|range| (range, new_text.as_str()));
 6558                    multi_buffer.edit(edits, auto_indent, cx);
 6559                });
 6560            }
 6561            linked_edits.apply(cx);
 6562            editor.refresh_edit_prediction(true, false, window, cx);
 6563        });
 6564        self.invalidate_autoclose_regions(&self.selections.disjoint_anchors_arc(), &snapshot);
 6565
 6566        let show_new_completions_on_confirm = completion
 6567            .confirm
 6568            .as_ref()
 6569            .is_some_and(|confirm| confirm(intent, window, cx));
 6570        if show_new_completions_on_confirm {
 6571            self.open_or_update_completions_menu(None, None, false, window, cx);
 6572        }
 6573
 6574        let provider = self.completion_provider.as_ref()?;
 6575
 6576        let lsp_store = self.project().map(|project| project.read(cx).lsp_store());
 6577        let command = lsp_store.as_ref().and_then(|lsp_store| {
 6578            let CompletionSource::Lsp {
 6579                lsp_completion,
 6580                server_id,
 6581                ..
 6582            } = &completion.source
 6583            else {
 6584                return None;
 6585            };
 6586            let lsp_command = lsp_completion.command.as_ref()?;
 6587            let available_commands = lsp_store
 6588                .read(cx)
 6589                .lsp_server_capabilities
 6590                .get(server_id)
 6591                .and_then(|server_capabilities| {
 6592                    server_capabilities
 6593                        .execute_command_provider
 6594                        .as_ref()
 6595                        .map(|options| options.commands.as_slice())
 6596                })?;
 6597            if available_commands.contains(&lsp_command.command) {
 6598                Some(CodeAction {
 6599                    server_id: *server_id,
 6600                    range: language::Anchor::MIN..language::Anchor::MIN,
 6601                    lsp_action: LspAction::Command(lsp_command.clone()),
 6602                    resolved: false,
 6603                })
 6604            } else {
 6605                None
 6606            }
 6607        });
 6608
 6609        drop(completion);
 6610        let apply_edits = provider.apply_additional_edits_for_completion(
 6611            buffer_handle.clone(),
 6612            completions_menu.completions.clone(),
 6613            candidate_id,
 6614            true,
 6615            cx,
 6616        );
 6617
 6618        let editor_settings = EditorSettings::get_global(cx);
 6619        if editor_settings.show_signature_help_after_edits || editor_settings.auto_signature_help {
 6620            // After the code completion is finished, users often want to know what signatures are needed.
 6621            // so we should automatically call signature_help
 6622            self.show_signature_help(&ShowSignatureHelp, window, cx);
 6623        }
 6624
 6625        Some(cx.spawn_in(window, async move |editor, cx| {
 6626            apply_edits.await?;
 6627
 6628            if let Some((lsp_store, command)) = lsp_store.zip(command) {
 6629                let title = command.lsp_action.title().to_owned();
 6630                let project_transaction = lsp_store
 6631                    .update(cx, |lsp_store, cx| {
 6632                        lsp_store.apply_code_action(buffer_handle, command, false, cx)
 6633                    })
 6634                    .await
 6635                    .context("applying post-completion command")?;
 6636                if let Some(workspace) = editor.read_with(cx, |editor, _| editor.workspace())? {
 6637                    Self::open_project_transaction(
 6638                        &editor,
 6639                        workspace.downgrade(),
 6640                        project_transaction,
 6641                        title,
 6642                        cx,
 6643                    )
 6644                    .await?;
 6645                }
 6646            }
 6647
 6648            Ok(())
 6649        }))
 6650    }
 6651
 6652    pub fn toggle_code_actions(
 6653        &mut self,
 6654        action: &ToggleCodeActions,
 6655        window: &mut Window,
 6656        cx: &mut Context<Self>,
 6657    ) {
 6658        let quick_launch = action.quick_launch;
 6659        let mut context_menu = self.context_menu.borrow_mut();
 6660        if let Some(CodeContextMenu::CodeActions(code_actions)) = context_menu.as_ref() {
 6661            if code_actions.deployed_from == action.deployed_from {
 6662                // Toggle if we're selecting the same one
 6663                *context_menu = None;
 6664                cx.notify();
 6665                return;
 6666            } else {
 6667                // Otherwise, clear it and start a new one
 6668                *context_menu = None;
 6669                cx.notify();
 6670            }
 6671        }
 6672        drop(context_menu);
 6673        let snapshot = self.snapshot(window, cx);
 6674        let deployed_from = action.deployed_from.clone();
 6675        let action = action.clone();
 6676        self.completion_tasks.clear();
 6677        self.discard_edit_prediction(EditPredictionDiscardReason::Ignored, cx);
 6678
 6679        let multibuffer_point = match &action.deployed_from {
 6680            Some(CodeActionSource::Indicator(row)) | Some(CodeActionSource::RunMenu(row)) => {
 6681                DisplayPoint::new(*row, 0).to_point(&snapshot)
 6682            }
 6683            _ => self
 6684                .selections
 6685                .newest::<Point>(&snapshot.display_snapshot)
 6686                .head(),
 6687        };
 6688        let Some((buffer, buffer_row)) = snapshot
 6689            .buffer_snapshot()
 6690            .buffer_line_for_row(MultiBufferRow(multibuffer_point.row))
 6691            .and_then(|(buffer_snapshot, range)| {
 6692                self.buffer()
 6693                    .read(cx)
 6694                    .buffer(buffer_snapshot.remote_id())
 6695                    .map(|buffer| (buffer, range.start.row))
 6696            })
 6697        else {
 6698            return;
 6699        };
 6700        let buffer_id = buffer.read(cx).remote_id();
 6701        let tasks = self
 6702            .tasks
 6703            .get(&(buffer_id, buffer_row))
 6704            .map(|t| Arc::new(t.to_owned()));
 6705
 6706        if !self.focus_handle.is_focused(window) {
 6707            return;
 6708        }
 6709        let project = self.project.clone();
 6710
 6711        let code_actions_task = match deployed_from {
 6712            Some(CodeActionSource::RunMenu(_)) => Task::ready(None),
 6713            _ => self.code_actions(buffer_row, window, cx),
 6714        };
 6715
 6716        let runnable_task = match deployed_from {
 6717            Some(CodeActionSource::Indicator(_)) => Task::ready(Ok(Default::default())),
 6718            _ => {
 6719                let mut task_context_task = Task::ready(None);
 6720                if let Some(tasks) = &tasks
 6721                    && let Some(project) = project
 6722                {
 6723                    task_context_task =
 6724                        Self::build_tasks_context(&project, &buffer, buffer_row, tasks, cx);
 6725                }
 6726
 6727                cx.spawn_in(window, {
 6728                    let buffer = buffer.clone();
 6729                    async move |editor, cx| {
 6730                        let task_context = task_context_task.await;
 6731
 6732                        let resolved_tasks =
 6733                            tasks
 6734                                .zip(task_context.clone())
 6735                                .map(|(tasks, task_context)| ResolvedTasks {
 6736                                    templates: tasks.resolve(&task_context).collect(),
 6737                                    position: snapshot.buffer_snapshot().anchor_before(Point::new(
 6738                                        multibuffer_point.row,
 6739                                        tasks.column,
 6740                                    )),
 6741                                });
 6742                        let debug_scenarios = editor
 6743                            .update(cx, |editor, cx| {
 6744                                editor.debug_scenarios(&resolved_tasks, &buffer, cx)
 6745                            })?
 6746                            .await;
 6747                        anyhow::Ok((resolved_tasks, debug_scenarios, task_context))
 6748                    }
 6749                })
 6750            }
 6751        };
 6752
 6753        cx.spawn_in(window, async move |editor, cx| {
 6754            let (resolved_tasks, debug_scenarios, task_context) = runnable_task.await?;
 6755            let code_actions = code_actions_task.await;
 6756            let spawn_straight_away = quick_launch
 6757                && resolved_tasks
 6758                    .as_ref()
 6759                    .is_some_and(|tasks| tasks.templates.len() == 1)
 6760                && code_actions
 6761                    .as_ref()
 6762                    .is_none_or(|actions| actions.is_empty())
 6763                && debug_scenarios.is_empty();
 6764
 6765            editor.update_in(cx, |editor, window, cx| {
 6766                crate::hover_popover::hide_hover(editor, cx);
 6767                let actions = CodeActionContents::new(
 6768                    resolved_tasks,
 6769                    code_actions,
 6770                    debug_scenarios,
 6771                    task_context.unwrap_or_default(),
 6772                );
 6773
 6774                // Don't show the menu if there are no actions available
 6775                if actions.is_empty() {
 6776                    cx.notify();
 6777                    return Task::ready(Ok(()));
 6778                }
 6779
 6780                *editor.context_menu.borrow_mut() =
 6781                    Some(CodeContextMenu::CodeActions(CodeActionsMenu {
 6782                        buffer,
 6783                        actions,
 6784                        selected_item: Default::default(),
 6785                        scroll_handle: UniformListScrollHandle::default(),
 6786                        deployed_from,
 6787                    }));
 6788                cx.notify();
 6789                if spawn_straight_away
 6790                    && let Some(task) = editor.confirm_code_action(
 6791                        &ConfirmCodeAction { item_ix: Some(0) },
 6792                        window,
 6793                        cx,
 6794                    )
 6795                {
 6796                    return task;
 6797                }
 6798
 6799                Task::ready(Ok(()))
 6800            })
 6801        })
 6802        .detach_and_log_err(cx);
 6803    }
 6804
 6805    fn debug_scenarios(
 6806        &mut self,
 6807        resolved_tasks: &Option<ResolvedTasks>,
 6808        buffer: &Entity<Buffer>,
 6809        cx: &mut App,
 6810    ) -> Task<Vec<task::DebugScenario>> {
 6811        maybe!({
 6812            let project = self.project()?;
 6813            let dap_store = project.read(cx).dap_store();
 6814            let mut scenarios = vec![];
 6815            let resolved_tasks = resolved_tasks.as_ref()?;
 6816            let buffer = buffer.read(cx);
 6817            let language = buffer.language()?;
 6818            let file = buffer.file();
 6819            let debug_adapter = language_settings(language.name().into(), file, cx)
 6820                .debuggers
 6821                .first()
 6822                .map(SharedString::from)
 6823                .or_else(|| language.config().debuggers.first().map(SharedString::from))?;
 6824
 6825            dap_store.update(cx, |dap_store, cx| {
 6826                for (_, task) in &resolved_tasks.templates {
 6827                    let maybe_scenario = dap_store.debug_scenario_for_build_task(
 6828                        task.original_task().clone(),
 6829                        debug_adapter.clone().into(),
 6830                        task.display_label().to_owned().into(),
 6831                        cx,
 6832                    );
 6833                    scenarios.push(maybe_scenario);
 6834                }
 6835            });
 6836            Some(cx.background_spawn(async move {
 6837                futures::future::join_all(scenarios)
 6838                    .await
 6839                    .into_iter()
 6840                    .flatten()
 6841                    .collect::<Vec<_>>()
 6842            }))
 6843        })
 6844        .unwrap_or_else(|| Task::ready(vec![]))
 6845    }
 6846
 6847    fn code_actions(
 6848        &mut self,
 6849        buffer_row: u32,
 6850        window: &mut Window,
 6851        cx: &mut Context<Self>,
 6852    ) -> Task<Option<Rc<[AvailableCodeAction]>>> {
 6853        let mut task = self.code_actions_task.take();
 6854        cx.spawn_in(window, async move |editor, cx| {
 6855            while let Some(prev_task) = task {
 6856                prev_task.await.log_err();
 6857                task = editor
 6858                    .update(cx, |this, _| this.code_actions_task.take())
 6859                    .ok()?;
 6860            }
 6861
 6862            editor
 6863                .update(cx, |editor, cx| {
 6864                    editor
 6865                        .available_code_actions
 6866                        .clone()
 6867                        .and_then(|(location, code_actions)| {
 6868                            let snapshot = location.buffer.read(cx).snapshot();
 6869                            let point_range = location.range.to_point(&snapshot);
 6870                            let point_range = point_range.start.row..=point_range.end.row;
 6871                            if point_range.contains(&buffer_row) {
 6872                                Some(code_actions)
 6873                            } else {
 6874                                None
 6875                            }
 6876                        })
 6877                })
 6878                .ok()
 6879                .flatten()
 6880        })
 6881    }
 6882
 6883    pub fn confirm_code_action(
 6884        &mut self,
 6885        action: &ConfirmCodeAction,
 6886        window: &mut Window,
 6887        cx: &mut Context<Self>,
 6888    ) -> Option<Task<Result<()>>> {
 6889        self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
 6890
 6891        let actions_menu =
 6892            if let CodeContextMenu::CodeActions(menu) = self.hide_context_menu(window, cx)? {
 6893                menu
 6894            } else {
 6895                return None;
 6896            };
 6897
 6898        let action_ix = action.item_ix.unwrap_or(actions_menu.selected_item);
 6899        let action = actions_menu.actions.get(action_ix)?;
 6900        let title = action.label();
 6901        let buffer = actions_menu.buffer;
 6902        let workspace = self.workspace()?;
 6903
 6904        match action {
 6905            CodeActionsItem::Task(task_source_kind, resolved_task) => {
 6906                workspace.update(cx, |workspace, cx| {
 6907                    workspace.schedule_resolved_task(
 6908                        task_source_kind,
 6909                        resolved_task,
 6910                        false,
 6911                        window,
 6912                        cx,
 6913                    );
 6914
 6915                    Some(Task::ready(Ok(())))
 6916                })
 6917            }
 6918            CodeActionsItem::CodeAction {
 6919                excerpt_id,
 6920                action,
 6921                provider,
 6922            } => {
 6923                let apply_code_action =
 6924                    provider.apply_code_action(buffer, action, excerpt_id, true, window, cx);
 6925                let workspace = workspace.downgrade();
 6926                Some(cx.spawn_in(window, async move |editor, cx| {
 6927                    let project_transaction = apply_code_action.await?;
 6928                    Self::open_project_transaction(
 6929                        &editor,
 6930                        workspace,
 6931                        project_transaction,
 6932                        title,
 6933                        cx,
 6934                    )
 6935                    .await
 6936                }))
 6937            }
 6938            CodeActionsItem::DebugScenario(scenario) => {
 6939                let context = actions_menu.actions.context.into();
 6940
 6941                workspace.update(cx, |workspace, cx| {
 6942                    dap::send_telemetry(&scenario, TelemetrySpawnLocation::Gutter, cx);
 6943                    workspace.start_debug_session(
 6944                        scenario,
 6945                        context,
 6946                        Some(buffer),
 6947                        None,
 6948                        window,
 6949                        cx,
 6950                    );
 6951                });
 6952                Some(Task::ready(Ok(())))
 6953            }
 6954        }
 6955    }
 6956
 6957    fn open_transaction_for_hidden_buffers(
 6958        workspace: Entity<Workspace>,
 6959        transaction: ProjectTransaction,
 6960        title: String,
 6961        window: &mut Window,
 6962        cx: &mut Context<Self>,
 6963    ) {
 6964        if transaction.0.is_empty() {
 6965            return;
 6966        }
 6967
 6968        let edited_buffers_already_open = {
 6969            let other_editors: Vec<Entity<Editor>> = workspace
 6970                .read(cx)
 6971                .panes()
 6972                .iter()
 6973                .flat_map(|pane| pane.read(cx).items_of_type::<Editor>())
 6974                .filter(|editor| editor.entity_id() != cx.entity_id())
 6975                .collect();
 6976
 6977            transaction.0.keys().all(|buffer| {
 6978                other_editors.iter().any(|editor| {
 6979                    let multi_buffer = editor.read(cx).buffer();
 6980                    multi_buffer.read(cx).is_singleton()
 6981                        && multi_buffer
 6982                            .read(cx)
 6983                            .as_singleton()
 6984                            .map_or(false, |singleton| {
 6985                                singleton.entity_id() == buffer.entity_id()
 6986                            })
 6987                })
 6988            })
 6989        };
 6990        if !edited_buffers_already_open {
 6991            let workspace = workspace.downgrade();
 6992            cx.defer_in(window, move |_, window, cx| {
 6993                cx.spawn_in(window, async move |editor, cx| {
 6994                    Self::open_project_transaction(&editor, workspace, transaction, title, cx)
 6995                        .await
 6996                        .ok()
 6997                })
 6998                .detach();
 6999            });
 7000        }
 7001    }
 7002
 7003    pub async fn open_project_transaction(
 7004        editor: &WeakEntity<Editor>,
 7005        workspace: WeakEntity<Workspace>,
 7006        transaction: ProjectTransaction,
 7007        title: String,
 7008        cx: &mut AsyncWindowContext,
 7009    ) -> Result<()> {
 7010        let mut entries = transaction.0.into_iter().collect::<Vec<_>>();
 7011        cx.update(|_, cx| {
 7012            entries.sort_unstable_by_key(|(buffer, _)| {
 7013                buffer.read(cx).file().map(|f| f.path().clone())
 7014            });
 7015        })?;
 7016        if entries.is_empty() {
 7017            return Ok(());
 7018        }
 7019
 7020        // If the project transaction's edits are all contained within this editor, then
 7021        // avoid opening a new editor to display them.
 7022
 7023        if let [(buffer, transaction)] = &*entries {
 7024            let excerpt = editor.update(cx, |editor, cx| {
 7025                editor
 7026                    .buffer()
 7027                    .read(cx)
 7028                    .excerpt_containing(editor.selections.newest_anchor().head(), cx)
 7029            })?;
 7030            if let Some((_, excerpted_buffer, excerpt_range)) = excerpt
 7031                && excerpted_buffer == *buffer
 7032            {
 7033                let all_edits_within_excerpt = buffer.read_with(cx, |buffer, _| {
 7034                    let excerpt_range = excerpt_range.to_offset(buffer);
 7035                    buffer
 7036                        .edited_ranges_for_transaction::<usize>(transaction)
 7037                        .all(|range| {
 7038                            excerpt_range.start <= range.start && excerpt_range.end >= range.end
 7039                        })
 7040                });
 7041
 7042                if all_edits_within_excerpt {
 7043                    return Ok(());
 7044                }
 7045            }
 7046        }
 7047
 7048        let mut ranges_to_highlight = Vec::new();
 7049        let excerpt_buffer = cx.new(|cx| {
 7050            let mut multibuffer = MultiBuffer::new(Capability::ReadWrite).with_title(title);
 7051            for (buffer_handle, transaction) in &entries {
 7052                let edited_ranges = buffer_handle
 7053                    .read(cx)
 7054                    .edited_ranges_for_transaction::<Point>(transaction)
 7055                    .collect::<Vec<_>>();
 7056                let (ranges, _) = multibuffer.set_excerpts_for_path(
 7057                    PathKey::for_buffer(buffer_handle, cx),
 7058                    buffer_handle.clone(),
 7059                    edited_ranges,
 7060                    multibuffer_context_lines(cx),
 7061                    cx,
 7062                );
 7063
 7064                ranges_to_highlight.extend(ranges);
 7065            }
 7066            multibuffer.push_transaction(entries.iter().map(|(b, t)| (b, t)), cx);
 7067            multibuffer
 7068        });
 7069
 7070        workspace.update_in(cx, |workspace, window, cx| {
 7071            let project = workspace.project().clone();
 7072            let editor =
 7073                cx.new(|cx| Editor::for_multibuffer(excerpt_buffer, Some(project), window, cx));
 7074            workspace.add_item_to_active_pane(Box::new(editor.clone()), None, true, window, cx);
 7075            editor.update(cx, |editor, cx| {
 7076                editor.highlight_background(
 7077                    HighlightKey::Editor,
 7078                    &ranges_to_highlight,
 7079                    |_, theme| theme.colors().editor_highlighted_line_background,
 7080                    cx,
 7081                );
 7082            });
 7083        })?;
 7084
 7085        Ok(())
 7086    }
 7087
 7088    pub fn clear_code_action_providers(&mut self) {
 7089        self.code_action_providers.clear();
 7090        self.available_code_actions.take();
 7091    }
 7092
 7093    pub fn add_code_action_provider(
 7094        &mut self,
 7095        provider: Rc<dyn CodeActionProvider>,
 7096        window: &mut Window,
 7097        cx: &mut Context<Self>,
 7098    ) {
 7099        if self
 7100            .code_action_providers
 7101            .iter()
 7102            .any(|existing_provider| existing_provider.id() == provider.id())
 7103        {
 7104            return;
 7105        }
 7106
 7107        self.code_action_providers.push(provider);
 7108        self.refresh_code_actions(window, cx);
 7109    }
 7110
 7111    pub fn remove_code_action_provider(
 7112        &mut self,
 7113        id: Arc<str>,
 7114        window: &mut Window,
 7115        cx: &mut Context<Self>,
 7116    ) {
 7117        self.code_action_providers
 7118            .retain(|provider| provider.id() != id);
 7119        self.refresh_code_actions(window, cx);
 7120    }
 7121
 7122    pub fn code_actions_enabled_for_toolbar(&self, cx: &App) -> bool {
 7123        !self.code_action_providers.is_empty()
 7124            && EditorSettings::get_global(cx).toolbar.code_actions
 7125    }
 7126
 7127    pub fn has_available_code_actions(&self) -> bool {
 7128        self.available_code_actions
 7129            .as_ref()
 7130            .is_some_and(|(_, actions)| !actions.is_empty())
 7131    }
 7132
 7133    fn render_inline_code_actions(
 7134        &self,
 7135        icon_size: ui::IconSize,
 7136        display_row: DisplayRow,
 7137        is_active: bool,
 7138        cx: &mut Context<Self>,
 7139    ) -> AnyElement {
 7140        let show_tooltip = !self.context_menu_visible();
 7141        IconButton::new("inline_code_actions", ui::IconName::BoltFilled)
 7142            .icon_size(icon_size)
 7143            .shape(ui::IconButtonShape::Square)
 7144            .icon_color(ui::Color::Hidden)
 7145            .toggle_state(is_active)
 7146            .when(show_tooltip, |this| {
 7147                this.tooltip({
 7148                    let focus_handle = self.focus_handle.clone();
 7149                    move |_window, cx| {
 7150                        Tooltip::for_action_in(
 7151                            "Toggle Code Actions",
 7152                            &ToggleCodeActions {
 7153                                deployed_from: None,
 7154                                quick_launch: false,
 7155                            },
 7156                            &focus_handle,
 7157                            cx,
 7158                        )
 7159                    }
 7160                })
 7161            })
 7162            .on_click(cx.listener(move |editor, _: &ClickEvent, window, cx| {
 7163                window.focus(&editor.focus_handle(cx), cx);
 7164                editor.toggle_code_actions(
 7165                    &crate::actions::ToggleCodeActions {
 7166                        deployed_from: Some(crate::actions::CodeActionSource::Indicator(
 7167                            display_row,
 7168                        )),
 7169                        quick_launch: false,
 7170                    },
 7171                    window,
 7172                    cx,
 7173                );
 7174            }))
 7175            .into_any_element()
 7176    }
 7177
 7178    pub fn context_menu(&self) -> &RefCell<Option<CodeContextMenu>> {
 7179        &self.context_menu
 7180    }
 7181
 7182    fn refresh_code_actions(&mut self, window: &mut Window, cx: &mut Context<Self>) {
 7183        self.code_actions_task = Some(cx.spawn_in(window, async move |this, cx| {
 7184            cx.background_executor()
 7185                .timer(CODE_ACTIONS_DEBOUNCE_TIMEOUT)
 7186                .await;
 7187
 7188            let (start_buffer, start, _, end, newest_selection) = this
 7189                .update(cx, |this, cx| {
 7190                    let newest_selection = this.selections.newest_anchor().clone();
 7191                    if newest_selection.head().diff_base_anchor.is_some() {
 7192                        return None;
 7193                    }
 7194                    let display_snapshot = this.display_snapshot(cx);
 7195                    let newest_selection_adjusted =
 7196                        this.selections.newest_adjusted(&display_snapshot);
 7197                    let buffer = this.buffer.read(cx);
 7198
 7199                    let (start_buffer, start) =
 7200                        buffer.text_anchor_for_position(newest_selection_adjusted.start, cx)?;
 7201                    let (end_buffer, end) =
 7202                        buffer.text_anchor_for_position(newest_selection_adjusted.end, cx)?;
 7203
 7204                    Some((start_buffer, start, end_buffer, end, newest_selection))
 7205                })?
 7206                .filter(|(start_buffer, _, end_buffer, _, _)| start_buffer == end_buffer)
 7207                .context(
 7208                    "Expected selection to lie in a single buffer when refreshing code actions",
 7209                )?;
 7210            let (providers, tasks) = this.update_in(cx, |this, window, cx| {
 7211                let providers = this.code_action_providers.clone();
 7212                let tasks = this
 7213                    .code_action_providers
 7214                    .iter()
 7215                    .map(|provider| provider.code_actions(&start_buffer, start..end, window, cx))
 7216                    .collect::<Vec<_>>();
 7217                (providers, tasks)
 7218            })?;
 7219
 7220            let mut actions = Vec::new();
 7221            for (provider, provider_actions) in
 7222                providers.into_iter().zip(future::join_all(tasks).await)
 7223            {
 7224                if let Some(provider_actions) = provider_actions.log_err() {
 7225                    actions.extend(provider_actions.into_iter().map(|action| {
 7226                        AvailableCodeAction {
 7227                            excerpt_id: newest_selection.start.excerpt_id,
 7228                            action,
 7229                            provider: provider.clone(),
 7230                        }
 7231                    }));
 7232                }
 7233            }
 7234
 7235            this.update(cx, |this, cx| {
 7236                this.available_code_actions = if actions.is_empty() {
 7237                    None
 7238                } else {
 7239                    Some((
 7240                        Location {
 7241                            buffer: start_buffer,
 7242                            range: start..end,
 7243                        },
 7244                        actions.into(),
 7245                    ))
 7246                };
 7247                cx.notify();
 7248            })
 7249        }));
 7250    }
 7251
 7252    fn start_inline_blame_timer(&mut self, window: &mut Window, cx: &mut Context<Self>) {
 7253        if let Some(delay) = ProjectSettings::get_global(cx).git.inline_blame_delay() {
 7254            self.show_git_blame_inline = false;
 7255
 7256            self.show_git_blame_inline_delay_task =
 7257                Some(cx.spawn_in(window, async move |this, cx| {
 7258                    cx.background_executor().timer(delay).await;
 7259
 7260                    this.update(cx, |this, cx| {
 7261                        this.show_git_blame_inline = true;
 7262                        cx.notify();
 7263                    })
 7264                    .log_err();
 7265                }));
 7266        }
 7267    }
 7268
 7269    pub fn blame_hover(&mut self, _: &BlameHover, window: &mut Window, cx: &mut Context<Self>) {
 7270        let snapshot = self.snapshot(window, cx);
 7271        let cursor = self
 7272            .selections
 7273            .newest::<Point>(&snapshot.display_snapshot)
 7274            .head();
 7275        let Some((buffer, point, _)) = snapshot.buffer_snapshot().point_to_buffer_point(cursor)
 7276        else {
 7277            return;
 7278        };
 7279
 7280        if self.blame.is_none() {
 7281            self.start_git_blame(true, window, cx);
 7282        }
 7283        let Some(blame) = self.blame.as_ref() else {
 7284            return;
 7285        };
 7286
 7287        let row_info = RowInfo {
 7288            buffer_id: Some(buffer.remote_id()),
 7289            buffer_row: Some(point.row),
 7290            ..Default::default()
 7291        };
 7292        let Some((buffer, blame_entry)) = blame
 7293            .update(cx, |blame, cx| blame.blame_for_rows(&[row_info], cx).next())
 7294            .flatten()
 7295        else {
 7296            return;
 7297        };
 7298
 7299        let anchor = self.selections.newest_anchor().head();
 7300        let position = self.to_pixel_point(anchor, &snapshot, window, cx);
 7301        if let (Some(position), Some(last_bounds)) = (position, self.last_bounds) {
 7302            self.show_blame_popover(
 7303                buffer,
 7304                &blame_entry,
 7305                position + last_bounds.origin,
 7306                true,
 7307                cx,
 7308            );
 7309        };
 7310    }
 7311
 7312    fn show_blame_popover(
 7313        &mut self,
 7314        buffer: BufferId,
 7315        blame_entry: &BlameEntry,
 7316        position: gpui::Point<Pixels>,
 7317        ignore_timeout: bool,
 7318        cx: &mut Context<Self>,
 7319    ) {
 7320        if let Some(state) = &mut self.inline_blame_popover {
 7321            state.hide_task.take();
 7322        } else {
 7323            let blame_popover_delay = EditorSettings::get_global(cx).hover_popover_delay.0;
 7324            let blame_entry = blame_entry.clone();
 7325            let show_task = cx.spawn(async move |editor, cx| {
 7326                if !ignore_timeout {
 7327                    cx.background_executor()
 7328                        .timer(std::time::Duration::from_millis(blame_popover_delay))
 7329                        .await;
 7330                }
 7331                editor
 7332                    .update(cx, |editor, cx| {
 7333                        editor.inline_blame_popover_show_task.take();
 7334                        let Some(blame) = editor.blame.as_ref() else {
 7335                            return;
 7336                        };
 7337                        let blame = blame.read(cx);
 7338                        let details = blame.details_for_entry(buffer, &blame_entry);
 7339                        let markdown = cx.new(|cx| {
 7340                            Markdown::new(
 7341                                details
 7342                                    .as_ref()
 7343                                    .map(|message| message.message.clone())
 7344                                    .unwrap_or_default(),
 7345                                None,
 7346                                None,
 7347                                cx,
 7348                            )
 7349                        });
 7350                        editor.inline_blame_popover = Some(InlineBlamePopover {
 7351                            position,
 7352                            hide_task: None,
 7353                            popover_bounds: None,
 7354                            popover_state: InlineBlamePopoverState {
 7355                                scroll_handle: ScrollHandle::new(),
 7356                                commit_message: details,
 7357                                markdown,
 7358                            },
 7359                            keyboard_grace: ignore_timeout,
 7360                        });
 7361                        cx.notify();
 7362                    })
 7363                    .ok();
 7364            });
 7365            self.inline_blame_popover_show_task = Some(show_task);
 7366        }
 7367    }
 7368
 7369    pub fn has_mouse_context_menu(&self) -> bool {
 7370        self.mouse_context_menu.is_some()
 7371    }
 7372
 7373    pub fn hide_blame_popover(&mut self, ignore_timeout: bool, cx: &mut Context<Self>) -> bool {
 7374        self.inline_blame_popover_show_task.take();
 7375        if let Some(state) = &mut self.inline_blame_popover {
 7376            let hide_task = cx.spawn(async move |editor, cx| {
 7377                if !ignore_timeout {
 7378                    cx.background_executor()
 7379                        .timer(std::time::Duration::from_millis(100))
 7380                        .await;
 7381                }
 7382                editor
 7383                    .update(cx, |editor, cx| {
 7384                        editor.inline_blame_popover.take();
 7385                        cx.notify();
 7386                    })
 7387                    .ok();
 7388            });
 7389            state.hide_task = Some(hide_task);
 7390            true
 7391        } else {
 7392            false
 7393        }
 7394    }
 7395
 7396    fn refresh_document_highlights(&mut self, cx: &mut Context<Self>) -> Option<()> {
 7397        if self.pending_rename.is_some() {
 7398            return None;
 7399        }
 7400
 7401        let provider = self.semantics_provider.clone()?;
 7402        let buffer = self.buffer.read(cx);
 7403        let newest_selection = self.selections.newest_anchor().clone();
 7404        let cursor_position = newest_selection.head();
 7405        let (cursor_buffer, cursor_buffer_position) =
 7406            buffer.text_anchor_for_position(cursor_position, cx)?;
 7407        let (tail_buffer, tail_buffer_position) =
 7408            buffer.text_anchor_for_position(newest_selection.tail(), cx)?;
 7409        if cursor_buffer != tail_buffer {
 7410            return None;
 7411        }
 7412
 7413        let snapshot = cursor_buffer.read(cx).snapshot();
 7414        let word_ranges = cx.background_spawn(async move {
 7415            // this might look odd to put on the background thread, but
 7416            // `surrounding_word` can be quite expensive as it calls into
 7417            // tree-sitter language scopes
 7418            let (start_word_range, _) = snapshot.surrounding_word(cursor_buffer_position, None);
 7419            let (end_word_range, _) = snapshot.surrounding_word(tail_buffer_position, None);
 7420            (start_word_range, end_word_range)
 7421        });
 7422
 7423        let debounce = EditorSettings::get_global(cx).lsp_highlight_debounce.0;
 7424        self.document_highlights_task = Some(cx.spawn(async move |this, cx| {
 7425            let (start_word_range, end_word_range) = word_ranges.await;
 7426            if start_word_range != end_word_range {
 7427                this.update(cx, |this, cx| {
 7428                    this.document_highlights_task.take();
 7429                    this.clear_background_highlights(HighlightKey::DocumentHighlightRead, cx);
 7430                    this.clear_background_highlights(HighlightKey::DocumentHighlightWrite, cx);
 7431                })
 7432                .ok();
 7433                return;
 7434            }
 7435            cx.background_executor()
 7436                .timer(Duration::from_millis(debounce))
 7437                .await;
 7438
 7439            let highlights = if let Some(highlights) = cx.update(|cx| {
 7440                provider.document_highlights(&cursor_buffer, cursor_buffer_position, cx)
 7441            }) {
 7442                highlights.await.log_err()
 7443            } else {
 7444                None
 7445            };
 7446
 7447            if let Some(highlights) = highlights {
 7448                this.update(cx, |this, cx| {
 7449                    if this.pending_rename.is_some() {
 7450                        return;
 7451                    }
 7452
 7453                    let buffer = this.buffer.read(cx);
 7454                    if buffer
 7455                        .text_anchor_for_position(cursor_position, cx)
 7456                        .is_none_or(|(buffer, _)| buffer != cursor_buffer)
 7457                    {
 7458                        return;
 7459                    }
 7460
 7461                    let cursor_buffer_snapshot = cursor_buffer.read(cx);
 7462                    let mut write_ranges = Vec::new();
 7463                    let mut read_ranges = Vec::new();
 7464                    for highlight in highlights {
 7465                        let buffer_id = cursor_buffer.read(cx).remote_id();
 7466                        for (excerpt_id, excerpt_range) in buffer.excerpts_for_buffer(buffer_id, cx)
 7467                        {
 7468                            let start = highlight
 7469                                .range
 7470                                .start
 7471                                .max(&excerpt_range.context.start, cursor_buffer_snapshot);
 7472                            let end = highlight
 7473                                .range
 7474                                .end
 7475                                .min(&excerpt_range.context.end, cursor_buffer_snapshot);
 7476                            if start.cmp(&end, cursor_buffer_snapshot).is_ge() {
 7477                                continue;
 7478                            }
 7479
 7480                            let range = Anchor::range_in_buffer(excerpt_id, *start..*end);
 7481                            if highlight.kind == lsp::DocumentHighlightKind::WRITE {
 7482                                write_ranges.push(range);
 7483                            } else {
 7484                                read_ranges.push(range);
 7485                            }
 7486                        }
 7487                    }
 7488
 7489                    this.highlight_background(
 7490                        HighlightKey::DocumentHighlightRead,
 7491                        &read_ranges,
 7492                        |_, theme| theme.colors().editor_document_highlight_read_background,
 7493                        cx,
 7494                    );
 7495                    this.highlight_background(
 7496                        HighlightKey::DocumentHighlightWrite,
 7497                        &write_ranges,
 7498                        |_, theme| theme.colors().editor_document_highlight_write_background,
 7499                        cx,
 7500                    );
 7501                    cx.notify();
 7502                })
 7503                .log_err();
 7504            }
 7505        }));
 7506        None
 7507    }
 7508
 7509    fn prepare_highlight_query_from_selection(
 7510        &mut self,
 7511        snapshot: &DisplaySnapshot,
 7512        cx: &mut Context<Editor>,
 7513    ) -> Option<(String, Range<Anchor>)> {
 7514        if matches!(self.mode, EditorMode::SingleLine) {
 7515            return None;
 7516        }
 7517        if !EditorSettings::get_global(cx).selection_highlight {
 7518            return None;
 7519        }
 7520        if self.selections.count() != 1 || self.selections.line_mode() {
 7521            return None;
 7522        }
 7523        let selection = self.selections.newest::<Point>(&snapshot);
 7524        // If the selection spans multiple rows OR it is empty
 7525        if selection.start.row != selection.end.row
 7526            || selection.start.column == selection.end.column
 7527        {
 7528            return None;
 7529        }
 7530        let selection_anchor_range = selection.range().to_anchors(snapshot.buffer_snapshot());
 7531        let query = snapshot
 7532            .buffer_snapshot()
 7533            .text_for_range(selection_anchor_range.clone())
 7534            .collect::<String>();
 7535        if query.trim().is_empty() {
 7536            return None;
 7537        }
 7538        Some((query, selection_anchor_range))
 7539    }
 7540
 7541    #[ztracing::instrument(skip_all)]
 7542    fn update_selection_occurrence_highlights(
 7543        &mut self,
 7544        multi_buffer_snapshot: MultiBufferSnapshot,
 7545        query_text: String,
 7546        query_range: Range<Anchor>,
 7547        multi_buffer_range_to_query: Range<Point>,
 7548        use_debounce: bool,
 7549        window: &mut Window,
 7550        cx: &mut Context<Editor>,
 7551    ) -> Task<()> {
 7552        cx.spawn_in(window, async move |editor, cx| {
 7553            if use_debounce {
 7554                cx.background_executor()
 7555                    .timer(SELECTION_HIGHLIGHT_DEBOUNCE_TIMEOUT)
 7556                    .await;
 7557            }
 7558            let match_task = cx.background_spawn(async move {
 7559                let buffer_ranges = multi_buffer_snapshot
 7560                    .range_to_buffer_ranges(
 7561                        multi_buffer_range_to_query.start..=multi_buffer_range_to_query.end,
 7562                    )
 7563                    .into_iter()
 7564                    .filter(|(_, excerpt_visible_range, _)| !excerpt_visible_range.is_empty());
 7565                let mut match_ranges = Vec::new();
 7566                let Ok(regex) = project::search::SearchQuery::text(
 7567                    query_text,
 7568                    false,
 7569                    false,
 7570                    false,
 7571                    Default::default(),
 7572                    Default::default(),
 7573                    false,
 7574                    None,
 7575                ) else {
 7576                    return Vec::default();
 7577                };
 7578                let query_range = query_range.to_anchors(&multi_buffer_snapshot);
 7579                for (buffer_snapshot, search_range, excerpt_id) in buffer_ranges {
 7580                    match_ranges.extend(
 7581                        regex
 7582                            .search(
 7583                                buffer_snapshot,
 7584                                Some(search_range.start.0..search_range.end.0),
 7585                            )
 7586                            .await
 7587                            .into_iter()
 7588                            .filter_map(|match_range| {
 7589                                let match_start = buffer_snapshot
 7590                                    .anchor_after(search_range.start + match_range.start);
 7591                                let match_end = buffer_snapshot
 7592                                    .anchor_before(search_range.start + match_range.end);
 7593                                let match_anchor_range =
 7594                                    Anchor::range_in_buffer(excerpt_id, match_start..match_end);
 7595                                (match_anchor_range != query_range).then_some(match_anchor_range)
 7596                            }),
 7597                    );
 7598                }
 7599                match_ranges
 7600            });
 7601            let match_ranges = match_task.await;
 7602            editor
 7603                .update_in(cx, |editor, _, cx| {
 7604                    if use_debounce {
 7605                        editor.clear_background_highlights(HighlightKey::SelectedTextHighlight, cx);
 7606                        editor.debounced_selection_highlight_complete = true;
 7607                    } else if editor.debounced_selection_highlight_complete {
 7608                        return;
 7609                    }
 7610                    if !match_ranges.is_empty() {
 7611                        editor.highlight_background(
 7612                            HighlightKey::SelectedTextHighlight,
 7613                            &match_ranges,
 7614                            |_, theme| theme.colors().editor_document_highlight_bracket_background,
 7615                            cx,
 7616                        )
 7617                    }
 7618                })
 7619                .log_err();
 7620        })
 7621    }
 7622
 7623    fn refresh_single_line_folds(&mut self, window: &mut Window, cx: &mut Context<Editor>) {
 7624        struct NewlineFold;
 7625        let type_id = std::any::TypeId::of::<NewlineFold>();
 7626        if !self.mode.is_single_line() {
 7627            return;
 7628        }
 7629        let snapshot = self.snapshot(window, cx);
 7630        if snapshot.buffer_snapshot().max_point().row == 0 {
 7631            return;
 7632        }
 7633        let task = cx.background_spawn(async move {
 7634            let new_newlines = snapshot
 7635                .buffer_chars_at(MultiBufferOffset(0))
 7636                .filter_map(|(c, i)| {
 7637                    if c == '\n' {
 7638                        Some(
 7639                            snapshot.buffer_snapshot().anchor_after(i)
 7640                                ..snapshot.buffer_snapshot().anchor_before(i + 1usize),
 7641                        )
 7642                    } else {
 7643                        None
 7644                    }
 7645                })
 7646                .collect::<Vec<_>>();
 7647            let existing_newlines = snapshot
 7648                .folds_in_range(MultiBufferOffset(0)..snapshot.buffer_snapshot().len())
 7649                .filter_map(|fold| {
 7650                    if fold.placeholder.type_tag == Some(type_id) {
 7651                        Some(fold.range.start..fold.range.end)
 7652                    } else {
 7653                        None
 7654                    }
 7655                })
 7656                .collect::<Vec<_>>();
 7657
 7658            (new_newlines, existing_newlines)
 7659        });
 7660        self.folding_newlines = cx.spawn(async move |this, cx| {
 7661            let (new_newlines, existing_newlines) = task.await;
 7662            if new_newlines == existing_newlines {
 7663                return;
 7664            }
 7665            let placeholder = FoldPlaceholder {
 7666                render: Arc::new(move |_, _, cx| {
 7667                    div()
 7668                        .bg(cx.theme().status().hint_background)
 7669                        .border_b_1()
 7670                        .size_full()
 7671                        .font(ThemeSettings::get_global(cx).buffer_font.clone())
 7672                        .border_color(cx.theme().status().hint)
 7673                        .child("\\n")
 7674                        .into_any()
 7675                }),
 7676                constrain_width: false,
 7677                merge_adjacent: false,
 7678                type_tag: Some(type_id),
 7679                collapsed_text: None,
 7680            };
 7681            let creases = new_newlines
 7682                .into_iter()
 7683                .map(|range| Crease::simple(range, placeholder.clone()))
 7684                .collect();
 7685            this.update(cx, |this, cx| {
 7686                this.display_map.update(cx, |display_map, cx| {
 7687                    display_map.remove_folds_with_type(existing_newlines, type_id, cx);
 7688                    display_map.fold(creases, cx);
 7689                });
 7690            })
 7691            .ok();
 7692        });
 7693    }
 7694
 7695    #[ztracing::instrument(skip_all)]
 7696    fn refresh_outline_symbols_at_cursor(&mut self, cx: &mut Context<Editor>) {
 7697        if !self.mode.is_full() {
 7698            return;
 7699        }
 7700        let cursor = self.selections.newest_anchor().head();
 7701        let multi_buffer_snapshot = self.buffer().read(cx).snapshot(cx);
 7702
 7703        if self.uses_lsp_document_symbols(cursor, &multi_buffer_snapshot, cx) {
 7704            self.outline_symbols_at_cursor =
 7705                self.lsp_symbols_at_cursor(cursor, &multi_buffer_snapshot, cx);
 7706            cx.emit(EditorEvent::OutlineSymbolsChanged);
 7707            cx.notify();
 7708        } else {
 7709            let syntax = cx.theme().syntax().clone();
 7710            let background_task = cx.background_spawn(async move {
 7711                multi_buffer_snapshot.symbols_containing(cursor, Some(&syntax))
 7712            });
 7713            self.refresh_outline_symbols_at_cursor_at_cursor_task =
 7714                cx.spawn(async move |this, cx| {
 7715                    let symbols = background_task.await;
 7716                    this.update(cx, |this, cx| {
 7717                        this.outline_symbols_at_cursor = symbols;
 7718                        cx.emit(EditorEvent::OutlineSymbolsChanged);
 7719                        cx.notify();
 7720                    })
 7721                    .ok();
 7722                });
 7723        }
 7724    }
 7725
 7726    #[ztracing::instrument(skip_all)]
 7727    fn refresh_selected_text_highlights(
 7728        &mut self,
 7729        snapshot: &DisplaySnapshot,
 7730        on_buffer_edit: bool,
 7731        window: &mut Window,
 7732        cx: &mut Context<Editor>,
 7733    ) {
 7734        let Some((query_text, query_range)) =
 7735            self.prepare_highlight_query_from_selection(snapshot, cx)
 7736        else {
 7737            self.clear_background_highlights(HighlightKey::SelectedTextHighlight, cx);
 7738            self.quick_selection_highlight_task.take();
 7739            self.debounced_selection_highlight_task.take();
 7740            self.debounced_selection_highlight_complete = false;
 7741            return;
 7742        };
 7743        let display_snapshot = self.display_map.update(cx, |map, cx| map.snapshot(cx));
 7744        let multi_buffer_snapshot = self.buffer().read(cx).snapshot(cx);
 7745        let query_changed = self
 7746            .quick_selection_highlight_task
 7747            .as_ref()
 7748            .is_none_or(|(prev_anchor_range, _)| prev_anchor_range != &query_range);
 7749        if query_changed {
 7750            self.debounced_selection_highlight_complete = false;
 7751        }
 7752        if on_buffer_edit || query_changed {
 7753            let multi_buffer_visible_start = self
 7754                .scroll_manager
 7755                .native_anchor(&display_snapshot, cx)
 7756                .anchor
 7757                .to_point(&multi_buffer_snapshot);
 7758            let multi_buffer_visible_end = multi_buffer_snapshot.clip_point(
 7759                multi_buffer_visible_start
 7760                    + Point::new(self.visible_line_count().unwrap_or(0.).ceil() as u32, 0),
 7761                Bias::Left,
 7762            );
 7763            let multi_buffer_visible_range = multi_buffer_visible_start..multi_buffer_visible_end;
 7764            self.quick_selection_highlight_task = Some((
 7765                query_range.clone(),
 7766                self.update_selection_occurrence_highlights(
 7767                    snapshot.buffer.clone(),
 7768                    query_text.clone(),
 7769                    query_range.clone(),
 7770                    multi_buffer_visible_range,
 7771                    false,
 7772                    window,
 7773                    cx,
 7774                ),
 7775            ));
 7776        }
 7777        if on_buffer_edit
 7778            || self
 7779                .debounced_selection_highlight_task
 7780                .as_ref()
 7781                .is_none_or(|(prev_anchor_range, _)| prev_anchor_range != &query_range)
 7782        {
 7783            let multi_buffer_start = multi_buffer_snapshot
 7784                .anchor_before(MultiBufferOffset(0))
 7785                .to_point(&multi_buffer_snapshot);
 7786            let multi_buffer_end = multi_buffer_snapshot
 7787                .anchor_after(multi_buffer_snapshot.len())
 7788                .to_point(&multi_buffer_snapshot);
 7789            let multi_buffer_full_range = multi_buffer_start..multi_buffer_end;
 7790            self.debounced_selection_highlight_task = Some((
 7791                query_range.clone(),
 7792                self.update_selection_occurrence_highlights(
 7793                    snapshot.buffer.clone(),
 7794                    query_text,
 7795                    query_range,
 7796                    multi_buffer_full_range,
 7797                    true,
 7798                    window,
 7799                    cx,
 7800                ),
 7801            ));
 7802        }
 7803    }
 7804
 7805    pub fn refresh_edit_prediction(
 7806        &mut self,
 7807        debounce: bool,
 7808        user_requested: bool,
 7809        window: &mut Window,
 7810        cx: &mut Context<Self>,
 7811    ) -> Option<()> {
 7812        let provider = self.edit_prediction_provider()?;
 7813        let cursor = self.selections.newest_anchor().head();
 7814        let (buffer, cursor_buffer_position) =
 7815            self.buffer.read(cx).text_anchor_for_position(cursor, cx)?;
 7816
 7817        if DisableAiSettings::is_ai_disabled_for_buffer(Some(&buffer), cx) {
 7818            return None;
 7819        }
 7820
 7821        if !self.edit_predictions_enabled_in_buffer(&buffer, cursor_buffer_position, cx) {
 7822            self.discard_edit_prediction(EditPredictionDiscardReason::Ignored, cx);
 7823            return None;
 7824        }
 7825
 7826        self.update_visible_edit_prediction(window, cx);
 7827
 7828        if !user_requested
 7829            && (!self.should_show_edit_predictions()
 7830                || !self.is_focused(window)
 7831                || buffer.read(cx).is_empty())
 7832        {
 7833            self.discard_edit_prediction(EditPredictionDiscardReason::Ignored, cx);
 7834            return None;
 7835        }
 7836
 7837        provider.refresh(buffer, cursor_buffer_position, debounce, cx);
 7838        Some(())
 7839    }
 7840
 7841    fn show_edit_predictions_in_menu(&self) -> bool {
 7842        match self.edit_prediction_settings {
 7843            EditPredictionSettings::Disabled => false,
 7844            EditPredictionSettings::Enabled { show_in_menu, .. } => show_in_menu,
 7845        }
 7846    }
 7847
 7848    pub fn edit_predictions_enabled(&self) -> bool {
 7849        match self.edit_prediction_settings {
 7850            EditPredictionSettings::Disabled => false,
 7851            EditPredictionSettings::Enabled { .. } => true,
 7852        }
 7853    }
 7854
 7855    fn edit_prediction_requires_modifier(&self) -> bool {
 7856        match self.edit_prediction_settings {
 7857            EditPredictionSettings::Disabled => false,
 7858            EditPredictionSettings::Enabled {
 7859                preview_requires_modifier,
 7860                ..
 7861            } => preview_requires_modifier,
 7862        }
 7863    }
 7864
 7865    pub fn update_edit_prediction_settings(&mut self, cx: &mut Context<Self>) {
 7866        if self.edit_prediction_provider.is_none() {
 7867            self.edit_prediction_settings = EditPredictionSettings::Disabled;
 7868            self.discard_edit_prediction(EditPredictionDiscardReason::Ignored, cx);
 7869            return;
 7870        }
 7871
 7872        let selection = self.selections.newest_anchor();
 7873        let cursor = selection.head();
 7874
 7875        if let Some((buffer, cursor_buffer_position)) =
 7876            self.buffer.read(cx).text_anchor_for_position(cursor, cx)
 7877        {
 7878            if DisableAiSettings::is_ai_disabled_for_buffer(Some(&buffer), cx) {
 7879                self.edit_prediction_settings = EditPredictionSettings::Disabled;
 7880                self.discard_edit_prediction(EditPredictionDiscardReason::Ignored, cx);
 7881                return;
 7882            }
 7883            self.edit_prediction_settings =
 7884                self.edit_prediction_settings_at_position(&buffer, cursor_buffer_position, cx);
 7885        }
 7886    }
 7887
 7888    fn edit_prediction_settings_at_position(
 7889        &self,
 7890        buffer: &Entity<Buffer>,
 7891        buffer_position: language::Anchor,
 7892        cx: &App,
 7893    ) -> EditPredictionSettings {
 7894        if !self.mode.is_full()
 7895            || !self.show_edit_predictions_override.unwrap_or(true)
 7896            || self.edit_predictions_disabled_in_scope(buffer, buffer_position, cx)
 7897        {
 7898            return EditPredictionSettings::Disabled;
 7899        }
 7900
 7901        let buffer = buffer.read(cx);
 7902
 7903        let file = buffer.file();
 7904
 7905        if !language_settings(buffer.language().map(|l| l.name()), file, cx).show_edit_predictions {
 7906            return EditPredictionSettings::Disabled;
 7907        };
 7908
 7909        let by_provider = matches!(
 7910            self.menu_edit_predictions_policy,
 7911            MenuEditPredictionsPolicy::ByProvider
 7912        );
 7913
 7914        let show_in_menu = by_provider
 7915            && self
 7916                .edit_prediction_provider
 7917                .as_ref()
 7918                .is_some_and(|provider| provider.provider.show_predictions_in_menu());
 7919
 7920        let preview_requires_modifier =
 7921            all_language_settings(file, cx).edit_predictions_mode() == EditPredictionsMode::Subtle;
 7922
 7923        EditPredictionSettings::Enabled {
 7924            show_in_menu,
 7925            preview_requires_modifier,
 7926        }
 7927    }
 7928
 7929    fn should_show_edit_predictions(&self) -> bool {
 7930        self.snippet_stack.is_empty() && self.edit_predictions_enabled()
 7931    }
 7932
 7933    pub fn edit_prediction_preview_is_active(&self) -> bool {
 7934        matches!(
 7935            self.edit_prediction_preview,
 7936            EditPredictionPreview::Active { .. }
 7937        )
 7938    }
 7939
 7940    pub fn edit_predictions_enabled_at_cursor(&self, cx: &App) -> bool {
 7941        let cursor = self.selections.newest_anchor().head();
 7942        if let Some((buffer, cursor_position)) =
 7943            self.buffer.read(cx).text_anchor_for_position(cursor, cx)
 7944        {
 7945            self.edit_predictions_enabled_in_buffer(&buffer, cursor_position, cx)
 7946        } else {
 7947            false
 7948        }
 7949    }
 7950
 7951    pub fn supports_minimap(&self, cx: &App) -> bool {
 7952        !self.minimap_visibility.disabled() && self.buffer_kind(cx) == ItemBufferKind::Singleton
 7953    }
 7954
 7955    fn edit_predictions_enabled_in_buffer(
 7956        &self,
 7957        buffer: &Entity<Buffer>,
 7958        buffer_position: language::Anchor,
 7959        cx: &App,
 7960    ) -> bool {
 7961        maybe!({
 7962            if self.read_only(cx) {
 7963                return Some(false);
 7964            }
 7965            let provider = self.edit_prediction_provider()?;
 7966            if !provider.is_enabled(buffer, buffer_position, cx) {
 7967                return Some(false);
 7968            }
 7969            let buffer = buffer.read(cx);
 7970            let Some(file) = buffer.file() else {
 7971                return Some(true);
 7972            };
 7973            let settings = all_language_settings(Some(file), cx);
 7974            Some(settings.edit_predictions_enabled_for_file(file, cx))
 7975        })
 7976        .unwrap_or(false)
 7977    }
 7978
 7979    pub fn show_edit_prediction(
 7980        &mut self,
 7981        _: &ShowEditPrediction,
 7982        window: &mut Window,
 7983        cx: &mut Context<Self>,
 7984    ) {
 7985        if !self.has_active_edit_prediction() {
 7986            self.refresh_edit_prediction(false, true, window, cx);
 7987            return;
 7988        }
 7989
 7990        self.update_visible_edit_prediction(window, cx);
 7991    }
 7992
 7993    pub fn display_cursor_names(
 7994        &mut self,
 7995        _: &DisplayCursorNames,
 7996        window: &mut Window,
 7997        cx: &mut Context<Self>,
 7998    ) {
 7999        self.show_cursor_names(window, cx);
 8000    }
 8001
 8002    fn show_cursor_names(&mut self, window: &mut Window, cx: &mut Context<Self>) {
 8003        self.show_cursor_names = true;
 8004        cx.notify();
 8005        cx.spawn_in(window, async move |this, cx| {
 8006            cx.background_executor().timer(CURSORS_VISIBLE_FOR).await;
 8007            this.update(cx, |this, cx| {
 8008                this.show_cursor_names = false;
 8009                cx.notify()
 8010            })
 8011            .ok()
 8012        })
 8013        .detach();
 8014    }
 8015
 8016    pub fn accept_partial_edit_prediction(
 8017        &mut self,
 8018        granularity: EditPredictionGranularity,
 8019        window: &mut Window,
 8020        cx: &mut Context<Self>,
 8021    ) {
 8022        if self.show_edit_predictions_in_menu() {
 8023            self.hide_context_menu(window, cx);
 8024        }
 8025
 8026        let Some(active_edit_prediction) = self.active_edit_prediction.as_ref() else {
 8027            return;
 8028        };
 8029
 8030        if !matches!(granularity, EditPredictionGranularity::Full) && self.selections.count() != 1 {
 8031            return;
 8032        }
 8033
 8034        match &active_edit_prediction.completion {
 8035            EditPrediction::MoveWithin { target, .. } => {
 8036                let target = *target;
 8037
 8038                if matches!(granularity, EditPredictionGranularity::Full) {
 8039                    if let Some(position_map) = &self.last_position_map {
 8040                        let target_row = target.to_display_point(&position_map.snapshot).row();
 8041                        let is_visible = position_map.visible_row_range.contains(&target_row);
 8042
 8043                        if is_visible || !self.edit_prediction_requires_modifier() {
 8044                            self.unfold_ranges(&[target..target], true, false, cx);
 8045                            self.change_selections(
 8046                                SelectionEffects::scroll(Autoscroll::newest()),
 8047                                window,
 8048                                cx,
 8049                                |selections| {
 8050                                    selections.select_anchor_ranges([target..target]);
 8051                                },
 8052                            );
 8053                            self.clear_row_highlights::<EditPredictionPreview>();
 8054                            self.edit_prediction_preview
 8055                                .set_previous_scroll_position(None);
 8056                        } else {
 8057                            // Highlight and request scroll
 8058                            self.edit_prediction_preview
 8059                                .set_previous_scroll_position(Some(
 8060                                    position_map.snapshot.scroll_anchor,
 8061                                ));
 8062                            self.highlight_rows::<EditPredictionPreview>(
 8063                                target..target,
 8064                                cx.theme().colors().editor_highlighted_line_background,
 8065                                RowHighlightOptions {
 8066                                    autoscroll: true,
 8067                                    ..Default::default()
 8068                                },
 8069                                cx,
 8070                            );
 8071                            self.request_autoscroll(Autoscroll::fit(), cx);
 8072                        }
 8073                    }
 8074                } else {
 8075                    self.change_selections(
 8076                        SelectionEffects::scroll(Autoscroll::newest()),
 8077                        window,
 8078                        cx,
 8079                        |selections| {
 8080                            selections.select_anchor_ranges([target..target]);
 8081                        },
 8082                    );
 8083                }
 8084            }
 8085            EditPrediction::MoveOutside { snapshot, target } => {
 8086                if let Some(workspace) = self.workspace() {
 8087                    Self::open_editor_at_anchor(snapshot, *target, &workspace, window, cx)
 8088                        .detach_and_log_err(cx);
 8089                }
 8090            }
 8091            EditPrediction::Edit {
 8092                edits,
 8093                cursor_position,
 8094                ..
 8095            } => {
 8096                self.report_edit_prediction_event(
 8097                    active_edit_prediction.completion_id.clone(),
 8098                    true,
 8099                    cx,
 8100                );
 8101
 8102                match granularity {
 8103                    EditPredictionGranularity::Full => {
 8104                        let transaction_id_prev = self.buffer.read(cx).last_transaction_id(cx);
 8105
 8106                        // Compute fallback cursor position BEFORE applying the edit,
 8107                        // so the anchor tracks through the edit correctly
 8108                        let fallback_cursor_target = {
 8109                            let snapshot = self.buffer.read(cx).snapshot(cx);
 8110                            edits.last().unwrap().0.end.bias_right(&snapshot)
 8111                        };
 8112
 8113                        self.buffer.update(cx, |buffer, cx| {
 8114                            buffer.edit(edits.iter().cloned(), None, cx)
 8115                        });
 8116
 8117                        if let Some(provider) = self.edit_prediction_provider() {
 8118                            provider.accept(cx);
 8119                        }
 8120
 8121                        // Resolve cursor position after the edit is applied
 8122                        let cursor_target = if let Some((anchor, offset)) = cursor_position {
 8123                            // The anchor tracks through the edit, then we add the offset
 8124                            let snapshot = self.buffer.read(cx).snapshot(cx);
 8125                            let base_offset = anchor.to_offset(&snapshot).0;
 8126                            let target_offset =
 8127                                MultiBufferOffset((base_offset + offset).min(snapshot.len().0));
 8128                            snapshot.anchor_after(target_offset)
 8129                        } else {
 8130                            fallback_cursor_target
 8131                        };
 8132
 8133                        self.change_selections(SelectionEffects::no_scroll(), window, cx, |s| {
 8134                            s.select_anchor_ranges([cursor_target..cursor_target]);
 8135                        });
 8136
 8137                        let selections = self.selections.disjoint_anchors_arc();
 8138                        if let Some(transaction_id_now) =
 8139                            self.buffer.read(cx).last_transaction_id(cx)
 8140                        {
 8141                            if transaction_id_prev != Some(transaction_id_now) {
 8142                                self.selection_history
 8143                                    .insert_transaction(transaction_id_now, selections);
 8144                            }
 8145                        }
 8146
 8147                        self.update_visible_edit_prediction(window, cx);
 8148                        if self.active_edit_prediction.is_none() {
 8149                            self.refresh_edit_prediction(true, true, window, cx);
 8150                        }
 8151                        cx.notify();
 8152                    }
 8153                    _ => {
 8154                        let snapshot = self.buffer.read(cx).snapshot(cx);
 8155                        let cursor_offset = self
 8156                            .selections
 8157                            .newest::<MultiBufferOffset>(&self.display_snapshot(cx))
 8158                            .head();
 8159
 8160                        let insertion = edits.iter().find_map(|(range, text)| {
 8161                            let range = range.to_offset(&snapshot);
 8162                            if range.is_empty() && range.start == cursor_offset {
 8163                                Some(text)
 8164                            } else {
 8165                                None
 8166                            }
 8167                        });
 8168
 8169                        if let Some(text) = insertion {
 8170                            let text_to_insert = match granularity {
 8171                                EditPredictionGranularity::Word => {
 8172                                    let mut partial = text
 8173                                        .chars()
 8174                                        .by_ref()
 8175                                        .take_while(|c| c.is_alphabetic())
 8176                                        .collect::<String>();
 8177                                    if partial.is_empty() {
 8178                                        partial = text
 8179                                            .chars()
 8180                                            .by_ref()
 8181                                            .take_while(|c| c.is_whitespace() || !c.is_alphabetic())
 8182                                            .collect::<String>();
 8183                                    }
 8184                                    partial
 8185                                }
 8186                                EditPredictionGranularity::Line => {
 8187                                    if let Some(line) = text.split_inclusive('\n').next() {
 8188                                        line.to_string()
 8189                                    } else {
 8190                                        text.to_string()
 8191                                    }
 8192                                }
 8193                                EditPredictionGranularity::Full => unreachable!(),
 8194                            };
 8195
 8196                            cx.emit(EditorEvent::InputHandled {
 8197                                utf16_range_to_replace: None,
 8198                                text: text_to_insert.clone().into(),
 8199                            });
 8200
 8201                            self.replace_selections(&text_to_insert, None, window, cx, false);
 8202                            self.refresh_edit_prediction(true, true, window, cx);
 8203                            cx.notify();
 8204                        } else {
 8205                            self.accept_partial_edit_prediction(
 8206                                EditPredictionGranularity::Full,
 8207                                window,
 8208                                cx,
 8209                            );
 8210                        }
 8211                    }
 8212                }
 8213            }
 8214        }
 8215
 8216        self.edit_prediction_requires_modifier_in_indent_conflict = false;
 8217    }
 8218
 8219    pub fn accept_next_word_edit_prediction(
 8220        &mut self,
 8221        _: &AcceptNextWordEditPrediction,
 8222        window: &mut Window,
 8223        cx: &mut Context<Self>,
 8224    ) {
 8225        self.accept_partial_edit_prediction(EditPredictionGranularity::Word, window, cx);
 8226    }
 8227
 8228    pub fn accept_next_line_edit_prediction(
 8229        &mut self,
 8230        _: &AcceptNextLineEditPrediction,
 8231        window: &mut Window,
 8232        cx: &mut Context<Self>,
 8233    ) {
 8234        self.accept_partial_edit_prediction(EditPredictionGranularity::Line, window, cx);
 8235    }
 8236
 8237    pub fn accept_edit_prediction(
 8238        &mut self,
 8239        _: &AcceptEditPrediction,
 8240        window: &mut Window,
 8241        cx: &mut Context<Self>,
 8242    ) {
 8243        self.accept_partial_edit_prediction(EditPredictionGranularity::Full, window, cx);
 8244    }
 8245
 8246    fn discard_edit_prediction(
 8247        &mut self,
 8248        reason: EditPredictionDiscardReason,
 8249        cx: &mut Context<Self>,
 8250    ) -> bool {
 8251        if reason == EditPredictionDiscardReason::Rejected {
 8252            let completion_id = self
 8253                .active_edit_prediction
 8254                .as_ref()
 8255                .and_then(|active_completion| active_completion.completion_id.clone());
 8256
 8257            self.report_edit_prediction_event(completion_id, false, cx);
 8258        }
 8259
 8260        if let Some(provider) = self.edit_prediction_provider() {
 8261            provider.discard(reason, cx);
 8262        }
 8263
 8264        self.take_active_edit_prediction(cx)
 8265    }
 8266
 8267    fn report_edit_prediction_event(&self, id: Option<SharedString>, accepted: bool, cx: &App) {
 8268        let Some(provider) = self.edit_prediction_provider() else {
 8269            return;
 8270        };
 8271
 8272        let Some((_, buffer, _)) = self
 8273            .buffer
 8274            .read(cx)
 8275            .excerpt_containing(self.selections.newest_anchor().head(), cx)
 8276        else {
 8277            return;
 8278        };
 8279
 8280        let extension = buffer
 8281            .read(cx)
 8282            .file()
 8283            .and_then(|file| Some(file.path().extension()?.to_string()));
 8284
 8285        let event_type = match accepted {
 8286            true => "Edit Prediction Accepted",
 8287            false => "Edit Prediction Discarded",
 8288        };
 8289        telemetry::event!(
 8290            event_type,
 8291            provider = provider.name(),
 8292            prediction_id = id,
 8293            suggestion_accepted = accepted,
 8294            file_extension = extension,
 8295        );
 8296    }
 8297
 8298    fn open_editor_at_anchor(
 8299        snapshot: &language::BufferSnapshot,
 8300        target: language::Anchor,
 8301        workspace: &Entity<Workspace>,
 8302        window: &mut Window,
 8303        cx: &mut App,
 8304    ) -> Task<Result<()>> {
 8305        workspace.update(cx, |workspace, cx| {
 8306            let path = snapshot.file().map(|file| file.full_path(cx));
 8307            let Some(path) =
 8308                path.and_then(|path| workspace.project().read(cx).find_project_path(path, cx))
 8309            else {
 8310                return Task::ready(Err(anyhow::anyhow!("Project path not found")));
 8311            };
 8312            let target = text::ToPoint::to_point(&target, snapshot);
 8313            let item = workspace.open_path(path, None, true, window, cx);
 8314            window.spawn(cx, async move |cx| {
 8315                let Some(editor) = item.await?.downcast::<Editor>() else {
 8316                    return Ok(());
 8317                };
 8318                editor
 8319                    .update_in(cx, |editor, window, cx| {
 8320                        editor.go_to_singleton_buffer_point(target, window, cx);
 8321                    })
 8322                    .ok();
 8323                anyhow::Ok(())
 8324            })
 8325        })
 8326    }
 8327
 8328    pub fn has_active_edit_prediction(&self) -> bool {
 8329        self.active_edit_prediction.is_some()
 8330    }
 8331
 8332    fn take_active_edit_prediction(&mut self, cx: &mut Context<Self>) -> bool {
 8333        let Some(active_edit_prediction) = self.active_edit_prediction.take() else {
 8334            return false;
 8335        };
 8336
 8337        self.splice_inlays(&active_edit_prediction.inlay_ids, Default::default(), cx);
 8338        self.clear_highlights(HighlightKey::EditPredictionHighlight, cx);
 8339        self.stale_edit_prediction_in_menu = Some(active_edit_prediction);
 8340        true
 8341    }
 8342
 8343    /// Returns true when we're displaying the edit prediction popover below the cursor
 8344    /// like we are not previewing and the LSP autocomplete menu is visible
 8345    /// or we are in `when_holding_modifier` mode.
 8346    pub fn edit_prediction_visible_in_cursor_popover(&self, has_completion: bool) -> bool {
 8347        if self.edit_prediction_preview_is_active()
 8348            || !self.show_edit_predictions_in_menu()
 8349            || !self.edit_predictions_enabled()
 8350        {
 8351            return false;
 8352        }
 8353
 8354        if self.has_visible_completions_menu() {
 8355            return true;
 8356        }
 8357
 8358        has_completion && self.edit_prediction_requires_modifier()
 8359    }
 8360
 8361    fn handle_modifiers_changed(
 8362        &mut self,
 8363        modifiers: Modifiers,
 8364        position_map: &PositionMap,
 8365        window: &mut Window,
 8366        cx: &mut Context<Self>,
 8367    ) {
 8368        // Ensure that the edit prediction preview is updated, even when not
 8369        // enabled, if there's an active edit prediction preview.
 8370        if self.show_edit_predictions_in_menu()
 8371            || matches!(
 8372                self.edit_prediction_preview,
 8373                EditPredictionPreview::Active { .. }
 8374            )
 8375        {
 8376            self.update_edit_prediction_preview(&modifiers, window, cx);
 8377        }
 8378
 8379        self.update_selection_mode(&modifiers, position_map, window, cx);
 8380
 8381        let mouse_position = window.mouse_position();
 8382        if !position_map.text_hitbox.is_hovered(window) {
 8383            return;
 8384        }
 8385
 8386        self.update_hovered_link(
 8387            position_map.point_for_position(mouse_position),
 8388            &position_map.snapshot,
 8389            modifiers,
 8390            window,
 8391            cx,
 8392        )
 8393    }
 8394
 8395    fn is_cmd_or_ctrl_pressed(modifiers: &Modifiers, cx: &mut Context<Self>) -> bool {
 8396        match EditorSettings::get_global(cx).multi_cursor_modifier {
 8397            MultiCursorModifier::Alt => modifiers.secondary(),
 8398            MultiCursorModifier::CmdOrCtrl => modifiers.alt,
 8399        }
 8400    }
 8401
 8402    fn is_alt_pressed(modifiers: &Modifiers, cx: &mut Context<Self>) -> bool {
 8403        match EditorSettings::get_global(cx).multi_cursor_modifier {
 8404            MultiCursorModifier::Alt => modifiers.alt,
 8405            MultiCursorModifier::CmdOrCtrl => modifiers.secondary(),
 8406        }
 8407    }
 8408
 8409    fn columnar_selection_mode(
 8410        modifiers: &Modifiers,
 8411        cx: &mut Context<Self>,
 8412    ) -> Option<ColumnarMode> {
 8413        if modifiers.shift && modifiers.number_of_modifiers() == 2 {
 8414            if Self::is_cmd_or_ctrl_pressed(modifiers, cx) {
 8415                Some(ColumnarMode::FromMouse)
 8416            } else if Self::is_alt_pressed(modifiers, cx) {
 8417                Some(ColumnarMode::FromSelection)
 8418            } else {
 8419                None
 8420            }
 8421        } else {
 8422            None
 8423        }
 8424    }
 8425
 8426    fn update_selection_mode(
 8427        &mut self,
 8428        modifiers: &Modifiers,
 8429        position_map: &PositionMap,
 8430        window: &mut Window,
 8431        cx: &mut Context<Self>,
 8432    ) {
 8433        let Some(mode) = Self::columnar_selection_mode(modifiers, cx) else {
 8434            return;
 8435        };
 8436        if self.selections.pending_anchor().is_none() {
 8437            return;
 8438        }
 8439
 8440        let mouse_position = window.mouse_position();
 8441        let point_for_position = position_map.point_for_position(mouse_position);
 8442        let position = point_for_position.previous_valid;
 8443
 8444        self.select(
 8445            SelectPhase::BeginColumnar {
 8446                position,
 8447                reset: false,
 8448                mode,
 8449                goal_column: point_for_position.exact_unclipped.column(),
 8450            },
 8451            window,
 8452            cx,
 8453        );
 8454    }
 8455
 8456    fn update_edit_prediction_preview(
 8457        &mut self,
 8458        modifiers: &Modifiers,
 8459        window: &mut Window,
 8460        cx: &mut Context<Self>,
 8461    ) {
 8462        let mut modifiers_held = false;
 8463
 8464        // Check bindings for all granularities.
 8465        // If the user holds the key for Word, Line, or Full, we want to show the preview.
 8466        let granularities = [
 8467            EditPredictionGranularity::Full,
 8468            EditPredictionGranularity::Line,
 8469            EditPredictionGranularity::Word,
 8470        ];
 8471
 8472        for granularity in granularities {
 8473            if let Some(keystroke) = self
 8474                .accept_edit_prediction_keybind(granularity, window, cx)
 8475                .keystroke()
 8476            {
 8477                modifiers_held = modifiers_held
 8478                    || (keystroke.modifiers() == modifiers && keystroke.modifiers().modified());
 8479            }
 8480        }
 8481
 8482        if modifiers_held {
 8483            if matches!(
 8484                self.edit_prediction_preview,
 8485                EditPredictionPreview::Inactive { .. }
 8486            ) {
 8487                self.edit_prediction_preview = EditPredictionPreview::Active {
 8488                    previous_scroll_position: None,
 8489                    since: Instant::now(),
 8490                };
 8491
 8492                self.update_visible_edit_prediction(window, cx);
 8493                cx.notify();
 8494            }
 8495        } else if let EditPredictionPreview::Active {
 8496            previous_scroll_position,
 8497            since,
 8498        } = self.edit_prediction_preview
 8499        {
 8500            if let (Some(previous_scroll_position), Some(position_map)) =
 8501                (previous_scroll_position, self.last_position_map.as_ref())
 8502            {
 8503                self.set_scroll_position(
 8504                    previous_scroll_position
 8505                        .scroll_position(&position_map.snapshot.display_snapshot),
 8506                    window,
 8507                    cx,
 8508                );
 8509            }
 8510
 8511            self.edit_prediction_preview = EditPredictionPreview::Inactive {
 8512                released_too_fast: since.elapsed() < Duration::from_millis(200),
 8513            };
 8514            self.clear_row_highlights::<EditPredictionPreview>();
 8515            self.update_visible_edit_prediction(window, cx);
 8516            cx.notify();
 8517        }
 8518    }
 8519
 8520    fn update_visible_edit_prediction(
 8521        &mut self,
 8522        _window: &mut Window,
 8523        cx: &mut Context<Self>,
 8524    ) -> Option<()> {
 8525        if self.ime_transaction.is_some() {
 8526            self.discard_edit_prediction(EditPredictionDiscardReason::Ignored, cx);
 8527            return None;
 8528        }
 8529
 8530        let selection = self.selections.newest_anchor();
 8531        let cursor = selection.head();
 8532        let multibuffer = self.buffer.read(cx).snapshot(cx);
 8533
 8534        // Check project-level disable_ai setting for the current buffer
 8535        if let Some((buffer, _)) = self.buffer.read(cx).text_anchor_for_position(cursor, cx) {
 8536            if DisableAiSettings::is_ai_disabled_for_buffer(Some(&buffer), cx) {
 8537                return None;
 8538            }
 8539        }
 8540        let offset_selection = selection.map(|endpoint| endpoint.to_offset(&multibuffer));
 8541        let excerpt_id = cursor.excerpt_id;
 8542
 8543        let show_in_menu = self.show_edit_predictions_in_menu();
 8544        let completions_menu_has_precedence = !show_in_menu
 8545            && (self.context_menu.borrow().is_some()
 8546                || (!self.completion_tasks.is_empty() && !self.has_active_edit_prediction()));
 8547
 8548        if completions_menu_has_precedence
 8549            || !offset_selection.is_empty()
 8550            || self
 8551                .active_edit_prediction
 8552                .as_ref()
 8553                .is_some_and(|completion| {
 8554                    let Some(invalidation_range) = completion.invalidation_range.as_ref() else {
 8555                        return false;
 8556                    };
 8557                    let invalidation_range = invalidation_range.to_offset(&multibuffer);
 8558                    let invalidation_range = invalidation_range.start..=invalidation_range.end;
 8559                    !invalidation_range.contains(&offset_selection.head())
 8560                })
 8561        {
 8562            self.discard_edit_prediction(EditPredictionDiscardReason::Ignored, cx);
 8563            return None;
 8564        }
 8565
 8566        self.take_active_edit_prediction(cx);
 8567        let Some(provider) = self.edit_prediction_provider() else {
 8568            self.edit_prediction_settings = EditPredictionSettings::Disabled;
 8569            return None;
 8570        };
 8571
 8572        let (buffer, cursor_buffer_position) =
 8573            self.buffer.read(cx).text_anchor_for_position(cursor, cx)?;
 8574
 8575        self.edit_prediction_settings =
 8576            self.edit_prediction_settings_at_position(&buffer, cursor_buffer_position, cx);
 8577
 8578        self.edit_prediction_indent_conflict = multibuffer.is_line_whitespace_upto(cursor);
 8579
 8580        if self.edit_prediction_indent_conflict {
 8581            let cursor_point = cursor.to_point(&multibuffer);
 8582            let mut suggested_indent = None;
 8583            multibuffer.suggested_indents_callback(
 8584                cursor_point.row..cursor_point.row + 1,
 8585                &mut |_, indent| {
 8586                    suggested_indent = Some(indent);
 8587                    ControlFlow::Break(())
 8588                },
 8589                cx,
 8590            );
 8591
 8592            if let Some(indent) = suggested_indent
 8593                && indent.len == cursor_point.column
 8594            {
 8595                self.edit_prediction_indent_conflict = false;
 8596            }
 8597        }
 8598
 8599        let edit_prediction = provider.suggest(&buffer, cursor_buffer_position, cx)?;
 8600
 8601        let (completion_id, edits, predicted_cursor_position, edit_preview) = match edit_prediction
 8602        {
 8603            edit_prediction_types::EditPrediction::Local {
 8604                id,
 8605                edits,
 8606                cursor_position,
 8607                edit_preview,
 8608            } => (id, edits, cursor_position, edit_preview),
 8609            edit_prediction_types::EditPrediction::Jump {
 8610                id,
 8611                snapshot,
 8612                target,
 8613            } => {
 8614                if let Some(provider) = &self.edit_prediction_provider {
 8615                    provider.provider.did_show(SuggestionDisplayType::Jump, cx);
 8616                }
 8617                self.stale_edit_prediction_in_menu = None;
 8618                self.active_edit_prediction = Some(EditPredictionState {
 8619                    inlay_ids: vec![],
 8620                    completion: EditPrediction::MoveOutside { snapshot, target },
 8621                    completion_id: id,
 8622                    invalidation_range: None,
 8623                });
 8624                cx.notify();
 8625                return Some(());
 8626            }
 8627        };
 8628
 8629        let edits = edits
 8630            .into_iter()
 8631            .flat_map(|(range, new_text)| {
 8632                Some((
 8633                    multibuffer.anchor_range_in_excerpt(excerpt_id, range)?,
 8634                    new_text,
 8635                ))
 8636            })
 8637            .collect::<Vec<_>>();
 8638        if edits.is_empty() {
 8639            return None;
 8640        }
 8641
 8642        let cursor_position = predicted_cursor_position.and_then(|predicted| {
 8643            let anchor = multibuffer.anchor_in_excerpt(excerpt_id, predicted.anchor)?;
 8644            Some((anchor, predicted.offset))
 8645        });
 8646
 8647        let first_edit_start = edits.first().unwrap().0.start;
 8648        let first_edit_start_point = first_edit_start.to_point(&multibuffer);
 8649        let edit_start_row = first_edit_start_point.row.saturating_sub(2);
 8650
 8651        let last_edit_end = edits.last().unwrap().0.end;
 8652        let last_edit_end_point = last_edit_end.to_point(&multibuffer);
 8653        let edit_end_row = cmp::min(multibuffer.max_point().row, last_edit_end_point.row + 2);
 8654
 8655        let cursor_row = cursor.to_point(&multibuffer).row;
 8656
 8657        let snapshot = multibuffer.buffer_for_excerpt(excerpt_id).cloned()?;
 8658
 8659        let mut inlay_ids = Vec::new();
 8660        let invalidation_row_range;
 8661        let move_invalidation_row_range = if cursor_row < edit_start_row {
 8662            Some(cursor_row..edit_end_row)
 8663        } else if cursor_row > edit_end_row {
 8664            Some(edit_start_row..cursor_row)
 8665        } else {
 8666            None
 8667        };
 8668        let supports_jump = self
 8669            .edit_prediction_provider
 8670            .as_ref()
 8671            .map(|provider| provider.provider.supports_jump_to_edit())
 8672            .unwrap_or(true);
 8673
 8674        let is_move = supports_jump
 8675            && (move_invalidation_row_range.is_some() || self.edit_predictions_hidden_for_vim_mode);
 8676        let completion = if is_move {
 8677            if let Some(provider) = &self.edit_prediction_provider {
 8678                provider.provider.did_show(SuggestionDisplayType::Jump, cx);
 8679            }
 8680            invalidation_row_range =
 8681                move_invalidation_row_range.unwrap_or(edit_start_row..edit_end_row);
 8682            let target = first_edit_start;
 8683            EditPrediction::MoveWithin { target, snapshot }
 8684        } else {
 8685            let show_completions_in_buffer = !self.edit_prediction_visible_in_cursor_popover(true)
 8686                && !self.edit_predictions_hidden_for_vim_mode;
 8687
 8688            let display_mode = if all_edits_insertions_or_deletions(&edits, &multibuffer) {
 8689                if provider.show_tab_accept_marker() {
 8690                    EditDisplayMode::TabAccept
 8691                } else {
 8692                    EditDisplayMode::Inline
 8693                }
 8694            } else {
 8695                EditDisplayMode::DiffPopover
 8696            };
 8697
 8698            if show_completions_in_buffer {
 8699                if let Some(provider) = &self.edit_prediction_provider {
 8700                    let suggestion_display_type = match display_mode {
 8701                        EditDisplayMode::DiffPopover => SuggestionDisplayType::DiffPopover,
 8702                        EditDisplayMode::Inline | EditDisplayMode::TabAccept => {
 8703                            SuggestionDisplayType::GhostText
 8704                        }
 8705                    };
 8706                    provider.provider.did_show(suggestion_display_type, cx);
 8707                }
 8708                if edits
 8709                    .iter()
 8710                    .all(|(range, _)| range.to_offset(&multibuffer).is_empty())
 8711                {
 8712                    let mut inlays = Vec::new();
 8713                    for (range, new_text) in &edits {
 8714                        let inlay = Inlay::edit_prediction(
 8715                            post_inc(&mut self.next_inlay_id),
 8716                            range.start,
 8717                            new_text.as_ref(),
 8718                        );
 8719                        inlay_ids.push(inlay.id);
 8720                        inlays.push(inlay);
 8721                    }
 8722
 8723                    self.splice_inlays(&[], inlays, cx);
 8724                } else {
 8725                    let background_color = cx.theme().status().deleted_background;
 8726                    self.highlight_text(
 8727                        HighlightKey::EditPredictionHighlight,
 8728                        edits.iter().map(|(range, _)| range.clone()).collect(),
 8729                        HighlightStyle {
 8730                            background_color: Some(background_color),
 8731                            ..Default::default()
 8732                        },
 8733                        cx,
 8734                    );
 8735                }
 8736            }
 8737
 8738            invalidation_row_range = edit_start_row..edit_end_row;
 8739
 8740            EditPrediction::Edit {
 8741                edits,
 8742                cursor_position,
 8743                edit_preview,
 8744                display_mode,
 8745                snapshot,
 8746            }
 8747        };
 8748
 8749        let invalidation_range = multibuffer
 8750            .anchor_before(Point::new(invalidation_row_range.start, 0))
 8751            ..multibuffer.anchor_after(Point::new(
 8752                invalidation_row_range.end,
 8753                multibuffer.line_len(MultiBufferRow(invalidation_row_range.end)),
 8754            ));
 8755
 8756        self.stale_edit_prediction_in_menu = None;
 8757        self.active_edit_prediction = Some(EditPredictionState {
 8758            inlay_ids,
 8759            completion,
 8760            completion_id,
 8761            invalidation_range: Some(invalidation_range),
 8762        });
 8763
 8764        cx.notify();
 8765
 8766        Some(())
 8767    }
 8768
 8769    pub fn edit_prediction_provider(&self) -> Option<Arc<dyn EditPredictionDelegateHandle>> {
 8770        Some(self.edit_prediction_provider.as_ref()?.provider.clone())
 8771    }
 8772
 8773    fn clear_tasks(&mut self) {
 8774        self.tasks.clear()
 8775    }
 8776
 8777    fn insert_tasks(&mut self, key: (BufferId, BufferRow), value: RunnableTasks) {
 8778        if self.tasks.insert(key, value).is_some() {
 8779            // This case should hopefully be rare, but just in case...
 8780            log::error!(
 8781                "multiple different run targets found on a single line, only the last target will be rendered"
 8782            )
 8783        }
 8784    }
 8785
 8786    /// Get all display points of breakpoints that will be rendered within editor
 8787    ///
 8788    /// This function is used to handle overlaps between breakpoints and Code action/runner symbol.
 8789    /// It's also used to set the color of line numbers with breakpoints to the breakpoint color.
 8790    /// TODO debugger: Use this function to color toggle symbols that house nested breakpoints
 8791    fn active_breakpoints(
 8792        &self,
 8793        range: Range<DisplayRow>,
 8794        window: &mut Window,
 8795        cx: &mut Context<Self>,
 8796    ) -> HashMap<DisplayRow, (Anchor, Breakpoint, Option<BreakpointSessionState>)> {
 8797        let mut breakpoint_display_points = HashMap::default();
 8798
 8799        let Some(breakpoint_store) = self.breakpoint_store.clone() else {
 8800            return breakpoint_display_points;
 8801        };
 8802
 8803        let snapshot = self.snapshot(window, cx);
 8804
 8805        let multi_buffer_snapshot = snapshot.buffer_snapshot();
 8806        let Some(project) = self.project() else {
 8807            return breakpoint_display_points;
 8808        };
 8809
 8810        let range = snapshot.display_point_to_point(DisplayPoint::new(range.start, 0), Bias::Left)
 8811            ..snapshot.display_point_to_point(DisplayPoint::new(range.end, 0), Bias::Right);
 8812
 8813        for (buffer_snapshot, range, excerpt_id) in
 8814            multi_buffer_snapshot.range_to_buffer_ranges(range.start..=range.end)
 8815        {
 8816            let Some(buffer) = project
 8817                .read(cx)
 8818                .buffer_for_id(buffer_snapshot.remote_id(), cx)
 8819            else {
 8820                continue;
 8821            };
 8822            let breakpoints = breakpoint_store.read(cx).breakpoints(
 8823                &buffer,
 8824                Some(
 8825                    buffer_snapshot.anchor_before(range.start)
 8826                        ..buffer_snapshot.anchor_after(range.end),
 8827                ),
 8828                buffer_snapshot,
 8829                cx,
 8830            );
 8831            for (breakpoint, state) in breakpoints {
 8832                let multi_buffer_anchor = Anchor::in_buffer(excerpt_id, breakpoint.position);
 8833                let position = multi_buffer_anchor
 8834                    .to_point(&multi_buffer_snapshot)
 8835                    .to_display_point(&snapshot);
 8836
 8837                breakpoint_display_points.insert(
 8838                    position.row(),
 8839                    (multi_buffer_anchor, breakpoint.bp.clone(), state),
 8840                );
 8841            }
 8842        }
 8843
 8844        breakpoint_display_points
 8845    }
 8846
 8847    fn breakpoint_context_menu(
 8848        &self,
 8849        anchor: Anchor,
 8850        window: &mut Window,
 8851        cx: &mut Context<Self>,
 8852    ) -> Entity<ui::ContextMenu> {
 8853        let weak_editor = cx.weak_entity();
 8854        let focus_handle = self.focus_handle(cx);
 8855
 8856        let row = self
 8857            .buffer
 8858            .read(cx)
 8859            .snapshot(cx)
 8860            .summary_for_anchor::<Point>(&anchor)
 8861            .row;
 8862
 8863        let breakpoint = self
 8864            .breakpoint_at_row(row, window, cx)
 8865            .map(|(anchor, bp)| (anchor, Arc::from(bp)));
 8866
 8867        let log_breakpoint_msg = if breakpoint.as_ref().is_some_and(|bp| bp.1.message.is_some()) {
 8868            "Edit Log Breakpoint"
 8869        } else {
 8870            "Set Log Breakpoint"
 8871        };
 8872
 8873        let condition_breakpoint_msg = if breakpoint
 8874            .as_ref()
 8875            .is_some_and(|bp| bp.1.condition.is_some())
 8876        {
 8877            "Edit Condition Breakpoint"
 8878        } else {
 8879            "Set Condition Breakpoint"
 8880        };
 8881
 8882        let hit_condition_breakpoint_msg = if breakpoint
 8883            .as_ref()
 8884            .is_some_and(|bp| bp.1.hit_condition.is_some())
 8885        {
 8886            "Edit Hit Condition Breakpoint"
 8887        } else {
 8888            "Set Hit Condition Breakpoint"
 8889        };
 8890
 8891        let set_breakpoint_msg = if breakpoint.as_ref().is_some() {
 8892            "Unset Breakpoint"
 8893        } else {
 8894            "Set Breakpoint"
 8895        };
 8896
 8897        let run_to_cursor = window.is_action_available(&RunToCursor, cx);
 8898
 8899        let toggle_state_msg = breakpoint.as_ref().map_or(None, |bp| match bp.1.state {
 8900            BreakpointState::Enabled => Some("Disable"),
 8901            BreakpointState::Disabled => Some("Enable"),
 8902        });
 8903
 8904        let (anchor, breakpoint) =
 8905            breakpoint.unwrap_or_else(|| (anchor, Arc::new(Breakpoint::new_standard())));
 8906
 8907        ui::ContextMenu::build(window, cx, |menu, _, _cx| {
 8908            menu.on_blur_subscription(Subscription::new(|| {}))
 8909                .context(focus_handle)
 8910                .when(run_to_cursor, |this| {
 8911                    let weak_editor = weak_editor.clone();
 8912                    this.entry("Run to Cursor", None, move |window, cx| {
 8913                        weak_editor
 8914                            .update(cx, |editor, cx| {
 8915                                editor.change_selections(
 8916                                    SelectionEffects::no_scroll(),
 8917                                    window,
 8918                                    cx,
 8919                                    |s| s.select_ranges([Point::new(row, 0)..Point::new(row, 0)]),
 8920                                );
 8921                            })
 8922                            .ok();
 8923
 8924                        window.dispatch_action(Box::new(RunToCursor), cx);
 8925                    })
 8926                    .separator()
 8927                })
 8928                .when_some(toggle_state_msg, |this, msg| {
 8929                    this.entry(msg, None, {
 8930                        let weak_editor = weak_editor.clone();
 8931                        let breakpoint = breakpoint.clone();
 8932                        move |_window, cx| {
 8933                            weak_editor
 8934                                .update(cx, |this, cx| {
 8935                                    this.edit_breakpoint_at_anchor(
 8936                                        anchor,
 8937                                        breakpoint.as_ref().clone(),
 8938                                        BreakpointEditAction::InvertState,
 8939                                        cx,
 8940                                    );
 8941                                })
 8942                                .log_err();
 8943                        }
 8944                    })
 8945                })
 8946                .entry(set_breakpoint_msg, None, {
 8947                    let weak_editor = weak_editor.clone();
 8948                    let breakpoint = breakpoint.clone();
 8949                    move |_window, cx| {
 8950                        weak_editor
 8951                            .update(cx, |this, cx| {
 8952                                this.edit_breakpoint_at_anchor(
 8953                                    anchor,
 8954                                    breakpoint.as_ref().clone(),
 8955                                    BreakpointEditAction::Toggle,
 8956                                    cx,
 8957                                );
 8958                            })
 8959                            .log_err();
 8960                    }
 8961                })
 8962                .entry(log_breakpoint_msg, None, {
 8963                    let breakpoint = breakpoint.clone();
 8964                    let weak_editor = weak_editor.clone();
 8965                    move |window, cx| {
 8966                        weak_editor
 8967                            .update(cx, |this, cx| {
 8968                                this.add_edit_breakpoint_block(
 8969                                    anchor,
 8970                                    breakpoint.as_ref(),
 8971                                    BreakpointPromptEditAction::Log,
 8972                                    window,
 8973                                    cx,
 8974                                );
 8975                            })
 8976                            .log_err();
 8977                    }
 8978                })
 8979                .entry(condition_breakpoint_msg, None, {
 8980                    let breakpoint = breakpoint.clone();
 8981                    let weak_editor = weak_editor.clone();
 8982                    move |window, cx| {
 8983                        weak_editor
 8984                            .update(cx, |this, cx| {
 8985                                this.add_edit_breakpoint_block(
 8986                                    anchor,
 8987                                    breakpoint.as_ref(),
 8988                                    BreakpointPromptEditAction::Condition,
 8989                                    window,
 8990                                    cx,
 8991                                );
 8992                            })
 8993                            .log_err();
 8994                    }
 8995                })
 8996                .entry(hit_condition_breakpoint_msg, None, move |window, cx| {
 8997                    weak_editor
 8998                        .update(cx, |this, cx| {
 8999                            this.add_edit_breakpoint_block(
 9000                                anchor,
 9001                                breakpoint.as_ref(),
 9002                                BreakpointPromptEditAction::HitCondition,
 9003                                window,
 9004                                cx,
 9005                            );
 9006                        })
 9007                        .log_err();
 9008                })
 9009        })
 9010    }
 9011
 9012    fn render_breakpoint(
 9013        &self,
 9014        position: Anchor,
 9015        row: DisplayRow,
 9016        breakpoint: &Breakpoint,
 9017        state: Option<BreakpointSessionState>,
 9018        cx: &mut Context<Self>,
 9019    ) -> IconButton {
 9020        let is_rejected = state.is_some_and(|s| !s.verified);
 9021        // Is it a breakpoint that shows up when hovering over gutter?
 9022        let (is_phantom, collides_with_existing) = self.gutter_breakpoint_indicator.0.map_or(
 9023            (false, false),
 9024            |PhantomBreakpointIndicator {
 9025                 is_active,
 9026                 display_row,
 9027                 collides_with_existing_breakpoint,
 9028             }| {
 9029                (
 9030                    is_active && display_row == row,
 9031                    collides_with_existing_breakpoint,
 9032                )
 9033            },
 9034        );
 9035
 9036        let (color, icon) = {
 9037            let icon = match (&breakpoint.message.is_some(), breakpoint.is_disabled()) {
 9038                (false, false) => ui::IconName::DebugBreakpoint,
 9039                (true, false) => ui::IconName::DebugLogBreakpoint,
 9040                (false, true) => ui::IconName::DebugDisabledBreakpoint,
 9041                (true, true) => ui::IconName::DebugDisabledLogBreakpoint,
 9042            };
 9043
 9044            let theme_colors = cx.theme().colors();
 9045
 9046            let color = if is_phantom {
 9047                if collides_with_existing {
 9048                    Color::Custom(
 9049                        theme_colors
 9050                            .debugger_accent
 9051                            .blend(theme_colors.text.opacity(0.6)),
 9052                    )
 9053                } else {
 9054                    Color::Hint
 9055                }
 9056            } else if is_rejected {
 9057                Color::Disabled
 9058            } else {
 9059                Color::Debugger
 9060            };
 9061
 9062            (color, icon)
 9063        };
 9064
 9065        let breakpoint = Arc::from(breakpoint.clone());
 9066
 9067        let alt_as_text = gpui::Keystroke {
 9068            modifiers: Modifiers::secondary_key(),
 9069            ..Default::default()
 9070        };
 9071        let primary_action_text = if breakpoint.is_disabled() {
 9072            "Enable breakpoint"
 9073        } else if is_phantom && !collides_with_existing {
 9074            "Set breakpoint"
 9075        } else {
 9076            "Unset breakpoint"
 9077        };
 9078        let focus_handle = self.focus_handle.clone();
 9079
 9080        let meta = if is_rejected {
 9081            SharedString::from("No executable code is associated with this line.")
 9082        } else if collides_with_existing && !breakpoint.is_disabled() {
 9083            SharedString::from(format!(
 9084                "{alt_as_text}-click to disable,\nright-click for more options."
 9085            ))
 9086        } else {
 9087            SharedString::from("Right-click for more options.")
 9088        };
 9089        IconButton::new(("breakpoint_indicator", row.0 as usize), icon)
 9090            .icon_size(IconSize::XSmall)
 9091            .size(ui::ButtonSize::None)
 9092            .when(is_rejected, |this| {
 9093                this.indicator(Indicator::icon(Icon::new(IconName::Warning)).color(Color::Warning))
 9094            })
 9095            .icon_color(color)
 9096            .style(ButtonStyle::Transparent)
 9097            .on_click(cx.listener({
 9098                move |editor, event: &ClickEvent, window, cx| {
 9099                    let edit_action = if event.modifiers().platform || breakpoint.is_disabled() {
 9100                        BreakpointEditAction::InvertState
 9101                    } else {
 9102                        BreakpointEditAction::Toggle
 9103                    };
 9104
 9105                    window.focus(&editor.focus_handle(cx), cx);
 9106                    editor.update_breakpoint_collision_on_toggle(row, &edit_action);
 9107                    editor.edit_breakpoint_at_anchor(
 9108                        position,
 9109                        breakpoint.as_ref().clone(),
 9110                        edit_action,
 9111                        cx,
 9112                    );
 9113                }
 9114            }))
 9115            .on_right_click(cx.listener(move |editor, event: &ClickEvent, window, cx| {
 9116                editor.set_breakpoint_context_menu(
 9117                    row,
 9118                    Some(position),
 9119                    event.position(),
 9120                    window,
 9121                    cx,
 9122                );
 9123            }))
 9124            .tooltip(move |_window, cx| {
 9125                Tooltip::with_meta_in(
 9126                    primary_action_text,
 9127                    Some(&ToggleBreakpoint),
 9128                    meta.clone(),
 9129                    &focus_handle,
 9130                    cx,
 9131                )
 9132            })
 9133    }
 9134
 9135    fn build_tasks_context(
 9136        project: &Entity<Project>,
 9137        buffer: &Entity<Buffer>,
 9138        buffer_row: u32,
 9139        tasks: &Arc<RunnableTasks>,
 9140        cx: &mut Context<Self>,
 9141    ) -> Task<Option<task::TaskContext>> {
 9142        let position = Point::new(buffer_row, tasks.column);
 9143        let range_start = buffer.read(cx).anchor_at(position, Bias::Right);
 9144        let location = Location {
 9145            buffer: buffer.clone(),
 9146            range: range_start..range_start,
 9147        };
 9148        // Fill in the environmental variables from the tree-sitter captures
 9149        let mut captured_task_variables = TaskVariables::default();
 9150        for (capture_name, value) in tasks.extra_variables.clone() {
 9151            captured_task_variables.insert(
 9152                task::VariableName::Custom(capture_name.into()),
 9153                value.clone(),
 9154            );
 9155        }
 9156        project.update(cx, |project, cx| {
 9157            project.task_store().update(cx, |task_store, cx| {
 9158                task_store.task_context_for_location(captured_task_variables, location, cx)
 9159            })
 9160        })
 9161    }
 9162
 9163    pub fn spawn_nearest_task(
 9164        &mut self,
 9165        action: &SpawnNearestTask,
 9166        window: &mut Window,
 9167        cx: &mut Context<Self>,
 9168    ) {
 9169        let Some((workspace, _)) = self.workspace.clone() else {
 9170            return;
 9171        };
 9172        let Some(project) = self.project.clone() else {
 9173            return;
 9174        };
 9175
 9176        // Try to find a closest, enclosing node using tree-sitter that has a task
 9177        let Some((buffer, buffer_row, tasks)) = self
 9178            .find_enclosing_node_task(cx)
 9179            // Or find the task that's closest in row-distance.
 9180            .or_else(|| self.find_closest_task(cx))
 9181        else {
 9182            return;
 9183        };
 9184
 9185        let reveal_strategy = action.reveal;
 9186        let task_context = Self::build_tasks_context(&project, &buffer, buffer_row, &tasks, cx);
 9187        cx.spawn_in(window, async move |_, cx| {
 9188            let context = task_context.await?;
 9189            let (task_source_kind, mut resolved_task) = tasks.resolve(&context).next()?;
 9190
 9191            let resolved = &mut resolved_task.resolved;
 9192            resolved.reveal = reveal_strategy;
 9193
 9194            workspace
 9195                .update_in(cx, |workspace, window, cx| {
 9196                    workspace.schedule_resolved_task(
 9197                        task_source_kind,
 9198                        resolved_task,
 9199                        false,
 9200                        window,
 9201                        cx,
 9202                    );
 9203                })
 9204                .ok()
 9205        })
 9206        .detach();
 9207    }
 9208
 9209    fn find_closest_task(
 9210        &mut self,
 9211        cx: &mut Context<Self>,
 9212    ) -> Option<(Entity<Buffer>, u32, Arc<RunnableTasks>)> {
 9213        let cursor_row = self
 9214            .selections
 9215            .newest_adjusted(&self.display_snapshot(cx))
 9216            .head()
 9217            .row;
 9218
 9219        let ((buffer_id, row), tasks) = self
 9220            .tasks
 9221            .iter()
 9222            .min_by_key(|((_, row), _)| cursor_row.abs_diff(*row))?;
 9223
 9224        let buffer = self.buffer.read(cx).buffer(*buffer_id)?;
 9225        let tasks = Arc::new(tasks.to_owned());
 9226        Some((buffer, *row, tasks))
 9227    }
 9228
 9229    fn find_enclosing_node_task(
 9230        &mut self,
 9231        cx: &mut Context<Self>,
 9232    ) -> Option<(Entity<Buffer>, u32, Arc<RunnableTasks>)> {
 9233        let snapshot = self.buffer.read(cx).snapshot(cx);
 9234        let offset = self
 9235            .selections
 9236            .newest::<MultiBufferOffset>(&self.display_snapshot(cx))
 9237            .head();
 9238        let mut excerpt = snapshot.excerpt_containing(offset..offset)?;
 9239        let offset = excerpt.map_offset_to_buffer(offset);
 9240        let buffer_id = excerpt.buffer().remote_id();
 9241
 9242        let layer = excerpt.buffer().syntax_layer_at(offset)?;
 9243        let mut cursor = layer.node().walk();
 9244
 9245        while cursor.goto_first_child_for_byte(offset.0).is_some() {
 9246            if cursor.node().end_byte() == offset.0 {
 9247                cursor.goto_next_sibling();
 9248            }
 9249        }
 9250
 9251        // Ascend to the smallest ancestor that contains the range and has a task.
 9252        loop {
 9253            let node = cursor.node();
 9254            let node_range = node.byte_range();
 9255            let symbol_start_row = excerpt.buffer().offset_to_point(node.start_byte()).row;
 9256
 9257            // Check if this node contains our offset
 9258            if node_range.start <= offset.0 && node_range.end >= offset.0 {
 9259                // If it contains offset, check for task
 9260                if let Some(tasks) = self.tasks.get(&(buffer_id, symbol_start_row)) {
 9261                    let buffer = self.buffer.read(cx).buffer(buffer_id)?;
 9262                    return Some((buffer, symbol_start_row, Arc::new(tasks.to_owned())));
 9263                }
 9264            }
 9265
 9266            if !cursor.goto_parent() {
 9267                break;
 9268            }
 9269        }
 9270        None
 9271    }
 9272
 9273    fn render_run_indicator(
 9274        &self,
 9275        _style: &EditorStyle,
 9276        is_active: bool,
 9277        row: DisplayRow,
 9278        breakpoint: Option<(Anchor, Breakpoint, Option<BreakpointSessionState>)>,
 9279        cx: &mut Context<Self>,
 9280    ) -> IconButton {
 9281        let color = Color::Muted;
 9282        let position = breakpoint.as_ref().map(|(anchor, _, _)| *anchor);
 9283
 9284        IconButton::new(
 9285            ("run_indicator", row.0 as usize),
 9286            ui::IconName::PlayOutlined,
 9287        )
 9288        .shape(ui::IconButtonShape::Square)
 9289        .icon_size(IconSize::XSmall)
 9290        .icon_color(color)
 9291        .toggle_state(is_active)
 9292        .on_click(cx.listener(move |editor, e: &ClickEvent, window, cx| {
 9293            let quick_launch = match e {
 9294                ClickEvent::Keyboard(_) => true,
 9295                ClickEvent::Mouse(e) => e.down.button == MouseButton::Left,
 9296            };
 9297
 9298            window.focus(&editor.focus_handle(cx), cx);
 9299            editor.toggle_code_actions(
 9300                &ToggleCodeActions {
 9301                    deployed_from: Some(CodeActionSource::RunMenu(row)),
 9302                    quick_launch,
 9303                },
 9304                window,
 9305                cx,
 9306            );
 9307        }))
 9308        .on_right_click(cx.listener(move |editor, event: &ClickEvent, window, cx| {
 9309            editor.set_breakpoint_context_menu(row, position, event.position(), window, cx);
 9310        }))
 9311    }
 9312
 9313    pub fn context_menu_visible(&self) -> bool {
 9314        !self.edit_prediction_preview_is_active()
 9315            && self
 9316                .context_menu
 9317                .borrow()
 9318                .as_ref()
 9319                .is_some_and(|menu| menu.visible())
 9320    }
 9321
 9322    pub fn context_menu_origin(&self) -> Option<ContextMenuOrigin> {
 9323        self.context_menu
 9324            .borrow()
 9325            .as_ref()
 9326            .map(|menu| menu.origin())
 9327    }
 9328
 9329    pub fn set_context_menu_options(&mut self, options: ContextMenuOptions) {
 9330        self.context_menu_options = Some(options);
 9331    }
 9332
 9333    const EDIT_PREDICTION_POPOVER_PADDING_X: Pixels = px(24.);
 9334    const EDIT_PREDICTION_POPOVER_PADDING_Y: Pixels = px(2.);
 9335
 9336    fn render_edit_prediction_popover(
 9337        &mut self,
 9338        text_bounds: &Bounds<Pixels>,
 9339        content_origin: gpui::Point<Pixels>,
 9340        right_margin: Pixels,
 9341        editor_snapshot: &EditorSnapshot,
 9342        visible_row_range: Range<DisplayRow>,
 9343        scroll_top: ScrollOffset,
 9344        scroll_bottom: ScrollOffset,
 9345        line_layouts: &[LineWithInvisibles],
 9346        line_height: Pixels,
 9347        scroll_position: gpui::Point<ScrollOffset>,
 9348        scroll_pixel_position: gpui::Point<ScrollPixelOffset>,
 9349        newest_selection_head: Option<DisplayPoint>,
 9350        editor_width: Pixels,
 9351        style: &EditorStyle,
 9352        window: &mut Window,
 9353        cx: &mut App,
 9354    ) -> Option<(AnyElement, gpui::Point<Pixels>)> {
 9355        if self.mode().is_minimap() {
 9356            return None;
 9357        }
 9358        let active_edit_prediction = self.active_edit_prediction.as_ref()?;
 9359
 9360        if self.edit_prediction_visible_in_cursor_popover(true) {
 9361            return None;
 9362        }
 9363
 9364        match &active_edit_prediction.completion {
 9365            EditPrediction::MoveWithin { target, .. } => {
 9366                let target_display_point = target.to_display_point(editor_snapshot);
 9367
 9368                if self.edit_prediction_requires_modifier() {
 9369                    if !self.edit_prediction_preview_is_active() {
 9370                        return None;
 9371                    }
 9372
 9373                    self.render_edit_prediction_modifier_jump_popover(
 9374                        text_bounds,
 9375                        content_origin,
 9376                        visible_row_range,
 9377                        line_layouts,
 9378                        line_height,
 9379                        scroll_pixel_position,
 9380                        newest_selection_head,
 9381                        target_display_point,
 9382                        window,
 9383                        cx,
 9384                    )
 9385                } else {
 9386                    self.render_edit_prediction_eager_jump_popover(
 9387                        text_bounds,
 9388                        content_origin,
 9389                        editor_snapshot,
 9390                        visible_row_range,
 9391                        scroll_top,
 9392                        scroll_bottom,
 9393                        line_height,
 9394                        scroll_pixel_position,
 9395                        target_display_point,
 9396                        editor_width,
 9397                        window,
 9398                        cx,
 9399                    )
 9400                }
 9401            }
 9402            EditPrediction::Edit {
 9403                display_mode: EditDisplayMode::Inline,
 9404                ..
 9405            } => None,
 9406            EditPrediction::Edit {
 9407                display_mode: EditDisplayMode::TabAccept,
 9408                edits,
 9409                ..
 9410            } => {
 9411                let range = &edits.first()?.0;
 9412                let target_display_point = range.end.to_display_point(editor_snapshot);
 9413
 9414                self.render_edit_prediction_end_of_line_popover(
 9415                    "Accept",
 9416                    editor_snapshot,
 9417                    visible_row_range,
 9418                    target_display_point,
 9419                    line_height,
 9420                    scroll_pixel_position,
 9421                    content_origin,
 9422                    editor_width,
 9423                    window,
 9424                    cx,
 9425                )
 9426            }
 9427            EditPrediction::Edit {
 9428                edits,
 9429                edit_preview,
 9430                display_mode: EditDisplayMode::DiffPopover,
 9431                snapshot,
 9432                ..
 9433            } => self.render_edit_prediction_diff_popover(
 9434                text_bounds,
 9435                content_origin,
 9436                right_margin,
 9437                editor_snapshot,
 9438                visible_row_range,
 9439                line_layouts,
 9440                line_height,
 9441                scroll_position,
 9442                scroll_pixel_position,
 9443                newest_selection_head,
 9444                editor_width,
 9445                style,
 9446                edits,
 9447                edit_preview,
 9448                snapshot,
 9449                window,
 9450                cx,
 9451            ),
 9452            EditPrediction::MoveOutside { snapshot, .. } => {
 9453                let mut element = self
 9454                    .render_edit_prediction_jump_outside_popover(snapshot, window, cx)
 9455                    .into_any();
 9456
 9457                let size = element.layout_as_root(AvailableSpace::min_size(), window, cx);
 9458                let origin_x = text_bounds.size.width - size.width - px(30.);
 9459                let origin = text_bounds.origin + gpui::Point::new(origin_x, px(16.));
 9460                element.prepaint_at(origin, window, cx);
 9461
 9462                Some((element, origin))
 9463            }
 9464        }
 9465    }
 9466
 9467    fn render_edit_prediction_modifier_jump_popover(
 9468        &mut self,
 9469        text_bounds: &Bounds<Pixels>,
 9470        content_origin: gpui::Point<Pixels>,
 9471        visible_row_range: Range<DisplayRow>,
 9472        line_layouts: &[LineWithInvisibles],
 9473        line_height: Pixels,
 9474        scroll_pixel_position: gpui::Point<ScrollPixelOffset>,
 9475        newest_selection_head: Option<DisplayPoint>,
 9476        target_display_point: DisplayPoint,
 9477        window: &mut Window,
 9478        cx: &mut App,
 9479    ) -> Option<(AnyElement, gpui::Point<Pixels>)> {
 9480        let scrolled_content_origin =
 9481            content_origin - gpui::Point::new(scroll_pixel_position.x.into(), Pixels::ZERO);
 9482
 9483        const SCROLL_PADDING_Y: Pixels = px(12.);
 9484
 9485        if target_display_point.row() < visible_row_range.start {
 9486            return self.render_edit_prediction_scroll_popover(
 9487                &|_| SCROLL_PADDING_Y,
 9488                IconName::ArrowUp,
 9489                visible_row_range,
 9490                line_layouts,
 9491                newest_selection_head,
 9492                scrolled_content_origin,
 9493                window,
 9494                cx,
 9495            );
 9496        } else if target_display_point.row() >= visible_row_range.end {
 9497            return self.render_edit_prediction_scroll_popover(
 9498                &|size| text_bounds.size.height - size.height - SCROLL_PADDING_Y,
 9499                IconName::ArrowDown,
 9500                visible_row_range,
 9501                line_layouts,
 9502                newest_selection_head,
 9503                scrolled_content_origin,
 9504                window,
 9505                cx,
 9506            );
 9507        }
 9508
 9509        const POLE_WIDTH: Pixels = px(2.);
 9510
 9511        let line_layout =
 9512            line_layouts.get(target_display_point.row().minus(visible_row_range.start) as usize)?;
 9513        let target_column = target_display_point.column() as usize;
 9514
 9515        let target_x = line_layout.x_for_index(target_column);
 9516        let target_y = (target_display_point.row().as_f64() * f64::from(line_height))
 9517            - scroll_pixel_position.y;
 9518
 9519        let flag_on_right = target_x < text_bounds.size.width / 2.;
 9520
 9521        let mut border_color = Self::edit_prediction_callout_popover_border_color(cx);
 9522        border_color.l += 0.001;
 9523
 9524        let mut element = v_flex()
 9525            .items_end()
 9526            .when(flag_on_right, |el| el.items_start())
 9527            .child(if flag_on_right {
 9528                self.render_edit_prediction_line_popover("Jump", None, window, cx)
 9529                    .rounded_bl(px(0.))
 9530                    .rounded_tl(px(0.))
 9531                    .border_l_2()
 9532                    .border_color(border_color)
 9533            } else {
 9534                self.render_edit_prediction_line_popover("Jump", None, window, cx)
 9535                    .rounded_br(px(0.))
 9536                    .rounded_tr(px(0.))
 9537                    .border_r_2()
 9538                    .border_color(border_color)
 9539            })
 9540            .child(div().w(POLE_WIDTH).bg(border_color).h(line_height))
 9541            .into_any();
 9542
 9543        let size = element.layout_as_root(AvailableSpace::min_size(), window, cx);
 9544
 9545        let mut origin = scrolled_content_origin + point(target_x, target_y.into())
 9546            - point(
 9547                if flag_on_right {
 9548                    POLE_WIDTH
 9549                } else {
 9550                    size.width - POLE_WIDTH
 9551                },
 9552                size.height - line_height,
 9553            );
 9554
 9555        origin.x = origin.x.max(content_origin.x);
 9556
 9557        element.prepaint_at(origin, window, cx);
 9558
 9559        Some((element, origin))
 9560    }
 9561
 9562    fn render_edit_prediction_scroll_popover(
 9563        &mut self,
 9564        to_y: &dyn Fn(Size<Pixels>) -> Pixels,
 9565        scroll_icon: IconName,
 9566        visible_row_range: Range<DisplayRow>,
 9567        line_layouts: &[LineWithInvisibles],
 9568        newest_selection_head: Option<DisplayPoint>,
 9569        scrolled_content_origin: gpui::Point<Pixels>,
 9570        window: &mut Window,
 9571        cx: &mut App,
 9572    ) -> Option<(AnyElement, gpui::Point<Pixels>)> {
 9573        let mut element = self
 9574            .render_edit_prediction_line_popover("Scroll", Some(scroll_icon), window, cx)
 9575            .into_any();
 9576
 9577        let size = element.layout_as_root(AvailableSpace::min_size(), window, cx);
 9578
 9579        let cursor = newest_selection_head?;
 9580        let cursor_row_layout =
 9581            line_layouts.get(cursor.row().minus(visible_row_range.start) as usize)?;
 9582        let cursor_column = cursor.column() as usize;
 9583
 9584        let cursor_character_x = cursor_row_layout.x_for_index(cursor_column);
 9585
 9586        let origin = scrolled_content_origin + point(cursor_character_x, to_y(size));
 9587
 9588        element.prepaint_at(origin, window, cx);
 9589        Some((element, origin))
 9590    }
 9591
 9592    fn render_edit_prediction_eager_jump_popover(
 9593        &mut self,
 9594        text_bounds: &Bounds<Pixels>,
 9595        content_origin: gpui::Point<Pixels>,
 9596        editor_snapshot: &EditorSnapshot,
 9597        visible_row_range: Range<DisplayRow>,
 9598        scroll_top: ScrollOffset,
 9599        scroll_bottom: ScrollOffset,
 9600        line_height: Pixels,
 9601        scroll_pixel_position: gpui::Point<ScrollPixelOffset>,
 9602        target_display_point: DisplayPoint,
 9603        editor_width: Pixels,
 9604        window: &mut Window,
 9605        cx: &mut App,
 9606    ) -> Option<(AnyElement, gpui::Point<Pixels>)> {
 9607        if target_display_point.row().as_f64() < scroll_top {
 9608            let mut element = self
 9609                .render_edit_prediction_line_popover(
 9610                    "Jump to Edit",
 9611                    Some(IconName::ArrowUp),
 9612                    window,
 9613                    cx,
 9614                )
 9615                .into_any();
 9616
 9617            let size = element.layout_as_root(AvailableSpace::min_size(), window, cx);
 9618            let offset = point(
 9619                (text_bounds.size.width - size.width) / 2.,
 9620                Self::EDIT_PREDICTION_POPOVER_PADDING_Y,
 9621            );
 9622
 9623            let origin = text_bounds.origin + offset;
 9624            element.prepaint_at(origin, window, cx);
 9625            Some((element, origin))
 9626        } else if (target_display_point.row().as_f64() + 1.) > scroll_bottom {
 9627            let mut element = self
 9628                .render_edit_prediction_line_popover(
 9629                    "Jump to Edit",
 9630                    Some(IconName::ArrowDown),
 9631                    window,
 9632                    cx,
 9633                )
 9634                .into_any();
 9635
 9636            let size = element.layout_as_root(AvailableSpace::min_size(), window, cx);
 9637            let offset = point(
 9638                (text_bounds.size.width - size.width) / 2.,
 9639                text_bounds.size.height - size.height - Self::EDIT_PREDICTION_POPOVER_PADDING_Y,
 9640            );
 9641
 9642            let origin = text_bounds.origin + offset;
 9643            element.prepaint_at(origin, window, cx);
 9644            Some((element, origin))
 9645        } else {
 9646            self.render_edit_prediction_end_of_line_popover(
 9647                "Jump to Edit",
 9648                editor_snapshot,
 9649                visible_row_range,
 9650                target_display_point,
 9651                line_height,
 9652                scroll_pixel_position,
 9653                content_origin,
 9654                editor_width,
 9655                window,
 9656                cx,
 9657            )
 9658        }
 9659    }
 9660
 9661    fn render_edit_prediction_end_of_line_popover(
 9662        self: &mut Editor,
 9663        label: &'static str,
 9664        editor_snapshot: &EditorSnapshot,
 9665        visible_row_range: Range<DisplayRow>,
 9666        target_display_point: DisplayPoint,
 9667        line_height: Pixels,
 9668        scroll_pixel_position: gpui::Point<ScrollPixelOffset>,
 9669        content_origin: gpui::Point<Pixels>,
 9670        editor_width: Pixels,
 9671        window: &mut Window,
 9672        cx: &mut App,
 9673    ) -> Option<(AnyElement, gpui::Point<Pixels>)> {
 9674        let target_line_end = DisplayPoint::new(
 9675            target_display_point.row(),
 9676            editor_snapshot.line_len(target_display_point.row()),
 9677        );
 9678
 9679        let mut element = self
 9680            .render_edit_prediction_line_popover(label, None, window, cx)
 9681            .into_any();
 9682
 9683        let size = element.layout_as_root(AvailableSpace::min_size(), window, cx);
 9684
 9685        let line_origin =
 9686            self.display_to_pixel_point(target_line_end, editor_snapshot, window, cx)?;
 9687
 9688        let start_point = content_origin - point(scroll_pixel_position.x.into(), Pixels::ZERO);
 9689        let mut origin = start_point
 9690            + line_origin
 9691            + point(Self::EDIT_PREDICTION_POPOVER_PADDING_X, Pixels::ZERO);
 9692        origin.x = origin.x.max(content_origin.x);
 9693
 9694        let max_x = content_origin.x + editor_width - size.width;
 9695
 9696        if origin.x > max_x {
 9697            let offset = line_height + Self::EDIT_PREDICTION_POPOVER_PADDING_Y;
 9698
 9699            let icon = if visible_row_range.contains(&(target_display_point.row() + 2)) {
 9700                origin.y += offset;
 9701                IconName::ArrowUp
 9702            } else {
 9703                origin.y -= offset;
 9704                IconName::ArrowDown
 9705            };
 9706
 9707            element = self
 9708                .render_edit_prediction_line_popover(label, Some(icon), window, cx)
 9709                .into_any();
 9710
 9711            let size = element.layout_as_root(AvailableSpace::min_size(), window, cx);
 9712
 9713            origin.x = content_origin.x + editor_width - size.width - px(2.);
 9714        }
 9715
 9716        element.prepaint_at(origin, window, cx);
 9717        Some((element, origin))
 9718    }
 9719
 9720    fn render_edit_prediction_diff_popover(
 9721        self: &Editor,
 9722        text_bounds: &Bounds<Pixels>,
 9723        content_origin: gpui::Point<Pixels>,
 9724        right_margin: Pixels,
 9725        editor_snapshot: &EditorSnapshot,
 9726        visible_row_range: Range<DisplayRow>,
 9727        line_layouts: &[LineWithInvisibles],
 9728        line_height: Pixels,
 9729        scroll_position: gpui::Point<ScrollOffset>,
 9730        scroll_pixel_position: gpui::Point<ScrollPixelOffset>,
 9731        newest_selection_head: Option<DisplayPoint>,
 9732        editor_width: Pixels,
 9733        style: &EditorStyle,
 9734        edits: &Vec<(Range<Anchor>, Arc<str>)>,
 9735        edit_preview: &Option<language::EditPreview>,
 9736        snapshot: &language::BufferSnapshot,
 9737        window: &mut Window,
 9738        cx: &mut App,
 9739    ) -> Option<(AnyElement, gpui::Point<Pixels>)> {
 9740        let edit_start = edits
 9741            .first()
 9742            .unwrap()
 9743            .0
 9744            .start
 9745            .to_display_point(editor_snapshot);
 9746        let edit_end = edits
 9747            .last()
 9748            .unwrap()
 9749            .0
 9750            .end
 9751            .to_display_point(editor_snapshot);
 9752
 9753        let is_visible = visible_row_range.contains(&edit_start.row())
 9754            || visible_row_range.contains(&edit_end.row());
 9755        if !is_visible {
 9756            return None;
 9757        }
 9758
 9759        let highlighted_edits = if let Some(edit_preview) = edit_preview.as_ref() {
 9760            crate::edit_prediction_edit_text(snapshot, edits, edit_preview, false, cx)
 9761        } else {
 9762            // Fallback for providers without edit_preview
 9763            crate::edit_prediction_fallback_text(edits, cx)
 9764        };
 9765
 9766        let styled_text = highlighted_edits.to_styled_text(&style.text);
 9767        let line_count = highlighted_edits.text.lines().count();
 9768
 9769        const BORDER_WIDTH: Pixels = px(1.);
 9770
 9771        let keybind = self.render_edit_prediction_accept_keybind(window, cx);
 9772        let has_keybind = keybind.is_some();
 9773
 9774        let mut element = h_flex()
 9775            .items_start()
 9776            .child(
 9777                h_flex()
 9778                    .bg(cx.theme().colors().editor_background)
 9779                    .border(BORDER_WIDTH)
 9780                    .shadow_xs()
 9781                    .border_color(cx.theme().colors().border)
 9782                    .rounded_l_lg()
 9783                    .when(line_count > 1, |el| el.rounded_br_lg())
 9784                    .pr_1()
 9785                    .child(styled_text),
 9786            )
 9787            .child(
 9788                h_flex()
 9789                    .h(line_height + BORDER_WIDTH * 2.)
 9790                    .px_1p5()
 9791                    .gap_1()
 9792                    // Workaround: For some reason, there's a gap if we don't do this
 9793                    .ml(-BORDER_WIDTH)
 9794                    .shadow(vec![gpui::BoxShadow {
 9795                        color: gpui::black().opacity(0.05),
 9796                        offset: point(px(1.), px(1.)),
 9797                        blur_radius: px(2.),
 9798                        spread_radius: px(0.),
 9799                    }])
 9800                    .bg(Editor::edit_prediction_line_popover_bg_color(cx))
 9801                    .border(BORDER_WIDTH)
 9802                    .border_color(cx.theme().colors().border)
 9803                    .rounded_r_lg()
 9804                    .id("edit_prediction_diff_popover_keybind")
 9805                    .when(!has_keybind, |el| {
 9806                        let status_colors = cx.theme().status();
 9807
 9808                        el.bg(status_colors.error_background)
 9809                            .border_color(status_colors.error.opacity(0.6))
 9810                            .child(Icon::new(IconName::Info).color(Color::Error))
 9811                            .cursor_default()
 9812                            .hoverable_tooltip(move |_window, cx| {
 9813                                cx.new(|_| MissingEditPredictionKeybindingTooltip).into()
 9814                            })
 9815                    })
 9816                    .children(keybind),
 9817            )
 9818            .into_any();
 9819
 9820        let longest_row =
 9821            editor_snapshot.longest_row_in_range(edit_start.row()..edit_end.row() + 1);
 9822        let longest_line_width = if visible_row_range.contains(&longest_row) {
 9823            line_layouts[(longest_row.0 - visible_row_range.start.0) as usize].width
 9824        } else {
 9825            layout_line(
 9826                longest_row,
 9827                editor_snapshot,
 9828                style,
 9829                editor_width,
 9830                |_| false,
 9831                window,
 9832                cx,
 9833            )
 9834            .width
 9835        };
 9836
 9837        let viewport_bounds =
 9838            Bounds::new(Default::default(), window.viewport_size()).extend(Edges {
 9839                right: -right_margin,
 9840                ..Default::default()
 9841            });
 9842
 9843        let x_after_longest = Pixels::from(
 9844            ScrollPixelOffset::from(
 9845                text_bounds.origin.x + longest_line_width + Self::EDIT_PREDICTION_POPOVER_PADDING_X,
 9846            ) - scroll_pixel_position.x,
 9847        );
 9848
 9849        let element_bounds = element.layout_as_root(AvailableSpace::min_size(), window, cx);
 9850
 9851        // Fully visible if it can be displayed within the window (allow overlapping other
 9852        // panes). However, this is only allowed if the popover starts within text_bounds.
 9853        let can_position_to_the_right = x_after_longest < text_bounds.right()
 9854            && x_after_longest + element_bounds.width < viewport_bounds.right();
 9855
 9856        let mut origin = if can_position_to_the_right {
 9857            point(
 9858                x_after_longest,
 9859                text_bounds.origin.y
 9860                    + Pixels::from(
 9861                        edit_start.row().as_f64() * ScrollPixelOffset::from(line_height)
 9862                            - scroll_pixel_position.y,
 9863                    ),
 9864            )
 9865        } else {
 9866            let cursor_row = newest_selection_head.map(|head| head.row());
 9867            let above_edit = edit_start
 9868                .row()
 9869                .0
 9870                .checked_sub(line_count as u32)
 9871                .map(DisplayRow);
 9872            let below_edit = Some(edit_end.row() + 1);
 9873            let above_cursor =
 9874                cursor_row.and_then(|row| row.0.checked_sub(line_count as u32).map(DisplayRow));
 9875            let below_cursor = cursor_row.map(|cursor_row| cursor_row + 1);
 9876
 9877            // Place the edit popover adjacent to the edit if there is a location
 9878            // available that is onscreen and does not obscure the cursor. Otherwise,
 9879            // place it adjacent to the cursor.
 9880            let row_target = [above_edit, below_edit, above_cursor, below_cursor]
 9881                .into_iter()
 9882                .flatten()
 9883                .find(|&start_row| {
 9884                    let end_row = start_row + line_count as u32;
 9885                    visible_row_range.contains(&start_row)
 9886                        && visible_row_range.contains(&end_row)
 9887                        && cursor_row
 9888                            .is_none_or(|cursor_row| !((start_row..end_row).contains(&cursor_row)))
 9889                })?;
 9890
 9891            content_origin
 9892                + point(
 9893                    Pixels::from(-scroll_pixel_position.x),
 9894                    Pixels::from(
 9895                        (row_target.as_f64() - scroll_position.y) * f64::from(line_height),
 9896                    ),
 9897                )
 9898        };
 9899
 9900        origin.x -= BORDER_WIDTH;
 9901
 9902        window.with_content_mask(
 9903            Some(gpui::ContentMask {
 9904                bounds: *text_bounds,
 9905            }),
 9906            |window| {
 9907                window.defer_draw(element, origin, 1, Some(window.content_mask()));
 9908            },
 9909        );
 9910
 9911        // Do not return an element, since it will already be drawn due to defer_draw.
 9912        None
 9913    }
 9914
 9915    fn edit_prediction_cursor_popover_height(&self) -> Pixels {
 9916        px(30.)
 9917    }
 9918
 9919    fn current_user_player_color(&self, cx: &mut App) -> PlayerColor {
 9920        if self.read_only(cx) {
 9921            cx.theme().players().read_only()
 9922        } else {
 9923            self.style.as_ref().unwrap().local_player
 9924        }
 9925    }
 9926
 9927    fn render_edit_prediction_accept_keybind(
 9928        &self,
 9929        window: &mut Window,
 9930        cx: &mut App,
 9931    ) -> Option<AnyElement> {
 9932        let accept_binding =
 9933            self.accept_edit_prediction_keybind(EditPredictionGranularity::Full, window, cx);
 9934        let accept_keystroke = accept_binding.keystroke()?;
 9935
 9936        let is_platform_style_mac = PlatformStyle::platform() == PlatformStyle::Mac;
 9937
 9938        let modifiers_color = if *accept_keystroke.modifiers() == window.modifiers() {
 9939            Color::Accent
 9940        } else {
 9941            Color::Muted
 9942        };
 9943
 9944        h_flex()
 9945            .px_0p5()
 9946            .when(is_platform_style_mac, |parent| parent.gap_0p5())
 9947            .font(theme::ThemeSettings::get_global(cx).buffer_font.clone())
 9948            .text_size(TextSize::XSmall.rems(cx))
 9949            .child(h_flex().children(ui::render_modifiers(
 9950                accept_keystroke.modifiers(),
 9951                PlatformStyle::platform(),
 9952                Some(modifiers_color),
 9953                Some(IconSize::XSmall.rems().into()),
 9954                true,
 9955            )))
 9956            .when(is_platform_style_mac, |parent| {
 9957                parent.child(accept_keystroke.key().to_string())
 9958            })
 9959            .when(!is_platform_style_mac, |parent| {
 9960                parent.child(
 9961                    Key::new(
 9962                        util::capitalize(accept_keystroke.key()),
 9963                        Some(Color::Default),
 9964                    )
 9965                    .size(Some(IconSize::XSmall.rems().into())),
 9966                )
 9967            })
 9968            .into_any()
 9969            .into()
 9970    }
 9971
 9972    fn render_edit_prediction_line_popover(
 9973        &self,
 9974        label: impl Into<SharedString>,
 9975        icon: Option<IconName>,
 9976        window: &mut Window,
 9977        cx: &mut App,
 9978    ) -> Stateful<Div> {
 9979        let padding_right = if icon.is_some() { px(4.) } else { px(8.) };
 9980
 9981        let keybind = self.render_edit_prediction_accept_keybind(window, cx);
 9982        let has_keybind = keybind.is_some();
 9983        let icons = Self::get_prediction_provider_icons(&self.edit_prediction_provider, cx);
 9984
 9985        h_flex()
 9986            .id("ep-line-popover")
 9987            .py_0p5()
 9988            .pl_1()
 9989            .pr(padding_right)
 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(label)
10011                    .size(LabelSize::Small)
10012                    .when(!has_keybind, |el| {
10013                        el.color(cx.theme().status().error.into()).strikethrough()
10014                    }),
10015            )
10016            .when(!has_keybind, |el| {
10017                el.child(
10018                    h_flex().ml_1().child(
10019                        Icon::new(IconName::Info)
10020                            .size(IconSize::Small)
10021                            .color(cx.theme().status().error.into()),
10022                    ),
10023                )
10024            })
10025            .when_some(icon, |element, icon| {
10026                element.child(
10027                    div()
10028                        .mt(px(1.5))
10029                        .child(Icon::new(icon).size(IconSize::Small)),
10030                )
10031            })
10032    }
10033
10034    fn render_edit_prediction_jump_outside_popover(
10035        &self,
10036        snapshot: &BufferSnapshot,
10037        window: &mut Window,
10038        cx: &mut App,
10039    ) -> Stateful<Div> {
10040        let keybind = self.render_edit_prediction_accept_keybind(window, cx);
10041        let has_keybind = keybind.is_some();
10042        let icons = Self::get_prediction_provider_icons(&self.edit_prediction_provider, cx);
10043
10044        let file_name = snapshot
10045            .file()
10046            .map(|file| SharedString::new(file.file_name(cx)))
10047            .unwrap_or(SharedString::new_static("untitled"));
10048
10049        h_flex()
10050            .id("ep-jump-outside-popover")
10051            .py_1()
10052            .px_2()
10053            .gap_1()
10054            .rounded_md()
10055            .border_1()
10056            .bg(Self::edit_prediction_line_popover_bg_color(cx))
10057            .border_color(Self::edit_prediction_callout_popover_border_color(cx))
10058            .shadow_xs()
10059            .when(!has_keybind, |el| {
10060                let status_colors = cx.theme().status();
10061
10062                el.bg(status_colors.error_background)
10063                    .border_color(status_colors.error.opacity(0.6))
10064                    .pl_2()
10065                    .child(Icon::new(icons.error).color(Color::Error))
10066                    .cursor_default()
10067                    .hoverable_tooltip(move |_window, cx| {
10068                        cx.new(|_| MissingEditPredictionKeybindingTooltip).into()
10069                    })
10070            })
10071            .children(keybind)
10072            .child(
10073                Label::new(file_name)
10074                    .size(LabelSize::Small)
10075                    .buffer_font(cx)
10076                    .when(!has_keybind, |el| {
10077                        el.color(cx.theme().status().error.into()).strikethrough()
10078                    }),
10079            )
10080            .when(!has_keybind, |el| {
10081                el.child(
10082                    h_flex().ml_1().child(
10083                        Icon::new(IconName::Info)
10084                            .size(IconSize::Small)
10085                            .color(cx.theme().status().error.into()),
10086                    ),
10087                )
10088            })
10089            .child(
10090                div()
10091                    .mt(px(1.5))
10092                    .child(Icon::new(IconName::ArrowUpRight).size(IconSize::Small)),
10093            )
10094    }
10095
10096    fn edit_prediction_line_popover_bg_color(cx: &App) -> Hsla {
10097        let accent_color = cx.theme().colors().text_accent;
10098        let editor_bg_color = cx.theme().colors().editor_background;
10099        editor_bg_color.blend(accent_color.opacity(0.1))
10100    }
10101
10102    fn edit_prediction_callout_popover_border_color(cx: &App) -> Hsla {
10103        let accent_color = cx.theme().colors().text_accent;
10104        let editor_bg_color = cx.theme().colors().editor_background;
10105        editor_bg_color.blend(accent_color.opacity(0.6))
10106    }
10107    fn get_prediction_provider_icons(
10108        provider: &Option<RegisteredEditPredictionDelegate>,
10109        cx: &App,
10110    ) -> edit_prediction_types::EditPredictionIconSet {
10111        match provider {
10112            Some(provider) => provider.provider.icons(cx),
10113            None => edit_prediction_types::EditPredictionIconSet::new(IconName::ZedPredict),
10114        }
10115    }
10116
10117    fn render_edit_prediction_cursor_popover(
10118        &self,
10119        min_width: Pixels,
10120        max_width: Pixels,
10121        cursor_point: Point,
10122        style: &EditorStyle,
10123        accept_keystroke: Option<&gpui::KeybindingKeystroke>,
10124        _window: &Window,
10125        cx: &mut Context<Editor>,
10126    ) -> Option<AnyElement> {
10127        let provider = self.edit_prediction_provider.as_ref()?;
10128        let icons = Self::get_prediction_provider_icons(&self.edit_prediction_provider, cx);
10129
10130        let is_refreshing = provider.provider.is_refreshing(cx);
10131
10132        fn pending_completion_container(icon: IconName) -> Div {
10133            h_flex().h_full().flex_1().gap_2().child(Icon::new(icon))
10134        }
10135
10136        let completion = match &self.active_edit_prediction {
10137            Some(prediction) => {
10138                if !self.has_visible_completions_menu() {
10139                    const RADIUS: Pixels = px(6.);
10140                    const BORDER_WIDTH: Pixels = px(1.);
10141
10142                    return Some(
10143                        h_flex()
10144                            .elevation_2(cx)
10145                            .border(BORDER_WIDTH)
10146                            .border_color(cx.theme().colors().border)
10147                            .when(accept_keystroke.is_none(), |el| {
10148                                el.border_color(cx.theme().status().error)
10149                            })
10150                            .rounded(RADIUS)
10151                            .rounded_tl(px(0.))
10152                            .overflow_hidden()
10153                            .child(div().px_1p5().child(match &prediction.completion {
10154                                EditPrediction::MoveWithin { target, snapshot } => {
10155                                    use text::ToPoint as _;
10156                                    if target.text_anchor.to_point(snapshot).row > cursor_point.row
10157                                    {
10158                                        Icon::new(icons.down)
10159                                    } else {
10160                                        Icon::new(icons.up)
10161                                    }
10162                                }
10163                                EditPrediction::MoveOutside { .. } => {
10164                                    // TODO [zeta2] custom icon for external jump?
10165                                    Icon::new(icons.base)
10166                                }
10167                                EditPrediction::Edit { .. } => Icon::new(icons.base),
10168                            }))
10169                            .child(
10170                                h_flex()
10171                                    .gap_1()
10172                                    .py_1()
10173                                    .px_2()
10174                                    .rounded_r(RADIUS - BORDER_WIDTH)
10175                                    .border_l_1()
10176                                    .border_color(cx.theme().colors().border)
10177                                    .bg(Self::edit_prediction_line_popover_bg_color(cx))
10178                                    .when(self.edit_prediction_preview.released_too_fast(), |el| {
10179                                        el.child(
10180                                            Label::new("Hold")
10181                                                .size(LabelSize::Small)
10182                                                .when(accept_keystroke.is_none(), |el| {
10183                                                    el.strikethrough()
10184                                                })
10185                                                .line_height_style(LineHeightStyle::UiLabel),
10186                                        )
10187                                    })
10188                                    .id("edit_prediction_cursor_popover_keybind")
10189                                    .when(accept_keystroke.is_none(), |el| {
10190                                        let status_colors = cx.theme().status();
10191
10192                                        el.bg(status_colors.error_background)
10193                                            .border_color(status_colors.error.opacity(0.6))
10194                                            .child(Icon::new(IconName::Info).color(Color::Error))
10195                                            .cursor_default()
10196                                            .hoverable_tooltip(move |_window, cx| {
10197                                                cx.new(|_| MissingEditPredictionKeybindingTooltip)
10198                                                    .into()
10199                                            })
10200                                    })
10201                                    .when_some(
10202                                        accept_keystroke.as_ref(),
10203                                        |el, accept_keystroke| {
10204                                            el.child(h_flex().children(ui::render_modifiers(
10205                                                accept_keystroke.modifiers(),
10206                                                PlatformStyle::platform(),
10207                                                Some(Color::Default),
10208                                                Some(IconSize::XSmall.rems().into()),
10209                                                false,
10210                                            )))
10211                                        },
10212                                    ),
10213                            )
10214                            .into_any(),
10215                    );
10216                }
10217
10218                self.render_edit_prediction_cursor_popover_preview(
10219                    prediction,
10220                    cursor_point,
10221                    style,
10222                    cx,
10223                )?
10224            }
10225
10226            None if is_refreshing => match &self.stale_edit_prediction_in_menu {
10227                Some(stale_completion) => self.render_edit_prediction_cursor_popover_preview(
10228                    stale_completion,
10229                    cursor_point,
10230                    style,
10231                    cx,
10232                )?,
10233
10234                None => pending_completion_container(icons.base)
10235                    .child(Label::new("...").size(LabelSize::Small)),
10236            },
10237
10238            None => pending_completion_container(icons.base)
10239                .child(Label::new("...").size(LabelSize::Small)),
10240        };
10241
10242        let completion = if is_refreshing || self.active_edit_prediction.is_none() {
10243            completion
10244                .with_animation(
10245                    "loading-completion",
10246                    Animation::new(Duration::from_secs(2))
10247                        .repeat()
10248                        .with_easing(pulsating_between(0.4, 0.8)),
10249                    |label, delta| label.opacity(delta),
10250                )
10251                .into_any_element()
10252        } else {
10253            completion.into_any_element()
10254        };
10255
10256        let has_completion = self.active_edit_prediction.is_some();
10257
10258        let is_platform_style_mac = PlatformStyle::platform() == PlatformStyle::Mac;
10259        Some(
10260            h_flex()
10261                .min_w(min_width)
10262                .max_w(max_width)
10263                .flex_1()
10264                .elevation_2(cx)
10265                .border_color(cx.theme().colors().border)
10266                .child(
10267                    div()
10268                        .flex_1()
10269                        .py_1()
10270                        .px_2()
10271                        .overflow_hidden()
10272                        .child(completion),
10273                )
10274                .when_some(accept_keystroke, |el, accept_keystroke| {
10275                    if !accept_keystroke.modifiers().modified() {
10276                        return el;
10277                    }
10278
10279                    el.child(
10280                        h_flex()
10281                            .h_full()
10282                            .border_l_1()
10283                            .rounded_r_lg()
10284                            .border_color(cx.theme().colors().border)
10285                            .bg(Self::edit_prediction_line_popover_bg_color(cx))
10286                            .gap_1()
10287                            .py_1()
10288                            .px_2()
10289                            .child(
10290                                h_flex()
10291                                    .font(theme::ThemeSettings::get_global(cx).buffer_font.clone())
10292                                    .when(is_platform_style_mac, |parent| parent.gap_1())
10293                                    .child(h_flex().children(ui::render_modifiers(
10294                                        accept_keystroke.modifiers(),
10295                                        PlatformStyle::platform(),
10296                                        Some(if !has_completion {
10297                                            Color::Muted
10298                                        } else {
10299                                            Color::Default
10300                                        }),
10301                                        None,
10302                                        false,
10303                                    ))),
10304                            )
10305                            .child(Label::new("Preview").into_any_element())
10306                            .opacity(if has_completion { 1.0 } else { 0.4 }),
10307                    )
10308                })
10309                .into_any(),
10310        )
10311    }
10312
10313    fn render_edit_prediction_cursor_popover_preview(
10314        &self,
10315        completion: &EditPredictionState,
10316        cursor_point: Point,
10317        style: &EditorStyle,
10318        cx: &mut Context<Editor>,
10319    ) -> Option<Div> {
10320        use text::ToPoint as _;
10321
10322        fn render_relative_row_jump(
10323            prefix: impl Into<String>,
10324            current_row: u32,
10325            target_row: u32,
10326        ) -> Div {
10327            let (row_diff, arrow) = if target_row < current_row {
10328                (current_row - target_row, IconName::ArrowUp)
10329            } else {
10330                (target_row - current_row, IconName::ArrowDown)
10331            };
10332
10333            h_flex()
10334                .child(
10335                    Label::new(format!("{}{}", prefix.into(), row_diff))
10336                        .color(Color::Muted)
10337                        .size(LabelSize::Small),
10338                )
10339                .child(Icon::new(arrow).color(Color::Muted).size(IconSize::Small))
10340        }
10341
10342        let supports_jump = self
10343            .edit_prediction_provider
10344            .as_ref()
10345            .map(|provider| provider.provider.supports_jump_to_edit())
10346            .unwrap_or(true);
10347
10348        let icons = Self::get_prediction_provider_icons(&self.edit_prediction_provider, cx);
10349
10350        match &completion.completion {
10351            EditPrediction::MoveWithin {
10352                target, snapshot, ..
10353            } => {
10354                if !supports_jump {
10355                    return None;
10356                }
10357
10358                Some(
10359                    h_flex()
10360                        .px_2()
10361                        .gap_2()
10362                        .flex_1()
10363                        .child(
10364                            if target.text_anchor.to_point(snapshot).row > cursor_point.row {
10365                                Icon::new(icons.down)
10366                            } else {
10367                                Icon::new(icons.up)
10368                            },
10369                        )
10370                        .child(Label::new("Jump to Edit")),
10371                )
10372            }
10373            EditPrediction::MoveOutside { snapshot, .. } => {
10374                let file_name = snapshot
10375                    .file()
10376                    .map(|file| file.file_name(cx))
10377                    .unwrap_or("untitled");
10378                Some(
10379                    h_flex()
10380                        .px_2()
10381                        .gap_2()
10382                        .flex_1()
10383                        .child(Icon::new(icons.base))
10384                        .child(Label::new(format!("Jump to {file_name}"))),
10385                )
10386            }
10387            EditPrediction::Edit {
10388                edits,
10389                edit_preview,
10390                snapshot,
10391                ..
10392            } => {
10393                let first_edit_row = edits.first()?.0.start.text_anchor.to_point(snapshot).row;
10394
10395                let (highlighted_edits, has_more_lines) =
10396                    if let Some(edit_preview) = edit_preview.as_ref() {
10397                        crate::edit_prediction_edit_text(snapshot, edits, edit_preview, true, cx)
10398                            .first_line_preview()
10399                    } else {
10400                        crate::edit_prediction_fallback_text(edits, cx).first_line_preview()
10401                    };
10402
10403                let styled_text = gpui::StyledText::new(highlighted_edits.text)
10404                    .with_default_highlights(&style.text, highlighted_edits.highlights);
10405
10406                let preview = h_flex()
10407                    .gap_1()
10408                    .min_w_16()
10409                    .child(styled_text)
10410                    .when(has_more_lines, |parent| parent.child(""));
10411
10412                let left = if supports_jump && first_edit_row != cursor_point.row {
10413                    render_relative_row_jump("", cursor_point.row, first_edit_row)
10414                        .into_any_element()
10415                } else {
10416                    Icon::new(icons.base).into_any_element()
10417                };
10418
10419                Some(
10420                    h_flex()
10421                        .h_full()
10422                        .flex_1()
10423                        .gap_2()
10424                        .pr_1()
10425                        .overflow_x_hidden()
10426                        .font(theme::ThemeSettings::get_global(cx).buffer_font.clone())
10427                        .child(left)
10428                        .child(preview),
10429                )
10430            }
10431        }
10432    }
10433
10434    pub fn render_context_menu(
10435        &mut self,
10436        max_height_in_lines: u32,
10437        window: &mut Window,
10438        cx: &mut Context<Editor>,
10439    ) -> Option<AnyElement> {
10440        let menu = self.context_menu.borrow();
10441        let menu = menu.as_ref()?;
10442        if !menu.visible() {
10443            return None;
10444        };
10445        self.style
10446            .as_ref()
10447            .map(|style| menu.render(style, max_height_in_lines, window, cx))
10448    }
10449
10450    fn render_context_menu_aside(
10451        &mut self,
10452        max_size: Size<Pixels>,
10453        window: &mut Window,
10454        cx: &mut Context<Editor>,
10455    ) -> Option<AnyElement> {
10456        self.context_menu.borrow_mut().as_mut().and_then(|menu| {
10457            if menu.visible() {
10458                menu.render_aside(max_size, window, cx)
10459            } else {
10460                None
10461            }
10462        })
10463    }
10464
10465    fn hide_context_menu(
10466        &mut self,
10467        window: &mut Window,
10468        cx: &mut Context<Self>,
10469    ) -> Option<CodeContextMenu> {
10470        cx.notify();
10471        self.completion_tasks.clear();
10472        let context_menu = self.context_menu.borrow_mut().take();
10473        self.stale_edit_prediction_in_menu.take();
10474        self.update_visible_edit_prediction(window, cx);
10475        if let Some(CodeContextMenu::Completions(_)) = &context_menu
10476            && let Some(completion_provider) = &self.completion_provider
10477        {
10478            completion_provider.selection_changed(None, window, cx);
10479        }
10480        context_menu
10481    }
10482
10483    fn show_snippet_choices(
10484        &mut self,
10485        choices: &Vec<String>,
10486        selection: Range<Anchor>,
10487        cx: &mut Context<Self>,
10488    ) {
10489        let Some((_, buffer, _)) = self
10490            .buffer()
10491            .read(cx)
10492            .excerpt_containing(selection.start, cx)
10493        else {
10494            return;
10495        };
10496        let Some((_, end_buffer, _)) = self.buffer().read(cx).excerpt_containing(selection.end, cx)
10497        else {
10498            return;
10499        };
10500        if buffer != end_buffer {
10501            log::error!("expected anchor range to have matching buffer IDs");
10502            return;
10503        }
10504
10505        let id = post_inc(&mut self.next_completion_id);
10506        let snippet_sort_order = EditorSettings::get_global(cx).snippet_sort_order;
10507        let mut context_menu = self.context_menu.borrow_mut();
10508        let old_menu = context_menu.take();
10509        *context_menu = Some(CodeContextMenu::Completions(
10510            CompletionsMenu::new_snippet_choices(
10511                id,
10512                true,
10513                choices,
10514                selection,
10515                buffer,
10516                old_menu.map(|menu| menu.primary_scroll_handle()),
10517                snippet_sort_order,
10518            ),
10519        ));
10520    }
10521
10522    pub fn insert_snippet(
10523        &mut self,
10524        insertion_ranges: &[Range<MultiBufferOffset>],
10525        snippet: Snippet,
10526        window: &mut Window,
10527        cx: &mut Context<Self>,
10528    ) -> Result<()> {
10529        struct Tabstop<T> {
10530            is_end_tabstop: bool,
10531            ranges: Vec<Range<T>>,
10532            choices: Option<Vec<String>>,
10533        }
10534
10535        let tabstops = self.buffer.update(cx, |buffer, cx| {
10536            let snippet_text: Arc<str> = snippet.text.clone().into();
10537            let edits = insertion_ranges
10538                .iter()
10539                .cloned()
10540                .map(|range| (range, snippet_text.clone()));
10541            let autoindent_mode = AutoindentMode::Block {
10542                original_indent_columns: Vec::new(),
10543            };
10544            buffer.edit(edits, Some(autoindent_mode), cx);
10545
10546            let snapshot = &*buffer.read(cx);
10547            let snippet = &snippet;
10548            snippet
10549                .tabstops
10550                .iter()
10551                .map(|tabstop| {
10552                    let is_end_tabstop = tabstop.ranges.first().is_some_and(|tabstop| {
10553                        tabstop.is_empty() && tabstop.start == snippet.text.len() as isize
10554                    });
10555                    let mut tabstop_ranges = tabstop
10556                        .ranges
10557                        .iter()
10558                        .flat_map(|tabstop_range| {
10559                            let mut delta = 0_isize;
10560                            insertion_ranges.iter().map(move |insertion_range| {
10561                                let insertion_start = insertion_range.start + delta;
10562                                delta += snippet.text.len() as isize
10563                                    - (insertion_range.end - insertion_range.start) as isize;
10564
10565                                let start =
10566                                    (insertion_start + tabstop_range.start).min(snapshot.len());
10567                                let end = (insertion_start + tabstop_range.end).min(snapshot.len());
10568                                snapshot.anchor_before(start)..snapshot.anchor_after(end)
10569                            })
10570                        })
10571                        .collect::<Vec<_>>();
10572                    tabstop_ranges.sort_unstable_by(|a, b| a.start.cmp(&b.start, snapshot));
10573
10574                    Tabstop {
10575                        is_end_tabstop,
10576                        ranges: tabstop_ranges,
10577                        choices: tabstop.choices.clone(),
10578                    }
10579                })
10580                .collect::<Vec<_>>()
10581        });
10582        if let Some(tabstop) = tabstops.first() {
10583            self.change_selections(Default::default(), window, cx, |s| {
10584                // Reverse order so that the first range is the newest created selection.
10585                // Completions will use it and autoscroll will prioritize it.
10586                s.select_ranges(tabstop.ranges.iter().rev().cloned());
10587            });
10588
10589            if let Some(choices) = &tabstop.choices
10590                && let Some(selection) = tabstop.ranges.first()
10591            {
10592                self.show_snippet_choices(choices, selection.clone(), cx)
10593            }
10594
10595            // If we're already at the last tabstop and it's at the end of the snippet,
10596            // we're done, we don't need to keep the state around.
10597            if !tabstop.is_end_tabstop {
10598                let choices = tabstops
10599                    .iter()
10600                    .map(|tabstop| tabstop.choices.clone())
10601                    .collect();
10602
10603                let ranges = tabstops
10604                    .into_iter()
10605                    .map(|tabstop| tabstop.ranges)
10606                    .collect::<Vec<_>>();
10607
10608                self.snippet_stack.push(SnippetState {
10609                    active_index: 0,
10610                    ranges,
10611                    choices,
10612                });
10613            }
10614
10615            // Check whether the just-entered snippet ends with an auto-closable bracket.
10616            if self.autoclose_regions.is_empty() {
10617                let snapshot = self.buffer.read(cx).snapshot(cx);
10618                for selection in &mut self.selections.all::<Point>(&self.display_snapshot(cx)) {
10619                    let selection_head = selection.head();
10620                    let Some(scope) = snapshot.language_scope_at(selection_head) else {
10621                        continue;
10622                    };
10623
10624                    let mut bracket_pair = None;
10625                    let max_lookup_length = scope
10626                        .brackets()
10627                        .map(|(pair, _)| {
10628                            pair.start
10629                                .as_str()
10630                                .chars()
10631                                .count()
10632                                .max(pair.end.as_str().chars().count())
10633                        })
10634                        .max();
10635                    if let Some(max_lookup_length) = max_lookup_length {
10636                        let next_text = snapshot
10637                            .chars_at(selection_head)
10638                            .take(max_lookup_length)
10639                            .collect::<String>();
10640                        let prev_text = snapshot
10641                            .reversed_chars_at(selection_head)
10642                            .take(max_lookup_length)
10643                            .collect::<String>();
10644
10645                        for (pair, enabled) in scope.brackets() {
10646                            if enabled
10647                                && pair.close
10648                                && prev_text.starts_with(pair.start.as_str())
10649                                && next_text.starts_with(pair.end.as_str())
10650                            {
10651                                bracket_pair = Some(pair.clone());
10652                                break;
10653                            }
10654                        }
10655                    }
10656
10657                    if let Some(pair) = bracket_pair {
10658                        let snapshot_settings = snapshot.language_settings_at(selection_head, cx);
10659                        let autoclose_enabled =
10660                            self.use_autoclose && snapshot_settings.use_autoclose;
10661                        if autoclose_enabled {
10662                            let start = snapshot.anchor_after(selection_head);
10663                            let end = snapshot.anchor_after(selection_head);
10664                            self.autoclose_regions.push(AutocloseRegion {
10665                                selection_id: selection.id,
10666                                range: start..end,
10667                                pair,
10668                            });
10669                        }
10670                    }
10671                }
10672            }
10673        }
10674        Ok(())
10675    }
10676
10677    pub fn move_to_next_snippet_tabstop(
10678        &mut self,
10679        window: &mut Window,
10680        cx: &mut Context<Self>,
10681    ) -> bool {
10682        self.move_to_snippet_tabstop(Bias::Right, window, cx)
10683    }
10684
10685    pub fn move_to_prev_snippet_tabstop(
10686        &mut self,
10687        window: &mut Window,
10688        cx: &mut Context<Self>,
10689    ) -> bool {
10690        self.move_to_snippet_tabstop(Bias::Left, window, cx)
10691    }
10692
10693    pub fn move_to_snippet_tabstop(
10694        &mut self,
10695        bias: Bias,
10696        window: &mut Window,
10697        cx: &mut Context<Self>,
10698    ) -> bool {
10699        if let Some(mut snippet) = self.snippet_stack.pop() {
10700            match bias {
10701                Bias::Left => {
10702                    if snippet.active_index > 0 {
10703                        snippet.active_index -= 1;
10704                    } else {
10705                        self.snippet_stack.push(snippet);
10706                        return false;
10707                    }
10708                }
10709                Bias::Right => {
10710                    if snippet.active_index + 1 < snippet.ranges.len() {
10711                        snippet.active_index += 1;
10712                    } else {
10713                        self.snippet_stack.push(snippet);
10714                        return false;
10715                    }
10716                }
10717            }
10718            if let Some(current_ranges) = snippet.ranges.get(snippet.active_index) {
10719                self.change_selections(Default::default(), window, cx, |s| {
10720                    // Reverse order so that the first range is the newest created selection.
10721                    // Completions will use it and autoscroll will prioritize it.
10722                    s.select_ranges(current_ranges.iter().rev().cloned())
10723                });
10724
10725                if let Some(choices) = &snippet.choices[snippet.active_index]
10726                    && let Some(selection) = current_ranges.first()
10727                {
10728                    self.show_snippet_choices(choices, selection.clone(), cx);
10729                }
10730
10731                // If snippet state is not at the last tabstop, push it back on the stack
10732                if snippet.active_index + 1 < snippet.ranges.len() {
10733                    self.snippet_stack.push(snippet);
10734                }
10735                return true;
10736            }
10737        }
10738
10739        false
10740    }
10741
10742    pub fn clear(&mut self, window: &mut Window, cx: &mut Context<Self>) {
10743        self.transact(window, cx, |this, window, cx| {
10744            this.select_all(&SelectAll, window, cx);
10745            this.insert("", window, cx);
10746        });
10747    }
10748
10749    pub fn backspace(&mut self, _: &Backspace, window: &mut Window, cx: &mut Context<Self>) {
10750        if self.read_only(cx) {
10751            return;
10752        }
10753        self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
10754        self.transact(window, cx, |this, window, cx| {
10755            this.select_autoclose_pair(window, cx);
10756
10757            let linked_edits = this.linked_edits_for_selections(Arc::from(""), cx);
10758
10759            let display_map = this.display_map.update(cx, |map, cx| map.snapshot(cx));
10760            let mut selections = this.selections.all::<MultiBufferPoint>(&display_map);
10761            for selection in &mut selections {
10762                if selection.is_empty() {
10763                    let old_head = selection.head();
10764                    let mut new_head =
10765                        movement::left(&display_map, old_head.to_display_point(&display_map))
10766                            .to_point(&display_map);
10767                    if let Some((buffer, line_buffer_range)) = display_map
10768                        .buffer_snapshot()
10769                        .buffer_line_for_row(MultiBufferRow(old_head.row))
10770                    {
10771                        let indent_size = buffer.indent_size_for_line(line_buffer_range.start.row);
10772                        let indent_len = match indent_size.kind {
10773                            IndentKind::Space => {
10774                                buffer.settings_at(line_buffer_range.start, cx).tab_size
10775                            }
10776                            IndentKind::Tab => NonZeroU32::new(1).unwrap(),
10777                        };
10778                        if old_head.column <= indent_size.len && old_head.column > 0 {
10779                            let indent_len = indent_len.get();
10780                            new_head = cmp::min(
10781                                new_head,
10782                                MultiBufferPoint::new(
10783                                    old_head.row,
10784                                    ((old_head.column - 1) / indent_len) * indent_len,
10785                                ),
10786                            );
10787                        }
10788                    }
10789
10790                    selection.set_head(new_head, SelectionGoal::None);
10791                }
10792            }
10793
10794            this.change_selections(Default::default(), window, cx, |s| s.select(selections));
10795            this.insert("", window, cx);
10796            linked_edits.apply_with_left_expansion(cx);
10797            this.refresh_edit_prediction(true, false, window, cx);
10798            refresh_linked_ranges(this, window, cx);
10799        });
10800    }
10801
10802    pub fn delete(&mut self, _: &Delete, window: &mut Window, cx: &mut Context<Self>) {
10803        if self.read_only(cx) {
10804            return;
10805        }
10806        self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
10807        self.transact(window, cx, |this, window, cx| {
10808            this.change_selections(Default::default(), window, cx, |s| {
10809                s.move_with(&mut |map, selection| {
10810                    if selection.is_empty() {
10811                        let cursor = movement::right(map, selection.head());
10812                        selection.end = cursor;
10813                        selection.reversed = true;
10814                        selection.goal = SelectionGoal::None;
10815                    }
10816                })
10817            });
10818            let linked_edits = this.linked_edits_for_selections(Arc::from(""), cx);
10819            this.insert("", window, cx);
10820            linked_edits.apply(cx);
10821            this.refresh_edit_prediction(true, false, window, cx);
10822            refresh_linked_ranges(this, window, cx);
10823        });
10824    }
10825
10826    pub fn backtab(&mut self, _: &Backtab, window: &mut Window, cx: &mut Context<Self>) {
10827        if self.mode.is_single_line() {
10828            cx.propagate();
10829            return;
10830        }
10831
10832        self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
10833        if self.move_to_prev_snippet_tabstop(window, cx) {
10834            return;
10835        }
10836        self.outdent(&Outdent, window, cx);
10837    }
10838
10839    pub fn next_snippet_tabstop(
10840        &mut self,
10841        _: &NextSnippetTabstop,
10842        window: &mut Window,
10843        cx: &mut Context<Self>,
10844    ) {
10845        if self.mode.is_single_line() || self.snippet_stack.is_empty() {
10846            cx.propagate();
10847            return;
10848        }
10849
10850        if self.move_to_next_snippet_tabstop(window, cx) {
10851            self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
10852            return;
10853        }
10854        cx.propagate();
10855    }
10856
10857    pub fn previous_snippet_tabstop(
10858        &mut self,
10859        _: &PreviousSnippetTabstop,
10860        window: &mut Window,
10861        cx: &mut Context<Self>,
10862    ) {
10863        if self.mode.is_single_line() || self.snippet_stack.is_empty() {
10864            cx.propagate();
10865            return;
10866        }
10867
10868        if self.move_to_prev_snippet_tabstop(window, cx) {
10869            self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
10870            return;
10871        }
10872        cx.propagate();
10873    }
10874
10875    pub fn tab(&mut self, _: &Tab, window: &mut Window, cx: &mut Context<Self>) {
10876        if self.mode.is_single_line() {
10877            cx.propagate();
10878            return;
10879        }
10880
10881        if self.move_to_next_snippet_tabstop(window, cx) {
10882            self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
10883            return;
10884        }
10885        if self.read_only(cx) {
10886            return;
10887        }
10888        self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
10889        let mut selections = self.selections.all_adjusted(&self.display_snapshot(cx));
10890        let buffer = self.buffer.read(cx);
10891        let snapshot = buffer.snapshot(cx);
10892        let rows_iter = selections.iter().map(|s| s.head().row);
10893        let suggested_indents = snapshot.suggested_indents(rows_iter, cx);
10894
10895        let has_some_cursor_in_whitespace = selections
10896            .iter()
10897            .filter(|selection| selection.is_empty())
10898            .any(|selection| {
10899                let cursor = selection.head();
10900                let current_indent = snapshot.indent_size_for_line(MultiBufferRow(cursor.row));
10901                cursor.column < current_indent.len
10902            });
10903
10904        let mut edits = Vec::new();
10905        let mut prev_edited_row = 0;
10906        let mut row_delta = 0;
10907        for selection in &mut selections {
10908            if selection.start.row != prev_edited_row {
10909                row_delta = 0;
10910            }
10911            prev_edited_row = selection.end.row;
10912
10913            // If cursor is after a list prefix, make selection non-empty to trigger line indent
10914            if selection.is_empty() {
10915                let cursor = selection.head();
10916                let settings = buffer.language_settings_at(cursor, cx);
10917                if settings.indent_list_on_tab {
10918                    if let Some(language) = snapshot.language_scope_at(Point::new(cursor.row, 0)) {
10919                        if is_list_prefix_row(MultiBufferRow(cursor.row), &snapshot, &language) {
10920                            row_delta = Self::indent_selection(
10921                                buffer, &snapshot, selection, &mut edits, row_delta, cx,
10922                            );
10923                            continue;
10924                        }
10925                    }
10926                }
10927            }
10928
10929            // If the selection is non-empty, then increase the indentation of the selected lines.
10930            if !selection.is_empty() {
10931                row_delta =
10932                    Self::indent_selection(buffer, &snapshot, selection, &mut edits, row_delta, cx);
10933                continue;
10934            }
10935
10936            let cursor = selection.head();
10937            let current_indent = snapshot.indent_size_for_line(MultiBufferRow(cursor.row));
10938            if let Some(suggested_indent) =
10939                suggested_indents.get(&MultiBufferRow(cursor.row)).copied()
10940            {
10941                // Don't do anything if already at suggested indent
10942                // and there is any other cursor which is not
10943                if has_some_cursor_in_whitespace
10944                    && cursor.column == current_indent.len
10945                    && current_indent.len == suggested_indent.len
10946                {
10947                    continue;
10948                }
10949
10950                // Adjust line and move cursor to suggested indent
10951                // if cursor is not at suggested indent
10952                if cursor.column < suggested_indent.len
10953                    && cursor.column <= current_indent.len
10954                    && current_indent.len <= suggested_indent.len
10955                {
10956                    selection.start = Point::new(cursor.row, suggested_indent.len);
10957                    selection.end = selection.start;
10958                    if row_delta == 0 {
10959                        edits.extend(Buffer::edit_for_indent_size_adjustment(
10960                            cursor.row,
10961                            current_indent,
10962                            suggested_indent,
10963                        ));
10964                        row_delta = suggested_indent.len - current_indent.len;
10965                    }
10966                    continue;
10967                }
10968
10969                // If current indent is more than suggested indent
10970                // only move cursor to current indent and skip indent
10971                if cursor.column < current_indent.len && current_indent.len > suggested_indent.len {
10972                    selection.start = Point::new(cursor.row, current_indent.len);
10973                    selection.end = selection.start;
10974                    continue;
10975                }
10976            }
10977
10978            // Otherwise, insert a hard or soft tab.
10979            let settings = buffer.language_settings_at(cursor, cx);
10980            let tab_size = if settings.hard_tabs {
10981                IndentSize::tab()
10982            } else {
10983                let tab_size = settings.tab_size.get();
10984                let indent_remainder = snapshot
10985                    .text_for_range(Point::new(cursor.row, 0)..cursor)
10986                    .flat_map(str::chars)
10987                    .fold(row_delta % tab_size, |counter: u32, c| {
10988                        if c == '\t' {
10989                            0
10990                        } else {
10991                            (counter + 1) % tab_size
10992                        }
10993                    });
10994
10995                let chars_to_next_tab_stop = tab_size - indent_remainder;
10996                IndentSize::spaces(chars_to_next_tab_stop)
10997            };
10998            selection.start = Point::new(cursor.row, cursor.column + row_delta + tab_size.len);
10999            selection.end = selection.start;
11000            edits.push((cursor..cursor, tab_size.chars().collect::<String>()));
11001            row_delta += tab_size.len;
11002        }
11003
11004        self.transact(window, cx, |this, window, cx| {
11005            this.buffer.update(cx, |b, cx| b.edit(edits, None, cx));
11006            this.change_selections(Default::default(), window, cx, |s| s.select(selections));
11007            this.refresh_edit_prediction(true, false, window, cx);
11008        });
11009    }
11010
11011    pub fn indent(&mut self, _: &Indent, window: &mut Window, cx: &mut Context<Self>) {
11012        if self.read_only(cx) {
11013            return;
11014        }
11015        if self.mode.is_single_line() {
11016            cx.propagate();
11017            return;
11018        }
11019
11020        self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
11021        let mut selections = self.selections.all::<Point>(&self.display_snapshot(cx));
11022        let mut prev_edited_row = 0;
11023        let mut row_delta = 0;
11024        let mut edits = Vec::new();
11025        let buffer = self.buffer.read(cx);
11026        let snapshot = buffer.snapshot(cx);
11027        for selection in &mut selections {
11028            if selection.start.row != prev_edited_row {
11029                row_delta = 0;
11030            }
11031            prev_edited_row = selection.end.row;
11032
11033            row_delta =
11034                Self::indent_selection(buffer, &snapshot, selection, &mut edits, row_delta, cx);
11035        }
11036
11037        self.transact(window, cx, |this, window, cx| {
11038            this.buffer.update(cx, |b, cx| b.edit(edits, None, cx));
11039            this.change_selections(Default::default(), window, cx, |s| s.select(selections));
11040        });
11041    }
11042
11043    fn indent_selection(
11044        buffer: &MultiBuffer,
11045        snapshot: &MultiBufferSnapshot,
11046        selection: &mut Selection<Point>,
11047        edits: &mut Vec<(Range<Point>, String)>,
11048        delta_for_start_row: u32,
11049        cx: &App,
11050    ) -> u32 {
11051        let settings = buffer.language_settings_at(selection.start, cx);
11052        let tab_size = settings.tab_size.get();
11053        let indent_kind = if settings.hard_tabs {
11054            IndentKind::Tab
11055        } else {
11056            IndentKind::Space
11057        };
11058        let mut start_row = selection.start.row;
11059        let mut end_row = selection.end.row + 1;
11060
11061        // If a selection ends at the beginning of a line, don't indent
11062        // that last line.
11063        if selection.end.column == 0 && selection.end.row > selection.start.row {
11064            end_row -= 1;
11065        }
11066
11067        // Avoid re-indenting a row that has already been indented by a
11068        // previous selection, but still update this selection's column
11069        // to reflect that indentation.
11070        if delta_for_start_row > 0 {
11071            start_row += 1;
11072            selection.start.column += delta_for_start_row;
11073            if selection.end.row == selection.start.row {
11074                selection.end.column += delta_for_start_row;
11075            }
11076        }
11077
11078        let mut delta_for_end_row = 0;
11079        let has_multiple_rows = start_row + 1 != end_row;
11080        for row in start_row..end_row {
11081            let current_indent = snapshot.indent_size_for_line(MultiBufferRow(row));
11082            let indent_delta = match (current_indent.kind, indent_kind) {
11083                (IndentKind::Space, IndentKind::Space) => {
11084                    let columns_to_next_tab_stop = tab_size - (current_indent.len % tab_size);
11085                    IndentSize::spaces(columns_to_next_tab_stop)
11086                }
11087                (IndentKind::Tab, IndentKind::Space) => IndentSize::spaces(tab_size),
11088                (_, IndentKind::Tab) => IndentSize::tab(),
11089            };
11090
11091            let start = if has_multiple_rows || current_indent.len < selection.start.column {
11092                0
11093            } else {
11094                selection.start.column
11095            };
11096            let row_start = Point::new(row, start);
11097            edits.push((
11098                row_start..row_start,
11099                indent_delta.chars().collect::<String>(),
11100            ));
11101
11102            // Update this selection's endpoints to reflect the indentation.
11103            if row == selection.start.row {
11104                selection.start.column += indent_delta.len;
11105            }
11106            if row == selection.end.row {
11107                selection.end.column += indent_delta.len;
11108                delta_for_end_row = indent_delta.len;
11109            }
11110        }
11111
11112        if selection.start.row == selection.end.row {
11113            delta_for_start_row + delta_for_end_row
11114        } else {
11115            delta_for_end_row
11116        }
11117    }
11118
11119    pub fn outdent(&mut self, _: &Outdent, window: &mut Window, cx: &mut Context<Self>) {
11120        if self.read_only(cx) {
11121            return;
11122        }
11123        if self.mode.is_single_line() {
11124            cx.propagate();
11125            return;
11126        }
11127
11128        self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
11129        let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
11130        let selections = self.selections.all::<Point>(&display_map);
11131        let mut deletion_ranges = Vec::new();
11132        let mut last_outdent = None;
11133        {
11134            let buffer = self.buffer.read(cx);
11135            let snapshot = buffer.snapshot(cx);
11136            for selection in &selections {
11137                let settings = buffer.language_settings_at(selection.start, cx);
11138                let tab_size = settings.tab_size.get();
11139                let mut rows = selection.spanned_rows(false, &display_map);
11140
11141                // Avoid re-outdenting a row that has already been outdented by a
11142                // previous selection.
11143                if let Some(last_row) = last_outdent
11144                    && last_row == rows.start
11145                {
11146                    rows.start = rows.start.next_row();
11147                }
11148                let has_multiple_rows = rows.len() > 1;
11149                for row in rows.iter_rows() {
11150                    let indent_size = snapshot.indent_size_for_line(row);
11151                    if indent_size.len > 0 {
11152                        let deletion_len = match indent_size.kind {
11153                            IndentKind::Space => {
11154                                let columns_to_prev_tab_stop = indent_size.len % tab_size;
11155                                if columns_to_prev_tab_stop == 0 {
11156                                    tab_size
11157                                } else {
11158                                    columns_to_prev_tab_stop
11159                                }
11160                            }
11161                            IndentKind::Tab => 1,
11162                        };
11163                        let start = if has_multiple_rows
11164                            || deletion_len > selection.start.column
11165                            || indent_size.len < selection.start.column
11166                        {
11167                            0
11168                        } else {
11169                            selection.start.column - deletion_len
11170                        };
11171                        deletion_ranges.push(
11172                            Point::new(row.0, start)..Point::new(row.0, start + deletion_len),
11173                        );
11174                        last_outdent = Some(row);
11175                    }
11176                }
11177            }
11178        }
11179
11180        self.transact(window, cx, |this, window, cx| {
11181            this.buffer.update(cx, |buffer, cx| {
11182                let empty_str: Arc<str> = Arc::default();
11183                buffer.edit(
11184                    deletion_ranges
11185                        .into_iter()
11186                        .map(|range| (range, empty_str.clone())),
11187                    None,
11188                    cx,
11189                );
11190            });
11191            let selections = this
11192                .selections
11193                .all::<MultiBufferOffset>(&this.display_snapshot(cx));
11194            this.change_selections(Default::default(), window, cx, |s| s.select(selections));
11195        });
11196    }
11197
11198    pub fn autoindent(&mut self, _: &AutoIndent, window: &mut Window, cx: &mut Context<Self>) {
11199        if self.read_only(cx) {
11200            return;
11201        }
11202        if self.mode.is_single_line() {
11203            cx.propagate();
11204            return;
11205        }
11206
11207        self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
11208        let selections = self
11209            .selections
11210            .all::<MultiBufferOffset>(&self.display_snapshot(cx))
11211            .into_iter()
11212            .map(|s| s.range());
11213
11214        self.transact(window, cx, |this, window, cx| {
11215            this.buffer.update(cx, |buffer, cx| {
11216                buffer.autoindent_ranges(selections, cx);
11217            });
11218            let selections = this
11219                .selections
11220                .all::<MultiBufferOffset>(&this.display_snapshot(cx));
11221            this.change_selections(Default::default(), window, cx, |s| s.select(selections));
11222        });
11223    }
11224
11225    pub fn delete_line(&mut self, _: &DeleteLine, window: &mut Window, cx: &mut Context<Self>) {
11226        self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
11227        let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
11228        let selections = self.selections.all::<Point>(&display_map);
11229
11230        let mut new_cursors = Vec::new();
11231        let mut edit_ranges = Vec::new();
11232        let mut selections = selections.iter().peekable();
11233        while let Some(selection) = selections.next() {
11234            let mut rows = selection.spanned_rows(false, &display_map);
11235
11236            // Accumulate contiguous regions of rows that we want to delete.
11237            while let Some(next_selection) = selections.peek() {
11238                let next_rows = next_selection.spanned_rows(false, &display_map);
11239                if next_rows.start <= rows.end {
11240                    rows.end = next_rows.end;
11241                    selections.next().unwrap();
11242                } else {
11243                    break;
11244                }
11245            }
11246
11247            let buffer = display_map.buffer_snapshot();
11248            let mut edit_start = ToOffset::to_offset(&Point::new(rows.start.0, 0), buffer);
11249            let (edit_end, target_row) = if buffer.max_point().row >= rows.end.0 {
11250                // If there's a line after the range, delete the \n from the end of the row range
11251                (
11252                    ToOffset::to_offset(&Point::new(rows.end.0, 0), buffer),
11253                    rows.end,
11254                )
11255            } else {
11256                // If there isn't a line after the range, delete the \n from the line before the
11257                // start of the row range
11258                edit_start = edit_start.saturating_sub_usize(1);
11259                (buffer.len(), rows.start.previous_row())
11260            };
11261
11262            let text_layout_details = self.text_layout_details(window, cx);
11263            let x = display_map.x_for_display_point(
11264                selection.head().to_display_point(&display_map),
11265                &text_layout_details,
11266            );
11267            let row = Point::new(target_row.0, 0)
11268                .to_display_point(&display_map)
11269                .row();
11270            let column = display_map.display_column_for_x(row, x, &text_layout_details);
11271
11272            new_cursors.push((
11273                selection.id,
11274                buffer.anchor_after(DisplayPoint::new(row, column).to_point(&display_map)),
11275                SelectionGoal::None,
11276            ));
11277            edit_ranges.push(edit_start..edit_end);
11278        }
11279
11280        self.transact(window, cx, |this, window, cx| {
11281            let buffer = this.buffer.update(cx, |buffer, cx| {
11282                let empty_str: Arc<str> = Arc::default();
11283                buffer.edit(
11284                    edit_ranges
11285                        .into_iter()
11286                        .map(|range| (range, empty_str.clone())),
11287                    None,
11288                    cx,
11289                );
11290                buffer.snapshot(cx)
11291            });
11292            let new_selections = new_cursors
11293                .into_iter()
11294                .map(|(id, cursor, goal)| {
11295                    let cursor = cursor.to_point(&buffer);
11296                    Selection {
11297                        id,
11298                        start: cursor,
11299                        end: cursor,
11300                        reversed: false,
11301                        goal,
11302                    }
11303                })
11304                .collect();
11305
11306            this.change_selections(Default::default(), window, cx, |s| {
11307                s.select(new_selections);
11308            });
11309        });
11310    }
11311
11312    pub fn join_lines_impl(
11313        &mut self,
11314        insert_whitespace: bool,
11315        window: &mut Window,
11316        cx: &mut Context<Self>,
11317    ) {
11318        if self.read_only(cx) {
11319            return;
11320        }
11321        let mut row_ranges = Vec::<Range<MultiBufferRow>>::new();
11322        for selection in self.selections.all::<Point>(&self.display_snapshot(cx)) {
11323            let start = MultiBufferRow(selection.start.row);
11324            // Treat single line selections as if they include the next line. Otherwise this action
11325            // would do nothing for single line selections individual cursors.
11326            let end = if selection.start.row == selection.end.row {
11327                MultiBufferRow(selection.start.row + 1)
11328            } else if selection.end.column == 0 {
11329                // If the selection ends at the start of a line, it's logically at the end of the
11330                // previous line (plus its newline).
11331                // Don't include the end line unless there's only one line selected.
11332                if selection.start.row + 1 == selection.end.row {
11333                    MultiBufferRow(selection.end.row)
11334                } else {
11335                    MultiBufferRow(selection.end.row - 1)
11336                }
11337            } else {
11338                MultiBufferRow(selection.end.row)
11339            };
11340
11341            if let Some(last_row_range) = row_ranges.last_mut()
11342                && start <= last_row_range.end
11343            {
11344                last_row_range.end = end;
11345                continue;
11346            }
11347            row_ranges.push(start..end);
11348        }
11349
11350        let snapshot = self.buffer.read(cx).snapshot(cx);
11351        let mut cursor_positions = Vec::new();
11352        for row_range in &row_ranges {
11353            let anchor = snapshot.anchor_before(Point::new(
11354                row_range.end.previous_row().0,
11355                snapshot.line_len(row_range.end.previous_row()),
11356            ));
11357            cursor_positions.push(anchor..anchor);
11358        }
11359
11360        self.transact(window, cx, |this, window, cx| {
11361            for row_range in row_ranges.into_iter().rev() {
11362                for row in row_range.iter_rows().rev() {
11363                    let end_of_line = Point::new(row.0, snapshot.line_len(row));
11364                    let next_line_row = row.next_row();
11365                    let indent = snapshot.indent_size_for_line(next_line_row);
11366                    let mut join_start_column = indent.len;
11367
11368                    if let Some(language_scope) =
11369                        snapshot.language_scope_at(Point::new(next_line_row.0, indent.len))
11370                    {
11371                        let line_end =
11372                            Point::new(next_line_row.0, snapshot.line_len(next_line_row));
11373                        let line_text_after_indent = snapshot
11374                            .text_for_range(Point::new(next_line_row.0, indent.len)..line_end)
11375                            .collect::<String>();
11376
11377                        if !line_text_after_indent.is_empty() {
11378                            let block_prefix = language_scope
11379                                .block_comment()
11380                                .map(|c| c.prefix.as_ref())
11381                                .filter(|p| !p.is_empty());
11382                            let doc_prefix = language_scope
11383                                .documentation_comment()
11384                                .map(|c| c.prefix.as_ref())
11385                                .filter(|p| !p.is_empty());
11386                            let all_prefixes = language_scope
11387                                .line_comment_prefixes()
11388                                .iter()
11389                                .map(|p| p.as_ref())
11390                                .chain(block_prefix)
11391                                .chain(doc_prefix)
11392                                .chain(language_scope.unordered_list().iter().map(|p| p.as_ref()));
11393
11394                            let mut longest_prefix_len = None;
11395                            for prefix in all_prefixes {
11396                                let trimmed = prefix.trim_end();
11397                                if line_text_after_indent.starts_with(trimmed) {
11398                                    let candidate_len =
11399                                        if line_text_after_indent.starts_with(prefix) {
11400                                            prefix.len()
11401                                        } else {
11402                                            trimmed.len()
11403                                        };
11404                                    if longest_prefix_len.map_or(true, |len| candidate_len > len) {
11405                                        longest_prefix_len = Some(candidate_len);
11406                                    }
11407                                }
11408                            }
11409
11410                            if let Some(prefix_len) = longest_prefix_len {
11411                                join_start_column =
11412                                    join_start_column.saturating_add(prefix_len as u32);
11413                            }
11414                        }
11415                    }
11416
11417                    let start_of_next_line = Point::new(next_line_row.0, join_start_column);
11418
11419                    let replace = if snapshot.line_len(next_line_row) > join_start_column
11420                        && insert_whitespace
11421                    {
11422                        " "
11423                    } else {
11424                        ""
11425                    };
11426
11427                    this.buffer.update(cx, |buffer, cx| {
11428                        buffer.edit([(end_of_line..start_of_next_line, replace)], None, cx)
11429                    });
11430                }
11431            }
11432
11433            this.change_selections(Default::default(), window, cx, |s| {
11434                s.select_anchor_ranges(cursor_positions)
11435            });
11436        });
11437    }
11438
11439    pub fn join_lines(&mut self, _: &JoinLines, window: &mut Window, cx: &mut Context<Self>) {
11440        self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
11441        self.join_lines_impl(true, window, cx);
11442    }
11443
11444    pub fn sort_lines_case_sensitive(
11445        &mut self,
11446        _: &SortLinesCaseSensitive,
11447        window: &mut Window,
11448        cx: &mut Context<Self>,
11449    ) {
11450        self.manipulate_immutable_lines(window, cx, |lines| lines.sort())
11451    }
11452
11453    pub fn sort_lines_by_length(
11454        &mut self,
11455        _: &SortLinesByLength,
11456        window: &mut Window,
11457        cx: &mut Context<Self>,
11458    ) {
11459        self.manipulate_immutable_lines(window, cx, |lines| {
11460            lines.sort_by_key(|&line| line.chars().count())
11461        })
11462    }
11463
11464    pub fn sort_lines_case_insensitive(
11465        &mut self,
11466        _: &SortLinesCaseInsensitive,
11467        window: &mut Window,
11468        cx: &mut Context<Self>,
11469    ) {
11470        self.manipulate_immutable_lines(window, cx, |lines| {
11471            lines.sort_by_key(|line| line.to_lowercase())
11472        })
11473    }
11474
11475    pub fn unique_lines_case_insensitive(
11476        &mut self,
11477        _: &UniqueLinesCaseInsensitive,
11478        window: &mut Window,
11479        cx: &mut Context<Self>,
11480    ) {
11481        self.manipulate_immutable_lines(window, cx, |lines| {
11482            let mut seen = HashSet::default();
11483            lines.retain(|line| seen.insert(line.to_lowercase()));
11484        })
11485    }
11486
11487    pub fn unique_lines_case_sensitive(
11488        &mut self,
11489        _: &UniqueLinesCaseSensitive,
11490        window: &mut Window,
11491        cx: &mut Context<Self>,
11492    ) {
11493        self.manipulate_immutable_lines(window, cx, |lines| {
11494            let mut seen = HashSet::default();
11495            lines.retain(|line| seen.insert(*line));
11496        })
11497    }
11498
11499    fn enable_wrap_selections_in_tag(&self, cx: &App) -> bool {
11500        let snapshot = self.buffer.read(cx).snapshot(cx);
11501        for selection in self.selections.disjoint_anchors_arc().iter() {
11502            if snapshot
11503                .language_at(selection.start)
11504                .and_then(|lang| lang.config().wrap_characters.as_ref())
11505                .is_some()
11506            {
11507                return true;
11508            }
11509        }
11510        false
11511    }
11512
11513    fn wrap_selections_in_tag(
11514        &mut self,
11515        _: &WrapSelectionsInTag,
11516        window: &mut Window,
11517        cx: &mut Context<Self>,
11518    ) {
11519        self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
11520
11521        let snapshot = self.buffer.read(cx).snapshot(cx);
11522
11523        let mut edits = Vec::new();
11524        let mut boundaries = Vec::new();
11525
11526        for selection in self
11527            .selections
11528            .all_adjusted(&self.display_snapshot(cx))
11529            .iter()
11530        {
11531            let Some(wrap_config) = snapshot
11532                .language_at(selection.start)
11533                .and_then(|lang| lang.config().wrap_characters.clone())
11534            else {
11535                continue;
11536            };
11537
11538            let open_tag = format!("{}{}", wrap_config.start_prefix, wrap_config.start_suffix);
11539            let close_tag = format!("{}{}", wrap_config.end_prefix, wrap_config.end_suffix);
11540
11541            let start_before = snapshot.anchor_before(selection.start);
11542            let end_after = snapshot.anchor_after(selection.end);
11543
11544            edits.push((start_before..start_before, open_tag));
11545            edits.push((end_after..end_after, close_tag));
11546
11547            boundaries.push((
11548                start_before,
11549                end_after,
11550                wrap_config.start_prefix.len(),
11551                wrap_config.end_suffix.len(),
11552            ));
11553        }
11554
11555        if edits.is_empty() {
11556            return;
11557        }
11558
11559        self.transact(window, cx, |this, window, cx| {
11560            let buffer = this.buffer.update(cx, |buffer, cx| {
11561                buffer.edit(edits, None, cx);
11562                buffer.snapshot(cx)
11563            });
11564
11565            let mut new_selections = Vec::with_capacity(boundaries.len() * 2);
11566            for (start_before, end_after, start_prefix_len, end_suffix_len) in
11567                boundaries.into_iter()
11568            {
11569                let open_offset = start_before.to_offset(&buffer) + start_prefix_len;
11570                let close_offset = end_after
11571                    .to_offset(&buffer)
11572                    .saturating_sub_usize(end_suffix_len);
11573                new_selections.push(open_offset..open_offset);
11574                new_selections.push(close_offset..close_offset);
11575            }
11576
11577            this.change_selections(Default::default(), window, cx, |s| {
11578                s.select_ranges(new_selections);
11579            });
11580
11581            this.request_autoscroll(Autoscroll::fit(), cx);
11582        });
11583    }
11584
11585    pub fn toggle_read_only(
11586        &mut self,
11587        _: &workspace::ToggleReadOnlyFile,
11588        _: &mut Window,
11589        cx: &mut Context<Self>,
11590    ) {
11591        if let Some(buffer) = self.buffer.read(cx).as_singleton() {
11592            buffer.update(cx, |buffer, cx| {
11593                buffer.set_capability(
11594                    match buffer.capability() {
11595                        Capability::ReadWrite => Capability::Read,
11596                        Capability::Read => Capability::ReadWrite,
11597                        Capability::ReadOnly => Capability::ReadOnly,
11598                    },
11599                    cx,
11600                );
11601            })
11602        }
11603    }
11604
11605    pub fn reload_file(&mut self, _: &ReloadFile, window: &mut Window, cx: &mut Context<Self>) {
11606        let Some(project) = self.project.clone() else {
11607            return;
11608        };
11609        let task = self.reload(project, window, cx);
11610        self.detach_and_notify_err(task, window, cx);
11611    }
11612
11613    pub fn restore_file(
11614        &mut self,
11615        _: &::git::RestoreFile,
11616        window: &mut Window,
11617        cx: &mut Context<Self>,
11618    ) {
11619        self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
11620        let mut buffer_ids = HashSet::default();
11621        let snapshot = self.buffer().read(cx).snapshot(cx);
11622        for selection in self
11623            .selections
11624            .all::<MultiBufferOffset>(&self.display_snapshot(cx))
11625        {
11626            buffer_ids.extend(snapshot.buffer_ids_for_range(selection.range()))
11627        }
11628
11629        let buffer = self.buffer().read(cx);
11630        let ranges = buffer_ids
11631            .into_iter()
11632            .flat_map(|buffer_id| buffer.excerpt_ranges_for_buffer(buffer_id, cx))
11633            .collect::<Vec<_>>();
11634
11635        self.restore_hunks_in_ranges(ranges, window, cx);
11636    }
11637
11638    pub fn git_restore(&mut self, _: &Restore, window: &mut Window, cx: &mut Context<Self>) {
11639        self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
11640        let selections = self
11641            .selections
11642            .all(&self.display_snapshot(cx))
11643            .into_iter()
11644            .map(|s| s.range())
11645            .collect();
11646        self.restore_hunks_in_ranges(selections, window, cx);
11647    }
11648
11649    pub fn restore_hunks_in_ranges(
11650        &mut self,
11651        ranges: Vec<Range<Point>>,
11652        window: &mut Window,
11653        cx: &mut Context<Editor>,
11654    ) {
11655        if self.delegate_stage_and_restore {
11656            let hunks = self.snapshot(window, cx).hunks_for_ranges(ranges);
11657            if !hunks.is_empty() {
11658                cx.emit(EditorEvent::RestoreRequested { hunks });
11659            }
11660            return;
11661        }
11662        let hunks = self.snapshot(window, cx).hunks_for_ranges(ranges);
11663        self.transact(window, cx, |editor, window, cx| {
11664            editor.restore_diff_hunks(hunks, cx);
11665            editor.change_selections(SelectionEffects::no_scroll(), window, cx, |selections| {
11666                selections.refresh()
11667            });
11668        });
11669    }
11670
11671    pub(crate) fn restore_diff_hunks(&self, hunks: Vec<MultiBufferDiffHunk>, cx: &mut App) {
11672        let mut revert_changes = HashMap::default();
11673        let chunk_by = hunks.into_iter().chunk_by(|hunk| hunk.buffer_id);
11674        for (buffer_id, hunks) in &chunk_by {
11675            let hunks = hunks.collect::<Vec<_>>();
11676            for hunk in &hunks {
11677                self.prepare_restore_change(&mut revert_changes, hunk, cx);
11678            }
11679            self.do_stage_or_unstage(false, buffer_id, hunks.into_iter(), cx);
11680        }
11681        if !revert_changes.is_empty() {
11682            self.buffer().update(cx, |multi_buffer, cx| {
11683                for (buffer_id, changes) in revert_changes {
11684                    if let Some(buffer) = multi_buffer.buffer(buffer_id) {
11685                        buffer.update(cx, |buffer, cx| {
11686                            buffer.edit(
11687                                changes
11688                                    .into_iter()
11689                                    .map(|(range, text)| (range, text.to_string())),
11690                                None,
11691                                cx,
11692                            );
11693                        });
11694                    }
11695                }
11696            });
11697        }
11698    }
11699
11700    pub fn status_for_buffer_id(&self, buffer_id: BufferId, cx: &App) -> Option<FileStatus> {
11701        if let Some(status) = self
11702            .addons
11703            .iter()
11704            .find_map(|(_, addon)| addon.override_status_for_buffer_id(buffer_id, cx))
11705        {
11706            return Some(status);
11707        }
11708        self.project
11709            .as_ref()?
11710            .read(cx)
11711            .status_for_buffer_id(buffer_id, cx)
11712    }
11713
11714    pub fn open_active_item_in_terminal(
11715        &mut self,
11716        _: &OpenInTerminal,
11717        window: &mut Window,
11718        cx: &mut Context<Self>,
11719    ) {
11720        if let Some(working_directory) = self.active_excerpt(cx).and_then(|(_, buffer, _)| {
11721            let project_path = buffer.read(cx).project_path(cx)?;
11722            let project = self.project()?.read(cx);
11723            let entry = project.entry_for_path(&project_path, cx)?;
11724            let parent = match &entry.canonical_path {
11725                Some(canonical_path) => canonical_path.to_path_buf(),
11726                None => project.absolute_path(&project_path, cx)?,
11727            }
11728            .parent()?
11729            .to_path_buf();
11730            Some(parent)
11731        }) {
11732            window.dispatch_action(
11733                OpenTerminal {
11734                    working_directory,
11735                    local: false,
11736                }
11737                .boxed_clone(),
11738                cx,
11739            );
11740        }
11741    }
11742
11743    fn set_breakpoint_context_menu(
11744        &mut self,
11745        display_row: DisplayRow,
11746        position: Option<Anchor>,
11747        clicked_point: gpui::Point<Pixels>,
11748        window: &mut Window,
11749        cx: &mut Context<Self>,
11750    ) {
11751        let source = self
11752            .buffer
11753            .read(cx)
11754            .snapshot(cx)
11755            .anchor_before(Point::new(display_row.0, 0u32));
11756
11757        let context_menu = self.breakpoint_context_menu(position.unwrap_or(source), window, cx);
11758
11759        self.mouse_context_menu = MouseContextMenu::pinned_to_editor(
11760            self,
11761            source,
11762            clicked_point,
11763            context_menu,
11764            window,
11765            cx,
11766        );
11767    }
11768
11769    fn add_edit_breakpoint_block(
11770        &mut self,
11771        anchor: Anchor,
11772        breakpoint: &Breakpoint,
11773        edit_action: BreakpointPromptEditAction,
11774        window: &mut Window,
11775        cx: &mut Context<Self>,
11776    ) {
11777        let weak_editor = cx.weak_entity();
11778        let bp_prompt = cx.new(|cx| {
11779            BreakpointPromptEditor::new(
11780                weak_editor,
11781                anchor,
11782                breakpoint.clone(),
11783                edit_action,
11784                window,
11785                cx,
11786            )
11787        });
11788
11789        let height = bp_prompt.update(cx, |this, cx| {
11790            this.prompt
11791                .update(cx, |prompt, cx| prompt.max_point(cx).row().0 + 1 + 2)
11792        });
11793        let cloned_prompt = bp_prompt.clone();
11794        let blocks = vec![BlockProperties {
11795            style: BlockStyle::Sticky,
11796            placement: BlockPlacement::Above(anchor),
11797            height: Some(height),
11798            render: Arc::new(move |cx| {
11799                *cloned_prompt.read(cx).editor_margins.lock() = *cx.margins;
11800                cloned_prompt.clone().into_any_element()
11801            }),
11802            priority: 0,
11803        }];
11804
11805        let focus_handle = bp_prompt.focus_handle(cx);
11806        window.focus(&focus_handle, cx);
11807
11808        let block_ids = self.insert_blocks(blocks, None, cx);
11809        bp_prompt.update(cx, |prompt, _| {
11810            prompt.add_block_ids(block_ids);
11811        });
11812    }
11813
11814    pub(crate) fn breakpoint_at_row(
11815        &self,
11816        row: u32,
11817        window: &mut Window,
11818        cx: &mut Context<Self>,
11819    ) -> Option<(Anchor, Breakpoint)> {
11820        let snapshot = self.snapshot(window, cx);
11821        let breakpoint_position = snapshot.buffer_snapshot().anchor_before(Point::new(row, 0));
11822
11823        self.breakpoint_at_anchor(breakpoint_position, &snapshot, cx)
11824    }
11825
11826    pub(crate) fn breakpoint_at_anchor(
11827        &self,
11828        breakpoint_position: Anchor,
11829        snapshot: &EditorSnapshot,
11830        cx: &mut Context<Self>,
11831    ) -> Option<(Anchor, Breakpoint)> {
11832        let buffer = self
11833            .buffer
11834            .read(cx)
11835            .buffer_for_anchor(breakpoint_position, cx)?;
11836
11837        let enclosing_excerpt = breakpoint_position.excerpt_id;
11838        let buffer_snapshot = buffer.read(cx).snapshot();
11839
11840        let row = buffer_snapshot
11841            .summary_for_anchor::<text::PointUtf16>(&breakpoint_position.text_anchor)
11842            .row;
11843
11844        let line_len = snapshot.buffer_snapshot().line_len(MultiBufferRow(row));
11845        let anchor_end = snapshot
11846            .buffer_snapshot()
11847            .anchor_after(Point::new(row, line_len));
11848
11849        self.breakpoint_store
11850            .as_ref()?
11851            .read_with(cx, |breakpoint_store, cx| {
11852                breakpoint_store
11853                    .breakpoints(
11854                        &buffer,
11855                        Some(breakpoint_position.text_anchor..anchor_end.text_anchor),
11856                        &buffer_snapshot,
11857                        cx,
11858                    )
11859                    .next()
11860                    .and_then(|(bp, _)| {
11861                        let breakpoint_row = buffer_snapshot
11862                            .summary_for_anchor::<text::PointUtf16>(&bp.position)
11863                            .row;
11864
11865                        if breakpoint_row == row {
11866                            snapshot
11867                                .buffer_snapshot()
11868                                .anchor_in_excerpt(enclosing_excerpt, bp.position)
11869                                .map(|position| (position, bp.bp.clone()))
11870                        } else {
11871                            None
11872                        }
11873                    })
11874            })
11875    }
11876
11877    pub fn edit_log_breakpoint(
11878        &mut self,
11879        _: &EditLogBreakpoint,
11880        window: &mut Window,
11881        cx: &mut Context<Self>,
11882    ) {
11883        if self.breakpoint_store.is_none() {
11884            return;
11885        }
11886
11887        for (anchor, breakpoint) in self.breakpoints_at_cursors(window, cx) {
11888            let breakpoint = breakpoint.unwrap_or_else(|| Breakpoint {
11889                message: None,
11890                state: BreakpointState::Enabled,
11891                condition: None,
11892                hit_condition: None,
11893            });
11894
11895            self.add_edit_breakpoint_block(
11896                anchor,
11897                &breakpoint,
11898                BreakpointPromptEditAction::Log,
11899                window,
11900                cx,
11901            );
11902        }
11903    }
11904
11905    fn breakpoints_at_cursors(
11906        &self,
11907        window: &mut Window,
11908        cx: &mut Context<Self>,
11909    ) -> Vec<(Anchor, Option<Breakpoint>)> {
11910        let snapshot = self.snapshot(window, cx);
11911        let cursors = self
11912            .selections
11913            .disjoint_anchors_arc()
11914            .iter()
11915            .map(|selection| {
11916                let cursor_position: Point = selection.head().to_point(&snapshot.buffer_snapshot());
11917
11918                let breakpoint_position = self
11919                    .breakpoint_at_row(cursor_position.row, window, cx)
11920                    .map(|bp| bp.0)
11921                    .unwrap_or_else(|| {
11922                        snapshot
11923                            .display_snapshot
11924                            .buffer_snapshot()
11925                            .anchor_after(Point::new(cursor_position.row, 0))
11926                    });
11927
11928                let breakpoint = self
11929                    .breakpoint_at_anchor(breakpoint_position, &snapshot, cx)
11930                    .map(|(anchor, breakpoint)| (anchor, Some(breakpoint)));
11931
11932                breakpoint.unwrap_or_else(|| (breakpoint_position, None))
11933            })
11934            // 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.
11935            .collect::<HashMap<Anchor, _>>();
11936
11937        cursors.into_iter().collect()
11938    }
11939
11940    pub fn enable_breakpoint(
11941        &mut self,
11942        _: &crate::actions::EnableBreakpoint,
11943        window: &mut Window,
11944        cx: &mut Context<Self>,
11945    ) {
11946        if self.breakpoint_store.is_none() {
11947            return;
11948        }
11949
11950        for (anchor, breakpoint) in self.breakpoints_at_cursors(window, cx) {
11951            let Some(breakpoint) = breakpoint.filter(|breakpoint| breakpoint.is_disabled()) else {
11952                continue;
11953            };
11954            self.edit_breakpoint_at_anchor(
11955                anchor,
11956                breakpoint,
11957                BreakpointEditAction::InvertState,
11958                cx,
11959            );
11960        }
11961    }
11962
11963    pub fn disable_breakpoint(
11964        &mut self,
11965        _: &crate::actions::DisableBreakpoint,
11966        window: &mut Window,
11967        cx: &mut Context<Self>,
11968    ) {
11969        if self.breakpoint_store.is_none() {
11970            return;
11971        }
11972
11973        for (anchor, breakpoint) in self.breakpoints_at_cursors(window, cx) {
11974            let Some(breakpoint) = breakpoint.filter(|breakpoint| breakpoint.is_enabled()) else {
11975                continue;
11976            };
11977            self.edit_breakpoint_at_anchor(
11978                anchor,
11979                breakpoint,
11980                BreakpointEditAction::InvertState,
11981                cx,
11982            );
11983        }
11984    }
11985
11986    pub fn toggle_breakpoint(
11987        &mut self,
11988        _: &crate::actions::ToggleBreakpoint,
11989        window: &mut Window,
11990        cx: &mut Context<Self>,
11991    ) {
11992        if self.breakpoint_store.is_none() {
11993            return;
11994        }
11995
11996        let snapshot = self.snapshot(window, cx);
11997        for (anchor, breakpoint) in self.breakpoints_at_cursors(window, cx) {
11998            if self.gutter_breakpoint_indicator.0.is_some() {
11999                let display_row = anchor
12000                    .to_point(snapshot.buffer_snapshot())
12001                    .to_display_point(&snapshot.display_snapshot)
12002                    .row();
12003                self.update_breakpoint_collision_on_toggle(
12004                    display_row,
12005                    &BreakpointEditAction::Toggle,
12006                );
12007            }
12008
12009            if let Some(breakpoint) = breakpoint {
12010                self.edit_breakpoint_at_anchor(
12011                    anchor,
12012                    breakpoint,
12013                    BreakpointEditAction::Toggle,
12014                    cx,
12015                );
12016            } else {
12017                self.edit_breakpoint_at_anchor(
12018                    anchor,
12019                    Breakpoint::new_standard(),
12020                    BreakpointEditAction::Toggle,
12021                    cx,
12022                );
12023            }
12024        }
12025    }
12026
12027    fn update_breakpoint_collision_on_toggle(
12028        &mut self,
12029        display_row: DisplayRow,
12030        edit_action: &BreakpointEditAction,
12031    ) {
12032        if let Some(ref mut breakpoint_indicator) = self.gutter_breakpoint_indicator.0 {
12033            if breakpoint_indicator.display_row == display_row
12034                && matches!(edit_action, BreakpointEditAction::Toggle)
12035            {
12036                breakpoint_indicator.collides_with_existing_breakpoint =
12037                    !breakpoint_indicator.collides_with_existing_breakpoint;
12038            }
12039        }
12040    }
12041
12042    pub fn edit_breakpoint_at_anchor(
12043        &mut self,
12044        breakpoint_position: Anchor,
12045        breakpoint: Breakpoint,
12046        edit_action: BreakpointEditAction,
12047        cx: &mut Context<Self>,
12048    ) {
12049        let Some(breakpoint_store) = &self.breakpoint_store else {
12050            return;
12051        };
12052
12053        let Some(buffer) = self
12054            .buffer
12055            .read(cx)
12056            .buffer_for_anchor(breakpoint_position, cx)
12057        else {
12058            return;
12059        };
12060
12061        breakpoint_store.update(cx, |breakpoint_store, cx| {
12062            breakpoint_store.toggle_breakpoint(
12063                buffer,
12064                BreakpointWithPosition {
12065                    position: breakpoint_position.text_anchor,
12066                    bp: breakpoint,
12067                },
12068                edit_action,
12069                cx,
12070            );
12071        });
12072
12073        cx.notify();
12074    }
12075
12076    #[cfg(any(test, feature = "test-support"))]
12077    pub fn breakpoint_store(&self) -> Option<Entity<BreakpointStore>> {
12078        self.breakpoint_store.clone()
12079    }
12080
12081    pub fn prepare_restore_change(
12082        &self,
12083        revert_changes: &mut HashMap<BufferId, Vec<(Range<text::Anchor>, Rope)>>,
12084        hunk: &MultiBufferDiffHunk,
12085        cx: &mut App,
12086    ) -> Option<()> {
12087        if hunk.is_created_file() {
12088            return None;
12089        }
12090        let buffer = self.buffer.read(cx);
12091        let diff = buffer.diff_for(hunk.buffer_id)?;
12092        let buffer = buffer.buffer(hunk.buffer_id)?;
12093        let buffer = buffer.read(cx);
12094        let original_text = diff
12095            .read(cx)
12096            .base_text(cx)
12097            .as_rope()
12098            .slice(hunk.diff_base_byte_range.start.0..hunk.diff_base_byte_range.end.0);
12099        let buffer_snapshot = buffer.snapshot();
12100        let buffer_revert_changes = revert_changes.entry(buffer.remote_id()).or_default();
12101        if let Err(i) = buffer_revert_changes.binary_search_by(|probe| {
12102            probe
12103                .0
12104                .start
12105                .cmp(&hunk.buffer_range.start, &buffer_snapshot)
12106                .then(probe.0.end.cmp(&hunk.buffer_range.end, &buffer_snapshot))
12107        }) {
12108            buffer_revert_changes.insert(i, (hunk.buffer_range.clone(), original_text));
12109            Some(())
12110        } else {
12111            None
12112        }
12113    }
12114
12115    pub fn reverse_lines(&mut self, _: &ReverseLines, window: &mut Window, cx: &mut Context<Self>) {
12116        self.manipulate_immutable_lines(window, cx, |lines| lines.reverse())
12117    }
12118
12119    pub fn shuffle_lines(&mut self, _: &ShuffleLines, window: &mut Window, cx: &mut Context<Self>) {
12120        self.manipulate_immutable_lines(window, cx, |lines| lines.shuffle(&mut rand::rng()))
12121    }
12122
12123    pub fn rotate_selections_forward(
12124        &mut self,
12125        _: &RotateSelectionsForward,
12126        window: &mut Window,
12127        cx: &mut Context<Self>,
12128    ) {
12129        self.rotate_selections(window, cx, false)
12130    }
12131
12132    pub fn rotate_selections_backward(
12133        &mut self,
12134        _: &RotateSelectionsBackward,
12135        window: &mut Window,
12136        cx: &mut Context<Self>,
12137    ) {
12138        self.rotate_selections(window, cx, true)
12139    }
12140
12141    fn rotate_selections(&mut self, window: &mut Window, cx: &mut Context<Self>, reverse: bool) {
12142        self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
12143        let display_snapshot = self.display_snapshot(cx);
12144        let selections = self.selections.all::<MultiBufferOffset>(&display_snapshot);
12145
12146        if selections.len() < 2 {
12147            return;
12148        }
12149
12150        let (edits, new_selections) = {
12151            let buffer = self.buffer.read(cx).read(cx);
12152            let has_selections = selections.iter().any(|s| !s.is_empty());
12153            if has_selections {
12154                let mut selected_texts: Vec<String> = selections
12155                    .iter()
12156                    .map(|selection| {
12157                        buffer
12158                            .text_for_range(selection.start..selection.end)
12159                            .collect()
12160                    })
12161                    .collect();
12162
12163                if reverse {
12164                    selected_texts.rotate_left(1);
12165                } else {
12166                    selected_texts.rotate_right(1);
12167                }
12168
12169                let mut offset_delta: i64 = 0;
12170                let mut new_selections = Vec::new();
12171                let edits: Vec<_> = selections
12172                    .iter()
12173                    .zip(selected_texts.iter())
12174                    .map(|(selection, new_text)| {
12175                        let old_len = (selection.end.0 - selection.start.0) as i64;
12176                        let new_len = new_text.len() as i64;
12177                        let adjusted_start =
12178                            MultiBufferOffset((selection.start.0 as i64 + offset_delta) as usize);
12179                        let adjusted_end =
12180                            MultiBufferOffset((adjusted_start.0 as i64 + new_len) as usize);
12181
12182                        new_selections.push(Selection {
12183                            id: selection.id,
12184                            start: adjusted_start,
12185                            end: adjusted_end,
12186                            reversed: selection.reversed,
12187                            goal: selection.goal,
12188                        });
12189
12190                        offset_delta += new_len - old_len;
12191                        (selection.start..selection.end, new_text.clone())
12192                    })
12193                    .collect();
12194                (edits, new_selections)
12195            } else {
12196                let mut all_rows: Vec<u32> = selections
12197                    .iter()
12198                    .map(|selection| buffer.offset_to_point(selection.start).row)
12199                    .collect();
12200                all_rows.sort_unstable();
12201                all_rows.dedup();
12202
12203                if all_rows.len() < 2 {
12204                    return;
12205                }
12206
12207                let line_ranges: Vec<Range<MultiBufferOffset>> = all_rows
12208                    .iter()
12209                    .map(|&row| {
12210                        let start = Point::new(row, 0);
12211                        let end = Point::new(row, buffer.line_len(MultiBufferRow(row)));
12212                        buffer.point_to_offset(start)..buffer.point_to_offset(end)
12213                    })
12214                    .collect();
12215
12216                let mut line_texts: Vec<String> = line_ranges
12217                    .iter()
12218                    .map(|range| buffer.text_for_range(range.clone()).collect())
12219                    .collect();
12220
12221                if reverse {
12222                    line_texts.rotate_left(1);
12223                } else {
12224                    line_texts.rotate_right(1);
12225                }
12226
12227                let edits = line_ranges
12228                    .iter()
12229                    .zip(line_texts.iter())
12230                    .map(|(range, new_text)| (range.clone(), new_text.clone()))
12231                    .collect();
12232
12233                let num_rows = all_rows.len();
12234                let row_to_index: std::collections::HashMap<u32, usize> = all_rows
12235                    .iter()
12236                    .enumerate()
12237                    .map(|(i, &row)| (row, i))
12238                    .collect();
12239
12240                // Compute new line start offsets after rotation (handles CRLF)
12241                let newline_len = line_ranges[1].start.0 - line_ranges[0].end.0;
12242                let first_line_start = line_ranges[0].start.0;
12243                let mut new_line_starts: Vec<usize> = vec![first_line_start];
12244                for text in line_texts.iter().take(num_rows - 1) {
12245                    let prev_start = *new_line_starts.last().unwrap();
12246                    new_line_starts.push(prev_start + text.len() + newline_len);
12247                }
12248
12249                let new_selections = selections
12250                    .iter()
12251                    .map(|selection| {
12252                        let point = buffer.offset_to_point(selection.start);
12253                        let old_index = row_to_index[&point.row];
12254                        let new_index = if reverse {
12255                            (old_index + num_rows - 1) % num_rows
12256                        } else {
12257                            (old_index + 1) % num_rows
12258                        };
12259                        let new_offset =
12260                            MultiBufferOffset(new_line_starts[new_index] + point.column as usize);
12261                        Selection {
12262                            id: selection.id,
12263                            start: new_offset,
12264                            end: new_offset,
12265                            reversed: selection.reversed,
12266                            goal: selection.goal,
12267                        }
12268                    })
12269                    .collect();
12270
12271                (edits, new_selections)
12272            }
12273        };
12274
12275        self.transact(window, cx, |this, window, cx| {
12276            this.buffer.update(cx, |buffer, cx| {
12277                buffer.edit(edits, None, cx);
12278            });
12279            this.change_selections(Default::default(), window, cx, |s| {
12280                s.select(new_selections);
12281            });
12282        });
12283    }
12284
12285    fn manipulate_lines<M>(
12286        &mut self,
12287        window: &mut Window,
12288        cx: &mut Context<Self>,
12289        mut manipulate: M,
12290    ) where
12291        M: FnMut(&str) -> LineManipulationResult,
12292    {
12293        self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
12294
12295        let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
12296        let buffer = self.buffer.read(cx).snapshot(cx);
12297
12298        let mut edits = Vec::new();
12299
12300        let selections = self.selections.all::<Point>(&display_map);
12301        let mut selections = selections.iter().peekable();
12302        let mut contiguous_row_selections = Vec::new();
12303        let mut new_selections = Vec::new();
12304        let mut added_lines = 0;
12305        let mut removed_lines = 0;
12306
12307        while let Some(selection) = selections.next() {
12308            let (start_row, end_row) = consume_contiguous_rows(
12309                &mut contiguous_row_selections,
12310                selection,
12311                &display_map,
12312                &mut selections,
12313            );
12314
12315            let start_point = Point::new(start_row.0, 0);
12316            let end_point = Point::new(
12317                end_row.previous_row().0,
12318                buffer.line_len(end_row.previous_row()),
12319            );
12320            let text = buffer
12321                .text_for_range(start_point..end_point)
12322                .collect::<String>();
12323
12324            let LineManipulationResult {
12325                new_text,
12326                line_count_before,
12327                line_count_after,
12328            } = manipulate(&text);
12329
12330            edits.push((start_point..end_point, new_text));
12331
12332            // Selections must change based on added and removed line count
12333            let start_row =
12334                MultiBufferRow(start_point.row + added_lines as u32 - removed_lines as u32);
12335            let end_row = MultiBufferRow(start_row.0 + line_count_after.saturating_sub(1) as u32);
12336            new_selections.push(Selection {
12337                id: selection.id,
12338                start: start_row,
12339                end: end_row,
12340                goal: SelectionGoal::None,
12341                reversed: selection.reversed,
12342            });
12343
12344            if line_count_after > line_count_before {
12345                added_lines += line_count_after - line_count_before;
12346            } else if line_count_before > line_count_after {
12347                removed_lines += line_count_before - line_count_after;
12348            }
12349        }
12350
12351        self.transact(window, cx, |this, window, cx| {
12352            let buffer = this.buffer.update(cx, |buffer, cx| {
12353                buffer.edit(edits, None, cx);
12354                buffer.snapshot(cx)
12355            });
12356
12357            // Recalculate offsets on newly edited buffer
12358            let new_selections = new_selections
12359                .iter()
12360                .map(|s| {
12361                    let start_point = Point::new(s.start.0, 0);
12362                    let end_point = Point::new(s.end.0, buffer.line_len(s.end));
12363                    Selection {
12364                        id: s.id,
12365                        start: buffer.point_to_offset(start_point),
12366                        end: buffer.point_to_offset(end_point),
12367                        goal: s.goal,
12368                        reversed: s.reversed,
12369                    }
12370                })
12371                .collect();
12372
12373            this.change_selections(Default::default(), window, cx, |s| {
12374                s.select(new_selections);
12375            });
12376
12377            this.request_autoscroll(Autoscroll::fit(), cx);
12378        });
12379    }
12380
12381    fn manipulate_immutable_lines<Fn>(
12382        &mut self,
12383        window: &mut Window,
12384        cx: &mut Context<Self>,
12385        mut callback: Fn,
12386    ) where
12387        Fn: FnMut(&mut Vec<&str>),
12388    {
12389        self.manipulate_lines(window, cx, |text| {
12390            let mut lines: Vec<&str> = text.split('\n').collect();
12391            let line_count_before = lines.len();
12392
12393            callback(&mut lines);
12394
12395            LineManipulationResult {
12396                new_text: lines.join("\n"),
12397                line_count_before,
12398                line_count_after: lines.len(),
12399            }
12400        });
12401    }
12402
12403    fn manipulate_mutable_lines<Fn>(
12404        &mut self,
12405        window: &mut Window,
12406        cx: &mut Context<Self>,
12407        mut callback: Fn,
12408    ) where
12409        Fn: FnMut(&mut Vec<Cow<'_, str>>),
12410    {
12411        self.manipulate_lines(window, cx, |text| {
12412            let mut lines: Vec<Cow<str>> = text.split('\n').map(Cow::from).collect();
12413            let line_count_before = lines.len();
12414
12415            callback(&mut lines);
12416
12417            LineManipulationResult {
12418                new_text: lines.join("\n"),
12419                line_count_before,
12420                line_count_after: lines.len(),
12421            }
12422        });
12423    }
12424
12425    pub fn convert_indentation_to_spaces(
12426        &mut self,
12427        _: &ConvertIndentationToSpaces,
12428        window: &mut Window,
12429        cx: &mut Context<Self>,
12430    ) {
12431        let settings = self.buffer.read(cx).language_settings(cx);
12432        let tab_size = settings.tab_size.get() as usize;
12433
12434        self.manipulate_mutable_lines(window, cx, |lines| {
12435            // Allocates a reasonably sized scratch buffer once for the whole loop
12436            let mut reindented_line = String::with_capacity(MAX_LINE_LEN);
12437            // Avoids recomputing spaces that could be inserted many times
12438            let space_cache: Vec<Vec<char>> = (1..=tab_size)
12439                .map(|n| IndentSize::spaces(n as u32).chars().collect())
12440                .collect();
12441
12442            for line in lines.iter_mut().filter(|line| !line.is_empty()) {
12443                let mut chars = line.as_ref().chars();
12444                let mut col = 0;
12445                let mut changed = false;
12446
12447                for ch in chars.by_ref() {
12448                    match ch {
12449                        ' ' => {
12450                            reindented_line.push(' ');
12451                            col += 1;
12452                        }
12453                        '\t' => {
12454                            // \t are converted to spaces depending on the current column
12455                            let spaces_len = tab_size - (col % tab_size);
12456                            reindented_line.extend(&space_cache[spaces_len - 1]);
12457                            col += spaces_len;
12458                            changed = true;
12459                        }
12460                        _ => {
12461                            // If we dont append before break, the character is consumed
12462                            reindented_line.push(ch);
12463                            break;
12464                        }
12465                    }
12466                }
12467
12468                if !changed {
12469                    reindented_line.clear();
12470                    continue;
12471                }
12472                // Append the rest of the line and replace old reference with new one
12473                reindented_line.extend(chars);
12474                *line = Cow::Owned(reindented_line.clone());
12475                reindented_line.clear();
12476            }
12477        });
12478    }
12479
12480    pub fn convert_indentation_to_tabs(
12481        &mut self,
12482        _: &ConvertIndentationToTabs,
12483        window: &mut Window,
12484        cx: &mut Context<Self>,
12485    ) {
12486        let settings = self.buffer.read(cx).language_settings(cx);
12487        let tab_size = settings.tab_size.get() as usize;
12488
12489        self.manipulate_mutable_lines(window, cx, |lines| {
12490            // Allocates a reasonably sized buffer once for the whole loop
12491            let mut reindented_line = String::with_capacity(MAX_LINE_LEN);
12492            // Avoids recomputing spaces that could be inserted many times
12493            let space_cache: Vec<Vec<char>> = (1..=tab_size)
12494                .map(|n| IndentSize::spaces(n as u32).chars().collect())
12495                .collect();
12496
12497            for line in lines.iter_mut().filter(|line| !line.is_empty()) {
12498                let mut chars = line.chars();
12499                let mut spaces_count = 0;
12500                let mut first_non_indent_char = None;
12501                let mut changed = false;
12502
12503                for ch in chars.by_ref() {
12504                    match ch {
12505                        ' ' => {
12506                            // Keep track of spaces. Append \t when we reach tab_size
12507                            spaces_count += 1;
12508                            changed = true;
12509                            if spaces_count == tab_size {
12510                                reindented_line.push('\t');
12511                                spaces_count = 0;
12512                            }
12513                        }
12514                        '\t' => {
12515                            reindented_line.push('\t');
12516                            spaces_count = 0;
12517                        }
12518                        _ => {
12519                            // Dont append it yet, we might have remaining spaces
12520                            first_non_indent_char = Some(ch);
12521                            break;
12522                        }
12523                    }
12524                }
12525
12526                if !changed {
12527                    reindented_line.clear();
12528                    continue;
12529                }
12530                // Remaining spaces that didn't make a full tab stop
12531                if spaces_count > 0 {
12532                    reindented_line.extend(&space_cache[spaces_count - 1]);
12533                }
12534                // If we consume an extra character that was not indentation, add it back
12535                if let Some(extra_char) = first_non_indent_char {
12536                    reindented_line.push(extra_char);
12537                }
12538                // Append the rest of the line and replace old reference with new one
12539                reindented_line.extend(chars);
12540                *line = Cow::Owned(reindented_line.clone());
12541                reindented_line.clear();
12542            }
12543        });
12544    }
12545
12546    pub fn convert_to_upper_case(
12547        &mut self,
12548        _: &ConvertToUpperCase,
12549        window: &mut Window,
12550        cx: &mut Context<Self>,
12551    ) {
12552        self.manipulate_text(window, cx, |text| text.to_uppercase())
12553    }
12554
12555    pub fn convert_to_lower_case(
12556        &mut self,
12557        _: &ConvertToLowerCase,
12558        window: &mut Window,
12559        cx: &mut Context<Self>,
12560    ) {
12561        self.manipulate_text(window, cx, |text| text.to_lowercase())
12562    }
12563
12564    pub fn convert_to_title_case(
12565        &mut self,
12566        _: &ConvertToTitleCase,
12567        window: &mut Window,
12568        cx: &mut Context<Self>,
12569    ) {
12570        self.manipulate_text(window, cx, |text| {
12571            text.split('\n')
12572                .map(|line| line.to_case(Case::Title))
12573                .join("\n")
12574        })
12575    }
12576
12577    pub fn convert_to_snake_case(
12578        &mut self,
12579        _: &ConvertToSnakeCase,
12580        window: &mut Window,
12581        cx: &mut Context<Self>,
12582    ) {
12583        self.manipulate_text(window, cx, |text| text.to_case(Case::Snake))
12584    }
12585
12586    pub fn convert_to_kebab_case(
12587        &mut self,
12588        _: &ConvertToKebabCase,
12589        window: &mut Window,
12590        cx: &mut Context<Self>,
12591    ) {
12592        self.manipulate_text(window, cx, |text| text.to_case(Case::Kebab))
12593    }
12594
12595    pub fn convert_to_upper_camel_case(
12596        &mut self,
12597        _: &ConvertToUpperCamelCase,
12598        window: &mut Window,
12599        cx: &mut Context<Self>,
12600    ) {
12601        self.manipulate_text(window, cx, |text| {
12602            text.split('\n')
12603                .map(|line| line.to_case(Case::UpperCamel))
12604                .join("\n")
12605        })
12606    }
12607
12608    pub fn convert_to_lower_camel_case(
12609        &mut self,
12610        _: &ConvertToLowerCamelCase,
12611        window: &mut Window,
12612        cx: &mut Context<Self>,
12613    ) {
12614        self.manipulate_text(window, cx, |text| text.to_case(Case::Camel))
12615    }
12616
12617    pub fn convert_to_opposite_case(
12618        &mut self,
12619        _: &ConvertToOppositeCase,
12620        window: &mut Window,
12621        cx: &mut Context<Self>,
12622    ) {
12623        self.manipulate_text(window, cx, |text| {
12624            text.chars()
12625                .fold(String::with_capacity(text.len()), |mut t, c| {
12626                    if c.is_uppercase() {
12627                        t.extend(c.to_lowercase());
12628                    } else {
12629                        t.extend(c.to_uppercase());
12630                    }
12631                    t
12632                })
12633        })
12634    }
12635
12636    pub fn convert_to_sentence_case(
12637        &mut self,
12638        _: &ConvertToSentenceCase,
12639        window: &mut Window,
12640        cx: &mut Context<Self>,
12641    ) {
12642        self.manipulate_text(window, cx, |text| text.to_case(Case::Sentence))
12643    }
12644
12645    pub fn toggle_case(&mut self, _: &ToggleCase, window: &mut Window, cx: &mut Context<Self>) {
12646        self.manipulate_text(window, cx, |text| {
12647            let has_upper_case_characters = text.chars().any(|c| c.is_uppercase());
12648            if has_upper_case_characters {
12649                text.to_lowercase()
12650            } else {
12651                text.to_uppercase()
12652            }
12653        })
12654    }
12655
12656    pub fn convert_to_rot13(
12657        &mut self,
12658        _: &ConvertToRot13,
12659        window: &mut Window,
12660        cx: &mut Context<Self>,
12661    ) {
12662        self.manipulate_text(window, cx, |text| {
12663            text.chars()
12664                .map(|c| match c {
12665                    'A'..='M' | 'a'..='m' => ((c as u8) + 13) as char,
12666                    'N'..='Z' | 'n'..='z' => ((c as u8) - 13) as char,
12667                    _ => c,
12668                })
12669                .collect()
12670        })
12671    }
12672
12673    pub fn convert_to_rot47(
12674        &mut self,
12675        _: &ConvertToRot47,
12676        window: &mut Window,
12677        cx: &mut Context<Self>,
12678    ) {
12679        self.manipulate_text(window, cx, |text| {
12680            text.chars()
12681                .map(|c| {
12682                    let code_point = c as u32;
12683                    if code_point >= 33 && code_point <= 126 {
12684                        return char::from_u32(33 + ((code_point + 14) % 94)).unwrap();
12685                    }
12686                    c
12687                })
12688                .collect()
12689        })
12690    }
12691
12692    fn manipulate_text<Fn>(&mut self, window: &mut Window, cx: &mut Context<Self>, mut callback: Fn)
12693    where
12694        Fn: FnMut(&str) -> String,
12695    {
12696        let buffer = self.buffer.read(cx).snapshot(cx);
12697
12698        let mut new_selections = Vec::new();
12699        let mut edits = Vec::new();
12700        let mut selection_adjustment = 0isize;
12701
12702        for selection in self.selections.all_adjusted(&self.display_snapshot(cx)) {
12703            let selection_is_empty = selection.is_empty();
12704
12705            let (start, end) = if selection_is_empty {
12706                let (word_range, _) = buffer.surrounding_word(selection.start, None);
12707                (word_range.start, word_range.end)
12708            } else {
12709                (
12710                    buffer.point_to_offset(selection.start),
12711                    buffer.point_to_offset(selection.end),
12712                )
12713            };
12714
12715            let text = buffer.text_for_range(start..end).collect::<String>();
12716            let old_length = text.len() as isize;
12717            let text = callback(&text);
12718
12719            new_selections.push(Selection {
12720                start: MultiBufferOffset((start.0 as isize - selection_adjustment) as usize),
12721                end: MultiBufferOffset(
12722                    ((start.0 + text.len()) as isize - selection_adjustment) as usize,
12723                ),
12724                goal: SelectionGoal::None,
12725                id: selection.id,
12726                reversed: selection.reversed,
12727            });
12728
12729            selection_adjustment += old_length - text.len() as isize;
12730
12731            edits.push((start..end, text));
12732        }
12733
12734        self.transact(window, cx, |this, window, cx| {
12735            this.buffer.update(cx, |buffer, cx| {
12736                buffer.edit(edits, None, cx);
12737            });
12738
12739            this.change_selections(Default::default(), window, cx, |s| {
12740                s.select(new_selections);
12741            });
12742
12743            this.request_autoscroll(Autoscroll::fit(), cx);
12744        });
12745    }
12746
12747    pub fn move_selection_on_drop(
12748        &mut self,
12749        selection: &Selection<Anchor>,
12750        target: DisplayPoint,
12751        is_cut: bool,
12752        window: &mut Window,
12753        cx: &mut Context<Self>,
12754    ) {
12755        let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
12756        let buffer = display_map.buffer_snapshot();
12757        let mut edits = Vec::new();
12758        let insert_point = display_map
12759            .clip_point(target, Bias::Left)
12760            .to_point(&display_map);
12761        let text = buffer
12762            .text_for_range(selection.start..selection.end)
12763            .collect::<String>();
12764        if is_cut {
12765            edits.push(((selection.start..selection.end), String::new()));
12766        }
12767        let insert_anchor = buffer.anchor_before(insert_point);
12768        edits.push(((insert_anchor..insert_anchor), text));
12769        let last_edit_start = insert_anchor.bias_left(buffer);
12770        let last_edit_end = insert_anchor.bias_right(buffer);
12771        self.transact(window, cx, |this, window, cx| {
12772            this.buffer.update(cx, |buffer, cx| {
12773                buffer.edit(edits, None, cx);
12774            });
12775            this.change_selections(Default::default(), window, cx, |s| {
12776                s.select_anchor_ranges([last_edit_start..last_edit_end]);
12777            });
12778        });
12779    }
12780
12781    pub fn clear_selection_drag_state(&mut self) {
12782        self.selection_drag_state = SelectionDragState::None;
12783    }
12784
12785    pub fn duplicate(
12786        &mut self,
12787        upwards: bool,
12788        whole_lines: bool,
12789        window: &mut Window,
12790        cx: &mut Context<Self>,
12791    ) {
12792        self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
12793
12794        let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
12795        let buffer = display_map.buffer_snapshot();
12796        let selections = self.selections.all::<Point>(&display_map);
12797
12798        let mut edits = Vec::new();
12799        let mut selections_iter = selections.iter().peekable();
12800        while let Some(selection) = selections_iter.next() {
12801            let mut rows = selection.spanned_rows(false, &display_map);
12802            // duplicate line-wise
12803            if whole_lines || selection.start == selection.end {
12804                // Avoid duplicating the same lines twice.
12805                while let Some(next_selection) = selections_iter.peek() {
12806                    let next_rows = next_selection.spanned_rows(false, &display_map);
12807                    if next_rows.start < rows.end {
12808                        rows.end = next_rows.end;
12809                        selections_iter.next().unwrap();
12810                    } else {
12811                        break;
12812                    }
12813                }
12814
12815                // Copy the text from the selected row region and splice it either at the start
12816                // or end of the region.
12817                let start = Point::new(rows.start.0, 0);
12818                let end = Point::new(
12819                    rows.end.previous_row().0,
12820                    buffer.line_len(rows.end.previous_row()),
12821                );
12822
12823                let mut text = buffer.text_for_range(start..end).collect::<String>();
12824
12825                let insert_location = if upwards {
12826                    // When duplicating upward, we need to insert before the current line.
12827                    // If we're on the last line and it doesn't end with a newline,
12828                    // we need to add a newline before the duplicated content.
12829                    let needs_leading_newline = rows.end.0 >= buffer.max_point().row
12830                        && buffer.max_point().column > 0
12831                        && !text.ends_with('\n');
12832
12833                    if needs_leading_newline {
12834                        text.insert(0, '\n');
12835                        end
12836                    } else {
12837                        text.push('\n');
12838                        Point::new(rows.start.0, 0)
12839                    }
12840                } else {
12841                    text.push('\n');
12842                    start
12843                };
12844                edits.push((insert_location..insert_location, text));
12845            } else {
12846                // duplicate character-wise
12847                let start = selection.start;
12848                let end = selection.end;
12849                let text = buffer.text_for_range(start..end).collect::<String>();
12850                edits.push((selection.end..selection.end, text));
12851            }
12852        }
12853
12854        self.transact(window, cx, |this, window, cx| {
12855            this.buffer.update(cx, |buffer, cx| {
12856                buffer.edit(edits, None, cx);
12857            });
12858
12859            // When duplicating upward with whole lines, move the cursor to the duplicated line
12860            if upwards && whole_lines {
12861                let display_map = this.display_map.update(cx, |map, cx| map.snapshot(cx));
12862
12863                this.change_selections(SelectionEffects::no_scroll(), window, cx, |s| {
12864                    let mut new_ranges = Vec::new();
12865                    let selections = s.all::<Point>(&display_map);
12866                    let mut selections_iter = selections.iter().peekable();
12867
12868                    while let Some(first_selection) = selections_iter.next() {
12869                        // Group contiguous selections together to find the total row span
12870                        let mut group_selections = vec![first_selection];
12871                        let mut rows = first_selection.spanned_rows(false, &display_map);
12872
12873                        while let Some(next_selection) = selections_iter.peek() {
12874                            let next_rows = next_selection.spanned_rows(false, &display_map);
12875                            if next_rows.start < rows.end {
12876                                rows.end = next_rows.end;
12877                                group_selections.push(selections_iter.next().unwrap());
12878                            } else {
12879                                break;
12880                            }
12881                        }
12882
12883                        let row_count = rows.end.0 - rows.start.0;
12884
12885                        // Move all selections in this group up by the total number of duplicated rows
12886                        for selection in group_selections {
12887                            let new_start = Point::new(
12888                                selection.start.row.saturating_sub(row_count),
12889                                selection.start.column,
12890                            );
12891
12892                            let new_end = Point::new(
12893                                selection.end.row.saturating_sub(row_count),
12894                                selection.end.column,
12895                            );
12896
12897                            new_ranges.push(new_start..new_end);
12898                        }
12899                    }
12900
12901                    s.select_ranges(new_ranges);
12902                });
12903            }
12904
12905            this.request_autoscroll(Autoscroll::fit(), cx);
12906        });
12907    }
12908
12909    pub fn duplicate_line_up(
12910        &mut self,
12911        _: &DuplicateLineUp,
12912        window: &mut Window,
12913        cx: &mut Context<Self>,
12914    ) {
12915        self.duplicate(true, true, window, cx);
12916    }
12917
12918    pub fn duplicate_line_down(
12919        &mut self,
12920        _: &DuplicateLineDown,
12921        window: &mut Window,
12922        cx: &mut Context<Self>,
12923    ) {
12924        self.duplicate(false, true, window, cx);
12925    }
12926
12927    pub fn duplicate_selection(
12928        &mut self,
12929        _: &DuplicateSelection,
12930        window: &mut Window,
12931        cx: &mut Context<Self>,
12932    ) {
12933        self.duplicate(false, false, window, cx);
12934    }
12935
12936    pub fn move_line_up(&mut self, _: &MoveLineUp, window: &mut Window, cx: &mut Context<Self>) {
12937        self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
12938        if self.mode.is_single_line() {
12939            cx.propagate();
12940            return;
12941        }
12942
12943        let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
12944        let buffer = self.buffer.read(cx).snapshot(cx);
12945
12946        let mut edits = Vec::new();
12947        let mut unfold_ranges = Vec::new();
12948        let mut refold_creases = Vec::new();
12949
12950        let selections = self.selections.all::<Point>(&display_map);
12951        let mut selections = selections.iter().peekable();
12952        let mut contiguous_row_selections = Vec::new();
12953        let mut new_selections = Vec::new();
12954
12955        while let Some(selection) = selections.next() {
12956            // Find all the selections that span a contiguous row range
12957            let (start_row, end_row) = consume_contiguous_rows(
12958                &mut contiguous_row_selections,
12959                selection,
12960                &display_map,
12961                &mut selections,
12962            );
12963
12964            // Move the text spanned by the row range to be before the line preceding the row range
12965            if start_row.0 > 0 {
12966                let range_to_move = Point::new(
12967                    start_row.previous_row().0,
12968                    buffer.line_len(start_row.previous_row()),
12969                )
12970                    ..Point::new(
12971                        end_row.previous_row().0,
12972                        buffer.line_len(end_row.previous_row()),
12973                    );
12974                let insertion_point = display_map
12975                    .prev_line_boundary(Point::new(start_row.previous_row().0, 0))
12976                    .0;
12977
12978                // Don't move lines across excerpts
12979                if buffer
12980                    .excerpt_containing(insertion_point..range_to_move.end)
12981                    .is_some()
12982                {
12983                    let text = buffer
12984                        .text_for_range(range_to_move.clone())
12985                        .flat_map(|s| s.chars())
12986                        .skip(1)
12987                        .chain(['\n'])
12988                        .collect::<String>();
12989
12990                    edits.push((
12991                        buffer.anchor_after(range_to_move.start)
12992                            ..buffer.anchor_before(range_to_move.end),
12993                        String::new(),
12994                    ));
12995                    let insertion_anchor = buffer.anchor_after(insertion_point);
12996                    edits.push((insertion_anchor..insertion_anchor, text));
12997
12998                    let row_delta = range_to_move.start.row - insertion_point.row + 1;
12999
13000                    // Move selections up
13001                    new_selections.extend(contiguous_row_selections.drain(..).map(
13002                        |mut selection| {
13003                            selection.start.row -= row_delta;
13004                            selection.end.row -= row_delta;
13005                            selection
13006                        },
13007                    ));
13008
13009                    // Move folds up
13010                    unfold_ranges.push(range_to_move.clone());
13011                    for fold in display_map.folds_in_range(
13012                        buffer.anchor_before(range_to_move.start)
13013                            ..buffer.anchor_after(range_to_move.end),
13014                    ) {
13015                        let mut start = fold.range.start.to_point(&buffer);
13016                        let mut end = fold.range.end.to_point(&buffer);
13017                        start.row -= row_delta;
13018                        end.row -= row_delta;
13019                        refold_creases.push(Crease::simple(start..end, fold.placeholder.clone()));
13020                    }
13021                }
13022            }
13023
13024            // If we didn't move line(s), preserve the existing selections
13025            new_selections.append(&mut contiguous_row_selections);
13026        }
13027
13028        self.transact(window, cx, |this, window, cx| {
13029            this.unfold_ranges(&unfold_ranges, true, true, cx);
13030            this.buffer.update(cx, |buffer, cx| {
13031                for (range, text) in edits {
13032                    buffer.edit([(range, text)], None, cx);
13033                }
13034            });
13035            this.fold_creases(refold_creases, true, window, cx);
13036            this.change_selections(Default::default(), window, cx, |s| {
13037                s.select(new_selections);
13038            })
13039        });
13040    }
13041
13042    pub fn move_line_down(
13043        &mut self,
13044        _: &MoveLineDown,
13045        window: &mut Window,
13046        cx: &mut Context<Self>,
13047    ) {
13048        self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
13049        if self.mode.is_single_line() {
13050            cx.propagate();
13051            return;
13052        }
13053
13054        let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
13055        let buffer = self.buffer.read(cx).snapshot(cx);
13056
13057        let mut edits = Vec::new();
13058        let mut unfold_ranges = Vec::new();
13059        let mut refold_creases = Vec::new();
13060
13061        let selections = self.selections.all::<Point>(&display_map);
13062        let mut selections = selections.iter().peekable();
13063        let mut contiguous_row_selections = Vec::new();
13064        let mut new_selections = Vec::new();
13065
13066        while let Some(selection) = selections.next() {
13067            // Find all the selections that span a contiguous row range
13068            let (start_row, end_row) = consume_contiguous_rows(
13069                &mut contiguous_row_selections,
13070                selection,
13071                &display_map,
13072                &mut selections,
13073            );
13074
13075            // Move the text spanned by the row range to be after the last line of the row range
13076            if end_row.0 <= buffer.max_point().row {
13077                let range_to_move =
13078                    MultiBufferPoint::new(start_row.0, 0)..MultiBufferPoint::new(end_row.0, 0);
13079                let insertion_point = display_map
13080                    .next_line_boundary(MultiBufferPoint::new(end_row.0, 0))
13081                    .0;
13082
13083                // Don't move lines across excerpt boundaries
13084                if buffer
13085                    .excerpt_containing(range_to_move.start..insertion_point)
13086                    .is_some()
13087                {
13088                    let mut text = String::from("\n");
13089                    text.extend(buffer.text_for_range(range_to_move.clone()));
13090                    text.pop(); // Drop trailing newline
13091                    edits.push((
13092                        buffer.anchor_after(range_to_move.start)
13093                            ..buffer.anchor_before(range_to_move.end),
13094                        String::new(),
13095                    ));
13096                    let insertion_anchor = buffer.anchor_after(insertion_point);
13097                    edits.push((insertion_anchor..insertion_anchor, text));
13098
13099                    let row_delta = insertion_point.row - range_to_move.end.row + 1;
13100
13101                    // Move selections down
13102                    new_selections.extend(contiguous_row_selections.drain(..).map(
13103                        |mut selection| {
13104                            selection.start.row += row_delta;
13105                            selection.end.row += row_delta;
13106                            selection
13107                        },
13108                    ));
13109
13110                    // Move folds down
13111                    unfold_ranges.push(range_to_move.clone());
13112                    for fold in display_map.folds_in_range(
13113                        buffer.anchor_before(range_to_move.start)
13114                            ..buffer.anchor_after(range_to_move.end),
13115                    ) {
13116                        let mut start = fold.range.start.to_point(&buffer);
13117                        let mut end = fold.range.end.to_point(&buffer);
13118                        start.row += row_delta;
13119                        end.row += row_delta;
13120                        refold_creases.push(Crease::simple(start..end, fold.placeholder.clone()));
13121                    }
13122                }
13123            }
13124
13125            // If we didn't move line(s), preserve the existing selections
13126            new_selections.append(&mut contiguous_row_selections);
13127        }
13128
13129        self.transact(window, cx, |this, window, cx| {
13130            this.unfold_ranges(&unfold_ranges, true, true, cx);
13131            this.buffer.update(cx, |buffer, cx| {
13132                for (range, text) in edits {
13133                    buffer.edit([(range, text)], None, cx);
13134                }
13135            });
13136            this.fold_creases(refold_creases, true, window, cx);
13137            this.change_selections(Default::default(), window, cx, |s| s.select(new_selections));
13138        });
13139    }
13140
13141    pub fn transpose(&mut self, _: &Transpose, window: &mut Window, cx: &mut Context<Self>) {
13142        self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
13143        let text_layout_details = &self.text_layout_details(window, cx);
13144        self.transact(window, cx, |this, window, cx| {
13145            let edits = this.change_selections(Default::default(), window, cx, |s| {
13146                let mut edits: Vec<(Range<MultiBufferOffset>, String)> = Default::default();
13147                s.move_with(&mut |display_map, selection| {
13148                    if !selection.is_empty() {
13149                        return;
13150                    }
13151
13152                    let mut head = selection.head();
13153                    let mut transpose_offset = head.to_offset(display_map, Bias::Right);
13154                    if head.column() == display_map.line_len(head.row()) {
13155                        transpose_offset = display_map
13156                            .buffer_snapshot()
13157                            .clip_offset(transpose_offset.saturating_sub_usize(1), Bias::Left);
13158                    }
13159
13160                    if transpose_offset == MultiBufferOffset(0) {
13161                        return;
13162                    }
13163
13164                    *head.column_mut() += 1;
13165                    head = display_map.clip_point(head, Bias::Right);
13166                    let goal = SelectionGoal::HorizontalPosition(
13167                        display_map
13168                            .x_for_display_point(head, text_layout_details)
13169                            .into(),
13170                    );
13171                    selection.collapse_to(head, goal);
13172
13173                    let transpose_start = display_map
13174                        .buffer_snapshot()
13175                        .clip_offset(transpose_offset.saturating_sub_usize(1), Bias::Left);
13176                    if edits.last().is_none_or(|e| e.0.end <= transpose_start) {
13177                        let transpose_end = display_map
13178                            .buffer_snapshot()
13179                            .clip_offset(transpose_offset + 1usize, Bias::Right);
13180                        if let Some(ch) = display_map
13181                            .buffer_snapshot()
13182                            .chars_at(transpose_start)
13183                            .next()
13184                        {
13185                            edits.push((transpose_start..transpose_offset, String::new()));
13186                            edits.push((transpose_end..transpose_end, ch.to_string()));
13187                        }
13188                    }
13189                });
13190                edits
13191            });
13192            this.buffer
13193                .update(cx, |buffer, cx| buffer.edit(edits, None, cx));
13194            let selections = this
13195                .selections
13196                .all::<MultiBufferOffset>(&this.display_snapshot(cx));
13197            this.change_selections(Default::default(), window, cx, |s| {
13198                s.select(selections);
13199            });
13200        });
13201    }
13202
13203    pub fn rewrap(&mut self, _: &Rewrap, _: &mut Window, cx: &mut Context<Self>) {
13204        self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
13205        if self.mode.is_single_line() {
13206            cx.propagate();
13207            return;
13208        }
13209
13210        self.rewrap_impl(RewrapOptions::default(), cx)
13211    }
13212
13213    pub fn rewrap_impl(&mut self, options: RewrapOptions, cx: &mut Context<Self>) {
13214        let buffer = self.buffer.read(cx).snapshot(cx);
13215        let selections = self.selections.all::<Point>(&self.display_snapshot(cx));
13216
13217        #[derive(Clone, Debug, PartialEq)]
13218        enum CommentFormat {
13219            /// single line comment, with prefix for line
13220            Line(String),
13221            /// single line within a block comment, with prefix for line
13222            BlockLine(String),
13223            /// a single line of a block comment that includes the initial delimiter
13224            BlockCommentWithStart(BlockCommentConfig),
13225            /// a single line of a block comment that includes the ending delimiter
13226            BlockCommentWithEnd(BlockCommentConfig),
13227        }
13228
13229        // Split selections to respect paragraph, indent, and comment prefix boundaries.
13230        let wrap_ranges = selections.into_iter().flat_map(|selection| {
13231            let mut non_blank_rows_iter = (selection.start.row..=selection.end.row)
13232                .filter(|row| !buffer.is_line_blank(MultiBufferRow(*row)))
13233                .peekable();
13234
13235            let first_row = if let Some(&row) = non_blank_rows_iter.peek() {
13236                row
13237            } else {
13238                return Vec::new();
13239            };
13240
13241            let language_settings = buffer.language_settings_at(selection.head(), cx);
13242            let language_scope = buffer.language_scope_at(selection.head());
13243
13244            let indent_and_prefix_for_row =
13245                |row: u32| -> (IndentSize, Option<CommentFormat>, Option<String>) {
13246                    let indent = buffer.indent_size_for_line(MultiBufferRow(row));
13247                    let (comment_prefix, rewrap_prefix) = if let Some(language_scope) =
13248                        &language_scope
13249                    {
13250                        let indent_end = Point::new(row, indent.len);
13251                        let line_end = Point::new(row, buffer.line_len(MultiBufferRow(row)));
13252                        let line_text_after_indent = buffer
13253                            .text_for_range(indent_end..line_end)
13254                            .collect::<String>();
13255
13256                        let is_within_comment_override = buffer
13257                            .language_scope_at(indent_end)
13258                            .is_some_and(|scope| scope.override_name() == Some("comment"));
13259                        let comment_delimiters = if is_within_comment_override {
13260                            // we are within a comment syntax node, but we don't
13261                            // yet know what kind of comment: block, doc or line
13262                            match (
13263                                language_scope.documentation_comment(),
13264                                language_scope.block_comment(),
13265                            ) {
13266                                (Some(config), _) | (_, Some(config))
13267                                    if buffer.contains_str_at(indent_end, &config.start) =>
13268                                {
13269                                    Some(CommentFormat::BlockCommentWithStart(config.clone()))
13270                                }
13271                                (Some(config), _) | (_, Some(config))
13272                                    if line_text_after_indent.ends_with(config.end.as_ref()) =>
13273                                {
13274                                    Some(CommentFormat::BlockCommentWithEnd(config.clone()))
13275                                }
13276                                (Some(config), _) | (_, Some(config))
13277                                    if buffer.contains_str_at(indent_end, &config.prefix) =>
13278                                {
13279                                    Some(CommentFormat::BlockLine(config.prefix.to_string()))
13280                                }
13281                                (_, _) => language_scope
13282                                    .line_comment_prefixes()
13283                                    .iter()
13284                                    .find(|prefix| buffer.contains_str_at(indent_end, prefix))
13285                                    .map(|prefix| CommentFormat::Line(prefix.to_string())),
13286                            }
13287                        } else {
13288                            // we not in an overridden comment node, but we may
13289                            // be within a non-overridden line comment node
13290                            language_scope
13291                                .line_comment_prefixes()
13292                                .iter()
13293                                .find(|prefix| buffer.contains_str_at(indent_end, prefix))
13294                                .map(|prefix| CommentFormat::Line(prefix.to_string()))
13295                        };
13296
13297                        let rewrap_prefix = language_scope
13298                            .rewrap_prefixes()
13299                            .iter()
13300                            .find_map(|prefix_regex| {
13301                                prefix_regex.find(&line_text_after_indent).map(|mat| {
13302                                    if mat.start() == 0 {
13303                                        Some(mat.as_str().to_string())
13304                                    } else {
13305                                        None
13306                                    }
13307                                })
13308                            })
13309                            .flatten();
13310                        (comment_delimiters, rewrap_prefix)
13311                    } else {
13312                        (None, None)
13313                    };
13314                    (indent, comment_prefix, rewrap_prefix)
13315                };
13316
13317            let mut ranges = Vec::new();
13318            let from_empty_selection = selection.is_empty();
13319
13320            let mut current_range_start = first_row;
13321            let mut prev_row = first_row;
13322            let (
13323                mut current_range_indent,
13324                mut current_range_comment_delimiters,
13325                mut current_range_rewrap_prefix,
13326            ) = indent_and_prefix_for_row(first_row);
13327
13328            for row in non_blank_rows_iter.skip(1) {
13329                let has_paragraph_break = row > prev_row + 1;
13330
13331                let (row_indent, row_comment_delimiters, row_rewrap_prefix) =
13332                    indent_and_prefix_for_row(row);
13333
13334                let has_indent_change = row_indent != current_range_indent;
13335                let has_comment_change = row_comment_delimiters != current_range_comment_delimiters;
13336
13337                let has_boundary_change = has_comment_change
13338                    || row_rewrap_prefix.is_some()
13339                    || (has_indent_change && current_range_comment_delimiters.is_some());
13340
13341                if has_paragraph_break || has_boundary_change {
13342                    ranges.push((
13343                        language_settings.clone(),
13344                        Point::new(current_range_start, 0)
13345                            ..Point::new(prev_row, buffer.line_len(MultiBufferRow(prev_row))),
13346                        current_range_indent,
13347                        current_range_comment_delimiters.clone(),
13348                        current_range_rewrap_prefix.clone(),
13349                        from_empty_selection,
13350                    ));
13351                    current_range_start = row;
13352                    current_range_indent = row_indent;
13353                    current_range_comment_delimiters = row_comment_delimiters;
13354                    current_range_rewrap_prefix = row_rewrap_prefix;
13355                }
13356                prev_row = row;
13357            }
13358
13359            ranges.push((
13360                language_settings.clone(),
13361                Point::new(current_range_start, 0)
13362                    ..Point::new(prev_row, buffer.line_len(MultiBufferRow(prev_row))),
13363                current_range_indent,
13364                current_range_comment_delimiters,
13365                current_range_rewrap_prefix,
13366                from_empty_selection,
13367            ));
13368
13369            ranges
13370        });
13371
13372        let mut edits = Vec::new();
13373        let mut rewrapped_row_ranges = Vec::<RangeInclusive<u32>>::new();
13374
13375        for (
13376            language_settings,
13377            wrap_range,
13378            mut indent_size,
13379            comment_prefix,
13380            rewrap_prefix,
13381            from_empty_selection,
13382        ) in wrap_ranges
13383        {
13384            let mut start_row = wrap_range.start.row;
13385            let mut end_row = wrap_range.end.row;
13386
13387            // Skip selections that overlap with a range that has already been rewrapped.
13388            let selection_range = start_row..end_row;
13389            if rewrapped_row_ranges
13390                .iter()
13391                .any(|range| range.overlaps(&selection_range))
13392            {
13393                continue;
13394            }
13395
13396            let tab_size = language_settings.tab_size;
13397
13398            let (line_prefix, inside_comment) = match &comment_prefix {
13399                Some(CommentFormat::Line(prefix) | CommentFormat::BlockLine(prefix)) => {
13400                    (Some(prefix.as_str()), true)
13401                }
13402                Some(CommentFormat::BlockCommentWithEnd(BlockCommentConfig { prefix, .. })) => {
13403                    (Some(prefix.as_ref()), true)
13404                }
13405                Some(CommentFormat::BlockCommentWithStart(BlockCommentConfig {
13406                    start: _,
13407                    end: _,
13408                    prefix,
13409                    tab_size,
13410                })) => {
13411                    indent_size.len += tab_size;
13412                    (Some(prefix.as_ref()), true)
13413                }
13414                None => (None, false),
13415            };
13416            let indent_prefix = indent_size.chars().collect::<String>();
13417            let line_prefix = format!("{indent_prefix}{}", line_prefix.unwrap_or(""));
13418
13419            let allow_rewrap_based_on_language = match language_settings.allow_rewrap {
13420                RewrapBehavior::InComments => inside_comment,
13421                RewrapBehavior::InSelections => !wrap_range.is_empty(),
13422                RewrapBehavior::Anywhere => true,
13423            };
13424
13425            let should_rewrap = options.override_language_settings
13426                || allow_rewrap_based_on_language
13427                || self.hard_wrap.is_some();
13428            if !should_rewrap {
13429                continue;
13430            }
13431
13432            if from_empty_selection {
13433                'expand_upwards: while start_row > 0 {
13434                    let prev_row = start_row - 1;
13435                    if buffer.contains_str_at(Point::new(prev_row, 0), &line_prefix)
13436                        && buffer.line_len(MultiBufferRow(prev_row)) as usize > line_prefix.len()
13437                        && !buffer.is_line_blank(MultiBufferRow(prev_row))
13438                    {
13439                        start_row = prev_row;
13440                    } else {
13441                        break 'expand_upwards;
13442                    }
13443                }
13444
13445                'expand_downwards: while end_row < buffer.max_point().row {
13446                    let next_row = end_row + 1;
13447                    if buffer.contains_str_at(Point::new(next_row, 0), &line_prefix)
13448                        && buffer.line_len(MultiBufferRow(next_row)) as usize > line_prefix.len()
13449                        && !buffer.is_line_blank(MultiBufferRow(next_row))
13450                    {
13451                        end_row = next_row;
13452                    } else {
13453                        break 'expand_downwards;
13454                    }
13455                }
13456            }
13457
13458            let start = Point::new(start_row, 0);
13459            let start_offset = ToOffset::to_offset(&start, &buffer);
13460            let end = Point::new(end_row, buffer.line_len(MultiBufferRow(end_row)));
13461            let selection_text = buffer.text_for_range(start..end).collect::<String>();
13462            let mut first_line_delimiter = None;
13463            let mut last_line_delimiter = None;
13464            let Some(lines_without_prefixes) = selection_text
13465                .lines()
13466                .enumerate()
13467                .map(|(ix, line)| {
13468                    let line_trimmed = line.trim_start();
13469                    if rewrap_prefix.is_some() && ix > 0 {
13470                        Ok(line_trimmed)
13471                    } else if let Some(
13472                        CommentFormat::BlockCommentWithStart(BlockCommentConfig {
13473                            start,
13474                            prefix,
13475                            end,
13476                            tab_size,
13477                        })
13478                        | CommentFormat::BlockCommentWithEnd(BlockCommentConfig {
13479                            start,
13480                            prefix,
13481                            end,
13482                            tab_size,
13483                        }),
13484                    ) = &comment_prefix
13485                    {
13486                        let line_trimmed = line_trimmed
13487                            .strip_prefix(start.as_ref())
13488                            .map(|s| {
13489                                let mut indent_size = indent_size;
13490                                indent_size.len -= tab_size;
13491                                let indent_prefix: String = indent_size.chars().collect();
13492                                first_line_delimiter = Some((indent_prefix, start));
13493                                s.trim_start()
13494                            })
13495                            .unwrap_or(line_trimmed);
13496                        let line_trimmed = line_trimmed
13497                            .strip_suffix(end.as_ref())
13498                            .map(|s| {
13499                                last_line_delimiter = Some(end);
13500                                s.trim_end()
13501                            })
13502                            .unwrap_or(line_trimmed);
13503                        let line_trimmed = line_trimmed
13504                            .strip_prefix(prefix.as_ref())
13505                            .unwrap_or(line_trimmed);
13506                        Ok(line_trimmed)
13507                    } else if let Some(CommentFormat::BlockLine(prefix)) = &comment_prefix {
13508                        line_trimmed.strip_prefix(prefix).with_context(|| {
13509                            format!("line did not start with prefix {prefix:?}: {line:?}")
13510                        })
13511                    } else {
13512                        line_trimmed
13513                            .strip_prefix(&line_prefix.trim_start())
13514                            .with_context(|| {
13515                                format!("line did not start with prefix {line_prefix:?}: {line:?}")
13516                            })
13517                    }
13518                })
13519                .collect::<Result<Vec<_>, _>>()
13520                .log_err()
13521            else {
13522                continue;
13523            };
13524
13525            let wrap_column = self.hard_wrap.unwrap_or_else(|| {
13526                buffer
13527                    .language_settings_at(Point::new(start_row, 0), cx)
13528                    .preferred_line_length as usize
13529            });
13530
13531            let subsequent_lines_prefix = if let Some(rewrap_prefix_str) = &rewrap_prefix {
13532                format!("{}{}", indent_prefix, " ".repeat(rewrap_prefix_str.len()))
13533            } else {
13534                line_prefix.clone()
13535            };
13536
13537            let wrapped_text = {
13538                let mut wrapped_text = wrap_with_prefix(
13539                    line_prefix,
13540                    subsequent_lines_prefix,
13541                    lines_without_prefixes.join("\n"),
13542                    wrap_column,
13543                    tab_size,
13544                    options.preserve_existing_whitespace,
13545                );
13546
13547                if let Some((indent, delimiter)) = first_line_delimiter {
13548                    wrapped_text = format!("{indent}{delimiter}\n{wrapped_text}");
13549                }
13550                if let Some(last_line) = last_line_delimiter {
13551                    wrapped_text = format!("{wrapped_text}\n{indent_prefix}{last_line}");
13552                }
13553
13554                wrapped_text
13555            };
13556
13557            // TODO: should always use char-based diff while still supporting cursor behavior that
13558            // matches vim.
13559            let mut diff_options = DiffOptions::default();
13560            if options.override_language_settings {
13561                diff_options.max_word_diff_len = 0;
13562                diff_options.max_word_diff_line_count = 0;
13563            } else {
13564                diff_options.max_word_diff_len = usize::MAX;
13565                diff_options.max_word_diff_line_count = usize::MAX;
13566            }
13567
13568            for (old_range, new_text) in
13569                text_diff_with_options(&selection_text, &wrapped_text, diff_options)
13570            {
13571                let edit_start = buffer.anchor_after(start_offset + old_range.start);
13572                let edit_end = buffer.anchor_after(start_offset + old_range.end);
13573                edits.push((edit_start..edit_end, new_text));
13574            }
13575
13576            rewrapped_row_ranges.push(start_row..=end_row);
13577        }
13578
13579        self.buffer
13580            .update(cx, |buffer, cx| buffer.edit(edits, None, cx));
13581    }
13582
13583    pub fn cut_common(
13584        &mut self,
13585        cut_no_selection_line: bool,
13586        window: &mut Window,
13587        cx: &mut Context<Self>,
13588    ) -> ClipboardItem {
13589        let mut text = String::new();
13590        let buffer = self.buffer.read(cx).snapshot(cx);
13591        let mut selections = self.selections.all::<Point>(&self.display_snapshot(cx));
13592        let mut clipboard_selections = Vec::with_capacity(selections.len());
13593        {
13594            let max_point = buffer.max_point();
13595            let mut is_first = true;
13596            let mut prev_selection_was_entire_line = false;
13597            for selection in &mut selections {
13598                let is_entire_line =
13599                    (selection.is_empty() && cut_no_selection_line) || self.selections.line_mode();
13600                if is_entire_line {
13601                    selection.start = Point::new(selection.start.row, 0);
13602                    if !selection.is_empty() && selection.end.column == 0 {
13603                        selection.end = cmp::min(max_point, selection.end);
13604                    } else {
13605                        selection.end = cmp::min(max_point, Point::new(selection.end.row + 1, 0));
13606                    }
13607                    selection.goal = SelectionGoal::None;
13608                }
13609                if is_first {
13610                    is_first = false;
13611                } else if !prev_selection_was_entire_line {
13612                    text += "\n";
13613                }
13614                prev_selection_was_entire_line = is_entire_line;
13615                let mut len = 0;
13616                for chunk in buffer.text_for_range(selection.start..selection.end) {
13617                    text.push_str(chunk);
13618                    len += chunk.len();
13619                }
13620
13621                clipboard_selections.push(ClipboardSelection::for_buffer(
13622                    len,
13623                    is_entire_line,
13624                    selection.range(),
13625                    &buffer,
13626                    self.project.as_ref(),
13627                    cx,
13628                ));
13629            }
13630        }
13631
13632        self.transact(window, cx, |this, window, cx| {
13633            this.change_selections(Default::default(), window, cx, |s| {
13634                s.select(selections);
13635            });
13636            this.insert("", window, cx);
13637        });
13638        ClipboardItem::new_string_with_json_metadata(text, clipboard_selections)
13639    }
13640
13641    pub fn cut(&mut self, _: &Cut, window: &mut Window, cx: &mut Context<Self>) {
13642        self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
13643        let item = self.cut_common(true, window, cx);
13644        cx.write_to_clipboard(item);
13645    }
13646
13647    pub fn kill_ring_cut(&mut self, _: &KillRingCut, window: &mut Window, cx: &mut Context<Self>) {
13648        self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
13649        self.change_selections(SelectionEffects::no_scroll(), window, cx, |s| {
13650            s.move_with(&mut |snapshot, sel| {
13651                if sel.is_empty() {
13652                    sel.end = DisplayPoint::new(sel.end.row(), snapshot.line_len(sel.end.row()));
13653                }
13654                if sel.is_empty() {
13655                    sel.end = DisplayPoint::new(sel.end.row() + 1_u32, 0);
13656                }
13657            });
13658        });
13659        let item = self.cut_common(false, window, cx);
13660        cx.set_global(KillRing(item))
13661    }
13662
13663    pub fn kill_ring_yank(
13664        &mut self,
13665        _: &KillRingYank,
13666        window: &mut Window,
13667        cx: &mut Context<Self>,
13668    ) {
13669        self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
13670        let (text, metadata) = if let Some(KillRing(item)) = cx.try_global() {
13671            if let Some(ClipboardEntry::String(kill_ring)) = item.entries().first() {
13672                (kill_ring.text().to_string(), kill_ring.metadata_json())
13673            } else {
13674                return;
13675            }
13676        } else {
13677            return;
13678        };
13679        self.do_paste(&text, metadata, false, window, cx);
13680    }
13681
13682    pub fn copy_and_trim(&mut self, _: &CopyAndTrim, _: &mut Window, cx: &mut Context<Self>) {
13683        self.do_copy(true, cx);
13684    }
13685
13686    pub fn copy(&mut self, _: &Copy, _: &mut Window, cx: &mut Context<Self>) {
13687        self.do_copy(false, cx);
13688    }
13689
13690    fn do_copy(&self, strip_leading_indents: bool, cx: &mut Context<Self>) {
13691        let selections = self.selections.all::<Point>(&self.display_snapshot(cx));
13692        let buffer = self.buffer.read(cx).read(cx);
13693        let mut text = String::new();
13694        let mut clipboard_selections = Vec::with_capacity(selections.len());
13695
13696        let max_point = buffer.max_point();
13697        let mut is_first = true;
13698        for selection in &selections {
13699            let mut start = selection.start;
13700            let mut end = selection.end;
13701            let is_entire_line = selection.is_empty() || self.selections.line_mode();
13702            let mut add_trailing_newline = false;
13703            if is_entire_line {
13704                start = Point::new(start.row, 0);
13705                let next_line_start = Point::new(end.row + 1, 0);
13706                if next_line_start <= max_point {
13707                    end = next_line_start;
13708                } else {
13709                    // We're on the last line without a trailing newline.
13710                    // Copy to the end of the line and add a newline afterwards.
13711                    end = Point::new(end.row, buffer.line_len(MultiBufferRow(end.row)));
13712                    add_trailing_newline = true;
13713                }
13714            }
13715
13716            let mut trimmed_selections = Vec::new();
13717            if strip_leading_indents && end.row.saturating_sub(start.row) > 0 {
13718                let row = MultiBufferRow(start.row);
13719                let first_indent = buffer.indent_size_for_line(row);
13720                if first_indent.len == 0 || start.column > first_indent.len {
13721                    trimmed_selections.push(start..end);
13722                } else {
13723                    trimmed_selections.push(
13724                        Point::new(row.0, first_indent.len)
13725                            ..Point::new(row.0, buffer.line_len(row)),
13726                    );
13727                    for row in start.row + 1..=end.row {
13728                        let mut line_len = buffer.line_len(MultiBufferRow(row));
13729                        if row == end.row {
13730                            line_len = end.column;
13731                        }
13732                        if line_len == 0 {
13733                            trimmed_selections.push(Point::new(row, 0)..Point::new(row, line_len));
13734                            continue;
13735                        }
13736                        let row_indent_size = buffer.indent_size_for_line(MultiBufferRow(row));
13737                        if row_indent_size.len >= first_indent.len {
13738                            trimmed_selections
13739                                .push(Point::new(row, first_indent.len)..Point::new(row, line_len));
13740                        } else {
13741                            trimmed_selections.clear();
13742                            trimmed_selections.push(start..end);
13743                            break;
13744                        }
13745                    }
13746                }
13747            } else {
13748                trimmed_selections.push(start..end);
13749            }
13750
13751            let is_multiline_trim = trimmed_selections.len() > 1;
13752            let mut selection_len: usize = 0;
13753            let prev_selection_was_entire_line = is_entire_line && !is_multiline_trim;
13754
13755            for trimmed_range in trimmed_selections {
13756                if is_first {
13757                    is_first = false;
13758                } else if is_multiline_trim || !prev_selection_was_entire_line {
13759                    text.push('\n');
13760                    if is_multiline_trim {
13761                        selection_len += 1;
13762                    }
13763                }
13764                for chunk in buffer.text_for_range(trimmed_range.start..trimmed_range.end) {
13765                    text.push_str(chunk);
13766                    selection_len += chunk.len();
13767                }
13768                if add_trailing_newline {
13769                    text.push('\n');
13770                    selection_len += 1;
13771                }
13772            }
13773
13774            clipboard_selections.push(ClipboardSelection::for_buffer(
13775                selection_len,
13776                is_entire_line,
13777                start..end,
13778                &buffer,
13779                self.project.as_ref(),
13780                cx,
13781            ));
13782        }
13783
13784        cx.write_to_clipboard(ClipboardItem::new_string_with_json_metadata(
13785            text,
13786            clipboard_selections,
13787        ));
13788    }
13789
13790    pub fn do_paste(
13791        &mut self,
13792        text: &String,
13793        clipboard_selections: Option<Vec<ClipboardSelection>>,
13794        handle_entire_lines: bool,
13795        window: &mut Window,
13796        cx: &mut Context<Self>,
13797    ) {
13798        if self.read_only(cx) {
13799            return;
13800        }
13801
13802        let clipboard_text = Cow::Borrowed(text.as_str());
13803
13804        self.transact(window, cx, |this, window, cx| {
13805            let had_active_edit_prediction = this.has_active_edit_prediction();
13806            let display_map = this.display_snapshot(cx);
13807            let old_selections = this.selections.all::<MultiBufferOffset>(&display_map);
13808            let cursor_offset = this
13809                .selections
13810                .last::<MultiBufferOffset>(&display_map)
13811                .head();
13812
13813            if let Some(mut clipboard_selections) = clipboard_selections {
13814                let all_selections_were_entire_line =
13815                    clipboard_selections.iter().all(|s| s.is_entire_line);
13816                let first_selection_indent_column =
13817                    clipboard_selections.first().map(|s| s.first_line_indent);
13818                if clipboard_selections.len() != old_selections.len() {
13819                    clipboard_selections.drain(..);
13820                }
13821                let mut auto_indent_on_paste = true;
13822
13823                this.buffer.update(cx, |buffer, cx| {
13824                    let snapshot = buffer.read(cx);
13825                    auto_indent_on_paste = snapshot
13826                        .language_settings_at(cursor_offset, cx)
13827                        .auto_indent_on_paste;
13828
13829                    let mut start_offset = 0;
13830                    let mut edits = Vec::new();
13831                    let mut original_indent_columns = Vec::new();
13832                    for (ix, selection) in old_selections.iter().enumerate() {
13833                        let to_insert;
13834                        let entire_line;
13835                        let original_indent_column;
13836                        if let Some(clipboard_selection) = clipboard_selections.get(ix) {
13837                            let end_offset = start_offset + clipboard_selection.len;
13838                            to_insert = &clipboard_text[start_offset..end_offset];
13839                            entire_line = clipboard_selection.is_entire_line;
13840                            start_offset = if entire_line {
13841                                end_offset
13842                            } else {
13843                                end_offset + 1
13844                            };
13845                            original_indent_column = Some(clipboard_selection.first_line_indent);
13846                        } else {
13847                            to_insert = &*clipboard_text;
13848                            entire_line = all_selections_were_entire_line;
13849                            original_indent_column = first_selection_indent_column
13850                        }
13851
13852                        let (range, to_insert) =
13853                            if selection.is_empty() && handle_entire_lines && entire_line {
13854                                // If the corresponding selection was empty when this slice of the
13855                                // clipboard text was written, then the entire line containing the
13856                                // selection was copied. If this selection is also currently empty,
13857                                // then paste the line before the current line of the buffer.
13858                                let column = selection.start.to_point(&snapshot).column as usize;
13859                                let line_start = selection.start - column;
13860                                (line_start..line_start, Cow::Borrowed(to_insert))
13861                            } else {
13862                                let language = snapshot.language_at(selection.head());
13863                                let range = selection.range();
13864                                if let Some(language) = language
13865                                    && language.name() == "Markdown"
13866                                {
13867                                    edit_for_markdown_paste(
13868                                        &snapshot,
13869                                        range,
13870                                        to_insert,
13871                                        url::Url::parse(to_insert).ok(),
13872                                    )
13873                                } else {
13874                                    (range, Cow::Borrowed(to_insert))
13875                                }
13876                            };
13877
13878                        edits.push((range, to_insert));
13879                        original_indent_columns.push(original_indent_column);
13880                    }
13881                    drop(snapshot);
13882
13883                    buffer.edit(
13884                        edits,
13885                        if auto_indent_on_paste {
13886                            Some(AutoindentMode::Block {
13887                                original_indent_columns,
13888                            })
13889                        } else {
13890                            None
13891                        },
13892                        cx,
13893                    );
13894                });
13895
13896                let selections = this
13897                    .selections
13898                    .all::<MultiBufferOffset>(&this.display_snapshot(cx));
13899                this.change_selections(Default::default(), window, cx, |s| s.select(selections));
13900            } else {
13901                let url = url::Url::parse(&clipboard_text).ok();
13902
13903                let auto_indent_mode = if !clipboard_text.is_empty() {
13904                    Some(AutoindentMode::Block {
13905                        original_indent_columns: Vec::new(),
13906                    })
13907                } else {
13908                    None
13909                };
13910
13911                let selection_anchors = this.buffer.update(cx, |buffer, cx| {
13912                    let snapshot = buffer.snapshot(cx);
13913
13914                    let anchors = old_selections
13915                        .iter()
13916                        .map(|s| {
13917                            let anchor = snapshot.anchor_after(s.head());
13918                            s.map(|_| anchor)
13919                        })
13920                        .collect::<Vec<_>>();
13921
13922                    let mut edits = Vec::new();
13923
13924                    // When pasting text without metadata (e.g. copied from an
13925                    // external editor using multiple cursors) and the number of
13926                    // lines matches the number of selections, distribute one
13927                    // line per cursor instead of pasting the whole text at each.
13928                    let lines: Vec<&str> = clipboard_text.split('\n').collect();
13929                    let distribute_lines =
13930                        old_selections.len() > 1 && lines.len() == old_selections.len();
13931
13932                    for (ix, selection) in old_selections.iter().enumerate() {
13933                        let language = snapshot.language_at(selection.head());
13934                        let range = selection.range();
13935
13936                        let text_for_cursor: &str = if distribute_lines {
13937                            lines[ix]
13938                        } else {
13939                            &clipboard_text
13940                        };
13941
13942                        let (edit_range, edit_text) = if let Some(language) = language
13943                            && language.name() == "Markdown"
13944                        {
13945                            edit_for_markdown_paste(&snapshot, range, text_for_cursor, url.clone())
13946                        } else {
13947                            (range, Cow::Borrowed(text_for_cursor))
13948                        };
13949
13950                        edits.push((edit_range, edit_text));
13951                    }
13952
13953                    drop(snapshot);
13954                    buffer.edit(edits, auto_indent_mode, cx);
13955
13956                    anchors
13957                });
13958
13959                this.change_selections(Default::default(), window, cx, |s| {
13960                    s.select_anchors(selection_anchors);
13961                });
13962            }
13963
13964            //   🤔                 |    ..     | show_in_menu |
13965            // | ..                  |   true        true
13966            // | had_edit_prediction |   false       true
13967
13968            let trigger_in_words =
13969                this.show_edit_predictions_in_menu() || !had_active_edit_prediction;
13970
13971            this.trigger_completion_on_input(text, trigger_in_words, window, cx);
13972        });
13973    }
13974
13975    pub fn diff_clipboard_with_selection(
13976        &mut self,
13977        _: &DiffClipboardWithSelection,
13978        window: &mut Window,
13979        cx: &mut Context<Self>,
13980    ) {
13981        let selections = self
13982            .selections
13983            .all::<MultiBufferOffset>(&self.display_snapshot(cx));
13984
13985        if selections.is_empty() {
13986            log::warn!("There should always be at least one selection in Zed. This is a bug.");
13987            return;
13988        };
13989
13990        let clipboard_text = match cx.read_from_clipboard() {
13991            Some(item) => match item.entries().first() {
13992                Some(ClipboardEntry::String(text)) => Some(text.text().to_string()),
13993                _ => None,
13994            },
13995            None => None,
13996        };
13997
13998        let Some(clipboard_text) = clipboard_text else {
13999            log::warn!("Clipboard doesn't contain text.");
14000            return;
14001        };
14002
14003        window.dispatch_action(
14004            Box::new(DiffClipboardWithSelectionData {
14005                clipboard_text,
14006                editor: cx.entity(),
14007            }),
14008            cx,
14009        );
14010    }
14011
14012    pub fn paste(&mut self, _: &Paste, window: &mut Window, cx: &mut Context<Self>) {
14013        self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
14014        if let Some(item) = cx.read_from_clipboard() {
14015            let entries = item.entries();
14016
14017            match entries.first() {
14018                // For now, we only support applying metadata if there's one string. In the future, we can incorporate all the selections
14019                // of all the pasted entries.
14020                Some(ClipboardEntry::String(clipboard_string)) if entries.len() == 1 => self
14021                    .do_paste(
14022                        clipboard_string.text(),
14023                        clipboard_string.metadata_json::<Vec<ClipboardSelection>>(),
14024                        true,
14025                        window,
14026                        cx,
14027                    ),
14028                _ => self.do_paste(&item.text().unwrap_or_default(), None, true, window, cx),
14029            }
14030        }
14031    }
14032
14033    pub fn undo(&mut self, _: &Undo, window: &mut Window, cx: &mut Context<Self>) {
14034        if self.read_only(cx) {
14035            return;
14036        }
14037
14038        self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
14039
14040        if let Some(transaction_id) = self.buffer.update(cx, |buffer, cx| buffer.undo(cx)) {
14041            if let Some((selections, _)) =
14042                self.selection_history.transaction(transaction_id).cloned()
14043            {
14044                self.change_selections(SelectionEffects::no_scroll(), window, cx, |s| {
14045                    s.select_anchors(selections.to_vec());
14046                });
14047            } else {
14048                log::error!(
14049                    "No entry in selection_history found for undo. \
14050                     This may correspond to a bug where undo does not update the selection. \
14051                     If this is occurring, please add details to \
14052                     https://github.com/zed-industries/zed/issues/22692"
14053                );
14054            }
14055            self.request_autoscroll(Autoscroll::fit(), cx);
14056            self.unmark_text(window, cx);
14057            self.refresh_edit_prediction(true, false, window, cx);
14058            cx.emit(EditorEvent::Edited { transaction_id });
14059            cx.emit(EditorEvent::TransactionUndone { transaction_id });
14060        }
14061    }
14062
14063    pub fn redo(&mut self, _: &Redo, window: &mut Window, cx: &mut Context<Self>) {
14064        if self.read_only(cx) {
14065            return;
14066        }
14067
14068        self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
14069
14070        if let Some(transaction_id) = self.buffer.update(cx, |buffer, cx| buffer.redo(cx)) {
14071            if let Some((_, Some(selections))) =
14072                self.selection_history.transaction(transaction_id).cloned()
14073            {
14074                self.change_selections(SelectionEffects::no_scroll(), window, cx, |s| {
14075                    s.select_anchors(selections.to_vec());
14076                });
14077            } else {
14078                log::error!(
14079                    "No entry in selection_history found for redo. \
14080                     This may correspond to a bug where undo does not update the selection. \
14081                     If this is occurring, please add details to \
14082                     https://github.com/zed-industries/zed/issues/22692"
14083                );
14084            }
14085            self.request_autoscroll(Autoscroll::fit(), cx);
14086            self.unmark_text(window, cx);
14087            self.refresh_edit_prediction(true, false, window, cx);
14088            cx.emit(EditorEvent::Edited { transaction_id });
14089        }
14090    }
14091
14092    pub fn finalize_last_transaction(&mut self, cx: &mut Context<Self>) {
14093        self.buffer
14094            .update(cx, |buffer, cx| buffer.finalize_last_transaction(cx));
14095    }
14096
14097    pub fn group_until_transaction(&mut self, tx_id: TransactionId, cx: &mut Context<Self>) {
14098        self.buffer
14099            .update(cx, |buffer, cx| buffer.group_until_transaction(tx_id, cx));
14100    }
14101
14102    pub fn move_left(&mut self, _: &MoveLeft, window: &mut Window, cx: &mut Context<Self>) {
14103        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
14104        self.change_selections(Default::default(), window, cx, |s| {
14105            s.move_with(&mut |map, selection| {
14106                let cursor = if selection.is_empty() {
14107                    movement::left(map, selection.start)
14108                } else {
14109                    selection.start
14110                };
14111                selection.collapse_to(cursor, SelectionGoal::None);
14112            });
14113        })
14114    }
14115
14116    pub fn select_left(&mut self, _: &SelectLeft, window: &mut Window, cx: &mut Context<Self>) {
14117        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
14118        self.change_selections(Default::default(), window, cx, |s| {
14119            s.move_heads_with(&mut |map, head, _| (movement::left(map, head), SelectionGoal::None));
14120        })
14121    }
14122
14123    pub fn move_right(&mut self, _: &MoveRight, window: &mut Window, cx: &mut Context<Self>) {
14124        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
14125        self.change_selections(Default::default(), window, cx, |s| {
14126            s.move_with(&mut |map, selection| {
14127                let cursor = if selection.is_empty() {
14128                    movement::right(map, selection.end)
14129                } else {
14130                    selection.end
14131                };
14132                selection.collapse_to(cursor, SelectionGoal::None)
14133            });
14134        })
14135    }
14136
14137    pub fn select_right(&mut self, _: &SelectRight, window: &mut Window, cx: &mut Context<Self>) {
14138        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
14139        self.change_selections(Default::default(), window, cx, |s| {
14140            s.move_heads_with(&mut |map, head, _| {
14141                (movement::right(map, head), SelectionGoal::None)
14142            });
14143        });
14144    }
14145
14146    pub fn move_up(&mut self, _: &MoveUp, window: &mut Window, cx: &mut Context<Self>) {
14147        if self.take_rename(true, window, cx).is_some() {
14148            return;
14149        }
14150
14151        if self.mode.is_single_line() {
14152            cx.propagate();
14153            return;
14154        }
14155
14156        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
14157
14158        let text_layout_details = &self.text_layout_details(window, cx);
14159        let selection_count = self.selections.count();
14160        let first_selection = self.selections.first_anchor();
14161
14162        self.change_selections(Default::default(), window, cx, |s| {
14163            s.move_with(&mut |map, selection| {
14164                if !selection.is_empty() {
14165                    selection.goal = SelectionGoal::None;
14166                }
14167                let (cursor, goal) = movement::up(
14168                    map,
14169                    selection.start,
14170                    selection.goal,
14171                    false,
14172                    text_layout_details,
14173                );
14174                selection.collapse_to(cursor, goal);
14175            });
14176        });
14177
14178        if selection_count == 1 && first_selection.range() == self.selections.first_anchor().range()
14179        {
14180            cx.propagate();
14181        }
14182    }
14183
14184    pub fn move_up_by_lines(
14185        &mut self,
14186        action: &MoveUpByLines,
14187        window: &mut Window,
14188        cx: &mut Context<Self>,
14189    ) {
14190        if self.take_rename(true, window, cx).is_some() {
14191            return;
14192        }
14193
14194        if self.mode.is_single_line() {
14195            cx.propagate();
14196            return;
14197        }
14198
14199        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
14200
14201        let text_layout_details = &self.text_layout_details(window, cx);
14202
14203        self.change_selections(Default::default(), window, cx, |s| {
14204            s.move_with(&mut |map, selection| {
14205                if !selection.is_empty() {
14206                    selection.goal = SelectionGoal::None;
14207                }
14208                let (cursor, goal) = movement::up_by_rows(
14209                    map,
14210                    selection.start,
14211                    action.lines,
14212                    selection.goal,
14213                    false,
14214                    text_layout_details,
14215                );
14216                selection.collapse_to(cursor, goal);
14217            });
14218        })
14219    }
14220
14221    pub fn move_down_by_lines(
14222        &mut self,
14223        action: &MoveDownByLines,
14224        window: &mut Window,
14225        cx: &mut Context<Self>,
14226    ) {
14227        if self.take_rename(true, window, cx).is_some() {
14228            return;
14229        }
14230
14231        if self.mode.is_single_line() {
14232            cx.propagate();
14233            return;
14234        }
14235
14236        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
14237
14238        let text_layout_details = &self.text_layout_details(window, cx);
14239
14240        self.change_selections(Default::default(), window, cx, |s| {
14241            s.move_with(&mut |map, selection| {
14242                if !selection.is_empty() {
14243                    selection.goal = SelectionGoal::None;
14244                }
14245                let (cursor, goal) = movement::down_by_rows(
14246                    map,
14247                    selection.start,
14248                    action.lines,
14249                    selection.goal,
14250                    false,
14251                    text_layout_details,
14252                );
14253                selection.collapse_to(cursor, goal);
14254            });
14255        })
14256    }
14257
14258    pub fn select_down_by_lines(
14259        &mut self,
14260        action: &SelectDownByLines,
14261        window: &mut Window,
14262        cx: &mut Context<Self>,
14263    ) {
14264        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
14265        let text_layout_details = &self.text_layout_details(window, cx);
14266        self.change_selections(Default::default(), window, cx, |s| {
14267            s.move_heads_with(&mut |map, head, goal| {
14268                movement::down_by_rows(map, head, action.lines, goal, false, text_layout_details)
14269            })
14270        })
14271    }
14272
14273    pub fn select_up_by_lines(
14274        &mut self,
14275        action: &SelectUpByLines,
14276        window: &mut Window,
14277        cx: &mut Context<Self>,
14278    ) {
14279        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
14280        let text_layout_details = &self.text_layout_details(window, cx);
14281        self.change_selections(Default::default(), window, cx, |s| {
14282            s.move_heads_with(&mut |map, head, goal| {
14283                movement::up_by_rows(map, head, action.lines, goal, false, text_layout_details)
14284            })
14285        })
14286    }
14287
14288    pub fn select_page_up(
14289        &mut self,
14290        _: &SelectPageUp,
14291        window: &mut Window,
14292        cx: &mut Context<Self>,
14293    ) {
14294        let Some(row_count) = self.visible_row_count() else {
14295            return;
14296        };
14297
14298        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
14299
14300        let text_layout_details = &self.text_layout_details(window, cx);
14301
14302        self.change_selections(Default::default(), window, cx, |s| {
14303            s.move_heads_with(&mut |map, head, goal| {
14304                movement::up_by_rows(map, head, row_count, goal, false, text_layout_details)
14305            })
14306        })
14307    }
14308
14309    pub fn move_page_up(
14310        &mut self,
14311        action: &MovePageUp,
14312        window: &mut Window,
14313        cx: &mut Context<Self>,
14314    ) {
14315        if self.take_rename(true, window, cx).is_some() {
14316            return;
14317        }
14318
14319        if self
14320            .context_menu
14321            .borrow_mut()
14322            .as_mut()
14323            .map(|menu| menu.select_first(self.completion_provider.as_deref(), window, cx))
14324            .unwrap_or(false)
14325        {
14326            return;
14327        }
14328
14329        if matches!(self.mode, EditorMode::SingleLine) {
14330            cx.propagate();
14331            return;
14332        }
14333
14334        let Some(row_count) = self.visible_row_count() else {
14335            return;
14336        };
14337
14338        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
14339
14340        let effects = if action.center_cursor {
14341            SelectionEffects::scroll(Autoscroll::center())
14342        } else {
14343            SelectionEffects::default()
14344        };
14345
14346        let text_layout_details = &self.text_layout_details(window, cx);
14347
14348        self.change_selections(effects, window, cx, |s| {
14349            s.move_with(&mut |map, selection| {
14350                if !selection.is_empty() {
14351                    selection.goal = SelectionGoal::None;
14352                }
14353                let (cursor, goal) = movement::up_by_rows(
14354                    map,
14355                    selection.end,
14356                    row_count,
14357                    selection.goal,
14358                    false,
14359                    text_layout_details,
14360                );
14361                selection.collapse_to(cursor, goal);
14362            });
14363        });
14364    }
14365
14366    pub fn select_up(&mut self, _: &SelectUp, window: &mut Window, cx: &mut Context<Self>) {
14367        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
14368        let text_layout_details = &self.text_layout_details(window, cx);
14369        self.change_selections(Default::default(), window, cx, |s| {
14370            s.move_heads_with(&mut |map, head, goal| {
14371                movement::up(map, head, goal, false, text_layout_details)
14372            })
14373        })
14374    }
14375
14376    pub fn move_down(&mut self, _: &MoveDown, window: &mut Window, cx: &mut Context<Self>) {
14377        self.take_rename(true, window, cx);
14378
14379        if self.mode.is_single_line() {
14380            cx.propagate();
14381            return;
14382        }
14383
14384        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
14385
14386        let text_layout_details = &self.text_layout_details(window, cx);
14387        let selection_count = self.selections.count();
14388        let first_selection = self.selections.first_anchor();
14389
14390        self.change_selections(Default::default(), window, cx, |s| {
14391            s.move_with(&mut |map, selection| {
14392                if !selection.is_empty() {
14393                    selection.goal = SelectionGoal::None;
14394                }
14395                let (cursor, goal) = movement::down(
14396                    map,
14397                    selection.end,
14398                    selection.goal,
14399                    false,
14400                    text_layout_details,
14401                );
14402                selection.collapse_to(cursor, goal);
14403            });
14404        });
14405
14406        if selection_count == 1 && first_selection.range() == self.selections.first_anchor().range()
14407        {
14408            cx.propagate();
14409        }
14410    }
14411
14412    pub fn select_page_down(
14413        &mut self,
14414        _: &SelectPageDown,
14415        window: &mut Window,
14416        cx: &mut Context<Self>,
14417    ) {
14418        let Some(row_count) = self.visible_row_count() else {
14419            return;
14420        };
14421
14422        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
14423
14424        let text_layout_details = &self.text_layout_details(window, cx);
14425
14426        self.change_selections(Default::default(), window, cx, |s| {
14427            s.move_heads_with(&mut |map, head, goal| {
14428                movement::down_by_rows(map, head, row_count, goal, false, text_layout_details)
14429            })
14430        })
14431    }
14432
14433    pub fn move_page_down(
14434        &mut self,
14435        action: &MovePageDown,
14436        window: &mut Window,
14437        cx: &mut Context<Self>,
14438    ) {
14439        if self.take_rename(true, window, cx).is_some() {
14440            return;
14441        }
14442
14443        if self
14444            .context_menu
14445            .borrow_mut()
14446            .as_mut()
14447            .map(|menu| menu.select_last(self.completion_provider.as_deref(), window, cx))
14448            .unwrap_or(false)
14449        {
14450            return;
14451        }
14452
14453        if matches!(self.mode, EditorMode::SingleLine) {
14454            cx.propagate();
14455            return;
14456        }
14457
14458        let Some(row_count) = self.visible_row_count() else {
14459            return;
14460        };
14461
14462        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
14463
14464        let effects = if action.center_cursor {
14465            SelectionEffects::scroll(Autoscroll::center())
14466        } else {
14467            SelectionEffects::default()
14468        };
14469
14470        let text_layout_details = &self.text_layout_details(window, cx);
14471        self.change_selections(effects, window, cx, |s| {
14472            s.move_with(&mut |map, selection| {
14473                if !selection.is_empty() {
14474                    selection.goal = SelectionGoal::None;
14475                }
14476                let (cursor, goal) = movement::down_by_rows(
14477                    map,
14478                    selection.end,
14479                    row_count,
14480                    selection.goal,
14481                    false,
14482                    text_layout_details,
14483                );
14484                selection.collapse_to(cursor, goal);
14485            });
14486        });
14487    }
14488
14489    pub fn select_down(&mut self, _: &SelectDown, window: &mut Window, cx: &mut Context<Self>) {
14490        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
14491        let text_layout_details = &self.text_layout_details(window, cx);
14492        self.change_selections(Default::default(), window, cx, |s| {
14493            s.move_heads_with(&mut |map, head, goal| {
14494                movement::down(map, head, goal, false, text_layout_details)
14495            })
14496        });
14497    }
14498
14499    pub fn context_menu_first(
14500        &mut self,
14501        _: &ContextMenuFirst,
14502        window: &mut Window,
14503        cx: &mut Context<Self>,
14504    ) {
14505        if let Some(context_menu) = self.context_menu.borrow_mut().as_mut() {
14506            context_menu.select_first(self.completion_provider.as_deref(), window, cx);
14507        }
14508    }
14509
14510    pub fn context_menu_prev(
14511        &mut self,
14512        _: &ContextMenuPrevious,
14513        window: &mut Window,
14514        cx: &mut Context<Self>,
14515    ) {
14516        if let Some(context_menu) = self.context_menu.borrow_mut().as_mut() {
14517            context_menu.select_prev(self.completion_provider.as_deref(), window, cx);
14518        }
14519    }
14520
14521    pub fn context_menu_next(
14522        &mut self,
14523        _: &ContextMenuNext,
14524        window: &mut Window,
14525        cx: &mut Context<Self>,
14526    ) {
14527        if let Some(context_menu) = self.context_menu.borrow_mut().as_mut() {
14528            context_menu.select_next(self.completion_provider.as_deref(), window, cx);
14529        }
14530    }
14531
14532    pub fn context_menu_last(
14533        &mut self,
14534        _: &ContextMenuLast,
14535        window: &mut Window,
14536        cx: &mut Context<Self>,
14537    ) {
14538        if let Some(context_menu) = self.context_menu.borrow_mut().as_mut() {
14539            context_menu.select_last(self.completion_provider.as_deref(), window, cx);
14540        }
14541    }
14542
14543    pub fn signature_help_prev(
14544        &mut self,
14545        _: &SignatureHelpPrevious,
14546        _: &mut Window,
14547        cx: &mut Context<Self>,
14548    ) {
14549        if let Some(popover) = self.signature_help_state.popover_mut() {
14550            if popover.current_signature == 0 {
14551                popover.current_signature = popover.signatures.len() - 1;
14552            } else {
14553                popover.current_signature -= 1;
14554            }
14555            cx.notify();
14556        }
14557    }
14558
14559    pub fn signature_help_next(
14560        &mut self,
14561        _: &SignatureHelpNext,
14562        _: &mut Window,
14563        cx: &mut Context<Self>,
14564    ) {
14565        if let Some(popover) = self.signature_help_state.popover_mut() {
14566            if popover.current_signature + 1 == popover.signatures.len() {
14567                popover.current_signature = 0;
14568            } else {
14569                popover.current_signature += 1;
14570            }
14571            cx.notify();
14572        }
14573    }
14574
14575    pub fn move_to_previous_word_start(
14576        &mut self,
14577        _: &MoveToPreviousWordStart,
14578        window: &mut Window,
14579        cx: &mut Context<Self>,
14580    ) {
14581        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
14582        self.change_selections(Default::default(), window, cx, |s| {
14583            s.move_cursors_with(&mut |map, head, _| {
14584                (
14585                    movement::previous_word_start(map, head),
14586                    SelectionGoal::None,
14587                )
14588            });
14589        })
14590    }
14591
14592    pub fn move_to_previous_subword_start(
14593        &mut self,
14594        _: &MoveToPreviousSubwordStart,
14595        window: &mut Window,
14596        cx: &mut Context<Self>,
14597    ) {
14598        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
14599        self.change_selections(Default::default(), window, cx, |s| {
14600            s.move_cursors_with(&mut |map, head, _| {
14601                (
14602                    movement::previous_subword_start(map, head),
14603                    SelectionGoal::None,
14604                )
14605            });
14606        })
14607    }
14608
14609    pub fn select_to_previous_word_start(
14610        &mut self,
14611        _: &SelectToPreviousWordStart,
14612        window: &mut Window,
14613        cx: &mut Context<Self>,
14614    ) {
14615        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
14616        self.change_selections(Default::default(), window, cx, |s| {
14617            s.move_heads_with(&mut |map, head, _| {
14618                (
14619                    movement::previous_word_start(map, head),
14620                    SelectionGoal::None,
14621                )
14622            });
14623        })
14624    }
14625
14626    pub fn select_to_previous_subword_start(
14627        &mut self,
14628        _: &SelectToPreviousSubwordStart,
14629        window: &mut Window,
14630        cx: &mut Context<Self>,
14631    ) {
14632        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
14633        self.change_selections(Default::default(), window, cx, |s| {
14634            s.move_heads_with(&mut |map, head, _| {
14635                (
14636                    movement::previous_subword_start(map, head),
14637                    SelectionGoal::None,
14638                )
14639            });
14640        })
14641    }
14642
14643    pub fn delete_to_previous_word_start(
14644        &mut self,
14645        action: &DeleteToPreviousWordStart,
14646        window: &mut Window,
14647        cx: &mut Context<Self>,
14648    ) {
14649        self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
14650        self.transact(window, cx, |this, window, cx| {
14651            this.select_autoclose_pair(window, cx);
14652            this.change_selections(Default::default(), window, cx, |s| {
14653                s.move_with(&mut |map, selection| {
14654                    if selection.is_empty() {
14655                        let mut cursor = if action.ignore_newlines {
14656                            movement::previous_word_start(map, selection.head())
14657                        } else {
14658                            movement::previous_word_start_or_newline(map, selection.head())
14659                        };
14660                        cursor = movement::adjust_greedy_deletion(
14661                            map,
14662                            selection.head(),
14663                            cursor,
14664                            action.ignore_brackets,
14665                        );
14666                        selection.set_head(cursor, SelectionGoal::None);
14667                    }
14668                });
14669            });
14670            this.insert("", window, cx);
14671        });
14672    }
14673
14674    pub fn delete_to_previous_subword_start(
14675        &mut self,
14676        action: &DeleteToPreviousSubwordStart,
14677        window: &mut Window,
14678        cx: &mut Context<Self>,
14679    ) {
14680        self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
14681        self.transact(window, cx, |this, window, cx| {
14682            this.select_autoclose_pair(window, cx);
14683            this.change_selections(Default::default(), window, cx, |s| {
14684                s.move_with(&mut |map, selection| {
14685                    if selection.is_empty() {
14686                        let mut cursor = if action.ignore_newlines {
14687                            movement::previous_subword_start(map, selection.head())
14688                        } else {
14689                            movement::previous_subword_start_or_newline(map, selection.head())
14690                        };
14691                        cursor = movement::adjust_greedy_deletion(
14692                            map,
14693                            selection.head(),
14694                            cursor,
14695                            action.ignore_brackets,
14696                        );
14697                        selection.set_head(cursor, SelectionGoal::None);
14698                    }
14699                });
14700            });
14701            this.insert("", window, cx);
14702        });
14703    }
14704
14705    pub fn move_to_next_word_end(
14706        &mut self,
14707        _: &MoveToNextWordEnd,
14708        window: &mut Window,
14709        cx: &mut Context<Self>,
14710    ) {
14711        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
14712        self.change_selections(Default::default(), window, cx, |s| {
14713            s.move_cursors_with(&mut |map, head, _| {
14714                (movement::next_word_end(map, head), SelectionGoal::None)
14715            });
14716        })
14717    }
14718
14719    pub fn move_to_next_subword_end(
14720        &mut self,
14721        _: &MoveToNextSubwordEnd,
14722        window: &mut Window,
14723        cx: &mut Context<Self>,
14724    ) {
14725        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
14726        self.change_selections(Default::default(), window, cx, |s| {
14727            s.move_cursors_with(&mut |map, head, _| {
14728                (movement::next_subword_end(map, head), SelectionGoal::None)
14729            });
14730        })
14731    }
14732
14733    pub fn select_to_next_word_end(
14734        &mut self,
14735        _: &SelectToNextWordEnd,
14736        window: &mut Window,
14737        cx: &mut Context<Self>,
14738    ) {
14739        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
14740        self.change_selections(Default::default(), window, cx, |s| {
14741            s.move_heads_with(&mut |map, head, _| {
14742                (movement::next_word_end(map, head), SelectionGoal::None)
14743            });
14744        })
14745    }
14746
14747    pub fn select_to_next_subword_end(
14748        &mut self,
14749        _: &SelectToNextSubwordEnd,
14750        window: &mut Window,
14751        cx: &mut Context<Self>,
14752    ) {
14753        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
14754        self.change_selections(Default::default(), window, cx, |s| {
14755            s.move_heads_with(&mut |map, head, _| {
14756                (movement::next_subword_end(map, head), SelectionGoal::None)
14757            });
14758        })
14759    }
14760
14761    pub fn delete_to_next_word_end(
14762        &mut self,
14763        action: &DeleteToNextWordEnd,
14764        window: &mut Window,
14765        cx: &mut Context<Self>,
14766    ) {
14767        self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
14768        self.transact(window, cx, |this, window, cx| {
14769            this.change_selections(Default::default(), window, cx, |s| {
14770                s.move_with(&mut |map, selection| {
14771                    if selection.is_empty() {
14772                        let mut cursor = if action.ignore_newlines {
14773                            movement::next_word_end(map, selection.head())
14774                        } else {
14775                            movement::next_word_end_or_newline(map, selection.head())
14776                        };
14777                        cursor = movement::adjust_greedy_deletion(
14778                            map,
14779                            selection.head(),
14780                            cursor,
14781                            action.ignore_brackets,
14782                        );
14783                        selection.set_head(cursor, SelectionGoal::None);
14784                    }
14785                });
14786            });
14787            this.insert("", window, cx);
14788        });
14789    }
14790
14791    pub fn delete_to_next_subword_end(
14792        &mut self,
14793        action: &DeleteToNextSubwordEnd,
14794        window: &mut Window,
14795        cx: &mut Context<Self>,
14796    ) {
14797        self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
14798        self.transact(window, cx, |this, window, cx| {
14799            this.change_selections(Default::default(), window, cx, |s| {
14800                s.move_with(&mut |map, selection| {
14801                    if selection.is_empty() {
14802                        let mut cursor = if action.ignore_newlines {
14803                            movement::next_subword_end(map, selection.head())
14804                        } else {
14805                            movement::next_subword_end_or_newline(map, selection.head())
14806                        };
14807                        cursor = movement::adjust_greedy_deletion(
14808                            map,
14809                            selection.head(),
14810                            cursor,
14811                            action.ignore_brackets,
14812                        );
14813                        selection.set_head(cursor, SelectionGoal::None);
14814                    }
14815                });
14816            });
14817            this.insert("", window, cx);
14818        });
14819    }
14820
14821    pub fn move_to_beginning_of_line(
14822        &mut self,
14823        action: &MoveToBeginningOfLine,
14824        window: &mut Window,
14825        cx: &mut Context<Self>,
14826    ) {
14827        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
14828        self.change_selections(Default::default(), window, cx, |s| {
14829            s.move_cursors_with(&mut |map, head, _| {
14830                (
14831                    movement::indented_line_beginning(
14832                        map,
14833                        head,
14834                        action.stop_at_soft_wraps,
14835                        action.stop_at_indent,
14836                    ),
14837                    SelectionGoal::None,
14838                )
14839            });
14840        })
14841    }
14842
14843    pub fn select_to_beginning_of_line(
14844        &mut self,
14845        action: &SelectToBeginningOfLine,
14846        window: &mut Window,
14847        cx: &mut Context<Self>,
14848    ) {
14849        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
14850        self.change_selections(Default::default(), window, cx, |s| {
14851            s.move_heads_with(&mut |map, head, _| {
14852                (
14853                    movement::indented_line_beginning(
14854                        map,
14855                        head,
14856                        action.stop_at_soft_wraps,
14857                        action.stop_at_indent,
14858                    ),
14859                    SelectionGoal::None,
14860                )
14861            });
14862        });
14863    }
14864
14865    pub fn delete_to_beginning_of_line(
14866        &mut self,
14867        action: &DeleteToBeginningOfLine,
14868        window: &mut Window,
14869        cx: &mut Context<Self>,
14870    ) {
14871        self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
14872        self.transact(window, cx, |this, window, cx| {
14873            this.change_selections(Default::default(), window, cx, |s| {
14874                s.move_with(&mut |_, selection| {
14875                    selection.reversed = true;
14876                });
14877            });
14878
14879            this.select_to_beginning_of_line(
14880                &SelectToBeginningOfLine {
14881                    stop_at_soft_wraps: false,
14882                    stop_at_indent: action.stop_at_indent,
14883                },
14884                window,
14885                cx,
14886            );
14887            this.backspace(&Backspace, window, cx);
14888        });
14889    }
14890
14891    pub fn move_to_end_of_line(
14892        &mut self,
14893        action: &MoveToEndOfLine,
14894        window: &mut Window,
14895        cx: &mut Context<Self>,
14896    ) {
14897        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
14898        self.change_selections(Default::default(), window, cx, |s| {
14899            s.move_cursors_with(&mut |map, head, _| {
14900                (
14901                    movement::line_end(map, head, action.stop_at_soft_wraps),
14902                    SelectionGoal::None,
14903                )
14904            });
14905        })
14906    }
14907
14908    pub fn select_to_end_of_line(
14909        &mut self,
14910        action: &SelectToEndOfLine,
14911        window: &mut Window,
14912        cx: &mut Context<Self>,
14913    ) {
14914        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
14915        self.change_selections(Default::default(), window, cx, |s| {
14916            s.move_heads_with(&mut |map, head, _| {
14917                (
14918                    movement::line_end(map, head, action.stop_at_soft_wraps),
14919                    SelectionGoal::None,
14920                )
14921            });
14922        })
14923    }
14924
14925    pub fn delete_to_end_of_line(
14926        &mut self,
14927        _: &DeleteToEndOfLine,
14928        window: &mut Window,
14929        cx: &mut Context<Self>,
14930    ) {
14931        self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
14932        self.transact(window, cx, |this, window, cx| {
14933            this.select_to_end_of_line(
14934                &SelectToEndOfLine {
14935                    stop_at_soft_wraps: false,
14936                },
14937                window,
14938                cx,
14939            );
14940            this.delete(&Delete, window, cx);
14941        });
14942    }
14943
14944    pub fn cut_to_end_of_line(
14945        &mut self,
14946        action: &CutToEndOfLine,
14947        window: &mut Window,
14948        cx: &mut Context<Self>,
14949    ) {
14950        self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
14951        self.transact(window, cx, |this, window, cx| {
14952            this.select_to_end_of_line(
14953                &SelectToEndOfLine {
14954                    stop_at_soft_wraps: false,
14955                },
14956                window,
14957                cx,
14958            );
14959            if !action.stop_at_newlines {
14960                this.change_selections(Default::default(), window, cx, |s| {
14961                    s.move_with(&mut |_, sel| {
14962                        if sel.is_empty() {
14963                            sel.end = DisplayPoint::new(sel.end.row() + 1_u32, 0);
14964                        }
14965                    });
14966                });
14967            }
14968            this.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
14969            let item = this.cut_common(false, window, cx);
14970            cx.write_to_clipboard(item);
14971        });
14972    }
14973
14974    pub fn move_to_start_of_paragraph(
14975        &mut self,
14976        _: &MoveToStartOfParagraph,
14977        window: &mut Window,
14978        cx: &mut Context<Self>,
14979    ) {
14980        if matches!(self.mode, EditorMode::SingleLine) {
14981            cx.propagate();
14982            return;
14983        }
14984        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
14985        self.change_selections(Default::default(), window, cx, |s| {
14986            s.move_with(&mut |map, selection| {
14987                selection.collapse_to(
14988                    movement::start_of_paragraph(map, selection.head(), 1),
14989                    SelectionGoal::None,
14990                )
14991            });
14992        })
14993    }
14994
14995    pub fn move_to_end_of_paragraph(
14996        &mut self,
14997        _: &MoveToEndOfParagraph,
14998        window: &mut Window,
14999        cx: &mut Context<Self>,
15000    ) {
15001        if matches!(self.mode, EditorMode::SingleLine) {
15002            cx.propagate();
15003            return;
15004        }
15005        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
15006        self.change_selections(Default::default(), window, cx, |s| {
15007            s.move_with(&mut |map, selection| {
15008                selection.collapse_to(
15009                    movement::end_of_paragraph(map, selection.head(), 1),
15010                    SelectionGoal::None,
15011                )
15012            });
15013        })
15014    }
15015
15016    pub fn select_to_start_of_paragraph(
15017        &mut self,
15018        _: &SelectToStartOfParagraph,
15019        window: &mut Window,
15020        cx: &mut Context<Self>,
15021    ) {
15022        if matches!(self.mode, EditorMode::SingleLine) {
15023            cx.propagate();
15024            return;
15025        }
15026        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
15027        self.change_selections(Default::default(), window, cx, |s| {
15028            s.move_heads_with(&mut |map, head, _| {
15029                (
15030                    movement::start_of_paragraph(map, head, 1),
15031                    SelectionGoal::None,
15032                )
15033            });
15034        })
15035    }
15036
15037    pub fn select_to_end_of_paragraph(
15038        &mut self,
15039        _: &SelectToEndOfParagraph,
15040        window: &mut Window,
15041        cx: &mut Context<Self>,
15042    ) {
15043        if matches!(self.mode, EditorMode::SingleLine) {
15044            cx.propagate();
15045            return;
15046        }
15047        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
15048        self.change_selections(Default::default(), window, cx, |s| {
15049            s.move_heads_with(&mut |map, head, _| {
15050                (
15051                    movement::end_of_paragraph(map, head, 1),
15052                    SelectionGoal::None,
15053                )
15054            });
15055        })
15056    }
15057
15058    pub fn move_to_start_of_excerpt(
15059        &mut self,
15060        _: &MoveToStartOfExcerpt,
15061        window: &mut Window,
15062        cx: &mut Context<Self>,
15063    ) {
15064        if matches!(self.mode, EditorMode::SingleLine) {
15065            cx.propagate();
15066            return;
15067        }
15068        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
15069        self.change_selections(Default::default(), window, cx, |s| {
15070            s.move_with(&mut |map, selection| {
15071                selection.collapse_to(
15072                    movement::start_of_excerpt(
15073                        map,
15074                        selection.head(),
15075                        workspace::searchable::Direction::Prev,
15076                    ),
15077                    SelectionGoal::None,
15078                )
15079            });
15080        })
15081    }
15082
15083    pub fn move_to_start_of_next_excerpt(
15084        &mut self,
15085        _: &MoveToStartOfNextExcerpt,
15086        window: &mut Window,
15087        cx: &mut Context<Self>,
15088    ) {
15089        if matches!(self.mode, EditorMode::SingleLine) {
15090            cx.propagate();
15091            return;
15092        }
15093
15094        self.change_selections(Default::default(), window, cx, |s| {
15095            s.move_with(&mut |map, selection| {
15096                selection.collapse_to(
15097                    movement::start_of_excerpt(
15098                        map,
15099                        selection.head(),
15100                        workspace::searchable::Direction::Next,
15101                    ),
15102                    SelectionGoal::None,
15103                )
15104            });
15105        })
15106    }
15107
15108    pub fn move_to_end_of_excerpt(
15109        &mut self,
15110        _: &MoveToEndOfExcerpt,
15111        window: &mut Window,
15112        cx: &mut Context<Self>,
15113    ) {
15114        if matches!(self.mode, EditorMode::SingleLine) {
15115            cx.propagate();
15116            return;
15117        }
15118        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
15119        self.change_selections(Default::default(), window, cx, |s| {
15120            s.move_with(&mut |map, selection| {
15121                selection.collapse_to(
15122                    movement::end_of_excerpt(
15123                        map,
15124                        selection.head(),
15125                        workspace::searchable::Direction::Next,
15126                    ),
15127                    SelectionGoal::None,
15128                )
15129            });
15130        })
15131    }
15132
15133    pub fn move_to_end_of_previous_excerpt(
15134        &mut self,
15135        _: &MoveToEndOfPreviousExcerpt,
15136        window: &mut Window,
15137        cx: &mut Context<Self>,
15138    ) {
15139        if matches!(self.mode, EditorMode::SingleLine) {
15140            cx.propagate();
15141            return;
15142        }
15143        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
15144        self.change_selections(Default::default(), window, cx, |s| {
15145            s.move_with(&mut |map, selection| {
15146                selection.collapse_to(
15147                    movement::end_of_excerpt(
15148                        map,
15149                        selection.head(),
15150                        workspace::searchable::Direction::Prev,
15151                    ),
15152                    SelectionGoal::None,
15153                )
15154            });
15155        })
15156    }
15157
15158    pub fn select_to_start_of_excerpt(
15159        &mut self,
15160        _: &SelectToStartOfExcerpt,
15161        window: &mut Window,
15162        cx: &mut Context<Self>,
15163    ) {
15164        if matches!(self.mode, EditorMode::SingleLine) {
15165            cx.propagate();
15166            return;
15167        }
15168        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
15169        self.change_selections(Default::default(), window, cx, |s| {
15170            s.move_heads_with(&mut |map, head, _| {
15171                (
15172                    movement::start_of_excerpt(map, head, workspace::searchable::Direction::Prev),
15173                    SelectionGoal::None,
15174                )
15175            });
15176        })
15177    }
15178
15179    pub fn select_to_start_of_next_excerpt(
15180        &mut self,
15181        _: &SelectToStartOfNextExcerpt,
15182        window: &mut Window,
15183        cx: &mut Context<Self>,
15184    ) {
15185        if matches!(self.mode, EditorMode::SingleLine) {
15186            cx.propagate();
15187            return;
15188        }
15189        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
15190        self.change_selections(Default::default(), window, cx, |s| {
15191            s.move_heads_with(&mut |map, head, _| {
15192                (
15193                    movement::start_of_excerpt(map, head, workspace::searchable::Direction::Next),
15194                    SelectionGoal::None,
15195                )
15196            });
15197        })
15198    }
15199
15200    pub fn select_to_end_of_excerpt(
15201        &mut self,
15202        _: &SelectToEndOfExcerpt,
15203        window: &mut Window,
15204        cx: &mut Context<Self>,
15205    ) {
15206        if matches!(self.mode, EditorMode::SingleLine) {
15207            cx.propagate();
15208            return;
15209        }
15210        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
15211        self.change_selections(Default::default(), window, cx, |s| {
15212            s.move_heads_with(&mut |map, head, _| {
15213                (
15214                    movement::end_of_excerpt(map, head, workspace::searchable::Direction::Next),
15215                    SelectionGoal::None,
15216                )
15217            });
15218        })
15219    }
15220
15221    pub fn select_to_end_of_previous_excerpt(
15222        &mut self,
15223        _: &SelectToEndOfPreviousExcerpt,
15224        window: &mut Window,
15225        cx: &mut Context<Self>,
15226    ) {
15227        if matches!(self.mode, EditorMode::SingleLine) {
15228            cx.propagate();
15229            return;
15230        }
15231        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
15232        self.change_selections(Default::default(), window, cx, |s| {
15233            s.move_heads_with(&mut |map, head, _| {
15234                (
15235                    movement::end_of_excerpt(map, head, workspace::searchable::Direction::Prev),
15236                    SelectionGoal::None,
15237                )
15238            });
15239        })
15240    }
15241
15242    pub fn move_to_beginning(
15243        &mut self,
15244        _: &MoveToBeginning,
15245        window: &mut Window,
15246        cx: &mut Context<Self>,
15247    ) {
15248        if matches!(self.mode, EditorMode::SingleLine) {
15249            cx.propagate();
15250            return;
15251        }
15252        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
15253        self.change_selections(Default::default(), window, cx, |s| {
15254            s.select_ranges(vec![Anchor::min()..Anchor::min()]);
15255        });
15256    }
15257
15258    pub fn select_to_beginning(
15259        &mut self,
15260        _: &SelectToBeginning,
15261        window: &mut Window,
15262        cx: &mut Context<Self>,
15263    ) {
15264        let mut selection = self.selections.last::<Point>(&self.display_snapshot(cx));
15265        selection.set_head(Point::zero(), SelectionGoal::None);
15266        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
15267        self.change_selections(Default::default(), window, cx, |s| {
15268            s.select(vec![selection]);
15269        });
15270    }
15271
15272    pub fn move_to_end(&mut self, _: &MoveToEnd, window: &mut Window, cx: &mut Context<Self>) {
15273        if matches!(self.mode, EditorMode::SingleLine) {
15274            cx.propagate();
15275            return;
15276        }
15277        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
15278        let cursor = self.buffer.read(cx).read(cx).len();
15279        self.change_selections(Default::default(), window, cx, |s| {
15280            s.select_ranges(vec![cursor..cursor])
15281        });
15282    }
15283
15284    pub fn set_nav_history(&mut self, nav_history: Option<ItemNavHistory>) {
15285        self.nav_history = nav_history;
15286    }
15287
15288    pub fn nav_history(&self) -> Option<&ItemNavHistory> {
15289        self.nav_history.as_ref()
15290    }
15291
15292    pub fn create_nav_history_entry(&mut self, cx: &mut Context<Self>) {
15293        self.push_to_nav_history(
15294            self.selections.newest_anchor().head(),
15295            None,
15296            false,
15297            true,
15298            cx,
15299        );
15300    }
15301
15302    fn navigation_data(&self, cursor_anchor: Anchor, cx: &mut Context<Self>) -> NavigationData {
15303        let display_snapshot = self.display_map.update(cx, |map, cx| map.snapshot(cx));
15304        let buffer = self.buffer.read(cx).read(cx);
15305        let cursor_position = cursor_anchor.to_point(&buffer);
15306        let scroll_anchor = self.scroll_manager.native_anchor(&display_snapshot, cx);
15307        let scroll_top_row = scroll_anchor.top_row(&buffer);
15308        drop(buffer);
15309
15310        NavigationData {
15311            cursor_anchor,
15312            cursor_position,
15313            scroll_anchor,
15314            scroll_top_row,
15315        }
15316    }
15317
15318    fn navigation_entry(
15319        &self,
15320        cursor_anchor: Anchor,
15321        cx: &mut Context<Self>,
15322    ) -> Option<NavigationEntry> {
15323        let Some(history) = self.nav_history.clone() else {
15324            return None;
15325        };
15326        let data = self.navigation_data(cursor_anchor, cx);
15327        Some(history.navigation_entry(Some(Arc::new(data) as Arc<dyn Any + Send + Sync>)))
15328    }
15329
15330    fn push_to_nav_history(
15331        &mut self,
15332        cursor_anchor: Anchor,
15333        new_position: Option<Point>,
15334        is_deactivate: bool,
15335        always: bool,
15336        cx: &mut Context<Self>,
15337    ) {
15338        let data = self.navigation_data(cursor_anchor, cx);
15339        if let Some(nav_history) = self.nav_history.as_mut() {
15340            if let Some(new_position) = new_position {
15341                let row_delta = (new_position.row as i64 - data.cursor_position.row as i64).abs();
15342                if row_delta == 0 || (row_delta < MIN_NAVIGATION_HISTORY_ROW_DELTA && !always) {
15343                    return;
15344                }
15345            }
15346
15347            nav_history.push(Some(data), cx);
15348            cx.emit(EditorEvent::PushedToNavHistory {
15349                anchor: cursor_anchor,
15350                is_deactivate,
15351            })
15352        }
15353    }
15354
15355    pub fn select_to_end(&mut self, _: &SelectToEnd, window: &mut Window, cx: &mut Context<Self>) {
15356        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
15357        let buffer = self.buffer.read(cx).snapshot(cx);
15358        let mut selection = self
15359            .selections
15360            .first::<MultiBufferOffset>(&self.display_snapshot(cx));
15361        selection.set_head(buffer.len(), SelectionGoal::None);
15362        self.change_selections(Default::default(), window, cx, |s| {
15363            s.select(vec![selection]);
15364        });
15365    }
15366
15367    pub fn select_all(&mut self, _: &SelectAll, window: &mut Window, cx: &mut Context<Self>) {
15368        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
15369        self.change_selections(SelectionEffects::no_scroll(), window, cx, |s| {
15370            s.select_ranges([Anchor::min()..Anchor::max()]);
15371        });
15372    }
15373
15374    pub fn select_line(&mut self, _: &SelectLine, window: &mut Window, cx: &mut Context<Self>) {
15375        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
15376        let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
15377        let mut selections = self.selections.all::<Point>(&display_map);
15378        let max_point = display_map.buffer_snapshot().max_point();
15379        for selection in &mut selections {
15380            let rows = selection.spanned_rows(true, &display_map);
15381            selection.start = Point::new(rows.start.0, 0);
15382            selection.end = cmp::min(max_point, Point::new(rows.end.0, 0));
15383            selection.reversed = false;
15384        }
15385        self.change_selections(Default::default(), window, cx, |s| {
15386            s.select(selections);
15387        });
15388    }
15389
15390    pub fn split_selection_into_lines(
15391        &mut self,
15392        action: &SplitSelectionIntoLines,
15393        window: &mut Window,
15394        cx: &mut Context<Self>,
15395    ) {
15396        let selections = self
15397            .selections
15398            .all::<Point>(&self.display_snapshot(cx))
15399            .into_iter()
15400            .map(|selection| selection.start..selection.end)
15401            .collect::<Vec<_>>();
15402        self.unfold_ranges(&selections, true, false, cx);
15403
15404        let mut new_selection_ranges = Vec::new();
15405        {
15406            let buffer = self.buffer.read(cx).read(cx);
15407            for selection in selections {
15408                for row in selection.start.row..selection.end.row {
15409                    let line_start = Point::new(row, 0);
15410                    let line_end = Point::new(row, buffer.line_len(MultiBufferRow(row)));
15411
15412                    if action.keep_selections {
15413                        // Keep the selection range for each line
15414                        let selection_start = if row == selection.start.row {
15415                            selection.start
15416                        } else {
15417                            line_start
15418                        };
15419                        new_selection_ranges.push(selection_start..line_end);
15420                    } else {
15421                        // Collapse to cursor at end of line
15422                        new_selection_ranges.push(line_end..line_end);
15423                    }
15424                }
15425
15426                let is_multiline_selection = selection.start.row != selection.end.row;
15427                // Don't insert last one if it's a multi-line selection ending at the start of a line,
15428                // so this action feels more ergonomic when paired with other selection operations
15429                let should_skip_last = is_multiline_selection && selection.end.column == 0;
15430                if !should_skip_last {
15431                    if action.keep_selections {
15432                        if is_multiline_selection {
15433                            let line_start = Point::new(selection.end.row, 0);
15434                            new_selection_ranges.push(line_start..selection.end);
15435                        } else {
15436                            new_selection_ranges.push(selection.start..selection.end);
15437                        }
15438                    } else {
15439                        new_selection_ranges.push(selection.end..selection.end);
15440                    }
15441                }
15442            }
15443        }
15444        self.change_selections(SelectionEffects::no_scroll(), window, cx, |s| {
15445            s.select_ranges(new_selection_ranges);
15446        });
15447    }
15448
15449    pub fn add_selection_above(
15450        &mut self,
15451        action: &AddSelectionAbove,
15452        window: &mut Window,
15453        cx: &mut Context<Self>,
15454    ) {
15455        self.add_selection(true, action.skip_soft_wrap, window, cx);
15456    }
15457
15458    pub fn add_selection_below(
15459        &mut self,
15460        action: &AddSelectionBelow,
15461        window: &mut Window,
15462        cx: &mut Context<Self>,
15463    ) {
15464        self.add_selection(false, action.skip_soft_wrap, window, cx);
15465    }
15466
15467    fn add_selection(
15468        &mut self,
15469        above: bool,
15470        skip_soft_wrap: bool,
15471        window: &mut Window,
15472        cx: &mut Context<Self>,
15473    ) {
15474        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
15475
15476        let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
15477        let all_selections = self.selections.all::<Point>(&display_map);
15478        let text_layout_details = self.text_layout_details(window, cx);
15479
15480        let (mut columnar_selections, new_selections_to_columnarize) = {
15481            if let Some(state) = self.add_selections_state.as_ref() {
15482                let columnar_selection_ids: HashSet<_> = state
15483                    .groups
15484                    .iter()
15485                    .flat_map(|group| group.stack.iter())
15486                    .copied()
15487                    .collect();
15488
15489                all_selections
15490                    .into_iter()
15491                    .partition(|s| columnar_selection_ids.contains(&s.id))
15492            } else {
15493                (Vec::new(), all_selections)
15494            }
15495        };
15496
15497        let mut state = self
15498            .add_selections_state
15499            .take()
15500            .unwrap_or_else(|| AddSelectionsState { groups: Vec::new() });
15501
15502        for selection in new_selections_to_columnarize {
15503            let range = selection.display_range(&display_map).sorted();
15504            let start_x = display_map.x_for_display_point(range.start, &text_layout_details);
15505            let end_x = display_map.x_for_display_point(range.end, &text_layout_details);
15506            let positions = start_x.min(end_x)..start_x.max(end_x);
15507            let mut stack = Vec::new();
15508            for row in range.start.row().0..=range.end.row().0 {
15509                if let Some(selection) = self.selections.build_columnar_selection(
15510                    &display_map,
15511                    DisplayRow(row),
15512                    &positions,
15513                    selection.reversed,
15514                    &text_layout_details,
15515                ) {
15516                    stack.push(selection.id);
15517                    columnar_selections.push(selection);
15518                }
15519            }
15520            if !stack.is_empty() {
15521                if above {
15522                    stack.reverse();
15523                }
15524                state.groups.push(AddSelectionsGroup { above, stack });
15525            }
15526        }
15527
15528        let mut final_selections = Vec::new();
15529        let end_row = if above {
15530            DisplayRow(0)
15531        } else {
15532            display_map.max_point().row()
15533        };
15534
15535        // When `skip_soft_wrap` is true, we use buffer columns instead of pixel
15536        // positions to place new selections, so we need to keep track of the
15537        // column range of the oldest selection in each group, because
15538        // intermediate selections may have been clamped to shorter lines.
15539        // selections may have been clamped to shorter lines.
15540        let mut goal_columns_by_selection_id = if skip_soft_wrap {
15541            let mut map = HashMap::default();
15542            for group in state.groups.iter() {
15543                if let Some(oldest_id) = group.stack.first() {
15544                    if let Some(oldest_selection) =
15545                        columnar_selections.iter().find(|s| s.id == *oldest_id)
15546                    {
15547                        let start_col = oldest_selection.start.column;
15548                        let end_col = oldest_selection.end.column;
15549                        let goal_columns = start_col.min(end_col)..start_col.max(end_col);
15550                        for id in &group.stack {
15551                            map.insert(*id, goal_columns.clone());
15552                        }
15553                    }
15554                }
15555            }
15556            map
15557        } else {
15558            HashMap::default()
15559        };
15560
15561        let mut last_added_item_per_group = HashMap::default();
15562        for group in state.groups.iter_mut() {
15563            if let Some(last_id) = group.stack.last() {
15564                last_added_item_per_group.insert(*last_id, group);
15565            }
15566        }
15567
15568        for selection in columnar_selections {
15569            if let Some(group) = last_added_item_per_group.get_mut(&selection.id) {
15570                if above == group.above {
15571                    let range = selection.display_range(&display_map).sorted();
15572                    debug_assert_eq!(range.start.row(), range.end.row());
15573                    let row = range.start.row();
15574                    let positions =
15575                        if let SelectionGoal::HorizontalRange { start, end } = selection.goal {
15576                            Pixels::from(start)..Pixels::from(end)
15577                        } else {
15578                            let start_x =
15579                                display_map.x_for_display_point(range.start, &text_layout_details);
15580                            let end_x =
15581                                display_map.x_for_display_point(range.end, &text_layout_details);
15582                            start_x.min(end_x)..start_x.max(end_x)
15583                        };
15584
15585                    let maybe_new_selection = if skip_soft_wrap {
15586                        let goal_columns = goal_columns_by_selection_id
15587                            .remove(&selection.id)
15588                            .unwrap_or_else(|| {
15589                                let start_col = selection.start.column;
15590                                let end_col = selection.end.column;
15591                                start_col.min(end_col)..start_col.max(end_col)
15592                            });
15593                        self.selections.find_next_columnar_selection_by_buffer_row(
15594                            &display_map,
15595                            row,
15596                            end_row,
15597                            above,
15598                            &goal_columns,
15599                            selection.reversed,
15600                            &text_layout_details,
15601                        )
15602                    } else {
15603                        self.selections.find_next_columnar_selection_by_display_row(
15604                            &display_map,
15605                            row,
15606                            end_row,
15607                            above,
15608                            &positions,
15609                            selection.reversed,
15610                            &text_layout_details,
15611                        )
15612                    };
15613
15614                    if let Some(new_selection) = maybe_new_selection {
15615                        group.stack.push(new_selection.id);
15616                        if above {
15617                            final_selections.push(new_selection);
15618                            final_selections.push(selection);
15619                        } else {
15620                            final_selections.push(selection);
15621                            final_selections.push(new_selection);
15622                        }
15623                    } else {
15624                        final_selections.push(selection);
15625                    }
15626                } else {
15627                    group.stack.pop();
15628                }
15629            } else {
15630                final_selections.push(selection);
15631            }
15632        }
15633
15634        self.change_selections(Default::default(), window, cx, |s| {
15635            s.select(final_selections);
15636        });
15637
15638        let final_selection_ids: HashSet<_> = self
15639            .selections
15640            .all::<Point>(&display_map)
15641            .iter()
15642            .map(|s| s.id)
15643            .collect();
15644        state.groups.retain_mut(|group| {
15645            // selections might get merged above so we remove invalid items from stacks
15646            group.stack.retain(|id| final_selection_ids.contains(id));
15647
15648            // single selection in stack can be treated as initial state
15649            group.stack.len() > 1
15650        });
15651
15652        if !state.groups.is_empty() {
15653            self.add_selections_state = Some(state);
15654        }
15655    }
15656
15657    pub fn insert_snippet_at_selections(
15658        &mut self,
15659        action: &InsertSnippet,
15660        window: &mut Window,
15661        cx: &mut Context<Self>,
15662    ) {
15663        self.try_insert_snippet_at_selections(action, window, cx)
15664            .log_err();
15665    }
15666
15667    fn try_insert_snippet_at_selections(
15668        &mut self,
15669        action: &InsertSnippet,
15670        window: &mut Window,
15671        cx: &mut Context<Self>,
15672    ) -> Result<()> {
15673        let insertion_ranges = self
15674            .selections
15675            .all::<MultiBufferOffset>(&self.display_snapshot(cx))
15676            .into_iter()
15677            .map(|selection| selection.range())
15678            .collect_vec();
15679
15680        let snippet = if let Some(snippet_body) = &action.snippet {
15681            if action.language.is_none() && action.name.is_none() {
15682                Snippet::parse(snippet_body)?
15683            } else {
15684                bail!("`snippet` is mutually exclusive with `language` and `name`")
15685            }
15686        } else if let Some(name) = &action.name {
15687            let project = self.project().context("no project")?;
15688            let snippet_store = project.read(cx).snippets().read(cx);
15689            let snippet = snippet_store
15690                .snippets_for(action.language.clone(), cx)
15691                .into_iter()
15692                .find(|snippet| snippet.name == *name)
15693                .context("snippet not found")?;
15694            Snippet::parse(&snippet.body)?
15695        } else {
15696            // todo(andrew): open modal to select snippet
15697            bail!("`name` or `snippet` is required")
15698        };
15699
15700        self.insert_snippet(&insertion_ranges, snippet, window, cx)
15701    }
15702
15703    fn select_match_ranges(
15704        &mut self,
15705        range: Range<MultiBufferOffset>,
15706        reversed: bool,
15707        replace_newest: bool,
15708        auto_scroll: Option<Autoscroll>,
15709        window: &mut Window,
15710        cx: &mut Context<Editor>,
15711    ) {
15712        self.unfold_ranges(
15713            std::slice::from_ref(&range),
15714            false,
15715            auto_scroll.is_some(),
15716            cx,
15717        );
15718        let effects = if let Some(scroll) = auto_scroll {
15719            SelectionEffects::scroll(scroll)
15720        } else {
15721            SelectionEffects::no_scroll()
15722        };
15723        self.change_selections(effects, window, cx, |s| {
15724            if replace_newest {
15725                s.delete(s.newest_anchor().id);
15726            }
15727            if reversed {
15728                s.insert_range(range.end..range.start);
15729            } else {
15730                s.insert_range(range);
15731            }
15732        });
15733    }
15734
15735    pub fn select_next_match_internal(
15736        &mut self,
15737        display_map: &DisplaySnapshot,
15738        replace_newest: bool,
15739        autoscroll: Option<Autoscroll>,
15740        window: &mut Window,
15741        cx: &mut Context<Self>,
15742    ) -> Result<()> {
15743        let buffer = display_map.buffer_snapshot();
15744        let mut selections = self.selections.all::<MultiBufferOffset>(&display_map);
15745        if let Some(mut select_next_state) = self.select_next_state.take() {
15746            let query = &select_next_state.query;
15747            if !select_next_state.done {
15748                let first_selection = selections.iter().min_by_key(|s| s.id).unwrap();
15749                let last_selection = selections.iter().max_by_key(|s| s.id).unwrap();
15750                let mut next_selected_range = None;
15751
15752                let bytes_after_last_selection =
15753                    buffer.bytes_in_range(last_selection.end..buffer.len());
15754                let bytes_before_first_selection =
15755                    buffer.bytes_in_range(MultiBufferOffset(0)..first_selection.start);
15756                let query_matches = query
15757                    .stream_find_iter(bytes_after_last_selection)
15758                    .map(|result| (last_selection.end, result))
15759                    .chain(
15760                        query
15761                            .stream_find_iter(bytes_before_first_selection)
15762                            .map(|result| (MultiBufferOffset(0), result)),
15763                    );
15764
15765                for (start_offset, query_match) in query_matches {
15766                    let query_match = query_match.unwrap(); // can only fail due to I/O
15767                    let offset_range =
15768                        start_offset + query_match.start()..start_offset + query_match.end();
15769
15770                    if !select_next_state.wordwise
15771                        || (!buffer.is_inside_word(offset_range.start, None)
15772                            && !buffer.is_inside_word(offset_range.end, None))
15773                    {
15774                        let idx = selections
15775                            .partition_point(|selection| selection.end <= offset_range.start);
15776                        let overlaps = selections
15777                            .get(idx)
15778                            .map_or(false, |selection| selection.start < offset_range.end);
15779
15780                        if !overlaps {
15781                            next_selected_range = Some(offset_range);
15782                            break;
15783                        }
15784                    }
15785                }
15786
15787                if let Some(next_selected_range) = next_selected_range {
15788                    self.select_match_ranges(
15789                        next_selected_range,
15790                        last_selection.reversed,
15791                        replace_newest,
15792                        autoscroll,
15793                        window,
15794                        cx,
15795                    );
15796                } else {
15797                    select_next_state.done = true;
15798                }
15799            }
15800
15801            self.select_next_state = Some(select_next_state);
15802        } else {
15803            let mut only_carets = true;
15804            let mut same_text_selected = true;
15805            let mut selected_text = None;
15806
15807            let mut selections_iter = selections.iter().peekable();
15808            while let Some(selection) = selections_iter.next() {
15809                if selection.start != selection.end {
15810                    only_carets = false;
15811                }
15812
15813                if same_text_selected {
15814                    if selected_text.is_none() {
15815                        selected_text =
15816                            Some(buffer.text_for_range(selection.range()).collect::<String>());
15817                    }
15818
15819                    if let Some(next_selection) = selections_iter.peek() {
15820                        if next_selection.len() == selection.len() {
15821                            let next_selected_text = buffer
15822                                .text_for_range(next_selection.range())
15823                                .collect::<String>();
15824                            if Some(next_selected_text) != selected_text {
15825                                same_text_selected = false;
15826                                selected_text = None;
15827                            }
15828                        } else {
15829                            same_text_selected = false;
15830                            selected_text = None;
15831                        }
15832                    }
15833                }
15834            }
15835
15836            if only_carets {
15837                for selection in &mut selections {
15838                    let (word_range, _) = buffer.surrounding_word(selection.start, None);
15839                    selection.start = word_range.start;
15840                    selection.end = word_range.end;
15841                    selection.goal = SelectionGoal::None;
15842                    selection.reversed = false;
15843                    self.select_match_ranges(
15844                        selection.start..selection.end,
15845                        selection.reversed,
15846                        replace_newest,
15847                        autoscroll,
15848                        window,
15849                        cx,
15850                    );
15851                }
15852
15853                if selections.len() == 1 {
15854                    let selection = selections
15855                        .last()
15856                        .expect("ensured that there's only one selection");
15857                    let query = buffer
15858                        .text_for_range(selection.start..selection.end)
15859                        .collect::<String>();
15860                    let is_empty = query.is_empty();
15861                    let select_state = SelectNextState {
15862                        query: self.build_query(&[query], cx)?,
15863                        wordwise: true,
15864                        done: is_empty,
15865                    };
15866                    self.select_next_state = Some(select_state);
15867                } else {
15868                    self.select_next_state = None;
15869                }
15870            } else if let Some(selected_text) = selected_text {
15871                self.select_next_state = Some(SelectNextState {
15872                    query: self.build_query(&[selected_text], cx)?,
15873                    wordwise: false,
15874                    done: false,
15875                });
15876                self.select_next_match_internal(
15877                    display_map,
15878                    replace_newest,
15879                    autoscroll,
15880                    window,
15881                    cx,
15882                )?;
15883            }
15884        }
15885        Ok(())
15886    }
15887
15888    pub fn select_all_matches(
15889        &mut self,
15890        _action: &SelectAllMatches,
15891        window: &mut Window,
15892        cx: &mut Context<Self>,
15893    ) -> Result<()> {
15894        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
15895
15896        let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
15897
15898        self.select_next_match_internal(&display_map, false, None, window, cx)?;
15899        let Some(select_next_state) = self.select_next_state.as_mut().filter(|state| !state.done)
15900        else {
15901            return Ok(());
15902        };
15903
15904        let mut new_selections = Vec::new();
15905
15906        let reversed = self
15907            .selections
15908            .oldest::<MultiBufferOffset>(&display_map)
15909            .reversed;
15910        let buffer = display_map.buffer_snapshot();
15911        let query_matches = select_next_state
15912            .query
15913            .stream_find_iter(buffer.bytes_in_range(MultiBufferOffset(0)..buffer.len()));
15914
15915        for query_match in query_matches.into_iter() {
15916            let query_match = query_match.context("query match for select all action")?; // can only fail due to I/O
15917            let offset_range = if reversed {
15918                MultiBufferOffset(query_match.end())..MultiBufferOffset(query_match.start())
15919            } else {
15920                MultiBufferOffset(query_match.start())..MultiBufferOffset(query_match.end())
15921            };
15922
15923            if !select_next_state.wordwise
15924                || (!buffer.is_inside_word(offset_range.start, None)
15925                    && !buffer.is_inside_word(offset_range.end, None))
15926            {
15927                new_selections.push(offset_range.start..offset_range.end);
15928            }
15929        }
15930
15931        select_next_state.done = true;
15932
15933        if new_selections.is_empty() {
15934            log::error!("bug: new_selections is empty in select_all_matches");
15935            return Ok(());
15936        }
15937
15938        self.unfold_ranges(&new_selections, false, false, cx);
15939        self.change_selections(SelectionEffects::no_scroll(), window, cx, |selections| {
15940            selections.select_ranges(new_selections)
15941        });
15942
15943        Ok(())
15944    }
15945
15946    pub fn select_next(
15947        &mut self,
15948        action: &SelectNext,
15949        window: &mut Window,
15950        cx: &mut Context<Self>,
15951    ) -> Result<()> {
15952        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
15953        let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
15954        self.select_next_match_internal(
15955            &display_map,
15956            action.replace_newest,
15957            Some(Autoscroll::newest()),
15958            window,
15959            cx,
15960        )
15961    }
15962
15963    pub fn select_previous(
15964        &mut self,
15965        action: &SelectPrevious,
15966        window: &mut Window,
15967        cx: &mut Context<Self>,
15968    ) -> Result<()> {
15969        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
15970        let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
15971        let buffer = display_map.buffer_snapshot();
15972        let mut selections = self.selections.all::<MultiBufferOffset>(&display_map);
15973        if let Some(mut select_prev_state) = self.select_prev_state.take() {
15974            let query = &select_prev_state.query;
15975            if !select_prev_state.done {
15976                let first_selection = selections.iter().min_by_key(|s| s.id).unwrap();
15977                let last_selection = selections.iter().max_by_key(|s| s.id).unwrap();
15978                let mut next_selected_range = None;
15979                // When we're iterating matches backwards, the oldest match will actually be the furthest one in the buffer.
15980                let bytes_before_last_selection =
15981                    buffer.reversed_bytes_in_range(MultiBufferOffset(0)..last_selection.start);
15982                let bytes_after_first_selection =
15983                    buffer.reversed_bytes_in_range(first_selection.end..buffer.len());
15984                let query_matches = query
15985                    .stream_find_iter(bytes_before_last_selection)
15986                    .map(|result| (last_selection.start, result))
15987                    .chain(
15988                        query
15989                            .stream_find_iter(bytes_after_first_selection)
15990                            .map(|result| (buffer.len(), result)),
15991                    );
15992                for (end_offset, query_match) in query_matches {
15993                    let query_match = query_match.unwrap(); // can only fail due to I/O
15994                    let offset_range =
15995                        end_offset - query_match.end()..end_offset - query_match.start();
15996
15997                    if !select_prev_state.wordwise
15998                        || (!buffer.is_inside_word(offset_range.start, None)
15999                            && !buffer.is_inside_word(offset_range.end, None))
16000                    {
16001                        next_selected_range = Some(offset_range);
16002                        break;
16003                    }
16004                }
16005
16006                if let Some(next_selected_range) = next_selected_range {
16007                    self.select_match_ranges(
16008                        next_selected_range,
16009                        last_selection.reversed,
16010                        action.replace_newest,
16011                        Some(Autoscroll::newest()),
16012                        window,
16013                        cx,
16014                    );
16015                } else {
16016                    select_prev_state.done = true;
16017                }
16018            }
16019
16020            self.select_prev_state = Some(select_prev_state);
16021        } else {
16022            let mut only_carets = true;
16023            let mut same_text_selected = true;
16024            let mut selected_text = None;
16025
16026            let mut selections_iter = selections.iter().peekable();
16027            while let Some(selection) = selections_iter.next() {
16028                if selection.start != selection.end {
16029                    only_carets = false;
16030                }
16031
16032                if same_text_selected {
16033                    if selected_text.is_none() {
16034                        selected_text =
16035                            Some(buffer.text_for_range(selection.range()).collect::<String>());
16036                    }
16037
16038                    if let Some(next_selection) = selections_iter.peek() {
16039                        if next_selection.len() == selection.len() {
16040                            let next_selected_text = buffer
16041                                .text_for_range(next_selection.range())
16042                                .collect::<String>();
16043                            if Some(next_selected_text) != selected_text {
16044                                same_text_selected = false;
16045                                selected_text = None;
16046                            }
16047                        } else {
16048                            same_text_selected = false;
16049                            selected_text = None;
16050                        }
16051                    }
16052                }
16053            }
16054
16055            if only_carets {
16056                for selection in &mut selections {
16057                    let (word_range, _) = buffer.surrounding_word(selection.start, None);
16058                    selection.start = word_range.start;
16059                    selection.end = word_range.end;
16060                    selection.goal = SelectionGoal::None;
16061                    selection.reversed = false;
16062                    self.select_match_ranges(
16063                        selection.start..selection.end,
16064                        selection.reversed,
16065                        action.replace_newest,
16066                        Some(Autoscroll::newest()),
16067                        window,
16068                        cx,
16069                    );
16070                }
16071                if selections.len() == 1 {
16072                    let selection = selections
16073                        .last()
16074                        .expect("ensured that there's only one selection");
16075                    let query = buffer
16076                        .text_for_range(selection.start..selection.end)
16077                        .collect::<String>();
16078                    let is_empty = query.is_empty();
16079                    let select_state = SelectNextState {
16080                        query: self.build_query(&[query.chars().rev().collect::<String>()], cx)?,
16081                        wordwise: true,
16082                        done: is_empty,
16083                    };
16084                    self.select_prev_state = Some(select_state);
16085                } else {
16086                    self.select_prev_state = None;
16087                }
16088            } else if let Some(selected_text) = selected_text {
16089                self.select_prev_state = Some(SelectNextState {
16090                    query: self
16091                        .build_query(&[selected_text.chars().rev().collect::<String>()], cx)?,
16092                    wordwise: false,
16093                    done: false,
16094                });
16095                self.select_previous(action, window, cx)?;
16096            }
16097        }
16098        Ok(())
16099    }
16100
16101    /// Builds an `AhoCorasick` automaton from the provided patterns, while
16102    /// setting the case sensitivity based on the global
16103    /// `SelectNextCaseSensitive` setting, if set, otherwise based on the
16104    /// editor's settings.
16105    fn build_query<I, P>(&self, patterns: I, cx: &Context<Self>) -> Result<AhoCorasick, BuildError>
16106    where
16107        I: IntoIterator<Item = P>,
16108        P: AsRef<[u8]>,
16109    {
16110        let case_sensitive = self
16111            .select_next_is_case_sensitive
16112            .unwrap_or_else(|| EditorSettings::get_global(cx).search.case_sensitive);
16113
16114        let mut builder = AhoCorasickBuilder::new();
16115        builder.ascii_case_insensitive(!case_sensitive);
16116        builder.build(patterns)
16117    }
16118
16119    pub fn find_next_match(
16120        &mut self,
16121        _: &FindNextMatch,
16122        window: &mut Window,
16123        cx: &mut Context<Self>,
16124    ) -> Result<()> {
16125        let selections = self.selections.disjoint_anchors_arc();
16126        match selections.first() {
16127            Some(first) if selections.len() >= 2 => {
16128                self.change_selections(Default::default(), window, cx, |s| {
16129                    s.select_ranges([first.range()]);
16130                });
16131            }
16132            _ => self.select_next(
16133                &SelectNext {
16134                    replace_newest: true,
16135                },
16136                window,
16137                cx,
16138            )?,
16139        }
16140        Ok(())
16141    }
16142
16143    pub fn find_previous_match(
16144        &mut self,
16145        _: &FindPreviousMatch,
16146        window: &mut Window,
16147        cx: &mut Context<Self>,
16148    ) -> Result<()> {
16149        let selections = self.selections.disjoint_anchors_arc();
16150        match selections.last() {
16151            Some(last) if selections.len() >= 2 => {
16152                self.change_selections(Default::default(), window, cx, |s| {
16153                    s.select_ranges([last.range()]);
16154                });
16155            }
16156            _ => self.select_previous(
16157                &SelectPrevious {
16158                    replace_newest: true,
16159                },
16160                window,
16161                cx,
16162            )?,
16163        }
16164        Ok(())
16165    }
16166
16167    pub fn toggle_comments(
16168        &mut self,
16169        action: &ToggleComments,
16170        window: &mut Window,
16171        cx: &mut Context<Self>,
16172    ) {
16173        if self.read_only(cx) {
16174            return;
16175        }
16176        self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
16177        let text_layout_details = &self.text_layout_details(window, cx);
16178        self.transact(window, cx, |this, window, cx| {
16179            let mut selections = this
16180                .selections
16181                .all::<MultiBufferPoint>(&this.display_snapshot(cx));
16182            let mut edits = Vec::new();
16183            let mut selection_edit_ranges = Vec::new();
16184            let mut last_toggled_row = None;
16185            let snapshot = this.buffer.read(cx).read(cx);
16186            let empty_str: Arc<str> = Arc::default();
16187            let mut suffixes_inserted = Vec::new();
16188            let ignore_indent = action.ignore_indent;
16189
16190            fn comment_prefix_range(
16191                snapshot: &MultiBufferSnapshot,
16192                row: MultiBufferRow,
16193                comment_prefix: &str,
16194                comment_prefix_whitespace: &str,
16195                ignore_indent: bool,
16196            ) -> Range<Point> {
16197                let indent_size = if ignore_indent {
16198                    0
16199                } else {
16200                    snapshot.indent_size_for_line(row).len
16201                };
16202
16203                let start = Point::new(row.0, indent_size);
16204
16205                let mut line_bytes = snapshot
16206                    .bytes_in_range(start..snapshot.max_point())
16207                    .flatten()
16208                    .copied();
16209
16210                // If this line currently begins with the line comment prefix, then record
16211                // the range containing the prefix.
16212                if line_bytes
16213                    .by_ref()
16214                    .take(comment_prefix.len())
16215                    .eq(comment_prefix.bytes())
16216                {
16217                    // Include any whitespace that matches the comment prefix.
16218                    let matching_whitespace_len = line_bytes
16219                        .zip(comment_prefix_whitespace.bytes())
16220                        .take_while(|(a, b)| a == b)
16221                        .count() as u32;
16222                    let end = Point::new(
16223                        start.row,
16224                        start.column + comment_prefix.len() as u32 + matching_whitespace_len,
16225                    );
16226                    start..end
16227                } else {
16228                    start..start
16229                }
16230            }
16231
16232            fn comment_suffix_range(
16233                snapshot: &MultiBufferSnapshot,
16234                row: MultiBufferRow,
16235                comment_suffix: &str,
16236                comment_suffix_has_leading_space: bool,
16237            ) -> Range<Point> {
16238                let end = Point::new(row.0, snapshot.line_len(row));
16239                let suffix_start_column = end.column.saturating_sub(comment_suffix.len() as u32);
16240
16241                let mut line_end_bytes = snapshot
16242                    .bytes_in_range(Point::new(end.row, suffix_start_column.saturating_sub(1))..end)
16243                    .flatten()
16244                    .copied();
16245
16246                let leading_space_len = if suffix_start_column > 0
16247                    && line_end_bytes.next() == Some(b' ')
16248                    && comment_suffix_has_leading_space
16249                {
16250                    1
16251                } else {
16252                    0
16253                };
16254
16255                // If this line currently begins with the line comment prefix, then record
16256                // the range containing the prefix.
16257                if line_end_bytes.by_ref().eq(comment_suffix.bytes()) {
16258                    let start = Point::new(end.row, suffix_start_column - leading_space_len);
16259                    start..end
16260                } else {
16261                    end..end
16262                }
16263            }
16264
16265            // TODO: Handle selections that cross excerpts
16266            for selection in &mut selections {
16267                let start_column = snapshot
16268                    .indent_size_for_line(MultiBufferRow(selection.start.row))
16269                    .len;
16270                let language = if let Some(language) =
16271                    snapshot.language_scope_at(Point::new(selection.start.row, start_column))
16272                {
16273                    language
16274                } else {
16275                    continue;
16276                };
16277
16278                selection_edit_ranges.clear();
16279
16280                // If multiple selections contain a given row, avoid processing that
16281                // row more than once.
16282                let mut start_row = MultiBufferRow(selection.start.row);
16283                if last_toggled_row == Some(start_row) {
16284                    start_row = start_row.next_row();
16285                }
16286                let end_row =
16287                    if selection.end.row > selection.start.row && selection.end.column == 0 {
16288                        MultiBufferRow(selection.end.row - 1)
16289                    } else {
16290                        MultiBufferRow(selection.end.row)
16291                    };
16292                last_toggled_row = Some(end_row);
16293
16294                if start_row > end_row {
16295                    continue;
16296                }
16297
16298                // If the language has line comments, toggle those.
16299                let mut full_comment_prefixes = language.line_comment_prefixes().to_vec();
16300
16301                // If ignore_indent is set, trim spaces from the right side of all full_comment_prefixes
16302                if ignore_indent {
16303                    full_comment_prefixes = full_comment_prefixes
16304                        .into_iter()
16305                        .map(|s| Arc::from(s.trim_end()))
16306                        .collect();
16307                }
16308
16309                if !full_comment_prefixes.is_empty() {
16310                    let first_prefix = full_comment_prefixes
16311                        .first()
16312                        .expect("prefixes is non-empty");
16313                    let prefix_trimmed_lengths = full_comment_prefixes
16314                        .iter()
16315                        .map(|p| p.trim_end_matches(' ').len())
16316                        .collect::<SmallVec<[usize; 4]>>();
16317
16318                    let mut all_selection_lines_are_comments = true;
16319
16320                    for row in start_row.0..=end_row.0 {
16321                        let row = MultiBufferRow(row);
16322                        if start_row < end_row && snapshot.is_line_blank(row) {
16323                            continue;
16324                        }
16325
16326                        let prefix_range = full_comment_prefixes
16327                            .iter()
16328                            .zip(prefix_trimmed_lengths.iter().copied())
16329                            .map(|(prefix, trimmed_prefix_len)| {
16330                                comment_prefix_range(
16331                                    snapshot.deref(),
16332                                    row,
16333                                    &prefix[..trimmed_prefix_len],
16334                                    &prefix[trimmed_prefix_len..],
16335                                    ignore_indent,
16336                                )
16337                            })
16338                            .max_by_key(|range| range.end.column - range.start.column)
16339                            .expect("prefixes is non-empty");
16340
16341                        if prefix_range.is_empty() {
16342                            all_selection_lines_are_comments = false;
16343                        }
16344
16345                        selection_edit_ranges.push(prefix_range);
16346                    }
16347
16348                    if all_selection_lines_are_comments {
16349                        edits.extend(
16350                            selection_edit_ranges
16351                                .iter()
16352                                .cloned()
16353                                .map(|range| (range, empty_str.clone())),
16354                        );
16355                    } else {
16356                        let min_column = selection_edit_ranges
16357                            .iter()
16358                            .map(|range| range.start.column)
16359                            .min()
16360                            .unwrap_or(0);
16361                        edits.extend(selection_edit_ranges.iter().map(|range| {
16362                            let position = Point::new(range.start.row, min_column);
16363                            (position..position, first_prefix.clone())
16364                        }));
16365                    }
16366                } else if let Some(BlockCommentConfig {
16367                    start: full_comment_prefix,
16368                    end: comment_suffix,
16369                    ..
16370                }) = language.block_comment()
16371                {
16372                    let comment_prefix = full_comment_prefix.trim_end_matches(' ');
16373                    let comment_prefix_whitespace = &full_comment_prefix[comment_prefix.len()..];
16374                    let prefix_range = comment_prefix_range(
16375                        snapshot.deref(),
16376                        start_row,
16377                        comment_prefix,
16378                        comment_prefix_whitespace,
16379                        ignore_indent,
16380                    );
16381                    let suffix_range = comment_suffix_range(
16382                        snapshot.deref(),
16383                        end_row,
16384                        comment_suffix.trim_start_matches(' '),
16385                        comment_suffix.starts_with(' '),
16386                    );
16387
16388                    if prefix_range.is_empty() || suffix_range.is_empty() {
16389                        edits.push((
16390                            prefix_range.start..prefix_range.start,
16391                            full_comment_prefix.clone(),
16392                        ));
16393                        edits.push((suffix_range.end..suffix_range.end, comment_suffix.clone()));
16394                        suffixes_inserted.push((end_row, comment_suffix.len()));
16395                    } else {
16396                        edits.push((prefix_range, empty_str.clone()));
16397                        edits.push((suffix_range, empty_str.clone()));
16398                    }
16399                } else {
16400                    continue;
16401                }
16402            }
16403
16404            drop(snapshot);
16405            this.buffer.update(cx, |buffer, cx| {
16406                buffer.edit(edits, None, cx);
16407            });
16408
16409            // Adjust selections so that they end before any comment suffixes that
16410            // were inserted.
16411            let mut suffixes_inserted = suffixes_inserted.into_iter().peekable();
16412            let mut selections = this.selections.all::<Point>(&this.display_snapshot(cx));
16413            let snapshot = this.buffer.read(cx).read(cx);
16414            for selection in &mut selections {
16415                while let Some((row, suffix_len)) = suffixes_inserted.peek().copied() {
16416                    match row.cmp(&MultiBufferRow(selection.end.row)) {
16417                        Ordering::Less => {
16418                            suffixes_inserted.next();
16419                            continue;
16420                        }
16421                        Ordering::Greater => break,
16422                        Ordering::Equal => {
16423                            if selection.end.column == snapshot.line_len(row) {
16424                                if selection.is_empty() {
16425                                    selection.start.column -= suffix_len as u32;
16426                                }
16427                                selection.end.column -= suffix_len as u32;
16428                            }
16429                            break;
16430                        }
16431                    }
16432                }
16433            }
16434
16435            drop(snapshot);
16436            this.change_selections(Default::default(), window, cx, |s| s.select(selections));
16437
16438            let selections = this.selections.all::<Point>(&this.display_snapshot(cx));
16439            let selections_on_single_row = selections.windows(2).all(|selections| {
16440                selections[0].start.row == selections[1].start.row
16441                    && selections[0].end.row == selections[1].end.row
16442                    && selections[0].start.row == selections[0].end.row
16443            });
16444            let selections_selecting = selections
16445                .iter()
16446                .any(|selection| selection.start != selection.end);
16447            let advance_downwards = action.advance_downwards
16448                && selections_on_single_row
16449                && !selections_selecting
16450                && !matches!(this.mode, EditorMode::SingleLine);
16451
16452            if advance_downwards {
16453                let snapshot = this.buffer.read(cx).snapshot(cx);
16454
16455                this.change_selections(Default::default(), window, cx, |s| {
16456                    s.move_cursors_with(&mut |display_snapshot, display_point, _| {
16457                        let mut point = display_point.to_point(display_snapshot);
16458                        point.row += 1;
16459                        point = snapshot.clip_point(point, Bias::Left);
16460                        let display_point = point.to_display_point(display_snapshot);
16461                        let goal = SelectionGoal::HorizontalPosition(
16462                            display_snapshot
16463                                .x_for_display_point(display_point, text_layout_details)
16464                                .into(),
16465                        );
16466                        (display_point, goal)
16467                    })
16468                });
16469            }
16470        });
16471    }
16472
16473    pub fn select_enclosing_symbol(
16474        &mut self,
16475        _: &SelectEnclosingSymbol,
16476        window: &mut Window,
16477        cx: &mut Context<Self>,
16478    ) {
16479        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
16480
16481        let buffer = self.buffer.read(cx).snapshot(cx);
16482        let old_selections = self
16483            .selections
16484            .all::<MultiBufferOffset>(&self.display_snapshot(cx))
16485            .into_boxed_slice();
16486
16487        fn update_selection(
16488            selection: &Selection<MultiBufferOffset>,
16489            buffer_snap: &MultiBufferSnapshot,
16490        ) -> Option<Selection<MultiBufferOffset>> {
16491            let cursor = selection.head();
16492            let (_buffer_id, symbols) = buffer_snap.symbols_containing(cursor, None)?;
16493            for symbol in symbols.iter().rev() {
16494                let start = symbol.range.start.to_offset(buffer_snap);
16495                let end = symbol.range.end.to_offset(buffer_snap);
16496                let new_range = start..end;
16497                if start < selection.start || end > selection.end {
16498                    return Some(Selection {
16499                        id: selection.id,
16500                        start: new_range.start,
16501                        end: new_range.end,
16502                        goal: SelectionGoal::None,
16503                        reversed: selection.reversed,
16504                    });
16505                }
16506            }
16507            None
16508        }
16509
16510        let mut selected_larger_symbol = false;
16511        let new_selections = old_selections
16512            .iter()
16513            .map(|selection| match update_selection(selection, &buffer) {
16514                Some(new_selection) => {
16515                    if new_selection.range() != selection.range() {
16516                        selected_larger_symbol = true;
16517                    }
16518                    new_selection
16519                }
16520                None => selection.clone(),
16521            })
16522            .collect::<Vec<_>>();
16523
16524        if selected_larger_symbol {
16525            self.change_selections(Default::default(), window, cx, |s| {
16526                s.select(new_selections);
16527            });
16528        }
16529    }
16530
16531    pub fn select_larger_syntax_node(
16532        &mut self,
16533        _: &SelectLargerSyntaxNode,
16534        window: &mut Window,
16535        cx: &mut Context<Self>,
16536    ) {
16537        let Some(visible_row_count) = self.visible_row_count() else {
16538            return;
16539        };
16540        let old_selections: Box<[_]> = self
16541            .selections
16542            .all::<MultiBufferOffset>(&self.display_snapshot(cx))
16543            .into();
16544        if old_selections.is_empty() {
16545            return;
16546        }
16547
16548        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
16549
16550        let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
16551        let buffer = self.buffer.read(cx).snapshot(cx);
16552
16553        let mut selected_larger_node = false;
16554        let mut new_selections = old_selections
16555            .iter()
16556            .map(|selection| {
16557                let old_range = selection.start..selection.end;
16558
16559                if let Some((node, _)) = buffer.syntax_ancestor(old_range.clone()) {
16560                    // manually select word at selection
16561                    if ["string_content", "inline"].contains(&node.kind()) {
16562                        let (word_range, _) = buffer.surrounding_word(old_range.start, None);
16563                        // ignore if word is already selected
16564                        if !word_range.is_empty() && old_range != word_range {
16565                            let (last_word_range, _) = buffer.surrounding_word(old_range.end, None);
16566                            // only select word if start and end point belongs to same word
16567                            if word_range == last_word_range {
16568                                selected_larger_node = true;
16569                                return Selection {
16570                                    id: selection.id,
16571                                    start: word_range.start,
16572                                    end: word_range.end,
16573                                    goal: SelectionGoal::None,
16574                                    reversed: selection.reversed,
16575                                };
16576                            }
16577                        }
16578                    }
16579                }
16580
16581                let mut new_range = old_range.clone();
16582                while let Some((node, range)) = buffer.syntax_ancestor(new_range.clone()) {
16583                    new_range = range;
16584                    if !node.is_named() {
16585                        continue;
16586                    }
16587                    if !display_map.intersects_fold(new_range.start)
16588                        && !display_map.intersects_fold(new_range.end)
16589                    {
16590                        break;
16591                    }
16592                }
16593
16594                selected_larger_node |= new_range != old_range;
16595                Selection {
16596                    id: selection.id,
16597                    start: new_range.start,
16598                    end: new_range.end,
16599                    goal: SelectionGoal::None,
16600                    reversed: selection.reversed,
16601                }
16602            })
16603            .collect::<Vec<_>>();
16604
16605        if !selected_larger_node {
16606            return; // don't put this call in the history
16607        }
16608
16609        // scroll based on transformation done to the last selection created by the user
16610        let (last_old, last_new) = old_selections
16611            .last()
16612            .zip(new_selections.last().cloned())
16613            .expect("old_selections isn't empty");
16614
16615        // revert selection
16616        let is_selection_reversed = {
16617            let should_newest_selection_be_reversed = last_old.start != last_new.start;
16618            new_selections.last_mut().expect("checked above").reversed =
16619                should_newest_selection_be_reversed;
16620            should_newest_selection_be_reversed
16621        };
16622
16623        if selected_larger_node {
16624            self.select_syntax_node_history.disable_clearing = true;
16625            self.change_selections(SelectionEffects::no_scroll(), window, cx, |s| {
16626                s.select(new_selections.clone());
16627            });
16628            self.select_syntax_node_history.disable_clearing = false;
16629        }
16630
16631        let start_row = last_new.start.to_display_point(&display_map).row().0;
16632        let end_row = last_new.end.to_display_point(&display_map).row().0;
16633        let selection_height = end_row - start_row + 1;
16634        let scroll_margin_rows = self.vertical_scroll_margin() as u32;
16635
16636        let fits_on_the_screen = visible_row_count >= selection_height + scroll_margin_rows * 2;
16637        let scroll_behavior = if fits_on_the_screen {
16638            self.request_autoscroll(Autoscroll::fit(), cx);
16639            SelectSyntaxNodeScrollBehavior::FitSelection
16640        } else if is_selection_reversed {
16641            self.scroll_cursor_top(&ScrollCursorTop, window, cx);
16642            SelectSyntaxNodeScrollBehavior::CursorTop
16643        } else {
16644            self.scroll_cursor_bottom(&ScrollCursorBottom, window, cx);
16645            SelectSyntaxNodeScrollBehavior::CursorBottom
16646        };
16647
16648        let old_selections: Box<[Selection<Anchor>]> = old_selections
16649            .iter()
16650            .map(|s| s.map(|offset| buffer.anchor_before(offset)))
16651            .collect();
16652        self.select_syntax_node_history.push((
16653            old_selections,
16654            scroll_behavior,
16655            is_selection_reversed,
16656        ));
16657    }
16658
16659    pub fn select_smaller_syntax_node(
16660        &mut self,
16661        _: &SelectSmallerSyntaxNode,
16662        window: &mut Window,
16663        cx: &mut Context<Self>,
16664    ) {
16665        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
16666
16667        if let Some((mut selections, scroll_behavior, is_selection_reversed)) =
16668            self.select_syntax_node_history.pop()
16669        {
16670            if let Some(selection) = selections.last_mut() {
16671                selection.reversed = is_selection_reversed;
16672            }
16673
16674            let snapshot = self.buffer.read(cx).snapshot(cx);
16675            let selections: Vec<Selection<MultiBufferOffset>> = selections
16676                .iter()
16677                .map(|s| s.map(|anchor| anchor.to_offset(&snapshot)))
16678                .collect();
16679
16680            self.select_syntax_node_history.disable_clearing = true;
16681            self.change_selections(SelectionEffects::no_scroll(), window, cx, |s| {
16682                s.select(selections);
16683            });
16684            self.select_syntax_node_history.disable_clearing = false;
16685
16686            match scroll_behavior {
16687                SelectSyntaxNodeScrollBehavior::CursorTop => {
16688                    self.scroll_cursor_top(&ScrollCursorTop, window, cx);
16689                }
16690                SelectSyntaxNodeScrollBehavior::FitSelection => {
16691                    self.request_autoscroll(Autoscroll::fit(), cx);
16692                }
16693                SelectSyntaxNodeScrollBehavior::CursorBottom => {
16694                    self.scroll_cursor_bottom(&ScrollCursorBottom, window, cx);
16695                }
16696            }
16697        }
16698    }
16699
16700    pub fn unwrap_syntax_node(
16701        &mut self,
16702        _: &UnwrapSyntaxNode,
16703        window: &mut Window,
16704        cx: &mut Context<Self>,
16705    ) {
16706        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
16707
16708        let buffer = self.buffer.read(cx).snapshot(cx);
16709        let selections = self
16710            .selections
16711            .all::<MultiBufferOffset>(&self.display_snapshot(cx))
16712            .into_iter()
16713            // subtracting the offset requires sorting
16714            .sorted_by_key(|i| i.start);
16715
16716        let full_edits = selections
16717            .into_iter()
16718            .filter_map(|selection| {
16719                let child = if selection.is_empty()
16720                    && let Some((_, ancestor_range)) =
16721                        buffer.syntax_ancestor(selection.start..selection.end)
16722                {
16723                    ancestor_range
16724                } else {
16725                    selection.range()
16726                };
16727
16728                let mut parent = child.clone();
16729                while let Some((_, ancestor_range)) = buffer.syntax_ancestor(parent.clone()) {
16730                    parent = ancestor_range;
16731                    if parent.start < child.start || parent.end > child.end {
16732                        break;
16733                    }
16734                }
16735
16736                if parent == child {
16737                    return None;
16738                }
16739                let text = buffer.text_for_range(child).collect::<String>();
16740                Some((selection.id, parent, text))
16741            })
16742            .collect::<Vec<_>>();
16743        if full_edits.is_empty() {
16744            return;
16745        }
16746
16747        self.transact(window, cx, |this, window, cx| {
16748            this.buffer.update(cx, |buffer, cx| {
16749                buffer.edit(
16750                    full_edits
16751                        .iter()
16752                        .map(|(_, p, t)| (p.clone(), t.clone()))
16753                        .collect::<Vec<_>>(),
16754                    None,
16755                    cx,
16756                );
16757            });
16758            this.change_selections(Default::default(), window, cx, |s| {
16759                let mut offset = 0;
16760                let mut selections = vec![];
16761                for (id, parent, text) in full_edits {
16762                    let start = parent.start - offset;
16763                    offset += (parent.end - parent.start) - text.len();
16764                    selections.push(Selection {
16765                        id,
16766                        start,
16767                        end: start + text.len(),
16768                        reversed: false,
16769                        goal: Default::default(),
16770                    });
16771                }
16772                s.select(selections);
16773            });
16774        });
16775    }
16776
16777    pub fn select_next_syntax_node(
16778        &mut self,
16779        _: &SelectNextSyntaxNode,
16780        window: &mut Window,
16781        cx: &mut Context<Self>,
16782    ) {
16783        let old_selections: Box<[_]> = self
16784            .selections
16785            .all::<MultiBufferOffset>(&self.display_snapshot(cx))
16786            .into();
16787        if old_selections.is_empty() {
16788            return;
16789        }
16790
16791        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
16792
16793        let buffer = self.buffer.read(cx).snapshot(cx);
16794        let mut selected_sibling = false;
16795
16796        let new_selections = old_selections
16797            .iter()
16798            .map(|selection| {
16799                let old_range = selection.start..selection.end;
16800
16801                let old_range =
16802                    old_range.start.to_offset(&buffer)..old_range.end.to_offset(&buffer);
16803                let excerpt = buffer.excerpt_containing(old_range.clone());
16804
16805                if let Some(mut excerpt) = excerpt
16806                    && let Some(node) = excerpt
16807                        .buffer()
16808                        .syntax_next_sibling(excerpt.map_range_to_buffer(old_range))
16809                {
16810                    let new_range = excerpt.map_range_from_buffer(
16811                        BufferOffset(node.byte_range().start)..BufferOffset(node.byte_range().end),
16812                    );
16813                    selected_sibling = true;
16814                    Selection {
16815                        id: selection.id,
16816                        start: new_range.start,
16817                        end: new_range.end,
16818                        goal: SelectionGoal::None,
16819                        reversed: selection.reversed,
16820                    }
16821                } else {
16822                    selection.clone()
16823                }
16824            })
16825            .collect::<Vec<_>>();
16826
16827        if selected_sibling {
16828            self.change_selections(
16829                SelectionEffects::scroll(Autoscroll::fit()),
16830                window,
16831                cx,
16832                |s| {
16833                    s.select(new_selections);
16834                },
16835            );
16836        }
16837    }
16838
16839    pub fn select_prev_syntax_node(
16840        &mut self,
16841        _: &SelectPreviousSyntaxNode,
16842        window: &mut Window,
16843        cx: &mut Context<Self>,
16844    ) {
16845        let old_selections: Box<[_]> = self
16846            .selections
16847            .all::<MultiBufferOffset>(&self.display_snapshot(cx))
16848            .into();
16849        if old_selections.is_empty() {
16850            return;
16851        }
16852
16853        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
16854
16855        let buffer = self.buffer.read(cx).snapshot(cx);
16856        let mut selected_sibling = false;
16857
16858        let new_selections = old_selections
16859            .iter()
16860            .map(|selection| {
16861                let old_range = selection.start..selection.end;
16862                let old_range =
16863                    old_range.start.to_offset(&buffer)..old_range.end.to_offset(&buffer);
16864                let excerpt = buffer.excerpt_containing(old_range.clone());
16865
16866                if let Some(mut excerpt) = excerpt
16867                    && let Some(node) = excerpt
16868                        .buffer()
16869                        .syntax_prev_sibling(excerpt.map_range_to_buffer(old_range))
16870                {
16871                    let new_range = excerpt.map_range_from_buffer(
16872                        BufferOffset(node.byte_range().start)..BufferOffset(node.byte_range().end),
16873                    );
16874                    selected_sibling = true;
16875                    Selection {
16876                        id: selection.id,
16877                        start: new_range.start,
16878                        end: new_range.end,
16879                        goal: SelectionGoal::None,
16880                        reversed: selection.reversed,
16881                    }
16882                } else {
16883                    selection.clone()
16884                }
16885            })
16886            .collect::<Vec<_>>();
16887
16888        if selected_sibling {
16889            self.change_selections(
16890                SelectionEffects::scroll(Autoscroll::fit()),
16891                window,
16892                cx,
16893                |s| {
16894                    s.select(new_selections);
16895                },
16896            );
16897        }
16898    }
16899
16900    pub fn move_to_start_of_larger_syntax_node(
16901        &mut self,
16902        _: &MoveToStartOfLargerSyntaxNode,
16903        window: &mut Window,
16904        cx: &mut Context<Self>,
16905    ) {
16906        self.move_cursors_to_syntax_nodes(window, cx, false);
16907    }
16908
16909    pub fn move_to_end_of_larger_syntax_node(
16910        &mut self,
16911        _: &MoveToEndOfLargerSyntaxNode,
16912        window: &mut Window,
16913        cx: &mut Context<Self>,
16914    ) {
16915        self.move_cursors_to_syntax_nodes(window, cx, true);
16916    }
16917
16918    fn find_syntax_node_boundary(
16919        &self,
16920        selection_pos: MultiBufferOffset,
16921        move_to_end: bool,
16922        display_map: &DisplaySnapshot,
16923        buffer: &MultiBufferSnapshot,
16924    ) -> MultiBufferOffset {
16925        let old_range = selection_pos..selection_pos;
16926        let mut new_pos = selection_pos;
16927        let mut search_range = old_range;
16928        while let Some((node, range)) = buffer.syntax_ancestor(search_range.clone()) {
16929            search_range = range.clone();
16930            if !node.is_named()
16931                || display_map.intersects_fold(range.start)
16932                || display_map.intersects_fold(range.end)
16933                // If cursor is already at the end of the syntax node, continue searching
16934                || (move_to_end && range.end == selection_pos)
16935                // If cursor is already at the start of the syntax node, continue searching
16936                || (!move_to_end && range.start == selection_pos)
16937            {
16938                continue;
16939            }
16940
16941            // If we found a string_content node, find the largest parent that is still string_content
16942            // Enables us to skip to the end of strings without taking multiple steps inside the string
16943            let (_, final_range) = if node.kind() == "string_content" {
16944                let mut current_node = node;
16945                let mut current_range = range;
16946                while let Some((parent, parent_range)) =
16947                    buffer.syntax_ancestor(current_range.clone())
16948                {
16949                    if parent.kind() == "string_content" {
16950                        current_node = parent;
16951                        current_range = parent_range;
16952                    } else {
16953                        break;
16954                    }
16955                }
16956
16957                (current_node, current_range)
16958            } else {
16959                (node, range)
16960            };
16961
16962            new_pos = if move_to_end {
16963                final_range.end
16964            } else {
16965                final_range.start
16966            };
16967
16968            break;
16969        }
16970
16971        new_pos
16972    }
16973
16974    fn move_cursors_to_syntax_nodes(
16975        &mut self,
16976        window: &mut Window,
16977        cx: &mut Context<Self>,
16978        move_to_end: bool,
16979    ) -> bool {
16980        let old_selections: Box<[_]> = self
16981            .selections
16982            .all::<MultiBufferOffset>(&self.display_snapshot(cx))
16983            .into();
16984        if old_selections.is_empty() {
16985            return false;
16986        }
16987
16988        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
16989
16990        let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
16991        let buffer = self.buffer.read(cx).snapshot(cx);
16992
16993        let mut any_cursor_moved = false;
16994        let new_selections = old_selections
16995            .iter()
16996            .map(|selection| {
16997                if !selection.is_empty() {
16998                    return selection.clone();
16999                }
17000
17001                let selection_pos = selection.head();
17002                let new_pos = self.find_syntax_node_boundary(
17003                    selection_pos,
17004                    move_to_end,
17005                    &display_map,
17006                    &buffer,
17007                );
17008
17009                any_cursor_moved |= new_pos != selection_pos;
17010
17011                Selection {
17012                    id: selection.id,
17013                    start: new_pos,
17014                    end: new_pos,
17015                    goal: SelectionGoal::None,
17016                    reversed: false,
17017                }
17018            })
17019            .collect::<Vec<_>>();
17020
17021        self.change_selections(Default::default(), window, cx, |s| {
17022            s.select(new_selections);
17023        });
17024        self.request_autoscroll(Autoscroll::newest(), cx);
17025
17026        any_cursor_moved
17027    }
17028
17029    pub fn select_to_start_of_larger_syntax_node(
17030        &mut self,
17031        _: &SelectToStartOfLargerSyntaxNode,
17032        window: &mut Window,
17033        cx: &mut Context<Self>,
17034    ) {
17035        self.select_to_syntax_nodes(window, cx, false);
17036    }
17037
17038    pub fn select_to_end_of_larger_syntax_node(
17039        &mut self,
17040        _: &SelectToEndOfLargerSyntaxNode,
17041        window: &mut Window,
17042        cx: &mut Context<Self>,
17043    ) {
17044        self.select_to_syntax_nodes(window, cx, true);
17045    }
17046
17047    fn select_to_syntax_nodes(
17048        &mut self,
17049        window: &mut Window,
17050        cx: &mut Context<Self>,
17051        move_to_end: bool,
17052    ) {
17053        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
17054
17055        let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
17056        let buffer = self.buffer.read(cx).snapshot(cx);
17057        let old_selections = self.selections.all::<MultiBufferOffset>(&display_map);
17058
17059        let new_selections = old_selections
17060            .iter()
17061            .map(|selection| {
17062                let new_pos = self.find_syntax_node_boundary(
17063                    selection.head(),
17064                    move_to_end,
17065                    &display_map,
17066                    &buffer,
17067                );
17068
17069                let mut new_selection = selection.clone();
17070                new_selection.set_head(new_pos, SelectionGoal::None);
17071                new_selection
17072            })
17073            .collect::<Vec<_>>();
17074
17075        self.change_selections(Default::default(), window, cx, |s| {
17076            s.select(new_selections);
17077        });
17078    }
17079
17080    fn refresh_runnables(&mut self, window: &mut Window, cx: &mut Context<Self>) -> Task<()> {
17081        if !EditorSettings::get_global(cx).gutter.runnables || !self.enable_runnables {
17082            self.clear_tasks();
17083            return Task::ready(());
17084        }
17085        let project = self.project().map(Entity::downgrade);
17086        let task_sources = self.lsp_task_sources(cx);
17087        let multi_buffer = self.buffer.downgrade();
17088        cx.spawn_in(window, async move |editor, cx| {
17089            cx.background_executor().timer(UPDATE_DEBOUNCE).await;
17090            let Some(project) = project.and_then(|p| p.upgrade()) else {
17091                return;
17092            };
17093            let Ok(display_snapshot) = editor.update(cx, |this, cx| {
17094                this.display_map.update(cx, |map, cx| map.snapshot(cx))
17095            }) else {
17096                return;
17097            };
17098
17099            let hide_runnables = project.update(cx, |project, _| project.is_via_collab());
17100            if hide_runnables {
17101                return;
17102            }
17103            let new_rows =
17104                cx.background_spawn({
17105                    let snapshot = display_snapshot.clone();
17106                    async move {
17107                        Self::fetch_runnable_ranges(&snapshot, Anchor::min()..Anchor::max())
17108                    }
17109                })
17110                    .await;
17111            let Ok(lsp_tasks) =
17112                cx.update(|_, cx| crate::lsp_tasks(project.clone(), &task_sources, None, cx))
17113            else {
17114                return;
17115            };
17116            let lsp_tasks = lsp_tasks.await;
17117
17118            let Ok(mut lsp_tasks_by_rows) = cx.update(|_, cx| {
17119                lsp_tasks
17120                    .into_iter()
17121                    .flat_map(|(kind, tasks)| {
17122                        tasks.into_iter().filter_map(move |(location, task)| {
17123                            Some((kind.clone(), location?, task))
17124                        })
17125                    })
17126                    .fold(HashMap::default(), |mut acc, (kind, location, task)| {
17127                        let buffer = location.target.buffer;
17128                        let buffer_snapshot = buffer.read(cx).snapshot();
17129                        let offset = display_snapshot.buffer_snapshot().excerpts().find_map(
17130                            |(excerpt_id, snapshot, _)| {
17131                                if snapshot.remote_id() == buffer_snapshot.remote_id() {
17132                                    display_snapshot
17133                                        .buffer_snapshot()
17134                                        .anchor_in_excerpt(excerpt_id, location.target.range.start)
17135                                } else {
17136                                    None
17137                                }
17138                            },
17139                        );
17140                        if let Some(offset) = offset {
17141                            let task_buffer_range =
17142                                location.target.range.to_point(&buffer_snapshot);
17143                            let context_buffer_range =
17144                                task_buffer_range.to_offset(&buffer_snapshot);
17145                            let context_range = BufferOffset(context_buffer_range.start)
17146                                ..BufferOffset(context_buffer_range.end);
17147
17148                            acc.entry((buffer_snapshot.remote_id(), task_buffer_range.start.row))
17149                                .or_insert_with(|| RunnableTasks {
17150                                    templates: Vec::new(),
17151                                    offset,
17152                                    column: task_buffer_range.start.column,
17153                                    extra_variables: HashMap::default(),
17154                                    context_range,
17155                                })
17156                                .templates
17157                                .push((kind, task.original_task().clone()));
17158                        }
17159
17160                        acc
17161                    })
17162            }) else {
17163                return;
17164            };
17165
17166            let Ok(prefer_lsp) = multi_buffer.update(cx, |buffer, cx| {
17167                buffer.language_settings(cx).tasks.prefer_lsp
17168            }) else {
17169                return;
17170            };
17171
17172            let rows = Self::runnable_rows(
17173                project,
17174                display_snapshot,
17175                prefer_lsp && !lsp_tasks_by_rows.is_empty(),
17176                new_rows,
17177                cx.clone(),
17178            )
17179            .await;
17180            editor
17181                .update(cx, |editor, _| {
17182                    editor.clear_tasks();
17183                    for (key, mut value) in rows {
17184                        if let Some(lsp_tasks) = lsp_tasks_by_rows.remove(&key) {
17185                            value.templates.extend(lsp_tasks.templates);
17186                        }
17187
17188                        editor.insert_tasks(key, value);
17189                    }
17190                    for (key, value) in lsp_tasks_by_rows {
17191                        editor.insert_tasks(key, value);
17192                    }
17193                })
17194                .ok();
17195        })
17196    }
17197    fn fetch_runnable_ranges(
17198        snapshot: &DisplaySnapshot,
17199        range: Range<Anchor>,
17200    ) -> Vec<(Range<MultiBufferOffset>, language::RunnableRange)> {
17201        snapshot.buffer_snapshot().runnable_ranges(range).collect()
17202    }
17203
17204    fn runnable_rows(
17205        project: Entity<Project>,
17206        snapshot: DisplaySnapshot,
17207        prefer_lsp: bool,
17208        runnable_ranges: Vec<(Range<MultiBufferOffset>, language::RunnableRange)>,
17209        cx: AsyncWindowContext,
17210    ) -> Task<Vec<((BufferId, BufferRow), RunnableTasks)>> {
17211        cx.spawn(async move |cx| {
17212            let mut runnable_rows = Vec::with_capacity(runnable_ranges.len());
17213            for (run_range, mut runnable) in runnable_ranges {
17214                let Some(tasks) = cx
17215                    .update(|_, cx| Self::templates_with_tags(&project, &mut runnable.runnable, cx))
17216                    .ok()
17217                else {
17218                    continue;
17219                };
17220                let mut tasks = tasks.await;
17221
17222                if prefer_lsp {
17223                    tasks.retain(|(task_kind, _)| {
17224                        !matches!(task_kind, TaskSourceKind::Language { .. })
17225                    });
17226                }
17227                if tasks.is_empty() {
17228                    continue;
17229                }
17230
17231                let point = run_range.start.to_point(&snapshot.buffer_snapshot());
17232                let Some(row) = snapshot
17233                    .buffer_snapshot()
17234                    .buffer_line_for_row(MultiBufferRow(point.row))
17235                    .map(|(_, range)| range.start.row)
17236                else {
17237                    continue;
17238                };
17239
17240                let context_range =
17241                    BufferOffset(runnable.full_range.start)..BufferOffset(runnable.full_range.end);
17242                runnable_rows.push((
17243                    (runnable.buffer_id, row),
17244                    RunnableTasks {
17245                        templates: tasks,
17246                        offset: snapshot.buffer_snapshot().anchor_before(run_range.start),
17247                        context_range,
17248                        column: point.column,
17249                        extra_variables: runnable.extra_captures,
17250                    },
17251                ));
17252            }
17253            runnable_rows
17254        })
17255    }
17256
17257    fn templates_with_tags(
17258        project: &Entity<Project>,
17259        runnable: &mut Runnable,
17260        cx: &mut App,
17261    ) -> Task<Vec<(TaskSourceKind, TaskTemplate)>> {
17262        let (inventory, worktree_id, file) = project.read_with(cx, |project, cx| {
17263            let (worktree_id, file) = project
17264                .buffer_for_id(runnable.buffer, cx)
17265                .and_then(|buffer| buffer.read(cx).file())
17266                .map(|file| (file.worktree_id(cx), file.clone()))
17267                .unzip();
17268
17269            (
17270                project.task_store().read(cx).task_inventory().cloned(),
17271                worktree_id,
17272                file,
17273            )
17274        });
17275
17276        let tags = mem::take(&mut runnable.tags);
17277        let language = runnable.language.clone();
17278        cx.spawn(async move |cx| {
17279            let mut templates_with_tags = Vec::new();
17280            if let Some(inventory) = inventory {
17281                for RunnableTag(tag) in tags {
17282                    let new_tasks = inventory.update(cx, |inventory, cx| {
17283                        inventory.list_tasks(file.clone(), Some(language.clone()), worktree_id, cx)
17284                    });
17285                    templates_with_tags.extend(new_tasks.await.into_iter().filter(
17286                        move |(_, template)| {
17287                            template.tags.iter().any(|source_tag| source_tag == &tag)
17288                        },
17289                    ));
17290                }
17291            }
17292            templates_with_tags.sort_by_key(|(kind, _)| kind.to_owned());
17293
17294            if let Some((leading_tag_source, _)) = templates_with_tags.first() {
17295                // Strongest source wins; if we have worktree tag binding, prefer that to
17296                // global and language bindings;
17297                // if we have a global binding, prefer that to language binding.
17298                let first_mismatch = templates_with_tags
17299                    .iter()
17300                    .position(|(tag_source, _)| tag_source != leading_tag_source);
17301                if let Some(index) = first_mismatch {
17302                    templates_with_tags.truncate(index);
17303                }
17304            }
17305
17306            templates_with_tags
17307        })
17308    }
17309
17310    pub fn move_to_enclosing_bracket(
17311        &mut self,
17312        _: &MoveToEnclosingBracket,
17313        window: &mut Window,
17314        cx: &mut Context<Self>,
17315    ) {
17316        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
17317        self.change_selections(Default::default(), window, cx, |s| {
17318            s.move_offsets_with(&mut |snapshot, selection| {
17319                let Some(enclosing_bracket_ranges) =
17320                    snapshot.enclosing_bracket_ranges(selection.start..selection.end)
17321                else {
17322                    return;
17323                };
17324
17325                let mut best_length = usize::MAX;
17326                let mut best_inside = false;
17327                let mut best_in_bracket_range = false;
17328                let mut best_destination = None;
17329                for (open, close) in enclosing_bracket_ranges {
17330                    let close = close.to_inclusive();
17331                    let length = *close.end() - open.start;
17332                    let inside = selection.start >= open.end && selection.end <= *close.start();
17333                    let in_bracket_range = open.to_inclusive().contains(&selection.head())
17334                        || close.contains(&selection.head());
17335
17336                    // If best is next to a bracket and current isn't, skip
17337                    if !in_bracket_range && best_in_bracket_range {
17338                        continue;
17339                    }
17340
17341                    // Prefer smaller lengths unless best is inside and current isn't
17342                    if length > best_length && (best_inside || !inside) {
17343                        continue;
17344                    }
17345
17346                    best_length = length;
17347                    best_inside = inside;
17348                    best_in_bracket_range = in_bracket_range;
17349                    best_destination = Some(
17350                        if close.contains(&selection.start) && close.contains(&selection.end) {
17351                            if inside { open.end } else { open.start }
17352                        } else if inside {
17353                            *close.start()
17354                        } else {
17355                            *close.end()
17356                        },
17357                    );
17358                }
17359
17360                if let Some(destination) = best_destination {
17361                    selection.collapse_to(destination, SelectionGoal::None);
17362                }
17363            })
17364        });
17365    }
17366
17367    pub fn undo_selection(
17368        &mut self,
17369        _: &UndoSelection,
17370        window: &mut Window,
17371        cx: &mut Context<Self>,
17372    ) {
17373        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
17374        if let Some(entry) = self.selection_history.undo_stack.pop_back() {
17375            self.selection_history.mode = SelectionHistoryMode::Undoing;
17376            self.with_selection_effects_deferred(window, cx, |this, window, cx| {
17377                this.end_selection(window, cx);
17378                this.change_selections(
17379                    SelectionEffects::scroll(Autoscroll::newest()),
17380                    window,
17381                    cx,
17382                    |s| s.select_anchors(entry.selections.to_vec()),
17383                );
17384            });
17385            self.selection_history.mode = SelectionHistoryMode::Normal;
17386
17387            self.select_next_state = entry.select_next_state;
17388            self.select_prev_state = entry.select_prev_state;
17389            self.add_selections_state = entry.add_selections_state;
17390        }
17391    }
17392
17393    pub fn redo_selection(
17394        &mut self,
17395        _: &RedoSelection,
17396        window: &mut Window,
17397        cx: &mut Context<Self>,
17398    ) {
17399        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
17400        if let Some(entry) = self.selection_history.redo_stack.pop_back() {
17401            self.selection_history.mode = SelectionHistoryMode::Redoing;
17402            self.with_selection_effects_deferred(window, cx, |this, window, cx| {
17403                this.end_selection(window, cx);
17404                this.change_selections(
17405                    SelectionEffects::scroll(Autoscroll::newest()),
17406                    window,
17407                    cx,
17408                    |s| s.select_anchors(entry.selections.to_vec()),
17409                );
17410            });
17411            self.selection_history.mode = SelectionHistoryMode::Normal;
17412
17413            self.select_next_state = entry.select_next_state;
17414            self.select_prev_state = entry.select_prev_state;
17415            self.add_selections_state = entry.add_selections_state;
17416        }
17417    }
17418
17419    pub fn expand_excerpts(
17420        &mut self,
17421        action: &ExpandExcerpts,
17422        _: &mut Window,
17423        cx: &mut Context<Self>,
17424    ) {
17425        self.expand_excerpts_for_direction(action.lines, ExpandExcerptDirection::UpAndDown, cx)
17426    }
17427
17428    pub fn expand_excerpts_down(
17429        &mut self,
17430        action: &ExpandExcerptsDown,
17431        _: &mut Window,
17432        cx: &mut Context<Self>,
17433    ) {
17434        self.expand_excerpts_for_direction(action.lines, ExpandExcerptDirection::Down, cx)
17435    }
17436
17437    pub fn expand_excerpts_up(
17438        &mut self,
17439        action: &ExpandExcerptsUp,
17440        _: &mut Window,
17441        cx: &mut Context<Self>,
17442    ) {
17443        self.expand_excerpts_for_direction(action.lines, ExpandExcerptDirection::Up, cx)
17444    }
17445
17446    pub fn expand_excerpts_for_direction(
17447        &mut self,
17448        lines: u32,
17449        direction: ExpandExcerptDirection,
17450        cx: &mut Context<Self>,
17451    ) {
17452        let selections = self.selections.disjoint_anchors_arc();
17453
17454        let lines = if lines == 0 {
17455            EditorSettings::get_global(cx).expand_excerpt_lines
17456        } else {
17457            lines
17458        };
17459
17460        let snapshot = self.buffer.read(cx).snapshot(cx);
17461        let excerpt_ids = selections
17462            .iter()
17463            .flat_map(|selection| snapshot.excerpt_ids_for_range(selection.range()))
17464            .unique()
17465            .sorted()
17466            .collect::<Vec<_>>();
17467
17468        if self.delegate_expand_excerpts {
17469            cx.emit(EditorEvent::ExpandExcerptsRequested {
17470                excerpt_ids,
17471                lines,
17472                direction,
17473            });
17474            return;
17475        }
17476
17477        self.buffer.update(cx, |buffer, cx| {
17478            buffer.expand_excerpts(excerpt_ids, lines, direction, cx)
17479        })
17480    }
17481
17482    pub fn expand_excerpt(
17483        &mut self,
17484        excerpt: ExcerptId,
17485        direction: ExpandExcerptDirection,
17486        window: &mut Window,
17487        cx: &mut Context<Self>,
17488    ) {
17489        let lines_to_expand = EditorSettings::get_global(cx).expand_excerpt_lines;
17490
17491        if self.delegate_expand_excerpts {
17492            cx.emit(EditorEvent::ExpandExcerptsRequested {
17493                excerpt_ids: vec![excerpt],
17494                lines: lines_to_expand,
17495                direction,
17496            });
17497            return;
17498        }
17499
17500        let current_scroll_position = self.scroll_position(cx);
17501        let mut scroll = None;
17502
17503        if direction == ExpandExcerptDirection::Down {
17504            let multi_buffer = self.buffer.read(cx);
17505            let snapshot = multi_buffer.snapshot(cx);
17506            if let Some(buffer_id) = snapshot.buffer_id_for_excerpt(excerpt)
17507                && let Some(buffer) = multi_buffer.buffer(buffer_id)
17508                && let Some(excerpt_range) = snapshot.context_range_for_excerpt(excerpt)
17509            {
17510                let buffer_snapshot = buffer.read(cx).snapshot();
17511                let excerpt_end_row = Point::from_anchor(&excerpt_range.end, &buffer_snapshot).row;
17512                let last_row = buffer_snapshot.max_point().row;
17513                let lines_below = last_row.saturating_sub(excerpt_end_row);
17514                if lines_below >= lines_to_expand {
17515                    scroll = Some(
17516                        current_scroll_position
17517                            + gpui::Point::new(0.0, lines_to_expand as ScrollOffset),
17518                    );
17519                }
17520            }
17521        }
17522        if direction == ExpandExcerptDirection::Up
17523            && self
17524                .buffer
17525                .read(cx)
17526                .snapshot(cx)
17527                .excerpt_before(excerpt)
17528                .is_none()
17529        {
17530            scroll = Some(current_scroll_position);
17531        }
17532
17533        self.buffer.update(cx, |buffer, cx| {
17534            buffer.expand_excerpts([excerpt], lines_to_expand, direction, cx)
17535        });
17536
17537        if let Some(new_scroll_position) = scroll {
17538            self.set_scroll_position(new_scroll_position, window, cx);
17539        }
17540    }
17541
17542    pub fn go_to_singleton_buffer_point(
17543        &mut self,
17544        point: Point,
17545        window: &mut Window,
17546        cx: &mut Context<Self>,
17547    ) {
17548        self.go_to_singleton_buffer_range(point..point, window, cx);
17549    }
17550
17551    pub fn go_to_singleton_buffer_range(
17552        &mut self,
17553        range: Range<Point>,
17554        window: &mut Window,
17555        cx: &mut Context<Self>,
17556    ) {
17557        let multibuffer = self.buffer().read(cx);
17558        let Some(buffer) = multibuffer.as_singleton() else {
17559            return;
17560        };
17561        let Some(start) = multibuffer.buffer_point_to_anchor(&buffer, range.start, cx) else {
17562            return;
17563        };
17564        let Some(end) = multibuffer.buffer_point_to_anchor(&buffer, range.end, cx) else {
17565            return;
17566        };
17567        self.change_selections(
17568            SelectionEffects::default().nav_history(true),
17569            window,
17570            cx,
17571            |s| s.select_anchor_ranges([start..end]),
17572        );
17573    }
17574
17575    pub fn go_to_diagnostic(
17576        &mut self,
17577        action: &GoToDiagnostic,
17578        window: &mut Window,
17579        cx: &mut Context<Self>,
17580    ) {
17581        if !self.diagnostics_enabled() {
17582            return;
17583        }
17584        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
17585        self.go_to_diagnostic_impl(Direction::Next, action.severity, window, cx)
17586    }
17587
17588    pub fn go_to_prev_diagnostic(
17589        &mut self,
17590        action: &GoToPreviousDiagnostic,
17591        window: &mut Window,
17592        cx: &mut Context<Self>,
17593    ) {
17594        if !self.diagnostics_enabled() {
17595            return;
17596        }
17597        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
17598        self.go_to_diagnostic_impl(Direction::Prev, action.severity, window, cx)
17599    }
17600
17601    pub fn go_to_diagnostic_impl(
17602        &mut self,
17603        direction: Direction,
17604        severity: GoToDiagnosticSeverityFilter,
17605        window: &mut Window,
17606        cx: &mut Context<Self>,
17607    ) {
17608        let buffer = self.buffer.read(cx).snapshot(cx);
17609        let selection = self
17610            .selections
17611            .newest::<MultiBufferOffset>(&self.display_snapshot(cx));
17612
17613        let mut active_group_id = None;
17614        if let ActiveDiagnostic::Group(active_group) = &self.active_diagnostics
17615            && active_group.active_range.start.to_offset(&buffer) == selection.start
17616        {
17617            active_group_id = Some(active_group.group_id);
17618        }
17619
17620        fn filtered<'a>(
17621            severity: GoToDiagnosticSeverityFilter,
17622            diagnostics: impl Iterator<Item = DiagnosticEntryRef<'a, MultiBufferOffset>>,
17623        ) -> impl Iterator<Item = DiagnosticEntryRef<'a, MultiBufferOffset>> {
17624            diagnostics
17625                .filter(move |entry| severity.matches(entry.diagnostic.severity))
17626                .filter(|entry| entry.range.start != entry.range.end)
17627                .filter(|entry| !entry.diagnostic.is_unnecessary)
17628        }
17629
17630        let before = filtered(
17631            severity,
17632            buffer
17633                .diagnostics_in_range(MultiBufferOffset(0)..selection.start)
17634                .filter(|entry| entry.range.start <= selection.start),
17635        );
17636        let after = filtered(
17637            severity,
17638            buffer
17639                .diagnostics_in_range(selection.start..buffer.len())
17640                .filter(|entry| entry.range.start >= selection.start),
17641        );
17642
17643        let mut found: Option<DiagnosticEntryRef<MultiBufferOffset>> = None;
17644        if direction == Direction::Prev {
17645            'outer: for prev_diagnostics in [before.collect::<Vec<_>>(), after.collect::<Vec<_>>()]
17646            {
17647                for diagnostic in prev_diagnostics.into_iter().rev() {
17648                    if diagnostic.range.start != selection.start
17649                        || active_group_id
17650                            .is_some_and(|active| diagnostic.diagnostic.group_id < active)
17651                    {
17652                        found = Some(diagnostic);
17653                        break 'outer;
17654                    }
17655                }
17656            }
17657        } else {
17658            for diagnostic in after.chain(before) {
17659                if diagnostic.range.start != selection.start
17660                    || active_group_id.is_some_and(|active| diagnostic.diagnostic.group_id > active)
17661                {
17662                    found = Some(diagnostic);
17663                    break;
17664                }
17665            }
17666        }
17667        let Some(next_diagnostic) = found else {
17668            return;
17669        };
17670
17671        let next_diagnostic_start = buffer.anchor_after(next_diagnostic.range.start);
17672        let Some(buffer_id) = buffer.buffer_id_for_anchor(next_diagnostic_start) else {
17673            return;
17674        };
17675        let snapshot = self.snapshot(window, cx);
17676        if snapshot.intersects_fold(next_diagnostic.range.start) {
17677            self.unfold_ranges(
17678                std::slice::from_ref(&next_diagnostic.range),
17679                true,
17680                false,
17681                cx,
17682            );
17683        }
17684        self.change_selections(Default::default(), window, cx, |s| {
17685            s.select_ranges(vec![
17686                next_diagnostic.range.start..next_diagnostic.range.start,
17687            ])
17688        });
17689        self.activate_diagnostics(buffer_id, next_diagnostic, window, cx);
17690        self.refresh_edit_prediction(false, true, window, cx);
17691    }
17692
17693    pub fn go_to_next_hunk(&mut self, _: &GoToHunk, window: &mut Window, cx: &mut Context<Self>) {
17694        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
17695        let snapshot = self.snapshot(window, cx);
17696        let selection = self.selections.newest::<Point>(&self.display_snapshot(cx));
17697        self.go_to_hunk_before_or_after_position(
17698            &snapshot,
17699            selection.head(),
17700            Direction::Next,
17701            window,
17702            cx,
17703        );
17704    }
17705
17706    pub fn go_to_hunk_before_or_after_position(
17707        &mut self,
17708        snapshot: &EditorSnapshot,
17709        position: Point,
17710        direction: Direction,
17711        window: &mut Window,
17712        cx: &mut Context<Editor>,
17713    ) {
17714        let row = if direction == Direction::Next {
17715            self.hunk_after_position(snapshot, position)
17716                .map(|hunk| hunk.row_range.start)
17717        } else {
17718            self.hunk_before_position(snapshot, position)
17719        };
17720
17721        if let Some(row) = row {
17722            let destination = Point::new(row.0, 0);
17723            let autoscroll = Autoscroll::center();
17724
17725            self.unfold_ranges(&[destination..destination], false, false, cx);
17726            self.change_selections(SelectionEffects::scroll(autoscroll), window, cx, |s| {
17727                s.select_ranges([destination..destination]);
17728            });
17729        }
17730    }
17731
17732    fn hunk_after_position(
17733        &mut self,
17734        snapshot: &EditorSnapshot,
17735        position: Point,
17736    ) -> Option<MultiBufferDiffHunk> {
17737        snapshot
17738            .buffer_snapshot()
17739            .diff_hunks_in_range(position..snapshot.buffer_snapshot().max_point())
17740            .find(|hunk| hunk.row_range.start.0 > position.row)
17741            .or_else(|| {
17742                snapshot
17743                    .buffer_snapshot()
17744                    .diff_hunks_in_range(Point::zero()..position)
17745                    .find(|hunk| hunk.row_range.end.0 < position.row)
17746            })
17747    }
17748
17749    fn go_to_prev_hunk(
17750        &mut self,
17751        _: &GoToPreviousHunk,
17752        window: &mut Window,
17753        cx: &mut Context<Self>,
17754    ) {
17755        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
17756        let snapshot = self.snapshot(window, cx);
17757        let selection = self.selections.newest::<Point>(&snapshot.display_snapshot);
17758        self.go_to_hunk_before_or_after_position(
17759            &snapshot,
17760            selection.head(),
17761            Direction::Prev,
17762            window,
17763            cx,
17764        );
17765    }
17766
17767    fn hunk_before_position(
17768        &mut self,
17769        snapshot: &EditorSnapshot,
17770        position: Point,
17771    ) -> Option<MultiBufferRow> {
17772        snapshot
17773            .buffer_snapshot()
17774            .diff_hunk_before(position)
17775            .or_else(|| snapshot.buffer_snapshot().diff_hunk_before(Point::MAX))
17776    }
17777
17778    fn go_to_next_change(
17779        &mut self,
17780        _: &GoToNextChange,
17781        window: &mut Window,
17782        cx: &mut Context<Self>,
17783    ) {
17784        if let Some(selections) = self
17785            .change_list
17786            .next_change(1, Direction::Next)
17787            .map(|s| s.to_vec())
17788        {
17789            self.change_selections(Default::default(), window, cx, |s| {
17790                let map = s.display_snapshot();
17791                s.select_display_ranges(selections.iter().map(|a| {
17792                    let point = a.to_display_point(&map);
17793                    point..point
17794                }))
17795            })
17796        }
17797    }
17798
17799    fn go_to_previous_change(
17800        &mut self,
17801        _: &GoToPreviousChange,
17802        window: &mut Window,
17803        cx: &mut Context<Self>,
17804    ) {
17805        if let Some(selections) = self
17806            .change_list
17807            .next_change(1, Direction::Prev)
17808            .map(|s| s.to_vec())
17809        {
17810            self.change_selections(Default::default(), window, cx, |s| {
17811                let map = s.display_snapshot();
17812                s.select_display_ranges(selections.iter().map(|a| {
17813                    let point = a.to_display_point(&map);
17814                    point..point
17815                }))
17816            })
17817        }
17818    }
17819
17820    pub fn go_to_next_document_highlight(
17821        &mut self,
17822        _: &GoToNextDocumentHighlight,
17823        window: &mut Window,
17824        cx: &mut Context<Self>,
17825    ) {
17826        self.go_to_document_highlight_before_or_after_position(Direction::Next, window, cx);
17827    }
17828
17829    pub fn go_to_prev_document_highlight(
17830        &mut self,
17831        _: &GoToPreviousDocumentHighlight,
17832        window: &mut Window,
17833        cx: &mut Context<Self>,
17834    ) {
17835        self.go_to_document_highlight_before_or_after_position(Direction::Prev, window, cx);
17836    }
17837
17838    pub fn go_to_document_highlight_before_or_after_position(
17839        &mut self,
17840        direction: Direction,
17841        window: &mut Window,
17842        cx: &mut Context<Editor>,
17843    ) {
17844        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
17845        let snapshot = self.snapshot(window, cx);
17846        let buffer = &snapshot.buffer_snapshot();
17847        let position = self
17848            .selections
17849            .newest::<Point>(&snapshot.display_snapshot)
17850            .head();
17851        let anchor_position = buffer.anchor_after(position);
17852
17853        // Get all document highlights (both read and write)
17854        let mut all_highlights = Vec::new();
17855
17856        if let Some((_, read_highlights)) = self
17857            .background_highlights
17858            .get(&HighlightKey::DocumentHighlightRead)
17859        {
17860            all_highlights.extend(read_highlights.iter());
17861        }
17862
17863        if let Some((_, write_highlights)) = self
17864            .background_highlights
17865            .get(&HighlightKey::DocumentHighlightWrite)
17866        {
17867            all_highlights.extend(write_highlights.iter());
17868        }
17869
17870        if all_highlights.is_empty() {
17871            return;
17872        }
17873
17874        // Sort highlights by position
17875        all_highlights.sort_by(|a, b| a.start.cmp(&b.start, buffer));
17876
17877        let target_highlight = match direction {
17878            Direction::Next => {
17879                // Find the first highlight after the current position
17880                all_highlights
17881                    .iter()
17882                    .find(|highlight| highlight.start.cmp(&anchor_position, buffer).is_gt())
17883            }
17884            Direction::Prev => {
17885                // Find the last highlight before the current position
17886                all_highlights
17887                    .iter()
17888                    .rev()
17889                    .find(|highlight| highlight.end.cmp(&anchor_position, buffer).is_lt())
17890            }
17891        };
17892
17893        if let Some(highlight) = target_highlight {
17894            let destination = highlight.start.to_point(buffer);
17895            let autoscroll = Autoscroll::center();
17896
17897            self.unfold_ranges(&[destination..destination], false, false, cx);
17898            self.change_selections(SelectionEffects::scroll(autoscroll), window, cx, |s| {
17899                s.select_ranges([destination..destination]);
17900            });
17901        }
17902    }
17903
17904    fn go_to_line<T: 'static>(
17905        &mut self,
17906        position: Anchor,
17907        highlight_color: Option<Hsla>,
17908        window: &mut Window,
17909        cx: &mut Context<Self>,
17910    ) {
17911        let snapshot = self.snapshot(window, cx).display_snapshot;
17912        let position = position.to_point(&snapshot.buffer_snapshot());
17913        let start = snapshot
17914            .buffer_snapshot()
17915            .clip_point(Point::new(position.row, 0), Bias::Left);
17916        let end = start + Point::new(1, 0);
17917        let start = snapshot.buffer_snapshot().anchor_before(start);
17918        let end = snapshot.buffer_snapshot().anchor_before(end);
17919
17920        self.highlight_rows::<T>(
17921            start..end,
17922            highlight_color
17923                .unwrap_or_else(|| cx.theme().colors().editor_highlighted_line_background),
17924            Default::default(),
17925            cx,
17926        );
17927
17928        if self.buffer.read(cx).is_singleton() {
17929            self.request_autoscroll(Autoscroll::center().for_anchor(start), cx);
17930        }
17931    }
17932
17933    pub fn go_to_definition(
17934        &mut self,
17935        _: &GoToDefinition,
17936        window: &mut Window,
17937        cx: &mut Context<Self>,
17938    ) -> Task<Result<Navigated>> {
17939        let definition =
17940            self.go_to_definition_of_kind(GotoDefinitionKind::Symbol, false, window, cx);
17941        let fallback_strategy = EditorSettings::get_global(cx).go_to_definition_fallback;
17942        cx.spawn_in(window, async move |editor, cx| {
17943            if definition.await? == Navigated::Yes {
17944                return Ok(Navigated::Yes);
17945            }
17946            match fallback_strategy {
17947                GoToDefinitionFallback::None => Ok(Navigated::No),
17948                GoToDefinitionFallback::FindAllReferences => {
17949                    match editor.update_in(cx, |editor, window, cx| {
17950                        editor.find_all_references(&FindAllReferences::default(), window, cx)
17951                    })? {
17952                        Some(references) => references.await,
17953                        None => Ok(Navigated::No),
17954                    }
17955                }
17956            }
17957        })
17958    }
17959
17960    pub fn go_to_declaration(
17961        &mut self,
17962        _: &GoToDeclaration,
17963        window: &mut Window,
17964        cx: &mut Context<Self>,
17965    ) -> Task<Result<Navigated>> {
17966        self.go_to_definition_of_kind(GotoDefinitionKind::Declaration, false, window, cx)
17967    }
17968
17969    pub fn go_to_declaration_split(
17970        &mut self,
17971        _: &GoToDeclaration,
17972        window: &mut Window,
17973        cx: &mut Context<Self>,
17974    ) -> Task<Result<Navigated>> {
17975        self.go_to_definition_of_kind(GotoDefinitionKind::Declaration, true, window, cx)
17976    }
17977
17978    pub fn go_to_implementation(
17979        &mut self,
17980        _: &GoToImplementation,
17981        window: &mut Window,
17982        cx: &mut Context<Self>,
17983    ) -> Task<Result<Navigated>> {
17984        self.go_to_definition_of_kind(GotoDefinitionKind::Implementation, false, window, cx)
17985    }
17986
17987    pub fn go_to_implementation_split(
17988        &mut self,
17989        _: &GoToImplementationSplit,
17990        window: &mut Window,
17991        cx: &mut Context<Self>,
17992    ) -> Task<Result<Navigated>> {
17993        self.go_to_definition_of_kind(GotoDefinitionKind::Implementation, true, window, cx)
17994    }
17995
17996    pub fn go_to_type_definition(
17997        &mut self,
17998        _: &GoToTypeDefinition,
17999        window: &mut Window,
18000        cx: &mut Context<Self>,
18001    ) -> Task<Result<Navigated>> {
18002        self.go_to_definition_of_kind(GotoDefinitionKind::Type, false, window, cx)
18003    }
18004
18005    pub fn go_to_definition_split(
18006        &mut self,
18007        _: &GoToDefinitionSplit,
18008        window: &mut Window,
18009        cx: &mut Context<Self>,
18010    ) -> Task<Result<Navigated>> {
18011        self.go_to_definition_of_kind(GotoDefinitionKind::Symbol, true, window, cx)
18012    }
18013
18014    pub fn go_to_type_definition_split(
18015        &mut self,
18016        _: &GoToTypeDefinitionSplit,
18017        window: &mut Window,
18018        cx: &mut Context<Self>,
18019    ) -> Task<Result<Navigated>> {
18020        self.go_to_definition_of_kind(GotoDefinitionKind::Type, true, window, cx)
18021    }
18022
18023    fn go_to_definition_of_kind(
18024        &mut self,
18025        kind: GotoDefinitionKind,
18026        split: bool,
18027        window: &mut Window,
18028        cx: &mut Context<Self>,
18029    ) -> Task<Result<Navigated>> {
18030        let Some(provider) = self.semantics_provider.clone() else {
18031            return Task::ready(Ok(Navigated::No));
18032        };
18033        let head = self
18034            .selections
18035            .newest::<MultiBufferOffset>(&self.display_snapshot(cx))
18036            .head();
18037        let buffer = self.buffer.read(cx);
18038        let Some((buffer, head)) = buffer.text_anchor_for_position(head, cx) else {
18039            return Task::ready(Ok(Navigated::No));
18040        };
18041        let Some(definitions) = provider.definitions(&buffer, head, kind, cx) else {
18042            return Task::ready(Ok(Navigated::No));
18043        };
18044
18045        let nav_entry = self.navigation_entry(self.selections.newest_anchor().head(), cx);
18046
18047        cx.spawn_in(window, async move |editor, cx| {
18048            let Some(definitions) = definitions.await? else {
18049                return Ok(Navigated::No);
18050            };
18051            let navigated = editor
18052                .update_in(cx, |editor, window, cx| {
18053                    editor.navigate_to_hover_links(
18054                        Some(kind),
18055                        definitions
18056                            .into_iter()
18057                            .filter(|location| {
18058                                hover_links::exclude_link_to_position(&buffer, &head, location, cx)
18059                            })
18060                            .map(HoverLink::Text)
18061                            .collect::<Vec<_>>(),
18062                        nav_entry,
18063                        split,
18064                        window,
18065                        cx,
18066                    )
18067                })?
18068                .await?;
18069            anyhow::Ok(navigated)
18070        })
18071    }
18072
18073    pub fn open_url(&mut self, _: &OpenUrl, window: &mut Window, cx: &mut Context<Self>) {
18074        let selection = self.selections.newest_anchor();
18075        let head = selection.head();
18076        let tail = selection.tail();
18077
18078        let Some((buffer, start_position)) =
18079            self.buffer.read(cx).text_anchor_for_position(head, cx)
18080        else {
18081            return;
18082        };
18083
18084        let end_position = if head != tail {
18085            let Some((_, pos)) = self.buffer.read(cx).text_anchor_for_position(tail, cx) else {
18086                return;
18087            };
18088            Some(pos)
18089        } else {
18090            None
18091        };
18092
18093        let url_finder = cx.spawn_in(window, async move |_editor, cx| {
18094            let url = if let Some(end_pos) = end_position {
18095                find_url_from_range(&buffer, start_position..end_pos, cx.clone())
18096            } else {
18097                find_url(&buffer, start_position, cx.clone()).map(|(_, url)| url)
18098            };
18099
18100            if let Some(url) = url {
18101                cx.update(|window, cx| {
18102                    if parse_zed_link(&url, cx).is_some() {
18103                        window.dispatch_action(Box::new(zed_actions::OpenZedUrl { url }), cx);
18104                    } else {
18105                        cx.open_url(&url);
18106                    }
18107                })?;
18108            }
18109
18110            anyhow::Ok(())
18111        });
18112
18113        url_finder.detach();
18114    }
18115
18116    pub fn open_selected_filename(
18117        &mut self,
18118        _: &OpenSelectedFilename,
18119        window: &mut Window,
18120        cx: &mut Context<Self>,
18121    ) {
18122        let Some(workspace) = self.workspace() else {
18123            return;
18124        };
18125
18126        let position = self.selections.newest_anchor().head();
18127
18128        let Some((buffer, buffer_position)) =
18129            self.buffer.read(cx).text_anchor_for_position(position, cx)
18130        else {
18131            return;
18132        };
18133
18134        let project = self.project.clone();
18135
18136        cx.spawn_in(window, async move |_, cx| {
18137            let result = find_file(&buffer, project, buffer_position, cx).await;
18138
18139            if let Some((_, path)) = result {
18140                workspace
18141                    .update_in(cx, |workspace, window, cx| {
18142                        workspace.open_resolved_path(path, window, cx)
18143                    })?
18144                    .await?;
18145            }
18146            anyhow::Ok(())
18147        })
18148        .detach();
18149    }
18150
18151    pub(crate) fn navigate_to_hover_links(
18152        &mut self,
18153        kind: Option<GotoDefinitionKind>,
18154        definitions: Vec<HoverLink>,
18155        origin: Option<NavigationEntry>,
18156        split: bool,
18157        window: &mut Window,
18158        cx: &mut Context<Editor>,
18159    ) -> Task<Result<Navigated>> {
18160        // Separate out url and file links, we can only handle one of them at most or an arbitrary number of locations
18161        let mut first_url_or_file = None;
18162        let definitions: Vec<_> = definitions
18163            .into_iter()
18164            .filter_map(|def| match def {
18165                HoverLink::Text(link) => Some(Task::ready(anyhow::Ok(Some(link.target)))),
18166                HoverLink::InlayHint(lsp_location, server_id) => {
18167                    let computation =
18168                        self.compute_target_location(lsp_location, server_id, window, cx);
18169                    Some(cx.background_spawn(computation))
18170                }
18171                HoverLink::Url(url) => {
18172                    first_url_or_file = Some(Either::Left(url));
18173                    None
18174                }
18175                HoverLink::File(path) => {
18176                    first_url_or_file = Some(Either::Right(path));
18177                    None
18178                }
18179            })
18180            .collect();
18181
18182        let workspace = self.workspace();
18183
18184        cx.spawn_in(window, async move |editor, cx| {
18185            let locations: Vec<Location> = future::join_all(definitions)
18186                .await
18187                .into_iter()
18188                .filter_map(|location| location.transpose())
18189                .collect::<Result<_>>()
18190                .context("location tasks")?;
18191            let mut locations = cx.update(|_, cx| {
18192                locations
18193                    .into_iter()
18194                    .map(|location| {
18195                        let buffer = location.buffer.read(cx);
18196                        (location.buffer, location.range.to_point(buffer))
18197                    })
18198                    .into_group_map()
18199            })?;
18200            let mut num_locations = 0;
18201            for ranges in locations.values_mut() {
18202                ranges.sort_by_key(|range| (range.start, Reverse(range.end)));
18203                ranges.dedup();
18204                num_locations += ranges.len();
18205            }
18206
18207            if num_locations > 1 {
18208                let tab_kind = match kind {
18209                    Some(GotoDefinitionKind::Implementation) => "Implementations",
18210                    Some(GotoDefinitionKind::Symbol) | None => "Definitions",
18211                    Some(GotoDefinitionKind::Declaration) => "Declarations",
18212                    Some(GotoDefinitionKind::Type) => "Types",
18213                };
18214                let title = editor
18215                    .update_in(cx, |_, _, cx| {
18216                        let target = locations
18217                            .iter()
18218                            .flat_map(|(k, v)| iter::repeat(k.clone()).zip(v))
18219                            .map(|(buffer, location)| {
18220                                buffer
18221                                    .read(cx)
18222                                    .text_for_range(location.clone())
18223                                    .collect::<String>()
18224                            })
18225                            .filter(|text| !text.contains('\n'))
18226                            .unique()
18227                            .take(3)
18228                            .join(", ");
18229                        if target.is_empty() {
18230                            tab_kind.to_owned()
18231                        } else {
18232                            format!("{tab_kind} for {target}")
18233                        }
18234                    })
18235                    .context("buffer title")?;
18236
18237                let Some(workspace) = workspace else {
18238                    return Ok(Navigated::No);
18239                };
18240
18241                let opened = workspace
18242                    .update_in(cx, |workspace, window, cx| {
18243                        let allow_preview = PreviewTabsSettings::get_global(cx)
18244                            .enable_preview_multibuffer_from_code_navigation;
18245                        if let Some((target_editor, target_pane)) =
18246                            Self::open_locations_in_multibuffer(
18247                                workspace,
18248                                locations,
18249                                title,
18250                                split,
18251                                allow_preview,
18252                                MultibufferSelectionMode::First,
18253                                window,
18254                                cx,
18255                            )
18256                        {
18257                            // We create our own nav history instead of using
18258                            // `target_editor.nav_history` because `nav_history`
18259                            // seems to be populated asynchronously when an item
18260                            // is added to a pane
18261                            let mut nav_history = target_pane
18262                                .update(cx, |pane, _| pane.nav_history_for_item(&target_editor));
18263                            target_editor.update(cx, |editor, cx| {
18264                                let nav_data = editor
18265                                    .navigation_data(editor.selections.newest_anchor().head(), cx);
18266                                let target =
18267                                    Some(nav_history.navigation_entry(Some(
18268                                        Arc::new(nav_data) as Arc<dyn Any + Send + Sync>
18269                                    )));
18270                                nav_history.push_tag(origin, target);
18271                            })
18272                        }
18273                    })
18274                    .is_ok();
18275
18276                anyhow::Ok(Navigated::from_bool(opened))
18277            } else if num_locations == 0 {
18278                // If there is one url or file, open it directly
18279                match first_url_or_file {
18280                    Some(Either::Left(url)) => {
18281                        cx.update(|window, cx| {
18282                            if parse_zed_link(&url, cx).is_some() {
18283                                window
18284                                    .dispatch_action(Box::new(zed_actions::OpenZedUrl { url }), cx);
18285                            } else {
18286                                cx.open_url(&url);
18287                            }
18288                        })?;
18289                        Ok(Navigated::Yes)
18290                    }
18291                    Some(Either::Right(path)) => {
18292                        // TODO(andrew): respect preview tab settings
18293                        //               `enable_keep_preview_on_code_navigation` and
18294                        //               `enable_preview_file_from_code_navigation`
18295                        let Some(workspace) = workspace else {
18296                            return Ok(Navigated::No);
18297                        };
18298                        workspace
18299                            .update_in(cx, |workspace, window, cx| {
18300                                workspace.open_resolved_path(path, window, cx)
18301                            })?
18302                            .await?;
18303                        Ok(Navigated::Yes)
18304                    }
18305                    None => Ok(Navigated::No),
18306                }
18307            } else {
18308                let (target_buffer, target_ranges) = locations.into_iter().next().unwrap();
18309                let target_range = target_ranges.first().unwrap().clone();
18310
18311                editor.update_in(cx, |editor, window, cx| {
18312                    let range = editor.range_for_match(&target_range);
18313                    let range = collapse_multiline_range(range);
18314
18315                    if !split
18316                        && Some(&target_buffer) == editor.buffer.read(cx).as_singleton().as_ref()
18317                    {
18318                        editor.go_to_singleton_buffer_range(range, window, cx);
18319
18320                        let target =
18321                            editor.navigation_entry(editor.selections.newest_anchor().head(), cx);
18322                        if let Some(mut nav_history) = editor.nav_history.clone() {
18323                            nav_history.push_tag(origin, target);
18324                        }
18325                    } else {
18326                        let Some(workspace) = workspace else {
18327                            return Navigated::No;
18328                        };
18329                        let pane = workspace.read(cx).active_pane().clone();
18330                        window.defer(cx, move |window, cx| {
18331                            let (target_editor, target_pane): (Entity<Self>, Entity<Pane>) =
18332                                workspace.update(cx, |workspace, cx| {
18333                                    let pane = if split {
18334                                        workspace.adjacent_pane(window, cx)
18335                                    } else {
18336                                        workspace.active_pane().clone()
18337                                    };
18338
18339                                    let preview_tabs_settings = PreviewTabsSettings::get_global(cx);
18340                                    let keep_old_preview = preview_tabs_settings
18341                                        .enable_keep_preview_on_code_navigation;
18342                                    let allow_new_preview = preview_tabs_settings
18343                                        .enable_preview_file_from_code_navigation;
18344
18345                                    let editor = workspace.open_project_item(
18346                                        pane.clone(),
18347                                        target_buffer.clone(),
18348                                        true,
18349                                        true,
18350                                        keep_old_preview,
18351                                        allow_new_preview,
18352                                        window,
18353                                        cx,
18354                                    );
18355                                    (editor, pane)
18356                                });
18357                            // We create our own nav history instead of using
18358                            // `target_editor.nav_history` because `nav_history`
18359                            // seems to be populated asynchronously when an item
18360                            // is added to a pane
18361                            let mut nav_history = target_pane
18362                                .update(cx, |pane, _| pane.nav_history_for_item(&target_editor));
18363                            target_editor.update(cx, |target_editor, cx| {
18364                                // When selecting a definition in a different buffer, disable the nav history
18365                                // to avoid creating a history entry at the previous cursor location.
18366                                pane.update(cx, |pane, _| pane.disable_history());
18367                                target_editor.go_to_singleton_buffer_range(range, window, cx);
18368
18369                                let nav_data = target_editor.navigation_data(
18370                                    target_editor.selections.newest_anchor().head(),
18371                                    cx,
18372                                );
18373                                let target =
18374                                    Some(nav_history.navigation_entry(Some(
18375                                        Arc::new(nav_data) as Arc<dyn Any + Send + Sync>
18376                                    )));
18377                                nav_history.push_tag(origin, target);
18378                                pane.update(cx, |pane, _| pane.enable_history());
18379                            });
18380                        });
18381                    }
18382                    Navigated::Yes
18383                })
18384            }
18385        })
18386    }
18387
18388    fn compute_target_location(
18389        &self,
18390        lsp_location: lsp::Location,
18391        server_id: LanguageServerId,
18392        window: &mut Window,
18393        cx: &mut Context<Self>,
18394    ) -> Task<anyhow::Result<Option<Location>>> {
18395        let Some(project) = self.project.clone() else {
18396            return Task::ready(Ok(None));
18397        };
18398
18399        cx.spawn_in(window, async move |editor, cx| {
18400            let location_task = editor.update(cx, |_, cx| {
18401                project.update(cx, |project, cx| {
18402                    project.open_local_buffer_via_lsp(lsp_location.uri.clone(), server_id, cx)
18403                })
18404            })?;
18405            let location = Some({
18406                let target_buffer_handle = location_task.await.context("open local buffer")?;
18407                let range = target_buffer_handle.read_with(cx, |target_buffer, _| {
18408                    let target_start = target_buffer
18409                        .clip_point_utf16(point_from_lsp(lsp_location.range.start), Bias::Left);
18410                    let target_end = target_buffer
18411                        .clip_point_utf16(point_from_lsp(lsp_location.range.end), Bias::Left);
18412                    target_buffer.anchor_after(target_start)
18413                        ..target_buffer.anchor_before(target_end)
18414                });
18415                Location {
18416                    buffer: target_buffer_handle,
18417                    range,
18418                }
18419            });
18420            Ok(location)
18421        })
18422    }
18423
18424    fn go_to_next_reference(
18425        &mut self,
18426        _: &GoToNextReference,
18427        window: &mut Window,
18428        cx: &mut Context<Self>,
18429    ) {
18430        let task = self.go_to_reference_before_or_after_position(Direction::Next, 1, window, cx);
18431        if let Some(task) = task {
18432            task.detach();
18433        };
18434    }
18435
18436    fn go_to_prev_reference(
18437        &mut self,
18438        _: &GoToPreviousReference,
18439        window: &mut Window,
18440        cx: &mut Context<Self>,
18441    ) {
18442        let task = self.go_to_reference_before_or_after_position(Direction::Prev, 1, window, cx);
18443        if let Some(task) = task {
18444            task.detach();
18445        };
18446    }
18447
18448    pub fn go_to_reference_before_or_after_position(
18449        &mut self,
18450        direction: Direction,
18451        count: usize,
18452        window: &mut Window,
18453        cx: &mut Context<Self>,
18454    ) -> Option<Task<Result<()>>> {
18455        let selection = self.selections.newest_anchor();
18456        let head = selection.head();
18457
18458        let multi_buffer = self.buffer.read(cx);
18459
18460        let (buffer, text_head) = multi_buffer.text_anchor_for_position(head, cx)?;
18461        let workspace = self.workspace()?;
18462        let project = workspace.read(cx).project().clone();
18463        let references =
18464            project.update(cx, |project, cx| project.references(&buffer, text_head, cx));
18465        Some(cx.spawn_in(window, async move |editor, cx| -> Result<()> {
18466            let Some(locations) = references.await? else {
18467                return Ok(());
18468            };
18469
18470            if locations.is_empty() {
18471                // totally normal - the cursor may be on something which is not
18472                // a symbol (e.g. a keyword)
18473                log::info!("no references found under cursor");
18474                return Ok(());
18475            }
18476
18477            let multi_buffer = editor.read_with(cx, |editor, _| editor.buffer().clone())?;
18478
18479            let (locations, current_location_index) =
18480                multi_buffer.update(cx, |multi_buffer, cx| {
18481                    let mut locations = locations
18482                        .into_iter()
18483                        .filter_map(|loc| {
18484                            let start = multi_buffer.buffer_anchor_to_anchor(
18485                                &loc.buffer,
18486                                loc.range.start,
18487                                cx,
18488                            )?;
18489                            let end = multi_buffer.buffer_anchor_to_anchor(
18490                                &loc.buffer,
18491                                loc.range.end,
18492                                cx,
18493                            )?;
18494                            Some(start..end)
18495                        })
18496                        .collect::<Vec<_>>();
18497
18498                    let multi_buffer_snapshot = multi_buffer.snapshot(cx);
18499                    // There is an O(n) implementation, but given this list will be
18500                    // small (usually <100 items), the extra O(log(n)) factor isn't
18501                    // worth the (surprisingly large amount of) extra complexity.
18502                    locations
18503                        .sort_unstable_by(|l, r| l.start.cmp(&r.start, &multi_buffer_snapshot));
18504
18505                    let head_offset = head.to_offset(&multi_buffer_snapshot);
18506
18507                    let current_location_index = locations.iter().position(|loc| {
18508                        loc.start.to_offset(&multi_buffer_snapshot) <= head_offset
18509                            && loc.end.to_offset(&multi_buffer_snapshot) >= head_offset
18510                    });
18511
18512                    (locations, current_location_index)
18513                });
18514
18515            let Some(current_location_index) = current_location_index else {
18516                // This indicates something has gone wrong, because we already
18517                // handle the "no references" case above
18518                log::error!(
18519                    "failed to find current reference under cursor. Total references: {}",
18520                    locations.len()
18521                );
18522                return Ok(());
18523            };
18524
18525            let destination_location_index = match direction {
18526                Direction::Next => (current_location_index + count) % locations.len(),
18527                Direction::Prev => {
18528                    (current_location_index + locations.len() - count % locations.len())
18529                        % locations.len()
18530                }
18531            };
18532
18533            // TODO(cameron): is this needed?
18534            // the thinking is to avoid "jumping to the current location" (avoid
18535            // polluting "jumplist" in vim terms)
18536            if current_location_index == destination_location_index {
18537                return Ok(());
18538            }
18539
18540            let Range { start, end } = locations[destination_location_index];
18541
18542            editor.update_in(cx, |editor, window, cx| {
18543                let effects = SelectionEffects::default();
18544
18545                editor.unfold_ranges(&[start..end], false, false, cx);
18546                editor.change_selections(effects, window, cx, |s| {
18547                    s.select_ranges([start..start]);
18548                });
18549            })?;
18550
18551            Ok(())
18552        }))
18553    }
18554
18555    pub fn find_all_references(
18556        &mut self,
18557        action: &FindAllReferences,
18558        window: &mut Window,
18559        cx: &mut Context<Self>,
18560    ) -> Option<Task<Result<Navigated>>> {
18561        let always_open_multibuffer = action.always_open_multibuffer;
18562        let selection = self.selections.newest_anchor();
18563        let multi_buffer = self.buffer.read(cx);
18564        let multi_buffer_snapshot = multi_buffer.snapshot(cx);
18565        let selection_offset = selection.map(|anchor| anchor.to_offset(&multi_buffer_snapshot));
18566        let selection_point = selection.map(|anchor| anchor.to_point(&multi_buffer_snapshot));
18567        let head = selection_offset.head();
18568
18569        let head_anchor = multi_buffer_snapshot.anchor_at(
18570            head,
18571            if head < selection_offset.tail() {
18572                Bias::Right
18573            } else {
18574                Bias::Left
18575            },
18576        );
18577
18578        match self
18579            .find_all_references_task_sources
18580            .binary_search_by(|anchor| anchor.cmp(&head_anchor, &multi_buffer_snapshot))
18581        {
18582            Ok(_) => {
18583                log::info!(
18584                    "Ignoring repeated FindAllReferences invocation with the position of already running task"
18585                );
18586                return None;
18587            }
18588            Err(i) => {
18589                self.find_all_references_task_sources.insert(i, head_anchor);
18590            }
18591        }
18592
18593        let (buffer, head) = multi_buffer.text_anchor_for_position(head, cx)?;
18594        let workspace = self.workspace()?;
18595        let project = workspace.read(cx).project().clone();
18596        let references = project.update(cx, |project, cx| project.references(&buffer, head, cx));
18597        Some(cx.spawn_in(window, async move |editor, cx| {
18598            let _cleanup = cx.on_drop(&editor, move |editor, _| {
18599                if let Ok(i) = editor
18600                    .find_all_references_task_sources
18601                    .binary_search_by(|anchor| anchor.cmp(&head_anchor, &multi_buffer_snapshot))
18602                {
18603                    editor.find_all_references_task_sources.remove(i);
18604                }
18605            });
18606
18607            let Some(locations) = references.await? else {
18608                return anyhow::Ok(Navigated::No);
18609            };
18610            let mut locations = cx.update(|_, cx| {
18611                locations
18612                    .into_iter()
18613                    .map(|location| {
18614                        let buffer = location.buffer.read(cx);
18615                        (location.buffer, location.range.to_point(buffer))
18616                    })
18617                    // if special-casing the single-match case, remove ranges
18618                    // that intersect current selection
18619                    .filter(|(location_buffer, location)| {
18620                        if always_open_multibuffer || &buffer != location_buffer {
18621                            return true;
18622                        }
18623
18624                        !location.contains_inclusive(&selection_point.range())
18625                    })
18626                    .into_group_map()
18627            })?;
18628            if locations.is_empty() {
18629                return anyhow::Ok(Navigated::No);
18630            }
18631            for ranges in locations.values_mut() {
18632                ranges.sort_by_key(|range| (range.start, Reverse(range.end)));
18633                ranges.dedup();
18634            }
18635            let mut num_locations = 0;
18636            for ranges in locations.values_mut() {
18637                ranges.sort_by_key(|range| (range.start, Reverse(range.end)));
18638                ranges.dedup();
18639                num_locations += ranges.len();
18640            }
18641
18642            if num_locations == 1 && !always_open_multibuffer {
18643                let (target_buffer, target_ranges) = locations.into_iter().next().unwrap();
18644                let target_range = target_ranges.first().unwrap().clone();
18645
18646                return editor.update_in(cx, |editor, window, cx| {
18647                    let range = target_range.to_point(target_buffer.read(cx));
18648                    let range = editor.range_for_match(&range);
18649                    let range = range.start..range.start;
18650
18651                    if Some(&target_buffer) == editor.buffer.read(cx).as_singleton().as_ref() {
18652                        editor.go_to_singleton_buffer_range(range, window, cx);
18653                    } else {
18654                        let pane = workspace.read(cx).active_pane().clone();
18655                        window.defer(cx, move |window, cx| {
18656                            let target_editor: Entity<Self> =
18657                                workspace.update(cx, |workspace, cx| {
18658                                    let pane = workspace.active_pane().clone();
18659
18660                                    let preview_tabs_settings = PreviewTabsSettings::get_global(cx);
18661                                    let keep_old_preview = preview_tabs_settings
18662                                        .enable_keep_preview_on_code_navigation;
18663                                    let allow_new_preview = preview_tabs_settings
18664                                        .enable_preview_file_from_code_navigation;
18665
18666                                    workspace.open_project_item(
18667                                        pane,
18668                                        target_buffer.clone(),
18669                                        true,
18670                                        true,
18671                                        keep_old_preview,
18672                                        allow_new_preview,
18673                                        window,
18674                                        cx,
18675                                    )
18676                                });
18677                            target_editor.update(cx, |target_editor, cx| {
18678                                // When selecting a definition in a different buffer, disable the nav history
18679                                // to avoid creating a history entry at the previous cursor location.
18680                                pane.update(cx, |pane, _| pane.disable_history());
18681                                target_editor.go_to_singleton_buffer_range(range, window, cx);
18682                                pane.update(cx, |pane, _| pane.enable_history());
18683                            });
18684                        });
18685                    }
18686                    Navigated::No
18687                });
18688            }
18689
18690            workspace.update_in(cx, |workspace, window, cx| {
18691                let target = locations
18692                    .iter()
18693                    .flat_map(|(k, v)| iter::repeat(k.clone()).zip(v))
18694                    .map(|(buffer, location)| {
18695                        buffer
18696                            .read(cx)
18697                            .text_for_range(location.clone())
18698                            .collect::<String>()
18699                    })
18700                    .filter(|text| !text.contains('\n'))
18701                    .unique()
18702                    .take(3)
18703                    .join(", ");
18704                let title = if target.is_empty() {
18705                    "References".to_owned()
18706                } else {
18707                    format!("References to {target}")
18708                };
18709                let allow_preview = PreviewTabsSettings::get_global(cx)
18710                    .enable_preview_multibuffer_from_code_navigation;
18711                Self::open_locations_in_multibuffer(
18712                    workspace,
18713                    locations,
18714                    title,
18715                    false,
18716                    allow_preview,
18717                    MultibufferSelectionMode::First,
18718                    window,
18719                    cx,
18720                );
18721                Navigated::Yes
18722            })
18723        }))
18724    }
18725
18726    /// Opens a multibuffer with the given project locations in it.
18727    pub fn open_locations_in_multibuffer(
18728        workspace: &mut Workspace,
18729        locations: std::collections::HashMap<Entity<Buffer>, Vec<Range<Point>>>,
18730        title: String,
18731        split: bool,
18732        allow_preview: bool,
18733        multibuffer_selection_mode: MultibufferSelectionMode,
18734        window: &mut Window,
18735        cx: &mut Context<Workspace>,
18736    ) -> Option<(Entity<Editor>, Entity<Pane>)> {
18737        if locations.is_empty() {
18738            log::error!("bug: open_locations_in_multibuffer called with empty list of locations");
18739            return None;
18740        }
18741
18742        let capability = workspace.project().read(cx).capability();
18743        let mut ranges = <Vec<Range<Anchor>>>::new();
18744
18745        // a key to find existing multibuffer editors with the same set of locations
18746        // to prevent us from opening more and more multibuffer tabs for searches and the like
18747        let mut key = (title.clone(), vec![]);
18748        let excerpt_buffer = cx.new(|cx| {
18749            let key = &mut key.1;
18750            let mut multibuffer = MultiBuffer::new(capability);
18751            for (buffer, mut ranges_for_buffer) in locations {
18752                ranges_for_buffer.sort_by_key(|range| (range.start, Reverse(range.end)));
18753                key.push((buffer.read(cx).remote_id(), ranges_for_buffer.clone()));
18754                let (new_ranges, _) = multibuffer.set_excerpts_for_path(
18755                    PathKey::for_buffer(&buffer, cx),
18756                    buffer.clone(),
18757                    ranges_for_buffer,
18758                    multibuffer_context_lines(cx),
18759                    cx,
18760                );
18761                ranges.extend(new_ranges)
18762            }
18763
18764            multibuffer.with_title(title)
18765        });
18766        let existing = workspace.active_pane().update(cx, |pane, cx| {
18767            pane.items()
18768                .filter_map(|item| item.downcast::<Editor>())
18769                .find(|editor| {
18770                    editor
18771                        .read(cx)
18772                        .lookup_key
18773                        .as_ref()
18774                        .and_then(|it| {
18775                            it.downcast_ref::<(String, Vec<(BufferId, Vec<Range<Point>>)>)>()
18776                        })
18777                        .is_some_and(|it| *it == key)
18778                })
18779        });
18780        let was_existing = existing.is_some();
18781        let editor = existing.unwrap_or_else(|| {
18782            cx.new(|cx| {
18783                let mut editor = Editor::for_multibuffer(
18784                    excerpt_buffer,
18785                    Some(workspace.project().clone()),
18786                    window,
18787                    cx,
18788                );
18789                editor.lookup_key = Some(Box::new(key));
18790                editor
18791            })
18792        });
18793        editor.update(cx, |editor, cx| match multibuffer_selection_mode {
18794            MultibufferSelectionMode::First => {
18795                if let Some(first_range) = ranges.first() {
18796                    editor.change_selections(
18797                        SelectionEffects::no_scroll(),
18798                        window,
18799                        cx,
18800                        |selections| {
18801                            selections.clear_disjoint();
18802                            selections.select_anchor_ranges(std::iter::once(first_range.clone()));
18803                        },
18804                    );
18805                }
18806                editor.highlight_background(
18807                    HighlightKey::Editor,
18808                    &ranges,
18809                    |_, theme| theme.colors().editor_highlighted_line_background,
18810                    cx,
18811                );
18812            }
18813            MultibufferSelectionMode::All => {
18814                editor.change_selections(SelectionEffects::no_scroll(), window, cx, |selections| {
18815                    selections.clear_disjoint();
18816                    selections.select_anchor_ranges(ranges);
18817                });
18818            }
18819        });
18820
18821        let item = Box::new(editor.clone());
18822
18823        let pane = if split {
18824            workspace.adjacent_pane(window, cx)
18825        } else {
18826            workspace.active_pane().clone()
18827        };
18828        let activate_pane = split;
18829
18830        let mut destination_index = None;
18831        pane.update(cx, |pane, cx| {
18832            if allow_preview && !was_existing {
18833                destination_index = pane.replace_preview_item_id(item.item_id(), window, cx);
18834            }
18835            if was_existing && !allow_preview {
18836                pane.unpreview_item_if_preview(item.item_id());
18837            }
18838            pane.add_item(item, activate_pane, true, destination_index, window, cx);
18839        });
18840
18841        Some((editor, pane))
18842    }
18843
18844    pub fn rename(
18845        &mut self,
18846        _: &Rename,
18847        window: &mut Window,
18848        cx: &mut Context<Self>,
18849    ) -> Option<Task<Result<()>>> {
18850        use language::ToOffset as _;
18851
18852        let provider = self.semantics_provider.clone()?;
18853        let selection = self.selections.newest_anchor().clone();
18854        let (cursor_buffer, cursor_buffer_position) = self
18855            .buffer
18856            .read(cx)
18857            .text_anchor_for_position(selection.head(), cx)?;
18858        let (tail_buffer, cursor_buffer_position_end) = self
18859            .buffer
18860            .read(cx)
18861            .text_anchor_for_position(selection.tail(), cx)?;
18862        if tail_buffer != cursor_buffer {
18863            return None;
18864        }
18865
18866        let snapshot = cursor_buffer.read(cx).snapshot();
18867        let cursor_buffer_offset = cursor_buffer_position.to_offset(&snapshot);
18868        let cursor_buffer_offset_end = cursor_buffer_position_end.to_offset(&snapshot);
18869        let prepare_rename = provider
18870            .range_for_rename(&cursor_buffer, cursor_buffer_position, cx)
18871            .unwrap_or_else(|| Task::ready(Ok(None)));
18872        drop(snapshot);
18873
18874        Some(cx.spawn_in(window, async move |this, cx| {
18875            let rename_range = if let Some(range) = prepare_rename.await? {
18876                Some(range)
18877            } else {
18878                this.update(cx, |this, cx| {
18879                    let buffer = this.buffer.read(cx).snapshot(cx);
18880                    let mut buffer_highlights = this
18881                        .document_highlights_for_position(selection.head(), &buffer)
18882                        .filter(|highlight| {
18883                            highlight.start.excerpt_id == selection.head().excerpt_id
18884                                && highlight.end.excerpt_id == selection.head().excerpt_id
18885                        });
18886                    buffer_highlights
18887                        .next()
18888                        .map(|highlight| highlight.start.text_anchor..highlight.end.text_anchor)
18889                })?
18890            };
18891            if let Some(rename_range) = rename_range {
18892                this.update_in(cx, |this, window, cx| {
18893                    let snapshot = cursor_buffer.read(cx).snapshot();
18894                    let rename_buffer_range = rename_range.to_offset(&snapshot);
18895                    let cursor_offset_in_rename_range =
18896                        cursor_buffer_offset.saturating_sub(rename_buffer_range.start);
18897                    let cursor_offset_in_rename_range_end =
18898                        cursor_buffer_offset_end.saturating_sub(rename_buffer_range.start);
18899
18900                    this.take_rename(false, window, cx);
18901                    let buffer = this.buffer.read(cx).read(cx);
18902                    let cursor_offset = selection.head().to_offset(&buffer);
18903                    let rename_start =
18904                        cursor_offset.saturating_sub_usize(cursor_offset_in_rename_range);
18905                    let rename_end = rename_start + rename_buffer_range.len();
18906                    let range = buffer.anchor_before(rename_start)..buffer.anchor_after(rename_end);
18907                    let mut old_highlight_id = None;
18908                    let old_name: Arc<str> = buffer
18909                        .chunks(rename_start..rename_end, true)
18910                        .map(|chunk| {
18911                            if old_highlight_id.is_none() {
18912                                old_highlight_id = chunk.syntax_highlight_id;
18913                            }
18914                            chunk.text
18915                        })
18916                        .collect::<String>()
18917                        .into();
18918
18919                    drop(buffer);
18920
18921                    // Position the selection in the rename editor so that it matches the current selection.
18922                    this.show_local_selections = false;
18923                    let rename_editor = cx.new(|cx| {
18924                        let mut editor = Editor::single_line(window, cx);
18925                        editor.buffer.update(cx, |buffer, cx| {
18926                            buffer.edit(
18927                                [(MultiBufferOffset(0)..MultiBufferOffset(0), old_name.clone())],
18928                                None,
18929                                cx,
18930                            )
18931                        });
18932                        let cursor_offset_in_rename_range =
18933                            MultiBufferOffset(cursor_offset_in_rename_range);
18934                        let cursor_offset_in_rename_range_end =
18935                            MultiBufferOffset(cursor_offset_in_rename_range_end);
18936                        let rename_selection_range = match cursor_offset_in_rename_range
18937                            .cmp(&cursor_offset_in_rename_range_end)
18938                        {
18939                            Ordering::Equal => {
18940                                editor.select_all(&SelectAll, window, cx);
18941                                return editor;
18942                            }
18943                            Ordering::Less => {
18944                                cursor_offset_in_rename_range..cursor_offset_in_rename_range_end
18945                            }
18946                            Ordering::Greater => {
18947                                cursor_offset_in_rename_range_end..cursor_offset_in_rename_range
18948                            }
18949                        };
18950                        if rename_selection_range.end.0 > old_name.len() {
18951                            editor.select_all(&SelectAll, window, cx);
18952                        } else {
18953                            editor.change_selections(Default::default(), window, cx, |s| {
18954                                s.select_ranges([rename_selection_range]);
18955                            });
18956                        }
18957                        editor
18958                    });
18959                    cx.subscribe(&rename_editor, |_, _, e: &EditorEvent, cx| {
18960                        if e == &EditorEvent::Focused {
18961                            cx.emit(EditorEvent::FocusedIn)
18962                        }
18963                    })
18964                    .detach();
18965
18966                    let write_highlights =
18967                        this.clear_background_highlights(HighlightKey::DocumentHighlightWrite, cx);
18968                    let read_highlights =
18969                        this.clear_background_highlights(HighlightKey::DocumentHighlightRead, cx);
18970                    let ranges = write_highlights
18971                        .iter()
18972                        .flat_map(|(_, ranges)| ranges.iter())
18973                        .chain(read_highlights.iter().flat_map(|(_, ranges)| ranges.iter()))
18974                        .cloned()
18975                        .collect();
18976
18977                    this.highlight_text(
18978                        HighlightKey::Rename,
18979                        ranges,
18980                        HighlightStyle {
18981                            fade_out: Some(0.6),
18982                            ..Default::default()
18983                        },
18984                        cx,
18985                    );
18986                    let rename_focus_handle = rename_editor.focus_handle(cx);
18987                    window.focus(&rename_focus_handle, cx);
18988                    let block_id = this.insert_blocks(
18989                        [BlockProperties {
18990                            style: BlockStyle::Flex,
18991                            placement: BlockPlacement::Below(range.start),
18992                            height: Some(1),
18993                            render: Arc::new({
18994                                let rename_editor = rename_editor.clone();
18995                                move |cx: &mut BlockContext| {
18996                                    let mut text_style = cx.editor_style.text.clone();
18997                                    if let Some(highlight_style) = old_highlight_id
18998                                        .and_then(|h| h.style(&cx.editor_style.syntax))
18999                                    {
19000                                        text_style = text_style.highlight(highlight_style);
19001                                    }
19002                                    div()
19003                                        .block_mouse_except_scroll()
19004                                        .pl(cx.anchor_x)
19005                                        .child(EditorElement::new(
19006                                            &rename_editor,
19007                                            EditorStyle {
19008                                                background: cx.theme().system().transparent,
19009                                                local_player: cx.editor_style.local_player,
19010                                                text: text_style,
19011                                                scrollbar_width: cx.editor_style.scrollbar_width,
19012                                                syntax: cx.editor_style.syntax.clone(),
19013                                                status: cx.editor_style.status.clone(),
19014                                                inlay_hints_style: HighlightStyle {
19015                                                    font_weight: Some(FontWeight::BOLD),
19016                                                    ..make_inlay_hints_style(cx.app)
19017                                                },
19018                                                edit_prediction_styles: make_suggestion_styles(
19019                                                    cx.app,
19020                                                ),
19021                                                ..EditorStyle::default()
19022                                            },
19023                                        ))
19024                                        .into_any_element()
19025                                }
19026                            }),
19027                            priority: 0,
19028                        }],
19029                        Some(Autoscroll::fit()),
19030                        cx,
19031                    )[0];
19032                    this.pending_rename = Some(RenameState {
19033                        range,
19034                        old_name,
19035                        editor: rename_editor,
19036                        block_id,
19037                    });
19038                })?;
19039            }
19040
19041            Ok(())
19042        }))
19043    }
19044
19045    pub fn confirm_rename(
19046        &mut self,
19047        _: &ConfirmRename,
19048        window: &mut Window,
19049        cx: &mut Context<Self>,
19050    ) -> Option<Task<Result<()>>> {
19051        let rename = self.take_rename(false, window, cx)?;
19052        let workspace = self.workspace()?.downgrade();
19053        let (buffer, start) = self
19054            .buffer
19055            .read(cx)
19056            .text_anchor_for_position(rename.range.start, cx)?;
19057        let (end_buffer, _) = self
19058            .buffer
19059            .read(cx)
19060            .text_anchor_for_position(rename.range.end, cx)?;
19061        if buffer != end_buffer {
19062            return None;
19063        }
19064
19065        let old_name = rename.old_name;
19066        let new_name = rename.editor.read(cx).text(cx);
19067
19068        let rename = self.semantics_provider.as_ref()?.perform_rename(
19069            &buffer,
19070            start,
19071            new_name.clone(),
19072            cx,
19073        )?;
19074
19075        Some(cx.spawn_in(window, async move |editor, cx| {
19076            let project_transaction = rename.await?;
19077            Self::open_project_transaction(
19078                &editor,
19079                workspace,
19080                project_transaction,
19081                format!("Rename: {}{}", old_name, new_name),
19082                cx,
19083            )
19084            .await?;
19085
19086            editor.update(cx, |editor, cx| {
19087                editor.refresh_document_highlights(cx);
19088            })?;
19089            Ok(())
19090        }))
19091    }
19092
19093    fn take_rename(
19094        &mut self,
19095        moving_cursor: bool,
19096        window: &mut Window,
19097        cx: &mut Context<Self>,
19098    ) -> Option<RenameState> {
19099        let rename = self.pending_rename.take()?;
19100        if rename.editor.focus_handle(cx).is_focused(window) {
19101            window.focus(&self.focus_handle, cx);
19102        }
19103
19104        self.remove_blocks(
19105            [rename.block_id].into_iter().collect(),
19106            Some(Autoscroll::fit()),
19107            cx,
19108        );
19109        self.clear_highlights(HighlightKey::Rename, cx);
19110        self.show_local_selections = true;
19111
19112        if moving_cursor {
19113            let cursor_in_rename_editor = rename.editor.update(cx, |editor, cx| {
19114                editor
19115                    .selections
19116                    .newest::<MultiBufferOffset>(&editor.display_snapshot(cx))
19117                    .head()
19118            });
19119
19120            // Update the selection to match the position of the selection inside
19121            // the rename editor.
19122            let snapshot = self.buffer.read(cx).read(cx);
19123            let rename_range = rename.range.to_offset(&snapshot);
19124            let cursor_in_editor = snapshot
19125                .clip_offset(rename_range.start + cursor_in_rename_editor, Bias::Left)
19126                .min(rename_range.end);
19127            drop(snapshot);
19128
19129            self.change_selections(SelectionEffects::no_scroll(), window, cx, |s| {
19130                s.select_ranges(vec![cursor_in_editor..cursor_in_editor])
19131            });
19132        } else {
19133            self.refresh_document_highlights(cx);
19134        }
19135
19136        Some(rename)
19137    }
19138
19139    pub fn pending_rename(&self) -> Option<&RenameState> {
19140        self.pending_rename.as_ref()
19141    }
19142
19143    fn format(
19144        &mut self,
19145        _: &Format,
19146        window: &mut Window,
19147        cx: &mut Context<Self>,
19148    ) -> Option<Task<Result<()>>> {
19149        self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
19150
19151        let project = match &self.project {
19152            Some(project) => project.clone(),
19153            None => return None,
19154        };
19155
19156        Some(self.perform_format(
19157            project,
19158            FormatTrigger::Manual,
19159            FormatTarget::Buffers(self.buffer.read(cx).all_buffers()),
19160            window,
19161            cx,
19162        ))
19163    }
19164
19165    fn format_selections(
19166        &mut self,
19167        _: &FormatSelections,
19168        window: &mut Window,
19169        cx: &mut Context<Self>,
19170    ) -> Option<Task<Result<()>>> {
19171        self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
19172
19173        let project = match &self.project {
19174            Some(project) => project.clone(),
19175            None => return None,
19176        };
19177
19178        let ranges = self
19179            .selections
19180            .all_adjusted(&self.display_snapshot(cx))
19181            .into_iter()
19182            .map(|selection| selection.range())
19183            .collect_vec();
19184
19185        Some(self.perform_format(
19186            project,
19187            FormatTrigger::Manual,
19188            FormatTarget::Ranges(ranges),
19189            window,
19190            cx,
19191        ))
19192    }
19193
19194    fn perform_format(
19195        &mut self,
19196        project: Entity<Project>,
19197        trigger: FormatTrigger,
19198        target: FormatTarget,
19199        window: &mut Window,
19200        cx: &mut Context<Self>,
19201    ) -> Task<Result<()>> {
19202        let buffer = self.buffer.clone();
19203        let (buffers, target) = match target {
19204            FormatTarget::Buffers(buffers) => (buffers, LspFormatTarget::Buffers),
19205            FormatTarget::Ranges(selection_ranges) => {
19206                let multi_buffer = buffer.read(cx);
19207                let snapshot = multi_buffer.read(cx);
19208                let mut buffers = HashSet::default();
19209                let mut buffer_id_to_ranges: BTreeMap<BufferId, Vec<Range<text::Anchor>>> =
19210                    BTreeMap::new();
19211                for selection_range in selection_ranges {
19212                    for (buffer, buffer_range, _) in
19213                        snapshot.range_to_buffer_ranges(selection_range.start..=selection_range.end)
19214                    {
19215                        let buffer_id = buffer.remote_id();
19216                        let start = buffer.anchor_before(buffer_range.start);
19217                        let end = buffer.anchor_after(buffer_range.end);
19218                        buffers.insert(multi_buffer.buffer(buffer_id).unwrap());
19219                        buffer_id_to_ranges
19220                            .entry(buffer_id)
19221                            .and_modify(|buffer_ranges| buffer_ranges.push(start..end))
19222                            .or_insert_with(|| vec![start..end]);
19223                    }
19224                }
19225                (buffers, LspFormatTarget::Ranges(buffer_id_to_ranges))
19226            }
19227        };
19228
19229        let transaction_id_prev = buffer.read(cx).last_transaction_id(cx);
19230        let selections_prev = transaction_id_prev
19231            .and_then(|transaction_id_prev| {
19232                // default to selections as they were after the last edit, if we have them,
19233                // instead of how they are now.
19234                // This will make it so that editing, moving somewhere else, formatting, then undoing the format
19235                // will take you back to where you made the last edit, instead of staying where you scrolled
19236                self.selection_history
19237                    .transaction(transaction_id_prev)
19238                    .map(|t| t.0.clone())
19239            })
19240            .unwrap_or_else(|| self.selections.disjoint_anchors_arc());
19241
19242        let mut timeout = cx.background_executor().timer(FORMAT_TIMEOUT).fuse();
19243        let format = project.update(cx, |project, cx| {
19244            project.format(buffers, target, true, trigger, cx)
19245        });
19246
19247        cx.spawn_in(window, async move |editor, cx| {
19248            let transaction = futures::select_biased! {
19249                transaction = format.log_err().fuse() => transaction,
19250                () = timeout => {
19251                    log::warn!("timed out waiting for formatting");
19252                    None
19253                }
19254            };
19255
19256            buffer.update(cx, |buffer, cx| {
19257                if let Some(transaction) = transaction
19258                    && !buffer.is_singleton()
19259                {
19260                    buffer.push_transaction(&transaction.0, cx);
19261                }
19262                cx.notify();
19263            });
19264
19265            if let Some(transaction_id_now) =
19266                buffer.read_with(cx, |b, cx| b.last_transaction_id(cx))
19267            {
19268                let has_new_transaction = transaction_id_prev != Some(transaction_id_now);
19269                if has_new_transaction {
19270                    editor
19271                        .update(cx, |editor, _| {
19272                            editor
19273                                .selection_history
19274                                .insert_transaction(transaction_id_now, selections_prev);
19275                        })
19276                        .ok();
19277                }
19278            }
19279
19280            Ok(())
19281        })
19282    }
19283
19284    fn organize_imports(
19285        &mut self,
19286        _: &OrganizeImports,
19287        window: &mut Window,
19288        cx: &mut Context<Self>,
19289    ) -> Option<Task<Result<()>>> {
19290        self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
19291        let project = match &self.project {
19292            Some(project) => project.clone(),
19293            None => return None,
19294        };
19295        Some(self.perform_code_action_kind(
19296            project,
19297            CodeActionKind::SOURCE_ORGANIZE_IMPORTS,
19298            window,
19299            cx,
19300        ))
19301    }
19302
19303    fn perform_code_action_kind(
19304        &mut self,
19305        project: Entity<Project>,
19306        kind: CodeActionKind,
19307        window: &mut Window,
19308        cx: &mut Context<Self>,
19309    ) -> Task<Result<()>> {
19310        let buffer = self.buffer.clone();
19311        let buffers = buffer.read(cx).all_buffers();
19312        let mut timeout = cx.background_executor().timer(CODE_ACTION_TIMEOUT).fuse();
19313        let apply_action = project.update(cx, |project, cx| {
19314            project.apply_code_action_kind(buffers, kind, true, cx)
19315        });
19316        cx.spawn_in(window, async move |_, cx| {
19317            let transaction = futures::select_biased! {
19318                () = timeout => {
19319                    log::warn!("timed out waiting for executing code action");
19320                    None
19321                }
19322                transaction = apply_action.log_err().fuse() => transaction,
19323            };
19324            buffer.update(cx, |buffer, cx| {
19325                // check if we need this
19326                if let Some(transaction) = transaction
19327                    && !buffer.is_singleton()
19328                {
19329                    buffer.push_transaction(&transaction.0, cx);
19330                }
19331                cx.notify();
19332            });
19333            Ok(())
19334        })
19335    }
19336
19337    pub fn restart_language_server(
19338        &mut self,
19339        _: &RestartLanguageServer,
19340        _: &mut Window,
19341        cx: &mut Context<Self>,
19342    ) {
19343        if let Some(project) = self.project.clone() {
19344            self.buffer.update(cx, |multi_buffer, cx| {
19345                project.update(cx, |project, cx| {
19346                    project.restart_language_servers_for_buffers(
19347                        multi_buffer.all_buffers().into_iter().collect(),
19348                        HashSet::default(),
19349                        cx,
19350                    );
19351                });
19352            })
19353        }
19354    }
19355
19356    pub fn stop_language_server(
19357        &mut self,
19358        _: &StopLanguageServer,
19359        _: &mut Window,
19360        cx: &mut Context<Self>,
19361    ) {
19362        if let Some(project) = self.project.clone() {
19363            self.buffer.update(cx, |multi_buffer, cx| {
19364                project.update(cx, |project, cx| {
19365                    project.stop_language_servers_for_buffers(
19366                        multi_buffer.all_buffers().into_iter().collect(),
19367                        HashSet::default(),
19368                        cx,
19369                    );
19370                });
19371            });
19372        }
19373    }
19374
19375    fn cancel_language_server_work(
19376        workspace: &mut Workspace,
19377        _: &actions::CancelLanguageServerWork,
19378        _: &mut Window,
19379        cx: &mut Context<Workspace>,
19380    ) {
19381        let project = workspace.project();
19382        let buffers = workspace
19383            .active_item(cx)
19384            .and_then(|item| item.act_as::<Editor>(cx))
19385            .map_or(HashSet::default(), |editor| {
19386                editor.read(cx).buffer.read(cx).all_buffers()
19387            });
19388        project.update(cx, |project, cx| {
19389            project.cancel_language_server_work_for_buffers(buffers, cx);
19390        });
19391    }
19392
19393    fn show_character_palette(
19394        &mut self,
19395        _: &ShowCharacterPalette,
19396        window: &mut Window,
19397        _: &mut Context<Self>,
19398    ) {
19399        window.show_character_palette();
19400    }
19401
19402    fn refresh_active_diagnostics(&mut self, cx: &mut Context<Editor>) {
19403        if !self.diagnostics_enabled() {
19404            return;
19405        }
19406
19407        if let ActiveDiagnostic::Group(active_diagnostics) = &mut self.active_diagnostics {
19408            let buffer = self.buffer.read(cx).snapshot(cx);
19409            let primary_range_start = active_diagnostics.active_range.start.to_offset(&buffer);
19410            let primary_range_end = active_diagnostics.active_range.end.to_offset(&buffer);
19411            let is_valid = buffer
19412                .diagnostics_in_range::<MultiBufferOffset>(primary_range_start..primary_range_end)
19413                .any(|entry| {
19414                    entry.diagnostic.is_primary
19415                        && !entry.range.is_empty()
19416                        && entry.range.start == primary_range_start
19417                        && entry.diagnostic.message == active_diagnostics.active_message
19418                });
19419
19420            if !is_valid {
19421                self.dismiss_diagnostics(cx);
19422            }
19423        }
19424    }
19425
19426    pub fn active_diagnostic_group(&self) -> Option<&ActiveDiagnosticGroup> {
19427        match &self.active_diagnostics {
19428            ActiveDiagnostic::Group(group) => Some(group),
19429            _ => None,
19430        }
19431    }
19432
19433    pub fn set_all_diagnostics_active(&mut self, cx: &mut Context<Self>) {
19434        if !self.diagnostics_enabled() {
19435            return;
19436        }
19437        self.dismiss_diagnostics(cx);
19438        self.active_diagnostics = ActiveDiagnostic::All;
19439    }
19440
19441    fn activate_diagnostics(
19442        &mut self,
19443        buffer_id: BufferId,
19444        diagnostic: DiagnosticEntryRef<'_, MultiBufferOffset>,
19445        window: &mut Window,
19446        cx: &mut Context<Self>,
19447    ) {
19448        if !self.diagnostics_enabled() || matches!(self.active_diagnostics, ActiveDiagnostic::All) {
19449            return;
19450        }
19451        self.dismiss_diagnostics(cx);
19452        let snapshot = self.snapshot(window, cx);
19453        let buffer = self.buffer.read(cx).snapshot(cx);
19454        let Some(renderer) = GlobalDiagnosticRenderer::global(cx) else {
19455            return;
19456        };
19457
19458        let diagnostic_group = buffer
19459            .diagnostic_group(buffer_id, diagnostic.diagnostic.group_id)
19460            .collect::<Vec<_>>();
19461
19462        let language_registry = self
19463            .project()
19464            .map(|project| project.read(cx).languages().clone());
19465
19466        let blocks = renderer.render_group(
19467            diagnostic_group,
19468            buffer_id,
19469            snapshot,
19470            cx.weak_entity(),
19471            language_registry,
19472            cx,
19473        );
19474
19475        let blocks = self.display_map.update(cx, |display_map, cx| {
19476            display_map.insert_blocks(blocks, cx).into_iter().collect()
19477        });
19478        self.active_diagnostics = ActiveDiagnostic::Group(ActiveDiagnosticGroup {
19479            active_range: buffer.anchor_before(diagnostic.range.start)
19480                ..buffer.anchor_after(diagnostic.range.end),
19481            active_message: diagnostic.diagnostic.message.clone(),
19482            group_id: diagnostic.diagnostic.group_id,
19483            blocks,
19484        });
19485        cx.notify();
19486    }
19487
19488    fn dismiss_diagnostics(&mut self, cx: &mut Context<Self>) {
19489        if matches!(self.active_diagnostics, ActiveDiagnostic::All) {
19490            return;
19491        };
19492
19493        let prev = mem::replace(&mut self.active_diagnostics, ActiveDiagnostic::None);
19494        if let ActiveDiagnostic::Group(group) = prev {
19495            self.display_map.update(cx, |display_map, cx| {
19496                display_map.remove_blocks(group.blocks, cx);
19497            });
19498            cx.notify();
19499        }
19500    }
19501
19502    /// Disable inline diagnostics rendering for this editor.
19503    pub fn disable_inline_diagnostics(&mut self) {
19504        self.inline_diagnostics_enabled = false;
19505        self.inline_diagnostics_update = Task::ready(());
19506        self.inline_diagnostics.clear();
19507    }
19508
19509    pub fn disable_diagnostics(&mut self, cx: &mut Context<Self>) {
19510        self.diagnostics_enabled = false;
19511        self.dismiss_diagnostics(cx);
19512        self.inline_diagnostics_update = Task::ready(());
19513        self.inline_diagnostics.clear();
19514    }
19515
19516    pub fn disable_word_completions(&mut self) {
19517        self.word_completions_enabled = false;
19518    }
19519
19520    pub fn diagnostics_enabled(&self) -> bool {
19521        self.diagnostics_enabled && self.mode.is_full()
19522    }
19523
19524    pub fn inline_diagnostics_enabled(&self) -> bool {
19525        self.inline_diagnostics_enabled && self.diagnostics_enabled()
19526    }
19527
19528    pub fn show_inline_diagnostics(&self) -> bool {
19529        self.show_inline_diagnostics
19530    }
19531
19532    pub fn toggle_inline_diagnostics(
19533        &mut self,
19534        _: &ToggleInlineDiagnostics,
19535        window: &mut Window,
19536        cx: &mut Context<Editor>,
19537    ) {
19538        self.show_inline_diagnostics = !self.show_inline_diagnostics;
19539        self.refresh_inline_diagnostics(false, window, cx);
19540    }
19541
19542    pub fn set_max_diagnostics_severity(&mut self, severity: DiagnosticSeverity, cx: &mut App) {
19543        self.diagnostics_max_severity = severity;
19544        self.display_map.update(cx, |display_map, _| {
19545            display_map.diagnostics_max_severity = self.diagnostics_max_severity;
19546        });
19547    }
19548
19549    pub fn toggle_diagnostics(
19550        &mut self,
19551        _: &ToggleDiagnostics,
19552        window: &mut Window,
19553        cx: &mut Context<Editor>,
19554    ) {
19555        if !self.diagnostics_enabled() {
19556            return;
19557        }
19558
19559        let new_severity = if self.diagnostics_max_severity == DiagnosticSeverity::Off {
19560            EditorSettings::get_global(cx)
19561                .diagnostics_max_severity
19562                .filter(|severity| severity != &DiagnosticSeverity::Off)
19563                .unwrap_or(DiagnosticSeverity::Hint)
19564        } else {
19565            DiagnosticSeverity::Off
19566        };
19567        self.set_max_diagnostics_severity(new_severity, cx);
19568        if self.diagnostics_max_severity == DiagnosticSeverity::Off {
19569            self.active_diagnostics = ActiveDiagnostic::None;
19570            self.inline_diagnostics_update = Task::ready(());
19571            self.inline_diagnostics.clear();
19572        } else {
19573            self.refresh_inline_diagnostics(false, window, cx);
19574        }
19575
19576        cx.notify();
19577    }
19578
19579    pub fn toggle_minimap(
19580        &mut self,
19581        _: &ToggleMinimap,
19582        window: &mut Window,
19583        cx: &mut Context<Editor>,
19584    ) {
19585        if self.supports_minimap(cx) {
19586            self.set_minimap_visibility(self.minimap_visibility.toggle_visibility(), window, cx);
19587        }
19588    }
19589
19590    fn refresh_inline_diagnostics(
19591        &mut self,
19592        debounce: bool,
19593        window: &mut Window,
19594        cx: &mut Context<Self>,
19595    ) {
19596        let max_severity = ProjectSettings::get_global(cx)
19597            .diagnostics
19598            .inline
19599            .max_severity
19600            .unwrap_or(self.diagnostics_max_severity);
19601
19602        if !self.inline_diagnostics_enabled()
19603            || !self.diagnostics_enabled()
19604            || !self.show_inline_diagnostics
19605            || max_severity == DiagnosticSeverity::Off
19606        {
19607            self.inline_diagnostics_update = Task::ready(());
19608            self.inline_diagnostics.clear();
19609            return;
19610        }
19611
19612        let debounce_ms = ProjectSettings::get_global(cx)
19613            .diagnostics
19614            .inline
19615            .update_debounce_ms;
19616        let debounce = if debounce && debounce_ms > 0 {
19617            Some(Duration::from_millis(debounce_ms))
19618        } else {
19619            None
19620        };
19621        self.inline_diagnostics_update = cx.spawn_in(window, async move |editor, cx| {
19622            if let Some(debounce) = debounce {
19623                cx.background_executor().timer(debounce).await;
19624            }
19625            let Some(snapshot) = editor.upgrade().map(|editor| {
19626                editor.update(cx, |editor, cx| editor.buffer().read(cx).snapshot(cx))
19627            }) else {
19628                return;
19629            };
19630
19631            let new_inline_diagnostics = cx
19632                .background_spawn(async move {
19633                    let mut inline_diagnostics = Vec::<(Anchor, InlineDiagnostic)>::new();
19634                    for diagnostic_entry in
19635                        snapshot.diagnostics_in_range(MultiBufferOffset(0)..snapshot.len())
19636                    {
19637                        let message = diagnostic_entry
19638                            .diagnostic
19639                            .message
19640                            .split_once('\n')
19641                            .map(|(line, _)| line)
19642                            .map(SharedString::new)
19643                            .unwrap_or_else(|| {
19644                                SharedString::new(&*diagnostic_entry.diagnostic.message)
19645                            });
19646                        let start_anchor = snapshot.anchor_before(diagnostic_entry.range.start);
19647                        let (Ok(i) | Err(i)) = inline_diagnostics
19648                            .binary_search_by(|(probe, _)| probe.cmp(&start_anchor, &snapshot));
19649                        inline_diagnostics.insert(
19650                            i,
19651                            (
19652                                start_anchor,
19653                                InlineDiagnostic {
19654                                    message,
19655                                    group_id: diagnostic_entry.diagnostic.group_id,
19656                                    start: diagnostic_entry.range.start.to_point(&snapshot),
19657                                    is_primary: diagnostic_entry.diagnostic.is_primary,
19658                                    severity: diagnostic_entry.diagnostic.severity,
19659                                },
19660                            ),
19661                        );
19662                    }
19663                    inline_diagnostics
19664                })
19665                .await;
19666
19667            editor
19668                .update(cx, |editor, cx| {
19669                    editor.inline_diagnostics = new_inline_diagnostics;
19670                    cx.notify();
19671                })
19672                .ok();
19673        });
19674    }
19675
19676    fn pull_diagnostics(
19677        &mut self,
19678        buffer_id: BufferId,
19679        _window: &Window,
19680        cx: &mut Context<Self>,
19681    ) -> Option<()> {
19682        // `ActiveDiagnostic::All` is a special mode where editor's diagnostics are managed by the external view,
19683        // skip any LSP updates for it.
19684
19685        if self.active_diagnostics == ActiveDiagnostic::All
19686            || !self.mode().is_full()
19687            || !self.diagnostics_enabled()
19688        {
19689            return None;
19690        }
19691        let pull_diagnostics_settings = ProjectSettings::get_global(cx)
19692            .diagnostics
19693            .lsp_pull_diagnostics;
19694        if !pull_diagnostics_settings.enabled {
19695            return None;
19696        }
19697        let debounce = Duration::from_millis(pull_diagnostics_settings.debounce_ms);
19698        let project = self.project()?.downgrade();
19699        let buffer = self.buffer().read(cx).buffer(buffer_id)?;
19700
19701        self.pull_diagnostics_task = cx.spawn(async move |_, cx| {
19702            cx.background_executor().timer(debounce).await;
19703            if let Ok(task) = project.update(cx, |project, cx| {
19704                project.lsp_store().update(cx, |lsp_store, cx| {
19705                    lsp_store.pull_diagnostics_for_buffer(buffer, cx)
19706                })
19707            }) {
19708                task.await.log_err();
19709            }
19710            project
19711                .update(cx, |project, cx| {
19712                    project.lsp_store().update(cx, |lsp_store, cx| {
19713                        lsp_store.pull_document_diagnostics_for_buffer_edit(buffer_id, cx);
19714                    })
19715                })
19716                .log_err();
19717        });
19718
19719        Some(())
19720    }
19721
19722    pub fn set_selections_from_remote(
19723        &mut self,
19724        selections: Vec<Selection<Anchor>>,
19725        pending_selection: Option<Selection<Anchor>>,
19726        window: &mut Window,
19727        cx: &mut Context<Self>,
19728    ) {
19729        let old_cursor_position = self.selections.newest_anchor().head();
19730        self.selections
19731            .change_with(&self.display_snapshot(cx), |s| {
19732                s.select_anchors(selections);
19733                if let Some(pending_selection) = pending_selection {
19734                    s.set_pending(pending_selection, SelectMode::Character);
19735                } else {
19736                    s.clear_pending();
19737                }
19738            });
19739        self.selections_did_change(
19740            false,
19741            &old_cursor_position,
19742            SelectionEffects::default(),
19743            window,
19744            cx,
19745        );
19746    }
19747
19748    pub fn transact(
19749        &mut self,
19750        window: &mut Window,
19751        cx: &mut Context<Self>,
19752        update: impl FnOnce(&mut Self, &mut Window, &mut Context<Self>),
19753    ) -> Option<TransactionId> {
19754        self.with_selection_effects_deferred(window, cx, |this, window, cx| {
19755            this.start_transaction_at(Instant::now(), window, cx);
19756            update(this, window, cx);
19757            this.end_transaction_at(Instant::now(), cx)
19758        })
19759    }
19760
19761    pub fn start_transaction_at(
19762        &mut self,
19763        now: Instant,
19764        window: &mut Window,
19765        cx: &mut Context<Self>,
19766    ) -> Option<TransactionId> {
19767        self.end_selection(window, cx);
19768        if let Some(tx_id) = self
19769            .buffer
19770            .update(cx, |buffer, cx| buffer.start_transaction_at(now, cx))
19771        {
19772            self.selection_history
19773                .insert_transaction(tx_id, self.selections.disjoint_anchors_arc());
19774            cx.emit(EditorEvent::TransactionBegun {
19775                transaction_id: tx_id,
19776            });
19777            Some(tx_id)
19778        } else {
19779            None
19780        }
19781    }
19782
19783    pub fn end_transaction_at(
19784        &mut self,
19785        now: Instant,
19786        cx: &mut Context<Self>,
19787    ) -> Option<TransactionId> {
19788        if let Some(transaction_id) = self
19789            .buffer
19790            .update(cx, |buffer, cx| buffer.end_transaction_at(now, cx))
19791        {
19792            if let Some((_, end_selections)) =
19793                self.selection_history.transaction_mut(transaction_id)
19794            {
19795                *end_selections = Some(self.selections.disjoint_anchors_arc());
19796            } else {
19797                log::error!("unexpectedly ended a transaction that wasn't started by this editor");
19798            }
19799
19800            cx.emit(EditorEvent::Edited { transaction_id });
19801            Some(transaction_id)
19802        } else {
19803            None
19804        }
19805    }
19806
19807    pub fn modify_transaction_selection_history(
19808        &mut self,
19809        transaction_id: TransactionId,
19810        modify: impl FnOnce(&mut (Arc<[Selection<Anchor>]>, Option<Arc<[Selection<Anchor>]>>)),
19811    ) -> bool {
19812        self.selection_history
19813            .transaction_mut(transaction_id)
19814            .map(modify)
19815            .is_some()
19816    }
19817
19818    pub fn set_mark(&mut self, _: &actions::SetMark, window: &mut Window, cx: &mut Context<Self>) {
19819        if self.selection_mark_mode {
19820            self.change_selections(SelectionEffects::no_scroll(), window, cx, |s| {
19821                s.move_with(&mut |_, sel| {
19822                    sel.collapse_to(sel.head(), SelectionGoal::None);
19823                });
19824            })
19825        }
19826        self.selection_mark_mode = true;
19827        cx.notify();
19828    }
19829
19830    pub fn swap_selection_ends(
19831        &mut self,
19832        _: &actions::SwapSelectionEnds,
19833        window: &mut Window,
19834        cx: &mut Context<Self>,
19835    ) {
19836        self.change_selections(SelectionEffects::no_scroll(), window, cx, |s| {
19837            s.move_with(&mut |_, sel| {
19838                if sel.start != sel.end {
19839                    sel.reversed = !sel.reversed
19840                }
19841            });
19842        });
19843        self.request_autoscroll(Autoscroll::newest(), cx);
19844        cx.notify();
19845    }
19846
19847    pub fn toggle_focus(
19848        workspace: &mut Workspace,
19849        _: &actions::ToggleFocus,
19850        window: &mut Window,
19851        cx: &mut Context<Workspace>,
19852    ) {
19853        let Some(item) = workspace.recent_active_item_by_type::<Self>(cx) else {
19854            return;
19855        };
19856        workspace.activate_item(&item, true, true, window, cx);
19857    }
19858
19859    pub fn toggle_fold(
19860        &mut self,
19861        _: &actions::ToggleFold,
19862        window: &mut Window,
19863        cx: &mut Context<Self>,
19864    ) {
19865        if self.buffer_kind(cx) == ItemBufferKind::Singleton {
19866            let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
19867            let selection = self.selections.newest::<Point>(&display_map);
19868
19869            let range = if selection.is_empty() {
19870                let point = selection.head().to_display_point(&display_map);
19871                let start = DisplayPoint::new(point.row(), 0).to_point(&display_map);
19872                let end = DisplayPoint::new(point.row(), display_map.line_len(point.row()))
19873                    .to_point(&display_map);
19874                start..end
19875            } else {
19876                selection.range()
19877            };
19878            if display_map.folds_in_range(range).next().is_some() {
19879                self.unfold_lines(&Default::default(), window, cx)
19880            } else {
19881                self.fold(&Default::default(), window, cx)
19882            }
19883        } else {
19884            let multi_buffer_snapshot = self.buffer.read(cx).snapshot(cx);
19885            let buffer_ids: HashSet<_> = self
19886                .selections
19887                .disjoint_anchor_ranges()
19888                .flat_map(|range| multi_buffer_snapshot.buffer_ids_for_range(range))
19889                .collect();
19890
19891            let should_unfold = buffer_ids
19892                .iter()
19893                .any(|buffer_id| self.is_buffer_folded(*buffer_id, cx));
19894
19895            for buffer_id in buffer_ids {
19896                if should_unfold {
19897                    self.unfold_buffer(buffer_id, cx);
19898                } else {
19899                    self.fold_buffer(buffer_id, cx);
19900                }
19901            }
19902        }
19903    }
19904
19905    pub fn toggle_fold_recursive(
19906        &mut self,
19907        _: &actions::ToggleFoldRecursive,
19908        window: &mut Window,
19909        cx: &mut Context<Self>,
19910    ) {
19911        let selection = self.selections.newest::<Point>(&self.display_snapshot(cx));
19912
19913        let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
19914        let range = if selection.is_empty() {
19915            let point = selection.head().to_display_point(&display_map);
19916            let start = DisplayPoint::new(point.row(), 0).to_point(&display_map);
19917            let end = DisplayPoint::new(point.row(), display_map.line_len(point.row()))
19918                .to_point(&display_map);
19919            start..end
19920        } else {
19921            selection.range()
19922        };
19923        if display_map.folds_in_range(range).next().is_some() {
19924            self.unfold_recursive(&Default::default(), window, cx)
19925        } else {
19926            self.fold_recursive(&Default::default(), window, cx)
19927        }
19928    }
19929
19930    pub fn fold(&mut self, _: &actions::Fold, window: &mut Window, cx: &mut Context<Self>) {
19931        if self.buffer_kind(cx) == ItemBufferKind::Singleton {
19932            let mut to_fold = Vec::new();
19933            let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
19934            let selections = self.selections.all_adjusted(&display_map);
19935
19936            for selection in selections {
19937                let range = selection.range().sorted();
19938                let buffer_start_row = range.start.row;
19939
19940                if range.start.row != range.end.row {
19941                    let mut found = false;
19942                    let mut row = range.start.row;
19943                    while row <= range.end.row {
19944                        if let Some(crease) = display_map.crease_for_buffer_row(MultiBufferRow(row))
19945                        {
19946                            found = true;
19947                            row = crease.range().end.row + 1;
19948                            to_fold.push(crease);
19949                        } else {
19950                            row += 1
19951                        }
19952                    }
19953                    if found {
19954                        continue;
19955                    }
19956                }
19957
19958                for row in (0..=range.start.row).rev() {
19959                    if let Some(crease) = display_map.crease_for_buffer_row(MultiBufferRow(row))
19960                        && crease.range().end.row >= buffer_start_row
19961                    {
19962                        to_fold.push(crease);
19963                        if row <= range.start.row {
19964                            break;
19965                        }
19966                    }
19967                }
19968            }
19969
19970            self.fold_creases(to_fold, true, window, cx);
19971        } else {
19972            let multi_buffer_snapshot = self.buffer.read(cx).snapshot(cx);
19973            let buffer_ids = self
19974                .selections
19975                .disjoint_anchor_ranges()
19976                .flat_map(|range| multi_buffer_snapshot.buffer_ids_for_range(range))
19977                .collect::<HashSet<_>>();
19978            for buffer_id in buffer_ids {
19979                self.fold_buffer(buffer_id, cx);
19980            }
19981        }
19982    }
19983
19984    pub fn toggle_fold_all(
19985        &mut self,
19986        _: &actions::ToggleFoldAll,
19987        window: &mut Window,
19988        cx: &mut Context<Self>,
19989    ) {
19990        let has_folds = if self.buffer.read(cx).is_singleton() {
19991            let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
19992            let has_folds = display_map
19993                .folds_in_range(MultiBufferOffset(0)..display_map.buffer_snapshot().len())
19994                .next()
19995                .is_some();
19996            has_folds
19997        } else {
19998            let buffer_ids = self.buffer.read(cx).excerpt_buffer_ids();
19999            let has_folds = buffer_ids
20000                .iter()
20001                .any(|buffer_id| self.is_buffer_folded(*buffer_id, cx));
20002            has_folds
20003        };
20004
20005        if has_folds {
20006            self.unfold_all(&actions::UnfoldAll, window, cx);
20007        } else {
20008            self.fold_all(&actions::FoldAll, window, cx);
20009        }
20010    }
20011
20012    fn fold_at_level(
20013        &mut self,
20014        fold_at: &FoldAtLevel,
20015        window: &mut Window,
20016        cx: &mut Context<Self>,
20017    ) {
20018        if !self.buffer.read(cx).is_singleton() {
20019            return;
20020        }
20021
20022        let fold_at_level = fold_at.0;
20023        let snapshot = self.buffer.read(cx).snapshot(cx);
20024        let mut to_fold = Vec::new();
20025        let mut stack = vec![(0, snapshot.max_row().0, 1)];
20026
20027        let row_ranges_to_keep: Vec<Range<u32>> = self
20028            .selections
20029            .all::<Point>(&self.display_snapshot(cx))
20030            .into_iter()
20031            .map(|sel| sel.start.row..sel.end.row)
20032            .collect();
20033
20034        while let Some((mut start_row, end_row, current_level)) = stack.pop() {
20035            while start_row < end_row {
20036                match self
20037                    .snapshot(window, cx)
20038                    .crease_for_buffer_row(MultiBufferRow(start_row))
20039                {
20040                    Some(crease) => {
20041                        let nested_start_row = crease.range().start.row + 1;
20042                        let nested_end_row = crease.range().end.row;
20043
20044                        if current_level < fold_at_level {
20045                            stack.push((nested_start_row, nested_end_row, current_level + 1));
20046                        } else if current_level == fold_at_level {
20047                            // Fold iff there is no selection completely contained within the fold region
20048                            if !row_ranges_to_keep.iter().any(|selection| {
20049                                selection.end >= nested_start_row
20050                                    && selection.start <= nested_end_row
20051                            }) {
20052                                to_fold.push(crease);
20053                            }
20054                        }
20055
20056                        start_row = nested_end_row + 1;
20057                    }
20058                    None => start_row += 1,
20059                }
20060            }
20061        }
20062
20063        self.fold_creases(to_fold, true, window, cx);
20064    }
20065
20066    pub fn fold_at_level_1(
20067        &mut self,
20068        _: &actions::FoldAtLevel1,
20069        window: &mut Window,
20070        cx: &mut Context<Self>,
20071    ) {
20072        self.fold_at_level(&actions::FoldAtLevel(1), window, cx);
20073    }
20074
20075    pub fn fold_at_level_2(
20076        &mut self,
20077        _: &actions::FoldAtLevel2,
20078        window: &mut Window,
20079        cx: &mut Context<Self>,
20080    ) {
20081        self.fold_at_level(&actions::FoldAtLevel(2), window, cx);
20082    }
20083
20084    pub fn fold_at_level_3(
20085        &mut self,
20086        _: &actions::FoldAtLevel3,
20087        window: &mut Window,
20088        cx: &mut Context<Self>,
20089    ) {
20090        self.fold_at_level(&actions::FoldAtLevel(3), window, cx);
20091    }
20092
20093    pub fn fold_at_level_4(
20094        &mut self,
20095        _: &actions::FoldAtLevel4,
20096        window: &mut Window,
20097        cx: &mut Context<Self>,
20098    ) {
20099        self.fold_at_level(&actions::FoldAtLevel(4), window, cx);
20100    }
20101
20102    pub fn fold_at_level_5(
20103        &mut self,
20104        _: &actions::FoldAtLevel5,
20105        window: &mut Window,
20106        cx: &mut Context<Self>,
20107    ) {
20108        self.fold_at_level(&actions::FoldAtLevel(5), window, cx);
20109    }
20110
20111    pub fn fold_at_level_6(
20112        &mut self,
20113        _: &actions::FoldAtLevel6,
20114        window: &mut Window,
20115        cx: &mut Context<Self>,
20116    ) {
20117        self.fold_at_level(&actions::FoldAtLevel(6), window, cx);
20118    }
20119
20120    pub fn fold_at_level_7(
20121        &mut self,
20122        _: &actions::FoldAtLevel7,
20123        window: &mut Window,
20124        cx: &mut Context<Self>,
20125    ) {
20126        self.fold_at_level(&actions::FoldAtLevel(7), window, cx);
20127    }
20128
20129    pub fn fold_at_level_8(
20130        &mut self,
20131        _: &actions::FoldAtLevel8,
20132        window: &mut Window,
20133        cx: &mut Context<Self>,
20134    ) {
20135        self.fold_at_level(&actions::FoldAtLevel(8), window, cx);
20136    }
20137
20138    pub fn fold_at_level_9(
20139        &mut self,
20140        _: &actions::FoldAtLevel9,
20141        window: &mut Window,
20142        cx: &mut Context<Self>,
20143    ) {
20144        self.fold_at_level(&actions::FoldAtLevel(9), window, cx);
20145    }
20146
20147    pub fn fold_all(&mut self, _: &actions::FoldAll, window: &mut Window, cx: &mut Context<Self>) {
20148        if self.buffer.read(cx).is_singleton() {
20149            let mut fold_ranges = Vec::new();
20150            let snapshot = self.buffer.read(cx).snapshot(cx);
20151
20152            for row in 0..snapshot.max_row().0 {
20153                if let Some(foldable_range) = self
20154                    .snapshot(window, cx)
20155                    .crease_for_buffer_row(MultiBufferRow(row))
20156                {
20157                    fold_ranges.push(foldable_range);
20158                }
20159            }
20160
20161            self.fold_creases(fold_ranges, true, window, cx);
20162        } else {
20163            self.toggle_fold_multiple_buffers = cx.spawn_in(window, async move |editor, cx| {
20164                editor
20165                    .update_in(cx, |editor, _, cx| {
20166                        for buffer_id in editor.buffer.read(cx).excerpt_buffer_ids() {
20167                            editor.fold_buffer(buffer_id, cx);
20168                        }
20169                    })
20170                    .ok();
20171            });
20172        }
20173    }
20174
20175    pub fn fold_function_bodies(
20176        &mut self,
20177        _: &actions::FoldFunctionBodies,
20178        window: &mut Window,
20179        cx: &mut Context<Self>,
20180    ) {
20181        let snapshot = self.buffer.read(cx).snapshot(cx);
20182
20183        let ranges = snapshot
20184            .text_object_ranges(
20185                MultiBufferOffset(0)..snapshot.len(),
20186                TreeSitterOptions::default(),
20187            )
20188            .filter_map(|(range, obj)| (obj == TextObject::InsideFunction).then_some(range))
20189            .collect::<Vec<_>>();
20190
20191        let creases = ranges
20192            .into_iter()
20193            .map(|range| Crease::simple(range, self.display_map.read(cx).fold_placeholder.clone()))
20194            .collect();
20195
20196        self.fold_creases(creases, true, window, cx);
20197    }
20198
20199    pub fn fold_recursive(
20200        &mut self,
20201        _: &actions::FoldRecursive,
20202        window: &mut Window,
20203        cx: &mut Context<Self>,
20204    ) {
20205        let mut to_fold = Vec::new();
20206        let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
20207        let selections = self.selections.all_adjusted(&display_map);
20208
20209        for selection in selections {
20210            let range = selection.range().sorted();
20211            let buffer_start_row = range.start.row;
20212
20213            if range.start.row != range.end.row {
20214                let mut found = false;
20215                for row in range.start.row..=range.end.row {
20216                    if let Some(crease) = display_map.crease_for_buffer_row(MultiBufferRow(row)) {
20217                        found = true;
20218                        to_fold.push(crease);
20219                    }
20220                }
20221                if found {
20222                    continue;
20223                }
20224            }
20225
20226            for row in (0..=range.start.row).rev() {
20227                if let Some(crease) = display_map.crease_for_buffer_row(MultiBufferRow(row)) {
20228                    if crease.range().end.row >= buffer_start_row {
20229                        to_fold.push(crease);
20230                    } else {
20231                        break;
20232                    }
20233                }
20234            }
20235        }
20236
20237        self.fold_creases(to_fold, true, window, cx);
20238    }
20239
20240    pub fn fold_at(
20241        &mut self,
20242        buffer_row: MultiBufferRow,
20243        window: &mut Window,
20244        cx: &mut Context<Self>,
20245    ) {
20246        let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
20247
20248        if let Some(crease) = display_map.crease_for_buffer_row(buffer_row) {
20249            let autoscroll = self
20250                .selections
20251                .all::<Point>(&display_map)
20252                .iter()
20253                .any(|selection| crease.range().overlaps(&selection.range()));
20254
20255            self.fold_creases(vec![crease], autoscroll, window, cx);
20256        }
20257    }
20258
20259    pub fn unfold_lines(&mut self, _: &UnfoldLines, _window: &mut Window, cx: &mut Context<Self>) {
20260        if self.buffer_kind(cx) == ItemBufferKind::Singleton {
20261            let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
20262            let buffer = display_map.buffer_snapshot();
20263            let selections = self.selections.all::<Point>(&display_map);
20264            let ranges = selections
20265                .iter()
20266                .map(|s| {
20267                    let range = s.display_range(&display_map).sorted();
20268                    let mut start = range.start.to_point(&display_map);
20269                    let mut end = range.end.to_point(&display_map);
20270                    start.column = 0;
20271                    end.column = buffer.line_len(MultiBufferRow(end.row));
20272                    start..end
20273                })
20274                .collect::<Vec<_>>();
20275
20276            self.unfold_ranges(&ranges, true, true, cx);
20277        } else {
20278            let multi_buffer_snapshot = self.buffer.read(cx).snapshot(cx);
20279            let buffer_ids = self
20280                .selections
20281                .disjoint_anchor_ranges()
20282                .flat_map(|range| multi_buffer_snapshot.buffer_ids_for_range(range))
20283                .collect::<HashSet<_>>();
20284            for buffer_id in buffer_ids {
20285                self.unfold_buffer(buffer_id, cx);
20286            }
20287        }
20288    }
20289
20290    pub fn unfold_recursive(
20291        &mut self,
20292        _: &UnfoldRecursive,
20293        _window: &mut Window,
20294        cx: &mut Context<Self>,
20295    ) {
20296        let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
20297        let selections = self.selections.all::<Point>(&display_map);
20298        let ranges = selections
20299            .iter()
20300            .map(|s| {
20301                let mut range = s.display_range(&display_map).sorted();
20302                *range.start.column_mut() = 0;
20303                *range.end.column_mut() = display_map.line_len(range.end.row());
20304                let start = range.start.to_point(&display_map);
20305                let end = range.end.to_point(&display_map);
20306                start..end
20307            })
20308            .collect::<Vec<_>>();
20309
20310        self.unfold_ranges(&ranges, true, true, cx);
20311    }
20312
20313    pub fn unfold_at(
20314        &mut self,
20315        buffer_row: MultiBufferRow,
20316        _window: &mut Window,
20317        cx: &mut Context<Self>,
20318    ) {
20319        let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
20320
20321        let intersection_range = Point::new(buffer_row.0, 0)
20322            ..Point::new(
20323                buffer_row.0,
20324                display_map.buffer_snapshot().line_len(buffer_row),
20325            );
20326
20327        let autoscroll = self
20328            .selections
20329            .all::<Point>(&display_map)
20330            .iter()
20331            .any(|selection| RangeExt::overlaps(&selection.range(), &intersection_range));
20332
20333        self.unfold_ranges(&[intersection_range], true, autoscroll, cx);
20334    }
20335
20336    pub fn unfold_all(
20337        &mut self,
20338        _: &actions::UnfoldAll,
20339        _window: &mut Window,
20340        cx: &mut Context<Self>,
20341    ) {
20342        if self.buffer.read(cx).is_singleton() {
20343            let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
20344            self.unfold_ranges(
20345                &[MultiBufferOffset(0)..display_map.buffer_snapshot().len()],
20346                true,
20347                true,
20348                cx,
20349            );
20350        } else {
20351            self.toggle_fold_multiple_buffers = cx.spawn(async move |editor, cx| {
20352                editor
20353                    .update(cx, |editor, cx| {
20354                        for buffer_id in editor.buffer.read(cx).excerpt_buffer_ids() {
20355                            editor.unfold_buffer(buffer_id, cx);
20356                        }
20357                    })
20358                    .ok();
20359            });
20360        }
20361    }
20362
20363    pub fn fold_selected_ranges(
20364        &mut self,
20365        _: &FoldSelectedRanges,
20366        window: &mut Window,
20367        cx: &mut Context<Self>,
20368    ) {
20369        let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
20370        let selections = self.selections.all_adjusted(&display_map);
20371        let ranges = selections
20372            .into_iter()
20373            .map(|s| Crease::simple(s.range(), display_map.fold_placeholder.clone()))
20374            .collect::<Vec<_>>();
20375        self.fold_creases(ranges, true, window, cx);
20376    }
20377
20378    pub fn fold_ranges<T: ToOffset + Clone>(
20379        &mut self,
20380        ranges: Vec<Range<T>>,
20381        auto_scroll: bool,
20382        window: &mut Window,
20383        cx: &mut Context<Self>,
20384    ) {
20385        let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
20386        let ranges = ranges
20387            .into_iter()
20388            .map(|r| Crease::simple(r, display_map.fold_placeholder.clone()))
20389            .collect::<Vec<_>>();
20390        self.fold_creases(ranges, auto_scroll, window, cx);
20391    }
20392
20393    pub fn fold_creases<T: ToOffset + Clone>(
20394        &mut self,
20395        creases: Vec<Crease<T>>,
20396        auto_scroll: bool,
20397        _window: &mut Window,
20398        cx: &mut Context<Self>,
20399    ) {
20400        if creases.is_empty() {
20401            return;
20402        }
20403
20404        self.display_map.update(cx, |map, cx| map.fold(creases, cx));
20405
20406        if auto_scroll {
20407            self.request_autoscroll(Autoscroll::fit(), cx);
20408        }
20409
20410        cx.notify();
20411
20412        self.scrollbar_marker_state.dirty = true;
20413        self.folds_did_change(cx);
20414    }
20415
20416    /// Removes any folds whose ranges intersect any of the given ranges.
20417    pub fn unfold_ranges<T: ToOffset + Clone>(
20418        &mut self,
20419        ranges: &[Range<T>],
20420        inclusive: bool,
20421        auto_scroll: bool,
20422        cx: &mut Context<Self>,
20423    ) {
20424        self.remove_folds_with(ranges, auto_scroll, cx, |map, cx| {
20425            map.unfold_intersecting(ranges.iter().cloned(), inclusive, cx);
20426        });
20427        self.folds_did_change(cx);
20428    }
20429
20430    pub fn fold_buffer(&mut self, buffer_id: BufferId, cx: &mut Context<Self>) {
20431        self.fold_buffers([buffer_id], cx);
20432    }
20433
20434    pub fn fold_buffers(
20435        &mut self,
20436        buffer_ids: impl IntoIterator<Item = BufferId>,
20437        cx: &mut Context<Self>,
20438    ) {
20439        if self.buffer().read(cx).is_singleton() {
20440            return;
20441        }
20442
20443        let ids_to_fold: Vec<BufferId> = buffer_ids
20444            .into_iter()
20445            .filter(|id| !self.is_buffer_folded(*id, cx))
20446            .collect();
20447
20448        if ids_to_fold.is_empty() {
20449            return;
20450        }
20451
20452        let mut all_folded_excerpt_ids = Vec::new();
20453        for buffer_id in &ids_to_fold {
20454            let folded_excerpts = self.buffer().read(cx).excerpts_for_buffer(*buffer_id, cx);
20455            all_folded_excerpt_ids.extend(folded_excerpts.into_iter().map(|(id, _)| id));
20456        }
20457
20458        self.display_map.update(cx, |display_map, cx| {
20459            display_map.fold_buffers(ids_to_fold.clone(), cx)
20460        });
20461
20462        let snapshot = self.display_snapshot(cx);
20463        self.selections.change_with(&snapshot, |selections| {
20464            for buffer_id in ids_to_fold {
20465                selections.remove_selections_from_buffer(buffer_id);
20466            }
20467        });
20468
20469        cx.emit(EditorEvent::BufferFoldToggled {
20470            ids: all_folded_excerpt_ids,
20471            folded: true,
20472        });
20473        cx.notify();
20474    }
20475
20476    pub fn unfold_buffer(&mut self, buffer_id: BufferId, cx: &mut Context<Self>) {
20477        if self.buffer().read(cx).is_singleton() || !self.is_buffer_folded(buffer_id, cx) {
20478            return;
20479        }
20480        let unfolded_excerpts = self.buffer().read(cx).excerpts_for_buffer(buffer_id, cx);
20481        self.display_map.update(cx, |display_map, cx| {
20482            display_map.unfold_buffers([buffer_id], cx);
20483        });
20484        cx.emit(EditorEvent::BufferFoldToggled {
20485            ids: unfolded_excerpts.iter().map(|&(id, _)| id).collect(),
20486            folded: false,
20487        });
20488        cx.notify();
20489    }
20490
20491    pub fn is_buffer_folded(&self, buffer: BufferId, cx: &App) -> bool {
20492        self.display_map.read(cx).is_buffer_folded(buffer)
20493    }
20494
20495    pub fn has_any_buffer_folded(&self, cx: &App) -> bool {
20496        if self.buffer().read(cx).is_singleton() {
20497            return false;
20498        }
20499        !self.folded_buffers(cx).is_empty()
20500    }
20501
20502    pub fn folded_buffers<'a>(&self, cx: &'a App) -> &'a HashSet<BufferId> {
20503        self.display_map.read(cx).folded_buffers()
20504    }
20505
20506    pub fn disable_header_for_buffer(&mut self, buffer_id: BufferId, cx: &mut Context<Self>) {
20507        self.display_map.update(cx, |display_map, cx| {
20508            display_map.disable_header_for_buffer(buffer_id, cx);
20509        });
20510        cx.notify();
20511    }
20512
20513    /// Removes any folds with the given ranges.
20514    pub fn remove_folds_with_type<T: ToOffset + Clone>(
20515        &mut self,
20516        ranges: &[Range<T>],
20517        type_id: TypeId,
20518        auto_scroll: bool,
20519        cx: &mut Context<Self>,
20520    ) {
20521        self.remove_folds_with(ranges, auto_scroll, cx, |map, cx| {
20522            map.remove_folds_with_type(ranges.iter().cloned(), type_id, cx)
20523        });
20524        self.folds_did_change(cx);
20525    }
20526
20527    fn remove_folds_with<T: ToOffset + Clone>(
20528        &mut self,
20529        ranges: &[Range<T>],
20530        auto_scroll: bool,
20531        cx: &mut Context<Self>,
20532        update: impl FnOnce(&mut DisplayMap, &mut Context<DisplayMap>),
20533    ) {
20534        if ranges.is_empty() {
20535            return;
20536        }
20537
20538        let mut buffers_affected = HashSet::default();
20539        let multi_buffer = self.buffer().read(cx);
20540        for range in ranges {
20541            if let Some((_, buffer, _)) = multi_buffer.excerpt_containing(range.start.clone(), cx) {
20542                buffers_affected.insert(buffer.read(cx).remote_id());
20543            };
20544        }
20545
20546        self.display_map.update(cx, update);
20547
20548        if auto_scroll {
20549            self.request_autoscroll(Autoscroll::fit(), cx);
20550        }
20551
20552        cx.notify();
20553        self.scrollbar_marker_state.dirty = true;
20554        self.active_indent_guides_state.dirty = true;
20555    }
20556
20557    pub fn update_renderer_widths(
20558        &mut self,
20559        widths: impl IntoIterator<Item = (ChunkRendererId, Pixels)>,
20560        cx: &mut Context<Self>,
20561    ) -> bool {
20562        self.display_map
20563            .update(cx, |map, cx| map.update_fold_widths(widths, cx))
20564    }
20565
20566    pub fn default_fold_placeholder(&self, cx: &App) -> FoldPlaceholder {
20567        self.display_map.read(cx).fold_placeholder.clone()
20568    }
20569
20570    pub fn set_expand_all_diff_hunks(&mut self, cx: &mut App) {
20571        self.buffer.update(cx, |buffer, cx| {
20572            buffer.set_all_diff_hunks_expanded(cx);
20573        });
20574    }
20575
20576    pub fn expand_all_diff_hunks(
20577        &mut self,
20578        _: &ExpandAllDiffHunks,
20579        _window: &mut Window,
20580        cx: &mut Context<Self>,
20581    ) {
20582        self.buffer.update(cx, |buffer, cx| {
20583            buffer.expand_diff_hunks(vec![Anchor::min()..Anchor::max()], cx)
20584        });
20585    }
20586
20587    pub fn collapse_all_diff_hunks(
20588        &mut self,
20589        _: &CollapseAllDiffHunks,
20590        _window: &mut Window,
20591        cx: &mut Context<Self>,
20592    ) {
20593        self.buffer.update(cx, |buffer, cx| {
20594            buffer.collapse_diff_hunks(vec![Anchor::min()..Anchor::max()], cx)
20595        });
20596    }
20597
20598    pub fn toggle_selected_diff_hunks(
20599        &mut self,
20600        _: &ToggleSelectedDiffHunks,
20601        _window: &mut Window,
20602        cx: &mut Context<Self>,
20603    ) {
20604        let ranges: Vec<_> = self
20605            .selections
20606            .disjoint_anchors()
20607            .iter()
20608            .map(|s| s.range())
20609            .collect();
20610        self.toggle_diff_hunks_in_ranges(ranges, cx);
20611    }
20612
20613    pub fn diff_hunks_in_ranges<'a>(
20614        &'a self,
20615        ranges: &'a [Range<Anchor>],
20616        buffer: &'a MultiBufferSnapshot,
20617    ) -> impl 'a + Iterator<Item = MultiBufferDiffHunk> {
20618        ranges.iter().flat_map(move |range| {
20619            let end_excerpt_id = range.end.excerpt_id;
20620            let range = range.to_point(buffer);
20621            let mut peek_end = range.end;
20622            if range.end.row < buffer.max_row().0 {
20623                peek_end = Point::new(range.end.row + 1, 0);
20624            }
20625            buffer
20626                .diff_hunks_in_range(range.start..peek_end)
20627                .filter(move |hunk| hunk.excerpt_id.cmp(&end_excerpt_id, buffer).is_le())
20628        })
20629    }
20630
20631    pub fn has_stageable_diff_hunks_in_ranges(
20632        &self,
20633        ranges: &[Range<Anchor>],
20634        snapshot: &MultiBufferSnapshot,
20635    ) -> bool {
20636        let mut hunks = self.diff_hunks_in_ranges(ranges, snapshot);
20637        hunks.any(|hunk| hunk.status().has_secondary_hunk())
20638    }
20639
20640    pub fn toggle_staged_selected_diff_hunks(
20641        &mut self,
20642        _: &::git::ToggleStaged,
20643        _: &mut Window,
20644        cx: &mut Context<Self>,
20645    ) {
20646        let snapshot = self.buffer.read(cx).snapshot(cx);
20647        let ranges: Vec<_> = self
20648            .selections
20649            .disjoint_anchors()
20650            .iter()
20651            .map(|s| s.range())
20652            .collect();
20653        let stage = self.has_stageable_diff_hunks_in_ranges(&ranges, &snapshot);
20654        self.stage_or_unstage_diff_hunks(stage, ranges, cx);
20655    }
20656
20657    pub fn set_render_diff_hunk_controls(
20658        &mut self,
20659        render_diff_hunk_controls: RenderDiffHunkControlsFn,
20660        cx: &mut Context<Self>,
20661    ) {
20662        self.render_diff_hunk_controls = render_diff_hunk_controls;
20663        cx.notify();
20664    }
20665
20666    pub fn stage_and_next(
20667        &mut self,
20668        _: &::git::StageAndNext,
20669        window: &mut Window,
20670        cx: &mut Context<Self>,
20671    ) {
20672        self.do_stage_or_unstage_and_next(true, window, cx);
20673    }
20674
20675    pub fn unstage_and_next(
20676        &mut self,
20677        _: &::git::UnstageAndNext,
20678        window: &mut Window,
20679        cx: &mut Context<Self>,
20680    ) {
20681        self.do_stage_or_unstage_and_next(false, window, cx);
20682    }
20683
20684    pub fn stage_or_unstage_diff_hunks(
20685        &mut self,
20686        stage: bool,
20687        ranges: Vec<Range<Anchor>>,
20688        cx: &mut Context<Self>,
20689    ) {
20690        if self.delegate_stage_and_restore {
20691            let snapshot = self.buffer.read(cx).snapshot(cx);
20692            let hunks: Vec<_> = self.diff_hunks_in_ranges(&ranges, &snapshot).collect();
20693            if !hunks.is_empty() {
20694                cx.emit(EditorEvent::StageOrUnstageRequested { stage, hunks });
20695            }
20696            return;
20697        }
20698        let task = self.save_buffers_for_ranges_if_needed(&ranges, cx);
20699        cx.spawn(async move |this, cx| {
20700            task.await?;
20701            this.update(cx, |this, cx| {
20702                let snapshot = this.buffer.read(cx).snapshot(cx);
20703                let chunk_by = this
20704                    .diff_hunks_in_ranges(&ranges, &snapshot)
20705                    .chunk_by(|hunk| hunk.buffer_id);
20706                for (buffer_id, hunks) in &chunk_by {
20707                    this.do_stage_or_unstage(stage, buffer_id, hunks, cx);
20708                }
20709            })
20710        })
20711        .detach_and_log_err(cx);
20712    }
20713
20714    fn save_buffers_for_ranges_if_needed(
20715        &mut self,
20716        ranges: &[Range<Anchor>],
20717        cx: &mut Context<Editor>,
20718    ) -> Task<Result<()>> {
20719        let multibuffer = self.buffer.read(cx);
20720        let snapshot = multibuffer.read(cx);
20721        let buffer_ids: HashSet<_> = ranges
20722            .iter()
20723            .flat_map(|range| snapshot.buffer_ids_for_range(range.clone()))
20724            .collect();
20725        drop(snapshot);
20726
20727        let mut buffers = HashSet::default();
20728        for buffer_id in buffer_ids {
20729            if let Some(buffer_entity) = multibuffer.buffer(buffer_id) {
20730                let buffer = buffer_entity.read(cx);
20731                if buffer.file().is_some_and(|file| file.disk_state().exists()) && buffer.is_dirty()
20732                {
20733                    buffers.insert(buffer_entity);
20734                }
20735            }
20736        }
20737
20738        if let Some(project) = &self.project {
20739            project.update(cx, |project, cx| project.save_buffers(buffers, cx))
20740        } else {
20741            Task::ready(Ok(()))
20742        }
20743    }
20744
20745    fn do_stage_or_unstage_and_next(
20746        &mut self,
20747        stage: bool,
20748        window: &mut Window,
20749        cx: &mut Context<Self>,
20750    ) {
20751        let ranges = self.selections.disjoint_anchor_ranges().collect::<Vec<_>>();
20752
20753        if ranges.iter().any(|range| range.start != range.end) {
20754            self.stage_or_unstage_diff_hunks(stage, ranges, cx);
20755            return;
20756        }
20757
20758        self.stage_or_unstage_diff_hunks(stage, ranges, cx);
20759        let snapshot = self.snapshot(window, cx);
20760        let position = self
20761            .selections
20762            .newest::<Point>(&snapshot.display_snapshot)
20763            .head();
20764        let mut row = snapshot
20765            .buffer_snapshot()
20766            .diff_hunks_in_range(position..snapshot.buffer_snapshot().max_point())
20767            .find(|hunk| hunk.row_range.start.0 > position.row)
20768            .map(|hunk| hunk.row_range.start);
20769
20770        let all_diff_hunks_expanded = self.buffer().read(cx).all_diff_hunks_expanded();
20771        // Outside of the project diff editor, wrap around to the beginning.
20772        if !all_diff_hunks_expanded {
20773            row = row.or_else(|| {
20774                snapshot
20775                    .buffer_snapshot()
20776                    .diff_hunks_in_range(Point::zero()..position)
20777                    .find(|hunk| hunk.row_range.end.0 < position.row)
20778                    .map(|hunk| hunk.row_range.start)
20779            });
20780        }
20781
20782        if let Some(row) = row {
20783            let destination = Point::new(row.0, 0);
20784            let autoscroll = Autoscroll::center();
20785
20786            self.unfold_ranges(&[destination..destination], false, false, cx);
20787            self.change_selections(SelectionEffects::scroll(autoscroll), window, cx, |s| {
20788                s.select_ranges([destination..destination]);
20789            });
20790        }
20791    }
20792
20793    pub(crate) fn do_stage_or_unstage(
20794        &self,
20795        stage: bool,
20796        buffer_id: BufferId,
20797        hunks: impl Iterator<Item = MultiBufferDiffHunk>,
20798        cx: &mut App,
20799    ) -> Option<()> {
20800        let project = self.project()?;
20801        let buffer = project.read(cx).buffer_for_id(buffer_id, cx)?;
20802        let diff = self.buffer.read(cx).diff_for(buffer_id)?;
20803        let buffer_snapshot = buffer.read(cx).snapshot();
20804        let file_exists = buffer_snapshot
20805            .file()
20806            .is_some_and(|file| file.disk_state().exists());
20807        diff.update(cx, |diff, cx| {
20808            diff.stage_or_unstage_hunks(
20809                stage,
20810                &hunks
20811                    .map(|hunk| buffer_diff::DiffHunk {
20812                        buffer_range: hunk.buffer_range,
20813                        // We don't need to pass in word diffs here because they're only used for rendering and
20814                        // this function changes internal state
20815                        base_word_diffs: Vec::default(),
20816                        buffer_word_diffs: Vec::default(),
20817                        diff_base_byte_range: hunk.diff_base_byte_range.start.0
20818                            ..hunk.diff_base_byte_range.end.0,
20819                        secondary_status: hunk.status.secondary,
20820                        range: Point::zero()..Point::zero(), // unused
20821                    })
20822                    .collect::<Vec<_>>(),
20823                &buffer_snapshot,
20824                file_exists,
20825                cx,
20826            )
20827        });
20828        None
20829    }
20830
20831    pub fn expand_selected_diff_hunks(&mut self, cx: &mut Context<Self>) {
20832        let ranges: Vec<_> = self
20833            .selections
20834            .disjoint_anchors()
20835            .iter()
20836            .map(|s| s.range())
20837            .collect();
20838        self.buffer
20839            .update(cx, |buffer, cx| buffer.expand_diff_hunks(ranges, cx))
20840    }
20841
20842    pub fn clear_expanded_diff_hunks(&mut self, cx: &mut Context<Self>) -> bool {
20843        self.buffer.update(cx, |buffer, cx| {
20844            let ranges = vec![Anchor::min()..Anchor::max()];
20845            if !buffer.all_diff_hunks_expanded()
20846                && buffer.has_expanded_diff_hunks_in_ranges(&ranges, cx)
20847            {
20848                buffer.collapse_diff_hunks(ranges, cx);
20849                true
20850            } else {
20851                false
20852            }
20853        })
20854    }
20855
20856    fn has_any_expanded_diff_hunks(&self, cx: &App) -> bool {
20857        if self.buffer.read(cx).all_diff_hunks_expanded() {
20858            return true;
20859        }
20860        let ranges = vec![Anchor::min()..Anchor::max()];
20861        self.buffer
20862            .read(cx)
20863            .has_expanded_diff_hunks_in_ranges(&ranges, cx)
20864    }
20865
20866    fn toggle_diff_hunks_in_ranges(
20867        &mut self,
20868        ranges: Vec<Range<Anchor>>,
20869        cx: &mut Context<Editor>,
20870    ) {
20871        self.buffer.update(cx, |buffer, cx| {
20872            let expand = !buffer.has_expanded_diff_hunks_in_ranges(&ranges, cx);
20873            buffer.expand_or_collapse_diff_hunks(ranges, expand, cx);
20874        })
20875    }
20876
20877    fn toggle_single_diff_hunk(&mut self, range: Range<Anchor>, cx: &mut Context<Self>) {
20878        self.buffer.update(cx, |buffer, cx| {
20879            buffer.toggle_single_diff_hunk(range, cx);
20880        })
20881    }
20882
20883    pub(crate) fn apply_all_diff_hunks(
20884        &mut self,
20885        _: &ApplyAllDiffHunks,
20886        window: &mut Window,
20887        cx: &mut Context<Self>,
20888    ) {
20889        self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
20890
20891        let buffers = self.buffer.read(cx).all_buffers();
20892        for branch_buffer in buffers {
20893            branch_buffer.update(cx, |branch_buffer, cx| {
20894                branch_buffer.merge_into_base(Vec::new(), cx);
20895            });
20896        }
20897
20898        if let Some(project) = self.project.clone() {
20899            self.save(
20900                SaveOptions {
20901                    format: true,
20902                    autosave: false,
20903                },
20904                project,
20905                window,
20906                cx,
20907            )
20908            .detach_and_log_err(cx);
20909        }
20910    }
20911
20912    pub(crate) fn apply_selected_diff_hunks(
20913        &mut self,
20914        _: &ApplyDiffHunk,
20915        window: &mut Window,
20916        cx: &mut Context<Self>,
20917    ) {
20918        self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
20919        let snapshot = self.snapshot(window, cx);
20920        let hunks = snapshot.hunks_for_ranges(
20921            self.selections
20922                .all(&snapshot.display_snapshot)
20923                .into_iter()
20924                .map(|selection| selection.range()),
20925        );
20926        let mut ranges_by_buffer = HashMap::default();
20927        self.transact(window, cx, |editor, _window, cx| {
20928            for hunk in hunks {
20929                if let Some(buffer) = editor.buffer.read(cx).buffer(hunk.buffer_id) {
20930                    ranges_by_buffer
20931                        .entry(buffer.clone())
20932                        .or_insert_with(Vec::new)
20933                        .push(hunk.buffer_range.to_offset(buffer.read(cx)));
20934                }
20935            }
20936
20937            for (buffer, ranges) in ranges_by_buffer {
20938                buffer.update(cx, |buffer, cx| {
20939                    buffer.merge_into_base(ranges, cx);
20940                });
20941            }
20942        });
20943
20944        if let Some(project) = self.project.clone() {
20945            self.save(
20946                SaveOptions {
20947                    format: true,
20948                    autosave: false,
20949                },
20950                project,
20951                window,
20952                cx,
20953            )
20954            .detach_and_log_err(cx);
20955        }
20956    }
20957
20958    pub fn set_gutter_hovered(&mut self, hovered: bool, cx: &mut Context<Self>) {
20959        if hovered != self.gutter_hovered {
20960            self.gutter_hovered = hovered;
20961            cx.notify();
20962        }
20963    }
20964
20965    pub fn insert_blocks(
20966        &mut self,
20967        blocks: impl IntoIterator<Item = BlockProperties<Anchor>>,
20968        autoscroll: Option<Autoscroll>,
20969        cx: &mut Context<Self>,
20970    ) -> Vec<CustomBlockId> {
20971        let blocks = self
20972            .display_map
20973            .update(cx, |display_map, cx| display_map.insert_blocks(blocks, cx));
20974        if let Some(autoscroll) = autoscroll {
20975            self.request_autoscroll(autoscroll, cx);
20976        }
20977        cx.notify();
20978        blocks
20979    }
20980
20981    pub fn resize_blocks(
20982        &mut self,
20983        heights: HashMap<CustomBlockId, u32>,
20984        autoscroll: Option<Autoscroll>,
20985        cx: &mut Context<Self>,
20986    ) {
20987        self.display_map
20988            .update(cx, |display_map, cx| display_map.resize_blocks(heights, cx));
20989        if let Some(autoscroll) = autoscroll {
20990            self.request_autoscroll(autoscroll, cx);
20991        }
20992        cx.notify();
20993    }
20994
20995    pub fn replace_blocks(
20996        &mut self,
20997        renderers: HashMap<CustomBlockId, RenderBlock>,
20998        autoscroll: Option<Autoscroll>,
20999        cx: &mut Context<Self>,
21000    ) {
21001        self.display_map
21002            .update(cx, |display_map, _cx| display_map.replace_blocks(renderers));
21003        if let Some(autoscroll) = autoscroll {
21004            self.request_autoscroll(autoscroll, cx);
21005        }
21006        cx.notify();
21007    }
21008
21009    pub fn remove_blocks(
21010        &mut self,
21011        block_ids: HashSet<CustomBlockId>,
21012        autoscroll: Option<Autoscroll>,
21013        cx: &mut Context<Self>,
21014    ) {
21015        self.display_map.update(cx, |display_map, cx| {
21016            display_map.remove_blocks(block_ids, cx)
21017        });
21018        if let Some(autoscroll) = autoscroll {
21019            self.request_autoscroll(autoscroll, cx);
21020        }
21021        cx.notify();
21022    }
21023
21024    pub fn row_for_block(
21025        &self,
21026        block_id: CustomBlockId,
21027        cx: &mut Context<Self>,
21028    ) -> Option<DisplayRow> {
21029        self.display_map
21030            .update(cx, |map, cx| map.row_for_block(block_id, cx))
21031    }
21032
21033    pub(crate) fn set_focused_block(&mut self, focused_block: FocusedBlock) {
21034        self.focused_block = Some(focused_block);
21035    }
21036
21037    pub(crate) fn take_focused_block(&mut self) -> Option<FocusedBlock> {
21038        self.focused_block.take()
21039    }
21040
21041    pub fn insert_creases(
21042        &mut self,
21043        creases: impl IntoIterator<Item = Crease<Anchor>>,
21044        cx: &mut Context<Self>,
21045    ) -> Vec<CreaseId> {
21046        self.display_map
21047            .update(cx, |map, cx| map.insert_creases(creases, cx))
21048    }
21049
21050    pub fn remove_creases(
21051        &mut self,
21052        ids: impl IntoIterator<Item = CreaseId>,
21053        cx: &mut Context<Self>,
21054    ) -> Vec<(CreaseId, Range<Anchor>)> {
21055        self.display_map
21056            .update(cx, |map, cx| map.remove_creases(ids, cx))
21057    }
21058
21059    pub fn longest_row(&self, cx: &mut App) -> DisplayRow {
21060        self.display_map
21061            .update(cx, |map, cx| map.snapshot(cx))
21062            .longest_row()
21063    }
21064
21065    pub fn max_point(&self, cx: &mut App) -> DisplayPoint {
21066        self.display_map
21067            .update(cx, |map, cx| map.snapshot(cx))
21068            .max_point()
21069    }
21070
21071    pub fn text(&self, cx: &App) -> String {
21072        self.buffer.read(cx).read(cx).text()
21073    }
21074
21075    pub fn is_empty(&self, cx: &App) -> bool {
21076        self.buffer.read(cx).read(cx).is_empty()
21077    }
21078
21079    pub fn text_option(&self, cx: &App) -> Option<String> {
21080        let text = self.text(cx);
21081        let text = text.trim();
21082
21083        if text.is_empty() {
21084            return None;
21085        }
21086
21087        Some(text.to_string())
21088    }
21089
21090    pub fn set_text(
21091        &mut self,
21092        text: impl Into<Arc<str>>,
21093        window: &mut Window,
21094        cx: &mut Context<Self>,
21095    ) {
21096        self.transact(window, cx, |this, _, cx| {
21097            this.buffer
21098                .read(cx)
21099                .as_singleton()
21100                .expect("you can only call set_text on editors for singleton buffers")
21101                .update(cx, |buffer, cx| buffer.set_text(text, cx));
21102        });
21103    }
21104
21105    pub fn display_text(&self, cx: &mut App) -> String {
21106        self.display_map
21107            .update(cx, |map, cx| map.snapshot(cx))
21108            .text()
21109    }
21110
21111    fn create_minimap(
21112        &self,
21113        minimap_settings: MinimapSettings,
21114        window: &mut Window,
21115        cx: &mut Context<Self>,
21116    ) -> Option<Entity<Self>> {
21117        (minimap_settings.minimap_enabled() && self.buffer_kind(cx) == ItemBufferKind::Singleton)
21118            .then(|| self.initialize_new_minimap(minimap_settings, window, cx))
21119    }
21120
21121    fn initialize_new_minimap(
21122        &self,
21123        minimap_settings: MinimapSettings,
21124        window: &mut Window,
21125        cx: &mut Context<Self>,
21126    ) -> Entity<Self> {
21127        const MINIMAP_FONT_WEIGHT: gpui::FontWeight = gpui::FontWeight::BLACK;
21128        const MINIMAP_FONT_FAMILY: SharedString = SharedString::new_static(".ZedMono");
21129
21130        let mut minimap = Editor::new_internal(
21131            EditorMode::Minimap {
21132                parent: cx.weak_entity(),
21133            },
21134            self.buffer.clone(),
21135            None,
21136            Some(self.display_map.clone()),
21137            window,
21138            cx,
21139        );
21140        let my_snapshot = self.display_map.update(cx, |map, cx| map.snapshot(cx));
21141        let minimap_snapshot = minimap.display_map.update(cx, |map, cx| map.snapshot(cx));
21142        minimap.scroll_manager.clone_state(
21143            &self.scroll_manager,
21144            &my_snapshot,
21145            &minimap_snapshot,
21146            cx,
21147        );
21148        minimap.set_text_style_refinement(TextStyleRefinement {
21149            font_size: Some(MINIMAP_FONT_SIZE),
21150            font_weight: Some(MINIMAP_FONT_WEIGHT),
21151            font_family: Some(MINIMAP_FONT_FAMILY),
21152            ..Default::default()
21153        });
21154        minimap.update_minimap_configuration(minimap_settings, cx);
21155        cx.new(|_| minimap)
21156    }
21157
21158    fn update_minimap_configuration(&mut self, minimap_settings: MinimapSettings, cx: &App) {
21159        let current_line_highlight = minimap_settings
21160            .current_line_highlight
21161            .unwrap_or_else(|| EditorSettings::get_global(cx).current_line_highlight);
21162        self.set_current_line_highlight(Some(current_line_highlight));
21163    }
21164
21165    pub fn minimap(&self) -> Option<&Entity<Self>> {
21166        self.minimap
21167            .as_ref()
21168            .filter(|_| self.minimap_visibility.visible())
21169    }
21170
21171    pub fn wrap_guides(&self, cx: &App) -> SmallVec<[(usize, bool); 2]> {
21172        let mut wrap_guides = smallvec![];
21173
21174        if self.show_wrap_guides == Some(false) {
21175            return wrap_guides;
21176        }
21177
21178        let settings = self.buffer.read(cx).language_settings(cx);
21179        if settings.show_wrap_guides {
21180            match self.soft_wrap_mode(cx) {
21181                SoftWrap::Column(soft_wrap) => {
21182                    wrap_guides.push((soft_wrap as usize, true));
21183                }
21184                SoftWrap::Bounded(soft_wrap) => {
21185                    wrap_guides.push((soft_wrap as usize, true));
21186                }
21187                SoftWrap::GitDiff | SoftWrap::None | SoftWrap::EditorWidth => {}
21188            }
21189            wrap_guides.extend(settings.wrap_guides.iter().map(|guide| (*guide, false)))
21190        }
21191
21192        wrap_guides
21193    }
21194
21195    pub fn soft_wrap_mode(&self, cx: &App) -> SoftWrap {
21196        let settings = self.buffer.read(cx).language_settings(cx);
21197        let mode = self.soft_wrap_mode_override.unwrap_or(settings.soft_wrap);
21198        match mode {
21199            language_settings::SoftWrap::PreferLine | language_settings::SoftWrap::None => {
21200                SoftWrap::None
21201            }
21202            language_settings::SoftWrap::EditorWidth => SoftWrap::EditorWidth,
21203            language_settings::SoftWrap::PreferredLineLength => {
21204                SoftWrap::Column(settings.preferred_line_length)
21205            }
21206            language_settings::SoftWrap::Bounded => {
21207                SoftWrap::Bounded(settings.preferred_line_length)
21208            }
21209        }
21210    }
21211
21212    pub fn set_soft_wrap_mode(
21213        &mut self,
21214        mode: language_settings::SoftWrap,
21215        cx: &mut Context<Self>,
21216    ) {
21217        self.soft_wrap_mode_override = Some(mode);
21218        cx.notify();
21219    }
21220
21221    pub fn set_hard_wrap(&mut self, hard_wrap: Option<usize>, cx: &mut Context<Self>) {
21222        self.hard_wrap = hard_wrap;
21223        cx.notify();
21224    }
21225
21226    pub fn set_text_style_refinement(&mut self, style: TextStyleRefinement) {
21227        self.text_style_refinement = Some(style);
21228    }
21229
21230    /// called by the Element so we know what style we were most recently rendered with.
21231    pub fn set_style(&mut self, style: EditorStyle, window: &mut Window, cx: &mut Context<Self>) {
21232        // We intentionally do not inform the display map about the minimap style
21233        // so that wrapping is not recalculated and stays consistent for the editor
21234        // and its linked minimap.
21235        if !self.mode.is_minimap() {
21236            let font = style.text.font();
21237            let font_size = style.text.font_size.to_pixels(window.rem_size());
21238            let display_map = self
21239                .placeholder_display_map
21240                .as_ref()
21241                .filter(|_| self.is_empty(cx))
21242                .unwrap_or(&self.display_map);
21243
21244            display_map.update(cx, |map, cx| map.set_font(font, font_size, cx));
21245        }
21246        self.style = Some(style);
21247    }
21248
21249    pub fn style(&mut self, cx: &App) -> &EditorStyle {
21250        if self.style.is_none() {
21251            self.style = Some(self.create_style(cx));
21252        }
21253        self.style.as_ref().unwrap()
21254    }
21255
21256    // Called by the element. This method is not designed to be called outside of the editor
21257    // element's layout code because it does not notify when rewrapping is computed synchronously.
21258    pub(crate) fn set_wrap_width(&self, width: Option<Pixels>, cx: &mut App) -> bool {
21259        if self.is_empty(cx) {
21260            self.placeholder_display_map
21261                .as_ref()
21262                .map_or(false, |display_map| {
21263                    display_map.update(cx, |map, cx| map.set_wrap_width(width, cx))
21264                })
21265        } else {
21266            self.display_map
21267                .update(cx, |map, cx| map.set_wrap_width(width, cx))
21268        }
21269    }
21270
21271    pub fn set_soft_wrap(&mut self) {
21272        self.soft_wrap_mode_override = Some(language_settings::SoftWrap::EditorWidth)
21273    }
21274
21275    pub fn toggle_soft_wrap(&mut self, _: &ToggleSoftWrap, _: &mut Window, cx: &mut Context<Self>) {
21276        if self.soft_wrap_mode_override.is_some() {
21277            self.soft_wrap_mode_override.take();
21278        } else {
21279            let soft_wrap = match self.soft_wrap_mode(cx) {
21280                SoftWrap::GitDiff => return,
21281                SoftWrap::None => language_settings::SoftWrap::EditorWidth,
21282                SoftWrap::EditorWidth | SoftWrap::Column(_) | SoftWrap::Bounded(_) => {
21283                    language_settings::SoftWrap::None
21284                }
21285            };
21286            self.soft_wrap_mode_override = Some(soft_wrap);
21287        }
21288        cx.notify();
21289    }
21290
21291    pub fn toggle_tab_bar(&mut self, _: &ToggleTabBar, _: &mut Window, cx: &mut Context<Self>) {
21292        let Some(workspace) = self.workspace() else {
21293            return;
21294        };
21295        let fs = workspace.read(cx).app_state().fs.clone();
21296        let current_show = TabBarSettings::get_global(cx).show;
21297        update_settings_file(fs, cx, move |setting, _| {
21298            setting.tab_bar.get_or_insert_default().show = Some(!current_show);
21299        });
21300    }
21301
21302    pub fn toggle_indent_guides(
21303        &mut self,
21304        _: &ToggleIndentGuides,
21305        _: &mut Window,
21306        cx: &mut Context<Self>,
21307    ) {
21308        let currently_enabled = self.should_show_indent_guides().unwrap_or_else(|| {
21309            self.buffer
21310                .read(cx)
21311                .language_settings(cx)
21312                .indent_guides
21313                .enabled
21314        });
21315        self.show_indent_guides = Some(!currently_enabled);
21316        cx.notify();
21317    }
21318
21319    fn should_show_indent_guides(&self) -> Option<bool> {
21320        self.show_indent_guides
21321    }
21322
21323    pub fn disable_indent_guides_for_buffer(
21324        &mut self,
21325        buffer_id: BufferId,
21326        cx: &mut Context<Self>,
21327    ) {
21328        self.buffers_with_disabled_indent_guides.insert(buffer_id);
21329        cx.notify();
21330    }
21331
21332    pub fn has_indent_guides_disabled_for_buffer(&self, buffer_id: BufferId) -> bool {
21333        self.buffers_with_disabled_indent_guides
21334            .contains(&buffer_id)
21335    }
21336
21337    pub fn toggle_line_numbers(
21338        &mut self,
21339        _: &ToggleLineNumbers,
21340        _: &mut Window,
21341        cx: &mut Context<Self>,
21342    ) {
21343        let mut editor_settings = EditorSettings::get_global(cx).clone();
21344        editor_settings.gutter.line_numbers = !editor_settings.gutter.line_numbers;
21345        EditorSettings::override_global(editor_settings, cx);
21346    }
21347
21348    pub fn line_numbers_enabled(&self, cx: &App) -> bool {
21349        if let Some(show_line_numbers) = self.show_line_numbers {
21350            return show_line_numbers;
21351        }
21352        EditorSettings::get_global(cx).gutter.line_numbers
21353    }
21354
21355    pub fn relative_line_numbers(&self, cx: &App) -> RelativeLineNumbers {
21356        match (
21357            self.use_relative_line_numbers,
21358            EditorSettings::get_global(cx).relative_line_numbers,
21359        ) {
21360            (None, setting) => setting,
21361            (Some(false), _) => RelativeLineNumbers::Disabled,
21362            (Some(true), RelativeLineNumbers::Wrapped) => RelativeLineNumbers::Wrapped,
21363            (Some(true), _) => RelativeLineNumbers::Enabled,
21364        }
21365    }
21366
21367    pub fn toggle_relative_line_numbers(
21368        &mut self,
21369        _: &ToggleRelativeLineNumbers,
21370        _: &mut Window,
21371        cx: &mut Context<Self>,
21372    ) {
21373        let is_relative = self.relative_line_numbers(cx);
21374        self.set_relative_line_number(Some(!is_relative.enabled()), cx)
21375    }
21376
21377    pub fn set_relative_line_number(&mut self, is_relative: Option<bool>, cx: &mut Context<Self>) {
21378        self.use_relative_line_numbers = is_relative;
21379        cx.notify();
21380    }
21381
21382    pub fn set_show_gutter(&mut self, show_gutter: bool, cx: &mut Context<Self>) {
21383        self.show_gutter = show_gutter;
21384        cx.notify();
21385    }
21386
21387    pub fn set_show_scrollbars(&mut self, show: bool, cx: &mut Context<Self>) {
21388        self.show_scrollbars = ScrollbarAxes {
21389            horizontal: show,
21390            vertical: show,
21391        };
21392        cx.notify();
21393    }
21394
21395    pub fn set_show_vertical_scrollbar(&mut self, show: bool, cx: &mut Context<Self>) {
21396        self.show_scrollbars.vertical = show;
21397        cx.notify();
21398    }
21399
21400    pub fn set_show_horizontal_scrollbar(&mut self, show: bool, cx: &mut Context<Self>) {
21401        self.show_scrollbars.horizontal = show;
21402        cx.notify();
21403    }
21404
21405    pub fn set_minimap_visibility(
21406        &mut self,
21407        minimap_visibility: MinimapVisibility,
21408        window: &mut Window,
21409        cx: &mut Context<Self>,
21410    ) {
21411        if self.minimap_visibility != minimap_visibility {
21412            if minimap_visibility.visible() && self.minimap.is_none() {
21413                let minimap_settings = EditorSettings::get_global(cx).minimap;
21414                self.minimap =
21415                    self.create_minimap(minimap_settings.with_show_override(), window, cx);
21416            }
21417            self.minimap_visibility = minimap_visibility;
21418            cx.notify();
21419        }
21420    }
21421
21422    pub fn disable_scrollbars_and_minimap(&mut self, window: &mut Window, cx: &mut Context<Self>) {
21423        self.set_show_scrollbars(false, cx);
21424        self.set_minimap_visibility(MinimapVisibility::Disabled, window, cx);
21425    }
21426
21427    pub fn hide_minimap_by_default(&mut self, window: &mut Window, cx: &mut Context<Self>) {
21428        self.set_minimap_visibility(self.minimap_visibility.hidden(), window, cx);
21429    }
21430
21431    /// Normally the text in full mode and auto height editors is padded on the
21432    /// left side by roughly half a character width for improved hit testing.
21433    ///
21434    /// Use this method to disable this for cases where this is not wanted (e.g.
21435    /// if you want to align the editor text with some other text above or below)
21436    /// or if you want to add this padding to single-line editors.
21437    pub fn set_offset_content(&mut self, offset_content: bool, cx: &mut Context<Self>) {
21438        self.offset_content = offset_content;
21439        cx.notify();
21440    }
21441
21442    pub fn set_show_line_numbers(&mut self, show_line_numbers: bool, cx: &mut Context<Self>) {
21443        self.show_line_numbers = Some(show_line_numbers);
21444        cx.notify();
21445    }
21446
21447    pub fn disable_expand_excerpt_buttons(&mut self, cx: &mut Context<Self>) {
21448        self.disable_expand_excerpt_buttons = true;
21449        cx.notify();
21450    }
21451
21452    pub fn set_number_deleted_lines(&mut self, number: bool, cx: &mut Context<Self>) {
21453        self.number_deleted_lines = number;
21454        cx.notify();
21455    }
21456
21457    pub fn set_delegate_expand_excerpts(&mut self, delegate: bool) {
21458        self.delegate_expand_excerpts = delegate;
21459    }
21460
21461    pub fn set_delegate_stage_and_restore(&mut self, delegate: bool) {
21462        self.delegate_stage_and_restore = delegate;
21463    }
21464
21465    pub fn set_delegate_open_excerpts(&mut self, delegate: bool) {
21466        self.delegate_open_excerpts = delegate;
21467    }
21468
21469    pub fn set_on_local_selections_changed(
21470        &mut self,
21471        callback: Option<Box<dyn Fn(Point, &mut Window, &mut Context<Self>) + 'static>>,
21472    ) {
21473        self.on_local_selections_changed = callback;
21474    }
21475
21476    pub fn set_suppress_selection_callback(&mut self, suppress: bool) {
21477        self.suppress_selection_callback = suppress;
21478    }
21479
21480    pub fn set_show_git_diff_gutter(&mut self, show_git_diff_gutter: bool, cx: &mut Context<Self>) {
21481        self.show_git_diff_gutter = Some(show_git_diff_gutter);
21482        cx.notify();
21483    }
21484
21485    pub fn set_show_code_actions(&mut self, show_code_actions: bool, cx: &mut Context<Self>) {
21486        self.show_code_actions = Some(show_code_actions);
21487        cx.notify();
21488    }
21489
21490    pub fn set_show_runnables(&mut self, show_runnables: bool, cx: &mut Context<Self>) {
21491        self.show_runnables = Some(show_runnables);
21492        cx.notify();
21493    }
21494
21495    pub fn set_show_breakpoints(&mut self, show_breakpoints: bool, cx: &mut Context<Self>) {
21496        self.show_breakpoints = Some(show_breakpoints);
21497        cx.notify();
21498    }
21499
21500    pub fn set_show_diff_review_button(&mut self, show: bool, cx: &mut Context<Self>) {
21501        self.show_diff_review_button = show;
21502        cx.notify();
21503    }
21504
21505    pub fn show_diff_review_button(&self) -> bool {
21506        self.show_diff_review_button
21507    }
21508
21509    pub fn render_diff_review_button(
21510        &self,
21511        display_row: DisplayRow,
21512        width: Pixels,
21513        cx: &mut Context<Self>,
21514    ) -> impl IntoElement {
21515        let text_color = cx.theme().colors().text;
21516        let icon_color = cx.theme().colors().icon_accent;
21517
21518        h_flex()
21519            .id("diff_review_button")
21520            .cursor_pointer()
21521            .w(width - px(1.))
21522            .h(relative(0.9))
21523            .justify_center()
21524            .rounded_sm()
21525            .border_1()
21526            .border_color(text_color.opacity(0.1))
21527            .bg(text_color.opacity(0.15))
21528            .hover(|s| {
21529                s.bg(icon_color.opacity(0.4))
21530                    .border_color(icon_color.opacity(0.5))
21531            })
21532            .child(Icon::new(IconName::Plus).size(IconSize::Small))
21533            .tooltip(Tooltip::text("Add Review (drag to select multiple lines)"))
21534            .on_mouse_down(
21535                gpui::MouseButton::Left,
21536                cx.listener(move |editor, _event: &gpui::MouseDownEvent, window, cx| {
21537                    editor.start_diff_review_drag(display_row, window, cx);
21538                }),
21539            )
21540    }
21541
21542    pub fn start_diff_review_drag(
21543        &mut self,
21544        display_row: DisplayRow,
21545        window: &mut Window,
21546        cx: &mut Context<Self>,
21547    ) {
21548        let snapshot = self.snapshot(window, cx);
21549        let point = snapshot
21550            .display_snapshot
21551            .display_point_to_point(DisplayPoint::new(display_row, 0), Bias::Left);
21552        let anchor = snapshot.buffer_snapshot().anchor_before(point);
21553        self.diff_review_drag_state = Some(DiffReviewDragState {
21554            start_anchor: anchor,
21555            current_anchor: anchor,
21556        });
21557        cx.notify();
21558    }
21559
21560    pub fn update_diff_review_drag(
21561        &mut self,
21562        display_row: DisplayRow,
21563        window: &mut Window,
21564        cx: &mut Context<Self>,
21565    ) {
21566        if self.diff_review_drag_state.is_none() {
21567            return;
21568        }
21569        let snapshot = self.snapshot(window, cx);
21570        let point = snapshot
21571            .display_snapshot
21572            .display_point_to_point(display_row.as_display_point(), Bias::Left);
21573        let anchor = snapshot.buffer_snapshot().anchor_before(point);
21574        if let Some(drag_state) = &mut self.diff_review_drag_state {
21575            drag_state.current_anchor = anchor;
21576            cx.notify();
21577        }
21578    }
21579
21580    pub fn end_diff_review_drag(&mut self, window: &mut Window, cx: &mut Context<Self>) {
21581        if let Some(drag_state) = self.diff_review_drag_state.take() {
21582            let snapshot = self.snapshot(window, cx);
21583            let range = drag_state.row_range(&snapshot.display_snapshot);
21584            self.show_diff_review_overlay(*range.start()..*range.end(), window, cx);
21585        }
21586        cx.notify();
21587    }
21588
21589    pub fn cancel_diff_review_drag(&mut self, cx: &mut Context<Self>) {
21590        self.diff_review_drag_state = None;
21591        cx.notify();
21592    }
21593
21594    /// Calculates the appropriate block height for the diff review overlay.
21595    /// Height is in lines: 2 for input row, 1 for header when comments exist,
21596    /// and 2 lines per comment when expanded.
21597    fn calculate_overlay_height(
21598        &self,
21599        hunk_key: &DiffHunkKey,
21600        comments_expanded: bool,
21601        snapshot: &MultiBufferSnapshot,
21602    ) -> u32 {
21603        let comment_count = self.hunk_comment_count(hunk_key, snapshot);
21604        let base_height: u32 = 2; // Input row with avatar and buttons
21605
21606        if comment_count == 0 {
21607            base_height
21608        } else if comments_expanded {
21609            // Header (1 line) + 2 lines per comment
21610            base_height + 1 + (comment_count as u32 * 2)
21611        } else {
21612            // Just header when collapsed
21613            base_height + 1
21614        }
21615    }
21616
21617    pub fn show_diff_review_overlay(
21618        &mut self,
21619        display_range: Range<DisplayRow>,
21620        window: &mut Window,
21621        cx: &mut Context<Self>,
21622    ) {
21623        let Range { start, end } = display_range.sorted();
21624
21625        let buffer_snapshot = self.buffer.read(cx).snapshot(cx);
21626        let editor_snapshot = self.snapshot(window, cx);
21627
21628        // Convert display rows to multibuffer points
21629        let start_point = editor_snapshot
21630            .display_snapshot
21631            .display_point_to_point(start.as_display_point(), Bias::Left);
21632        let end_point = editor_snapshot
21633            .display_snapshot
21634            .display_point_to_point(end.as_display_point(), Bias::Left);
21635        let end_multi_buffer_row = MultiBufferRow(end_point.row);
21636
21637        // Create anchor range for the selected lines (start of first line to end of last line)
21638        let line_end = Point::new(
21639            end_point.row,
21640            buffer_snapshot.line_len(end_multi_buffer_row),
21641        );
21642        let anchor_range =
21643            buffer_snapshot.anchor_after(start_point)..buffer_snapshot.anchor_before(line_end);
21644
21645        // Compute the hunk key for this display row
21646        let file_path = buffer_snapshot
21647            .file_at(start_point)
21648            .map(|file: &Arc<dyn language::File>| file.path().clone())
21649            .unwrap_or_else(|| Arc::from(util::rel_path::RelPath::empty()));
21650        let hunk_start_anchor = buffer_snapshot.anchor_before(start_point);
21651        let new_hunk_key = DiffHunkKey {
21652            file_path,
21653            hunk_start_anchor,
21654        };
21655
21656        // Check if we already have an overlay for this hunk
21657        if let Some(existing_overlay) = self.diff_review_overlays.iter().find(|overlay| {
21658            Self::hunk_keys_match(&overlay.hunk_key, &new_hunk_key, &buffer_snapshot)
21659        }) {
21660            // Just focus the existing overlay's prompt editor
21661            let focus_handle = existing_overlay.prompt_editor.focus_handle(cx);
21662            window.focus(&focus_handle, cx);
21663            return;
21664        }
21665
21666        // Dismiss overlays that have no comments for their hunks
21667        self.dismiss_overlays_without_comments(cx);
21668
21669        // Get the current user's avatar URI from the project's user_store
21670        let user_avatar_uri = self.project.as_ref().and_then(|project| {
21671            let user_store = project.read(cx).user_store();
21672            user_store
21673                .read(cx)
21674                .current_user()
21675                .map(|user| user.avatar_uri.clone())
21676        });
21677
21678        // Create anchor at the end of the last row so the block appears immediately below it
21679        // Use multibuffer coordinates for anchor creation
21680        let line_len = buffer_snapshot.line_len(end_multi_buffer_row);
21681        let anchor = buffer_snapshot.anchor_after(Point::new(end_multi_buffer_row.0, line_len));
21682
21683        // Use the hunk key we already computed
21684        let hunk_key = new_hunk_key;
21685
21686        // Create the prompt editor for the review input
21687        let prompt_editor = cx.new(|cx| {
21688            let mut editor = Editor::single_line(window, cx);
21689            editor.set_placeholder_text("Add a review comment...", window, cx);
21690            editor
21691        });
21692
21693        // Register the Newline action on the prompt editor to submit the review
21694        let parent_editor = cx.entity().downgrade();
21695        let subscription = prompt_editor.update(cx, |prompt_editor, _cx| {
21696            prompt_editor.register_action({
21697                let parent_editor = parent_editor.clone();
21698                move |_: &crate::actions::Newline, window, cx| {
21699                    if let Some(editor) = parent_editor.upgrade() {
21700                        editor.update(cx, |editor, cx| {
21701                            editor.submit_diff_review_comment(window, cx);
21702                        });
21703                    }
21704                }
21705            })
21706        });
21707
21708        // Calculate initial height based on existing comments for this hunk
21709        let initial_height = self.calculate_overlay_height(&hunk_key, true, &buffer_snapshot);
21710
21711        // Create the overlay block
21712        let prompt_editor_for_render = prompt_editor.clone();
21713        let hunk_key_for_render = hunk_key.clone();
21714        let editor_handle = cx.entity().downgrade();
21715        let block = BlockProperties {
21716            style: BlockStyle::Sticky,
21717            placement: BlockPlacement::Below(anchor),
21718            height: Some(initial_height),
21719            render: Arc::new(move |cx| {
21720                Self::render_diff_review_overlay(
21721                    &prompt_editor_for_render,
21722                    &hunk_key_for_render,
21723                    &editor_handle,
21724                    cx,
21725                )
21726            }),
21727            priority: 0,
21728        };
21729
21730        let block_ids = self.insert_blocks([block], None, cx);
21731        let Some(block_id) = block_ids.into_iter().next() else {
21732            log::error!("Failed to insert diff review overlay block");
21733            return;
21734        };
21735
21736        self.diff_review_overlays.push(DiffReviewOverlay {
21737            anchor_range,
21738            block_id,
21739            prompt_editor: prompt_editor.clone(),
21740            hunk_key,
21741            comments_expanded: true,
21742            inline_edit_editors: HashMap::default(),
21743            inline_edit_subscriptions: HashMap::default(),
21744            user_avatar_uri,
21745            _subscription: subscription,
21746        });
21747
21748        // Focus the prompt editor
21749        let focus_handle = prompt_editor.focus_handle(cx);
21750        window.focus(&focus_handle, cx);
21751
21752        cx.notify();
21753    }
21754
21755    /// Dismisses all diff review overlays.
21756    pub fn dismiss_all_diff_review_overlays(&mut self, cx: &mut Context<Self>) {
21757        if self.diff_review_overlays.is_empty() {
21758            return;
21759        }
21760        let block_ids: HashSet<_> = self
21761            .diff_review_overlays
21762            .drain(..)
21763            .map(|overlay| overlay.block_id)
21764            .collect();
21765        self.remove_blocks(block_ids, None, cx);
21766        cx.notify();
21767    }
21768
21769    /// Dismisses overlays that have no comments stored for their hunks.
21770    /// Keeps overlays that have at least one comment.
21771    fn dismiss_overlays_without_comments(&mut self, cx: &mut Context<Self>) {
21772        let snapshot = self.buffer.read(cx).snapshot(cx);
21773
21774        // First, compute which overlays have comments (to avoid borrow issues with retain)
21775        let overlays_with_comments: Vec<bool> = self
21776            .diff_review_overlays
21777            .iter()
21778            .map(|overlay| self.hunk_comment_count(&overlay.hunk_key, &snapshot) > 0)
21779            .collect();
21780
21781        // Now collect block IDs to remove and retain overlays
21782        let mut block_ids_to_remove = HashSet::default();
21783        let mut index = 0;
21784        self.diff_review_overlays.retain(|overlay| {
21785            let has_comments = overlays_with_comments[index];
21786            index += 1;
21787            if !has_comments {
21788                block_ids_to_remove.insert(overlay.block_id);
21789            }
21790            has_comments
21791        });
21792
21793        if !block_ids_to_remove.is_empty() {
21794            self.remove_blocks(block_ids_to_remove, None, cx);
21795            cx.notify();
21796        }
21797    }
21798
21799    /// Refreshes the diff review overlay block to update its height and render function.
21800    /// Uses resize_blocks and replace_blocks to avoid visual flicker from remove+insert.
21801    fn refresh_diff_review_overlay_height(
21802        &mut self,
21803        hunk_key: &DiffHunkKey,
21804        _window: &mut Window,
21805        cx: &mut Context<Self>,
21806    ) {
21807        // Extract all needed data from overlay first to avoid borrow conflicts
21808        let snapshot = self.buffer.read(cx).snapshot(cx);
21809        let (comments_expanded, block_id, prompt_editor) = {
21810            let Some(overlay) = self
21811                .diff_review_overlays
21812                .iter()
21813                .find(|overlay| Self::hunk_keys_match(&overlay.hunk_key, hunk_key, &snapshot))
21814            else {
21815                return;
21816            };
21817
21818            (
21819                overlay.comments_expanded,
21820                overlay.block_id,
21821                overlay.prompt_editor.clone(),
21822            )
21823        };
21824
21825        // Calculate new height
21826        let snapshot = self.buffer.read(cx).snapshot(cx);
21827        let new_height = self.calculate_overlay_height(hunk_key, comments_expanded, &snapshot);
21828
21829        // Update the block height using resize_blocks (avoids flicker)
21830        let mut heights = HashMap::default();
21831        heights.insert(block_id, new_height);
21832        self.resize_blocks(heights, None, cx);
21833
21834        // Update the render function using replace_blocks (avoids flicker)
21835        let hunk_key_for_render = hunk_key.clone();
21836        let editor_handle = cx.entity().downgrade();
21837        let render: Arc<dyn Fn(&mut BlockContext) -> AnyElement + Send + Sync> =
21838            Arc::new(move |cx| {
21839                Self::render_diff_review_overlay(
21840                    &prompt_editor,
21841                    &hunk_key_for_render,
21842                    &editor_handle,
21843                    cx,
21844                )
21845            });
21846
21847        let mut renderers = HashMap::default();
21848        renderers.insert(block_id, render);
21849        self.replace_blocks(renderers, None, cx);
21850    }
21851
21852    /// Action handler for SubmitDiffReviewComment.
21853    pub fn submit_diff_review_comment_action(
21854        &mut self,
21855        _: &SubmitDiffReviewComment,
21856        window: &mut Window,
21857        cx: &mut Context<Self>,
21858    ) {
21859        self.submit_diff_review_comment(window, cx);
21860    }
21861
21862    /// Stores the diff review comment locally.
21863    /// Comments are stored per-hunk and can later be batch-submitted to the Agent panel.
21864    pub fn submit_diff_review_comment(&mut self, window: &mut Window, cx: &mut Context<Self>) {
21865        // Find the overlay that currently has focus
21866        let overlay_index = self
21867            .diff_review_overlays
21868            .iter()
21869            .position(|overlay| overlay.prompt_editor.focus_handle(cx).is_focused(window));
21870        let Some(overlay_index) = overlay_index else {
21871            return;
21872        };
21873        let overlay = &self.diff_review_overlays[overlay_index];
21874
21875        let comment_text = overlay.prompt_editor.read(cx).text(cx).trim().to_string();
21876        if comment_text.is_empty() {
21877            return;
21878        }
21879
21880        let anchor_range = overlay.anchor_range.clone();
21881        let hunk_key = overlay.hunk_key.clone();
21882
21883        self.add_review_comment(hunk_key.clone(), comment_text, anchor_range, cx);
21884
21885        // Clear the prompt editor but keep the overlay open
21886        if let Some(overlay) = self.diff_review_overlays.get(overlay_index) {
21887            overlay.prompt_editor.update(cx, |editor, cx| {
21888                editor.clear(window, cx);
21889            });
21890        }
21891
21892        // Refresh the overlay to update the block height for the new comment
21893        self.refresh_diff_review_overlay_height(&hunk_key, window, cx);
21894
21895        cx.notify();
21896    }
21897
21898    /// Returns the prompt editor for the diff review overlay, if one is active.
21899    /// This is primarily used for testing.
21900    pub fn diff_review_prompt_editor(&self) -> Option<&Entity<Editor>> {
21901        self.diff_review_overlays
21902            .first()
21903            .map(|overlay| &overlay.prompt_editor)
21904    }
21905
21906    /// Returns the line range for the first diff review overlay, if one is active.
21907    /// Returns (start_row, end_row) as physical line numbers in the underlying file.
21908    pub fn diff_review_line_range(&self, cx: &App) -> Option<(u32, u32)> {
21909        let overlay = self.diff_review_overlays.first()?;
21910        let snapshot = self.buffer.read(cx).snapshot(cx);
21911        let start_point = overlay.anchor_range.start.to_point(&snapshot);
21912        let end_point = overlay.anchor_range.end.to_point(&snapshot);
21913        let start_row = snapshot
21914            .point_to_buffer_point(start_point)
21915            .map(|(_, p, _)| p.row)
21916            .unwrap_or(start_point.row);
21917        let end_row = snapshot
21918            .point_to_buffer_point(end_point)
21919            .map(|(_, p, _)| p.row)
21920            .unwrap_or(end_point.row);
21921        Some((start_row, end_row))
21922    }
21923
21924    /// Sets whether the comments section is expanded in the diff review overlay.
21925    /// This is primarily used for testing.
21926    pub fn set_diff_review_comments_expanded(&mut self, expanded: bool, cx: &mut Context<Self>) {
21927        for overlay in &mut self.diff_review_overlays {
21928            overlay.comments_expanded = expanded;
21929        }
21930        cx.notify();
21931    }
21932
21933    /// Compares two DiffHunkKeys for equality by resolving their anchors.
21934    fn hunk_keys_match(a: &DiffHunkKey, b: &DiffHunkKey, snapshot: &MultiBufferSnapshot) -> bool {
21935        a.file_path == b.file_path
21936            && a.hunk_start_anchor.to_point(snapshot) == b.hunk_start_anchor.to_point(snapshot)
21937    }
21938
21939    /// Returns comments for a specific hunk, ordered by creation time.
21940    pub fn comments_for_hunk<'a>(
21941        &'a self,
21942        key: &DiffHunkKey,
21943        snapshot: &MultiBufferSnapshot,
21944    ) -> &'a [StoredReviewComment] {
21945        let key_point = key.hunk_start_anchor.to_point(snapshot);
21946        self.stored_review_comments
21947            .iter()
21948            .find(|(k, _)| {
21949                k.file_path == key.file_path && k.hunk_start_anchor.to_point(snapshot) == key_point
21950            })
21951            .map(|(_, comments)| comments.as_slice())
21952            .unwrap_or(&[])
21953    }
21954
21955    /// Returns the total count of stored review comments across all hunks.
21956    pub fn total_review_comment_count(&self) -> usize {
21957        self.stored_review_comments
21958            .iter()
21959            .map(|(_, v)| v.len())
21960            .sum()
21961    }
21962
21963    /// Returns the count of comments for a specific hunk.
21964    pub fn hunk_comment_count(&self, key: &DiffHunkKey, snapshot: &MultiBufferSnapshot) -> usize {
21965        let key_point = key.hunk_start_anchor.to_point(snapshot);
21966        self.stored_review_comments
21967            .iter()
21968            .find(|(k, _)| {
21969                k.file_path == key.file_path && k.hunk_start_anchor.to_point(snapshot) == key_point
21970            })
21971            .map(|(_, v)| v.len())
21972            .unwrap_or(0)
21973    }
21974
21975    /// Adds a new review comment to a specific hunk.
21976    pub fn add_review_comment(
21977        &mut self,
21978        hunk_key: DiffHunkKey,
21979        comment: String,
21980        anchor_range: Range<Anchor>,
21981        cx: &mut Context<Self>,
21982    ) -> usize {
21983        let id = self.next_review_comment_id;
21984        self.next_review_comment_id += 1;
21985
21986        let stored_comment = StoredReviewComment::new(id, comment, anchor_range);
21987
21988        let snapshot = self.buffer.read(cx).snapshot(cx);
21989        let key_point = hunk_key.hunk_start_anchor.to_point(&snapshot);
21990
21991        // Find existing entry for this hunk or add a new one
21992        if let Some((_, comments)) = self.stored_review_comments.iter_mut().find(|(k, _)| {
21993            k.file_path == hunk_key.file_path
21994                && k.hunk_start_anchor.to_point(&snapshot) == key_point
21995        }) {
21996            comments.push(stored_comment);
21997        } else {
21998            self.stored_review_comments
21999                .push((hunk_key, vec![stored_comment]));
22000        }
22001
22002        cx.emit(EditorEvent::ReviewCommentsChanged {
22003            total_count: self.total_review_comment_count(),
22004        });
22005        cx.notify();
22006        id
22007    }
22008
22009    /// Removes a review comment by ID from any hunk.
22010    pub fn remove_review_comment(&mut self, id: usize, cx: &mut Context<Self>) -> bool {
22011        for (_, comments) in self.stored_review_comments.iter_mut() {
22012            if let Some(index) = comments.iter().position(|c| c.id == id) {
22013                comments.remove(index);
22014                cx.emit(EditorEvent::ReviewCommentsChanged {
22015                    total_count: self.total_review_comment_count(),
22016                });
22017                cx.notify();
22018                return true;
22019            }
22020        }
22021        false
22022    }
22023
22024    /// Updates a review comment's text by ID.
22025    pub fn update_review_comment(
22026        &mut self,
22027        id: usize,
22028        new_comment: String,
22029        cx: &mut Context<Self>,
22030    ) -> bool {
22031        for (_, comments) in self.stored_review_comments.iter_mut() {
22032            if let Some(comment) = comments.iter_mut().find(|c| c.id == id) {
22033                comment.comment = new_comment;
22034                comment.is_editing = false;
22035                cx.emit(EditorEvent::ReviewCommentsChanged {
22036                    total_count: self.total_review_comment_count(),
22037                });
22038                cx.notify();
22039                return true;
22040            }
22041        }
22042        false
22043    }
22044
22045    /// Sets a comment's editing state.
22046    pub fn set_comment_editing(&mut self, id: usize, is_editing: bool, cx: &mut Context<Self>) {
22047        for (_, comments) in self.stored_review_comments.iter_mut() {
22048            if let Some(comment) = comments.iter_mut().find(|c| c.id == id) {
22049                comment.is_editing = is_editing;
22050                cx.notify();
22051                return;
22052            }
22053        }
22054    }
22055
22056    /// Takes all stored comments from all hunks, clearing the storage.
22057    /// Returns a Vec of (hunk_key, comments) pairs.
22058    pub fn take_all_review_comments(
22059        &mut self,
22060        cx: &mut Context<Self>,
22061    ) -> Vec<(DiffHunkKey, Vec<StoredReviewComment>)> {
22062        // Dismiss all overlays when taking comments (e.g., when sending to agent)
22063        self.dismiss_all_diff_review_overlays(cx);
22064        let comments = std::mem::take(&mut self.stored_review_comments);
22065        // Reset the ID counter since all comments have been taken
22066        self.next_review_comment_id = 0;
22067        cx.emit(EditorEvent::ReviewCommentsChanged { total_count: 0 });
22068        cx.notify();
22069        comments
22070    }
22071
22072    /// Removes review comments whose anchors are no longer valid or whose
22073    /// associated diff hunks no longer exist.
22074    ///
22075    /// This should be called when the buffer changes to prevent orphaned comments
22076    /// from accumulating.
22077    pub fn cleanup_orphaned_review_comments(&mut self, cx: &mut Context<Self>) {
22078        let snapshot = self.buffer.read(cx).snapshot(cx);
22079        let original_count = self.total_review_comment_count();
22080
22081        // Remove comments with invalid hunk anchors
22082        self.stored_review_comments
22083            .retain(|(hunk_key, _)| hunk_key.hunk_start_anchor.is_valid(&snapshot));
22084
22085        // Also clean up individual comments with invalid anchor ranges
22086        for (_, comments) in &mut self.stored_review_comments {
22087            comments.retain(|comment| {
22088                comment.range.start.is_valid(&snapshot) && comment.range.end.is_valid(&snapshot)
22089            });
22090        }
22091
22092        // Remove empty hunk entries
22093        self.stored_review_comments
22094            .retain(|(_, comments)| !comments.is_empty());
22095
22096        let new_count = self.total_review_comment_count();
22097        if new_count != original_count {
22098            cx.emit(EditorEvent::ReviewCommentsChanged {
22099                total_count: new_count,
22100            });
22101            cx.notify();
22102        }
22103    }
22104
22105    /// Toggles the expanded state of the comments section in the overlay.
22106    pub fn toggle_review_comments_expanded(
22107        &mut self,
22108        _: &ToggleReviewCommentsExpanded,
22109        window: &mut Window,
22110        cx: &mut Context<Self>,
22111    ) {
22112        // Find the overlay that currently has focus, or use the first one
22113        let overlay_info = self.diff_review_overlays.iter_mut().find_map(|overlay| {
22114            if overlay.prompt_editor.focus_handle(cx).is_focused(window) {
22115                overlay.comments_expanded = !overlay.comments_expanded;
22116                Some(overlay.hunk_key.clone())
22117            } else {
22118                None
22119            }
22120        });
22121
22122        // If no focused overlay found, toggle the first one
22123        let hunk_key = overlay_info.or_else(|| {
22124            self.diff_review_overlays.first_mut().map(|overlay| {
22125                overlay.comments_expanded = !overlay.comments_expanded;
22126                overlay.hunk_key.clone()
22127            })
22128        });
22129
22130        if let Some(hunk_key) = hunk_key {
22131            self.refresh_diff_review_overlay_height(&hunk_key, window, cx);
22132            cx.notify();
22133        }
22134    }
22135
22136    /// Handles the EditReviewComment action - sets a comment into editing mode.
22137    pub fn edit_review_comment(
22138        &mut self,
22139        action: &EditReviewComment,
22140        window: &mut Window,
22141        cx: &mut Context<Self>,
22142    ) {
22143        let comment_id = action.id;
22144
22145        // Set the comment to editing mode
22146        self.set_comment_editing(comment_id, true, cx);
22147
22148        // Find the overlay that contains this comment and create an inline editor if needed
22149        // First, find which hunk this comment belongs to
22150        let hunk_key = self
22151            .stored_review_comments
22152            .iter()
22153            .find_map(|(key, comments)| {
22154                if comments.iter().any(|c| c.id == comment_id) {
22155                    Some(key.clone())
22156                } else {
22157                    None
22158                }
22159            });
22160
22161        let snapshot = self.buffer.read(cx).snapshot(cx);
22162        if let Some(hunk_key) = hunk_key {
22163            if let Some(overlay) = self
22164                .diff_review_overlays
22165                .iter_mut()
22166                .find(|overlay| Self::hunk_keys_match(&overlay.hunk_key, &hunk_key, &snapshot))
22167            {
22168                if let std::collections::hash_map::Entry::Vacant(entry) =
22169                    overlay.inline_edit_editors.entry(comment_id)
22170                {
22171                    // Find the comment text
22172                    let comment_text = self
22173                        .stored_review_comments
22174                        .iter()
22175                        .flat_map(|(_, comments)| comments)
22176                        .find(|c| c.id == comment_id)
22177                        .map(|c| c.comment.clone())
22178                        .unwrap_or_default();
22179
22180                    // Create inline editor
22181                    let parent_editor = cx.entity().downgrade();
22182                    let inline_editor = cx.new(|cx| {
22183                        let mut editor = Editor::single_line(window, cx);
22184                        editor.set_text(&*comment_text, window, cx);
22185                        // Select all text for easy replacement
22186                        editor.select_all(&crate::actions::SelectAll, window, cx);
22187                        editor
22188                    });
22189
22190                    // Register the Newline action to confirm the edit
22191                    let subscription = inline_editor.update(cx, |inline_editor, _cx| {
22192                        inline_editor.register_action({
22193                            let parent_editor = parent_editor.clone();
22194                            move |_: &crate::actions::Newline, window, cx| {
22195                                if let Some(editor) = parent_editor.upgrade() {
22196                                    editor.update(cx, |editor, cx| {
22197                                        editor.confirm_edit_review_comment(comment_id, window, cx);
22198                                    });
22199                                }
22200                            }
22201                        })
22202                    });
22203
22204                    // Store the subscription to keep the action handler alive
22205                    overlay
22206                        .inline_edit_subscriptions
22207                        .insert(comment_id, subscription);
22208
22209                    // Focus the inline editor
22210                    let focus_handle = inline_editor.focus_handle(cx);
22211                    window.focus(&focus_handle, cx);
22212
22213                    entry.insert(inline_editor);
22214                }
22215            }
22216        }
22217
22218        cx.notify();
22219    }
22220
22221    /// Confirms an inline edit of a review comment.
22222    pub fn confirm_edit_review_comment(
22223        &mut self,
22224        comment_id: usize,
22225        _window: &mut Window,
22226        cx: &mut Context<Self>,
22227    ) {
22228        // Get the new text from the inline editor
22229        // Find the overlay containing this comment's inline editor
22230        let snapshot = self.buffer.read(cx).snapshot(cx);
22231        let hunk_key = self
22232            .stored_review_comments
22233            .iter()
22234            .find_map(|(key, comments)| {
22235                if comments.iter().any(|c| c.id == comment_id) {
22236                    Some(key.clone())
22237                } else {
22238                    None
22239                }
22240            });
22241
22242        let new_text = hunk_key
22243            .as_ref()
22244            .and_then(|hunk_key| {
22245                self.diff_review_overlays
22246                    .iter()
22247                    .find(|overlay| Self::hunk_keys_match(&overlay.hunk_key, hunk_key, &snapshot))
22248            })
22249            .as_ref()
22250            .and_then(|overlay| overlay.inline_edit_editors.get(&comment_id))
22251            .map(|editor| editor.read(cx).text(cx).trim().to_string());
22252
22253        if let Some(new_text) = new_text {
22254            if !new_text.is_empty() {
22255                self.update_review_comment(comment_id, new_text, cx);
22256            }
22257        }
22258
22259        // Remove the inline editor and its subscription
22260        if let Some(hunk_key) = hunk_key {
22261            if let Some(overlay) = self
22262                .diff_review_overlays
22263                .iter_mut()
22264                .find(|overlay| Self::hunk_keys_match(&overlay.hunk_key, &hunk_key, &snapshot))
22265            {
22266                overlay.inline_edit_editors.remove(&comment_id);
22267                overlay.inline_edit_subscriptions.remove(&comment_id);
22268            }
22269        }
22270
22271        // Clear editing state
22272        self.set_comment_editing(comment_id, false, cx);
22273    }
22274
22275    /// Cancels an inline edit of a review comment.
22276    pub fn cancel_edit_review_comment(
22277        &mut self,
22278        comment_id: usize,
22279        _window: &mut Window,
22280        cx: &mut Context<Self>,
22281    ) {
22282        // Find which hunk this comment belongs to
22283        let hunk_key = self
22284            .stored_review_comments
22285            .iter()
22286            .find_map(|(key, comments)| {
22287                if comments.iter().any(|c| c.id == comment_id) {
22288                    Some(key.clone())
22289                } else {
22290                    None
22291                }
22292            });
22293
22294        // Remove the inline editor and its subscription
22295        if let Some(hunk_key) = hunk_key {
22296            let snapshot = self.buffer.read(cx).snapshot(cx);
22297            if let Some(overlay) = self
22298                .diff_review_overlays
22299                .iter_mut()
22300                .find(|overlay| Self::hunk_keys_match(&overlay.hunk_key, &hunk_key, &snapshot))
22301            {
22302                overlay.inline_edit_editors.remove(&comment_id);
22303                overlay.inline_edit_subscriptions.remove(&comment_id);
22304            }
22305        }
22306
22307        // Clear editing state
22308        self.set_comment_editing(comment_id, false, cx);
22309    }
22310
22311    /// Action handler for ConfirmEditReviewComment.
22312    pub fn confirm_edit_review_comment_action(
22313        &mut self,
22314        action: &ConfirmEditReviewComment,
22315        window: &mut Window,
22316        cx: &mut Context<Self>,
22317    ) {
22318        self.confirm_edit_review_comment(action.id, window, cx);
22319    }
22320
22321    /// Action handler for CancelEditReviewComment.
22322    pub fn cancel_edit_review_comment_action(
22323        &mut self,
22324        action: &CancelEditReviewComment,
22325        window: &mut Window,
22326        cx: &mut Context<Self>,
22327    ) {
22328        self.cancel_edit_review_comment(action.id, window, cx);
22329    }
22330
22331    /// Handles the DeleteReviewComment action - removes a comment.
22332    pub fn delete_review_comment(
22333        &mut self,
22334        action: &DeleteReviewComment,
22335        window: &mut Window,
22336        cx: &mut Context<Self>,
22337    ) {
22338        // Get the hunk key before removing the comment
22339        // Find the hunk key from the comment itself
22340        let comment_id = action.id;
22341        let hunk_key = self
22342            .stored_review_comments
22343            .iter()
22344            .find_map(|(key, comments)| {
22345                if comments.iter().any(|c| c.id == comment_id) {
22346                    Some(key.clone())
22347                } else {
22348                    None
22349                }
22350            });
22351
22352        // Also get it from the overlay for refresh purposes
22353        let overlay_hunk_key = self
22354            .diff_review_overlays
22355            .first()
22356            .map(|o| o.hunk_key.clone());
22357
22358        self.remove_review_comment(action.id, cx);
22359
22360        // Refresh the overlay height after removing a comment
22361        if let Some(hunk_key) = hunk_key.or(overlay_hunk_key) {
22362            self.refresh_diff_review_overlay_height(&hunk_key, window, cx);
22363        }
22364    }
22365
22366    fn render_diff_review_overlay(
22367        prompt_editor: &Entity<Editor>,
22368        hunk_key: &DiffHunkKey,
22369        editor_handle: &WeakEntity<Editor>,
22370        cx: &mut BlockContext,
22371    ) -> AnyElement {
22372        fn format_line_ranges(ranges: &[(u32, u32)]) -> Option<String> {
22373            if ranges.is_empty() {
22374                return None;
22375            }
22376            let formatted: Vec<String> = ranges
22377                .iter()
22378                .map(|(start, end)| {
22379                    let start_line = start + 1;
22380                    let end_line = end + 1;
22381                    if start_line == end_line {
22382                        format!("Line {start_line}")
22383                    } else {
22384                        format!("Lines {start_line}-{end_line}")
22385                    }
22386                })
22387                .collect();
22388            // Don't show label for single line in single excerpt
22389            if ranges.len() == 1 && ranges[0].0 == ranges[0].1 {
22390                return None;
22391            }
22392            Some(formatted.join(""))
22393        }
22394
22395        let theme = cx.theme();
22396        let colors = theme.colors();
22397
22398        let (comments, comments_expanded, inline_editors, user_avatar_uri, line_ranges) =
22399            editor_handle
22400                .upgrade()
22401                .map(|editor| {
22402                    let editor = editor.read(cx);
22403                    let snapshot = editor.buffer().read(cx).snapshot(cx);
22404                    let comments = editor.comments_for_hunk(hunk_key, &snapshot).to_vec();
22405                    let (expanded, editors, avatar_uri, line_ranges) = editor
22406                        .diff_review_overlays
22407                        .iter()
22408                        .find(|overlay| {
22409                            Editor::hunk_keys_match(&overlay.hunk_key, hunk_key, &snapshot)
22410                        })
22411                        .map(|o| {
22412                            let start_point = o.anchor_range.start.to_point(&snapshot);
22413                            let end_point = o.anchor_range.end.to_point(&snapshot);
22414                            // Get line ranges per excerpt to detect discontinuities
22415                            let buffer_ranges =
22416                                snapshot.range_to_buffer_ranges(start_point..end_point);
22417                            let ranges: Vec<(u32, u32)> = buffer_ranges
22418                                .iter()
22419                                .map(|(buffer, range, _)| {
22420                                    let start = buffer.offset_to_point(range.start.0).row;
22421                                    let end = buffer.offset_to_point(range.end.0).row;
22422                                    (start, end)
22423                                })
22424                                .collect();
22425                            (
22426                                o.comments_expanded,
22427                                o.inline_edit_editors.clone(),
22428                                o.user_avatar_uri.clone(),
22429                                if ranges.is_empty() {
22430                                    None
22431                                } else {
22432                                    Some(ranges)
22433                                },
22434                            )
22435                        })
22436                        .unwrap_or((true, HashMap::default(), None, None));
22437                    (comments, expanded, editors, avatar_uri, line_ranges)
22438                })
22439                .unwrap_or((Vec::new(), true, HashMap::default(), None, None));
22440
22441        let comment_count = comments.len();
22442        let avatar_size = px(20.);
22443        let action_icon_size = IconSize::XSmall;
22444
22445        v_flex()
22446            .w_full()
22447            .bg(colors.editor_background)
22448            .border_b_1()
22449            .border_color(colors.border)
22450            .px_2()
22451            .pb_2()
22452            .gap_2()
22453            // Line range indicator (only shown for multi-line selections or multiple excerpts)
22454            .when_some(line_ranges, |el, ranges| {
22455                let label = format_line_ranges(&ranges);
22456                if let Some(label) = label {
22457                    el.child(
22458                        h_flex()
22459                            .w_full()
22460                            .px_2()
22461                            .child(Label::new(label).size(LabelSize::Small).color(Color::Muted)),
22462                    )
22463                } else {
22464                    el
22465                }
22466            })
22467            // Top row: editable input with user's avatar
22468            .child(
22469                h_flex()
22470                    .w_full()
22471                    .items_center()
22472                    .gap_2()
22473                    .px_2()
22474                    .py_1p5()
22475                    .rounded_md()
22476                    .bg(colors.surface_background)
22477                    .child(
22478                        div()
22479                            .size(avatar_size)
22480                            .flex_shrink_0()
22481                            .rounded_full()
22482                            .overflow_hidden()
22483                            .child(if let Some(ref avatar_uri) = user_avatar_uri {
22484                                Avatar::new(avatar_uri.clone())
22485                                    .size(avatar_size)
22486                                    .into_any_element()
22487                            } else {
22488                                Icon::new(IconName::Person)
22489                                    .size(IconSize::Small)
22490                                    .color(ui::Color::Muted)
22491                                    .into_any_element()
22492                            }),
22493                    )
22494                    .child(
22495                        div()
22496                            .flex_1()
22497                            .border_1()
22498                            .border_color(colors.border)
22499                            .rounded_md()
22500                            .bg(colors.editor_background)
22501                            .px_2()
22502                            .py_1()
22503                            .child(prompt_editor.clone()),
22504                    )
22505                    .child(
22506                        h_flex()
22507                            .flex_shrink_0()
22508                            .gap_1()
22509                            .child(
22510                                IconButton::new("diff-review-close", IconName::Close)
22511                                    .icon_color(ui::Color::Muted)
22512                                    .icon_size(action_icon_size)
22513                                    .tooltip(Tooltip::text("Close"))
22514                                    .on_click(|_, window, cx| {
22515                                        window
22516                                            .dispatch_action(Box::new(crate::actions::Cancel), cx);
22517                                    }),
22518                            )
22519                            .child(
22520                                IconButton::new("diff-review-add", IconName::Return)
22521                                    .icon_color(ui::Color::Muted)
22522                                    .icon_size(action_icon_size)
22523                                    .tooltip(Tooltip::text("Add comment"))
22524                                    .on_click(|_, window, cx| {
22525                                        window.dispatch_action(
22526                                            Box::new(crate::actions::SubmitDiffReviewComment),
22527                                            cx,
22528                                        );
22529                                    }),
22530                            ),
22531                    ),
22532            )
22533            // Expandable comments section (only shown when there are comments)
22534            .when(comment_count > 0, |el| {
22535                el.child(Self::render_comments_section(
22536                    comments,
22537                    comments_expanded,
22538                    inline_editors,
22539                    user_avatar_uri,
22540                    avatar_size,
22541                    action_icon_size,
22542                    colors,
22543                ))
22544            })
22545            .into_any_element()
22546    }
22547
22548    fn render_comments_section(
22549        comments: Vec<StoredReviewComment>,
22550        expanded: bool,
22551        inline_editors: HashMap<usize, Entity<Editor>>,
22552        user_avatar_uri: Option<SharedUri>,
22553        avatar_size: Pixels,
22554        action_icon_size: IconSize,
22555        colors: &theme::ThemeColors,
22556    ) -> impl IntoElement {
22557        let comment_count = comments.len();
22558
22559        v_flex()
22560            .w_full()
22561            .gap_1()
22562            // Header with expand/collapse toggle
22563            .child(
22564                h_flex()
22565                    .id("review-comments-header")
22566                    .w_full()
22567                    .items_center()
22568                    .gap_1()
22569                    .px_2()
22570                    .py_1()
22571                    .cursor_pointer()
22572                    .rounded_md()
22573                    .hover(|style| style.bg(colors.ghost_element_hover))
22574                    .on_click(|_, window: &mut Window, cx| {
22575                        window.dispatch_action(
22576                            Box::new(crate::actions::ToggleReviewCommentsExpanded),
22577                            cx,
22578                        );
22579                    })
22580                    .child(
22581                        Icon::new(if expanded {
22582                            IconName::ChevronDown
22583                        } else {
22584                            IconName::ChevronRight
22585                        })
22586                        .size(IconSize::Small)
22587                        .color(ui::Color::Muted),
22588                    )
22589                    .child(
22590                        Label::new(format!(
22591                            "{} Comment{}",
22592                            comment_count,
22593                            if comment_count == 1 { "" } else { "s" }
22594                        ))
22595                        .size(LabelSize::Small)
22596                        .color(Color::Muted),
22597                    ),
22598            )
22599            // Comments list (when expanded)
22600            .when(expanded, |el| {
22601                el.children(comments.into_iter().map(|comment| {
22602                    let inline_editor = inline_editors.get(&comment.id).cloned();
22603                    Self::render_comment_row(
22604                        comment,
22605                        inline_editor,
22606                        user_avatar_uri.clone(),
22607                        avatar_size,
22608                        action_icon_size,
22609                        colors,
22610                    )
22611                }))
22612            })
22613    }
22614
22615    fn render_comment_row(
22616        comment: StoredReviewComment,
22617        inline_editor: Option<Entity<Editor>>,
22618        user_avatar_uri: Option<SharedUri>,
22619        avatar_size: Pixels,
22620        action_icon_size: IconSize,
22621        colors: &theme::ThemeColors,
22622    ) -> impl IntoElement {
22623        let comment_id = comment.id;
22624        let is_editing = inline_editor.is_some();
22625
22626        h_flex()
22627            .w_full()
22628            .items_center()
22629            .gap_2()
22630            .px_2()
22631            .py_1p5()
22632            .rounded_md()
22633            .bg(colors.surface_background)
22634            .child(
22635                div()
22636                    .size(avatar_size)
22637                    .flex_shrink_0()
22638                    .rounded_full()
22639                    .overflow_hidden()
22640                    .child(if let Some(ref avatar_uri) = user_avatar_uri {
22641                        Avatar::new(avatar_uri.clone())
22642                            .size(avatar_size)
22643                            .into_any_element()
22644                    } else {
22645                        Icon::new(IconName::Person)
22646                            .size(IconSize::Small)
22647                            .color(ui::Color::Muted)
22648                            .into_any_element()
22649                    }),
22650            )
22651            .child(if let Some(editor) = inline_editor {
22652                // Inline edit mode: show an editable text field
22653                div()
22654                    .flex_1()
22655                    .border_1()
22656                    .border_color(colors.border)
22657                    .rounded_md()
22658                    .bg(colors.editor_background)
22659                    .px_2()
22660                    .py_1()
22661                    .child(editor)
22662                    .into_any_element()
22663            } else {
22664                // Display mode: show the comment text
22665                div()
22666                    .flex_1()
22667                    .text_sm()
22668                    .text_color(colors.text)
22669                    .child(comment.comment)
22670                    .into_any_element()
22671            })
22672            .child(if is_editing {
22673                // Editing mode: show close and confirm buttons
22674                h_flex()
22675                    .gap_1()
22676                    .child(
22677                        IconButton::new(
22678                            format!("diff-review-cancel-edit-{comment_id}"),
22679                            IconName::Close,
22680                        )
22681                        .icon_color(ui::Color::Muted)
22682                        .icon_size(action_icon_size)
22683                        .tooltip(Tooltip::text("Cancel"))
22684                        .on_click(move |_, window, cx| {
22685                            window.dispatch_action(
22686                                Box::new(crate::actions::CancelEditReviewComment {
22687                                    id: comment_id,
22688                                }),
22689                                cx,
22690                            );
22691                        }),
22692                    )
22693                    .child(
22694                        IconButton::new(
22695                            format!("diff-review-confirm-edit-{comment_id}"),
22696                            IconName::Return,
22697                        )
22698                        .icon_color(ui::Color::Muted)
22699                        .icon_size(action_icon_size)
22700                        .tooltip(Tooltip::text("Confirm"))
22701                        .on_click(move |_, window, cx| {
22702                            window.dispatch_action(
22703                                Box::new(crate::actions::ConfirmEditReviewComment {
22704                                    id: comment_id,
22705                                }),
22706                                cx,
22707                            );
22708                        }),
22709                    )
22710                    .into_any_element()
22711            } else {
22712                // Display mode: no action buttons for now (edit/delete not yet implemented)
22713                gpui::Empty.into_any_element()
22714            })
22715    }
22716
22717    pub fn set_masked(&mut self, masked: bool, cx: &mut Context<Self>) {
22718        if self.display_map.read(cx).masked != masked {
22719            self.display_map.update(cx, |map, _| map.masked = masked);
22720        }
22721        cx.notify()
22722    }
22723
22724    pub fn set_show_wrap_guides(&mut self, show_wrap_guides: bool, cx: &mut Context<Self>) {
22725        self.show_wrap_guides = Some(show_wrap_guides);
22726        cx.notify();
22727    }
22728
22729    pub fn set_show_indent_guides(&mut self, show_indent_guides: bool, cx: &mut Context<Self>) {
22730        self.show_indent_guides = Some(show_indent_guides);
22731        cx.notify();
22732    }
22733
22734    pub fn working_directory(&self, cx: &App) -> Option<PathBuf> {
22735        if let Some(buffer) = self.buffer().read(cx).as_singleton() {
22736            if let Some(file) = buffer.read(cx).file().and_then(|f| f.as_local())
22737                && let Some(dir) = file.abs_path(cx).parent()
22738            {
22739                return Some(dir.to_owned());
22740            }
22741        }
22742
22743        None
22744    }
22745
22746    fn target_file<'a>(&self, cx: &'a App) -> Option<&'a dyn language::LocalFile> {
22747        self.active_excerpt(cx)?
22748            .1
22749            .read(cx)
22750            .file()
22751            .and_then(|f| f.as_local())
22752    }
22753
22754    pub fn target_file_abs_path(&self, cx: &mut Context<Self>) -> Option<PathBuf> {
22755        self.active_excerpt(cx).and_then(|(_, buffer, _)| {
22756            let buffer = buffer.read(cx);
22757            if let Some(project_path) = buffer.project_path(cx) {
22758                let project = self.project()?.read(cx);
22759                project.absolute_path(&project_path, cx)
22760            } else {
22761                buffer
22762                    .file()
22763                    .and_then(|file| file.as_local().map(|file| file.abs_path(cx)))
22764            }
22765        })
22766    }
22767
22768    pub fn reveal_in_finder(
22769        &mut self,
22770        _: &RevealInFileManager,
22771        _window: &mut Window,
22772        cx: &mut Context<Self>,
22773    ) {
22774        if let Some(path) = self.target_file_abs_path(cx) {
22775            if let Some(project) = self.project() {
22776                project.update(cx, |project, cx| project.reveal_path(&path, cx));
22777            } else {
22778                cx.reveal_path(&path);
22779            }
22780        }
22781    }
22782
22783    pub fn copy_path(
22784        &mut self,
22785        _: &zed_actions::workspace::CopyPath,
22786        _window: &mut Window,
22787        cx: &mut Context<Self>,
22788    ) {
22789        if let Some(path) = self.target_file_abs_path(cx)
22790            && let Some(path) = path.to_str()
22791        {
22792            cx.write_to_clipboard(ClipboardItem::new_string(path.to_string()));
22793        } else {
22794            cx.propagate();
22795        }
22796    }
22797
22798    pub fn copy_relative_path(
22799        &mut self,
22800        _: &zed_actions::workspace::CopyRelativePath,
22801        _window: &mut Window,
22802        cx: &mut Context<Self>,
22803    ) {
22804        if let Some(path) = self.active_excerpt(cx).and_then(|(_, buffer, _)| {
22805            let project = self.project()?.read(cx);
22806            let path = buffer.read(cx).file()?.path();
22807            let path = path.display(project.path_style(cx));
22808            Some(path)
22809        }) {
22810            cx.write_to_clipboard(ClipboardItem::new_string(path.to_string()));
22811        } else {
22812            cx.propagate();
22813        }
22814    }
22815
22816    /// Returns the project path for the editor's buffer, if any buffer is
22817    /// opened in the editor.
22818    pub fn project_path(&self, cx: &App) -> Option<ProjectPath> {
22819        if let Some(buffer) = self.buffer.read(cx).as_singleton() {
22820            buffer.read(cx).project_path(cx)
22821        } else {
22822            None
22823        }
22824    }
22825
22826    // Returns true if the editor handled a go-to-line request
22827    pub fn go_to_active_debug_line(&mut self, window: &mut Window, cx: &mut Context<Self>) -> bool {
22828        maybe!({
22829            let breakpoint_store = self.breakpoint_store.as_ref()?;
22830
22831            let (active_stack_frame, debug_line_pane_id) = {
22832                let store = breakpoint_store.read(cx);
22833                let active_stack_frame = store.active_position().cloned();
22834                let debug_line_pane_id = store.active_debug_line_pane_id();
22835                (active_stack_frame, debug_line_pane_id)
22836            };
22837
22838            let Some(active_stack_frame) = active_stack_frame else {
22839                self.clear_row_highlights::<ActiveDebugLine>();
22840                return None;
22841            };
22842
22843            if let Some(debug_line_pane_id) = debug_line_pane_id {
22844                if let Some(workspace) = self
22845                    .workspace
22846                    .as_ref()
22847                    .and_then(|(workspace, _)| workspace.upgrade())
22848                {
22849                    let editor_pane_id = workspace
22850                        .read(cx)
22851                        .pane_for_item_id(cx.entity_id())
22852                        .map(|pane| pane.entity_id());
22853
22854                    if editor_pane_id.is_some_and(|id| id != debug_line_pane_id) {
22855                        self.clear_row_highlights::<ActiveDebugLine>();
22856                        return None;
22857                    }
22858                }
22859            }
22860
22861            let position = active_stack_frame.position;
22862            let buffer_id = position.buffer_id?;
22863            let snapshot = self
22864                .project
22865                .as_ref()?
22866                .read(cx)
22867                .buffer_for_id(buffer_id, cx)?
22868                .read(cx)
22869                .snapshot();
22870
22871            let mut handled = false;
22872            for (id, ExcerptRange { context, .. }) in
22873                self.buffer.read(cx).excerpts_for_buffer(buffer_id, cx)
22874            {
22875                if context.start.cmp(&position, &snapshot).is_ge()
22876                    || context.end.cmp(&position, &snapshot).is_lt()
22877                {
22878                    continue;
22879                }
22880                let snapshot = self.buffer.read(cx).snapshot(cx);
22881                let multibuffer_anchor = snapshot.anchor_in_excerpt(id, position)?;
22882
22883                handled = true;
22884                self.clear_row_highlights::<ActiveDebugLine>();
22885
22886                self.go_to_line::<ActiveDebugLine>(
22887                    multibuffer_anchor,
22888                    Some(cx.theme().colors().editor_debugger_active_line_background),
22889                    window,
22890                    cx,
22891                );
22892
22893                cx.notify();
22894            }
22895
22896            handled.then_some(())
22897        })
22898        .is_some()
22899    }
22900
22901    pub fn copy_file_name_without_extension(
22902        &mut self,
22903        _: &CopyFileNameWithoutExtension,
22904        _: &mut Window,
22905        cx: &mut Context<Self>,
22906    ) {
22907        if let Some(file_stem) = self.active_excerpt(cx).and_then(|(_, buffer, _)| {
22908            let file = buffer.read(cx).file()?;
22909            file.path().file_stem()
22910        }) {
22911            cx.write_to_clipboard(ClipboardItem::new_string(file_stem.to_string()));
22912        }
22913    }
22914
22915    pub fn copy_file_name(&mut self, _: &CopyFileName, _: &mut Window, cx: &mut Context<Self>) {
22916        if let Some(file_name) = self.active_excerpt(cx).and_then(|(_, buffer, _)| {
22917            let file = buffer.read(cx).file()?;
22918            Some(file.file_name(cx))
22919        }) {
22920            cx.write_to_clipboard(ClipboardItem::new_string(file_name.to_string()));
22921        }
22922    }
22923
22924    pub fn toggle_git_blame(
22925        &mut self,
22926        _: &::git::Blame,
22927        window: &mut Window,
22928        cx: &mut Context<Self>,
22929    ) {
22930        self.show_git_blame_gutter = !self.show_git_blame_gutter;
22931
22932        if self.show_git_blame_gutter && !self.has_blame_entries(cx) {
22933            self.start_git_blame(true, window, cx);
22934        }
22935
22936        cx.notify();
22937    }
22938
22939    pub fn toggle_git_blame_inline(
22940        &mut self,
22941        _: &ToggleGitBlameInline,
22942        window: &mut Window,
22943        cx: &mut Context<Self>,
22944    ) {
22945        self.toggle_git_blame_inline_internal(true, window, cx);
22946        cx.notify();
22947    }
22948
22949    pub fn open_git_blame_commit(
22950        &mut self,
22951        _: &OpenGitBlameCommit,
22952        window: &mut Window,
22953        cx: &mut Context<Self>,
22954    ) {
22955        self.open_git_blame_commit_internal(window, cx);
22956    }
22957
22958    fn open_git_blame_commit_internal(
22959        &mut self,
22960        window: &mut Window,
22961        cx: &mut Context<Self>,
22962    ) -> Option<()> {
22963        let blame = self.blame.as_ref()?;
22964        let snapshot = self.snapshot(window, cx);
22965        let cursor = self
22966            .selections
22967            .newest::<Point>(&snapshot.display_snapshot)
22968            .head();
22969        let (buffer, point, _) = snapshot.buffer_snapshot().point_to_buffer_point(cursor)?;
22970        let (_, blame_entry) = blame
22971            .update(cx, |blame, cx| {
22972                blame
22973                    .blame_for_rows(
22974                        &[RowInfo {
22975                            buffer_id: Some(buffer.remote_id()),
22976                            buffer_row: Some(point.row),
22977                            ..Default::default()
22978                        }],
22979                        cx,
22980                    )
22981                    .next()
22982            })
22983            .flatten()?;
22984        let renderer = cx.global::<GlobalBlameRenderer>().0.clone();
22985        let repo = blame.read(cx).repository(cx, buffer.remote_id())?;
22986        let workspace = self.workspace()?.downgrade();
22987        renderer.open_blame_commit(blame_entry, repo, workspace, window, cx);
22988        None
22989    }
22990
22991    pub fn git_blame_inline_enabled(&self) -> bool {
22992        self.git_blame_inline_enabled
22993    }
22994
22995    pub fn toggle_selection_menu(
22996        &mut self,
22997        _: &ToggleSelectionMenu,
22998        _: &mut Window,
22999        cx: &mut Context<Self>,
23000    ) {
23001        self.show_selection_menu = self
23002            .show_selection_menu
23003            .map(|show_selections_menu| !show_selections_menu)
23004            .or_else(|| Some(!EditorSettings::get_global(cx).toolbar.selections_menu));
23005
23006        cx.notify();
23007    }
23008
23009    pub fn selection_menu_enabled(&self, cx: &App) -> bool {
23010        self.show_selection_menu
23011            .unwrap_or_else(|| EditorSettings::get_global(cx).toolbar.selections_menu)
23012    }
23013
23014    fn start_git_blame(
23015        &mut self,
23016        user_triggered: bool,
23017        window: &mut Window,
23018        cx: &mut Context<Self>,
23019    ) {
23020        if let Some(project) = self.project() {
23021            if let Some(buffer) = self.buffer().read(cx).as_singleton()
23022                && buffer.read(cx).file().is_none()
23023            {
23024                return;
23025            }
23026
23027            let focused = self.focus_handle(cx).contains_focused(window, cx);
23028
23029            let project = project.clone();
23030            let blame = cx
23031                .new(|cx| GitBlame::new(self.buffer.clone(), project, user_triggered, focused, cx));
23032            self.blame_subscription =
23033                Some(cx.observe_in(&blame, window, |_, _, _, cx| cx.notify()));
23034            self.blame = Some(blame);
23035        }
23036    }
23037
23038    fn toggle_git_blame_inline_internal(
23039        &mut self,
23040        user_triggered: bool,
23041        window: &mut Window,
23042        cx: &mut Context<Self>,
23043    ) {
23044        if self.git_blame_inline_enabled {
23045            self.git_blame_inline_enabled = false;
23046            self.show_git_blame_inline = false;
23047            self.show_git_blame_inline_delay_task.take();
23048        } else {
23049            self.git_blame_inline_enabled = true;
23050            self.start_git_blame_inline(user_triggered, window, cx);
23051        }
23052
23053        cx.notify();
23054    }
23055
23056    fn start_git_blame_inline(
23057        &mut self,
23058        user_triggered: bool,
23059        window: &mut Window,
23060        cx: &mut Context<Self>,
23061    ) {
23062        self.start_git_blame(user_triggered, window, cx);
23063
23064        if ProjectSettings::get_global(cx)
23065            .git
23066            .inline_blame_delay()
23067            .is_some()
23068        {
23069            self.start_inline_blame_timer(window, cx);
23070        } else {
23071            self.show_git_blame_inline = true
23072        }
23073    }
23074
23075    pub fn blame(&self) -> Option<&Entity<GitBlame>> {
23076        self.blame.as_ref()
23077    }
23078
23079    pub fn show_git_blame_gutter(&self) -> bool {
23080        self.show_git_blame_gutter
23081    }
23082
23083    pub fn render_git_blame_gutter(&self, cx: &App) -> bool {
23084        !self.mode().is_minimap() && self.show_git_blame_gutter && self.has_blame_entries(cx)
23085    }
23086
23087    pub fn render_git_blame_inline(&self, window: &Window, cx: &App) -> bool {
23088        self.show_git_blame_inline
23089            && (self.focus_handle.is_focused(window) || self.inline_blame_popover.is_some())
23090            && !self.newest_selection_head_on_empty_line(cx)
23091            && self.has_blame_entries(cx)
23092    }
23093
23094    fn has_blame_entries(&self, cx: &App) -> bool {
23095        self.blame()
23096            .is_some_and(|blame| blame.read(cx).has_generated_entries())
23097    }
23098
23099    fn newest_selection_head_on_empty_line(&self, cx: &App) -> bool {
23100        let cursor_anchor = self.selections.newest_anchor().head();
23101
23102        let snapshot = self.buffer.read(cx).snapshot(cx);
23103        let buffer_row = MultiBufferRow(cursor_anchor.to_point(&snapshot).row);
23104
23105        snapshot.line_len(buffer_row) == 0
23106    }
23107
23108    fn get_permalink_to_line(&self, cx: &mut Context<Self>) -> Task<Result<url::Url>> {
23109        let buffer_and_selection = maybe!({
23110            let selection = self.selections.newest::<Point>(&self.display_snapshot(cx));
23111            let selection_range = selection.range();
23112
23113            let multi_buffer = self.buffer().read(cx);
23114            let multi_buffer_snapshot = multi_buffer.snapshot(cx);
23115            let buffer_ranges = multi_buffer_snapshot
23116                .range_to_buffer_ranges(selection_range.start..=selection_range.end);
23117
23118            let (buffer, range, _) = if selection.reversed {
23119                buffer_ranges.first()
23120            } else {
23121                buffer_ranges.last()
23122            }?;
23123
23124            let buffer_range = range.to_point(buffer);
23125
23126            let Some(buffer_diff) = multi_buffer.diff_for(buffer.remote_id()) else {
23127                return Some((
23128                    multi_buffer.buffer(buffer.remote_id()).unwrap(),
23129                    buffer_range.start.row..buffer_range.end.row,
23130                ));
23131            };
23132
23133            let buffer_diff_snapshot = buffer_diff.read(cx).snapshot(cx);
23134            let start =
23135                buffer_diff_snapshot.buffer_point_to_base_text_point(buffer_range.start, buffer);
23136            let end =
23137                buffer_diff_snapshot.buffer_point_to_base_text_point(buffer_range.end, buffer);
23138
23139            Some((
23140                multi_buffer.buffer(buffer.remote_id()).unwrap(),
23141                start.row..end.row,
23142            ))
23143        });
23144
23145        let Some((buffer, selection)) = buffer_and_selection else {
23146            return Task::ready(Err(anyhow!("failed to determine buffer and selection")));
23147        };
23148
23149        let Some(project) = self.project() else {
23150            return Task::ready(Err(anyhow!("editor does not have project")));
23151        };
23152
23153        project.update(cx, |project, cx| {
23154            project.get_permalink_to_line(&buffer, selection, cx)
23155        })
23156    }
23157
23158    pub fn copy_permalink_to_line(
23159        &mut self,
23160        _: &CopyPermalinkToLine,
23161        window: &mut Window,
23162        cx: &mut Context<Self>,
23163    ) {
23164        let permalink_task = self.get_permalink_to_line(cx);
23165        let workspace = self.workspace();
23166
23167        cx.spawn_in(window, async move |_, cx| match permalink_task.await {
23168            Ok(permalink) => {
23169                cx.update(|_, cx| {
23170                    cx.write_to_clipboard(ClipboardItem::new_string(permalink.to_string()));
23171                })
23172                .ok();
23173            }
23174            Err(err) => {
23175                let message = format!("Failed to copy permalink: {err}");
23176
23177                anyhow::Result::<()>::Err(err).log_err();
23178
23179                if let Some(workspace) = workspace {
23180                    workspace
23181                        .update_in(cx, |workspace, _, cx| {
23182                            struct CopyPermalinkToLine;
23183
23184                            workspace.show_toast(
23185                                Toast::new(
23186                                    NotificationId::unique::<CopyPermalinkToLine>(),
23187                                    message,
23188                                ),
23189                                cx,
23190                            )
23191                        })
23192                        .ok();
23193                }
23194            }
23195        })
23196        .detach();
23197    }
23198
23199    pub fn copy_file_location(
23200        &mut self,
23201        _: &CopyFileLocation,
23202        _: &mut Window,
23203        cx: &mut Context<Self>,
23204    ) {
23205        let selection = self
23206            .selections
23207            .newest::<Point>(&self.display_snapshot(cx))
23208            .start
23209            .row
23210            + 1;
23211        if let Some(file_location) = self.active_excerpt(cx).and_then(|(_, buffer, _)| {
23212            let project = self.project()?.read(cx);
23213            let file = buffer.read(cx).file()?;
23214            let path = file.path().display(project.path_style(cx));
23215
23216            Some(format!("{path}:{selection}"))
23217        }) {
23218            cx.write_to_clipboard(ClipboardItem::new_string(file_location));
23219        }
23220    }
23221
23222    pub fn open_permalink_to_line(
23223        &mut self,
23224        _: &OpenPermalinkToLine,
23225        window: &mut Window,
23226        cx: &mut Context<Self>,
23227    ) {
23228        let permalink_task = self.get_permalink_to_line(cx);
23229        let workspace = self.workspace();
23230
23231        cx.spawn_in(window, async move |_, cx| match permalink_task.await {
23232            Ok(permalink) => {
23233                cx.update(|_, cx| {
23234                    cx.open_url(permalink.as_ref());
23235                })
23236                .ok();
23237            }
23238            Err(err) => {
23239                let message = format!("Failed to open permalink: {err}");
23240
23241                anyhow::Result::<()>::Err(err).log_err();
23242
23243                if let Some(workspace) = workspace {
23244                    workspace.update(cx, |workspace, cx| {
23245                        struct OpenPermalinkToLine;
23246
23247                        workspace.show_toast(
23248                            Toast::new(NotificationId::unique::<OpenPermalinkToLine>(), message),
23249                            cx,
23250                        )
23251                    });
23252                }
23253            }
23254        })
23255        .detach();
23256    }
23257
23258    pub fn insert_uuid_v4(
23259        &mut self,
23260        _: &InsertUuidV4,
23261        window: &mut Window,
23262        cx: &mut Context<Self>,
23263    ) {
23264        self.insert_uuid(UuidVersion::V4, window, cx);
23265    }
23266
23267    pub fn insert_uuid_v7(
23268        &mut self,
23269        _: &InsertUuidV7,
23270        window: &mut Window,
23271        cx: &mut Context<Self>,
23272    ) {
23273        self.insert_uuid(UuidVersion::V7, window, cx);
23274    }
23275
23276    fn insert_uuid(&mut self, version: UuidVersion, window: &mut Window, cx: &mut Context<Self>) {
23277        self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
23278        self.transact(window, cx, |this, window, cx| {
23279            let edits = this
23280                .selections
23281                .all::<Point>(&this.display_snapshot(cx))
23282                .into_iter()
23283                .map(|selection| {
23284                    let uuid = match version {
23285                        UuidVersion::V4 => uuid::Uuid::new_v4(),
23286                        UuidVersion::V7 => uuid::Uuid::now_v7(),
23287                    };
23288
23289                    (selection.range(), uuid.to_string())
23290                });
23291            this.edit(edits, cx);
23292            this.refresh_edit_prediction(true, false, window, cx);
23293        });
23294    }
23295
23296    pub fn open_selections_in_multibuffer(
23297        &mut self,
23298        _: &OpenSelectionsInMultibuffer,
23299        window: &mut Window,
23300        cx: &mut Context<Self>,
23301    ) {
23302        let multibuffer = self.buffer.read(cx);
23303
23304        let Some(buffer) = multibuffer.as_singleton() else {
23305            return;
23306        };
23307
23308        let Some(workspace) = self.workspace() else {
23309            return;
23310        };
23311
23312        let title = multibuffer.title(cx).to_string();
23313
23314        let locations = self
23315            .selections
23316            .all_anchors(&self.display_snapshot(cx))
23317            .iter()
23318            .map(|selection| {
23319                (
23320                    buffer.clone(),
23321                    (selection.start.text_anchor..selection.end.text_anchor)
23322                        .to_point(buffer.read(cx)),
23323                )
23324            })
23325            .into_group_map();
23326
23327        cx.spawn_in(window, async move |_, cx| {
23328            workspace.update_in(cx, |workspace, window, cx| {
23329                Self::open_locations_in_multibuffer(
23330                    workspace,
23331                    locations,
23332                    format!("Selections for '{title}'"),
23333                    false,
23334                    false,
23335                    MultibufferSelectionMode::All,
23336                    window,
23337                    cx,
23338                );
23339            })
23340        })
23341        .detach();
23342    }
23343
23344    /// Adds a row highlight for the given range. If a row has multiple highlights, the
23345    /// last highlight added will be used.
23346    ///
23347    /// If the range ends at the beginning of a line, then that line will not be highlighted.
23348    pub fn highlight_rows<T: 'static>(
23349        &mut self,
23350        range: Range<Anchor>,
23351        color: Hsla,
23352        options: RowHighlightOptions,
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 ix = row_highlights.binary_search_by(|highlight| {
23358            Ordering::Equal
23359                .then_with(|| highlight.range.start.cmp(&range.start, &snapshot))
23360                .then_with(|| highlight.range.end.cmp(&range.end, &snapshot))
23361        });
23362
23363        if let Err(mut ix) = ix {
23364            let index = post_inc(&mut self.highlight_order);
23365
23366            // If this range intersects with the preceding highlight, then merge it with
23367            // the preceding highlight. Otherwise insert a new highlight.
23368            let mut merged = false;
23369            if ix > 0 {
23370                let prev_highlight = &mut row_highlights[ix - 1];
23371                if prev_highlight
23372                    .range
23373                    .end
23374                    .cmp(&range.start, &snapshot)
23375                    .is_ge()
23376                {
23377                    ix -= 1;
23378                    if prev_highlight.range.end.cmp(&range.end, &snapshot).is_lt() {
23379                        prev_highlight.range.end = range.end;
23380                    }
23381                    merged = true;
23382                    prev_highlight.index = index;
23383                    prev_highlight.color = color;
23384                    prev_highlight.options = options;
23385                }
23386            }
23387
23388            if !merged {
23389                row_highlights.insert(
23390                    ix,
23391                    RowHighlight {
23392                        range,
23393                        index,
23394                        color,
23395                        options,
23396                        type_id: TypeId::of::<T>(),
23397                    },
23398                );
23399            }
23400
23401            // If any of the following highlights intersect with this one, merge them.
23402            while let Some(next_highlight) = row_highlights.get(ix + 1) {
23403                let highlight = &row_highlights[ix];
23404                if next_highlight
23405                    .range
23406                    .start
23407                    .cmp(&highlight.range.end, &snapshot)
23408                    .is_le()
23409                {
23410                    if next_highlight
23411                        .range
23412                        .end
23413                        .cmp(&highlight.range.end, &snapshot)
23414                        .is_gt()
23415                    {
23416                        row_highlights[ix].range.end = next_highlight.range.end;
23417                    }
23418                    row_highlights.remove(ix + 1);
23419                } else {
23420                    break;
23421                }
23422            }
23423        }
23424    }
23425
23426    /// Remove any highlighted row ranges of the given type that intersect the
23427    /// given ranges.
23428    pub fn remove_highlighted_rows<T: 'static>(
23429        &mut self,
23430        ranges_to_remove: Vec<Range<Anchor>>,
23431        cx: &mut Context<Self>,
23432    ) {
23433        let snapshot = self.buffer().read(cx).snapshot(cx);
23434        let row_highlights = self.highlighted_rows.entry(TypeId::of::<T>()).or_default();
23435        let mut ranges_to_remove = ranges_to_remove.iter().peekable();
23436        row_highlights.retain(|highlight| {
23437            while let Some(range_to_remove) = ranges_to_remove.peek() {
23438                match range_to_remove.end.cmp(&highlight.range.start, &snapshot) {
23439                    Ordering::Less | Ordering::Equal => {
23440                        ranges_to_remove.next();
23441                    }
23442                    Ordering::Greater => {
23443                        match range_to_remove.start.cmp(&highlight.range.end, &snapshot) {
23444                            Ordering::Less | Ordering::Equal => {
23445                                return false;
23446                            }
23447                            Ordering::Greater => break,
23448                        }
23449                    }
23450                }
23451            }
23452
23453            true
23454        })
23455    }
23456
23457    /// Clear all anchor ranges for a certain highlight context type, so no corresponding rows will be highlighted.
23458    pub fn clear_row_highlights<T: 'static>(&mut self) {
23459        self.highlighted_rows.remove(&TypeId::of::<T>());
23460    }
23461
23462    /// For a highlight given context type, gets all anchor ranges that will be used for row highlighting.
23463    pub fn highlighted_rows<T: 'static>(&self) -> impl '_ + Iterator<Item = (Range<Anchor>, Hsla)> {
23464        self.highlighted_rows
23465            .get(&TypeId::of::<T>())
23466            .map_or(&[] as &[_], |vec| vec.as_slice())
23467            .iter()
23468            .map(|highlight| (highlight.range.clone(), highlight.color))
23469    }
23470
23471    /// Merges all anchor ranges for all context types ever set, picking the last highlight added in case of a row conflict.
23472    /// Returns a map of display rows that are highlighted and their corresponding highlight color.
23473    /// Allows to ignore certain kinds of highlights.
23474    pub fn highlighted_display_rows(
23475        &self,
23476        window: &mut Window,
23477        cx: &mut App,
23478    ) -> BTreeMap<DisplayRow, LineHighlight> {
23479        let snapshot = self.snapshot(window, cx);
23480        let mut used_highlight_orders = HashMap::default();
23481        self.highlighted_rows
23482            .iter()
23483            .flat_map(|(_, highlighted_rows)| highlighted_rows.iter())
23484            .fold(
23485                BTreeMap::<DisplayRow, LineHighlight>::new(),
23486                |mut unique_rows, highlight| {
23487                    let start = highlight.range.start.to_display_point(&snapshot);
23488                    let end = highlight.range.end.to_display_point(&snapshot);
23489                    let start_row = start.row().0;
23490                    let end_row = if !highlight.range.end.text_anchor.is_max() && end.column() == 0
23491                    {
23492                        end.row().0.saturating_sub(1)
23493                    } else {
23494                        end.row().0
23495                    };
23496                    for row in start_row..=end_row {
23497                        let used_index =
23498                            used_highlight_orders.entry(row).or_insert(highlight.index);
23499                        if highlight.index >= *used_index {
23500                            *used_index = highlight.index;
23501                            unique_rows.insert(
23502                                DisplayRow(row),
23503                                LineHighlight {
23504                                    include_gutter: highlight.options.include_gutter,
23505                                    border: None,
23506                                    background: highlight.color.into(),
23507                                    type_id: Some(highlight.type_id),
23508                                },
23509                            );
23510                        }
23511                    }
23512                    unique_rows
23513                },
23514            )
23515    }
23516
23517    pub fn highlighted_display_row_for_autoscroll(
23518        &self,
23519        snapshot: &DisplaySnapshot,
23520    ) -> Option<DisplayRow> {
23521        self.highlighted_rows
23522            .values()
23523            .flat_map(|highlighted_rows| highlighted_rows.iter())
23524            .filter_map(|highlight| {
23525                if highlight.options.autoscroll {
23526                    Some(highlight.range.start.to_display_point(snapshot).row())
23527                } else {
23528                    None
23529                }
23530            })
23531            .min()
23532    }
23533
23534    pub fn set_search_within_ranges(&mut self, ranges: &[Range<Anchor>], cx: &mut Context<Self>) {
23535        self.highlight_background(
23536            HighlightKey::SearchWithinRange,
23537            ranges,
23538            |_, colors| colors.colors().editor_document_highlight_read_background,
23539            cx,
23540        )
23541    }
23542
23543    pub fn set_breadcrumb_header(&mut self, new_header: String) {
23544        self.breadcrumb_header = Some(new_header);
23545    }
23546
23547    pub fn clear_search_within_ranges(&mut self, cx: &mut Context<Self>) {
23548        self.clear_background_highlights(HighlightKey::SearchWithinRange, cx);
23549    }
23550
23551    pub fn highlight_background(
23552        &mut self,
23553        key: HighlightKey,
23554        ranges: &[Range<Anchor>],
23555        color_fetcher: impl Fn(&usize, &Theme) -> Hsla + Send + Sync + 'static,
23556        cx: &mut Context<Self>,
23557    ) {
23558        self.background_highlights
23559            .insert(key, (Arc::new(color_fetcher), Arc::from(ranges)));
23560        self.scrollbar_marker_state.dirty = true;
23561        cx.notify();
23562    }
23563
23564    pub fn clear_background_highlights(
23565        &mut self,
23566        key: HighlightKey,
23567        cx: &mut Context<Self>,
23568    ) -> Option<BackgroundHighlight> {
23569        let text_highlights = self.background_highlights.remove(&key)?;
23570        if !text_highlights.1.is_empty() {
23571            self.scrollbar_marker_state.dirty = true;
23572            cx.notify();
23573        }
23574        Some(text_highlights)
23575    }
23576
23577    pub fn highlight_gutter<T: 'static>(
23578        &mut self,
23579        ranges: impl Into<Vec<Range<Anchor>>>,
23580        color_fetcher: fn(&App) -> Hsla,
23581        cx: &mut Context<Self>,
23582    ) {
23583        self.gutter_highlights
23584            .insert(TypeId::of::<T>(), (color_fetcher, ranges.into()));
23585        cx.notify();
23586    }
23587
23588    pub fn clear_gutter_highlights<T: 'static>(
23589        &mut self,
23590        cx: &mut Context<Self>,
23591    ) -> Option<GutterHighlight> {
23592        cx.notify();
23593        self.gutter_highlights.remove(&TypeId::of::<T>())
23594    }
23595
23596    pub fn insert_gutter_highlight<T: 'static>(
23597        &mut self,
23598        range: Range<Anchor>,
23599        color_fetcher: fn(&App) -> Hsla,
23600        cx: &mut Context<Self>,
23601    ) {
23602        let snapshot = self.buffer().read(cx).snapshot(cx);
23603        let mut highlights = self
23604            .gutter_highlights
23605            .remove(&TypeId::of::<T>())
23606            .map(|(_, highlights)| highlights)
23607            .unwrap_or_default();
23608        let ix = highlights.binary_search_by(|highlight| {
23609            Ordering::Equal
23610                .then_with(|| highlight.start.cmp(&range.start, &snapshot))
23611                .then_with(|| highlight.end.cmp(&range.end, &snapshot))
23612        });
23613        if let Err(ix) = ix {
23614            highlights.insert(ix, range);
23615        }
23616        self.gutter_highlights
23617            .insert(TypeId::of::<T>(), (color_fetcher, highlights));
23618    }
23619
23620    pub fn remove_gutter_highlights<T: 'static>(
23621        &mut self,
23622        ranges_to_remove: Vec<Range<Anchor>>,
23623        cx: &mut Context<Self>,
23624    ) {
23625        let snapshot = self.buffer().read(cx).snapshot(cx);
23626        let Some((color_fetcher, mut gutter_highlights)) =
23627            self.gutter_highlights.remove(&TypeId::of::<T>())
23628        else {
23629            return;
23630        };
23631        let mut ranges_to_remove = ranges_to_remove.iter().peekable();
23632        gutter_highlights.retain(|highlight| {
23633            while let Some(range_to_remove) = ranges_to_remove.peek() {
23634                match range_to_remove.end.cmp(&highlight.start, &snapshot) {
23635                    Ordering::Less | Ordering::Equal => {
23636                        ranges_to_remove.next();
23637                    }
23638                    Ordering::Greater => {
23639                        match range_to_remove.start.cmp(&highlight.end, &snapshot) {
23640                            Ordering::Less | Ordering::Equal => {
23641                                return false;
23642                            }
23643                            Ordering::Greater => break,
23644                        }
23645                    }
23646                }
23647            }
23648
23649            true
23650        });
23651        self.gutter_highlights
23652            .insert(TypeId::of::<T>(), (color_fetcher, gutter_highlights));
23653    }
23654
23655    #[cfg(any(test, feature = "test-support"))]
23656    pub fn all_text_highlights(
23657        &self,
23658        window: &mut Window,
23659        cx: &mut Context<Self>,
23660    ) -> Vec<(HighlightStyle, Vec<Range<DisplayPoint>>)> {
23661        let snapshot = self.snapshot(window, cx);
23662        self.display_map.update(cx, |display_map, _| {
23663            display_map
23664                .all_text_highlights()
23665                .map(|(_, highlight)| {
23666                    let (style, ranges) = highlight.as_ref();
23667                    (
23668                        *style,
23669                        ranges
23670                            .iter()
23671                            .map(|range| range.clone().to_display_points(&snapshot))
23672                            .collect(),
23673                    )
23674                })
23675                .collect()
23676        })
23677    }
23678
23679    #[cfg(any(test, feature = "test-support"))]
23680    pub fn all_text_background_highlights(
23681        &self,
23682        window: &mut Window,
23683        cx: &mut Context<Self>,
23684    ) -> Vec<(Range<DisplayPoint>, Hsla)> {
23685        let snapshot = self.snapshot(window, cx);
23686        let buffer = &snapshot.buffer_snapshot();
23687        let start = buffer.anchor_before(MultiBufferOffset(0));
23688        let end = buffer.anchor_after(buffer.len());
23689        self.sorted_background_highlights_in_range(start..end, &snapshot, cx.theme())
23690    }
23691
23692    #[cfg(any(test, feature = "test-support"))]
23693    pub fn sorted_background_highlights_in_range(
23694        &self,
23695        search_range: Range<Anchor>,
23696        display_snapshot: &DisplaySnapshot,
23697        theme: &Theme,
23698    ) -> Vec<(Range<DisplayPoint>, Hsla)> {
23699        let mut res = self.background_highlights_in_range(search_range, display_snapshot, theme);
23700        res.sort_by(|a, b| {
23701            a.0.start
23702                .cmp(&b.0.start)
23703                .then_with(|| a.0.end.cmp(&b.0.end))
23704                .then_with(|| a.1.cmp(&b.1))
23705        });
23706        res
23707    }
23708
23709    #[cfg(any(test, feature = "test-support"))]
23710    pub fn search_background_highlights(&mut self, cx: &mut Context<Self>) -> Vec<Range<Point>> {
23711        let snapshot = self.buffer().read(cx).snapshot(cx);
23712
23713        let highlights = self
23714            .background_highlights
23715            .get(&HighlightKey::BufferSearchHighlights);
23716
23717        if let Some((_color, ranges)) = highlights {
23718            ranges
23719                .iter()
23720                .map(|range| range.start.to_point(&snapshot)..range.end.to_point(&snapshot))
23721                .collect_vec()
23722        } else {
23723            vec![]
23724        }
23725    }
23726
23727    fn document_highlights_for_position<'a>(
23728        &'a self,
23729        position: Anchor,
23730        buffer: &'a MultiBufferSnapshot,
23731    ) -> impl 'a + Iterator<Item = &'a Range<Anchor>> {
23732        let read_highlights = self
23733            .background_highlights
23734            .get(&HighlightKey::DocumentHighlightRead)
23735            .map(|h| &h.1);
23736        let write_highlights = self
23737            .background_highlights
23738            .get(&HighlightKey::DocumentHighlightWrite)
23739            .map(|h| &h.1);
23740        let left_position = position.bias_left(buffer);
23741        let right_position = position.bias_right(buffer);
23742        read_highlights
23743            .into_iter()
23744            .chain(write_highlights)
23745            .flat_map(move |ranges| {
23746                let start_ix = match ranges.binary_search_by(|probe| {
23747                    let cmp = probe.end.cmp(&left_position, buffer);
23748                    if cmp.is_ge() {
23749                        Ordering::Greater
23750                    } else {
23751                        Ordering::Less
23752                    }
23753                }) {
23754                    Ok(i) | Err(i) => i,
23755                };
23756
23757                ranges[start_ix..]
23758                    .iter()
23759                    .take_while(move |range| range.start.cmp(&right_position, buffer).is_le())
23760            })
23761    }
23762
23763    pub fn has_background_highlights(&self, key: HighlightKey) -> bool {
23764        self.background_highlights
23765            .get(&key)
23766            .is_some_and(|(_, highlights)| !highlights.is_empty())
23767    }
23768
23769    /// Returns all background highlights for a given range.
23770    ///
23771    /// The order of highlights is not deterministic, do sort the ranges if needed for the logic.
23772    pub fn background_highlights_in_range(
23773        &self,
23774        search_range: Range<Anchor>,
23775        display_snapshot: &DisplaySnapshot,
23776        theme: &Theme,
23777    ) -> Vec<(Range<DisplayPoint>, Hsla)> {
23778        let mut results = Vec::new();
23779        for (color_fetcher, ranges) in self.background_highlights.values() {
23780            let start_ix = match ranges.binary_search_by(|probe| {
23781                let cmp = probe
23782                    .end
23783                    .cmp(&search_range.start, &display_snapshot.buffer_snapshot());
23784                if cmp.is_gt() {
23785                    Ordering::Greater
23786                } else {
23787                    Ordering::Less
23788                }
23789            }) {
23790                Ok(i) | Err(i) => i,
23791            };
23792            for (index, range) in ranges[start_ix..].iter().enumerate() {
23793                if range
23794                    .start
23795                    .cmp(&search_range.end, &display_snapshot.buffer_snapshot())
23796                    .is_ge()
23797                {
23798                    break;
23799                }
23800
23801                let color = color_fetcher(&(start_ix + index), theme);
23802                let start = range.start.to_display_point(display_snapshot);
23803                let end = range.end.to_display_point(display_snapshot);
23804                results.push((start..end, color))
23805            }
23806        }
23807        results
23808    }
23809
23810    pub fn gutter_highlights_in_range(
23811        &self,
23812        search_range: Range<Anchor>,
23813        display_snapshot: &DisplaySnapshot,
23814        cx: &App,
23815    ) -> Vec<(Range<DisplayPoint>, Hsla)> {
23816        let mut results = Vec::new();
23817        for (color_fetcher, ranges) in self.gutter_highlights.values() {
23818            let color = color_fetcher(cx);
23819            let start_ix = match ranges.binary_search_by(|probe| {
23820                let cmp = probe
23821                    .end
23822                    .cmp(&search_range.start, &display_snapshot.buffer_snapshot());
23823                if cmp.is_gt() {
23824                    Ordering::Greater
23825                } else {
23826                    Ordering::Less
23827                }
23828            }) {
23829                Ok(i) | Err(i) => i,
23830            };
23831            for range in &ranges[start_ix..] {
23832                if range
23833                    .start
23834                    .cmp(&search_range.end, &display_snapshot.buffer_snapshot())
23835                    .is_ge()
23836                {
23837                    break;
23838                }
23839
23840                let start = range.start.to_display_point(display_snapshot);
23841                let end = range.end.to_display_point(display_snapshot);
23842                results.push((start..end, color))
23843            }
23844        }
23845        results
23846    }
23847
23848    /// Get the text ranges corresponding to the redaction query
23849    pub fn redacted_ranges(
23850        &self,
23851        search_range: Range<Anchor>,
23852        display_snapshot: &DisplaySnapshot,
23853        cx: &App,
23854    ) -> Vec<Range<DisplayPoint>> {
23855        display_snapshot
23856            .buffer_snapshot()
23857            .redacted_ranges(search_range, |file| {
23858                if let Some(file) = file {
23859                    file.is_private()
23860                        && EditorSettings::get(
23861                            Some(SettingsLocation {
23862                                worktree_id: file.worktree_id(cx),
23863                                path: file.path().as_ref(),
23864                            }),
23865                            cx,
23866                        )
23867                        .redact_private_values
23868                } else {
23869                    false
23870                }
23871            })
23872            .map(|range| {
23873                range.start.to_display_point(display_snapshot)
23874                    ..range.end.to_display_point(display_snapshot)
23875            })
23876            .collect()
23877    }
23878
23879    pub fn highlight_text_key(
23880        &mut self,
23881        key: HighlightKey,
23882        ranges: Vec<Range<Anchor>>,
23883        style: HighlightStyle,
23884        merge: bool,
23885        cx: &mut Context<Self>,
23886    ) {
23887        self.display_map.update(cx, |map, cx| {
23888            map.highlight_text(key, ranges, style, merge, cx);
23889        });
23890        cx.notify();
23891    }
23892
23893    pub fn highlight_text(
23894        &mut self,
23895        key: HighlightKey,
23896        ranges: Vec<Range<Anchor>>,
23897        style: HighlightStyle,
23898        cx: &mut Context<Self>,
23899    ) {
23900        self.display_map.update(cx, |map, cx| {
23901            map.highlight_text(key, ranges, style, false, cx)
23902        });
23903        cx.notify();
23904    }
23905
23906    pub fn text_highlights<'a>(
23907        &'a self,
23908        key: HighlightKey,
23909        cx: &'a App,
23910    ) -> Option<(HighlightStyle, &'a [Range<Anchor>])> {
23911        self.display_map.read(cx).text_highlights(key)
23912    }
23913
23914    pub fn clear_highlights(&mut self, key: HighlightKey, cx: &mut Context<Self>) {
23915        let cleared = self
23916            .display_map
23917            .update(cx, |map, _| map.clear_highlights(key));
23918        if cleared {
23919            cx.notify();
23920        }
23921    }
23922
23923    pub fn clear_highlights_with(
23924        &mut self,
23925        f: &mut dyn FnMut(&HighlightKey) -> bool,
23926        cx: &mut Context<Self>,
23927    ) {
23928        let cleared = self
23929            .display_map
23930            .update(cx, |map, _| map.clear_highlights_with(f));
23931        if cleared {
23932            cx.notify();
23933        }
23934    }
23935
23936    pub fn show_local_cursors(&self, window: &mut Window, cx: &mut App) -> bool {
23937        (self.read_only(cx) || self.blink_manager.read(cx).visible())
23938            && self.focus_handle.is_focused(window)
23939    }
23940
23941    pub fn set_show_cursor_when_unfocused(&mut self, is_enabled: bool, cx: &mut Context<Self>) {
23942        self.show_cursor_when_unfocused = is_enabled;
23943        cx.notify();
23944    }
23945
23946    fn on_buffer_changed(&mut self, _: Entity<MultiBuffer>, cx: &mut Context<Self>) {
23947        cx.notify();
23948    }
23949
23950    fn on_debug_session_event(
23951        &mut self,
23952        _session: Entity<Session>,
23953        event: &SessionEvent,
23954        cx: &mut Context<Self>,
23955    ) {
23956        if let SessionEvent::InvalidateInlineValue = event {
23957            self.refresh_inline_values(cx);
23958        }
23959    }
23960
23961    pub fn refresh_inline_values(&mut self, cx: &mut Context<Self>) {
23962        let Some(semantics) = self.semantics_provider.clone() else {
23963            return;
23964        };
23965
23966        if !self.inline_value_cache.enabled {
23967            let inlays = std::mem::take(&mut self.inline_value_cache.inlays);
23968            self.splice_inlays(&inlays, Vec::new(), cx);
23969            return;
23970        }
23971
23972        let current_execution_position = self
23973            .highlighted_rows
23974            .get(&TypeId::of::<ActiveDebugLine>())
23975            .and_then(|lines| lines.last().map(|line| line.range.end));
23976
23977        self.inline_value_cache.refresh_task = cx.spawn(async move |editor, cx| {
23978            let inline_values = editor
23979                .update(cx, |editor, cx| {
23980                    let Some(current_execution_position) = current_execution_position else {
23981                        return Some(Task::ready(Ok(Vec::new())));
23982                    };
23983
23984                    let buffer = editor.buffer.read_with(cx, |buffer, cx| {
23985                        let snapshot = buffer.snapshot(cx);
23986
23987                        let excerpt = snapshot.excerpt_containing(
23988                            current_execution_position..current_execution_position,
23989                        )?;
23990
23991                        editor.buffer.read(cx).buffer(excerpt.buffer_id())
23992                    })?;
23993
23994                    let range =
23995                        buffer.read(cx).anchor_before(0)..current_execution_position.text_anchor;
23996
23997                    semantics.inline_values(buffer, range, cx)
23998                })
23999                .ok()
24000                .flatten()?
24001                .await
24002                .context("refreshing debugger inlays")
24003                .log_err()?;
24004
24005            let mut buffer_inline_values: HashMap<BufferId, Vec<InlayHint>> = HashMap::default();
24006
24007            for (buffer_id, inline_value) in inline_values
24008                .into_iter()
24009                .filter_map(|hint| Some((hint.position.buffer_id?, hint)))
24010            {
24011                buffer_inline_values
24012                    .entry(buffer_id)
24013                    .or_default()
24014                    .push(inline_value);
24015            }
24016
24017            editor
24018                .update(cx, |editor, cx| {
24019                    let snapshot = editor.buffer.read(cx).snapshot(cx);
24020                    let mut new_inlays = Vec::default();
24021
24022                    for (excerpt_id, buffer_snapshot, _) in snapshot.excerpts() {
24023                        let buffer_id = buffer_snapshot.remote_id();
24024                        buffer_inline_values
24025                            .get(&buffer_id)
24026                            .into_iter()
24027                            .flatten()
24028                            .for_each(|hint| {
24029                                let inlay = Inlay::debugger(
24030                                    post_inc(&mut editor.next_inlay_id),
24031                                    Anchor::in_buffer(excerpt_id, hint.position),
24032                                    hint.text(),
24033                                );
24034                                if !inlay.text().chars().contains(&'\n') {
24035                                    new_inlays.push(inlay);
24036                                }
24037                            });
24038                    }
24039
24040                    let mut inlay_ids = new_inlays.iter().map(|inlay| inlay.id).collect();
24041                    std::mem::swap(&mut editor.inline_value_cache.inlays, &mut inlay_ids);
24042
24043                    editor.splice_inlays(&inlay_ids, new_inlays, cx);
24044                })
24045                .ok()?;
24046            Some(())
24047        });
24048    }
24049
24050    fn on_buffer_event(
24051        &mut self,
24052        multibuffer: &Entity<MultiBuffer>,
24053        event: &multi_buffer::Event,
24054        window: &mut Window,
24055        cx: &mut Context<Self>,
24056    ) {
24057        match event {
24058            multi_buffer::Event::Edited { edited_buffer } => {
24059                self.scrollbar_marker_state.dirty = true;
24060                self.active_indent_guides_state.dirty = true;
24061                self.refresh_active_diagnostics(cx);
24062                self.refresh_code_actions(window, cx);
24063                self.refresh_single_line_folds(window, cx);
24064                let snapshot = self.snapshot(window, cx);
24065                self.refresh_matching_bracket_highlights(&snapshot, cx);
24066                self.refresh_outline_symbols_at_cursor(cx);
24067                self.refresh_sticky_headers(&snapshot, cx);
24068                if self.has_active_edit_prediction() {
24069                    self.update_visible_edit_prediction(window, cx);
24070                }
24071
24072                // Clean up orphaned review comments after edits
24073                self.cleanup_orphaned_review_comments(cx);
24074
24075                if let Some(buffer) = edited_buffer {
24076                    if buffer.read(cx).file().is_none() {
24077                        cx.emit(EditorEvent::TitleChanged);
24078                    }
24079
24080                    if self.project.is_some() {
24081                        let buffer_id = buffer.read(cx).remote_id();
24082                        self.register_buffer(buffer_id, cx);
24083                        self.update_lsp_data(Some(buffer_id), window, cx);
24084                        self.refresh_inlay_hints(
24085                            InlayHintRefreshReason::BufferEdited(buffer_id),
24086                            cx,
24087                        );
24088                    }
24089                }
24090
24091                cx.emit(EditorEvent::BufferEdited);
24092                cx.emit(SearchEvent::MatchesInvalidated);
24093
24094                let Some(project) = &self.project else { return };
24095                let (telemetry, is_via_ssh) = {
24096                    let project = project.read(cx);
24097                    let telemetry = project.client().telemetry().clone();
24098                    let is_via_ssh = project.is_via_remote_server();
24099                    (telemetry, is_via_ssh)
24100                };
24101                telemetry.log_edit_event("editor", is_via_ssh);
24102            }
24103            multi_buffer::Event::ExcerptsAdded {
24104                buffer,
24105                predecessor,
24106                excerpts,
24107            } => {
24108                self.tasks_update_task = Some(self.refresh_runnables(window, cx));
24109                let buffer_id = buffer.read(cx).remote_id();
24110                if self.buffer.read(cx).diff_for(buffer_id).is_none()
24111                    && let Some(project) = &self.project
24112                {
24113                    update_uncommitted_diff_for_buffer(
24114                        cx.entity(),
24115                        project,
24116                        [buffer.clone()],
24117                        self.buffer.clone(),
24118                        cx,
24119                    )
24120                    .detach();
24121                }
24122                self.semantic_token_state
24123                    .invalidate_buffer(&buffer.read(cx).remote_id());
24124                self.update_lsp_data(Some(buffer_id), window, cx);
24125                self.refresh_inlay_hints(InlayHintRefreshReason::NewLinesShown, cx);
24126                self.colorize_brackets(false, cx);
24127                self.refresh_selected_text_highlights(&self.display_snapshot(cx), true, window, cx);
24128                cx.emit(EditorEvent::ExcerptsAdded {
24129                    buffer: buffer.clone(),
24130                    predecessor: *predecessor,
24131                    excerpts: excerpts.clone(),
24132                });
24133            }
24134            multi_buffer::Event::ExcerptsRemoved {
24135                ids,
24136                removed_buffer_ids,
24137            } => {
24138                if let Some(inlay_hints) = &mut self.inlay_hints {
24139                    inlay_hints.remove_inlay_chunk_data(removed_buffer_ids);
24140                }
24141                self.refresh_inlay_hints(InlayHintRefreshReason::ExcerptsRemoved(ids.clone()), cx);
24142                for buffer_id in removed_buffer_ids {
24143                    self.registered_buffers.remove(buffer_id);
24144                    self.tasks
24145                        .retain(|(task_buffer_id, _), _| task_buffer_id != buffer_id);
24146                    self.semantic_token_state.invalidate_buffer(buffer_id);
24147                    self.display_map.update(cx, |display_map, cx| {
24148                        display_map.invalidate_semantic_highlights(*buffer_id);
24149                        display_map.clear_lsp_folding_ranges(*buffer_id, cx);
24150                    });
24151                }
24152
24153                self.display_map.update(cx, |display_map, cx| {
24154                    display_map.unfold_buffers(removed_buffer_ids.iter().copied(), cx);
24155                });
24156
24157                jsx_tag_auto_close::refresh_enabled_in_any_buffer(self, multibuffer, cx);
24158                cx.emit(EditorEvent::ExcerptsRemoved {
24159                    ids: ids.clone(),
24160                    removed_buffer_ids: removed_buffer_ids.clone(),
24161                });
24162            }
24163            multi_buffer::Event::ExcerptsEdited {
24164                excerpt_ids,
24165                buffer_ids,
24166            } => {
24167                self.display_map.update(cx, |map, cx| {
24168                    map.unfold_buffers(buffer_ids.iter().copied(), cx)
24169                });
24170                cx.emit(EditorEvent::ExcerptsEdited {
24171                    ids: excerpt_ids.clone(),
24172                });
24173            }
24174            multi_buffer::Event::ExcerptsExpanded { ids } => {
24175                self.refresh_inlay_hints(InlayHintRefreshReason::NewLinesShown, cx);
24176                self.refresh_document_highlights(cx);
24177                let snapshot = multibuffer.read(cx).snapshot(cx);
24178                for id in ids {
24179                    self.bracket_fetched_tree_sitter_chunks.remove(id);
24180                    if let Some(buffer) = snapshot.buffer_for_excerpt(*id) {
24181                        self.semantic_token_state
24182                            .invalidate_buffer(&buffer.remote_id());
24183                    }
24184                }
24185                self.colorize_brackets(false, cx);
24186                self.update_lsp_data(None, window, cx);
24187                cx.emit(EditorEvent::ExcerptsExpanded { ids: ids.clone() })
24188            }
24189            multi_buffer::Event::Reparsed(buffer_id) => {
24190                self.tasks_update_task = Some(self.refresh_runnables(window, cx));
24191                self.refresh_selected_text_highlights(&self.display_snapshot(cx), true, window, cx);
24192                self.colorize_brackets(true, cx);
24193                jsx_tag_auto_close::refresh_enabled_in_any_buffer(self, multibuffer, cx);
24194
24195                cx.emit(EditorEvent::Reparsed(*buffer_id));
24196            }
24197            multi_buffer::Event::DiffHunksToggled => {
24198                self.tasks_update_task = Some(self.refresh_runnables(window, cx));
24199            }
24200            multi_buffer::Event::LanguageChanged(buffer_id, is_fresh_language) => {
24201                if !is_fresh_language {
24202                    self.registered_buffers.remove(&buffer_id);
24203                }
24204                jsx_tag_auto_close::refresh_enabled_in_any_buffer(self, multibuffer, cx);
24205                cx.emit(EditorEvent::Reparsed(*buffer_id));
24206                cx.notify();
24207            }
24208            multi_buffer::Event::DirtyChanged => cx.emit(EditorEvent::DirtyChanged),
24209            multi_buffer::Event::Saved => cx.emit(EditorEvent::Saved),
24210            multi_buffer::Event::FileHandleChanged
24211            | multi_buffer::Event::Reloaded
24212            | multi_buffer::Event::BufferDiffChanged => cx.emit(EditorEvent::TitleChanged),
24213            multi_buffer::Event::DiagnosticsUpdated => {
24214                self.update_diagnostics_state(window, cx);
24215            }
24216            _ => {}
24217        };
24218    }
24219
24220    fn update_diagnostics_state(&mut self, window: &mut Window, cx: &mut Context<'_, Editor>) {
24221        if !self.diagnostics_enabled() {
24222            return;
24223        }
24224        self.refresh_active_diagnostics(cx);
24225        self.refresh_inline_diagnostics(true, window, cx);
24226        self.scrollbar_marker_state.dirty = true;
24227        cx.notify();
24228    }
24229
24230    pub fn start_temporary_diff_override(&mut self) {
24231        self.load_diff_task.take();
24232        self.temporary_diff_override = true;
24233    }
24234
24235    pub fn end_temporary_diff_override(&mut self, cx: &mut Context<Self>) {
24236        self.temporary_diff_override = false;
24237        self.set_render_diff_hunk_controls(Arc::new(render_diff_hunk_controls), cx);
24238        self.buffer.update(cx, |buffer, cx| {
24239            buffer.set_all_diff_hunks_collapsed(cx);
24240        });
24241
24242        if let Some(project) = self.project.clone() {
24243            self.load_diff_task = Some(
24244                update_uncommitted_diff_for_buffer(
24245                    cx.entity(),
24246                    &project,
24247                    self.buffer.read(cx).all_buffers(),
24248                    self.buffer.clone(),
24249                    cx,
24250                )
24251                .shared(),
24252            );
24253        }
24254    }
24255
24256    fn on_display_map_changed(
24257        &mut self,
24258        _: Entity<DisplayMap>,
24259        _: &mut Window,
24260        cx: &mut Context<Self>,
24261    ) {
24262        cx.notify();
24263    }
24264
24265    fn fetch_accent_data(&self, cx: &App) -> Option<AccentData> {
24266        if !self.mode.is_full() {
24267            return None;
24268        }
24269
24270        let theme_settings = theme::ThemeSettings::get_global(cx);
24271        let theme = cx.theme();
24272        let accent_colors = theme.accents().clone();
24273
24274        let accent_overrides = theme_settings
24275            .theme_overrides
24276            .get(theme.name.as_ref())
24277            .map(|theme_style| &theme_style.accents)
24278            .into_iter()
24279            .flatten()
24280            .chain(
24281                theme_settings
24282                    .experimental_theme_overrides
24283                    .as_ref()
24284                    .map(|overrides| &overrides.accents)
24285                    .into_iter()
24286                    .flatten(),
24287            )
24288            .flat_map(|accent| accent.0.clone().map(SharedString::from))
24289            .collect();
24290
24291        Some(AccentData {
24292            colors: accent_colors,
24293            overrides: accent_overrides,
24294        })
24295    }
24296
24297    fn fetch_applicable_language_settings(
24298        &self,
24299        cx: &App,
24300    ) -> HashMap<Option<LanguageName>, LanguageSettings> {
24301        if !self.mode.is_full() {
24302            return HashMap::default();
24303        }
24304
24305        self.buffer().read(cx).all_buffers().into_iter().fold(
24306            HashMap::default(),
24307            |mut acc, buffer| {
24308                let buffer = buffer.read(cx);
24309                let language = buffer.language().map(|language| language.name());
24310                if let hash_map::Entry::Vacant(v) = acc.entry(language.clone()) {
24311                    let file = buffer.file();
24312                    v.insert(language_settings(language, file, cx).into_owned());
24313                }
24314                acc
24315            },
24316        )
24317    }
24318
24319    fn settings_changed(&mut self, window: &mut Window, cx: &mut Context<Self>) {
24320        let new_language_settings = self.fetch_applicable_language_settings(cx);
24321        let language_settings_changed = new_language_settings != self.applicable_language_settings;
24322        self.applicable_language_settings = new_language_settings;
24323
24324        let new_accents = self.fetch_accent_data(cx);
24325        let accents_changed = new_accents != self.accent_data;
24326        self.accent_data = new_accents;
24327
24328        if self.diagnostics_enabled() {
24329            let new_severity = EditorSettings::get_global(cx)
24330                .diagnostics_max_severity
24331                .unwrap_or(DiagnosticSeverity::Hint);
24332            self.set_max_diagnostics_severity(new_severity, cx);
24333        }
24334        self.tasks_update_task = Some(self.refresh_runnables(window, cx));
24335        self.update_edit_prediction_settings(cx);
24336        self.refresh_edit_prediction(true, false, window, cx);
24337        self.refresh_inline_values(cx);
24338
24339        let old_cursor_shape = self.cursor_shape;
24340        let old_show_breadcrumbs = self.show_breadcrumbs;
24341
24342        {
24343            let editor_settings = EditorSettings::get_global(cx);
24344            self.scroll_manager.vertical_scroll_margin = editor_settings.vertical_scroll_margin;
24345            self.show_breadcrumbs = editor_settings.toolbar.breadcrumbs;
24346            self.cursor_shape = editor_settings.cursor_shape.unwrap_or_default();
24347            self.hide_mouse_mode = editor_settings.hide_mouse.unwrap_or_default();
24348        }
24349
24350        if old_cursor_shape != self.cursor_shape {
24351            cx.emit(EditorEvent::CursorShapeChanged);
24352        }
24353
24354        if old_show_breadcrumbs != self.show_breadcrumbs {
24355            cx.emit(EditorEvent::BreadcrumbsChanged);
24356        }
24357
24358        let (restore_unsaved_buffers, show_inline_diagnostics, inline_blame_enabled) = {
24359            let project_settings = ProjectSettings::get_global(cx);
24360            (
24361                project_settings.session.restore_unsaved_buffers,
24362                project_settings.diagnostics.inline.enabled,
24363                project_settings.git.inline_blame.enabled,
24364            )
24365        };
24366        self.buffer_serialization = self
24367            .should_serialize_buffer()
24368            .then(|| BufferSerialization::new(restore_unsaved_buffers));
24369
24370        if self.mode.is_full() {
24371            if self.show_inline_diagnostics != show_inline_diagnostics {
24372                self.show_inline_diagnostics = show_inline_diagnostics;
24373                self.refresh_inline_diagnostics(false, window, cx);
24374            }
24375
24376            if self.git_blame_inline_enabled != inline_blame_enabled {
24377                self.toggle_git_blame_inline_internal(false, window, cx);
24378            }
24379
24380            let minimap_settings = EditorSettings::get_global(cx).minimap;
24381            if self.minimap_visibility != MinimapVisibility::Disabled {
24382                if self.minimap_visibility.settings_visibility()
24383                    != minimap_settings.minimap_enabled()
24384                {
24385                    self.set_minimap_visibility(
24386                        MinimapVisibility::for_mode(self.mode(), cx),
24387                        window,
24388                        cx,
24389                    );
24390                } else if let Some(minimap_entity) = self.minimap.as_ref() {
24391                    minimap_entity.update(cx, |minimap_editor, cx| {
24392                        minimap_editor.update_minimap_configuration(minimap_settings, cx)
24393                    })
24394                }
24395            }
24396
24397            if language_settings_changed || accents_changed {
24398                self.colorize_brackets(true, cx);
24399            }
24400
24401            if language_settings_changed {
24402                self.clear_disabled_lsp_folding_ranges(window, cx);
24403                self.refresh_document_symbols(None, cx);
24404            }
24405
24406            if let Some(inlay_splice) = self.colors.as_mut().and_then(|colors| {
24407                colors.render_mode_updated(EditorSettings::get_global(cx).lsp_document_colors)
24408            }) {
24409                if !inlay_splice.is_empty() {
24410                    self.splice_inlays(&inlay_splice.to_remove, inlay_splice.to_insert, cx);
24411                }
24412                self.refresh_document_colors(None, window, cx);
24413            }
24414
24415            self.refresh_inlay_hints(
24416                InlayHintRefreshReason::SettingsChange(inlay_hint_settings(
24417                    self.selections.newest_anchor().head(),
24418                    &self.buffer.read(cx).snapshot(cx),
24419                    cx,
24420                )),
24421                cx,
24422            );
24423
24424            let new_semantic_token_rules = ProjectSettings::get_global(cx)
24425                .global_lsp_settings
24426                .semantic_token_rules
24427                .clone();
24428            let semantic_token_rules_changed = self
24429                .semantic_token_state
24430                .update_rules(new_semantic_token_rules);
24431            if language_settings_changed || semantic_token_rules_changed {
24432                self.invalidate_semantic_tokens(None);
24433                self.refresh_semantic_tokens(None, None, cx);
24434            }
24435        }
24436
24437        cx.notify();
24438    }
24439
24440    fn theme_changed(&mut self, _: &mut Window, cx: &mut Context<Self>) {
24441        if !self.mode.is_full() {
24442            return;
24443        }
24444
24445        let new_accents = self.fetch_accent_data(cx);
24446        if new_accents != self.accent_data {
24447            self.accent_data = new_accents;
24448            self.colorize_brackets(true, cx);
24449        }
24450
24451        self.invalidate_semantic_tokens(None);
24452        self.refresh_semantic_tokens(None, None, cx);
24453    }
24454
24455    pub fn set_searchable(&mut self, searchable: bool) {
24456        self.searchable = searchable;
24457    }
24458
24459    pub fn searchable(&self) -> bool {
24460        self.searchable
24461    }
24462
24463    pub fn open_excerpts_in_split(
24464        &mut self,
24465        _: &OpenExcerptsSplit,
24466        window: &mut Window,
24467        cx: &mut Context<Self>,
24468    ) {
24469        self.open_excerpts_common(None, true, window, cx)
24470    }
24471
24472    pub fn open_excerpts(&mut self, _: &OpenExcerpts, window: &mut Window, cx: &mut Context<Self>) {
24473        self.open_excerpts_common(None, false, window, cx)
24474    }
24475
24476    pub(crate) fn open_excerpts_common(
24477        &mut self,
24478        jump_data: Option<JumpData>,
24479        split: bool,
24480        window: &mut Window,
24481        cx: &mut Context<Self>,
24482    ) {
24483        if self.buffer.read(cx).is_singleton() {
24484            cx.propagate();
24485            return;
24486        }
24487
24488        let mut new_selections_by_buffer = HashMap::default();
24489        match &jump_data {
24490            Some(JumpData::MultiBufferPoint {
24491                excerpt_id,
24492                position,
24493                anchor,
24494                line_offset_from_top,
24495            }) => {
24496                let multi_buffer_snapshot = self.buffer.read(cx).snapshot(cx);
24497                if let Some(buffer) = multi_buffer_snapshot
24498                    .buffer_id_for_excerpt(*excerpt_id)
24499                    .and_then(|buffer_id| self.buffer.read(cx).buffer(buffer_id))
24500                {
24501                    let buffer_snapshot = buffer.read(cx).snapshot();
24502                    let jump_to_point = if buffer_snapshot.can_resolve(anchor) {
24503                        language::ToPoint::to_point(anchor, &buffer_snapshot)
24504                    } else {
24505                        buffer_snapshot.clip_point(*position, Bias::Left)
24506                    };
24507                    let jump_to_offset = buffer_snapshot.point_to_offset(jump_to_point);
24508                    new_selections_by_buffer.insert(
24509                        buffer,
24510                        (
24511                            vec![BufferOffset(jump_to_offset)..BufferOffset(jump_to_offset)],
24512                            Some(*line_offset_from_top),
24513                        ),
24514                    );
24515                }
24516            }
24517            Some(JumpData::MultiBufferRow {
24518                row,
24519                line_offset_from_top,
24520            }) => {
24521                let point = MultiBufferPoint::new(row.0, 0);
24522                if let Some((buffer, buffer_point, _)) =
24523                    self.buffer.read(cx).point_to_buffer_point(point, cx)
24524                {
24525                    let buffer_offset = buffer.read(cx).point_to_offset(buffer_point);
24526                    new_selections_by_buffer
24527                        .entry(buffer)
24528                        .or_insert((Vec::new(), Some(*line_offset_from_top)))
24529                        .0
24530                        .push(BufferOffset(buffer_offset)..BufferOffset(buffer_offset))
24531                }
24532            }
24533            None => {
24534                let selections = self
24535                    .selections
24536                    .all::<MultiBufferOffset>(&self.display_snapshot(cx));
24537                let multi_buffer = self.buffer.read(cx);
24538                for selection in selections {
24539                    for (snapshot, range, _, anchor) in multi_buffer
24540                        .snapshot(cx)
24541                        .range_to_buffer_ranges_with_deleted_hunks(selection.range())
24542                    {
24543                        if let Some(anchor) = anchor {
24544                            let Some(buffer_handle) = multi_buffer.buffer_for_anchor(anchor, cx)
24545                            else {
24546                                continue;
24547                            };
24548                            let offset = text::ToOffset::to_offset(
24549                                &anchor.text_anchor,
24550                                &buffer_handle.read(cx).snapshot(),
24551                            );
24552                            let range = BufferOffset(offset)..BufferOffset(offset);
24553                            new_selections_by_buffer
24554                                .entry(buffer_handle)
24555                                .or_insert((Vec::new(), None))
24556                                .0
24557                                .push(range)
24558                        } else {
24559                            let Some(buffer_handle) = multi_buffer.buffer(snapshot.remote_id())
24560                            else {
24561                                continue;
24562                            };
24563                            new_selections_by_buffer
24564                                .entry(buffer_handle)
24565                                .or_insert((Vec::new(), None))
24566                                .0
24567                                .push(range)
24568                        }
24569                    }
24570                }
24571            }
24572        }
24573
24574        if self.delegate_open_excerpts {
24575            let selections_by_buffer: HashMap<_, _> = new_selections_by_buffer
24576                .into_iter()
24577                .map(|(buffer, value)| (buffer.read(cx).remote_id(), value))
24578                .collect();
24579            if !selections_by_buffer.is_empty() {
24580                cx.emit(EditorEvent::OpenExcerptsRequested {
24581                    selections_by_buffer,
24582                    split,
24583                });
24584            }
24585            return;
24586        }
24587
24588        let Some(workspace) = self.workspace() else {
24589            cx.propagate();
24590            return;
24591        };
24592
24593        new_selections_by_buffer
24594            .retain(|buffer, _| buffer.read(cx).file().is_none_or(|file| file.can_open()));
24595
24596        if new_selections_by_buffer.is_empty() {
24597            return;
24598        }
24599
24600        Self::open_buffers_in_workspace(
24601            workspace.downgrade(),
24602            new_selections_by_buffer,
24603            split,
24604            window,
24605            cx,
24606        );
24607    }
24608
24609    pub(crate) fn open_buffers_in_workspace(
24610        workspace: WeakEntity<Workspace>,
24611        new_selections_by_buffer: HashMap<
24612            Entity<language::Buffer>,
24613            (Vec<Range<BufferOffset>>, Option<u32>),
24614        >,
24615        split: bool,
24616        window: &mut Window,
24617        cx: &mut App,
24618    ) {
24619        // We defer the pane interaction because we ourselves are a workspace item
24620        // and activating a new item causes the pane to call a method on us reentrantly,
24621        // which panics if we're on the stack.
24622        window.defer(cx, move |window, cx| {
24623            workspace
24624                .update(cx, |workspace, cx| {
24625                    let pane = if split {
24626                        workspace.adjacent_pane(window, cx)
24627                    } else {
24628                        workspace.active_pane().clone()
24629                    };
24630
24631                    for (buffer, (ranges, scroll_offset)) in new_selections_by_buffer {
24632                        let buffer_read = buffer.read(cx);
24633                        let (has_file, is_project_file) = if let Some(file) = buffer_read.file() {
24634                            (true, project::File::from_dyn(Some(file)).is_some())
24635                        } else {
24636                            (false, false)
24637                        };
24638
24639                        // If project file is none workspace.open_project_item will fail to open the excerpt
24640                        // in a pre existing workspace item if one exists, because Buffer entity_id will be None
24641                        // so we check if there's a tab match in that case first
24642                        let editor = (!has_file || !is_project_file)
24643                            .then(|| {
24644                                // Handle file-less buffers separately: those are not really the project items, so won't have a project path or entity id,
24645                                // so `workspace.open_project_item` will never find them, always opening a new editor.
24646                                // Instead, we try to activate the existing editor in the pane first.
24647                                let (editor, pane_item_index, pane_item_id) =
24648                                    pane.read(cx).items().enumerate().find_map(|(i, item)| {
24649                                        let editor = item.downcast::<Editor>()?;
24650                                        let singleton_buffer =
24651                                            editor.read(cx).buffer().read(cx).as_singleton()?;
24652                                        if singleton_buffer == buffer {
24653                                            Some((editor, i, item.item_id()))
24654                                        } else {
24655                                            None
24656                                        }
24657                                    })?;
24658                                pane.update(cx, |pane, cx| {
24659                                    pane.activate_item(pane_item_index, true, true, window, cx);
24660                                    if !PreviewTabsSettings::get_global(cx)
24661                                        .enable_preview_from_multibuffer
24662                                    {
24663                                        pane.unpreview_item_if_preview(pane_item_id);
24664                                    }
24665                                });
24666                                Some(editor)
24667                            })
24668                            .flatten()
24669                            .unwrap_or_else(|| {
24670                                let keep_old_preview = PreviewTabsSettings::get_global(cx)
24671                                    .enable_keep_preview_on_code_navigation;
24672                                let allow_new_preview = PreviewTabsSettings::get_global(cx)
24673                                    .enable_preview_from_multibuffer;
24674                                workspace.open_project_item::<Self>(
24675                                    pane.clone(),
24676                                    buffer,
24677                                    true,
24678                                    true,
24679                                    keep_old_preview,
24680                                    allow_new_preview,
24681                                    window,
24682                                    cx,
24683                                )
24684                            });
24685
24686                        editor.update(cx, |editor, cx| {
24687                            if has_file && !is_project_file {
24688                                editor.set_read_only(true);
24689                            }
24690                            let autoscroll = match scroll_offset {
24691                                Some(scroll_offset) => {
24692                                    Autoscroll::top_relative(scroll_offset as usize)
24693                                }
24694                                None => Autoscroll::newest(),
24695                            };
24696                            let nav_history = editor.nav_history.take();
24697                            let multibuffer_snapshot = editor.buffer().read(cx).snapshot(cx);
24698                            let Some((excerpt_id, _, buffer_snapshot)) =
24699                                multibuffer_snapshot.as_singleton()
24700                            else {
24701                                return;
24702                            };
24703                            editor.change_selections(
24704                                SelectionEffects::scroll(autoscroll),
24705                                window,
24706                                cx,
24707                                |s| {
24708                                    s.select_ranges(ranges.into_iter().map(|range| {
24709                                        let range = buffer_snapshot.anchor_before(range.start)
24710                                            ..buffer_snapshot.anchor_after(range.end);
24711                                        multibuffer_snapshot
24712                                            .anchor_range_in_excerpt(excerpt_id, range)
24713                                            .unwrap()
24714                                    }));
24715                                },
24716                            );
24717                            editor.nav_history = nav_history;
24718                        });
24719                    }
24720                })
24721                .ok();
24722        });
24723    }
24724
24725    fn marked_text_ranges(&self, cx: &App) -> Option<Vec<Range<MultiBufferOffsetUtf16>>> {
24726        let snapshot = self.buffer.read(cx).read(cx);
24727        let (_, ranges) = self.text_highlights(HighlightKey::InputComposition, cx)?;
24728        Some(
24729            ranges
24730                .iter()
24731                .map(move |range| {
24732                    range.start.to_offset_utf16(&snapshot)..range.end.to_offset_utf16(&snapshot)
24733                })
24734                .collect(),
24735        )
24736    }
24737
24738    fn selection_replacement_ranges(
24739        &self,
24740        range: Range<MultiBufferOffsetUtf16>,
24741        cx: &mut App,
24742    ) -> Vec<Range<MultiBufferOffsetUtf16>> {
24743        let selections = self
24744            .selections
24745            .all::<MultiBufferOffsetUtf16>(&self.display_snapshot(cx));
24746        let newest_selection = selections
24747            .iter()
24748            .max_by_key(|selection| selection.id)
24749            .unwrap();
24750        let start_delta = range.start.0.0 as isize - newest_selection.start.0.0 as isize;
24751        let end_delta = range.end.0.0 as isize - newest_selection.end.0.0 as isize;
24752        let snapshot = self.buffer.read(cx).read(cx);
24753        selections
24754            .into_iter()
24755            .map(|mut selection| {
24756                selection.start.0.0 =
24757                    (selection.start.0.0 as isize).saturating_add(start_delta) as usize;
24758                selection.end.0.0 = (selection.end.0.0 as isize).saturating_add(end_delta) as usize;
24759                snapshot.clip_offset_utf16(selection.start, Bias::Left)
24760                    ..snapshot.clip_offset_utf16(selection.end, Bias::Right)
24761            })
24762            .collect()
24763    }
24764
24765    fn report_editor_event(
24766        &self,
24767        reported_event: ReportEditorEvent,
24768        file_extension: Option<String>,
24769        cx: &App,
24770    ) {
24771        if cfg!(any(test, feature = "test-support")) {
24772            return;
24773        }
24774
24775        let Some(project) = &self.project else { return };
24776
24777        // If None, we are in a file without an extension
24778        let file = self
24779            .buffer
24780            .read(cx)
24781            .as_singleton()
24782            .and_then(|b| b.read(cx).file());
24783        let file_extension = file_extension.or(file
24784            .as_ref()
24785            .and_then(|file| Path::new(file.file_name(cx)).extension())
24786            .and_then(|e| e.to_str())
24787            .map(|a| a.to_string()));
24788
24789        let vim_mode = vim_mode_setting::VimModeSetting::try_get(cx)
24790            .map(|vim_mode| vim_mode.0)
24791            .unwrap_or(false);
24792
24793        let edit_predictions_provider = all_language_settings(file, cx).edit_predictions.provider;
24794        let copilot_enabled = edit_predictions_provider
24795            == language::language_settings::EditPredictionProvider::Copilot;
24796        let copilot_enabled_for_language = self
24797            .buffer
24798            .read(cx)
24799            .language_settings(cx)
24800            .show_edit_predictions;
24801
24802        let project = project.read(cx);
24803        let event_type = reported_event.event_type();
24804
24805        if let ReportEditorEvent::Saved { auto_saved } = reported_event {
24806            telemetry::event!(
24807                event_type,
24808                type = if auto_saved {"autosave"} else {"manual"},
24809                file_extension,
24810                vim_mode,
24811                copilot_enabled,
24812                copilot_enabled_for_language,
24813                edit_predictions_provider,
24814                is_via_ssh = project.is_via_remote_server(),
24815            );
24816        } else {
24817            telemetry::event!(
24818                event_type,
24819                file_extension,
24820                vim_mode,
24821                copilot_enabled,
24822                copilot_enabled_for_language,
24823                edit_predictions_provider,
24824                is_via_ssh = project.is_via_remote_server(),
24825            );
24826        };
24827    }
24828
24829    /// Copy the highlighted chunks to the clipboard as JSON. The format is an array of lines,
24830    /// with each line being an array of {text, highlight} objects.
24831    fn copy_highlight_json(
24832        &mut self,
24833        _: &CopyHighlightJson,
24834        window: &mut Window,
24835        cx: &mut Context<Self>,
24836    ) {
24837        #[derive(Serialize)]
24838        struct Chunk<'a> {
24839            text: String,
24840            highlight: Option<&'a str>,
24841        }
24842
24843        let snapshot = self.buffer.read(cx).snapshot(cx);
24844        let range = self
24845            .selected_text_range(false, window, cx)
24846            .and_then(|selection| {
24847                if selection.range.is_empty() {
24848                    None
24849                } else {
24850                    Some(
24851                        snapshot.offset_utf16_to_offset(MultiBufferOffsetUtf16(OffsetUtf16(
24852                            selection.range.start,
24853                        )))
24854                            ..snapshot.offset_utf16_to_offset(MultiBufferOffsetUtf16(OffsetUtf16(
24855                                selection.range.end,
24856                            ))),
24857                    )
24858                }
24859            })
24860            .unwrap_or_else(|| MultiBufferOffset(0)..snapshot.len());
24861
24862        let chunks = snapshot.chunks(range, true);
24863        let mut lines = Vec::new();
24864        let mut line: VecDeque<Chunk> = VecDeque::new();
24865
24866        let Some(style) = self.style.as_ref() else {
24867            return;
24868        };
24869
24870        for chunk in chunks {
24871            let highlight = chunk
24872                .syntax_highlight_id
24873                .and_then(|id| id.name(&style.syntax));
24874            let mut chunk_lines = chunk.text.split('\n').peekable();
24875            while let Some(text) = chunk_lines.next() {
24876                let mut merged_with_last_token = false;
24877                if let Some(last_token) = line.back_mut()
24878                    && last_token.highlight == highlight
24879                {
24880                    last_token.text.push_str(text);
24881                    merged_with_last_token = true;
24882                }
24883
24884                if !merged_with_last_token {
24885                    line.push_back(Chunk {
24886                        text: text.into(),
24887                        highlight,
24888                    });
24889                }
24890
24891                if chunk_lines.peek().is_some() {
24892                    if line.len() > 1 && line.front().unwrap().text.is_empty() {
24893                        line.pop_front();
24894                    }
24895                    if line.len() > 1 && line.back().unwrap().text.is_empty() {
24896                        line.pop_back();
24897                    }
24898
24899                    lines.push(mem::take(&mut line));
24900                }
24901            }
24902        }
24903
24904        let Some(lines) = serde_json::to_string_pretty(&lines).log_err() else {
24905            return;
24906        };
24907        cx.write_to_clipboard(ClipboardItem::new_string(lines));
24908    }
24909
24910    pub fn open_context_menu(
24911        &mut self,
24912        _: &OpenContextMenu,
24913        window: &mut Window,
24914        cx: &mut Context<Self>,
24915    ) {
24916        self.request_autoscroll(Autoscroll::newest(), cx);
24917        let position = self
24918            .selections
24919            .newest_display(&self.display_snapshot(cx))
24920            .start;
24921        mouse_context_menu::deploy_context_menu(self, None, position, window, cx);
24922    }
24923
24924    pub fn replay_insert_event(
24925        &mut self,
24926        text: &str,
24927        relative_utf16_range: Option<Range<isize>>,
24928        window: &mut Window,
24929        cx: &mut Context<Self>,
24930    ) {
24931        if !self.input_enabled {
24932            cx.emit(EditorEvent::InputIgnored { text: text.into() });
24933            return;
24934        }
24935        if let Some(relative_utf16_range) = relative_utf16_range {
24936            let selections = self
24937                .selections
24938                .all::<MultiBufferOffsetUtf16>(&self.display_snapshot(cx));
24939            self.change_selections(SelectionEffects::no_scroll(), window, cx, |s| {
24940                let new_ranges = selections.into_iter().map(|range| {
24941                    let start = MultiBufferOffsetUtf16(OffsetUtf16(
24942                        range
24943                            .head()
24944                            .0
24945                            .0
24946                            .saturating_add_signed(relative_utf16_range.start),
24947                    ));
24948                    let end = MultiBufferOffsetUtf16(OffsetUtf16(
24949                        range
24950                            .head()
24951                            .0
24952                            .0
24953                            .saturating_add_signed(relative_utf16_range.end),
24954                    ));
24955                    start..end
24956                });
24957                s.select_ranges(new_ranges);
24958            });
24959        }
24960
24961        self.handle_input(text, window, cx);
24962    }
24963
24964    pub fn is_focused(&self, window: &Window) -> bool {
24965        self.focus_handle.is_focused(window)
24966    }
24967
24968    fn handle_focus(&mut self, window: &mut Window, cx: &mut Context<Self>) {
24969        cx.emit(EditorEvent::Focused);
24970
24971        if let Some(descendant) = self
24972            .last_focused_descendant
24973            .take()
24974            .and_then(|descendant| descendant.upgrade())
24975        {
24976            window.focus(&descendant, cx);
24977        } else {
24978            if let Some(blame) = self.blame.as_ref() {
24979                blame.update(cx, GitBlame::focus)
24980            }
24981
24982            self.blink_manager.update(cx, BlinkManager::enable);
24983            self.show_cursor_names(window, cx);
24984            self.buffer.update(cx, |buffer, cx| {
24985                buffer.finalize_last_transaction(cx);
24986                if self.leader_id.is_none() {
24987                    buffer.set_active_selections(
24988                        &self.selections.disjoint_anchors_arc(),
24989                        self.selections.line_mode(),
24990                        self.cursor_shape,
24991                        cx,
24992                    );
24993                }
24994            });
24995
24996            if let Some(position_map) = self.last_position_map.clone() {
24997                EditorElement::mouse_moved(
24998                    self,
24999                    &MouseMoveEvent {
25000                        position: window.mouse_position(),
25001                        pressed_button: None,
25002                        modifiers: window.modifiers(),
25003                    },
25004                    &position_map,
25005                    None,
25006                    window,
25007                    cx,
25008                );
25009            }
25010        }
25011    }
25012
25013    fn handle_focus_in(&mut self, _: &mut Window, cx: &mut Context<Self>) {
25014        cx.emit(EditorEvent::FocusedIn)
25015    }
25016
25017    fn handle_focus_out(
25018        &mut self,
25019        event: FocusOutEvent,
25020        _window: &mut Window,
25021        cx: &mut Context<Self>,
25022    ) {
25023        if event.blurred != self.focus_handle {
25024            self.last_focused_descendant = Some(event.blurred);
25025        }
25026        self.selection_drag_state = SelectionDragState::None;
25027        self.refresh_inlay_hints(InlayHintRefreshReason::ModifiersChanged(false), cx);
25028    }
25029
25030    pub fn handle_blur(&mut self, window: &mut Window, cx: &mut Context<Self>) {
25031        self.blink_manager.update(cx, BlinkManager::disable);
25032        self.buffer
25033            .update(cx, |buffer, cx| buffer.remove_active_selections(cx));
25034
25035        if let Some(blame) = self.blame.as_ref() {
25036            blame.update(cx, GitBlame::blur)
25037        }
25038        if !self.hover_state.focused(window, cx) {
25039            hide_hover(self, cx);
25040        }
25041        if !self
25042            .context_menu
25043            .borrow()
25044            .as_ref()
25045            .is_some_and(|context_menu| context_menu.focused(window, cx))
25046        {
25047            self.hide_context_menu(window, cx);
25048        }
25049        self.take_active_edit_prediction(cx);
25050        cx.emit(EditorEvent::Blurred);
25051        cx.notify();
25052    }
25053
25054    pub fn observe_pending_input(&mut self, window: &mut Window, cx: &mut Context<Self>) {
25055        let mut pending: String = window
25056            .pending_input_keystrokes()
25057            .into_iter()
25058            .flatten()
25059            .filter_map(|keystroke| keystroke.key_char.clone())
25060            .collect();
25061
25062        if !self.input_enabled || self.read_only || !self.focus_handle.is_focused(window) {
25063            pending = "".to_string();
25064        }
25065
25066        let existing_pending = self
25067            .text_highlights(HighlightKey::PendingInput, cx)
25068            .map(|(_, ranges)| ranges.to_vec());
25069        if existing_pending.is_none() && pending.is_empty() {
25070            return;
25071        }
25072        let transaction =
25073            self.transact(window, cx, |this, window, cx| {
25074                let selections = this
25075                    .selections
25076                    .all::<MultiBufferOffset>(&this.display_snapshot(cx));
25077                let edits = selections
25078                    .iter()
25079                    .map(|selection| (selection.end..selection.end, pending.clone()));
25080                this.edit(edits, cx);
25081                this.change_selections(SelectionEffects::no_scroll(), window, cx, |s| {
25082                    s.select_ranges(selections.into_iter().enumerate().map(|(ix, sel)| {
25083                        sel.start + ix * pending.len()..sel.end + ix * pending.len()
25084                    }));
25085                });
25086                if let Some(existing_ranges) = existing_pending {
25087                    let edits = existing_ranges.iter().map(|range| (range.clone(), ""));
25088                    this.edit(edits, cx);
25089                }
25090            });
25091
25092        let snapshot = self.snapshot(window, cx);
25093        let ranges = self
25094            .selections
25095            .all::<MultiBufferOffset>(&snapshot.display_snapshot)
25096            .into_iter()
25097            .map(|selection| {
25098                snapshot.buffer_snapshot().anchor_after(selection.end)
25099                    ..snapshot
25100                        .buffer_snapshot()
25101                        .anchor_before(selection.end + pending.len())
25102            })
25103            .collect();
25104
25105        if pending.is_empty() {
25106            self.clear_highlights(HighlightKey::PendingInput, cx);
25107        } else {
25108            self.highlight_text(
25109                HighlightKey::PendingInput,
25110                ranges,
25111                HighlightStyle {
25112                    underline: Some(UnderlineStyle {
25113                        thickness: px(1.),
25114                        color: None,
25115                        wavy: false,
25116                    }),
25117                    ..Default::default()
25118                },
25119                cx,
25120            );
25121        }
25122
25123        self.ime_transaction = self.ime_transaction.or(transaction);
25124        if let Some(transaction) = self.ime_transaction {
25125            self.buffer.update(cx, |buffer, cx| {
25126                buffer.group_until_transaction(transaction, cx);
25127            });
25128        }
25129
25130        if self
25131            .text_highlights(HighlightKey::PendingInput, cx)
25132            .is_none()
25133        {
25134            self.ime_transaction.take();
25135        }
25136    }
25137
25138    pub fn register_action_renderer(
25139        &mut self,
25140        listener: impl Fn(&Editor, &mut Window, &mut Context<Editor>) + 'static,
25141    ) -> Subscription {
25142        let id = self.next_editor_action_id.post_inc();
25143        self.editor_actions
25144            .borrow_mut()
25145            .insert(id, Box::new(listener));
25146
25147        let editor_actions = self.editor_actions.clone();
25148        Subscription::new(move || {
25149            editor_actions.borrow_mut().remove(&id);
25150        })
25151    }
25152
25153    pub fn register_action<A: Action>(
25154        &mut self,
25155        listener: impl Fn(&A, &mut Window, &mut App) + 'static,
25156    ) -> Subscription {
25157        let id = self.next_editor_action_id.post_inc();
25158        let listener = Arc::new(listener);
25159        self.editor_actions.borrow_mut().insert(
25160            id,
25161            Box::new(move |_, window, _| {
25162                let listener = listener.clone();
25163                window.on_action(TypeId::of::<A>(), move |action, phase, window, cx| {
25164                    let action = action.downcast_ref().unwrap();
25165                    if phase == DispatchPhase::Bubble {
25166                        listener(action, window, cx)
25167                    }
25168                })
25169            }),
25170        );
25171
25172        let editor_actions = self.editor_actions.clone();
25173        Subscription::new(move || {
25174            editor_actions.borrow_mut().remove(&id);
25175        })
25176    }
25177
25178    pub fn file_header_size(&self) -> u32 {
25179        FILE_HEADER_HEIGHT
25180    }
25181
25182    pub fn restore(
25183        &mut self,
25184        revert_changes: HashMap<BufferId, Vec<(Range<text::Anchor>, Rope)>>,
25185        window: &mut Window,
25186        cx: &mut Context<Self>,
25187    ) {
25188        self.buffer().update(cx, |multi_buffer, cx| {
25189            for (buffer_id, changes) in revert_changes {
25190                if let Some(buffer) = multi_buffer.buffer(buffer_id) {
25191                    buffer.update(cx, |buffer, cx| {
25192                        buffer.edit(
25193                            changes
25194                                .into_iter()
25195                                .map(|(range, text)| (range, text.to_string())),
25196                            None,
25197                            cx,
25198                        );
25199                    });
25200                }
25201            }
25202        });
25203        self.change_selections(SelectionEffects::no_scroll(), window, cx, |selections| {
25204            selections.refresh()
25205        });
25206    }
25207
25208    pub fn to_pixel_point(
25209        &mut self,
25210        source: Anchor,
25211        editor_snapshot: &EditorSnapshot,
25212        window: &mut Window,
25213        cx: &mut App,
25214    ) -> Option<gpui::Point<Pixels>> {
25215        let source_point = source.to_display_point(editor_snapshot);
25216        self.display_to_pixel_point(source_point, editor_snapshot, window, cx)
25217    }
25218
25219    pub fn display_to_pixel_point(
25220        &mut self,
25221        source: DisplayPoint,
25222        editor_snapshot: &EditorSnapshot,
25223        window: &mut Window,
25224        cx: &mut App,
25225    ) -> Option<gpui::Point<Pixels>> {
25226        let line_height = self.style(cx).text.line_height_in_pixels(window.rem_size());
25227        let text_layout_details = self.text_layout_details(window, cx);
25228        let scroll_top = text_layout_details
25229            .scroll_anchor
25230            .scroll_position(editor_snapshot)
25231            .y;
25232
25233        if source.row().as_f64() < scroll_top.floor() {
25234            return None;
25235        }
25236        let source_x = editor_snapshot.x_for_display_point(source, &text_layout_details);
25237        let source_y = line_height * (source.row().as_f64() - scroll_top) as f32;
25238        Some(gpui::Point::new(source_x, source_y))
25239    }
25240
25241    pub fn has_visible_completions_menu(&self) -> bool {
25242        !self.edit_prediction_preview_is_active()
25243            && self.context_menu.borrow().as_ref().is_some_and(|menu| {
25244                menu.visible() && matches!(menu, CodeContextMenu::Completions(_))
25245            })
25246    }
25247
25248    pub fn register_addon<T: Addon>(&mut self, instance: T) {
25249        if self.mode.is_minimap() {
25250            return;
25251        }
25252        self.addons
25253            .insert(std::any::TypeId::of::<T>(), Box::new(instance));
25254    }
25255
25256    pub fn unregister_addon<T: Addon>(&mut self) {
25257        self.addons.remove(&std::any::TypeId::of::<T>());
25258    }
25259
25260    pub fn addon<T: Addon>(&self) -> Option<&T> {
25261        let type_id = std::any::TypeId::of::<T>();
25262        self.addons
25263            .get(&type_id)
25264            .and_then(|item| item.to_any().downcast_ref::<T>())
25265    }
25266
25267    pub fn addon_mut<T: Addon>(&mut self) -> Option<&mut T> {
25268        let type_id = std::any::TypeId::of::<T>();
25269        self.addons
25270            .get_mut(&type_id)
25271            .and_then(|item| item.to_any_mut()?.downcast_mut::<T>())
25272    }
25273
25274    fn character_dimensions(&self, window: &mut Window, cx: &mut App) -> CharacterDimensions {
25275        let text_layout_details = self.text_layout_details(window, cx);
25276        let style = &text_layout_details.editor_style;
25277        let font_id = window.text_system().resolve_font(&style.text.font());
25278        let font_size = style.text.font_size.to_pixels(window.rem_size());
25279        let line_height = style.text.line_height_in_pixels(window.rem_size());
25280        let em_width = window.text_system().em_width(font_id, font_size).unwrap();
25281        let em_advance = window.text_system().em_advance(font_id, font_size).unwrap();
25282
25283        CharacterDimensions {
25284            em_width,
25285            em_advance,
25286            line_height,
25287        }
25288    }
25289
25290    pub fn wait_for_diff_to_load(&self) -> Option<Shared<Task<()>>> {
25291        self.load_diff_task.clone()
25292    }
25293
25294    fn read_metadata_from_db(
25295        &mut self,
25296        item_id: u64,
25297        workspace_id: WorkspaceId,
25298        window: &mut Window,
25299        cx: &mut Context<Editor>,
25300    ) {
25301        if self.buffer_kind(cx) == ItemBufferKind::Singleton
25302            && !self.mode.is_minimap()
25303            && WorkspaceSettings::get(None, cx).restore_on_startup
25304                != RestoreOnStartupBehavior::EmptyTab
25305        {
25306            let buffer_snapshot = OnceCell::new();
25307
25308            // Get file path for path-based fold lookup
25309            let file_path: Option<Arc<Path>> =
25310                self.buffer().read(cx).as_singleton().and_then(|buffer| {
25311                    project::File::from_dyn(buffer.read(cx).file())
25312                        .map(|file| Arc::from(file.abs_path(cx)))
25313                });
25314
25315            // Try file_folds (path-based) first, fallback to editor_folds (migration)
25316            let (folds, needs_migration) = if let Some(ref path) = file_path {
25317                if let Some(folds) = DB.get_file_folds(workspace_id, path).log_err()
25318                    && !folds.is_empty()
25319                {
25320                    (Some(folds), false)
25321                } else if let Some(folds) = DB.get_editor_folds(item_id, workspace_id).log_err()
25322                    && !folds.is_empty()
25323                {
25324                    // Found old editor_folds data, will migrate to file_folds
25325                    (Some(folds), true)
25326                } else {
25327                    (None, false)
25328                }
25329            } else {
25330                // No file path, try editor_folds as fallback
25331                let folds = DB.get_editor_folds(item_id, workspace_id).log_err();
25332                (folds.filter(|f| !f.is_empty()), false)
25333            };
25334
25335            if let Some(folds) = folds {
25336                let snapshot = buffer_snapshot.get_or_init(|| self.buffer.read(cx).snapshot(cx));
25337                let snapshot_len = snapshot.len().0;
25338
25339                // Helper: search for fingerprint in buffer, return offset if found
25340                let find_fingerprint = |fingerprint: &str, search_start: usize| -> Option<usize> {
25341                    // Ensure we start at a character boundary (defensive)
25342                    let search_start = snapshot
25343                        .clip_offset(MultiBufferOffset(search_start), Bias::Left)
25344                        .0;
25345                    let search_end = snapshot_len.saturating_sub(fingerprint.len());
25346
25347                    let mut byte_offset = search_start;
25348                    for ch in snapshot.chars_at(MultiBufferOffset(search_start)) {
25349                        if byte_offset > search_end {
25350                            break;
25351                        }
25352                        if snapshot.contains_str_at(MultiBufferOffset(byte_offset), fingerprint) {
25353                            return Some(byte_offset);
25354                        }
25355                        byte_offset += ch.len_utf8();
25356                    }
25357                    None
25358                };
25359
25360                // Track search position to handle duplicate fingerprints correctly.
25361                // Folds are stored in document order, so we advance after each match.
25362                let mut search_start = 0usize;
25363
25364                // Collect db_folds for migration (only folds with valid fingerprints)
25365                let mut db_folds_for_migration: Vec<(usize, usize, String, String)> = Vec::new();
25366
25367                let valid_folds: Vec<_> = folds
25368                    .into_iter()
25369                    .filter_map(|(stored_start, stored_end, start_fp, end_fp)| {
25370                        // Skip folds without fingerprints (old data before migration)
25371                        let sfp = start_fp?;
25372                        let efp = end_fp?;
25373                        let efp_len = efp.len();
25374
25375                        // Fast path: check if fingerprints match at stored offsets
25376                        // Note: end_fp is content BEFORE fold end, so check at (stored_end - efp_len)
25377                        let start_matches = stored_start < snapshot_len
25378                            && snapshot.contains_str_at(MultiBufferOffset(stored_start), &sfp);
25379                        let efp_check_pos = stored_end.saturating_sub(efp_len);
25380                        let end_matches = efp_check_pos >= stored_start
25381                            && stored_end <= snapshot_len
25382                            && snapshot.contains_str_at(MultiBufferOffset(efp_check_pos), &efp);
25383
25384                        let (new_start, new_end) = if start_matches && end_matches {
25385                            // Offsets unchanged, use stored values
25386                            (stored_start, stored_end)
25387                        } else if sfp == efp {
25388                            // Short fold: identical fingerprints can only match once per search
25389                            // Use stored fold length to compute new_end
25390                            let new_start = find_fingerprint(&sfp, search_start)?;
25391                            let fold_len = stored_end - stored_start;
25392                            let new_end = new_start + fold_len;
25393                            (new_start, new_end)
25394                        } else {
25395                            // Slow path: search for fingerprints in buffer
25396                            let new_start = find_fingerprint(&sfp, search_start)?;
25397                            // Search for end_fp after start, then add efp_len to get actual fold end
25398                            let efp_pos = find_fingerprint(&efp, new_start + sfp.len())?;
25399                            let new_end = efp_pos + efp_len;
25400                            (new_start, new_end)
25401                        };
25402
25403                        // Advance search position for next fold
25404                        search_start = new_end;
25405
25406                        // Validate fold makes sense (end must be after start)
25407                        if new_end <= new_start {
25408                            return None;
25409                        }
25410
25411                        // Collect for migration if needed
25412                        if needs_migration {
25413                            db_folds_for_migration.push((new_start, new_end, sfp, efp));
25414                        }
25415
25416                        Some(
25417                            snapshot.clip_offset(MultiBufferOffset(new_start), Bias::Left)
25418                                ..snapshot.clip_offset(MultiBufferOffset(new_end), Bias::Right),
25419                        )
25420                    })
25421                    .collect();
25422
25423                if !valid_folds.is_empty() {
25424                    self.fold_ranges(valid_folds, false, window, cx);
25425
25426                    // Migrate from editor_folds to file_folds if we loaded from old table
25427                    if needs_migration {
25428                        if let Some(ref path) = file_path {
25429                            let path = path.clone();
25430                            cx.spawn(async move |_, _| {
25431                                DB.save_file_folds(workspace_id, path, db_folds_for_migration)
25432                                    .await
25433                                    .log_err();
25434                            })
25435                            .detach();
25436                        }
25437                    }
25438                }
25439            }
25440
25441            if let Some(selections) = DB.get_editor_selections(item_id, workspace_id).log_err()
25442                && !selections.is_empty()
25443            {
25444                let snapshot = buffer_snapshot.get_or_init(|| self.buffer.read(cx).snapshot(cx));
25445                // skip adding the initial selection to selection history
25446                self.selection_history.mode = SelectionHistoryMode::Skipping;
25447                self.change_selections(SelectionEffects::no_scroll(), window, cx, |s| {
25448                    s.select_ranges(selections.into_iter().map(|(start, end)| {
25449                        snapshot.clip_offset(MultiBufferOffset(start), Bias::Left)
25450                            ..snapshot.clip_offset(MultiBufferOffset(end), Bias::Right)
25451                    }));
25452                });
25453                self.selection_history.mode = SelectionHistoryMode::Normal;
25454            };
25455        }
25456
25457        self.read_scroll_position_from_db(item_id, workspace_id, window, cx);
25458    }
25459
25460    /// Load folds from the file_folds database table by file path.
25461    /// Used when manually opening a file that was previously closed.
25462    fn load_folds_from_db(
25463        &mut self,
25464        workspace_id: WorkspaceId,
25465        file_path: PathBuf,
25466        window: &mut Window,
25467        cx: &mut Context<Editor>,
25468    ) {
25469        if self.mode.is_minimap()
25470            || WorkspaceSettings::get(None, cx).restore_on_startup
25471                == RestoreOnStartupBehavior::EmptyTab
25472        {
25473            return;
25474        }
25475
25476        let Some(folds) = DB.get_file_folds(workspace_id, &file_path).log_err() else {
25477            return;
25478        };
25479        if folds.is_empty() {
25480            return;
25481        }
25482
25483        let snapshot = self.buffer.read(cx).snapshot(cx);
25484        let snapshot_len = snapshot.len().0;
25485
25486        // Helper: search for fingerprint in buffer, return offset if found
25487        let find_fingerprint = |fingerprint: &str, search_start: usize| -> Option<usize> {
25488            let search_start = snapshot
25489                .clip_offset(MultiBufferOffset(search_start), Bias::Left)
25490                .0;
25491            let search_end = snapshot_len.saturating_sub(fingerprint.len());
25492
25493            let mut byte_offset = search_start;
25494            for ch in snapshot.chars_at(MultiBufferOffset(search_start)) {
25495                if byte_offset > search_end {
25496                    break;
25497                }
25498                if snapshot.contains_str_at(MultiBufferOffset(byte_offset), fingerprint) {
25499                    return Some(byte_offset);
25500                }
25501                byte_offset += ch.len_utf8();
25502            }
25503            None
25504        };
25505
25506        let mut search_start = 0usize;
25507
25508        let valid_folds: Vec<_> = folds
25509            .into_iter()
25510            .filter_map(|(stored_start, stored_end, start_fp, end_fp)| {
25511                let sfp = start_fp?;
25512                let efp = end_fp?;
25513                let efp_len = efp.len();
25514
25515                let start_matches = stored_start < snapshot_len
25516                    && snapshot.contains_str_at(MultiBufferOffset(stored_start), &sfp);
25517                let efp_check_pos = stored_end.saturating_sub(efp_len);
25518                let end_matches = efp_check_pos >= stored_start
25519                    && stored_end <= snapshot_len
25520                    && snapshot.contains_str_at(MultiBufferOffset(efp_check_pos), &efp);
25521
25522                let (new_start, new_end) = if start_matches && end_matches {
25523                    (stored_start, stored_end)
25524                } else if sfp == efp {
25525                    let new_start = find_fingerprint(&sfp, search_start)?;
25526                    let fold_len = stored_end - stored_start;
25527                    let new_end = new_start + fold_len;
25528                    (new_start, new_end)
25529                } else {
25530                    let new_start = find_fingerprint(&sfp, search_start)?;
25531                    let efp_pos = find_fingerprint(&efp, new_start + sfp.len())?;
25532                    let new_end = efp_pos + efp_len;
25533                    (new_start, new_end)
25534                };
25535
25536                search_start = new_end;
25537
25538                if new_end <= new_start {
25539                    return None;
25540                }
25541
25542                Some(
25543                    snapshot.clip_offset(MultiBufferOffset(new_start), Bias::Left)
25544                        ..snapshot.clip_offset(MultiBufferOffset(new_end), Bias::Right),
25545                )
25546            })
25547            .collect();
25548
25549        if !valid_folds.is_empty() {
25550            self.fold_ranges(valid_folds, false, window, cx);
25551        }
25552    }
25553
25554    fn update_lsp_data(
25555        &mut self,
25556        for_buffer: Option<BufferId>,
25557        window: &mut Window,
25558        cx: &mut Context<'_, Self>,
25559    ) {
25560        if !self.enable_lsp_data {
25561            return;
25562        }
25563
25564        if let Some(buffer_id) = for_buffer {
25565            self.pull_diagnostics(buffer_id, window, cx);
25566        }
25567        self.refresh_semantic_tokens(for_buffer, None, cx);
25568        self.refresh_document_colors(for_buffer, window, cx);
25569        self.refresh_folding_ranges(for_buffer, window, cx);
25570        self.refresh_document_symbols(for_buffer, cx);
25571    }
25572
25573    fn register_visible_buffers(&mut self, cx: &mut Context<Self>) {
25574        if !self.mode().is_full() {
25575            return;
25576        }
25577        for (_, (visible_buffer, _, _)) in self.visible_excerpts(true, cx) {
25578            self.register_buffer(visible_buffer.read(cx).remote_id(), cx);
25579        }
25580    }
25581
25582    fn register_buffer(&mut self, buffer_id: BufferId, cx: &mut Context<Self>) {
25583        if !self.mode().is_full() {
25584            return;
25585        }
25586
25587        if !self.registered_buffers.contains_key(&buffer_id)
25588            && let Some(project) = self.project.as_ref()
25589        {
25590            if let Some(buffer) = self.buffer.read(cx).buffer(buffer_id) {
25591                project.update(cx, |project, cx| {
25592                    self.registered_buffers.insert(
25593                        buffer_id,
25594                        project.register_buffer_with_language_servers(&buffer, cx),
25595                    );
25596                });
25597            } else {
25598                self.registered_buffers.remove(&buffer_id);
25599            }
25600        }
25601    }
25602
25603    fn create_style(&self, cx: &App) -> EditorStyle {
25604        let settings = ThemeSettings::get_global(cx);
25605
25606        let mut text_style = match self.mode {
25607            EditorMode::SingleLine | EditorMode::AutoHeight { .. } => TextStyle {
25608                color: cx.theme().colors().editor_foreground,
25609                font_family: settings.ui_font.family.clone(),
25610                font_features: settings.ui_font.features.clone(),
25611                font_fallbacks: settings.ui_font.fallbacks.clone(),
25612                font_size: rems(0.875).into(),
25613                font_weight: settings.ui_font.weight,
25614                line_height: relative(settings.buffer_line_height.value()),
25615                ..Default::default()
25616            },
25617            EditorMode::Full { .. } | EditorMode::Minimap { .. } => TextStyle {
25618                color: cx.theme().colors().editor_foreground,
25619                font_family: settings.buffer_font.family.clone(),
25620                font_features: settings.buffer_font.features.clone(),
25621                font_fallbacks: settings.buffer_font.fallbacks.clone(),
25622                font_size: settings.buffer_font_size(cx).into(),
25623                font_weight: settings.buffer_font.weight,
25624                line_height: relative(settings.buffer_line_height.value()),
25625                ..Default::default()
25626            },
25627        };
25628        if let Some(text_style_refinement) = &self.text_style_refinement {
25629            text_style.refine(text_style_refinement)
25630        }
25631
25632        let background = match self.mode {
25633            EditorMode::SingleLine => cx.theme().system().transparent,
25634            EditorMode::AutoHeight { .. } => cx.theme().system().transparent,
25635            EditorMode::Full { .. } => cx.theme().colors().editor_background,
25636            EditorMode::Minimap { .. } => cx.theme().colors().editor_background.opacity(0.7),
25637        };
25638
25639        EditorStyle {
25640            background,
25641            border: cx.theme().colors().border,
25642            local_player: cx.theme().players().local(),
25643            text: text_style,
25644            scrollbar_width: EditorElement::SCROLLBAR_WIDTH,
25645            syntax: cx.theme().syntax().clone(),
25646            status: cx.theme().status().clone(),
25647            inlay_hints_style: make_inlay_hints_style(cx),
25648            edit_prediction_styles: make_suggestion_styles(cx),
25649            unnecessary_code_fade: settings.unnecessary_code_fade,
25650            show_underlines: self.diagnostics_enabled(),
25651        }
25652    }
25653
25654    fn breadcrumbs_inner(&self, cx: &App) -> Option<Vec<BreadcrumbText>> {
25655        let multibuffer = self.buffer().read(cx);
25656        let is_singleton = multibuffer.is_singleton();
25657        let (buffer_id, symbols) = self.outline_symbols_at_cursor.as_ref()?;
25658        let buffer = multibuffer.buffer(*buffer_id)?;
25659
25660        let buffer = buffer.read(cx);
25661        let settings = ThemeSettings::get_global(cx);
25662        // In a multi-buffer layout, we don't want to include the filename in the breadcrumbs
25663        let mut breadcrumbs = if is_singleton {
25664            let text = self.breadcrumb_header.clone().unwrap_or_else(|| {
25665                buffer
25666                    .snapshot()
25667                    .resolve_file_path(
25668                        self.project
25669                            .as_ref()
25670                            .map(|project| project.read(cx).visible_worktrees(cx).count() > 1)
25671                            .unwrap_or_default(),
25672                        cx,
25673                    )
25674                    .unwrap_or_else(|| {
25675                        if multibuffer.is_singleton() {
25676                            multibuffer.title(cx).to_string()
25677                        } else {
25678                            "untitled".to_string()
25679                        }
25680                    })
25681            });
25682            vec![BreadcrumbText {
25683                text,
25684                highlights: None,
25685                font: Some(settings.buffer_font.clone()),
25686            }]
25687        } else {
25688            vec![]
25689        };
25690
25691        breadcrumbs.extend(symbols.iter().map(|symbol| BreadcrumbText {
25692            text: symbol.text.clone(),
25693            highlights: Some(symbol.highlight_ranges.clone()),
25694            font: Some(settings.buffer_font.clone()),
25695        }));
25696        Some(breadcrumbs)
25697    }
25698
25699    fn disable_lsp_data(&mut self) {
25700        self.enable_lsp_data = false;
25701    }
25702
25703    fn disable_runnables(&mut self) {
25704        self.enable_runnables = false;
25705    }
25706}
25707
25708fn edit_for_markdown_paste<'a>(
25709    buffer: &MultiBufferSnapshot,
25710    range: Range<MultiBufferOffset>,
25711    to_insert: &'a str,
25712    url: Option<url::Url>,
25713) -> (Range<MultiBufferOffset>, Cow<'a, str>) {
25714    if url.is_none() {
25715        return (range, Cow::Borrowed(to_insert));
25716    };
25717
25718    let old_text = buffer.text_for_range(range.clone()).collect::<String>();
25719
25720    let new_text = if range.is_empty() || url::Url::parse(&old_text).is_ok() {
25721        Cow::Borrowed(to_insert)
25722    } else {
25723        Cow::Owned(format!("[{old_text}]({to_insert})"))
25724    };
25725    (range, new_text)
25726}
25727
25728fn process_completion_for_edit(
25729    completion: &Completion,
25730    intent: CompletionIntent,
25731    buffer: &Entity<Buffer>,
25732    cursor_position: &text::Anchor,
25733    cx: &mut Context<Editor>,
25734) -> CompletionEdit {
25735    let buffer = buffer.read(cx);
25736    let buffer_snapshot = buffer.snapshot();
25737    let (snippet, new_text) = if completion.is_snippet() {
25738        let mut snippet_source = completion.new_text.clone();
25739        // Workaround for typescript language server issues so that methods don't expand within
25740        // strings and functions with type expressions. The previous point is used because the query
25741        // for function identifier doesn't match when the cursor is immediately after. See PR #30312
25742        let previous_point = text::ToPoint::to_point(cursor_position, &buffer_snapshot);
25743        let previous_point = if previous_point.column > 0 {
25744            cursor_position.to_previous_offset(&buffer_snapshot)
25745        } else {
25746            cursor_position.to_offset(&buffer_snapshot)
25747        };
25748        if let Some(scope) = buffer_snapshot.language_scope_at(previous_point)
25749            && scope.prefers_label_for_snippet_in_completion()
25750            && let Some(label) = completion.label()
25751            && matches!(
25752                completion.kind(),
25753                Some(CompletionItemKind::FUNCTION) | Some(CompletionItemKind::METHOD)
25754            )
25755        {
25756            snippet_source = label;
25757        }
25758        match Snippet::parse(&snippet_source).log_err() {
25759            Some(parsed_snippet) => (Some(parsed_snippet.clone()), parsed_snippet.text),
25760            None => (None, completion.new_text.clone()),
25761        }
25762    } else {
25763        (None, completion.new_text.clone())
25764    };
25765
25766    let mut range_to_replace = {
25767        let replace_range = &completion.replace_range;
25768        if let CompletionSource::Lsp {
25769            insert_range: Some(insert_range),
25770            ..
25771        } = &completion.source
25772        {
25773            debug_assert_eq!(
25774                insert_range.start, replace_range.start,
25775                "insert_range and replace_range should start at the same position"
25776            );
25777            debug_assert!(
25778                insert_range
25779                    .start
25780                    .cmp(cursor_position, &buffer_snapshot)
25781                    .is_le(),
25782                "insert_range should start before or at cursor position"
25783            );
25784            debug_assert!(
25785                replace_range
25786                    .start
25787                    .cmp(cursor_position, &buffer_snapshot)
25788                    .is_le(),
25789                "replace_range should start before or at cursor position"
25790            );
25791
25792            let should_replace = match intent {
25793                CompletionIntent::CompleteWithInsert => false,
25794                CompletionIntent::CompleteWithReplace => true,
25795                CompletionIntent::Complete | CompletionIntent::Compose => {
25796                    let insert_mode =
25797                        language_settings(buffer.language().map(|l| l.name()), buffer.file(), cx)
25798                            .completions
25799                            .lsp_insert_mode;
25800                    match insert_mode {
25801                        LspInsertMode::Insert => false,
25802                        LspInsertMode::Replace => true,
25803                        LspInsertMode::ReplaceSubsequence => {
25804                            let mut text_to_replace = buffer.chars_for_range(
25805                                buffer.anchor_before(replace_range.start)
25806                                    ..buffer.anchor_after(replace_range.end),
25807                            );
25808                            let mut current_needle = text_to_replace.next();
25809                            for haystack_ch in completion.label.text.chars() {
25810                                if let Some(needle_ch) = current_needle
25811                                    && haystack_ch.eq_ignore_ascii_case(&needle_ch)
25812                                {
25813                                    current_needle = text_to_replace.next();
25814                                }
25815                            }
25816                            current_needle.is_none()
25817                        }
25818                        LspInsertMode::ReplaceSuffix => {
25819                            if replace_range
25820                                .end
25821                                .cmp(cursor_position, &buffer_snapshot)
25822                                .is_gt()
25823                            {
25824                                let range_after_cursor = *cursor_position..replace_range.end;
25825                                let text_after_cursor = buffer
25826                                    .text_for_range(
25827                                        buffer.anchor_before(range_after_cursor.start)
25828                                            ..buffer.anchor_after(range_after_cursor.end),
25829                                    )
25830                                    .collect::<String>()
25831                                    .to_ascii_lowercase();
25832                                completion
25833                                    .label
25834                                    .text
25835                                    .to_ascii_lowercase()
25836                                    .ends_with(&text_after_cursor)
25837                            } else {
25838                                true
25839                            }
25840                        }
25841                    }
25842                }
25843            };
25844
25845            if should_replace {
25846                replace_range.clone()
25847            } else {
25848                insert_range.clone()
25849            }
25850        } else {
25851            replace_range.clone()
25852        }
25853    };
25854
25855    if range_to_replace
25856        .end
25857        .cmp(cursor_position, &buffer_snapshot)
25858        .is_lt()
25859    {
25860        range_to_replace.end = *cursor_position;
25861    }
25862
25863    let replace_range = range_to_replace.to_offset(buffer);
25864    CompletionEdit {
25865        new_text,
25866        replace_range: BufferOffset(replace_range.start)..BufferOffset(replace_range.end),
25867        snippet,
25868    }
25869}
25870
25871struct CompletionEdit {
25872    new_text: String,
25873    replace_range: Range<BufferOffset>,
25874    snippet: Option<Snippet>,
25875}
25876
25877fn comment_delimiter_for_newline(
25878    start_point: &Point,
25879    buffer: &MultiBufferSnapshot,
25880    language: &LanguageScope,
25881) -> Option<Arc<str>> {
25882    let delimiters = language.line_comment_prefixes();
25883    let max_len_of_delimiter = delimiters.iter().map(|delimiter| delimiter.len()).max()?;
25884    let (snapshot, range) = buffer.buffer_line_for_row(MultiBufferRow(start_point.row))?;
25885
25886    let num_of_whitespaces = snapshot
25887        .chars_for_range(range.clone())
25888        .take_while(|c| c.is_whitespace())
25889        .count();
25890    let comment_candidate = snapshot
25891        .chars_for_range(range.clone())
25892        .skip(num_of_whitespaces)
25893        .take(max_len_of_delimiter + 2)
25894        .collect::<String>();
25895    let (delimiter, trimmed_len, is_repl) = delimiters
25896        .iter()
25897        .filter_map(|delimiter| {
25898            let prefix = delimiter.trim_end();
25899            if comment_candidate.starts_with(prefix) {
25900                let is_repl = if let Some(stripped_comment) = comment_candidate.strip_prefix(prefix)
25901                {
25902                    stripped_comment.starts_with(" %%")
25903                } else {
25904                    false
25905                };
25906                Some((delimiter, prefix.len(), is_repl))
25907            } else {
25908                None
25909            }
25910        })
25911        .max_by_key(|(_, len, _)| *len)?;
25912
25913    if let Some(BlockCommentConfig {
25914        start: block_start, ..
25915    }) = language.block_comment()
25916    {
25917        let block_start_trimmed = block_start.trim_end();
25918        if block_start_trimmed.starts_with(delimiter.trim_end()) {
25919            let line_content = snapshot
25920                .chars_for_range(range.clone())
25921                .skip(num_of_whitespaces)
25922                .take(block_start_trimmed.len())
25923                .collect::<String>();
25924
25925            if line_content.starts_with(block_start_trimmed) {
25926                return None;
25927            }
25928        }
25929    }
25930
25931    let cursor_is_placed_after_comment_marker =
25932        num_of_whitespaces + trimmed_len <= start_point.column as usize;
25933    if cursor_is_placed_after_comment_marker {
25934        if !is_repl {
25935            return Some(delimiter.clone());
25936        }
25937
25938        let line_content_after_cursor: String = snapshot
25939            .chars_for_range(range)
25940            .skip(start_point.column as usize)
25941            .collect();
25942
25943        if line_content_after_cursor.trim().is_empty() {
25944            return None;
25945        } else {
25946            return Some(delimiter.clone());
25947        }
25948    } else {
25949        None
25950    }
25951}
25952
25953fn documentation_delimiter_for_newline(
25954    start_point: &Point,
25955    buffer: &MultiBufferSnapshot,
25956    language: &LanguageScope,
25957    newline_config: &mut NewlineConfig,
25958) -> Option<Arc<str>> {
25959    let BlockCommentConfig {
25960        start: start_tag,
25961        end: end_tag,
25962        prefix: delimiter,
25963        tab_size: len,
25964    } = language.documentation_comment()?;
25965    let is_within_block_comment = buffer
25966        .language_scope_at(*start_point)
25967        .is_some_and(|scope| scope.override_name() == Some("comment"));
25968    if !is_within_block_comment {
25969        return None;
25970    }
25971
25972    let (snapshot, range) = buffer.buffer_line_for_row(MultiBufferRow(start_point.row))?;
25973
25974    let num_of_whitespaces = snapshot
25975        .chars_for_range(range.clone())
25976        .take_while(|c| c.is_whitespace())
25977        .count();
25978
25979    // 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.
25980    let column = start_point.column;
25981    let cursor_is_after_start_tag = {
25982        let start_tag_len = start_tag.len();
25983        let start_tag_line = snapshot
25984            .chars_for_range(range.clone())
25985            .skip(num_of_whitespaces)
25986            .take(start_tag_len)
25987            .collect::<String>();
25988        if start_tag_line.starts_with(start_tag.as_ref()) {
25989            num_of_whitespaces + start_tag_len <= column as usize
25990        } else {
25991            false
25992        }
25993    };
25994
25995    let cursor_is_after_delimiter = {
25996        let delimiter_trim = delimiter.trim_end();
25997        let delimiter_line = snapshot
25998            .chars_for_range(range.clone())
25999            .skip(num_of_whitespaces)
26000            .take(delimiter_trim.len())
26001            .collect::<String>();
26002        if delimiter_line.starts_with(delimiter_trim) {
26003            num_of_whitespaces + delimiter_trim.len() <= column as usize
26004        } else {
26005            false
26006        }
26007    };
26008
26009    let mut needs_extra_line = false;
26010    let mut extra_line_additional_indent = IndentSize::spaces(0);
26011
26012    let cursor_is_before_end_tag_if_exists = {
26013        let mut char_position = 0u32;
26014        let mut end_tag_offset = None;
26015
26016        'outer: for chunk in snapshot.text_for_range(range) {
26017            if let Some(byte_pos) = chunk.find(&**end_tag) {
26018                let chars_before_match = chunk[..byte_pos].chars().count() as u32;
26019                end_tag_offset = Some(char_position + chars_before_match);
26020                break 'outer;
26021            }
26022            char_position += chunk.chars().count() as u32;
26023        }
26024
26025        if let Some(end_tag_offset) = end_tag_offset {
26026            let cursor_is_before_end_tag = column <= end_tag_offset;
26027            if cursor_is_after_start_tag {
26028                if cursor_is_before_end_tag {
26029                    needs_extra_line = true;
26030                }
26031                let cursor_is_at_start_of_end_tag = column == end_tag_offset;
26032                if cursor_is_at_start_of_end_tag {
26033                    extra_line_additional_indent.len = *len;
26034                }
26035            }
26036            cursor_is_before_end_tag
26037        } else {
26038            true
26039        }
26040    };
26041
26042    if (cursor_is_after_start_tag || cursor_is_after_delimiter)
26043        && cursor_is_before_end_tag_if_exists
26044    {
26045        let additional_indent = if cursor_is_after_start_tag {
26046            IndentSize::spaces(*len)
26047        } else {
26048            IndentSize::spaces(0)
26049        };
26050
26051        *newline_config = NewlineConfig::Newline {
26052            additional_indent,
26053            extra_line_additional_indent: if needs_extra_line {
26054                Some(extra_line_additional_indent)
26055            } else {
26056                None
26057            },
26058            prevent_auto_indent: true,
26059        };
26060        Some(delimiter.clone())
26061    } else {
26062        None
26063    }
26064}
26065
26066const ORDERED_LIST_MAX_MARKER_LEN: usize = 16;
26067
26068fn list_delimiter_for_newline(
26069    start_point: &Point,
26070    buffer: &MultiBufferSnapshot,
26071    language: &LanguageScope,
26072    newline_config: &mut NewlineConfig,
26073) -> Option<Arc<str>> {
26074    let (snapshot, range) = buffer.buffer_line_for_row(MultiBufferRow(start_point.row))?;
26075
26076    let num_of_whitespaces = snapshot
26077        .chars_for_range(range.clone())
26078        .take_while(|c| c.is_whitespace())
26079        .count();
26080
26081    let task_list_entries: Vec<_> = language
26082        .task_list()
26083        .into_iter()
26084        .flat_map(|config| {
26085            config
26086                .prefixes
26087                .iter()
26088                .map(|prefix| (prefix.as_ref(), config.continuation.as_ref()))
26089        })
26090        .collect();
26091    let unordered_list_entries: Vec<_> = language
26092        .unordered_list()
26093        .iter()
26094        .map(|marker| (marker.as_ref(), marker.as_ref()))
26095        .collect();
26096
26097    let all_entries: Vec<_> = task_list_entries
26098        .into_iter()
26099        .chain(unordered_list_entries)
26100        .collect();
26101
26102    if let Some(max_prefix_len) = all_entries.iter().map(|(p, _)| p.len()).max() {
26103        let candidate: String = snapshot
26104            .chars_for_range(range.clone())
26105            .skip(num_of_whitespaces)
26106            .take(max_prefix_len)
26107            .collect();
26108
26109        if let Some((prefix, continuation)) = all_entries
26110            .iter()
26111            .filter(|(prefix, _)| candidate.starts_with(*prefix))
26112            .max_by_key(|(prefix, _)| prefix.len())
26113        {
26114            let end_of_prefix = num_of_whitespaces + prefix.len();
26115            let cursor_is_after_prefix = end_of_prefix <= start_point.column as usize;
26116            let has_content_after_marker = snapshot
26117                .chars_for_range(range)
26118                .skip(end_of_prefix)
26119                .any(|c| !c.is_whitespace());
26120
26121            if has_content_after_marker && cursor_is_after_prefix {
26122                return Some((*continuation).into());
26123            }
26124
26125            if start_point.column as usize == end_of_prefix {
26126                if num_of_whitespaces == 0 {
26127                    *newline_config = NewlineConfig::ClearCurrentLine;
26128                } else {
26129                    *newline_config = NewlineConfig::UnindentCurrentLine {
26130                        continuation: (*continuation).into(),
26131                    };
26132                }
26133            }
26134
26135            return None;
26136        }
26137    }
26138
26139    let candidate: String = snapshot
26140        .chars_for_range(range.clone())
26141        .skip(num_of_whitespaces)
26142        .take(ORDERED_LIST_MAX_MARKER_LEN)
26143        .collect();
26144
26145    for ordered_config in language.ordered_list() {
26146        let regex = match Regex::new(&ordered_config.pattern) {
26147            Ok(r) => r,
26148            Err(_) => continue,
26149        };
26150
26151        if let Some(captures) = regex.captures(&candidate) {
26152            let full_match = captures.get(0)?;
26153            let marker_len = full_match.len();
26154            let end_of_prefix = num_of_whitespaces + marker_len;
26155            let cursor_is_after_prefix = end_of_prefix <= start_point.column as usize;
26156
26157            let has_content_after_marker = snapshot
26158                .chars_for_range(range)
26159                .skip(end_of_prefix)
26160                .any(|c| !c.is_whitespace());
26161
26162            if has_content_after_marker && cursor_is_after_prefix {
26163                let number: u32 = captures.get(1)?.as_str().parse().ok()?;
26164                let continuation = ordered_config
26165                    .format
26166                    .replace("{1}", &(number + 1).to_string());
26167                return Some(continuation.into());
26168            }
26169
26170            if start_point.column as usize == end_of_prefix {
26171                let continuation = ordered_config.format.replace("{1}", "1");
26172                if num_of_whitespaces == 0 {
26173                    *newline_config = NewlineConfig::ClearCurrentLine;
26174                } else {
26175                    *newline_config = NewlineConfig::UnindentCurrentLine {
26176                        continuation: continuation.into(),
26177                    };
26178                }
26179            }
26180
26181            return None;
26182        }
26183    }
26184
26185    None
26186}
26187
26188fn is_list_prefix_row(
26189    row: MultiBufferRow,
26190    buffer: &MultiBufferSnapshot,
26191    language: &LanguageScope,
26192) -> bool {
26193    let Some((snapshot, range)) = buffer.buffer_line_for_row(row) else {
26194        return false;
26195    };
26196
26197    let num_of_whitespaces = snapshot
26198        .chars_for_range(range.clone())
26199        .take_while(|c| c.is_whitespace())
26200        .count();
26201
26202    let task_list_prefixes: Vec<_> = language
26203        .task_list()
26204        .into_iter()
26205        .flat_map(|config| {
26206            config
26207                .prefixes
26208                .iter()
26209                .map(|p| p.as_ref())
26210                .collect::<Vec<_>>()
26211        })
26212        .collect();
26213    let unordered_list_markers: Vec<_> = language
26214        .unordered_list()
26215        .iter()
26216        .map(|marker| marker.as_ref())
26217        .collect();
26218    let all_prefixes: Vec<_> = task_list_prefixes
26219        .into_iter()
26220        .chain(unordered_list_markers)
26221        .collect();
26222    if let Some(max_prefix_len) = all_prefixes.iter().map(|p| p.len()).max() {
26223        let candidate: String = snapshot
26224            .chars_for_range(range.clone())
26225            .skip(num_of_whitespaces)
26226            .take(max_prefix_len)
26227            .collect();
26228        if all_prefixes
26229            .iter()
26230            .any(|prefix| candidate.starts_with(*prefix))
26231        {
26232            return true;
26233        }
26234    }
26235
26236    let ordered_list_candidate: String = snapshot
26237        .chars_for_range(range)
26238        .skip(num_of_whitespaces)
26239        .take(ORDERED_LIST_MAX_MARKER_LEN)
26240        .collect();
26241    for ordered_config in language.ordered_list() {
26242        let regex = match Regex::new(&ordered_config.pattern) {
26243            Ok(r) => r,
26244            Err(_) => continue,
26245        };
26246        if let Some(captures) = regex.captures(&ordered_list_candidate) {
26247            return captures.get(0).is_some();
26248        }
26249    }
26250
26251    false
26252}
26253
26254#[derive(Debug)]
26255enum NewlineConfig {
26256    /// Insert newline with optional additional indent and optional extra blank line
26257    Newline {
26258        additional_indent: IndentSize,
26259        extra_line_additional_indent: Option<IndentSize>,
26260        prevent_auto_indent: bool,
26261    },
26262    /// Clear the current line
26263    ClearCurrentLine,
26264    /// Unindent the current line and add continuation
26265    UnindentCurrentLine { continuation: Arc<str> },
26266}
26267
26268impl NewlineConfig {
26269    fn has_extra_line(&self) -> bool {
26270        matches!(
26271            self,
26272            Self::Newline {
26273                extra_line_additional_indent: Some(_),
26274                ..
26275            }
26276        )
26277    }
26278
26279    fn insert_extra_newline_brackets(
26280        buffer: &MultiBufferSnapshot,
26281        range: Range<MultiBufferOffset>,
26282        language: &language::LanguageScope,
26283    ) -> bool {
26284        let leading_whitespace_len = buffer
26285            .reversed_chars_at(range.start)
26286            .take_while(|c| c.is_whitespace() && *c != '\n')
26287            .map(|c| c.len_utf8())
26288            .sum::<usize>();
26289        let trailing_whitespace_len = buffer
26290            .chars_at(range.end)
26291            .take_while(|c| c.is_whitespace() && *c != '\n')
26292            .map(|c| c.len_utf8())
26293            .sum::<usize>();
26294        let range = range.start - leading_whitespace_len..range.end + trailing_whitespace_len;
26295
26296        language.brackets().any(|(pair, enabled)| {
26297            let pair_start = pair.start.trim_end();
26298            let pair_end = pair.end.trim_start();
26299
26300            enabled
26301                && pair.newline
26302                && buffer.contains_str_at(range.end, pair_end)
26303                && buffer.contains_str_at(
26304                    range.start.saturating_sub_usize(pair_start.len()),
26305                    pair_start,
26306                )
26307        })
26308    }
26309
26310    fn insert_extra_newline_tree_sitter(
26311        buffer: &MultiBufferSnapshot,
26312        range: Range<MultiBufferOffset>,
26313    ) -> bool {
26314        let (buffer, range) = match buffer
26315            .range_to_buffer_ranges(range.start..=range.end)
26316            .as_slice()
26317        {
26318            [(buffer, range, _)] => (*buffer, range.clone()),
26319            _ => return false,
26320        };
26321        let pair = {
26322            let mut result: Option<BracketMatch<usize>> = None;
26323
26324            for pair in buffer
26325                .all_bracket_ranges(range.start.0..range.end.0)
26326                .filter(move |pair| {
26327                    pair.open_range.start <= range.start.0 && pair.close_range.end >= range.end.0
26328                })
26329            {
26330                let len = pair.close_range.end - pair.open_range.start;
26331
26332                if let Some(existing) = &result {
26333                    let existing_len = existing.close_range.end - existing.open_range.start;
26334                    if len > existing_len {
26335                        continue;
26336                    }
26337                }
26338
26339                result = Some(pair);
26340            }
26341
26342            result
26343        };
26344        let Some(pair) = pair else {
26345            return false;
26346        };
26347        pair.newline_only
26348            && buffer
26349                .chars_for_range(pair.open_range.end..range.start.0)
26350                .chain(buffer.chars_for_range(range.end.0..pair.close_range.start))
26351                .all(|c| c.is_whitespace() && c != '\n')
26352    }
26353}
26354
26355fn update_uncommitted_diff_for_buffer(
26356    editor: Entity<Editor>,
26357    project: &Entity<Project>,
26358    buffers: impl IntoIterator<Item = Entity<Buffer>>,
26359    buffer: Entity<MultiBuffer>,
26360    cx: &mut App,
26361) -> Task<()> {
26362    let mut tasks = Vec::new();
26363    project.update(cx, |project, cx| {
26364        for buffer in buffers {
26365            if project::File::from_dyn(buffer.read(cx).file()).is_some() {
26366                tasks.push(project.open_uncommitted_diff(buffer.clone(), cx))
26367            }
26368        }
26369    });
26370    cx.spawn(async move |cx| {
26371        let diffs = future::join_all(tasks).await;
26372        if editor.read_with(cx, |editor, _cx| editor.temporary_diff_override) {
26373            return;
26374        }
26375
26376        buffer.update(cx, |buffer, cx| {
26377            for diff in diffs.into_iter().flatten() {
26378                buffer.add_diff(diff, cx);
26379            }
26380        });
26381    })
26382}
26383
26384fn char_len_with_expanded_tabs(offset: usize, text: &str, tab_size: NonZeroU32) -> usize {
26385    let tab_size = tab_size.get() as usize;
26386    let mut width = offset;
26387
26388    for ch in text.chars() {
26389        width += if ch == '\t' {
26390            tab_size - (width % tab_size)
26391        } else {
26392            1
26393        };
26394    }
26395
26396    width - offset
26397}
26398
26399#[cfg(test)]
26400mod tests {
26401    use super::*;
26402
26403    #[test]
26404    fn test_string_size_with_expanded_tabs() {
26405        let nz = |val| NonZeroU32::new(val).unwrap();
26406        assert_eq!(char_len_with_expanded_tabs(0, "", nz(4)), 0);
26407        assert_eq!(char_len_with_expanded_tabs(0, "hello", nz(4)), 5);
26408        assert_eq!(char_len_with_expanded_tabs(0, "\thello", nz(4)), 9);
26409        assert_eq!(char_len_with_expanded_tabs(0, "abc\tab", nz(4)), 6);
26410        assert_eq!(char_len_with_expanded_tabs(0, "hello\t", nz(4)), 8);
26411        assert_eq!(char_len_with_expanded_tabs(0, "\t\t", nz(8)), 16);
26412        assert_eq!(char_len_with_expanded_tabs(0, "x\t", nz(8)), 8);
26413        assert_eq!(char_len_with_expanded_tabs(7, "x\t", nz(8)), 9);
26414    }
26415}
26416
26417/// Tokenizes a string into runs of text that should stick together, or that is whitespace.
26418struct WordBreakingTokenizer<'a> {
26419    input: &'a str,
26420}
26421
26422impl<'a> WordBreakingTokenizer<'a> {
26423    fn new(input: &'a str) -> Self {
26424        Self { input }
26425    }
26426}
26427
26428fn is_char_ideographic(ch: char) -> bool {
26429    use unicode_script::Script::*;
26430    use unicode_script::UnicodeScript;
26431    matches!(ch.script(), Han | Tangut | Yi)
26432}
26433
26434fn is_grapheme_ideographic(text: &str) -> bool {
26435    text.chars().any(is_char_ideographic)
26436}
26437
26438fn is_grapheme_whitespace(text: &str) -> bool {
26439    text.chars().any(|x| x.is_whitespace())
26440}
26441
26442fn should_stay_with_preceding_ideograph(text: &str) -> bool {
26443    text.chars()
26444        .next()
26445        .is_some_and(|ch| matches!(ch, '。' | '、' | ',' | '?' | '!' | ':' | ';' | '…'))
26446}
26447
26448#[derive(PartialEq, Eq, Debug, Clone, Copy)]
26449enum WordBreakToken<'a> {
26450    Word { token: &'a str, grapheme_len: usize },
26451    InlineWhitespace { token: &'a str, grapheme_len: usize },
26452    Newline,
26453}
26454
26455impl<'a> Iterator for WordBreakingTokenizer<'a> {
26456    /// Yields a span, the count of graphemes in the token, and whether it was
26457    /// whitespace. Note that it also breaks at word boundaries.
26458    type Item = WordBreakToken<'a>;
26459
26460    fn next(&mut self) -> Option<Self::Item> {
26461        use unicode_segmentation::UnicodeSegmentation;
26462        if self.input.is_empty() {
26463            return None;
26464        }
26465
26466        let mut iter = self.input.graphemes(true).peekable();
26467        let mut offset = 0;
26468        let mut grapheme_len = 0;
26469        if let Some(first_grapheme) = iter.next() {
26470            let is_newline = first_grapheme == "\n";
26471            let is_whitespace = is_grapheme_whitespace(first_grapheme);
26472            offset += first_grapheme.len();
26473            grapheme_len += 1;
26474            if is_grapheme_ideographic(first_grapheme) && !is_whitespace {
26475                if let Some(grapheme) = iter.peek().copied()
26476                    && should_stay_with_preceding_ideograph(grapheme)
26477                {
26478                    offset += grapheme.len();
26479                    grapheme_len += 1;
26480                }
26481            } else {
26482                let mut words = self.input[offset..].split_word_bound_indices().peekable();
26483                let mut next_word_bound = words.peek().copied();
26484                if next_word_bound.is_some_and(|(i, _)| i == 0) {
26485                    next_word_bound = words.next();
26486                }
26487                while let Some(grapheme) = iter.peek().copied() {
26488                    if next_word_bound.is_some_and(|(i, _)| i == offset) {
26489                        break;
26490                    };
26491                    if is_grapheme_whitespace(grapheme) != is_whitespace
26492                        || (grapheme == "\n") != is_newline
26493                    {
26494                        break;
26495                    };
26496                    offset += grapheme.len();
26497                    grapheme_len += 1;
26498                    iter.next();
26499                }
26500            }
26501            let token = &self.input[..offset];
26502            self.input = &self.input[offset..];
26503            if token == "\n" {
26504                Some(WordBreakToken::Newline)
26505            } else if is_whitespace {
26506                Some(WordBreakToken::InlineWhitespace {
26507                    token,
26508                    grapheme_len,
26509                })
26510            } else {
26511                Some(WordBreakToken::Word {
26512                    token,
26513                    grapheme_len,
26514                })
26515            }
26516        } else {
26517            None
26518        }
26519    }
26520}
26521
26522#[test]
26523fn test_word_breaking_tokenizer() {
26524    let tests: &[(&str, &[WordBreakToken<'static>])] = &[
26525        ("", &[]),
26526        ("  ", &[whitespace("  ", 2)]),
26527        ("Ʒ", &[word("Ʒ", 1)]),
26528        ("Ǽ", &[word("Ǽ", 1)]),
26529        ("", &[word("", 1)]),
26530        ("⋑⋑", &[word("⋑⋑", 2)]),
26531        (
26532            "原理,进而",
26533            &[word("", 1), word("理,", 2), word("", 1), word("", 1)],
26534        ),
26535        (
26536            "hello world",
26537            &[word("hello", 5), whitespace(" ", 1), word("world", 5)],
26538        ),
26539        (
26540            "hello, world",
26541            &[word("hello,", 6), whitespace(" ", 1), word("world", 5)],
26542        ),
26543        (
26544            "  hello world",
26545            &[
26546                whitespace("  ", 2),
26547                word("hello", 5),
26548                whitespace(" ", 1),
26549                word("world", 5),
26550            ],
26551        ),
26552        (
26553            "这是什么 \n 钢笔",
26554            &[
26555                word("", 1),
26556                word("", 1),
26557                word("", 1),
26558                word("", 1),
26559                whitespace(" ", 1),
26560                newline(),
26561                whitespace(" ", 1),
26562                word("", 1),
26563                word("", 1),
26564            ],
26565        ),
26566        (" mutton", &[whitespace("", 1), word("mutton", 6)]),
26567    ];
26568
26569    fn word(token: &'static str, grapheme_len: usize) -> WordBreakToken<'static> {
26570        WordBreakToken::Word {
26571            token,
26572            grapheme_len,
26573        }
26574    }
26575
26576    fn whitespace(token: &'static str, grapheme_len: usize) -> WordBreakToken<'static> {
26577        WordBreakToken::InlineWhitespace {
26578            token,
26579            grapheme_len,
26580        }
26581    }
26582
26583    fn newline() -> WordBreakToken<'static> {
26584        WordBreakToken::Newline
26585    }
26586
26587    for (input, result) in tests {
26588        assert_eq!(
26589            WordBreakingTokenizer::new(input)
26590                .collect::<Vec<_>>()
26591                .as_slice(),
26592            *result,
26593        );
26594    }
26595}
26596
26597fn wrap_with_prefix(
26598    first_line_prefix: String,
26599    subsequent_lines_prefix: String,
26600    unwrapped_text: String,
26601    wrap_column: usize,
26602    tab_size: NonZeroU32,
26603    preserve_existing_whitespace: bool,
26604) -> String {
26605    let first_line_prefix_len = char_len_with_expanded_tabs(0, &first_line_prefix, tab_size);
26606    let subsequent_lines_prefix_len =
26607        char_len_with_expanded_tabs(0, &subsequent_lines_prefix, tab_size);
26608    let mut wrapped_text = String::new();
26609    let mut current_line = first_line_prefix;
26610    let mut is_first_line = true;
26611
26612    let tokenizer = WordBreakingTokenizer::new(&unwrapped_text);
26613    let mut current_line_len = first_line_prefix_len;
26614    let mut in_whitespace = false;
26615    for token in tokenizer {
26616        let have_preceding_whitespace = in_whitespace;
26617        match token {
26618            WordBreakToken::Word {
26619                token,
26620                grapheme_len,
26621            } => {
26622                in_whitespace = false;
26623                let current_prefix_len = if is_first_line {
26624                    first_line_prefix_len
26625                } else {
26626                    subsequent_lines_prefix_len
26627                };
26628                if current_line_len + grapheme_len > wrap_column
26629                    && current_line_len != current_prefix_len
26630                {
26631                    wrapped_text.push_str(current_line.trim_end());
26632                    wrapped_text.push('\n');
26633                    is_first_line = false;
26634                    current_line = subsequent_lines_prefix.clone();
26635                    current_line_len = subsequent_lines_prefix_len;
26636                }
26637                current_line.push_str(token);
26638                current_line_len += grapheme_len;
26639            }
26640            WordBreakToken::InlineWhitespace {
26641                mut token,
26642                mut grapheme_len,
26643            } => {
26644                in_whitespace = true;
26645                if have_preceding_whitespace && !preserve_existing_whitespace {
26646                    continue;
26647                }
26648                if !preserve_existing_whitespace {
26649                    // Keep a single whitespace grapheme as-is
26650                    if let Some(first) =
26651                        unicode_segmentation::UnicodeSegmentation::graphemes(token, true).next()
26652                    {
26653                        token = first;
26654                    } else {
26655                        token = " ";
26656                    }
26657                    grapheme_len = 1;
26658                }
26659                let current_prefix_len = if is_first_line {
26660                    first_line_prefix_len
26661                } else {
26662                    subsequent_lines_prefix_len
26663                };
26664                if current_line_len + grapheme_len > wrap_column {
26665                    wrapped_text.push_str(current_line.trim_end());
26666                    wrapped_text.push('\n');
26667                    is_first_line = false;
26668                    current_line = subsequent_lines_prefix.clone();
26669                    current_line_len = subsequent_lines_prefix_len;
26670                } else if current_line_len != current_prefix_len || preserve_existing_whitespace {
26671                    current_line.push_str(token);
26672                    current_line_len += grapheme_len;
26673                }
26674            }
26675            WordBreakToken::Newline => {
26676                in_whitespace = true;
26677                let current_prefix_len = if is_first_line {
26678                    first_line_prefix_len
26679                } else {
26680                    subsequent_lines_prefix_len
26681                };
26682                if preserve_existing_whitespace {
26683                    wrapped_text.push_str(current_line.trim_end());
26684                    wrapped_text.push('\n');
26685                    is_first_line = false;
26686                    current_line = subsequent_lines_prefix.clone();
26687                    current_line_len = subsequent_lines_prefix_len;
26688                } else if have_preceding_whitespace {
26689                    continue;
26690                } else if current_line_len + 1 > wrap_column
26691                    && current_line_len != current_prefix_len
26692                {
26693                    wrapped_text.push_str(current_line.trim_end());
26694                    wrapped_text.push('\n');
26695                    is_first_line = false;
26696                    current_line = subsequent_lines_prefix.clone();
26697                    current_line_len = subsequent_lines_prefix_len;
26698                } else if current_line_len != current_prefix_len {
26699                    current_line.push(' ');
26700                    current_line_len += 1;
26701                }
26702            }
26703        }
26704    }
26705
26706    if !current_line.is_empty() {
26707        wrapped_text.push_str(&current_line);
26708    }
26709    wrapped_text
26710}
26711
26712#[test]
26713fn test_wrap_with_prefix() {
26714    assert_eq!(
26715        wrap_with_prefix(
26716            "# ".to_string(),
26717            "# ".to_string(),
26718            "abcdefg".to_string(),
26719            4,
26720            NonZeroU32::new(4).unwrap(),
26721            false,
26722        ),
26723        "# abcdefg"
26724    );
26725    assert_eq!(
26726        wrap_with_prefix(
26727            "".to_string(),
26728            "".to_string(),
26729            "\thello world".to_string(),
26730            8,
26731            NonZeroU32::new(4).unwrap(),
26732            false,
26733        ),
26734        "hello\nworld"
26735    );
26736    assert_eq!(
26737        wrap_with_prefix(
26738            "// ".to_string(),
26739            "// ".to_string(),
26740            "xx \nyy zz aa bb cc".to_string(),
26741            12,
26742            NonZeroU32::new(4).unwrap(),
26743            false,
26744        ),
26745        "// xx yy zz\n// aa bb cc"
26746    );
26747    assert_eq!(
26748        wrap_with_prefix(
26749            String::new(),
26750            String::new(),
26751            "这是什么 \n 钢笔".to_string(),
26752            3,
26753            NonZeroU32::new(4).unwrap(),
26754            false,
26755        ),
26756        "这是什\n么 钢\n"
26757    );
26758    assert_eq!(
26759        wrap_with_prefix(
26760            String::new(),
26761            String::new(),
26762            format!("foo{}bar", '\u{2009}'), // thin space
26763            80,
26764            NonZeroU32::new(4).unwrap(),
26765            false,
26766        ),
26767        format!("foo{}bar", '\u{2009}')
26768    );
26769}
26770
26771pub trait CollaborationHub {
26772    fn collaborators<'a>(&self, cx: &'a App) -> &'a HashMap<PeerId, Collaborator>;
26773    fn user_participant_indices<'a>(&self, cx: &'a App) -> &'a HashMap<u64, ParticipantIndex>;
26774    fn user_names(&self, cx: &App) -> HashMap<u64, SharedString>;
26775}
26776
26777impl CollaborationHub for Entity<Project> {
26778    fn collaborators<'a>(&self, cx: &'a App) -> &'a HashMap<PeerId, Collaborator> {
26779        self.read(cx).collaborators()
26780    }
26781
26782    fn user_participant_indices<'a>(&self, cx: &'a App) -> &'a HashMap<u64, ParticipantIndex> {
26783        self.read(cx).user_store().read(cx).participant_indices()
26784    }
26785
26786    fn user_names(&self, cx: &App) -> HashMap<u64, SharedString> {
26787        let this = self.read(cx);
26788        let user_ids = this.collaborators().values().map(|c| c.user_id);
26789        this.user_store().read(cx).participant_names(user_ids, cx)
26790    }
26791}
26792
26793pub trait SemanticsProvider {
26794    fn hover(
26795        &self,
26796        buffer: &Entity<Buffer>,
26797        position: text::Anchor,
26798        cx: &mut App,
26799    ) -> Option<Task<Option<Vec<project::Hover>>>>;
26800
26801    fn inline_values(
26802        &self,
26803        buffer_handle: Entity<Buffer>,
26804        range: Range<text::Anchor>,
26805        cx: &mut App,
26806    ) -> Option<Task<anyhow::Result<Vec<InlayHint>>>>;
26807
26808    fn applicable_inlay_chunks(
26809        &self,
26810        buffer: &Entity<Buffer>,
26811        ranges: &[Range<text::Anchor>],
26812        cx: &mut App,
26813    ) -> Vec<Range<BufferRow>>;
26814
26815    fn invalidate_inlay_hints(&self, for_buffers: &HashSet<BufferId>, cx: &mut App);
26816
26817    fn inlay_hints(
26818        &self,
26819        invalidate: InvalidationStrategy,
26820        buffer: Entity<Buffer>,
26821        ranges: Vec<Range<text::Anchor>>,
26822        known_chunks: Option<(clock::Global, HashSet<Range<BufferRow>>)>,
26823        cx: &mut App,
26824    ) -> Option<HashMap<Range<BufferRow>, Task<Result<CacheInlayHints>>>>;
26825
26826    fn semantic_tokens(
26827        &self,
26828        buffer: Entity<Buffer>,
26829        refresh: Option<RefreshForServer>,
26830        cx: &mut App,
26831    ) -> Option<Shared<Task<std::result::Result<BufferSemanticTokens, Arc<anyhow::Error>>>>>;
26832
26833    fn supports_inlay_hints(&self, buffer: &Entity<Buffer>, cx: &mut App) -> bool;
26834
26835    fn supports_semantic_tokens(&self, buffer: &Entity<Buffer>, cx: &mut App) -> bool;
26836
26837    fn document_highlights(
26838        &self,
26839        buffer: &Entity<Buffer>,
26840        position: text::Anchor,
26841        cx: &mut App,
26842    ) -> Option<Task<Result<Vec<DocumentHighlight>>>>;
26843
26844    fn definitions(
26845        &self,
26846        buffer: &Entity<Buffer>,
26847        position: text::Anchor,
26848        kind: GotoDefinitionKind,
26849        cx: &mut App,
26850    ) -> Option<Task<Result<Option<Vec<LocationLink>>>>>;
26851
26852    fn range_for_rename(
26853        &self,
26854        buffer: &Entity<Buffer>,
26855        position: text::Anchor,
26856        cx: &mut App,
26857    ) -> Option<Task<Result<Option<Range<text::Anchor>>>>>;
26858
26859    fn perform_rename(
26860        &self,
26861        buffer: &Entity<Buffer>,
26862        position: text::Anchor,
26863        new_name: String,
26864        cx: &mut App,
26865    ) -> Option<Task<Result<ProjectTransaction>>>;
26866}
26867
26868pub trait CompletionProvider {
26869    fn completions(
26870        &self,
26871        excerpt_id: ExcerptId,
26872        buffer: &Entity<Buffer>,
26873        buffer_position: text::Anchor,
26874        trigger: CompletionContext,
26875        window: &mut Window,
26876        cx: &mut Context<Editor>,
26877    ) -> Task<Result<Vec<CompletionResponse>>>;
26878
26879    fn resolve_completions(
26880        &self,
26881        _buffer: Entity<Buffer>,
26882        _completion_indices: Vec<usize>,
26883        _completions: Rc<RefCell<Box<[Completion]>>>,
26884        _cx: &mut Context<Editor>,
26885    ) -> Task<Result<bool>> {
26886        Task::ready(Ok(false))
26887    }
26888
26889    fn apply_additional_edits_for_completion(
26890        &self,
26891        _buffer: Entity<Buffer>,
26892        _completions: Rc<RefCell<Box<[Completion]>>>,
26893        _completion_index: usize,
26894        _push_to_history: bool,
26895        _cx: &mut Context<Editor>,
26896    ) -> Task<Result<Option<language::Transaction>>> {
26897        Task::ready(Ok(None))
26898    }
26899
26900    fn is_completion_trigger(
26901        &self,
26902        buffer: &Entity<Buffer>,
26903        position: language::Anchor,
26904        text: &str,
26905        trigger_in_words: bool,
26906        cx: &mut Context<Editor>,
26907    ) -> bool;
26908
26909    fn selection_changed(&self, _mat: Option<&StringMatch>, _window: &mut Window, _cx: &mut App) {}
26910
26911    fn sort_completions(&self) -> bool {
26912        true
26913    }
26914
26915    fn filter_completions(&self) -> bool {
26916        true
26917    }
26918
26919    fn show_snippets(&self) -> bool {
26920        false
26921    }
26922}
26923
26924pub trait CodeActionProvider {
26925    fn id(&self) -> Arc<str>;
26926
26927    fn code_actions(
26928        &self,
26929        buffer: &Entity<Buffer>,
26930        range: Range<text::Anchor>,
26931        window: &mut Window,
26932        cx: &mut App,
26933    ) -> Task<Result<Vec<CodeAction>>>;
26934
26935    fn apply_code_action(
26936        &self,
26937        buffer_handle: Entity<Buffer>,
26938        action: CodeAction,
26939        excerpt_id: ExcerptId,
26940        push_to_history: bool,
26941        window: &mut Window,
26942        cx: &mut App,
26943    ) -> Task<Result<ProjectTransaction>>;
26944}
26945
26946impl CodeActionProvider for Entity<Project> {
26947    fn id(&self) -> Arc<str> {
26948        "project".into()
26949    }
26950
26951    fn code_actions(
26952        &self,
26953        buffer: &Entity<Buffer>,
26954        range: Range<text::Anchor>,
26955        _window: &mut Window,
26956        cx: &mut App,
26957    ) -> Task<Result<Vec<CodeAction>>> {
26958        self.update(cx, |project, cx| {
26959            let code_lens_actions = project.code_lens_actions(buffer, range.clone(), cx);
26960            let code_actions = project.code_actions(buffer, range, None, cx);
26961            cx.background_spawn(async move {
26962                let (code_lens_actions, code_actions) = join(code_lens_actions, code_actions).await;
26963                Ok(code_lens_actions
26964                    .context("code lens fetch")?
26965                    .into_iter()
26966                    .flatten()
26967                    .chain(
26968                        code_actions
26969                            .context("code action fetch")?
26970                            .into_iter()
26971                            .flatten(),
26972                    )
26973                    .collect())
26974            })
26975        })
26976    }
26977
26978    fn apply_code_action(
26979        &self,
26980        buffer_handle: Entity<Buffer>,
26981        action: CodeAction,
26982        _excerpt_id: ExcerptId,
26983        push_to_history: bool,
26984        _window: &mut Window,
26985        cx: &mut App,
26986    ) -> Task<Result<ProjectTransaction>> {
26987        self.update(cx, |project, cx| {
26988            project.apply_code_action(buffer_handle, action, push_to_history, cx)
26989        })
26990    }
26991}
26992
26993fn snippet_completions(
26994    project: &Project,
26995    buffer: &Entity<Buffer>,
26996    buffer_anchor: text::Anchor,
26997    classifier: CharClassifier,
26998    cx: &mut App,
26999) -> Task<Result<CompletionResponse>> {
27000    let languages = buffer.read(cx).languages_at(buffer_anchor);
27001    let snippet_store = project.snippets().read(cx);
27002
27003    let scopes: Vec<_> = languages
27004        .iter()
27005        .filter_map(|language| {
27006            let language_name = language.lsp_id();
27007            let snippets = snippet_store.snippets_for(Some(language_name), cx);
27008
27009            if snippets.is_empty() {
27010                None
27011            } else {
27012                Some((language.default_scope(), snippets))
27013            }
27014        })
27015        .collect();
27016
27017    if scopes.is_empty() {
27018        return Task::ready(Ok(CompletionResponse {
27019            completions: vec![],
27020            display_options: CompletionDisplayOptions::default(),
27021            is_incomplete: false,
27022        }));
27023    }
27024
27025    let snapshot = buffer.read(cx).text_snapshot();
27026    let executor = cx.background_executor().clone();
27027
27028    cx.background_spawn(async move {
27029        let is_word_char = |c| classifier.is_word(c);
27030
27031        let mut is_incomplete = false;
27032        let mut completions: Vec<Completion> = Vec::new();
27033
27034        const MAX_PREFIX_LEN: usize = 128;
27035        let buffer_offset = text::ToOffset::to_offset(&buffer_anchor, &snapshot);
27036        let window_start = buffer_offset.saturating_sub(MAX_PREFIX_LEN);
27037        let window_start = snapshot.clip_offset(window_start, Bias::Left);
27038
27039        let max_buffer_window: String = snapshot
27040            .text_for_range(window_start..buffer_offset)
27041            .collect();
27042
27043        if max_buffer_window.is_empty() {
27044            return Ok(CompletionResponse {
27045                completions: vec![],
27046                display_options: CompletionDisplayOptions::default(),
27047                is_incomplete: true,
27048            });
27049        }
27050
27051        for (_scope, snippets) in scopes.into_iter() {
27052            // Sort snippets by word count to match longer snippet prefixes first.
27053            let mut sorted_snippet_candidates = snippets
27054                .iter()
27055                .enumerate()
27056                .flat_map(|(snippet_ix, snippet)| {
27057                    snippet
27058                        .prefix
27059                        .iter()
27060                        .enumerate()
27061                        .map(move |(prefix_ix, prefix)| {
27062                            let word_count =
27063                                snippet_candidate_suffixes(prefix, &is_word_char).count();
27064                            ((snippet_ix, prefix_ix), prefix, word_count)
27065                        })
27066                })
27067                .collect_vec();
27068            sorted_snippet_candidates
27069                .sort_unstable_by_key(|(_, _, word_count)| Reverse(*word_count));
27070
27071            // Each prefix may be matched multiple times; the completion menu must filter out duplicates.
27072
27073            let buffer_windows = snippet_candidate_suffixes(&max_buffer_window, &is_word_char)
27074                .take(
27075                    sorted_snippet_candidates
27076                        .first()
27077                        .map(|(_, _, word_count)| *word_count)
27078                        .unwrap_or_default(),
27079                )
27080                .collect_vec();
27081
27082            const MAX_RESULTS: usize = 100;
27083            // Each match also remembers how many characters from the buffer it consumed
27084            let mut matches: Vec<(StringMatch, usize)> = vec![];
27085
27086            let mut snippet_list_cutoff_index = 0;
27087            for (buffer_index, buffer_window) in buffer_windows.iter().enumerate().rev() {
27088                let word_count = buffer_index + 1;
27089                // Increase `snippet_list_cutoff_index` until we have all of the
27090                // snippets with sufficiently many words.
27091                while sorted_snippet_candidates
27092                    .get(snippet_list_cutoff_index)
27093                    .is_some_and(|(_ix, _prefix, snippet_word_count)| {
27094                        *snippet_word_count >= word_count
27095                    })
27096                {
27097                    snippet_list_cutoff_index += 1;
27098                }
27099
27100                // Take only the candidates with at least `word_count` many words
27101                let snippet_candidates_at_word_len =
27102                    &sorted_snippet_candidates[..snippet_list_cutoff_index];
27103
27104                let candidates = snippet_candidates_at_word_len
27105                    .iter()
27106                    .map(|(_snippet_ix, prefix, _snippet_word_count)| prefix)
27107                    .enumerate() // index in `sorted_snippet_candidates`
27108                    // First char must match
27109                    .filter(|(_ix, prefix)| {
27110                        itertools::equal(
27111                            prefix
27112                                .chars()
27113                                .next()
27114                                .into_iter()
27115                                .flat_map(|c| c.to_lowercase()),
27116                            buffer_window
27117                                .chars()
27118                                .next()
27119                                .into_iter()
27120                                .flat_map(|c| c.to_lowercase()),
27121                        )
27122                    })
27123                    .map(|(ix, prefix)| StringMatchCandidate::new(ix, prefix))
27124                    .collect::<Vec<StringMatchCandidate>>();
27125
27126                matches.extend(
27127                    fuzzy::match_strings(
27128                        &candidates,
27129                        &buffer_window,
27130                        buffer_window.chars().any(|c| c.is_uppercase()),
27131                        true,
27132                        MAX_RESULTS - matches.len(), // always prioritize longer snippets
27133                        &Default::default(),
27134                        executor.clone(),
27135                    )
27136                    .await
27137                    .into_iter()
27138                    .map(|string_match| (string_match, buffer_window.len())),
27139                );
27140
27141                if matches.len() >= MAX_RESULTS {
27142                    break;
27143                }
27144            }
27145
27146            let to_lsp = |point: &text::Anchor| {
27147                let end = text::ToPointUtf16::to_point_utf16(point, &snapshot);
27148                point_to_lsp(end)
27149            };
27150            let lsp_end = to_lsp(&buffer_anchor);
27151
27152            if matches.len() >= MAX_RESULTS {
27153                is_incomplete = true;
27154            }
27155
27156            completions.extend(matches.iter().map(|(string_match, buffer_window_len)| {
27157                let ((snippet_index, prefix_index), matching_prefix, _snippet_word_count) =
27158                    sorted_snippet_candidates[string_match.candidate_id];
27159                let snippet = &snippets[snippet_index];
27160                let start = buffer_offset - buffer_window_len;
27161                let start = snapshot.anchor_before(start);
27162                let range = start..buffer_anchor;
27163                let lsp_start = to_lsp(&start);
27164                let lsp_range = lsp::Range {
27165                    start: lsp_start,
27166                    end: lsp_end,
27167                };
27168                Completion {
27169                    replace_range: range,
27170                    new_text: snippet.body.clone(),
27171                    source: CompletionSource::Lsp {
27172                        insert_range: None,
27173                        server_id: LanguageServerId(usize::MAX),
27174                        resolved: true,
27175                        lsp_completion: Box::new(lsp::CompletionItem {
27176                            label: snippet.prefix.first().unwrap().clone(),
27177                            kind: Some(CompletionItemKind::SNIPPET),
27178                            label_details: snippet.description.as_ref().map(|description| {
27179                                lsp::CompletionItemLabelDetails {
27180                                    detail: Some(description.clone()),
27181                                    description: None,
27182                                }
27183                            }),
27184                            insert_text_format: Some(InsertTextFormat::SNIPPET),
27185                            text_edit: Some(lsp::CompletionTextEdit::InsertAndReplace(
27186                                lsp::InsertReplaceEdit {
27187                                    new_text: snippet.body.clone(),
27188                                    insert: lsp_range,
27189                                    replace: lsp_range,
27190                                },
27191                            )),
27192                            filter_text: Some(snippet.body.clone()),
27193                            sort_text: Some(char::MAX.to_string()),
27194                            ..lsp::CompletionItem::default()
27195                        }),
27196                        lsp_defaults: None,
27197                    },
27198                    label: CodeLabel {
27199                        text: matching_prefix.clone(),
27200                        runs: Vec::new(),
27201                        filter_range: 0..matching_prefix.len(),
27202                    },
27203                    icon_path: None,
27204                    documentation: Some(CompletionDocumentation::SingleLineAndMultiLinePlainText {
27205                        single_line: snippet.name.clone().into(),
27206                        plain_text: snippet
27207                            .description
27208                            .clone()
27209                            .map(|description| description.into()),
27210                    }),
27211                    insert_text_mode: None,
27212                    confirm: None,
27213                    match_start: Some(start),
27214                    snippet_deduplication_key: Some((snippet_index, prefix_index)),
27215                }
27216            }));
27217        }
27218
27219        Ok(CompletionResponse {
27220            completions,
27221            display_options: CompletionDisplayOptions::default(),
27222            is_incomplete,
27223        })
27224    })
27225}
27226
27227impl CompletionProvider for Entity<Project> {
27228    fn completions(
27229        &self,
27230        _excerpt_id: ExcerptId,
27231        buffer: &Entity<Buffer>,
27232        buffer_position: text::Anchor,
27233        options: CompletionContext,
27234        _window: &mut Window,
27235        cx: &mut Context<Editor>,
27236    ) -> Task<Result<Vec<CompletionResponse>>> {
27237        self.update(cx, |project, cx| {
27238            let task = project.completions(buffer, buffer_position, options, cx);
27239            cx.background_spawn(task)
27240        })
27241    }
27242
27243    fn resolve_completions(
27244        &self,
27245        buffer: Entity<Buffer>,
27246        completion_indices: Vec<usize>,
27247        completions: Rc<RefCell<Box<[Completion]>>>,
27248        cx: &mut Context<Editor>,
27249    ) -> Task<Result<bool>> {
27250        self.update(cx, |project, cx| {
27251            project.lsp_store().update(cx, |lsp_store, cx| {
27252                lsp_store.resolve_completions(buffer, completion_indices, completions, cx)
27253            })
27254        })
27255    }
27256
27257    fn apply_additional_edits_for_completion(
27258        &self,
27259        buffer: Entity<Buffer>,
27260        completions: Rc<RefCell<Box<[Completion]>>>,
27261        completion_index: usize,
27262        push_to_history: bool,
27263        cx: &mut Context<Editor>,
27264    ) -> Task<Result<Option<language::Transaction>>> {
27265        self.update(cx, |project, cx| {
27266            project.lsp_store().update(cx, |lsp_store, cx| {
27267                lsp_store.apply_additional_edits_for_completion(
27268                    buffer,
27269                    completions,
27270                    completion_index,
27271                    push_to_history,
27272                    cx,
27273                )
27274            })
27275        })
27276    }
27277
27278    fn is_completion_trigger(
27279        &self,
27280        buffer: &Entity<Buffer>,
27281        position: language::Anchor,
27282        text: &str,
27283        trigger_in_words: bool,
27284        cx: &mut Context<Editor>,
27285    ) -> bool {
27286        let mut chars = text.chars();
27287        let char = if let Some(char) = chars.next() {
27288            char
27289        } else {
27290            return false;
27291        };
27292        if chars.next().is_some() {
27293            return false;
27294        }
27295
27296        let buffer = buffer.read(cx);
27297        let snapshot = buffer.snapshot();
27298        let classifier = snapshot
27299            .char_classifier_at(position)
27300            .scope_context(Some(CharScopeContext::Completion));
27301        if trigger_in_words && classifier.is_word(char) {
27302            return true;
27303        }
27304
27305        buffer.completion_triggers().contains(text)
27306    }
27307
27308    fn show_snippets(&self) -> bool {
27309        true
27310    }
27311}
27312
27313impl SemanticsProvider for WeakEntity<Project> {
27314    fn hover(
27315        &self,
27316        buffer: &Entity<Buffer>,
27317        position: text::Anchor,
27318        cx: &mut App,
27319    ) -> Option<Task<Option<Vec<project::Hover>>>> {
27320        self.update(cx, |project, cx| project.hover(buffer, position, cx))
27321            .ok()
27322    }
27323
27324    fn document_highlights(
27325        &self,
27326        buffer: &Entity<Buffer>,
27327        position: text::Anchor,
27328        cx: &mut App,
27329    ) -> Option<Task<Result<Vec<DocumentHighlight>>>> {
27330        self.update(cx, |project, cx| {
27331            project.document_highlights(buffer, position, cx)
27332        })
27333        .ok()
27334    }
27335
27336    fn definitions(
27337        &self,
27338        buffer: &Entity<Buffer>,
27339        position: text::Anchor,
27340        kind: GotoDefinitionKind,
27341        cx: &mut App,
27342    ) -> Option<Task<Result<Option<Vec<LocationLink>>>>> {
27343        self.update(cx, |project, cx| match kind {
27344            GotoDefinitionKind::Symbol => project.definitions(buffer, position, cx),
27345            GotoDefinitionKind::Declaration => project.declarations(buffer, position, cx),
27346            GotoDefinitionKind::Type => project.type_definitions(buffer, position, cx),
27347            GotoDefinitionKind::Implementation => project.implementations(buffer, position, cx),
27348        })
27349        .ok()
27350    }
27351
27352    fn supports_inlay_hints(&self, buffer: &Entity<Buffer>, cx: &mut App) -> bool {
27353        self.update(cx, |project, cx| {
27354            if project
27355                .active_debug_session(cx)
27356                .is_some_and(|(session, _)| session.read(cx).any_stopped_thread())
27357            {
27358                return true;
27359            }
27360
27361            buffer.update(cx, |buffer, cx| {
27362                project.any_language_server_supports_inlay_hints(buffer, cx)
27363            })
27364        })
27365        .unwrap_or(false)
27366    }
27367
27368    fn supports_semantic_tokens(&self, buffer: &Entity<Buffer>, cx: &mut App) -> bool {
27369        self.update(cx, |project, cx| {
27370            buffer.update(cx, |buffer, cx| {
27371                project.any_language_server_supports_semantic_tokens(buffer, cx)
27372            })
27373        })
27374        .unwrap_or(false)
27375    }
27376
27377    fn inline_values(
27378        &self,
27379        buffer_handle: Entity<Buffer>,
27380        range: Range<text::Anchor>,
27381        cx: &mut App,
27382    ) -> Option<Task<anyhow::Result<Vec<InlayHint>>>> {
27383        self.update(cx, |project, cx| {
27384            let (session, active_stack_frame) = project.active_debug_session(cx)?;
27385
27386            Some(project.inline_values(session, active_stack_frame, buffer_handle, range, cx))
27387        })
27388        .ok()
27389        .flatten()
27390    }
27391
27392    fn applicable_inlay_chunks(
27393        &self,
27394        buffer: &Entity<Buffer>,
27395        ranges: &[Range<text::Anchor>],
27396        cx: &mut App,
27397    ) -> Vec<Range<BufferRow>> {
27398        self.update(cx, |project, cx| {
27399            project.lsp_store().update(cx, |lsp_store, cx| {
27400                lsp_store.applicable_inlay_chunks(buffer, ranges, cx)
27401            })
27402        })
27403        .unwrap_or_default()
27404    }
27405
27406    fn invalidate_inlay_hints(&self, for_buffers: &HashSet<BufferId>, cx: &mut App) {
27407        self.update(cx, |project, cx| {
27408            project.lsp_store().update(cx, |lsp_store, _| {
27409                lsp_store.invalidate_inlay_hints(for_buffers)
27410            })
27411        })
27412        .ok();
27413    }
27414
27415    fn inlay_hints(
27416        &self,
27417        invalidate: InvalidationStrategy,
27418        buffer: Entity<Buffer>,
27419        ranges: Vec<Range<text::Anchor>>,
27420        known_chunks: Option<(clock::Global, HashSet<Range<BufferRow>>)>,
27421        cx: &mut App,
27422    ) -> Option<HashMap<Range<BufferRow>, Task<Result<CacheInlayHints>>>> {
27423        self.update(cx, |project, cx| {
27424            project.lsp_store().update(cx, |lsp_store, cx| {
27425                lsp_store.inlay_hints(invalidate, buffer, ranges, known_chunks, cx)
27426            })
27427        })
27428        .ok()
27429    }
27430
27431    fn semantic_tokens(
27432        &self,
27433        buffer: Entity<Buffer>,
27434        refresh: Option<RefreshForServer>,
27435        cx: &mut App,
27436    ) -> Option<Shared<Task<std::result::Result<BufferSemanticTokens, Arc<anyhow::Error>>>>> {
27437        self.update(cx, |this, cx| {
27438            this.lsp_store().update(cx, |lsp_store, cx| {
27439                lsp_store.semantic_tokens(buffer, refresh, cx)
27440            })
27441        })
27442        .ok()
27443    }
27444
27445    fn range_for_rename(
27446        &self,
27447        buffer: &Entity<Buffer>,
27448        position: text::Anchor,
27449        cx: &mut App,
27450    ) -> Option<Task<Result<Option<Range<text::Anchor>>>>> {
27451        self.update(cx, |project, cx| {
27452            let buffer = buffer.clone();
27453            let task = project.prepare_rename(buffer.clone(), position, cx);
27454            cx.spawn(async move |_, cx| {
27455                Ok(match task.await? {
27456                    PrepareRenameResponse::Success(range) => Some(range),
27457                    PrepareRenameResponse::InvalidPosition => None,
27458                    PrepareRenameResponse::OnlyUnpreparedRenameSupported => {
27459                        // Fallback on using TreeSitter info to determine identifier range
27460                        buffer.read_with(cx, |buffer, _| {
27461                            let snapshot = buffer.snapshot();
27462                            let (range, kind) = snapshot.surrounding_word(position, None);
27463                            if kind != Some(CharKind::Word) {
27464                                return None;
27465                            }
27466                            Some(
27467                                snapshot.anchor_before(range.start)
27468                                    ..snapshot.anchor_after(range.end),
27469                            )
27470                        })
27471                    }
27472                })
27473            })
27474        })
27475        .ok()
27476    }
27477
27478    fn perform_rename(
27479        &self,
27480        buffer: &Entity<Buffer>,
27481        position: text::Anchor,
27482        new_name: String,
27483        cx: &mut App,
27484    ) -> Option<Task<Result<ProjectTransaction>>> {
27485        self.update(cx, |project, cx| {
27486            project.perform_rename(buffer.clone(), position, new_name, cx)
27487        })
27488        .ok()
27489    }
27490}
27491
27492fn consume_contiguous_rows(
27493    contiguous_row_selections: &mut Vec<Selection<Point>>,
27494    selection: &Selection<Point>,
27495    display_map: &DisplaySnapshot,
27496    selections: &mut Peekable<std::slice::Iter<Selection<Point>>>,
27497) -> (MultiBufferRow, MultiBufferRow) {
27498    contiguous_row_selections.push(selection.clone());
27499    let start_row = starting_row(selection, display_map);
27500    let mut end_row = ending_row(selection, display_map);
27501
27502    while let Some(next_selection) = selections.peek() {
27503        if next_selection.start.row <= end_row.0 {
27504            end_row = ending_row(next_selection, display_map);
27505            contiguous_row_selections.push(selections.next().unwrap().clone());
27506        } else {
27507            break;
27508        }
27509    }
27510    (start_row, end_row)
27511}
27512
27513fn starting_row(selection: &Selection<Point>, display_map: &DisplaySnapshot) -> MultiBufferRow {
27514    if selection.start.column > 0 {
27515        MultiBufferRow(display_map.prev_line_boundary(selection.start).0.row)
27516    } else {
27517        MultiBufferRow(selection.start.row)
27518    }
27519}
27520
27521fn ending_row(next_selection: &Selection<Point>, display_map: &DisplaySnapshot) -> MultiBufferRow {
27522    if next_selection.end.column > 0 || next_selection.is_empty() {
27523        MultiBufferRow(display_map.next_line_boundary(next_selection.end).0.row + 1)
27524    } else {
27525        MultiBufferRow(next_selection.end.row)
27526    }
27527}
27528
27529impl EditorSnapshot {
27530    pub fn remote_selections_in_range<'a>(
27531        &'a self,
27532        range: &'a Range<Anchor>,
27533        collaboration_hub: &dyn CollaborationHub,
27534        cx: &'a App,
27535    ) -> impl 'a + Iterator<Item = RemoteSelection> {
27536        let participant_names = collaboration_hub.user_names(cx);
27537        let participant_indices = collaboration_hub.user_participant_indices(cx);
27538        let collaborators_by_peer_id = collaboration_hub.collaborators(cx);
27539        let collaborators_by_replica_id = collaborators_by_peer_id
27540            .values()
27541            .map(|collaborator| (collaborator.replica_id, collaborator))
27542            .collect::<HashMap<_, _>>();
27543        self.buffer_snapshot()
27544            .selections_in_range(range, false)
27545            .filter_map(move |(replica_id, line_mode, cursor_shape, selection)| {
27546                if replica_id == ReplicaId::AGENT {
27547                    Some(RemoteSelection {
27548                        replica_id,
27549                        selection,
27550                        cursor_shape,
27551                        line_mode,
27552                        collaborator_id: CollaboratorId::Agent,
27553                        user_name: Some("Agent".into()),
27554                        color: cx.theme().players().agent(),
27555                    })
27556                } else {
27557                    let collaborator = collaborators_by_replica_id.get(&replica_id)?;
27558                    let participant_index = participant_indices.get(&collaborator.user_id).copied();
27559                    let user_name = participant_names.get(&collaborator.user_id).cloned();
27560                    Some(RemoteSelection {
27561                        replica_id,
27562                        selection,
27563                        cursor_shape,
27564                        line_mode,
27565                        collaborator_id: CollaboratorId::PeerId(collaborator.peer_id),
27566                        user_name,
27567                        color: if let Some(index) = participant_index {
27568                            cx.theme().players().color_for_participant(index.0)
27569                        } else {
27570                            cx.theme().players().absent()
27571                        },
27572                    })
27573                }
27574            })
27575    }
27576
27577    pub fn hunks_for_ranges(
27578        &self,
27579        ranges: impl IntoIterator<Item = Range<Point>>,
27580    ) -> Vec<MultiBufferDiffHunk> {
27581        let mut hunks = Vec::new();
27582        let mut processed_buffer_rows: HashMap<BufferId, HashSet<Range<text::Anchor>>> =
27583            HashMap::default();
27584        for query_range in ranges {
27585            let query_rows =
27586                MultiBufferRow(query_range.start.row)..MultiBufferRow(query_range.end.row + 1);
27587            for hunk in self.buffer_snapshot().diff_hunks_in_range(
27588                Point::new(query_rows.start.0, 0)..Point::new(query_rows.end.0, 0),
27589            ) {
27590                // Include deleted hunks that are adjacent to the query range, because
27591                // otherwise they would be missed.
27592                let mut intersects_range = hunk.row_range.overlaps(&query_rows);
27593                if hunk.status().is_deleted() {
27594                    intersects_range |= hunk.row_range.start == query_rows.end;
27595                    intersects_range |= hunk.row_range.end == query_rows.start;
27596                }
27597                if intersects_range {
27598                    if !processed_buffer_rows
27599                        .entry(hunk.buffer_id)
27600                        .or_default()
27601                        .insert(hunk.buffer_range.start..hunk.buffer_range.end)
27602                    {
27603                        continue;
27604                    }
27605                    hunks.push(hunk);
27606                }
27607            }
27608        }
27609
27610        hunks
27611    }
27612
27613    fn display_diff_hunks_for_rows<'a>(
27614        &'a self,
27615        display_rows: Range<DisplayRow>,
27616        folded_buffers: &'a HashSet<BufferId>,
27617    ) -> impl 'a + Iterator<Item = DisplayDiffHunk> {
27618        let buffer_start = DisplayPoint::new(display_rows.start, 0).to_point(self);
27619        let buffer_end = DisplayPoint::new(display_rows.end, 0).to_point(self);
27620
27621        self.buffer_snapshot()
27622            .diff_hunks_in_range(buffer_start..buffer_end)
27623            .filter_map(|hunk| {
27624                if folded_buffers.contains(&hunk.buffer_id)
27625                    || (hunk.row_range.is_empty() && self.buffer.all_diff_hunks_expanded())
27626                {
27627                    return None;
27628                }
27629
27630                let hunk_start_point = Point::new(hunk.row_range.start.0, 0);
27631                let hunk_end_point = if hunk.row_range.end > hunk.row_range.start {
27632                    let last_row = MultiBufferRow(hunk.row_range.end.0 - 1);
27633                    let line_len = self.buffer_snapshot().line_len(last_row);
27634                    Point::new(last_row.0, line_len)
27635                } else {
27636                    Point::new(hunk.row_range.end.0, 0)
27637                };
27638
27639                let hunk_display_start = self.point_to_display_point(hunk_start_point, Bias::Left);
27640                let hunk_display_end = self.point_to_display_point(hunk_end_point, Bias::Right);
27641
27642                let display_hunk = if hunk_display_start.column() != 0 {
27643                    DisplayDiffHunk::Folded {
27644                        display_row: hunk_display_start.row(),
27645                    }
27646                } else {
27647                    let mut end_row = hunk_display_end.row();
27648                    if hunk.row_range.end > hunk.row_range.start || hunk_display_end.column() > 0 {
27649                        end_row.0 += 1;
27650                    }
27651                    let is_created_file = hunk.is_created_file();
27652
27653                    DisplayDiffHunk::Unfolded {
27654                        status: hunk.status(),
27655                        diff_base_byte_range: hunk.diff_base_byte_range.start.0
27656                            ..hunk.diff_base_byte_range.end.0,
27657                        word_diffs: hunk.word_diffs,
27658                        display_row_range: hunk_display_start.row()..end_row,
27659                        multi_buffer_range: Anchor::range_in_buffer(
27660                            hunk.excerpt_id,
27661                            hunk.buffer_range,
27662                        ),
27663                        is_created_file,
27664                    }
27665                };
27666
27667                Some(display_hunk)
27668            })
27669    }
27670
27671    pub fn language_at<T: ToOffset>(&self, position: T) -> Option<&Arc<Language>> {
27672        self.display_snapshot
27673            .buffer_snapshot()
27674            .language_at(position)
27675    }
27676
27677    pub fn is_focused(&self) -> bool {
27678        self.is_focused
27679    }
27680
27681    pub fn placeholder_text(&self) -> Option<String> {
27682        self.placeholder_display_snapshot
27683            .as_ref()
27684            .map(|display_map| display_map.text())
27685    }
27686
27687    pub fn scroll_position(&self) -> gpui::Point<ScrollOffset> {
27688        self.scroll_anchor.scroll_position(&self.display_snapshot)
27689    }
27690
27691    pub fn gutter_dimensions(
27692        &self,
27693        font_id: FontId,
27694        font_size: Pixels,
27695        style: &EditorStyle,
27696        window: &mut Window,
27697        cx: &App,
27698    ) -> GutterDimensions {
27699        if self.show_gutter
27700            && let Some(ch_width) = cx.text_system().ch_width(font_id, font_size).log_err()
27701            && let Some(ch_advance) = cx.text_system().ch_advance(font_id, font_size).log_err()
27702        {
27703            let show_git_gutter = self.show_git_diff_gutter.unwrap_or_else(|| {
27704                matches!(
27705                    ProjectSettings::get_global(cx).git.git_gutter,
27706                    GitGutterSetting::TrackedFiles
27707                )
27708            });
27709            let gutter_settings = EditorSettings::get_global(cx).gutter;
27710            let show_line_numbers = self
27711                .show_line_numbers
27712                .unwrap_or(gutter_settings.line_numbers);
27713            let line_gutter_width = if show_line_numbers {
27714                // Avoid flicker-like gutter resizes when the line number gains another digit by
27715                // only resizing the gutter on files with > 10**min_line_number_digits lines.
27716                let min_width_for_number_on_gutter =
27717                    ch_advance * gutter_settings.min_line_number_digits as f32;
27718                self.max_line_number_width(style, window)
27719                    .max(min_width_for_number_on_gutter)
27720            } else {
27721                0.0.into()
27722            };
27723
27724            let show_runnables = self.show_runnables.unwrap_or(gutter_settings.runnables);
27725            let show_breakpoints = self.show_breakpoints.unwrap_or(gutter_settings.breakpoints);
27726
27727            let git_blame_entries_width =
27728                self.git_blame_gutter_max_author_length
27729                    .map(|max_author_length| {
27730                        let renderer = cx.global::<GlobalBlameRenderer>().0.clone();
27731                        const MAX_RELATIVE_TIMESTAMP: &str = "60 minutes ago";
27732
27733                        /// The number of characters to dedicate to gaps and margins.
27734                        const SPACING_WIDTH: usize = 4;
27735
27736                        let max_char_count = max_author_length.min(renderer.max_author_length())
27737                            + ::git::SHORT_SHA_LENGTH
27738                            + MAX_RELATIVE_TIMESTAMP.len()
27739                            + SPACING_WIDTH;
27740
27741                        ch_advance * max_char_count
27742                    });
27743
27744            let is_singleton = self.buffer_snapshot().is_singleton();
27745
27746            let mut left_padding = git_blame_entries_width.unwrap_or(Pixels::ZERO);
27747            left_padding += if !is_singleton {
27748                ch_width * 4.0
27749            } else if show_runnables || show_breakpoints {
27750                ch_width * 3.0
27751            } else if show_git_gutter && show_line_numbers {
27752                ch_width * 2.0
27753            } else if show_git_gutter || show_line_numbers {
27754                ch_width
27755            } else {
27756                px(0.)
27757            };
27758
27759            let shows_folds = is_singleton && gutter_settings.folds;
27760
27761            let right_padding = if shows_folds && show_line_numbers {
27762                ch_width * 4.0
27763            } else if shows_folds || (!is_singleton && show_line_numbers) {
27764                ch_width * 3.0
27765            } else if show_line_numbers {
27766                ch_width
27767            } else {
27768                px(0.)
27769            };
27770
27771            GutterDimensions {
27772                left_padding,
27773                right_padding,
27774                width: line_gutter_width + left_padding + right_padding,
27775                margin: GutterDimensions::default_gutter_margin(font_id, font_size, cx),
27776                git_blame_entries_width,
27777            }
27778        } else if self.offset_content {
27779            GutterDimensions::default_with_margin(font_id, font_size, cx)
27780        } else {
27781            GutterDimensions::default()
27782        }
27783    }
27784
27785    pub fn render_crease_toggle(
27786        &self,
27787        buffer_row: MultiBufferRow,
27788        row_contains_cursor: bool,
27789        editor: Entity<Editor>,
27790        window: &mut Window,
27791        cx: &mut App,
27792    ) -> Option<AnyElement> {
27793        let folded = self.is_line_folded(buffer_row);
27794        let mut is_foldable = false;
27795
27796        if let Some(crease) = self
27797            .crease_snapshot
27798            .query_row(buffer_row, self.buffer_snapshot())
27799        {
27800            is_foldable = true;
27801            match crease {
27802                Crease::Inline { render_toggle, .. } | Crease::Block { render_toggle, .. } => {
27803                    if let Some(render_toggle) = render_toggle {
27804                        let toggle_callback =
27805                            Arc::new(move |folded, window: &mut Window, cx: &mut App| {
27806                                if folded {
27807                                    editor.update(cx, |editor, cx| {
27808                                        editor.fold_at(buffer_row, window, cx)
27809                                    });
27810                                } else {
27811                                    editor.update(cx, |editor, cx| {
27812                                        editor.unfold_at(buffer_row, window, cx)
27813                                    });
27814                                }
27815                            });
27816                        return Some((render_toggle)(
27817                            buffer_row,
27818                            folded,
27819                            toggle_callback,
27820                            window,
27821                            cx,
27822                        ));
27823                    }
27824                }
27825            }
27826        }
27827
27828        is_foldable |= !self.use_lsp_folding_ranges && self.starts_indent(buffer_row);
27829
27830        if folded || (is_foldable && (row_contains_cursor || self.gutter_hovered)) {
27831            Some(
27832                Disclosure::new(("gutter_crease", buffer_row.0), !folded)
27833                    .toggle_state(folded)
27834                    .on_click(window.listener_for(&editor, move |this, _e, window, cx| {
27835                        if folded {
27836                            this.unfold_at(buffer_row, window, cx);
27837                        } else {
27838                            this.fold_at(buffer_row, window, cx);
27839                        }
27840                    }))
27841                    .into_any_element(),
27842            )
27843        } else {
27844            None
27845        }
27846    }
27847
27848    pub fn render_crease_trailer(
27849        &self,
27850        buffer_row: MultiBufferRow,
27851        window: &mut Window,
27852        cx: &mut App,
27853    ) -> Option<AnyElement> {
27854        let folded = self.is_line_folded(buffer_row);
27855        if let Crease::Inline { render_trailer, .. } = self
27856            .crease_snapshot
27857            .query_row(buffer_row, self.buffer_snapshot())?
27858        {
27859            let render_trailer = render_trailer.as_ref()?;
27860            Some(render_trailer(buffer_row, folded, window, cx))
27861        } else {
27862            None
27863        }
27864    }
27865
27866    pub fn max_line_number_width(&self, style: &EditorStyle, window: &mut Window) -> Pixels {
27867        let digit_count = self.widest_line_number().ilog10() + 1;
27868        column_pixels(style, digit_count as usize, window)
27869    }
27870
27871    /// Returns the line delta from `base` to `line` in the multibuffer, ignoring wrapped lines.
27872    ///
27873    /// This is positive if `base` is before `line`.
27874    fn relative_line_delta(
27875        &self,
27876        current_selection_head: DisplayRow,
27877        first_visible_row: DisplayRow,
27878        consider_wrapped_lines: bool,
27879    ) -> i64 {
27880        let current_selection_head = current_selection_head.as_display_point().to_point(self);
27881        let first_visible_row = first_visible_row.as_display_point().to_point(self);
27882
27883        if consider_wrapped_lines {
27884            let wrap_snapshot = self.wrap_snapshot();
27885            let base_wrap_row = wrap_snapshot
27886                .make_wrap_point(current_selection_head, Bias::Left)
27887                .row();
27888            let wrap_row = wrap_snapshot
27889                .make_wrap_point(first_visible_row, Bias::Left)
27890                .row();
27891
27892            wrap_row.0 as i64 - base_wrap_row.0 as i64
27893        } else {
27894            let fold_snapshot = self.fold_snapshot();
27895            let base_fold_row = fold_snapshot
27896                .to_fold_point(self.to_inlay_point(current_selection_head), Bias::Left)
27897                .row();
27898            let fold_row = fold_snapshot
27899                .to_fold_point(self.to_inlay_point(first_visible_row), Bias::Left)
27900                .row();
27901
27902            fold_row as i64 - base_fold_row as i64
27903        }
27904    }
27905
27906    /// Returns the unsigned relative line number to display for each row in `rows`.
27907    ///
27908    /// Wrapped rows are excluded from the hashmap if `count_relative_lines` is `false`.
27909    pub fn calculate_relative_line_numbers(
27910        &self,
27911        rows: &Range<DisplayRow>,
27912        current_selection_head: DisplayRow,
27913        count_wrapped_lines: bool,
27914    ) -> HashMap<DisplayRow, u32> {
27915        let initial_offset =
27916            self.relative_line_delta(current_selection_head, rows.start, count_wrapped_lines);
27917
27918        self.row_infos(rows.start)
27919            .take(rows.len())
27920            .enumerate()
27921            .map(|(i, row_info)| (DisplayRow(rows.start.0 + i as u32), row_info))
27922            .filter(|(_row, row_info)| {
27923                row_info.buffer_row.is_some()
27924                    || (count_wrapped_lines && row_info.wrapped_buffer_row.is_some())
27925            })
27926            .enumerate()
27927            .filter_map(|(i, (row, row_info))| {
27928                // We want to ensure here that the current line has absolute
27929                // numbering, even if we are in a soft-wrapped line. With the
27930                // exception that if we are in a deleted line, we should number this
27931                // relative with 0, as otherwise it would have no line number at all
27932                let relative_line_number = (initial_offset + i as i64).unsigned_abs() as u32;
27933
27934                (relative_line_number != 0
27935                    || row_info
27936                        .diff_status
27937                        .is_some_and(|status| status.is_deleted()))
27938                .then_some((row, relative_line_number))
27939            })
27940            .collect()
27941    }
27942}
27943
27944pub fn column_pixels(style: &EditorStyle, column: usize, window: &Window) -> Pixels {
27945    let font_size = style.text.font_size.to_pixels(window.rem_size());
27946    let layout = window.text_system().shape_line(
27947        SharedString::from(" ".repeat(column)),
27948        font_size,
27949        &[TextRun {
27950            len: column,
27951            font: style.text.font(),
27952            color: Hsla::default(),
27953            ..Default::default()
27954        }],
27955        None,
27956    );
27957
27958    layout.width
27959}
27960
27961impl Deref for EditorSnapshot {
27962    type Target = DisplaySnapshot;
27963
27964    fn deref(&self) -> &Self::Target {
27965        &self.display_snapshot
27966    }
27967}
27968
27969#[derive(Clone, Debug, PartialEq, Eq)]
27970pub enum EditorEvent {
27971    /// Emitted when the stored review comments change (added, removed, or updated).
27972    ReviewCommentsChanged {
27973        /// The new total count of review comments.
27974        total_count: usize,
27975    },
27976    InputIgnored {
27977        text: Arc<str>,
27978    },
27979    InputHandled {
27980        utf16_range_to_replace: Option<Range<isize>>,
27981        text: Arc<str>,
27982    },
27983    ExcerptsAdded {
27984        buffer: Entity<Buffer>,
27985        predecessor: ExcerptId,
27986        excerpts: Vec<(ExcerptId, ExcerptRange<language::Anchor>)>,
27987    },
27988    ExcerptsRemoved {
27989        ids: Vec<ExcerptId>,
27990        removed_buffer_ids: Vec<BufferId>,
27991    },
27992    BufferFoldToggled {
27993        ids: Vec<ExcerptId>,
27994        folded: bool,
27995    },
27996    ExcerptsEdited {
27997        ids: Vec<ExcerptId>,
27998    },
27999    ExcerptsExpanded {
28000        ids: Vec<ExcerptId>,
28001    },
28002    ExpandExcerptsRequested {
28003        excerpt_ids: Vec<ExcerptId>,
28004        lines: u32,
28005        direction: ExpandExcerptDirection,
28006    },
28007    StageOrUnstageRequested {
28008        stage: bool,
28009        hunks: Vec<MultiBufferDiffHunk>,
28010    },
28011    OpenExcerptsRequested {
28012        selections_by_buffer: HashMap<BufferId, (Vec<Range<BufferOffset>>, Option<u32>)>,
28013        split: bool,
28014    },
28015    RestoreRequested {
28016        hunks: Vec<MultiBufferDiffHunk>,
28017    },
28018    BufferEdited,
28019    Edited {
28020        transaction_id: clock::Lamport,
28021    },
28022    Reparsed(BufferId),
28023    Focused,
28024    FocusedIn,
28025    Blurred,
28026    DirtyChanged,
28027    Saved,
28028    TitleChanged,
28029    SelectionsChanged {
28030        local: bool,
28031    },
28032    ScrollPositionChanged {
28033        local: bool,
28034        autoscroll: bool,
28035    },
28036    TransactionUndone {
28037        transaction_id: clock::Lamport,
28038    },
28039    TransactionBegun {
28040        transaction_id: clock::Lamport,
28041    },
28042    CursorShapeChanged,
28043    BreadcrumbsChanged,
28044    OutlineSymbolsChanged,
28045    PushedToNavHistory {
28046        anchor: Anchor,
28047        is_deactivate: bool,
28048    },
28049}
28050
28051impl EventEmitter<EditorEvent> for Editor {}
28052
28053impl Focusable for Editor {
28054    fn focus_handle(&self, _cx: &App) -> FocusHandle {
28055        self.focus_handle.clone()
28056    }
28057}
28058
28059impl Render for Editor {
28060    fn render(&mut self, _: &mut Window, cx: &mut Context<Self>) -> impl IntoElement {
28061        EditorElement::new(&cx.entity(), self.create_style(cx))
28062    }
28063}
28064
28065impl EntityInputHandler for Editor {
28066    fn text_for_range(
28067        &mut self,
28068        range_utf16: Range<usize>,
28069        adjusted_range: &mut Option<Range<usize>>,
28070        _: &mut Window,
28071        cx: &mut Context<Self>,
28072    ) -> Option<String> {
28073        let snapshot = self.buffer.read(cx).read(cx);
28074        let start = snapshot.clip_offset_utf16(
28075            MultiBufferOffsetUtf16(OffsetUtf16(range_utf16.start)),
28076            Bias::Left,
28077        );
28078        let end = snapshot.clip_offset_utf16(
28079            MultiBufferOffsetUtf16(OffsetUtf16(range_utf16.end)),
28080            Bias::Right,
28081        );
28082        if (start.0.0..end.0.0) != range_utf16 {
28083            adjusted_range.replace(start.0.0..end.0.0);
28084        }
28085        Some(snapshot.text_for_range(start..end).collect())
28086    }
28087
28088    fn selected_text_range(
28089        &mut self,
28090        ignore_disabled_input: bool,
28091        _: &mut Window,
28092        cx: &mut Context<Self>,
28093    ) -> Option<UTF16Selection> {
28094        // Prevent the IME menu from appearing when holding down an alphabetic key
28095        // while input is disabled.
28096        if !ignore_disabled_input && !self.input_enabled {
28097            return None;
28098        }
28099
28100        let selection = self
28101            .selections
28102            .newest::<MultiBufferOffsetUtf16>(&self.display_snapshot(cx));
28103        let range = selection.range();
28104
28105        Some(UTF16Selection {
28106            range: range.start.0.0..range.end.0.0,
28107            reversed: selection.reversed,
28108        })
28109    }
28110
28111    fn marked_text_range(&self, _: &mut Window, cx: &mut Context<Self>) -> Option<Range<usize>> {
28112        let snapshot = self.buffer.read(cx).read(cx);
28113        let range = self
28114            .text_highlights(HighlightKey::InputComposition, cx)?
28115            .1
28116            .first()?;
28117        Some(range.start.to_offset_utf16(&snapshot).0.0..range.end.to_offset_utf16(&snapshot).0.0)
28118    }
28119
28120    fn unmark_text(&mut self, _: &mut Window, cx: &mut Context<Self>) {
28121        self.clear_highlights(HighlightKey::InputComposition, cx);
28122        self.ime_transaction.take();
28123    }
28124
28125    fn replace_text_in_range(
28126        &mut self,
28127        range_utf16: Option<Range<usize>>,
28128        text: &str,
28129        window: &mut Window,
28130        cx: &mut Context<Self>,
28131    ) {
28132        if !self.input_enabled {
28133            cx.emit(EditorEvent::InputIgnored { text: text.into() });
28134            return;
28135        }
28136
28137        self.transact(window, cx, |this, window, cx| {
28138            let new_selected_ranges = if let Some(range_utf16) = range_utf16 {
28139                let range_utf16 = MultiBufferOffsetUtf16(OffsetUtf16(range_utf16.start))
28140                    ..MultiBufferOffsetUtf16(OffsetUtf16(range_utf16.end));
28141                Some(this.selection_replacement_ranges(range_utf16, cx))
28142            } else {
28143                this.marked_text_ranges(cx)
28144            };
28145
28146            let range_to_replace = new_selected_ranges.as_ref().and_then(|ranges_to_replace| {
28147                let newest_selection_id = this.selections.newest_anchor().id;
28148                this.selections
28149                    .all::<MultiBufferOffsetUtf16>(&this.display_snapshot(cx))
28150                    .iter()
28151                    .zip(ranges_to_replace.iter())
28152                    .find_map(|(selection, range)| {
28153                        if selection.id == newest_selection_id {
28154                            Some(
28155                                (range.start.0.0 as isize - selection.head().0.0 as isize)
28156                                    ..(range.end.0.0 as isize - selection.head().0.0 as isize),
28157                            )
28158                        } else {
28159                            None
28160                        }
28161                    })
28162            });
28163
28164            cx.emit(EditorEvent::InputHandled {
28165                utf16_range_to_replace: range_to_replace,
28166                text: text.into(),
28167            });
28168
28169            if let Some(new_selected_ranges) = new_selected_ranges {
28170                this.change_selections(SelectionEffects::no_scroll(), window, cx, |selections| {
28171                    selections.select_ranges(new_selected_ranges)
28172                });
28173                this.backspace(&Default::default(), window, cx);
28174            }
28175
28176            this.handle_input(text, window, cx);
28177        });
28178
28179        if let Some(transaction) = self.ime_transaction {
28180            self.buffer.update(cx, |buffer, cx| {
28181                buffer.group_until_transaction(transaction, cx);
28182            });
28183        }
28184
28185        self.unmark_text(window, cx);
28186    }
28187
28188    fn replace_and_mark_text_in_range(
28189        &mut self,
28190        range_utf16: Option<Range<usize>>,
28191        text: &str,
28192        new_selected_range_utf16: Option<Range<usize>>,
28193        window: &mut Window,
28194        cx: &mut Context<Self>,
28195    ) {
28196        if !self.input_enabled {
28197            return;
28198        }
28199
28200        let transaction = self.transact(window, cx, |this, window, cx| {
28201            let ranges_to_replace = if let Some(mut marked_ranges) = this.marked_text_ranges(cx) {
28202                let snapshot = this.buffer.read(cx).read(cx);
28203                if let Some(relative_range_utf16) = range_utf16.as_ref() {
28204                    for marked_range in &mut marked_ranges {
28205                        marked_range.end = marked_range.start + relative_range_utf16.end;
28206                        marked_range.start += relative_range_utf16.start;
28207                        marked_range.start =
28208                            snapshot.clip_offset_utf16(marked_range.start, Bias::Left);
28209                        marked_range.end =
28210                            snapshot.clip_offset_utf16(marked_range.end, Bias::Right);
28211                    }
28212                }
28213                Some(marked_ranges)
28214            } else if let Some(range_utf16) = range_utf16 {
28215                let range_utf16 = MultiBufferOffsetUtf16(OffsetUtf16(range_utf16.start))
28216                    ..MultiBufferOffsetUtf16(OffsetUtf16(range_utf16.end));
28217                Some(this.selection_replacement_ranges(range_utf16, cx))
28218            } else {
28219                None
28220            };
28221
28222            let range_to_replace = ranges_to_replace.as_ref().and_then(|ranges_to_replace| {
28223                let newest_selection_id = this.selections.newest_anchor().id;
28224                this.selections
28225                    .all::<MultiBufferOffsetUtf16>(&this.display_snapshot(cx))
28226                    .iter()
28227                    .zip(ranges_to_replace.iter())
28228                    .find_map(|(selection, range)| {
28229                        if selection.id == newest_selection_id {
28230                            Some(
28231                                (range.start.0.0 as isize - selection.head().0.0 as isize)
28232                                    ..(range.end.0.0 as isize - selection.head().0.0 as isize),
28233                            )
28234                        } else {
28235                            None
28236                        }
28237                    })
28238            });
28239
28240            cx.emit(EditorEvent::InputHandled {
28241                utf16_range_to_replace: range_to_replace,
28242                text: text.into(),
28243            });
28244
28245            if let Some(ranges) = ranges_to_replace {
28246                this.change_selections(SelectionEffects::no_scroll(), window, cx, |s| {
28247                    s.select_ranges(ranges)
28248                });
28249            }
28250
28251            let marked_ranges = {
28252                let snapshot = this.buffer.read(cx).read(cx);
28253                this.selections
28254                    .disjoint_anchors_arc()
28255                    .iter()
28256                    .map(|selection| {
28257                        selection.start.bias_left(&snapshot)..selection.end.bias_right(&snapshot)
28258                    })
28259                    .collect::<Vec<_>>()
28260            };
28261
28262            if text.is_empty() {
28263                this.unmark_text(window, cx);
28264            } else {
28265                this.highlight_text(
28266                    HighlightKey::InputComposition,
28267                    marked_ranges.clone(),
28268                    HighlightStyle {
28269                        underline: Some(UnderlineStyle {
28270                            thickness: px(1.),
28271                            color: None,
28272                            wavy: false,
28273                        }),
28274                        ..Default::default()
28275                    },
28276                    cx,
28277                );
28278            }
28279
28280            // Disable auto-closing when composing text (i.e. typing a `"` on a Brazilian keyboard)
28281            let use_autoclose = this.use_autoclose;
28282            let use_auto_surround = this.use_auto_surround;
28283            this.set_use_autoclose(false);
28284            this.set_use_auto_surround(false);
28285            this.handle_input(text, window, cx);
28286            this.set_use_autoclose(use_autoclose);
28287            this.set_use_auto_surround(use_auto_surround);
28288
28289            if let Some(new_selected_range) = new_selected_range_utf16 {
28290                let snapshot = this.buffer.read(cx).read(cx);
28291                let new_selected_ranges = marked_ranges
28292                    .into_iter()
28293                    .map(|marked_range| {
28294                        let insertion_start = marked_range.start.to_offset_utf16(&snapshot).0;
28295                        let new_start = MultiBufferOffsetUtf16(OffsetUtf16(
28296                            insertion_start.0 + new_selected_range.start,
28297                        ));
28298                        let new_end = MultiBufferOffsetUtf16(OffsetUtf16(
28299                            insertion_start.0 + new_selected_range.end,
28300                        ));
28301                        snapshot.clip_offset_utf16(new_start, Bias::Left)
28302                            ..snapshot.clip_offset_utf16(new_end, Bias::Right)
28303                    })
28304                    .collect::<Vec<_>>();
28305
28306                drop(snapshot);
28307                this.change_selections(SelectionEffects::no_scroll(), window, cx, |selections| {
28308                    selections.select_ranges(new_selected_ranges)
28309                });
28310            }
28311        });
28312
28313        self.ime_transaction = self.ime_transaction.or(transaction);
28314        if let Some(transaction) = self.ime_transaction {
28315            self.buffer.update(cx, |buffer, cx| {
28316                buffer.group_until_transaction(transaction, cx);
28317            });
28318        }
28319
28320        if self
28321            .text_highlights(HighlightKey::InputComposition, cx)
28322            .is_none()
28323        {
28324            self.ime_transaction.take();
28325        }
28326    }
28327
28328    fn bounds_for_range(
28329        &mut self,
28330        range_utf16: Range<usize>,
28331        element_bounds: gpui::Bounds<Pixels>,
28332        window: &mut Window,
28333        cx: &mut Context<Self>,
28334    ) -> Option<gpui::Bounds<Pixels>> {
28335        let text_layout_details = self.text_layout_details(window, cx);
28336        let CharacterDimensions {
28337            em_width,
28338            em_advance,
28339            line_height,
28340        } = self.character_dimensions(window, cx);
28341
28342        let snapshot = self.snapshot(window, cx);
28343        let scroll_position = snapshot.scroll_position();
28344        let scroll_left = scroll_position.x * ScrollOffset::from(em_advance);
28345
28346        let start =
28347            MultiBufferOffsetUtf16(OffsetUtf16(range_utf16.start)).to_display_point(&snapshot);
28348        let x = Pixels::from(
28349            ScrollOffset::from(
28350                snapshot.x_for_display_point(start, &text_layout_details)
28351                    + self.gutter_dimensions.full_width(),
28352            ) - scroll_left,
28353        );
28354        let y = line_height * (start.row().as_f64() - scroll_position.y) as f32;
28355
28356        Some(Bounds {
28357            origin: element_bounds.origin + point(x, y),
28358            size: size(em_width, line_height),
28359        })
28360    }
28361
28362    fn character_index_for_point(
28363        &mut self,
28364        point: gpui::Point<Pixels>,
28365        _window: &mut Window,
28366        _cx: &mut Context<Self>,
28367    ) -> Option<usize> {
28368        let position_map = self.last_position_map.as_ref()?;
28369        if !position_map.text_hitbox.contains(&point) {
28370            return None;
28371        }
28372        let display_point = position_map.point_for_position(point).previous_valid;
28373        let anchor = position_map
28374            .snapshot
28375            .display_point_to_anchor(display_point, Bias::Left);
28376        let utf16_offset = anchor.to_offset_utf16(&position_map.snapshot.buffer_snapshot());
28377        Some(utf16_offset.0.0)
28378    }
28379
28380    fn accepts_text_input(&self, _window: &mut Window, _cx: &mut Context<Self>) -> bool {
28381        self.input_enabled
28382    }
28383}
28384
28385trait SelectionExt {
28386    fn display_range(&self, map: &DisplaySnapshot) -> Range<DisplayPoint>;
28387    fn spanned_rows(
28388        &self,
28389        include_end_if_at_line_start: bool,
28390        map: &DisplaySnapshot,
28391    ) -> Range<MultiBufferRow>;
28392}
28393
28394impl<T: ToPoint + ToOffset> SelectionExt for Selection<T> {
28395    fn display_range(&self, map: &DisplaySnapshot) -> Range<DisplayPoint> {
28396        let start = self
28397            .start
28398            .to_point(map.buffer_snapshot())
28399            .to_display_point(map);
28400        let end = self
28401            .end
28402            .to_point(map.buffer_snapshot())
28403            .to_display_point(map);
28404        if self.reversed {
28405            end..start
28406        } else {
28407            start..end
28408        }
28409    }
28410
28411    fn spanned_rows(
28412        &self,
28413        include_end_if_at_line_start: bool,
28414        map: &DisplaySnapshot,
28415    ) -> Range<MultiBufferRow> {
28416        let start = self.start.to_point(map.buffer_snapshot());
28417        let mut end = self.end.to_point(map.buffer_snapshot());
28418        if !include_end_if_at_line_start && start.row != end.row && end.column == 0 {
28419            end.row -= 1;
28420        }
28421
28422        let buffer_start = map.prev_line_boundary(start).0;
28423        let buffer_end = map.next_line_boundary(end).0;
28424        MultiBufferRow(buffer_start.row)..MultiBufferRow(buffer_end.row + 1)
28425    }
28426}
28427
28428impl<T: InvalidationRegion> InvalidationStack<T> {
28429    fn invalidate<S>(&mut self, selections: &[Selection<S>], buffer: &MultiBufferSnapshot)
28430    where
28431        S: Clone + ToOffset,
28432    {
28433        while let Some(region) = self.last() {
28434            let all_selections_inside_invalidation_ranges =
28435                if selections.len() == region.ranges().len() {
28436                    selections
28437                        .iter()
28438                        .zip(region.ranges().iter().map(|r| r.to_offset(buffer)))
28439                        .all(|(selection, invalidation_range)| {
28440                            let head = selection.head().to_offset(buffer);
28441                            invalidation_range.start <= head && invalidation_range.end >= head
28442                        })
28443                } else {
28444                    false
28445                };
28446
28447            if all_selections_inside_invalidation_ranges {
28448                break;
28449            } else {
28450                self.pop();
28451            }
28452        }
28453    }
28454}
28455
28456#[derive(Clone)]
28457struct ErasedEditorImpl(Entity<Editor>);
28458
28459impl ui_input::ErasedEditor for ErasedEditorImpl {
28460    fn text(&self, cx: &App) -> String {
28461        self.0.read(cx).text(cx)
28462    }
28463
28464    fn set_text(&self, text: &str, window: &mut Window, cx: &mut App) {
28465        self.0.update(cx, |this, cx| {
28466            this.set_text(text, window, cx);
28467        })
28468    }
28469
28470    fn clear(&self, window: &mut Window, cx: &mut App) {
28471        self.0.update(cx, |this, cx| this.clear(window, cx));
28472    }
28473
28474    fn set_placeholder_text(&self, text: &str, window: &mut Window, cx: &mut App) {
28475        self.0.update(cx, |this, cx| {
28476            this.set_placeholder_text(text, window, cx);
28477        });
28478    }
28479
28480    fn focus_handle(&self, cx: &App) -> FocusHandle {
28481        self.0.read(cx).focus_handle(cx)
28482    }
28483
28484    fn render(&self, _: &mut Window, cx: &App) -> AnyElement {
28485        let settings = ThemeSettings::get_global(cx);
28486        let theme_color = cx.theme().colors();
28487
28488        let text_style = TextStyle {
28489            font_family: settings.ui_font.family.clone(),
28490            font_features: settings.ui_font.features.clone(),
28491            font_size: rems(0.875).into(),
28492            font_weight: settings.ui_font.weight,
28493            font_style: FontStyle::Normal,
28494            line_height: relative(1.2),
28495            color: theme_color.text,
28496            ..Default::default()
28497        };
28498        let editor_style = EditorStyle {
28499            background: theme_color.ghost_element_background,
28500            local_player: cx.theme().players().local(),
28501            syntax: cx.theme().syntax().clone(),
28502            text: text_style,
28503            ..Default::default()
28504        };
28505        EditorElement::new(&self.0, editor_style).into_any()
28506    }
28507
28508    fn as_any(&self) -> &dyn Any {
28509        &self.0
28510    }
28511
28512    fn move_selection_to_end(&self, window: &mut Window, cx: &mut App) {
28513        self.0.update(cx, |editor, cx| {
28514            let editor_offset = editor.buffer().read(cx).len(cx);
28515            editor.change_selections(
28516                SelectionEffects::scroll(Autoscroll::Next),
28517                window,
28518                cx,
28519                |s| s.select_ranges(Some(editor_offset..editor_offset)),
28520            );
28521        });
28522    }
28523
28524    fn subscribe(
28525        &self,
28526        mut callback: Box<dyn FnMut(ui_input::ErasedEditorEvent, &mut Window, &mut App) + 'static>,
28527        window: &mut Window,
28528        cx: &mut App,
28529    ) -> Subscription {
28530        window.subscribe(&self.0, cx, move |_, event: &EditorEvent, window, cx| {
28531            let event = match event {
28532                EditorEvent::BufferEdited => ui_input::ErasedEditorEvent::BufferEdited,
28533                EditorEvent::Blurred => ui_input::ErasedEditorEvent::Blurred,
28534                _ => return,
28535            };
28536            (callback)(event, window, cx);
28537        })
28538    }
28539
28540    fn set_masked(&self, masked: bool, _window: &mut Window, cx: &mut App) {
28541        self.0.update(cx, |editor, cx| {
28542            editor.set_masked(masked, cx);
28543        });
28544    }
28545}
28546impl<T> Default for InvalidationStack<T> {
28547    fn default() -> Self {
28548        Self(Default::default())
28549    }
28550}
28551
28552impl<T> Deref for InvalidationStack<T> {
28553    type Target = Vec<T>;
28554
28555    fn deref(&self) -> &Self::Target {
28556        &self.0
28557    }
28558}
28559
28560impl<T> DerefMut for InvalidationStack<T> {
28561    fn deref_mut(&mut self) -> &mut Self::Target {
28562        &mut self.0
28563    }
28564}
28565
28566impl InvalidationRegion for SnippetState {
28567    fn ranges(&self) -> &[Range<Anchor>] {
28568        &self.ranges[self.active_index]
28569    }
28570}
28571
28572fn edit_prediction_edit_text(
28573    current_snapshot: &BufferSnapshot,
28574    edits: &[(Range<Anchor>, impl AsRef<str>)],
28575    edit_preview: &EditPreview,
28576    include_deletions: bool,
28577    cx: &App,
28578) -> HighlightedText {
28579    let edits = edits
28580        .iter()
28581        .map(|(anchor, text)| (anchor.start.text_anchor..anchor.end.text_anchor, text))
28582        .collect::<Vec<_>>();
28583
28584    edit_preview.highlight_edits(current_snapshot, &edits, include_deletions, cx)
28585}
28586
28587fn edit_prediction_fallback_text(edits: &[(Range<Anchor>, Arc<str>)], cx: &App) -> HighlightedText {
28588    // Fallback for providers that don't provide edit_preview (like Copilot)
28589    // Just show the raw edit text with basic styling
28590    let mut text = String::new();
28591    let mut highlights = Vec::new();
28592
28593    let insertion_highlight_style = HighlightStyle {
28594        color: Some(cx.theme().colors().text),
28595        ..Default::default()
28596    };
28597
28598    for (_, edit_text) in edits {
28599        let start_offset = text.len();
28600        text.push_str(edit_text);
28601        let end_offset = text.len();
28602
28603        if start_offset < end_offset {
28604            highlights.push((start_offset..end_offset, insertion_highlight_style));
28605        }
28606    }
28607
28608    HighlightedText {
28609        text: text.into(),
28610        highlights,
28611    }
28612}
28613
28614pub fn diagnostic_style(severity: lsp::DiagnosticSeverity, colors: &StatusColors) -> Hsla {
28615    match severity {
28616        lsp::DiagnosticSeverity::ERROR => colors.error,
28617        lsp::DiagnosticSeverity::WARNING => colors.warning,
28618        lsp::DiagnosticSeverity::INFORMATION => colors.info,
28619        lsp::DiagnosticSeverity::HINT => colors.info,
28620        _ => colors.ignored,
28621    }
28622}
28623
28624pub fn styled_runs_for_code_label<'a>(
28625    label: &'a CodeLabel,
28626    syntax_theme: &'a theme::SyntaxTheme,
28627    local_player: &'a theme::PlayerColor,
28628) -> impl 'a + Iterator<Item = (Range<usize>, HighlightStyle)> {
28629    let fade_out = HighlightStyle {
28630        fade_out: Some(0.35),
28631        ..Default::default()
28632    };
28633
28634    let mut prev_end = label.filter_range.end;
28635    label
28636        .runs
28637        .iter()
28638        .enumerate()
28639        .flat_map(move |(ix, (range, highlight_id))| {
28640            let style = if *highlight_id == language::HighlightId::TABSTOP_INSERT_ID {
28641                HighlightStyle {
28642                    color: Some(local_player.cursor),
28643                    ..Default::default()
28644                }
28645            } else if *highlight_id == language::HighlightId::TABSTOP_REPLACE_ID {
28646                HighlightStyle {
28647                    background_color: Some(local_player.selection),
28648                    ..Default::default()
28649                }
28650            } else if let Some(style) = highlight_id.style(syntax_theme) {
28651                style
28652            } else {
28653                return Default::default();
28654            };
28655            let muted_style = style.highlight(fade_out);
28656
28657            let mut runs = SmallVec::<[(Range<usize>, HighlightStyle); 3]>::new();
28658            if range.start >= label.filter_range.end {
28659                if range.start > prev_end {
28660                    runs.push((prev_end..range.start, fade_out));
28661                }
28662                runs.push((range.clone(), muted_style));
28663            } else if range.end <= label.filter_range.end {
28664                runs.push((range.clone(), style));
28665            } else {
28666                runs.push((range.start..label.filter_range.end, style));
28667                runs.push((label.filter_range.end..range.end, muted_style));
28668            }
28669            prev_end = cmp::max(prev_end, range.end);
28670
28671            if ix + 1 == label.runs.len() && label.text.len() > prev_end {
28672                runs.push((prev_end..label.text.len(), fade_out));
28673            }
28674
28675            runs
28676        })
28677}
28678
28679pub(crate) fn split_words(text: &str) -> impl std::iter::Iterator<Item = &str> + '_ {
28680    let mut prev_index = 0;
28681    let mut prev_codepoint: Option<char> = None;
28682    text.char_indices()
28683        .chain([(text.len(), '\0')])
28684        .filter_map(move |(index, codepoint)| {
28685            let prev_codepoint = prev_codepoint.replace(codepoint)?;
28686            let is_boundary = index == text.len()
28687                || !prev_codepoint.is_uppercase() && codepoint.is_uppercase()
28688                || !prev_codepoint.is_alphanumeric() && codepoint.is_alphanumeric();
28689            if is_boundary {
28690                let chunk = &text[prev_index..index];
28691                prev_index = index;
28692                Some(chunk)
28693            } else {
28694                None
28695            }
28696        })
28697}
28698
28699/// Given a string of text immediately before the cursor, iterates over possible
28700/// strings a snippet could match to. More precisely: returns an iterator over
28701/// suffixes of `text` created by splitting at word boundaries (before & after
28702/// every non-word character).
28703///
28704/// Shorter suffixes are returned first.
28705pub(crate) fn snippet_candidate_suffixes<'a>(
28706    text: &'a str,
28707    is_word_char: &'a dyn Fn(char) -> bool,
28708) -> impl std::iter::Iterator<Item = &'a str> + 'a {
28709    let mut prev_index = text.len();
28710    let mut prev_codepoint = None;
28711    text.char_indices()
28712        .rev()
28713        .chain([(0, '\0')])
28714        .filter_map(move |(index, codepoint)| {
28715            let prev_index = std::mem::replace(&mut prev_index, index);
28716            let prev_codepoint = prev_codepoint.replace(codepoint)?;
28717            if is_word_char(prev_codepoint) && is_word_char(codepoint) {
28718                None
28719            } else {
28720                let chunk = &text[prev_index..]; // go to end of string
28721                Some(chunk)
28722            }
28723        })
28724}
28725
28726pub trait RangeToAnchorExt: Sized {
28727    fn to_anchors(self, snapshot: &MultiBufferSnapshot) -> Range<Anchor>;
28728
28729    fn to_display_points(self, snapshot: &EditorSnapshot) -> Range<DisplayPoint> {
28730        let anchor_range = self.to_anchors(&snapshot.buffer_snapshot());
28731        anchor_range.start.to_display_point(snapshot)..anchor_range.end.to_display_point(snapshot)
28732    }
28733}
28734
28735impl<T: ToOffset> RangeToAnchorExt for Range<T> {
28736    fn to_anchors(self, snapshot: &MultiBufferSnapshot) -> Range<Anchor> {
28737        let start_offset = self.start.to_offset(snapshot);
28738        let end_offset = self.end.to_offset(snapshot);
28739        if start_offset == end_offset {
28740            snapshot.anchor_before(start_offset)..snapshot.anchor_before(end_offset)
28741        } else {
28742            snapshot.anchor_after(self.start)..snapshot.anchor_before(self.end)
28743        }
28744    }
28745}
28746
28747pub trait RowExt {
28748    fn as_f64(&self) -> f64;
28749
28750    fn next_row(&self) -> Self;
28751
28752    fn previous_row(&self) -> Self;
28753
28754    fn minus(&self, other: Self) -> u32;
28755}
28756
28757impl RowExt for DisplayRow {
28758    fn as_f64(&self) -> f64 {
28759        self.0 as _
28760    }
28761
28762    fn next_row(&self) -> Self {
28763        Self(self.0 + 1)
28764    }
28765
28766    fn previous_row(&self) -> Self {
28767        Self(self.0.saturating_sub(1))
28768    }
28769
28770    fn minus(&self, other: Self) -> u32 {
28771        self.0 - other.0
28772    }
28773}
28774
28775impl RowExt for MultiBufferRow {
28776    fn as_f64(&self) -> f64 {
28777        self.0 as _
28778    }
28779
28780    fn next_row(&self) -> Self {
28781        Self(self.0 + 1)
28782    }
28783
28784    fn previous_row(&self) -> Self {
28785        Self(self.0.saturating_sub(1))
28786    }
28787
28788    fn minus(&self, other: Self) -> u32 {
28789        self.0 - other.0
28790    }
28791}
28792
28793trait RowRangeExt {
28794    type Row;
28795
28796    fn len(&self) -> usize;
28797
28798    fn iter_rows(&self) -> impl DoubleEndedIterator<Item = Self::Row>;
28799}
28800
28801impl RowRangeExt for Range<MultiBufferRow> {
28802    type Row = MultiBufferRow;
28803
28804    fn len(&self) -> usize {
28805        (self.end.0 - self.start.0) as usize
28806    }
28807
28808    fn iter_rows(&self) -> impl DoubleEndedIterator<Item = MultiBufferRow> {
28809        (self.start.0..self.end.0).map(MultiBufferRow)
28810    }
28811}
28812
28813impl RowRangeExt for Range<DisplayRow> {
28814    type Row = DisplayRow;
28815
28816    fn len(&self) -> usize {
28817        (self.end.0 - self.start.0) as usize
28818    }
28819
28820    fn iter_rows(&self) -> impl DoubleEndedIterator<Item = DisplayRow> {
28821        (self.start.0..self.end.0).map(DisplayRow)
28822    }
28823}
28824
28825/// If select range has more than one line, we
28826/// just point the cursor to range.start.
28827fn collapse_multiline_range(range: Range<Point>) -> Range<Point> {
28828    if range.start.row == range.end.row {
28829        range
28830    } else {
28831        range.start..range.start
28832    }
28833}
28834pub struct KillRing(ClipboardItem);
28835impl Global for KillRing {}
28836
28837const UPDATE_DEBOUNCE: Duration = Duration::from_millis(50);
28838
28839enum BreakpointPromptEditAction {
28840    Log,
28841    Condition,
28842    HitCondition,
28843}
28844
28845struct BreakpointPromptEditor {
28846    pub(crate) prompt: Entity<Editor>,
28847    editor: WeakEntity<Editor>,
28848    breakpoint_anchor: Anchor,
28849    breakpoint: Breakpoint,
28850    edit_action: BreakpointPromptEditAction,
28851    block_ids: HashSet<CustomBlockId>,
28852    editor_margins: Arc<Mutex<EditorMargins>>,
28853    _subscriptions: Vec<Subscription>,
28854}
28855
28856impl BreakpointPromptEditor {
28857    const MAX_LINES: u8 = 4;
28858
28859    fn new(
28860        editor: WeakEntity<Editor>,
28861        breakpoint_anchor: Anchor,
28862        breakpoint: Breakpoint,
28863        edit_action: BreakpointPromptEditAction,
28864        window: &mut Window,
28865        cx: &mut Context<Self>,
28866    ) -> Self {
28867        let base_text = match edit_action {
28868            BreakpointPromptEditAction::Log => breakpoint.message.as_ref(),
28869            BreakpointPromptEditAction::Condition => breakpoint.condition.as_ref(),
28870            BreakpointPromptEditAction::HitCondition => breakpoint.hit_condition.as_ref(),
28871        }
28872        .map(|msg| msg.to_string())
28873        .unwrap_or_default();
28874
28875        let buffer = cx.new(|cx| Buffer::local(base_text, cx));
28876        let buffer = cx.new(|cx| MultiBuffer::singleton(buffer, cx));
28877
28878        let prompt = cx.new(|cx| {
28879            let mut prompt = Editor::new(
28880                EditorMode::AutoHeight {
28881                    min_lines: 1,
28882                    max_lines: Some(Self::MAX_LINES as usize),
28883                },
28884                buffer,
28885                None,
28886                window,
28887                cx,
28888            );
28889            prompt.set_soft_wrap_mode(language::language_settings::SoftWrap::EditorWidth, cx);
28890            prompt.set_show_cursor_when_unfocused(false, cx);
28891            prompt.set_placeholder_text(
28892                match edit_action {
28893                    BreakpointPromptEditAction::Log => "Message to log when a breakpoint is hit. Expressions within {} are interpolated.",
28894                    BreakpointPromptEditAction::Condition => "Condition when a breakpoint is hit. Expressions within {} are interpolated.",
28895                    BreakpointPromptEditAction::HitCondition => "How many breakpoint hits to ignore",
28896                },
28897                window,
28898                cx,
28899            );
28900
28901            prompt
28902        });
28903
28904        Self {
28905            prompt,
28906            editor,
28907            breakpoint_anchor,
28908            breakpoint,
28909            edit_action,
28910            editor_margins: Arc::new(Mutex::new(EditorMargins::default())),
28911            block_ids: Default::default(),
28912            _subscriptions: vec![],
28913        }
28914    }
28915
28916    pub(crate) fn add_block_ids(&mut self, block_ids: Vec<CustomBlockId>) {
28917        self.block_ids.extend(block_ids)
28918    }
28919
28920    fn confirm(&mut self, _: &menu::Confirm, window: &mut Window, cx: &mut Context<Self>) {
28921        if let Some(editor) = self.editor.upgrade() {
28922            let message = self
28923                .prompt
28924                .read(cx)
28925                .buffer
28926                .read(cx)
28927                .as_singleton()
28928                .expect("A multi buffer in breakpoint prompt isn't possible")
28929                .read(cx)
28930                .as_rope()
28931                .to_string();
28932
28933            editor.update(cx, |editor, cx| {
28934                editor.edit_breakpoint_at_anchor(
28935                    self.breakpoint_anchor,
28936                    self.breakpoint.clone(),
28937                    match self.edit_action {
28938                        BreakpointPromptEditAction::Log => {
28939                            BreakpointEditAction::EditLogMessage(message.into())
28940                        }
28941                        BreakpointPromptEditAction::Condition => {
28942                            BreakpointEditAction::EditCondition(message.into())
28943                        }
28944                        BreakpointPromptEditAction::HitCondition => {
28945                            BreakpointEditAction::EditHitCondition(message.into())
28946                        }
28947                    },
28948                    cx,
28949                );
28950
28951                editor.remove_blocks(self.block_ids.clone(), None, cx);
28952                cx.focus_self(window);
28953            });
28954        }
28955    }
28956
28957    fn cancel(&mut self, _: &menu::Cancel, window: &mut Window, cx: &mut Context<Self>) {
28958        self.editor
28959            .update(cx, |editor, cx| {
28960                editor.remove_blocks(self.block_ids.clone(), None, cx);
28961                window.focus(&editor.focus_handle, cx);
28962            })
28963            .log_err();
28964    }
28965
28966    fn render_prompt_editor(&self, cx: &mut Context<Self>) -> impl IntoElement {
28967        let settings = ThemeSettings::get_global(cx);
28968        let text_style = TextStyle {
28969            color: if self.prompt.read(cx).read_only(cx) {
28970                cx.theme().colors().text_disabled
28971            } else {
28972                cx.theme().colors().text
28973            },
28974            font_family: settings.buffer_font.family.clone(),
28975            font_fallbacks: settings.buffer_font.fallbacks.clone(),
28976            font_size: settings.buffer_font_size(cx).into(),
28977            font_weight: settings.buffer_font.weight,
28978            line_height: relative(settings.buffer_line_height.value()),
28979            ..Default::default()
28980        };
28981        EditorElement::new(
28982            &self.prompt,
28983            EditorStyle {
28984                background: cx.theme().colors().editor_background,
28985                local_player: cx.theme().players().local(),
28986                text: text_style,
28987                ..Default::default()
28988            },
28989        )
28990    }
28991}
28992
28993impl Render for BreakpointPromptEditor {
28994    fn render(&mut self, window: &mut Window, cx: &mut Context<Self>) -> impl IntoElement {
28995        let editor_margins = *self.editor_margins.lock();
28996        let gutter_dimensions = editor_margins.gutter;
28997        h_flex()
28998            .key_context("Editor")
28999            .bg(cx.theme().colors().editor_background)
29000            .border_y_1()
29001            .border_color(cx.theme().status().info_border)
29002            .size_full()
29003            .py(window.line_height() / 2.5)
29004            .on_action(cx.listener(Self::confirm))
29005            .on_action(cx.listener(Self::cancel))
29006            .child(h_flex().w(gutter_dimensions.full_width() + (gutter_dimensions.margin / 2.0)))
29007            .child(div().flex_1().child(self.render_prompt_editor(cx)))
29008    }
29009}
29010
29011impl Focusable for BreakpointPromptEditor {
29012    fn focus_handle(&self, cx: &App) -> FocusHandle {
29013        self.prompt.focus_handle(cx)
29014    }
29015}
29016
29017fn all_edits_insertions_or_deletions(
29018    edits: &Vec<(Range<Anchor>, Arc<str>)>,
29019    snapshot: &MultiBufferSnapshot,
29020) -> bool {
29021    let mut all_insertions = true;
29022    let mut all_deletions = true;
29023
29024    for (range, new_text) in edits.iter() {
29025        let range_is_empty = range.to_offset(snapshot).is_empty();
29026        let text_is_empty = new_text.is_empty();
29027
29028        if range_is_empty != text_is_empty {
29029            if range_is_empty {
29030                all_deletions = false;
29031            } else {
29032                all_insertions = false;
29033            }
29034        } else {
29035            return false;
29036        }
29037
29038        if !all_insertions && !all_deletions {
29039            return false;
29040        }
29041    }
29042    all_insertions || all_deletions
29043}
29044
29045struct MissingEditPredictionKeybindingTooltip;
29046
29047impl Render for MissingEditPredictionKeybindingTooltip {
29048    fn render(&mut self, _: &mut Window, cx: &mut Context<Self>) -> impl IntoElement {
29049        ui::tooltip_container(cx, |container, cx| {
29050            container
29051                .flex_shrink_0()
29052                .max_w_80()
29053                .min_h(rems_from_px(124.))
29054                .justify_between()
29055                .child(
29056                    v_flex()
29057                        .flex_1()
29058                        .text_ui_sm(cx)
29059                        .child(Label::new("Conflict with Accept Keybinding"))
29060                        .child("Your keymap currently overrides the default accept keybinding. To continue, assign one keybinding for the `editor::AcceptEditPrediction` action.")
29061                )
29062                .child(
29063                    h_flex()
29064                        .pb_1()
29065                        .gap_1()
29066                        .items_end()
29067                        .w_full()
29068                        .child(Button::new("open-keymap", "Assign Keybinding").size(ButtonSize::Compact).on_click(|_ev, window, cx| {
29069                            window.dispatch_action(zed_actions::OpenKeymapFile.boxed_clone(), cx)
29070                        }))
29071                        .child(Button::new("see-docs", "See Docs").size(ButtonSize::Compact).on_click(|_ev, _window, cx| {
29072                            cx.open_url("https://zed.dev/docs/completions#edit-predictions-missing-keybinding");
29073                        })),
29074                )
29075        })
29076    }
29077}
29078
29079#[derive(Debug, Clone, Copy, PartialEq)]
29080pub struct LineHighlight {
29081    pub background: Background,
29082    pub border: Option<gpui::Hsla>,
29083    pub include_gutter: bool,
29084    pub type_id: Option<TypeId>,
29085}
29086
29087struct LineManipulationResult {
29088    pub new_text: String,
29089    pub line_count_before: usize,
29090    pub line_count_after: usize,
29091}
29092
29093fn render_diff_hunk_controls(
29094    row: u32,
29095    status: &DiffHunkStatus,
29096    hunk_range: Range<Anchor>,
29097    is_created_file: bool,
29098    line_height: Pixels,
29099    editor: &Entity<Editor>,
29100    _window: &mut Window,
29101    cx: &mut App,
29102) -> AnyElement {
29103    h_flex()
29104        .h(line_height)
29105        .mr_1()
29106        .gap_1()
29107        .px_0p5()
29108        .pb_1()
29109        .border_x_1()
29110        .border_b_1()
29111        .border_color(cx.theme().colors().border_variant)
29112        .rounded_b_lg()
29113        .bg(cx.theme().colors().editor_background)
29114        .gap_1()
29115        .block_mouse_except_scroll()
29116        .shadow_md()
29117        .child(if status.has_secondary_hunk() {
29118            Button::new(("stage", row as u64), "Stage")
29119                .alpha(if status.is_pending() { 0.66 } else { 1.0 })
29120                .tooltip({
29121                    let focus_handle = editor.focus_handle(cx);
29122                    move |_window, cx| {
29123                        Tooltip::for_action_in(
29124                            "Stage Hunk",
29125                            &::git::ToggleStaged,
29126                            &focus_handle,
29127                            cx,
29128                        )
29129                    }
29130                })
29131                .on_click({
29132                    let editor = editor.clone();
29133                    move |_event, _window, cx| {
29134                        editor.update(cx, |editor, cx| {
29135                            editor.stage_or_unstage_diff_hunks(
29136                                true,
29137                                vec![hunk_range.start..hunk_range.start],
29138                                cx,
29139                            );
29140                        });
29141                    }
29142                })
29143        } else {
29144            Button::new(("unstage", row as u64), "Unstage")
29145                .alpha(if status.is_pending() { 0.66 } else { 1.0 })
29146                .tooltip({
29147                    let focus_handle = editor.focus_handle(cx);
29148                    move |_window, cx| {
29149                        Tooltip::for_action_in(
29150                            "Unstage Hunk",
29151                            &::git::ToggleStaged,
29152                            &focus_handle,
29153                            cx,
29154                        )
29155                    }
29156                })
29157                .on_click({
29158                    let editor = editor.clone();
29159                    move |_event, _window, cx| {
29160                        editor.update(cx, |editor, cx| {
29161                            editor.stage_or_unstage_diff_hunks(
29162                                false,
29163                                vec![hunk_range.start..hunk_range.start],
29164                                cx,
29165                            );
29166                        });
29167                    }
29168                })
29169        })
29170        .child(
29171            Button::new(("restore", row as u64), "Restore")
29172                .tooltip({
29173                    let focus_handle = editor.focus_handle(cx);
29174                    move |_window, cx| {
29175                        Tooltip::for_action_in("Restore Hunk", &::git::Restore, &focus_handle, cx)
29176                    }
29177                })
29178                .on_click({
29179                    let editor = editor.clone();
29180                    move |_event, window, cx| {
29181                        editor.update(cx, |editor, cx| {
29182                            let snapshot = editor.snapshot(window, cx);
29183                            let point = hunk_range.start.to_point(&snapshot.buffer_snapshot());
29184                            editor.restore_hunks_in_ranges(vec![point..point], window, cx);
29185                        });
29186                    }
29187                })
29188                .disabled(is_created_file),
29189        )
29190        .when(
29191            !editor.read(cx).buffer().read(cx).all_diff_hunks_expanded(),
29192            |el| {
29193                el.child(
29194                    IconButton::new(("next-hunk", row as u64), IconName::ArrowDown)
29195                        .shape(IconButtonShape::Square)
29196                        .icon_size(IconSize::Small)
29197                        // .disabled(!has_multiple_hunks)
29198                        .tooltip({
29199                            let focus_handle = editor.focus_handle(cx);
29200                            move |_window, cx| {
29201                                Tooltip::for_action_in("Next Hunk", &GoToHunk, &focus_handle, cx)
29202                            }
29203                        })
29204                        .on_click({
29205                            let editor = editor.clone();
29206                            move |_event, window, cx| {
29207                                editor.update(cx, |editor, cx| {
29208                                    let snapshot = editor.snapshot(window, cx);
29209                                    let position =
29210                                        hunk_range.end.to_point(&snapshot.buffer_snapshot());
29211                                    editor.go_to_hunk_before_or_after_position(
29212                                        &snapshot,
29213                                        position,
29214                                        Direction::Next,
29215                                        window,
29216                                        cx,
29217                                    );
29218                                    editor.expand_selected_diff_hunks(cx);
29219                                });
29220                            }
29221                        }),
29222                )
29223                .child(
29224                    IconButton::new(("prev-hunk", row as u64), IconName::ArrowUp)
29225                        .shape(IconButtonShape::Square)
29226                        .icon_size(IconSize::Small)
29227                        // .disabled(!has_multiple_hunks)
29228                        .tooltip({
29229                            let focus_handle = editor.focus_handle(cx);
29230                            move |_window, cx| {
29231                                Tooltip::for_action_in(
29232                                    "Previous Hunk",
29233                                    &GoToPreviousHunk,
29234                                    &focus_handle,
29235                                    cx,
29236                                )
29237                            }
29238                        })
29239                        .on_click({
29240                            let editor = editor.clone();
29241                            move |_event, window, cx| {
29242                                editor.update(cx, |editor, cx| {
29243                                    let snapshot = editor.snapshot(window, cx);
29244                                    let point =
29245                                        hunk_range.start.to_point(&snapshot.buffer_snapshot());
29246                                    editor.go_to_hunk_before_or_after_position(
29247                                        &snapshot,
29248                                        point,
29249                                        Direction::Prev,
29250                                        window,
29251                                        cx,
29252                                    );
29253                                    editor.expand_selected_diff_hunks(cx);
29254                                });
29255                            }
29256                        }),
29257                )
29258            },
29259        )
29260        .into_any_element()
29261}
29262
29263pub fn multibuffer_context_lines(cx: &App) -> u32 {
29264    EditorSettings::try_get(cx)
29265        .map(|settings| settings.excerpt_context_lines)
29266        .unwrap_or(2)
29267        .min(32)
29268}