editor.rs

    1#![allow(rustdoc::private_intra_doc_links)]
    2//! This is the place where everything editor-related is stored (data-wise) and displayed (ui-wise).
    3//! The main point of interest in this crate is [`Editor`] type, which is used in every other Zed part as a user input element.
    4//! It comes in different flavors: single line, multiline and a fixed height one.
    5//!
    6//! Editor contains of multiple large submodules:
    7//! * [`element`] — the place where all rendering happens
    8//! * [`display_map`] - chunks up text in the editor into the logical blocks, establishes coordinates and mapping between each of them.
    9//!   Contains all metadata related to text transformations (folds, fake inlay text insertions, soft wraps, tab markup, etc.).
   10//!
   11//! All other submodules and structs are mostly concerned with holding editor data about the way it displays current buffer region(s).
   12//!
   13//! If you're looking to improve Vim mode, you should check out Vim crate that wraps Editor and overrides its behavior.
   14pub mod actions;
   15pub mod blink_manager;
   16mod bracket_colorization;
   17mod clangd_ext;
   18pub mod code_context_menus;
   19pub mod display_map;
   20mod document_colors;
   21mod document_symbols;
   22mod editor_settings;
   23mod element;
   24mod folding_ranges;
   25mod git;
   26mod highlight_matching_bracket;
   27mod hover_links;
   28pub mod hover_popover;
   29mod indent_guides;
   30mod inlays;
   31pub mod items;
   32mod jsx_tag_auto_close;
   33mod linked_editing_ranges;
   34mod lsp_ext;
   35mod mouse_context_menu;
   36pub mod movement;
   37mod persistence;
   38mod runnables;
   39mod rust_analyzer_ext;
   40pub mod scroll;
   41mod selections_collection;
   42pub mod semantic_tokens;
   43mod split;
   44pub mod split_editor_view;
   45
   46#[cfg(test)]
   47mod code_completion_tests;
   48#[cfg(test)]
   49mod edit_prediction_tests;
   50#[cfg(test)]
   51mod editor_tests;
   52mod signature_help;
   53#[cfg(any(test, feature = "test-support"))]
   54pub mod test;
   55
   56pub(crate) use actions::*;
   57pub use display_map::{
   58    ChunkRenderer, ChunkRendererContext, DisplayPoint, FoldPlaceholder, HighlightKey,
   59    SemanticTokenHighlight,
   60};
   61pub use edit_prediction_types::Direction;
   62pub use editor_settings::{
   63    CompletionDetailAlignment, CurrentLineHighlight, DiffViewStyle, DocumentColorsRenderMode,
   64    EditorSettings, HideMouseMode, ScrollBeyondLastLine, ScrollbarAxes, SearchSettings,
   65    ShowMinimap,
   66};
   67pub use element::{
   68    CursorLayout, EditorElement, HighlightedRange, HighlightedRangeLine, PointForPosition,
   69    render_breadcrumb_text,
   70};
   71pub use git::blame::BlameRenderer;
   72pub use hover_popover::hover_markdown_style;
   73pub use inlays::Inlay;
   74pub use items::MAX_TAB_TITLE_LEN;
   75pub use linked_editing_ranges::LinkedEdits;
   76pub use lsp::CompletionContext;
   77pub use lsp_ext::lsp_tasks;
   78pub use multi_buffer::{
   79    Anchor, AnchorRangeExt, BufferOffset, ExcerptId, ExcerptRange, MBTextSummary, MultiBuffer,
   80    MultiBufferOffset, MultiBufferOffsetUtf16, MultiBufferSnapshot, PathKey, RowInfo, ToOffset,
   81    ToPoint,
   82};
   83pub use split::{SplittableEditor, ToggleSplitDiff};
   84pub use split_editor_view::SplitEditorView;
   85pub use text::Bias;
   86
   87use ::git::{Restore, blame::BlameEntry, commit::ParsedCommitMessage, status::FileStatus};
   88use aho_corasick::{AhoCorasick, AhoCorasickBuilder, BuildError};
   89use anyhow::{Context as _, Result, anyhow, bail};
   90use blink_manager::BlinkManager;
   91use buffer_diff::DiffHunkStatus;
   92use client::{Collaborator, ParticipantIndex, parse_zed_link};
   93use clock::ReplicaId;
   94use code_context_menus::{
   95    AvailableCodeAction, CodeActionContents, CodeActionsItem, CodeActionsMenu, CodeContextMenu,
   96    CompletionsMenu, ContextMenuOrigin,
   97};
   98use collections::{BTreeMap, HashMap, HashSet, VecDeque};
   99use convert_case::{Case, Casing};
  100use dap::TelemetrySpawnLocation;
  101use display_map::*;
  102use document_colors::LspColorData;
  103use edit_prediction_types::{
  104    EditPredictionDelegate, EditPredictionDelegateHandle, EditPredictionDiscardReason,
  105    EditPredictionGranularity, SuggestionDisplayType,
  106};
  107use editor_settings::{GoToDefinitionFallback, Minimap as MinimapSettings};
  108use element::{LineWithInvisibles, PositionMap, layout_line};
  109use futures::{
  110    FutureExt,
  111    future::{self, Shared, join},
  112};
  113use fuzzy::{StringMatch, StringMatchCandidate};
  114use git::blame::{GitBlame, GlobalBlameRenderer};
  115use gpui::{
  116    Action, Animation, AnimationExt, AnyElement, App, AppContext, AsyncWindowContext,
  117    AvailableSpace, Background, Bounds, ClickEvent, ClipboardEntry, ClipboardItem, Context,
  118    DispatchPhase, Edges, Entity, EntityId, EntityInputHandler, EventEmitter, FocusHandle,
  119    FocusOutEvent, Focusable, FontId, FontStyle, FontWeight, Global, HighlightStyle, Hsla,
  120    KeyContext, Modifiers, MouseButton, MouseDownEvent, MouseMoveEvent, PaintQuad, ParentElement,
  121    Pixels, PressureStage, Render, ScrollHandle, SharedString, SharedUri, Size, Stateful, Styled,
  122    Subscription, Task, TextRun, TextStyle, TextStyleRefinement, UTF16Selection, UnderlineStyle,
  123    UniformListScrollHandle, WeakEntity, WeakFocusHandle, Window, div, point, prelude::*,
  124    pulsating_between, px, relative, size,
  125};
  126use hover_links::{HoverLink, HoveredLinkState, find_file};
  127use hover_popover::{HoverState, hide_hover};
  128use indent_guides::ActiveIndentGuidesState;
  129use inlays::{InlaySplice, inlay_hints::InlayHintRefreshReason};
  130use itertools::{Either, Itertools};
  131use language::{
  132    AutoindentMode, BlockCommentConfig, BracketMatch, BracketPair, Buffer, BufferRow,
  133    BufferSnapshot, Capability, CharClassifier, CharKind, CharScopeContext, CodeLabel, CursorShape,
  134    DiagnosticEntryRef, DiffOptions, EditPredictionsMode, EditPreview, HighlightedText, IndentKind,
  135    IndentSize, Language, LanguageName, LanguageRegistry, LanguageScope, LocalFile, OffsetRangeExt,
  136    OutlineItem, Point, Selection, SelectionGoal, TextObject, TransactionId, TreeSitterOptions,
  137    WordsQuery,
  138    language_settings::{
  139        self, AllLanguageSettings, LanguageSettings, LspInsertMode, RewrapBehavior,
  140        WordsCompletionMode, all_language_settings,
  141    },
  142    point_from_lsp, point_to_lsp, text_diff_with_options,
  143};
  144use linked_editing_ranges::refresh_linked_ranges;
  145use lsp::{
  146    CodeActionKind, CompletionItemKind, CompletionTriggerKind, InsertTextFormat, InsertTextMode,
  147    LanguageServerId,
  148};
  149use markdown::Markdown;
  150use mouse_context_menu::MouseContextMenu;
  151use movement::TextLayoutDetails;
  152use multi_buffer::{
  153    ExcerptInfo, ExpandExcerptDirection, MultiBufferDiffHunk, MultiBufferPoint, MultiBufferRow,
  154};
  155use parking_lot::Mutex;
  156use persistence::EditorDb;
  157use project::{
  158    BreakpointWithPosition, CodeAction, Completion, CompletionDisplayOptions, CompletionIntent,
  159    CompletionResponse, CompletionSource, DisableAiSettings, DocumentHighlight, InlayHint, InlayId,
  160    InvalidationStrategy, Location, LocationLink, LspAction, PrepareRenameResponse, Project,
  161    ProjectItem, ProjectPath, ProjectTransaction,
  162    debugger::{
  163        breakpoint_store::{
  164            Breakpoint, BreakpointEditAction, BreakpointSessionState, BreakpointState,
  165            BreakpointStore, BreakpointStoreEvent,
  166        },
  167        session::{Session, SessionEvent},
  168    },
  169    git_store::GitStoreEvent,
  170    lsp_store::{
  171        BufferSemanticTokens, CacheInlayHints, CompletionDocumentation, FormatTrigger,
  172        LspFormatTarget, OpenLspBufferHandle, RefreshForServer,
  173    },
  174    project_settings::{DiagnosticSeverity, GoToDiagnosticSeverityFilter, ProjectSettings},
  175};
  176use rand::seq::SliceRandom;
  177use regex::Regex;
  178use rpc::{ErrorCode, ErrorExt, proto::PeerId};
  179use scroll::{Autoscroll, OngoingScroll, ScrollAnchor, ScrollManager, SharedScrollAnchor};
  180use selections_collection::{MutableSelectionsCollection, SelectionsCollection};
  181use serde::{Deserialize, Serialize};
  182use settings::{
  183    GitGutterSetting, RelativeLineNumbers, Settings, SettingsLocation, SettingsStore,
  184    update_settings_file,
  185};
  186use smallvec::{SmallVec, smallvec};
  187use snippet::Snippet;
  188use std::{
  189    any::{Any, TypeId},
  190    borrow::Cow,
  191    cell::{OnceCell, RefCell},
  192    cmp::{self, Ordering, Reverse},
  193    collections::hash_map,
  194    iter::{self, Peekable},
  195    mem,
  196    num::NonZeroU32,
  197    ops::{ControlFlow, Deref, DerefMut, Not, Range, RangeInclusive},
  198    path::{Path, PathBuf},
  199    rc::Rc,
  200    sync::Arc,
  201    time::{Duration, Instant},
  202};
  203use task::TaskVariables;
  204use text::{BufferId, FromAnchor, OffsetUtf16, Rope, ToOffset as _, ToPoint as _};
  205use theme::{
  206    AccentColors, ActiveTheme, GlobalTheme, PlayerColor, StatusColors, SyntaxTheme, Theme,
  207};
  208use theme_settings::{ThemeSettings, observe_buffer_font_size_adjustment};
  209use ui::{
  210    Avatar, ButtonSize, ButtonStyle, ContextMenu, Disclosure, IconButton, IconButtonShape,
  211    IconName, IconSize, Indicator, Key, Tooltip, h_flex, prelude::*, scrollbars::ScrollbarAutoHide,
  212    utils::WithRemSize,
  213};
  214use ui_input::ErasedEditor;
  215use util::{RangeExt, ResultExt, TryFutureExt, maybe, post_inc};
  216use workspace::{
  217    CollaboratorId, Item as WorkspaceItem, ItemId, ItemNavHistory, NavigationEntry, OpenInTerminal,
  218    OpenTerminal, Pane, RestoreOnStartupBehavior, SERIALIZATION_THROTTLE_TIME, SplitDirection,
  219    TabBarSettings, Toast, ViewId, Workspace, WorkspaceId, WorkspaceSettings,
  220    item::{ItemBufferKind, ItemHandle, PreviewTabsSettings, SaveOptions},
  221    notifications::{DetachAndPromptErr, NotificationId, NotifyTaskExt},
  222    searchable::SearchEvent,
  223};
  224pub use zed_actions::editor::RevealInFileManager;
  225use zed_actions::editor::{MoveDown, MoveUp};
  226
  227use crate::{
  228    code_context_menus::CompletionsMenuSource,
  229    editor_settings::MultiCursorModifier,
  230    hover_links::{find_url, find_url_from_range},
  231    inlays::{
  232        InlineValueCache,
  233        inlay_hints::{LspInlayHintData, inlay_hint_settings},
  234    },
  235    runnables::{ResolvedTasks, RunnableData, RunnableTasks},
  236    scroll::{ScrollOffset, ScrollPixelOffset},
  237    selections_collection::resolve_selections_wrapping_blocks,
  238    semantic_tokens::SemanticTokenState,
  239    signature_help::{SignatureHelpHiddenBy, SignatureHelpState},
  240};
  241
  242pub const FILE_HEADER_HEIGHT: u32 = 2;
  243pub const BUFFER_HEADER_PADDING: Rems = rems(0.25);
  244pub const MULTI_BUFFER_EXCERPT_HEADER_HEIGHT: u32 = 1;
  245const CURSOR_BLINK_INTERVAL: Duration = Duration::from_millis(500);
  246const MAX_LINE_LEN: usize = 1024;
  247const MIN_NAVIGATION_HISTORY_ROW_DELTA: i64 = 10;
  248const MAX_SELECTION_HISTORY_LEN: usize = 1024;
  249pub(crate) const CURSORS_VISIBLE_FOR: Duration = Duration::from_millis(2000);
  250#[doc(hidden)]
  251pub const CODE_ACTIONS_DEBOUNCE_TIMEOUT: Duration = Duration::from_millis(250);
  252pub const SELECTION_HIGHLIGHT_DEBOUNCE_TIMEOUT: Duration = Duration::from_millis(100);
  253
  254pub(crate) const CODE_ACTION_TIMEOUT: Duration = Duration::from_secs(5);
  255pub(crate) const FORMAT_TIMEOUT: Duration = Duration::from_secs(5);
  256pub(crate) const SCROLL_CENTER_TOP_BOTTOM_DEBOUNCE_TIMEOUT: Duration = Duration::from_secs(1);
  257pub const LSP_REQUEST_DEBOUNCE_TIMEOUT: Duration = Duration::from_millis(50);
  258
  259pub(crate) const EDIT_PREDICTION_KEY_CONTEXT: &str = "edit_prediction";
  260pub(crate) const MINIMAP_FONT_SIZE: AbsoluteLength = AbsoluteLength::Pixels(px(2.));
  261
  262pub type RenderDiffHunkControlsFn = Arc<
  263    dyn Fn(
  264        u32,
  265        &DiffHunkStatus,
  266        Range<Anchor>,
  267        bool,
  268        Pixels,
  269        &Entity<Editor>,
  270        &mut Window,
  271        &mut App,
  272    ) -> AnyElement,
  273>;
  274
  275enum ReportEditorEvent {
  276    Saved { auto_saved: bool },
  277    EditorOpened,
  278    Closed,
  279}
  280
  281impl ReportEditorEvent {
  282    pub fn event_type(&self) -> &'static str {
  283        match self {
  284            Self::Saved { .. } => "Editor Saved",
  285            Self::EditorOpened => "Editor Opened",
  286            Self::Closed => "Editor Closed",
  287        }
  288    }
  289}
  290
  291pub enum ActiveDebugLine {}
  292pub enum DebugStackFrameLine {}
  293
  294pub enum ConflictsOuter {}
  295pub enum ConflictsOurs {}
  296pub enum ConflictsTheirs {}
  297pub enum ConflictsOursMarker {}
  298pub enum ConflictsTheirsMarker {}
  299
  300pub struct HunkAddedColor;
  301pub struct HunkRemovedColor;
  302
  303#[derive(Debug, Copy, Clone, PartialEq, Eq)]
  304pub enum Navigated {
  305    Yes,
  306    No,
  307}
  308
  309impl Navigated {
  310    pub fn from_bool(yes: bool) -> Navigated {
  311        if yes { Navigated::Yes } else { Navigated::No }
  312    }
  313}
  314
  315#[derive(Debug, Clone, PartialEq, Eq)]
  316enum DisplayDiffHunk {
  317    Folded {
  318        display_row: DisplayRow,
  319    },
  320    Unfolded {
  321        is_created_file: bool,
  322        diff_base_byte_range: Range<usize>,
  323        display_row_range: Range<DisplayRow>,
  324        multi_buffer_range: Range<Anchor>,
  325        status: DiffHunkStatus,
  326        word_diffs: Vec<Range<MultiBufferOffset>>,
  327    },
  328}
  329
  330pub enum HideMouseCursorOrigin {
  331    TypingAction,
  332    MovementAction,
  333}
  334
  335pub fn init(cx: &mut App) {
  336    cx.set_global(GlobalBlameRenderer(Arc::new(())));
  337    cx.set_global(breadcrumbs::RenderBreadcrumbText(render_breadcrumb_text));
  338
  339    workspace::register_project_item::<Editor>(cx);
  340    workspace::FollowableViewRegistry::register::<Editor>(cx);
  341    workspace::register_serializable_item::<Editor>(cx);
  342
  343    cx.observe_new(
  344        |workspace: &mut Workspace, _: Option<&mut Window>, _cx: &mut Context<Workspace>| {
  345            workspace.register_action(Editor::new_file);
  346            workspace.register_action(Editor::new_file_split);
  347            workspace.register_action(Editor::new_file_vertical);
  348            workspace.register_action(Editor::new_file_horizontal);
  349            workspace.register_action(Editor::cancel_language_server_work);
  350            workspace.register_action(Editor::toggle_focus);
  351        },
  352    )
  353    .detach();
  354
  355    cx.on_action(move |_: &workspace::NewFile, cx| {
  356        let app_state = workspace::AppState::global(cx);
  357        workspace::open_new(
  358            Default::default(),
  359            app_state,
  360            cx,
  361            |workspace, window, cx| Editor::new_file(workspace, &Default::default(), window, cx),
  362        )
  363        .detach_and_log_err(cx);
  364    })
  365    .on_action(move |_: &workspace::NewWindow, cx| {
  366        let app_state = workspace::AppState::global(cx);
  367        workspace::open_new(
  368            Default::default(),
  369            app_state,
  370            cx,
  371            |workspace, window, cx| {
  372                cx.activate(true);
  373                Editor::new_file(workspace, &Default::default(), window, cx)
  374            },
  375        )
  376        .detach_and_log_err(cx);
  377    });
  378    _ = ui_input::ERASED_EDITOR_FACTORY.set(|window, cx| {
  379        Arc::new(ErasedEditorImpl(
  380            cx.new(|cx| Editor::single_line(window, cx)),
  381        )) as Arc<dyn ErasedEditor>
  382    });
  383    _ = multi_buffer::EXCERPT_CONTEXT_LINES.set(multibuffer_context_lines);
  384}
  385
  386pub fn set_blame_renderer(renderer: impl BlameRenderer + 'static, cx: &mut App) {
  387    cx.set_global(GlobalBlameRenderer(Arc::new(renderer)));
  388}
  389
  390pub trait DiagnosticRenderer {
  391    fn render_group(
  392        &self,
  393        diagnostic_group: Vec<DiagnosticEntryRef<'_, Point>>,
  394        buffer_id: BufferId,
  395        snapshot: EditorSnapshot,
  396        editor: WeakEntity<Editor>,
  397        language_registry: Option<Arc<LanguageRegistry>>,
  398        cx: &mut App,
  399    ) -> Vec<BlockProperties<Anchor>>;
  400
  401    fn render_hover(
  402        &self,
  403        diagnostic_group: Vec<DiagnosticEntryRef<'_, Point>>,
  404        range: Range<Point>,
  405        buffer_id: BufferId,
  406        language_registry: Option<Arc<LanguageRegistry>>,
  407        cx: &mut App,
  408    ) -> Option<Entity<markdown::Markdown>>;
  409
  410    fn open_link(
  411        &self,
  412        editor: &mut Editor,
  413        link: SharedString,
  414        window: &mut Window,
  415        cx: &mut Context<Editor>,
  416    );
  417}
  418
  419pub(crate) struct GlobalDiagnosticRenderer(pub Arc<dyn DiagnosticRenderer>);
  420
  421impl GlobalDiagnosticRenderer {
  422    fn global(cx: &App) -> Option<Arc<dyn DiagnosticRenderer>> {
  423        cx.try_global::<Self>().map(|g| g.0.clone())
  424    }
  425}
  426
  427impl gpui::Global for GlobalDiagnosticRenderer {}
  428pub fn set_diagnostic_renderer(renderer: impl DiagnosticRenderer + 'static, cx: &mut App) {
  429    cx.set_global(GlobalDiagnosticRenderer(Arc::new(renderer)));
  430}
  431
  432pub struct SearchWithinRange;
  433
  434trait InvalidationRegion {
  435    fn ranges(&self) -> &[Range<Anchor>];
  436}
  437
  438#[derive(Clone, Debug, PartialEq)]
  439pub enum SelectPhase {
  440    Begin {
  441        position: DisplayPoint,
  442        add: bool,
  443        click_count: usize,
  444    },
  445    BeginColumnar {
  446        position: DisplayPoint,
  447        reset: bool,
  448        mode: ColumnarMode,
  449        goal_column: u32,
  450    },
  451    Extend {
  452        position: DisplayPoint,
  453        click_count: usize,
  454    },
  455    Update {
  456        position: DisplayPoint,
  457        goal_column: u32,
  458        scroll_delta: gpui::Point<f32>,
  459    },
  460    End,
  461}
  462
  463#[derive(Clone, Debug, PartialEq)]
  464pub enum ColumnarMode {
  465    FromMouse,
  466    FromSelection,
  467}
  468
  469#[derive(Clone, Debug)]
  470pub enum SelectMode {
  471    Character,
  472    Word(Range<Anchor>),
  473    Line(Range<Anchor>),
  474    All,
  475}
  476
  477#[derive(Copy, Clone, Default, PartialEq, Eq, Debug)]
  478pub enum SizingBehavior {
  479    /// The editor will layout itself using `size_full` and will include the vertical
  480    /// scroll margin as requested by user settings.
  481    #[default]
  482    Default,
  483    /// The editor will layout itself using `size_full`, but will not have any
  484    /// vertical overscroll.
  485    ExcludeOverscrollMargin,
  486    /// The editor will request a vertical size according to its content and will be
  487    /// layouted without a vertical scroll margin.
  488    SizeByContent,
  489}
  490
  491#[derive(Clone, PartialEq, Eq, Debug)]
  492pub enum EditorMode {
  493    SingleLine,
  494    AutoHeight {
  495        min_lines: usize,
  496        max_lines: Option<usize>,
  497    },
  498    Full {
  499        /// When set to `true`, the editor will scale its UI elements with the buffer font size.
  500        scale_ui_elements_with_buffer_font_size: bool,
  501        /// When set to `true`, the editor will render a background for the active line.
  502        show_active_line_background: bool,
  503        /// Determines the sizing behavior for this editor
  504        sizing_behavior: SizingBehavior,
  505    },
  506    Minimap {
  507        parent: WeakEntity<Editor>,
  508    },
  509}
  510
  511impl EditorMode {
  512    pub fn full() -> Self {
  513        Self::Full {
  514            scale_ui_elements_with_buffer_font_size: true,
  515            show_active_line_background: true,
  516            sizing_behavior: SizingBehavior::Default,
  517        }
  518    }
  519
  520    #[inline]
  521    pub fn is_full(&self) -> bool {
  522        matches!(self, Self::Full { .. })
  523    }
  524
  525    #[inline]
  526    pub fn is_single_line(&self) -> bool {
  527        matches!(self, Self::SingleLine { .. })
  528    }
  529
  530    #[inline]
  531    fn is_minimap(&self) -> bool {
  532        matches!(self, Self::Minimap { .. })
  533    }
  534}
  535
  536#[derive(Copy, Clone, Debug)]
  537pub enum SoftWrap {
  538    /// Prefer not to wrap at all.
  539    ///
  540    /// Note: this is currently internal, as actually limited by [`crate::MAX_LINE_LEN`] until it wraps.
  541    /// The mode is used inside git diff hunks, where it's seems currently more useful to not wrap as much as possible.
  542    GitDiff,
  543    /// Prefer a single line generally, unless an overly long line is encountered.
  544    None,
  545    /// Soft wrap lines that exceed the editor width.
  546    EditorWidth,
  547    /// Soft wrap lines at the preferred line length.
  548    Column(u32),
  549    /// Soft wrap line at the preferred line length or the editor width (whichever is smaller).
  550    Bounded(u32),
  551}
  552
  553#[derive(Clone)]
  554pub struct EditorStyle {
  555    pub background: Hsla,
  556    pub border: Hsla,
  557    pub local_player: PlayerColor,
  558    pub text: TextStyle,
  559    pub scrollbar_width: Pixels,
  560    pub syntax: Arc<SyntaxTheme>,
  561    pub status: StatusColors,
  562    pub inlay_hints_style: HighlightStyle,
  563    pub edit_prediction_styles: EditPredictionStyles,
  564    pub unnecessary_code_fade: f32,
  565    pub show_underlines: bool,
  566}
  567
  568impl Default for EditorStyle {
  569    fn default() -> Self {
  570        Self {
  571            background: Hsla::default(),
  572            border: Hsla::default(),
  573            local_player: PlayerColor::default(),
  574            text: TextStyle::default(),
  575            scrollbar_width: Pixels::default(),
  576            syntax: Default::default(),
  577            // HACK: Status colors don't have a real default.
  578            // We should look into removing the status colors from the editor
  579            // style and retrieve them directly from the theme.
  580            status: StatusColors::dark(),
  581            inlay_hints_style: HighlightStyle::default(),
  582            edit_prediction_styles: EditPredictionStyles {
  583                insertion: HighlightStyle::default(),
  584                whitespace: HighlightStyle::default(),
  585            },
  586            unnecessary_code_fade: Default::default(),
  587            show_underlines: true,
  588        }
  589    }
  590}
  591
  592pub fn make_inlay_hints_style(cx: &App) -> HighlightStyle {
  593    let show_background = AllLanguageSettings::get_global(cx)
  594        .defaults
  595        .inlay_hints
  596        .show_background;
  597
  598    let mut style = cx
  599        .theme()
  600        .syntax()
  601        .style_for_name("hint")
  602        .unwrap_or_default();
  603
  604    if style.color.is_none() {
  605        style.color = Some(cx.theme().status().hint);
  606    }
  607
  608    if !show_background {
  609        style.background_color = None;
  610        return style;
  611    }
  612
  613    if style.background_color.is_none() {
  614        style.background_color = Some(cx.theme().status().hint_background);
  615    }
  616
  617    style
  618}
  619
  620pub fn make_suggestion_styles(cx: &App) -> EditPredictionStyles {
  621    EditPredictionStyles {
  622        insertion: HighlightStyle {
  623            color: Some(cx.theme().status().predictive),
  624            ..HighlightStyle::default()
  625        },
  626        whitespace: HighlightStyle {
  627            background_color: Some(cx.theme().status().created_background),
  628            ..HighlightStyle::default()
  629        },
  630    }
  631}
  632
  633type CompletionId = usize;
  634
  635pub(crate) enum EditDisplayMode {
  636    TabAccept,
  637    DiffPopover,
  638    Inline,
  639}
  640
  641enum EditPrediction {
  642    Edit {
  643        edits: Vec<(Range<Anchor>, Arc<str>)>,
  644        /// Predicted cursor position as (anchor, offset_from_anchor).
  645        /// The anchor is in multibuffer coordinates; after applying edits,
  646        /// resolve the anchor and add the offset to get the final cursor position.
  647        cursor_position: Option<(Anchor, usize)>,
  648        edit_preview: Option<EditPreview>,
  649        display_mode: EditDisplayMode,
  650        snapshot: BufferSnapshot,
  651    },
  652    /// Move to a specific location in the active editor
  653    MoveWithin {
  654        target: Anchor,
  655        snapshot: BufferSnapshot,
  656    },
  657    /// Move to a specific location in a different editor (not the active one)
  658    MoveOutside {
  659        target: language::Anchor,
  660        snapshot: BufferSnapshot,
  661    },
  662}
  663
  664struct EditPredictionState {
  665    inlay_ids: Vec<InlayId>,
  666    completion: EditPrediction,
  667    completion_id: Option<SharedString>,
  668    invalidation_range: Option<Range<Anchor>>,
  669}
  670
  671enum EditPredictionSettings {
  672    Disabled,
  673    Enabled {
  674        show_in_menu: bool,
  675        preview_requires_modifier: bool,
  676    },
  677}
  678
  679#[derive(Debug, Clone)]
  680struct InlineDiagnostic {
  681    message: SharedString,
  682    group_id: usize,
  683    is_primary: bool,
  684    start: Point,
  685    severity: lsp::DiagnosticSeverity,
  686}
  687
  688pub enum MenuEditPredictionsPolicy {
  689    Never,
  690    ByProvider,
  691}
  692
  693pub enum EditPredictionPreview {
  694    /// Modifier is not pressed
  695    Inactive { released_too_fast: bool },
  696    /// Modifier pressed
  697    Active {
  698        since: Instant,
  699        previous_scroll_position: Option<SharedScrollAnchor>,
  700    },
  701}
  702
  703#[derive(Copy, Clone, Eq, PartialEq)]
  704enum EditPredictionKeybindSurface {
  705    Inline,
  706    CursorPopoverCompact,
  707    CursorPopoverExpanded,
  708}
  709
  710#[derive(Copy, Clone, Eq, PartialEq, Debug)]
  711enum EditPredictionKeybindAction {
  712    Accept,
  713    Preview,
  714}
  715
  716struct EditPredictionKeybindDisplay {
  717    #[cfg(test)]
  718    accept_keystroke: Option<gpui::KeybindingKeystroke>,
  719    #[cfg(test)]
  720    preview_keystroke: Option<gpui::KeybindingKeystroke>,
  721    displayed_keystroke: Option<gpui::KeybindingKeystroke>,
  722    action: EditPredictionKeybindAction,
  723    missing_accept_keystroke: bool,
  724    show_hold_label: bool,
  725}
  726
  727impl EditPredictionPreview {
  728    pub fn released_too_fast(&self) -> bool {
  729        match self {
  730            EditPredictionPreview::Inactive { released_too_fast } => *released_too_fast,
  731            EditPredictionPreview::Active { .. } => false,
  732        }
  733    }
  734
  735    pub fn set_previous_scroll_position(&mut self, scroll_position: Option<SharedScrollAnchor>) {
  736        if let EditPredictionPreview::Active {
  737            previous_scroll_position,
  738            ..
  739        } = self
  740        {
  741            *previous_scroll_position = scroll_position;
  742        }
  743    }
  744}
  745
  746pub struct ContextMenuOptions {
  747    pub min_entries_visible: usize,
  748    pub max_entries_visible: usize,
  749    pub placement: Option<ContextMenuPlacement>,
  750}
  751
  752#[derive(Debug, Clone, PartialEq, Eq)]
  753pub enum ContextMenuPlacement {
  754    Above,
  755    Below,
  756}
  757
  758#[derive(Copy, Clone, Eq, PartialEq, PartialOrd, Ord, Debug, Default)]
  759struct EditorActionId(usize);
  760
  761impl EditorActionId {
  762    pub fn post_inc(&mut self) -> Self {
  763        let answer = self.0;
  764
  765        *self = Self(answer + 1);
  766
  767        Self(answer)
  768    }
  769}
  770
  771// type GetFieldEditorTheme = dyn Fn(&theme::Theme) -> theme::FieldEditor;
  772// type OverrideTextStyle = dyn Fn(&EditorStyle) -> Option<HighlightStyle>;
  773
  774type BackgroundHighlight = (
  775    Arc<dyn Fn(&usize, &Theme) -> Hsla + Send + Sync>,
  776    Arc<[Range<Anchor>]>,
  777);
  778type GutterHighlight = (fn(&App) -> Hsla, Vec<Range<Anchor>>);
  779
  780#[derive(Default)]
  781struct ScrollbarMarkerState {
  782    scrollbar_size: Size<Pixels>,
  783    dirty: bool,
  784    markers: Arc<[PaintQuad]>,
  785    pending_refresh: Option<Task<Result<()>>>,
  786}
  787
  788impl ScrollbarMarkerState {
  789    fn should_refresh(&self, scrollbar_size: Size<Pixels>) -> bool {
  790        self.pending_refresh.is_none() && (self.scrollbar_size != scrollbar_size || self.dirty)
  791    }
  792}
  793
  794#[derive(Clone, Copy, PartialEq, Eq)]
  795pub enum MinimapVisibility {
  796    Disabled,
  797    Enabled {
  798        /// The configuration currently present in the users settings.
  799        setting_configuration: bool,
  800        /// Whether to override the currently set visibility from the users setting.
  801        toggle_override: bool,
  802    },
  803}
  804
  805impl MinimapVisibility {
  806    fn for_mode(mode: &EditorMode, cx: &App) -> Self {
  807        if mode.is_full() {
  808            Self::Enabled {
  809                setting_configuration: EditorSettings::get_global(cx).minimap.minimap_enabled(),
  810                toggle_override: false,
  811            }
  812        } else {
  813            Self::Disabled
  814        }
  815    }
  816
  817    fn hidden(&self) -> Self {
  818        match *self {
  819            Self::Enabled {
  820                setting_configuration,
  821                ..
  822            } => Self::Enabled {
  823                setting_configuration,
  824                toggle_override: setting_configuration,
  825            },
  826            Self::Disabled => Self::Disabled,
  827        }
  828    }
  829
  830    fn disabled(&self) -> bool {
  831        matches!(*self, Self::Disabled)
  832    }
  833
  834    fn settings_visibility(&self) -> bool {
  835        match *self {
  836            Self::Enabled {
  837                setting_configuration,
  838                ..
  839            } => setting_configuration,
  840            _ => false,
  841        }
  842    }
  843
  844    fn visible(&self) -> bool {
  845        match *self {
  846            Self::Enabled {
  847                setting_configuration,
  848                toggle_override,
  849            } => setting_configuration ^ toggle_override,
  850            _ => false,
  851        }
  852    }
  853
  854    fn toggle_visibility(&self) -> Self {
  855        match *self {
  856            Self::Enabled {
  857                toggle_override,
  858                setting_configuration,
  859            } => Self::Enabled {
  860                setting_configuration,
  861                toggle_override: !toggle_override,
  862            },
  863            Self::Disabled => Self::Disabled,
  864        }
  865    }
  866}
  867
  868#[derive(Debug, Clone, Copy, PartialEq, Eq)]
  869pub enum BufferSerialization {
  870    All,
  871    NonDirtyBuffers,
  872}
  873
  874impl BufferSerialization {
  875    fn new(restore_unsaved_buffers: bool) -> Self {
  876        if restore_unsaved_buffers {
  877            Self::All
  878        } else {
  879            Self::NonDirtyBuffers
  880        }
  881    }
  882}
  883
  884/// Addons allow storing per-editor state in other crates (e.g. Vim)
  885pub trait Addon: 'static {
  886    fn extend_key_context(&self, _: &mut KeyContext, _: &App) {}
  887
  888    fn render_buffer_header_controls(
  889        &self,
  890        _: &ExcerptInfo,
  891        _: &Window,
  892        _: &App,
  893    ) -> Option<AnyElement> {
  894        None
  895    }
  896
  897    fn override_status_for_buffer_id(&self, _: BufferId, _: &App) -> Option<FileStatus> {
  898        None
  899    }
  900
  901    fn to_any(&self) -> &dyn std::any::Any;
  902
  903    fn to_any_mut(&mut self) -> Option<&mut dyn std::any::Any> {
  904        None
  905    }
  906}
  907
  908struct ChangeLocation {
  909    current: Option<Vec<Anchor>>,
  910    original: Vec<Anchor>,
  911}
  912impl ChangeLocation {
  913    fn locations(&self) -> &[Anchor] {
  914        self.current.as_ref().unwrap_or(&self.original)
  915    }
  916}
  917
  918/// A set of caret positions, registered when the editor was edited.
  919pub struct ChangeList {
  920    changes: Vec<ChangeLocation>,
  921    /// Currently "selected" change.
  922    position: Option<usize>,
  923}
  924
  925impl ChangeList {
  926    pub fn new() -> Self {
  927        Self {
  928            changes: Vec::new(),
  929            position: None,
  930        }
  931    }
  932
  933    /// Moves to the next change in the list (based on the direction given) and returns the caret positions for the next change.
  934    /// If reaches the end of the list in the direction, returns the corresponding change until called for a different direction.
  935    pub fn next_change(&mut self, count: usize, direction: Direction) -> Option<&[Anchor]> {
  936        if self.changes.is_empty() {
  937            return None;
  938        }
  939
  940        let prev = self.position.unwrap_or(self.changes.len());
  941        let next = if direction == Direction::Prev {
  942            prev.saturating_sub(count)
  943        } else {
  944            (prev + count).min(self.changes.len() - 1)
  945        };
  946        self.position = Some(next);
  947        self.changes.get(next).map(|change| change.locations())
  948    }
  949
  950    /// Adds a new change to the list, resetting the change list position.
  951    pub fn push_to_change_list(&mut self, group: bool, new_positions: Vec<Anchor>) {
  952        self.position.take();
  953        if let Some(last) = self.changes.last_mut()
  954            && group
  955        {
  956            last.current = Some(new_positions)
  957        } else {
  958            self.changes.push(ChangeLocation {
  959                original: new_positions,
  960                current: None,
  961            });
  962        }
  963    }
  964
  965    pub fn last(&self) -> Option<&[Anchor]> {
  966        self.changes.last().map(|change| change.locations())
  967    }
  968
  969    pub fn last_before_grouping(&self) -> Option<&[Anchor]> {
  970        self.changes.last().map(|change| change.original.as_slice())
  971    }
  972
  973    pub fn invert_last_group(&mut self) {
  974        if let Some(last) = self.changes.last_mut()
  975            && let Some(current) = last.current.as_mut()
  976        {
  977            mem::swap(&mut last.original, current);
  978        }
  979    }
  980}
  981
  982#[derive(Clone)]
  983struct InlineBlamePopoverState {
  984    scroll_handle: ScrollHandle,
  985    commit_message: Option<ParsedCommitMessage>,
  986    markdown: Entity<Markdown>,
  987}
  988
  989struct InlineBlamePopover {
  990    position: gpui::Point<Pixels>,
  991    hide_task: Option<Task<()>>,
  992    popover_bounds: Option<Bounds<Pixels>>,
  993    popover_state: InlineBlamePopoverState,
  994    keyboard_grace: bool,
  995}
  996
  997enum SelectionDragState {
  998    /// State when no drag related activity is detected.
  999    None,
 1000    /// State when the mouse is down on a selection that is about to be dragged.
 1001    ReadyToDrag {
 1002        selection: Selection<Anchor>,
 1003        click_position: gpui::Point<Pixels>,
 1004        mouse_down_time: Instant,
 1005    },
 1006    /// State when the mouse is dragging the selection in the editor.
 1007    Dragging {
 1008        selection: Selection<Anchor>,
 1009        drop_cursor: Selection<Anchor>,
 1010        hide_drop_cursor: bool,
 1011    },
 1012}
 1013
 1014enum ColumnarSelectionState {
 1015    FromMouse {
 1016        selection_tail: Anchor,
 1017        display_point: Option<DisplayPoint>,
 1018    },
 1019    FromSelection {
 1020        selection_tail: Anchor,
 1021    },
 1022}
 1023
 1024/// Represents a breakpoint indicator that shows up when hovering over lines in the gutter that don't have
 1025/// a breakpoint on them.
 1026#[derive(Clone, Copy, Debug, PartialEq, Eq)]
 1027struct PhantomBreakpointIndicator {
 1028    display_row: DisplayRow,
 1029    /// There's a small debounce between hovering over the line and showing the indicator.
 1030    /// We don't want to show the indicator when moving the mouse from editor to e.g. project panel.
 1031    is_active: bool,
 1032    collides_with_existing_breakpoint: bool,
 1033}
 1034
 1035/// Represents a diff review button indicator that shows up when hovering over lines in the gutter
 1036/// in diff view mode.
 1037#[derive(Clone, Copy, Debug, PartialEq, Eq)]
 1038pub(crate) struct PhantomDiffReviewIndicator {
 1039    /// The starting anchor of the selection (or the only row if not dragging).
 1040    pub start: Anchor,
 1041    /// The ending anchor of the selection. Equal to start_anchor for single-line selection.
 1042    pub end: Anchor,
 1043    /// There's a small debounce between hovering over the line and showing the indicator.
 1044    /// We don't want to show the indicator when moving the mouse from editor to e.g. project panel.
 1045    pub is_active: bool,
 1046}
 1047
 1048#[derive(Clone, Debug)]
 1049pub(crate) struct DiffReviewDragState {
 1050    pub start_anchor: Anchor,
 1051    pub current_anchor: Anchor,
 1052}
 1053
 1054impl DiffReviewDragState {
 1055    pub fn row_range(&self, snapshot: &DisplaySnapshot) -> std::ops::RangeInclusive<DisplayRow> {
 1056        let start = self.start_anchor.to_display_point(snapshot).row();
 1057        let current = self.current_anchor.to_display_point(snapshot).row();
 1058
 1059        (start..=current).sorted()
 1060    }
 1061}
 1062
 1063/// Identifies a specific hunk in the diff buffer.
 1064/// Used as a key to group comments by their location.
 1065#[derive(Clone, Debug)]
 1066pub struct DiffHunkKey {
 1067    /// The file path (relative to worktree) this hunk belongs to.
 1068    pub file_path: Arc<util::rel_path::RelPath>,
 1069    /// An anchor at the start of the hunk. This tracks position as the buffer changes.
 1070    pub hunk_start_anchor: Anchor,
 1071}
 1072
 1073/// A review comment stored locally before being sent to the Agent panel.
 1074#[derive(Clone)]
 1075pub struct StoredReviewComment {
 1076    /// Unique identifier for this comment (for edit/delete operations).
 1077    pub id: usize,
 1078    /// The comment text entered by the user.
 1079    pub comment: String,
 1080    /// Anchors for the code range being reviewed.
 1081    pub range: Range<Anchor>,
 1082    /// Timestamp when the comment was created (for chronological ordering).
 1083    pub created_at: Instant,
 1084    /// Whether this comment is currently being edited inline.
 1085    pub is_editing: bool,
 1086}
 1087
 1088impl StoredReviewComment {
 1089    pub fn new(id: usize, comment: String, anchor_range: Range<Anchor>) -> Self {
 1090        Self {
 1091            id,
 1092            comment,
 1093            range: anchor_range,
 1094            created_at: Instant::now(),
 1095            is_editing: false,
 1096        }
 1097    }
 1098}
 1099
 1100/// Represents an active diff review overlay that appears when clicking the "Add Review" button.
 1101pub(crate) struct DiffReviewOverlay {
 1102    pub anchor_range: Range<Anchor>,
 1103    /// The block ID for the overlay.
 1104    pub block_id: CustomBlockId,
 1105    /// The editor entity for the review input.
 1106    pub prompt_editor: Entity<Editor>,
 1107    /// The hunk key this overlay belongs to.
 1108    pub hunk_key: DiffHunkKey,
 1109    /// Whether the comments section is expanded.
 1110    pub comments_expanded: bool,
 1111    /// Editors for comments currently being edited inline.
 1112    /// Key: comment ID, Value: Editor entity for inline editing.
 1113    pub inline_edit_editors: HashMap<usize, Entity<Editor>>,
 1114    /// Subscriptions for inline edit editors' action handlers.
 1115    /// Key: comment ID, Value: Subscription keeping the Newline action handler alive.
 1116    pub inline_edit_subscriptions: HashMap<usize, Subscription>,
 1117    /// The current user's avatar URI for display in comment rows.
 1118    pub user_avatar_uri: Option<SharedUri>,
 1119    /// Subscription to keep the action handler alive.
 1120    _subscription: Subscription,
 1121}
 1122
 1123/// Zed's primary implementation of text input, allowing users to edit a [`MultiBuffer`].
 1124///
 1125/// See the [module level documentation](self) for more information.
 1126pub struct Editor {
 1127    focus_handle: FocusHandle,
 1128    last_focused_descendant: Option<WeakFocusHandle>,
 1129    /// The text buffer being edited
 1130    buffer: Entity<MultiBuffer>,
 1131    /// Map of how text in the buffer should be displayed.
 1132    /// Handles soft wraps, folds, fake inlay text insertions, etc.
 1133    pub display_map: Entity<DisplayMap>,
 1134    placeholder_display_map: Option<Entity<DisplayMap>>,
 1135    pub selections: SelectionsCollection,
 1136    pub scroll_manager: ScrollManager,
 1137    /// When inline assist editors are linked, they all render cursors because
 1138    /// typing enters text into each of them, even the ones that aren't focused.
 1139    pub(crate) show_cursor_when_unfocused: bool,
 1140    columnar_selection_state: Option<ColumnarSelectionState>,
 1141    add_selections_state: Option<AddSelectionsState>,
 1142    select_next_state: Option<SelectNextState>,
 1143    select_prev_state: Option<SelectNextState>,
 1144    selection_history: SelectionHistory,
 1145    defer_selection_effects: bool,
 1146    deferred_selection_effects_state: Option<DeferredSelectionEffectsState>,
 1147    autoclose_regions: Vec<AutocloseRegion>,
 1148    snippet_stack: InvalidationStack<SnippetState>,
 1149    select_syntax_node_history: SelectSyntaxNodeHistory,
 1150    ime_transaction: Option<TransactionId>,
 1151    pub diagnostics_max_severity: DiagnosticSeverity,
 1152    active_diagnostics: ActiveDiagnostic,
 1153    show_inline_diagnostics: bool,
 1154    inline_diagnostics_update: Task<()>,
 1155    inline_diagnostics_enabled: bool,
 1156    diagnostics_enabled: bool,
 1157    word_completions_enabled: bool,
 1158    inline_diagnostics: Vec<(Anchor, InlineDiagnostic)>,
 1159    soft_wrap_mode_override: Option<language_settings::SoftWrap>,
 1160    hard_wrap: Option<usize>,
 1161    project: Option<Entity<Project>>,
 1162    semantics_provider: Option<Rc<dyn SemanticsProvider>>,
 1163    completion_provider: Option<Rc<dyn CompletionProvider>>,
 1164    collaboration_hub: Option<Box<dyn CollaborationHub>>,
 1165    blink_manager: Entity<BlinkManager>,
 1166    show_cursor_names: bool,
 1167    hovered_cursors: HashMap<HoveredCursor, Task<()>>,
 1168    pub show_local_selections: bool,
 1169    mode: EditorMode,
 1170    show_breadcrumbs: bool,
 1171    show_gutter: bool,
 1172    show_scrollbars: ScrollbarAxes,
 1173    minimap_visibility: MinimapVisibility,
 1174    offset_content: bool,
 1175    disable_expand_excerpt_buttons: bool,
 1176    delegate_expand_excerpts: bool,
 1177    delegate_stage_and_restore: bool,
 1178    delegate_open_excerpts: bool,
 1179    enable_lsp_data: bool,
 1180    enable_runnables: bool,
 1181    show_line_numbers: Option<bool>,
 1182    use_relative_line_numbers: Option<bool>,
 1183    show_git_diff_gutter: Option<bool>,
 1184    show_code_actions: Option<bool>,
 1185    show_runnables: Option<bool>,
 1186    show_breakpoints: Option<bool>,
 1187    show_diff_review_button: bool,
 1188    show_wrap_guides: Option<bool>,
 1189    show_indent_guides: Option<bool>,
 1190    buffers_with_disabled_indent_guides: HashSet<BufferId>,
 1191    highlight_order: usize,
 1192    highlighted_rows: HashMap<TypeId, Vec<RowHighlight>>,
 1193    background_highlights: HashMap<HighlightKey, BackgroundHighlight>,
 1194    gutter_highlights: HashMap<TypeId, GutterHighlight>,
 1195    scrollbar_marker_state: ScrollbarMarkerState,
 1196    active_indent_guides_state: ActiveIndentGuidesState,
 1197    nav_history: Option<ItemNavHistory>,
 1198    context_menu: RefCell<Option<CodeContextMenu>>,
 1199    context_menu_options: Option<ContextMenuOptions>,
 1200    mouse_context_menu: Option<MouseContextMenu>,
 1201    completion_tasks: Vec<(CompletionId, Task<()>)>,
 1202    inline_blame_popover: Option<InlineBlamePopover>,
 1203    inline_blame_popover_show_task: Option<Task<()>>,
 1204    signature_help_state: SignatureHelpState,
 1205    auto_signature_help: Option<bool>,
 1206    find_all_references_task_sources: Vec<Anchor>,
 1207    next_completion_id: CompletionId,
 1208    available_code_actions: Option<(Location, Rc<[AvailableCodeAction]>)>,
 1209    code_actions_task: Option<Task<Result<()>>>,
 1210    quick_selection_highlight_task: Option<(Range<Anchor>, Task<()>)>,
 1211    debounced_selection_highlight_task: Option<(Range<Anchor>, Task<()>)>,
 1212    debounced_selection_highlight_complete: bool,
 1213    document_highlights_task: Option<Task<()>>,
 1214    linked_editing_range_task: Option<Task<Option<()>>>,
 1215    linked_edit_ranges: linked_editing_ranges::LinkedEditingRanges,
 1216    pending_rename: Option<RenameState>,
 1217    searchable: bool,
 1218    cursor_shape: CursorShape,
 1219    /// Whether the cursor is offset one character to the left when something is
 1220    /// selected (needed for vim visual mode)
 1221    cursor_offset_on_selection: bool,
 1222    current_line_highlight: Option<CurrentLineHighlight>,
 1223    /// Whether to collapse search match ranges to just their start position.
 1224    /// When true, navigating to a match positions the cursor at the match
 1225    /// without selecting the matched text.
 1226    collapse_matches: bool,
 1227    autoindent_mode: Option<AutoindentMode>,
 1228    workspace: Option<(WeakEntity<Workspace>, Option<WorkspaceId>)>,
 1229    input_enabled: bool,
 1230    expects_character_input: bool,
 1231    use_modal_editing: bool,
 1232    read_only: bool,
 1233    leader_id: Option<CollaboratorId>,
 1234    remote_id: Option<ViewId>,
 1235    pub hover_state: HoverState,
 1236    pending_mouse_down: Option<Rc<RefCell<Option<MouseDownEvent>>>>,
 1237    prev_pressure_stage: Option<PressureStage>,
 1238    gutter_hovered: bool,
 1239    hovered_link_state: Option<HoveredLinkState>,
 1240    edit_prediction_provider: Option<RegisteredEditPredictionDelegate>,
 1241    code_action_providers: Vec<Rc<dyn CodeActionProvider>>,
 1242    active_edit_prediction: Option<EditPredictionState>,
 1243    /// Used to prevent flickering as the user types while the menu is open
 1244    stale_edit_prediction_in_menu: Option<EditPredictionState>,
 1245    edit_prediction_settings: EditPredictionSettings,
 1246    edit_predictions_hidden_for_vim_mode: bool,
 1247    show_edit_predictions_override: Option<bool>,
 1248    show_completions_on_input_override: Option<bool>,
 1249    menu_edit_predictions_policy: MenuEditPredictionsPolicy,
 1250    edit_prediction_preview: EditPredictionPreview,
 1251    in_leading_whitespace: bool,
 1252    next_inlay_id: usize,
 1253    next_color_inlay_id: usize,
 1254    _subscriptions: Vec<Subscription>,
 1255    pixel_position_of_newest_cursor: Option<gpui::Point<Pixels>>,
 1256    gutter_dimensions: GutterDimensions,
 1257    style: Option<EditorStyle>,
 1258    text_style_refinement: Option<TextStyleRefinement>,
 1259    next_editor_action_id: EditorActionId,
 1260    editor_actions: Rc<
 1261        RefCell<BTreeMap<EditorActionId, Box<dyn Fn(&Editor, &mut Window, &mut Context<Self>)>>>,
 1262    >,
 1263    use_autoclose: bool,
 1264    use_auto_surround: bool,
 1265    auto_replace_emoji_shortcode: bool,
 1266    jsx_tag_auto_close_enabled_in_any_buffer: bool,
 1267    show_git_blame_gutter: bool,
 1268    show_git_blame_inline: bool,
 1269    show_git_blame_inline_delay_task: Option<Task<()>>,
 1270    git_blame_inline_enabled: bool,
 1271    render_diff_hunk_controls: RenderDiffHunkControlsFn,
 1272    buffer_serialization: Option<BufferSerialization>,
 1273    show_selection_menu: Option<bool>,
 1274    blame: Option<Entity<GitBlame>>,
 1275    blame_subscription: Option<Subscription>,
 1276    custom_context_menu: Option<
 1277        Box<
 1278            dyn 'static
 1279                + Fn(
 1280                    &mut Self,
 1281                    DisplayPoint,
 1282                    &mut Window,
 1283                    &mut Context<Self>,
 1284                ) -> Option<Entity<ui::ContextMenu>>,
 1285        >,
 1286    >,
 1287    last_bounds: Option<Bounds<Pixels>>,
 1288    last_position_map: Option<Rc<PositionMap>>,
 1289    expect_bounds_change: Option<Bounds<Pixels>>,
 1290    runnables: RunnableData,
 1291    breakpoint_store: Option<Entity<BreakpointStore>>,
 1292    gutter_breakpoint_indicator: (Option<PhantomBreakpointIndicator>, Option<Task<()>>),
 1293    pub(crate) gutter_diff_review_indicator: (Option<PhantomDiffReviewIndicator>, Option<Task<()>>),
 1294    pub(crate) diff_review_drag_state: Option<DiffReviewDragState>,
 1295    /// Active diff review overlays. Multiple overlays can be open simultaneously
 1296    /// when hunks have comments stored.
 1297    pub(crate) diff_review_overlays: Vec<DiffReviewOverlay>,
 1298    /// Stored review comments grouped by hunk.
 1299    /// Uses a Vec instead of HashMap because DiffHunkKey contains an Anchor
 1300    /// which doesn't implement Hash/Eq in a way suitable for HashMap keys.
 1301    stored_review_comments: Vec<(DiffHunkKey, Vec<StoredReviewComment>)>,
 1302    /// Counter for generating unique comment IDs.
 1303    next_review_comment_id: usize,
 1304    hovered_diff_hunk_row: Option<DisplayRow>,
 1305    pull_diagnostics_task: Task<()>,
 1306    in_project_search: bool,
 1307    previous_search_ranges: Option<Arc<[Range<Anchor>]>>,
 1308    breadcrumb_header: Option<String>,
 1309    focused_block: Option<FocusedBlock>,
 1310    next_scroll_position: NextScrollCursorCenterTopBottom,
 1311    addons: HashMap<TypeId, Box<dyn Addon>>,
 1312    registered_buffers: HashMap<BufferId, OpenLspBufferHandle>,
 1313    load_diff_task: Option<Shared<Task<()>>>,
 1314    /// Whether we are temporarily displaying a diff other than git's
 1315    temporary_diff_override: bool,
 1316    selection_mark_mode: bool,
 1317    toggle_fold_multiple_buffers: Task<()>,
 1318    _scroll_cursor_center_top_bottom_task: Task<()>,
 1319    serialize_selections: Task<()>,
 1320    serialize_folds: Task<()>,
 1321    mouse_cursor_hidden: bool,
 1322    minimap: Option<Entity<Self>>,
 1323    hide_mouse_mode: HideMouseMode,
 1324    pub change_list: ChangeList,
 1325    inline_value_cache: InlineValueCache,
 1326    number_deleted_lines: bool,
 1327
 1328    selection_drag_state: SelectionDragState,
 1329    colors: Option<LspColorData>,
 1330    post_scroll_update: Task<()>,
 1331    refresh_colors_task: Task<()>,
 1332    use_document_folding_ranges: bool,
 1333    refresh_folding_ranges_task: Task<()>,
 1334    inlay_hints: Option<LspInlayHintData>,
 1335    folding_newlines: Task<()>,
 1336    select_next_is_case_sensitive: Option<bool>,
 1337    pub lookup_key: Option<Box<dyn Any + Send + Sync>>,
 1338    on_local_selections_changed:
 1339        Option<Box<dyn Fn(Point, &mut Window, &mut Context<Self>) + 'static>>,
 1340    suppress_selection_callback: bool,
 1341    applicable_language_settings: HashMap<Option<LanguageName>, LanguageSettings>,
 1342    accent_data: Option<AccentData>,
 1343    bracket_fetched_tree_sitter_chunks: HashMap<ExcerptId, HashSet<Range<BufferRow>>>,
 1344    semantic_token_state: SemanticTokenState,
 1345    pub(crate) refresh_matching_bracket_highlights_task: Task<()>,
 1346    refresh_document_symbols_task: Shared<Task<()>>,
 1347    lsp_document_symbols: HashMap<BufferId, Vec<OutlineItem<text::Anchor>>>,
 1348    refresh_outline_symbols_at_cursor_at_cursor_task: Task<()>,
 1349    outline_symbols_at_cursor: Option<(BufferId, Vec<OutlineItem<Anchor>>)>,
 1350    sticky_headers_task: Task<()>,
 1351    sticky_headers: Option<Vec<OutlineItem<Anchor>>>,
 1352    pub(crate) colorize_brackets_task: Task<()>,
 1353}
 1354
 1355#[derive(Debug, PartialEq)]
 1356struct AccentData {
 1357    colors: AccentColors,
 1358    overrides: Vec<SharedString>,
 1359}
 1360
 1361fn debounce_value(debounce_ms: u64) -> Option<Duration> {
 1362    if debounce_ms > 0 {
 1363        Some(Duration::from_millis(debounce_ms))
 1364    } else {
 1365        None
 1366    }
 1367}
 1368
 1369#[derive(Copy, Clone, Debug, PartialEq, Eq, Default)]
 1370enum NextScrollCursorCenterTopBottom {
 1371    #[default]
 1372    Center,
 1373    Top,
 1374    Bottom,
 1375}
 1376
 1377impl NextScrollCursorCenterTopBottom {
 1378    fn next(&self) -> Self {
 1379        match self {
 1380            Self::Center => Self::Top,
 1381            Self::Top => Self::Bottom,
 1382            Self::Bottom => Self::Center,
 1383        }
 1384    }
 1385}
 1386
 1387#[derive(Clone)]
 1388pub struct EditorSnapshot {
 1389    pub mode: EditorMode,
 1390    show_gutter: bool,
 1391    offset_content: bool,
 1392    show_line_numbers: Option<bool>,
 1393    number_deleted_lines: bool,
 1394    show_git_diff_gutter: Option<bool>,
 1395    show_code_actions: Option<bool>,
 1396    show_runnables: Option<bool>,
 1397    show_breakpoints: Option<bool>,
 1398    git_blame_gutter_max_author_length: Option<usize>,
 1399    pub display_snapshot: DisplaySnapshot,
 1400    pub placeholder_display_snapshot: Option<DisplaySnapshot>,
 1401    is_focused: bool,
 1402    scroll_anchor: SharedScrollAnchor,
 1403    ongoing_scroll: OngoingScroll,
 1404    current_line_highlight: CurrentLineHighlight,
 1405    gutter_hovered: bool,
 1406    semantic_tokens_enabled: bool,
 1407}
 1408
 1409#[derive(Default, Debug, Clone, Copy)]
 1410pub struct GutterDimensions {
 1411    pub left_padding: Pixels,
 1412    pub right_padding: Pixels,
 1413    pub width: Pixels,
 1414    pub margin: Pixels,
 1415    pub git_blame_entries_width: Option<Pixels>,
 1416}
 1417
 1418impl GutterDimensions {
 1419    fn default_with_margin(font_id: FontId, font_size: Pixels, cx: &App) -> Self {
 1420        Self {
 1421            margin: Self::default_gutter_margin(font_id, font_size, cx),
 1422            ..Default::default()
 1423        }
 1424    }
 1425
 1426    fn default_gutter_margin(font_id: FontId, font_size: Pixels, cx: &App) -> Pixels {
 1427        -cx.text_system().descent(font_id, font_size)
 1428    }
 1429    /// The full width of the space taken up by the gutter.
 1430    pub fn full_width(&self) -> Pixels {
 1431        self.margin + self.width
 1432    }
 1433
 1434    /// The width of the space reserved for the fold indicators,
 1435    /// use alongside 'justify_end' and `gutter_width` to
 1436    /// right align content with the line numbers
 1437    pub fn fold_area_width(&self) -> Pixels {
 1438        self.margin + self.right_padding
 1439    }
 1440}
 1441
 1442struct CharacterDimensions {
 1443    em_width: Pixels,
 1444    em_advance: Pixels,
 1445    line_height: Pixels,
 1446}
 1447
 1448#[derive(Debug)]
 1449pub struct RemoteSelection {
 1450    pub replica_id: ReplicaId,
 1451    pub selection: Selection<Anchor>,
 1452    pub cursor_shape: CursorShape,
 1453    pub collaborator_id: CollaboratorId,
 1454    pub line_mode: bool,
 1455    pub user_name: Option<SharedString>,
 1456    pub color: PlayerColor,
 1457}
 1458
 1459#[derive(Clone, Debug)]
 1460struct SelectionHistoryEntry {
 1461    selections: Arc<[Selection<Anchor>]>,
 1462    select_next_state: Option<SelectNextState>,
 1463    select_prev_state: Option<SelectNextState>,
 1464    add_selections_state: Option<AddSelectionsState>,
 1465}
 1466
 1467#[derive(Copy, Clone, Default, Debug, PartialEq, Eq)]
 1468enum SelectionHistoryMode {
 1469    #[default]
 1470    Normal,
 1471    Undoing,
 1472    Redoing,
 1473    Skipping,
 1474}
 1475
 1476#[derive(Clone, PartialEq, Eq, Hash)]
 1477struct HoveredCursor {
 1478    replica_id: ReplicaId,
 1479    selection_id: usize,
 1480}
 1481
 1482#[derive(Debug)]
 1483/// SelectionEffects controls the side-effects of updating the selection.
 1484///
 1485/// The default behaviour does "what you mostly want":
 1486/// - it pushes to the nav history if the cursor moved by >10 lines
 1487/// - it re-triggers completion requests
 1488/// - it scrolls to fit
 1489///
 1490/// You might want to modify these behaviours. For example when doing a "jump"
 1491/// like go to definition, we always want to add to nav history; but when scrolling
 1492/// in vim mode we never do.
 1493///
 1494/// Similarly, you might want to disable scrolling if you don't want the viewport to
 1495/// move.
 1496#[derive(Clone)]
 1497pub struct SelectionEffects {
 1498    nav_history: Option<bool>,
 1499    completions: bool,
 1500    scroll: Option<Autoscroll>,
 1501}
 1502
 1503impl Default for SelectionEffects {
 1504    fn default() -> Self {
 1505        Self {
 1506            nav_history: None,
 1507            completions: true,
 1508            scroll: Some(Autoscroll::fit()),
 1509        }
 1510    }
 1511}
 1512impl SelectionEffects {
 1513    pub fn scroll(scroll: Autoscroll) -> Self {
 1514        Self {
 1515            scroll: Some(scroll),
 1516            ..Default::default()
 1517        }
 1518    }
 1519
 1520    pub fn no_scroll() -> Self {
 1521        Self {
 1522            scroll: None,
 1523            ..Default::default()
 1524        }
 1525    }
 1526
 1527    pub fn completions(self, completions: bool) -> Self {
 1528        Self {
 1529            completions,
 1530            ..self
 1531        }
 1532    }
 1533
 1534    pub fn nav_history(self, nav_history: bool) -> Self {
 1535        Self {
 1536            nav_history: Some(nav_history),
 1537            ..self
 1538        }
 1539    }
 1540}
 1541
 1542struct DeferredSelectionEffectsState {
 1543    changed: bool,
 1544    effects: SelectionEffects,
 1545    old_cursor_position: Anchor,
 1546    history_entry: SelectionHistoryEntry,
 1547}
 1548
 1549#[derive(Default)]
 1550struct SelectionHistory {
 1551    #[allow(clippy::type_complexity)]
 1552    selections_by_transaction:
 1553        HashMap<TransactionId, (Arc<[Selection<Anchor>]>, Option<Arc<[Selection<Anchor>]>>)>,
 1554    mode: SelectionHistoryMode,
 1555    undo_stack: VecDeque<SelectionHistoryEntry>,
 1556    redo_stack: VecDeque<SelectionHistoryEntry>,
 1557}
 1558
 1559impl SelectionHistory {
 1560    #[track_caller]
 1561    fn insert_transaction(
 1562        &mut self,
 1563        transaction_id: TransactionId,
 1564        selections: Arc<[Selection<Anchor>]>,
 1565    ) {
 1566        if selections.is_empty() {
 1567            log::error!(
 1568                "SelectionHistory::insert_transaction called with empty selections. Caller: {}",
 1569                std::panic::Location::caller()
 1570            );
 1571            return;
 1572        }
 1573        self.selections_by_transaction
 1574            .insert(transaction_id, (selections, None));
 1575    }
 1576
 1577    #[allow(clippy::type_complexity)]
 1578    fn transaction(
 1579        &self,
 1580        transaction_id: TransactionId,
 1581    ) -> Option<&(Arc<[Selection<Anchor>]>, Option<Arc<[Selection<Anchor>]>>)> {
 1582        self.selections_by_transaction.get(&transaction_id)
 1583    }
 1584
 1585    #[allow(clippy::type_complexity)]
 1586    fn transaction_mut(
 1587        &mut self,
 1588        transaction_id: TransactionId,
 1589    ) -> Option<&mut (Arc<[Selection<Anchor>]>, Option<Arc<[Selection<Anchor>]>>)> {
 1590        self.selections_by_transaction.get_mut(&transaction_id)
 1591    }
 1592
 1593    fn push(&mut self, entry: SelectionHistoryEntry) {
 1594        if !entry.selections.is_empty() {
 1595            match self.mode {
 1596                SelectionHistoryMode::Normal => {
 1597                    self.push_undo(entry);
 1598                    self.redo_stack.clear();
 1599                }
 1600                SelectionHistoryMode::Undoing => self.push_redo(entry),
 1601                SelectionHistoryMode::Redoing => self.push_undo(entry),
 1602                SelectionHistoryMode::Skipping => {}
 1603            }
 1604        }
 1605    }
 1606
 1607    fn push_undo(&mut self, entry: SelectionHistoryEntry) {
 1608        if self
 1609            .undo_stack
 1610            .back()
 1611            .is_none_or(|e| e.selections != entry.selections)
 1612        {
 1613            self.undo_stack.push_back(entry);
 1614            if self.undo_stack.len() > MAX_SELECTION_HISTORY_LEN {
 1615                self.undo_stack.pop_front();
 1616            }
 1617        }
 1618    }
 1619
 1620    fn push_redo(&mut self, entry: SelectionHistoryEntry) {
 1621        if self
 1622            .redo_stack
 1623            .back()
 1624            .is_none_or(|e| e.selections != entry.selections)
 1625        {
 1626            self.redo_stack.push_back(entry);
 1627            if self.redo_stack.len() > MAX_SELECTION_HISTORY_LEN {
 1628                self.redo_stack.pop_front();
 1629            }
 1630        }
 1631    }
 1632}
 1633
 1634#[derive(Clone, Copy)]
 1635pub struct RowHighlightOptions {
 1636    pub autoscroll: bool,
 1637    pub include_gutter: bool,
 1638}
 1639
 1640impl Default for RowHighlightOptions {
 1641    fn default() -> Self {
 1642        Self {
 1643            autoscroll: Default::default(),
 1644            include_gutter: true,
 1645        }
 1646    }
 1647}
 1648
 1649struct RowHighlight {
 1650    index: usize,
 1651    range: Range<Anchor>,
 1652    color: Hsla,
 1653    options: RowHighlightOptions,
 1654    type_id: TypeId,
 1655}
 1656
 1657#[derive(Clone, Debug)]
 1658struct AddSelectionsState {
 1659    groups: Vec<AddSelectionsGroup>,
 1660}
 1661
 1662#[derive(Clone, Debug)]
 1663struct AddSelectionsGroup {
 1664    above: bool,
 1665    stack: Vec<usize>,
 1666}
 1667
 1668#[derive(Clone)]
 1669struct SelectNextState {
 1670    query: AhoCorasick,
 1671    wordwise: bool,
 1672    done: bool,
 1673}
 1674
 1675impl std::fmt::Debug for SelectNextState {
 1676    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
 1677        f.debug_struct(std::any::type_name::<Self>())
 1678            .field("wordwise", &self.wordwise)
 1679            .field("done", &self.done)
 1680            .finish()
 1681    }
 1682}
 1683
 1684#[derive(Debug)]
 1685struct AutocloseRegion {
 1686    selection_id: usize,
 1687    range: Range<Anchor>,
 1688    pair: BracketPair,
 1689}
 1690
 1691#[derive(Debug)]
 1692struct SnippetState {
 1693    ranges: Vec<Vec<Range<Anchor>>>,
 1694    active_index: usize,
 1695    choices: Vec<Option<Vec<String>>>,
 1696}
 1697
 1698#[doc(hidden)]
 1699pub struct RenameState {
 1700    pub range: Range<Anchor>,
 1701    pub old_name: Arc<str>,
 1702    pub editor: Entity<Editor>,
 1703    block_id: CustomBlockId,
 1704}
 1705
 1706struct InvalidationStack<T>(Vec<T>);
 1707
 1708struct RegisteredEditPredictionDelegate {
 1709    provider: Arc<dyn EditPredictionDelegateHandle>,
 1710    _subscription: Subscription,
 1711}
 1712
 1713#[derive(Debug, PartialEq, Eq)]
 1714pub struct ActiveDiagnosticGroup {
 1715    pub active_range: Range<Anchor>,
 1716    pub active_message: String,
 1717    pub group_id: usize,
 1718    pub blocks: HashSet<CustomBlockId>,
 1719}
 1720
 1721#[derive(Debug, PartialEq, Eq)]
 1722
 1723pub(crate) enum ActiveDiagnostic {
 1724    None,
 1725    All,
 1726    Group(ActiveDiagnosticGroup),
 1727}
 1728
 1729#[derive(Serialize, Deserialize, Clone, Debug)]
 1730pub struct ClipboardSelection {
 1731    /// The number of bytes in this selection.
 1732    pub len: usize,
 1733    /// Whether this was a full-line selection.
 1734    pub is_entire_line: bool,
 1735    /// The indentation of the first line when this content was originally copied.
 1736    pub first_line_indent: u32,
 1737    #[serde(default)]
 1738    pub file_path: Option<PathBuf>,
 1739    #[serde(default)]
 1740    pub line_range: Option<RangeInclusive<u32>>,
 1741}
 1742
 1743impl ClipboardSelection {
 1744    pub fn for_buffer(
 1745        len: usize,
 1746        is_entire_line: bool,
 1747        range: Range<Point>,
 1748        buffer: &MultiBufferSnapshot,
 1749        project: Option<&Entity<Project>>,
 1750        cx: &App,
 1751    ) -> Self {
 1752        let first_line_indent = buffer
 1753            .indent_size_for_line(MultiBufferRow(range.start.row))
 1754            .len;
 1755
 1756        let file_path = util::maybe!({
 1757            let project = project?.read(cx);
 1758            let file = buffer.file_at(range.start)?;
 1759            let project_path = ProjectPath {
 1760                worktree_id: file.worktree_id(cx),
 1761                path: file.path().clone(),
 1762            };
 1763            project.absolute_path(&project_path, cx)
 1764        });
 1765
 1766        let line_range = file_path.as_ref().and_then(|_| {
 1767            let (_, start_point, start_excerpt_id) = buffer.point_to_buffer_point(range.start)?;
 1768            let (_, end_point, end_excerpt_id) = buffer.point_to_buffer_point(range.end)?;
 1769            if start_excerpt_id == end_excerpt_id {
 1770                Some(start_point.row..=end_point.row)
 1771            } else {
 1772                None
 1773            }
 1774        });
 1775
 1776        Self {
 1777            len,
 1778            is_entire_line,
 1779            first_line_indent,
 1780            file_path,
 1781            line_range,
 1782        }
 1783    }
 1784}
 1785
 1786// selections, scroll behavior, was newest selection reversed
 1787type SelectSyntaxNodeHistoryState = (
 1788    Box<[Selection<Anchor>]>,
 1789    SelectSyntaxNodeScrollBehavior,
 1790    bool,
 1791);
 1792
 1793#[derive(Default)]
 1794struct SelectSyntaxNodeHistory {
 1795    stack: Vec<SelectSyntaxNodeHistoryState>,
 1796    // disable temporarily to allow changing selections without losing the stack
 1797    pub disable_clearing: bool,
 1798}
 1799
 1800impl SelectSyntaxNodeHistory {
 1801    pub fn try_clear(&mut self) {
 1802        if !self.disable_clearing {
 1803            self.stack.clear();
 1804        }
 1805    }
 1806
 1807    pub fn push(&mut self, selection: SelectSyntaxNodeHistoryState) {
 1808        self.stack.push(selection);
 1809    }
 1810
 1811    pub fn pop(&mut self) -> Option<SelectSyntaxNodeHistoryState> {
 1812        self.stack.pop()
 1813    }
 1814}
 1815
 1816enum SelectSyntaxNodeScrollBehavior {
 1817    CursorTop,
 1818    FitSelection,
 1819    CursorBottom,
 1820}
 1821
 1822#[derive(Debug, Clone, Copy)]
 1823pub(crate) struct NavigationData {
 1824    cursor_anchor: Anchor,
 1825    cursor_position: Point,
 1826    scroll_anchor: ScrollAnchor,
 1827    scroll_top_row: u32,
 1828}
 1829
 1830#[derive(Debug, Clone, Copy, PartialEq, Eq)]
 1831pub enum GotoDefinitionKind {
 1832    Symbol,
 1833    Declaration,
 1834    Type,
 1835    Implementation,
 1836}
 1837
 1838pub enum FormatTarget {
 1839    Buffers(HashSet<Entity<Buffer>>),
 1840    Ranges(Vec<Range<MultiBufferPoint>>),
 1841}
 1842
 1843pub(crate) struct FocusedBlock {
 1844    id: BlockId,
 1845    focus_handle: WeakFocusHandle,
 1846}
 1847
 1848#[derive(Clone, Debug)]
 1849pub enum JumpData {
 1850    MultiBufferRow {
 1851        row: MultiBufferRow,
 1852        line_offset_from_top: u32,
 1853    },
 1854    MultiBufferPoint {
 1855        excerpt_id: ExcerptId,
 1856        position: Point,
 1857        anchor: text::Anchor,
 1858        line_offset_from_top: u32,
 1859    },
 1860}
 1861
 1862pub enum MultibufferSelectionMode {
 1863    First,
 1864    All,
 1865}
 1866
 1867#[derive(Clone, Copy, Debug, Default)]
 1868pub struct RewrapOptions {
 1869    pub override_language_settings: bool,
 1870    pub preserve_existing_whitespace: bool,
 1871    pub line_length: Option<usize>,
 1872}
 1873
 1874impl Editor {
 1875    pub fn single_line(window: &mut Window, cx: &mut Context<Self>) -> Self {
 1876        let buffer = cx.new(|cx| Buffer::local("", cx));
 1877        let buffer = cx.new(|cx| MultiBuffer::singleton(buffer, cx));
 1878        Self::new(EditorMode::SingleLine, buffer, None, window, cx)
 1879    }
 1880
 1881    pub fn multi_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::full(), buffer, None, window, cx)
 1885    }
 1886
 1887    pub fn auto_height(
 1888        min_lines: usize,
 1889        max_lines: usize,
 1890        window: &mut Window,
 1891        cx: &mut Context<Self>,
 1892    ) -> Self {
 1893        let buffer = cx.new(|cx| Buffer::local("", cx));
 1894        let buffer = cx.new(|cx| MultiBuffer::singleton(buffer, cx));
 1895        Self::new(
 1896            EditorMode::AutoHeight {
 1897                min_lines,
 1898                max_lines: Some(max_lines),
 1899            },
 1900            buffer,
 1901            None,
 1902            window,
 1903            cx,
 1904        )
 1905    }
 1906
 1907    /// Creates a new auto-height editor with a minimum number of lines but no maximum.
 1908    /// The editor grows as tall as needed to fit its content.
 1909    pub fn auto_height_unbounded(
 1910        min_lines: usize,
 1911        window: &mut Window,
 1912        cx: &mut Context<Self>,
 1913    ) -> Self {
 1914        let buffer = cx.new(|cx| Buffer::local("", cx));
 1915        let buffer = cx.new(|cx| MultiBuffer::singleton(buffer, cx));
 1916        Self::new(
 1917            EditorMode::AutoHeight {
 1918                min_lines,
 1919                max_lines: None,
 1920            },
 1921            buffer,
 1922            None,
 1923            window,
 1924            cx,
 1925        )
 1926    }
 1927
 1928    pub fn for_buffer(
 1929        buffer: Entity<Buffer>,
 1930        project: Option<Entity<Project>>,
 1931        window: &mut Window,
 1932        cx: &mut Context<Self>,
 1933    ) -> Self {
 1934        let buffer = cx.new(|cx| MultiBuffer::singleton(buffer, cx));
 1935        Self::new(EditorMode::full(), buffer, project, window, cx)
 1936    }
 1937
 1938    pub fn for_multibuffer(
 1939        buffer: Entity<MultiBuffer>,
 1940        project: Option<Entity<Project>>,
 1941        window: &mut Window,
 1942        cx: &mut Context<Self>,
 1943    ) -> Self {
 1944        Self::new(EditorMode::full(), buffer, project, window, cx)
 1945    }
 1946
 1947    pub fn clone(&self, window: &mut Window, cx: &mut Context<Self>) -> Self {
 1948        let mut clone = Self::new(
 1949            self.mode.clone(),
 1950            self.buffer.clone(),
 1951            self.project.clone(),
 1952            window,
 1953            cx,
 1954        );
 1955        let my_snapshot = self.display_map.update(cx, |display_map, cx| {
 1956            let snapshot = display_map.snapshot(cx);
 1957            clone.display_map.update(cx, |display_map, cx| {
 1958                display_map.set_state(&snapshot, cx);
 1959            });
 1960            snapshot
 1961        });
 1962        let clone_snapshot = clone.display_map.update(cx, |map, cx| map.snapshot(cx));
 1963        clone.folds_did_change(cx);
 1964        clone.selections.clone_state(&self.selections);
 1965        clone
 1966            .scroll_manager
 1967            .clone_state(&self.scroll_manager, &my_snapshot, &clone_snapshot, cx);
 1968        clone.searchable = self.searchable;
 1969        clone.read_only = self.read_only;
 1970        clone.buffers_with_disabled_indent_guides =
 1971            self.buffers_with_disabled_indent_guides.clone();
 1972        clone
 1973    }
 1974
 1975    pub fn new(
 1976        mode: EditorMode,
 1977        buffer: Entity<MultiBuffer>,
 1978        project: Option<Entity<Project>>,
 1979        window: &mut Window,
 1980        cx: &mut Context<Self>,
 1981    ) -> Self {
 1982        Editor::new_internal(mode, buffer, project, None, window, cx)
 1983    }
 1984
 1985    pub fn refresh_sticky_headers(
 1986        &mut self,
 1987        display_snapshot: &DisplaySnapshot,
 1988        cx: &mut Context<Editor>,
 1989    ) {
 1990        if !self.mode.is_full() {
 1991            return;
 1992        }
 1993        let multi_buffer = display_snapshot.buffer_snapshot();
 1994        let scroll_anchor = self
 1995            .scroll_manager
 1996            .native_anchor(display_snapshot, cx)
 1997            .anchor;
 1998        let Some((excerpt_id, _, buffer)) = multi_buffer.as_singleton() else {
 1999            return;
 2000        };
 2001        let buffer = buffer.clone();
 2002
 2003        let buffer_visible_start = scroll_anchor.text_anchor.to_point(&buffer);
 2004        let max_row = buffer.max_point().row;
 2005        let start_row = buffer_visible_start.row.min(max_row);
 2006        let end_row = (buffer_visible_start.row + 10).min(max_row);
 2007
 2008        let syntax = self.style(cx).syntax.clone();
 2009        let background_task = cx.background_spawn(async move {
 2010            buffer
 2011                .outline_items_containing(
 2012                    Point::new(start_row, 0)..Point::new(end_row, 0),
 2013                    true,
 2014                    Some(syntax.as_ref()),
 2015                )
 2016                .into_iter()
 2017                .map(|outline_item| OutlineItem {
 2018                    depth: outline_item.depth,
 2019                    range: Anchor::range_in_buffer(excerpt_id, outline_item.range),
 2020                    source_range_for_text: Anchor::range_in_buffer(
 2021                        excerpt_id,
 2022                        outline_item.source_range_for_text,
 2023                    ),
 2024                    text: outline_item.text,
 2025                    highlight_ranges: outline_item.highlight_ranges,
 2026                    name_ranges: outline_item.name_ranges,
 2027                    body_range: outline_item
 2028                        .body_range
 2029                        .map(|range| Anchor::range_in_buffer(excerpt_id, range)),
 2030                    annotation_range: outline_item
 2031                        .annotation_range
 2032                        .map(|range| Anchor::range_in_buffer(excerpt_id, range)),
 2033                })
 2034                .collect()
 2035        });
 2036        self.sticky_headers_task = cx.spawn(async move |this, cx| {
 2037            let sticky_headers = background_task.await;
 2038            this.update(cx, |this, cx| {
 2039                this.sticky_headers = Some(sticky_headers);
 2040                cx.notify();
 2041            })
 2042            .ok();
 2043        });
 2044    }
 2045
 2046    fn new_internal(
 2047        mode: EditorMode,
 2048        multi_buffer: Entity<MultiBuffer>,
 2049        project: Option<Entity<Project>>,
 2050        display_map: Option<Entity<DisplayMap>>,
 2051        window: &mut Window,
 2052        cx: &mut Context<Self>,
 2053    ) -> Self {
 2054        debug_assert!(
 2055            display_map.is_none() || mode.is_minimap(),
 2056            "Providing a display map for a new editor is only intended for the minimap and might have unintended side effects otherwise!"
 2057        );
 2058
 2059        let full_mode = mode.is_full();
 2060        let is_minimap = mode.is_minimap();
 2061        let diagnostics_max_severity = if full_mode {
 2062            EditorSettings::get_global(cx)
 2063                .diagnostics_max_severity
 2064                .unwrap_or(DiagnosticSeverity::Hint)
 2065        } else {
 2066            DiagnosticSeverity::Off
 2067        };
 2068        let style = window.text_style();
 2069        let font_size = style.font_size.to_pixels(window.rem_size());
 2070        let editor = cx.entity().downgrade();
 2071        let fold_placeholder = FoldPlaceholder {
 2072            constrain_width: false,
 2073            render: Arc::new(move |fold_id, fold_range, cx| {
 2074                let editor = editor.clone();
 2075                FoldPlaceholder::fold_element(fold_id, cx)
 2076                    .cursor_pointer()
 2077                    .child("")
 2078                    .on_mouse_down(MouseButton::Left, |_, _, cx| cx.stop_propagation())
 2079                    .on_click(move |_, _window, cx| {
 2080                        editor
 2081                            .update(cx, |editor, cx| {
 2082                                editor.unfold_ranges(
 2083                                    &[fold_range.start..fold_range.end],
 2084                                    true,
 2085                                    false,
 2086                                    cx,
 2087                                );
 2088                                cx.stop_propagation();
 2089                            })
 2090                            .ok();
 2091                    })
 2092                    .into_any()
 2093            }),
 2094            merge_adjacent: true,
 2095            ..FoldPlaceholder::default()
 2096        };
 2097        let display_map = display_map.unwrap_or_else(|| {
 2098            cx.new(|cx| {
 2099                DisplayMap::new(
 2100                    multi_buffer.clone(),
 2101                    style.font(),
 2102                    font_size,
 2103                    None,
 2104                    FILE_HEADER_HEIGHT,
 2105                    MULTI_BUFFER_EXCERPT_HEADER_HEIGHT,
 2106                    fold_placeholder,
 2107                    diagnostics_max_severity,
 2108                    cx,
 2109                )
 2110            })
 2111        });
 2112
 2113        let selections = SelectionsCollection::new();
 2114
 2115        let blink_manager = cx.new(|cx| {
 2116            let mut blink_manager = BlinkManager::new(
 2117                CURSOR_BLINK_INTERVAL,
 2118                |cx| EditorSettings::get_global(cx).cursor_blink,
 2119                cx,
 2120            );
 2121            if is_minimap {
 2122                blink_manager.disable(cx);
 2123            }
 2124            blink_manager
 2125        });
 2126
 2127        let soft_wrap_mode_override =
 2128            matches!(mode, EditorMode::SingleLine).then(|| language_settings::SoftWrap::None);
 2129
 2130        let mut project_subscriptions = Vec::new();
 2131        if full_mode && let Some(project) = project.as_ref() {
 2132            project_subscriptions.push(cx.subscribe_in(
 2133                project,
 2134                window,
 2135                |editor, _, event, window, cx| match event {
 2136                    project::Event::RefreshCodeLens => {
 2137                        // we always query lens with actions, without storing them, always refreshing them
 2138                    }
 2139                    project::Event::RefreshInlayHints {
 2140                        server_id,
 2141                        request_id,
 2142                    } => {
 2143                        editor.refresh_inlay_hints(
 2144                            InlayHintRefreshReason::RefreshRequested {
 2145                                server_id: *server_id,
 2146                                request_id: *request_id,
 2147                            },
 2148                            cx,
 2149                        );
 2150                    }
 2151                    project::Event::RefreshSemanticTokens {
 2152                        server_id,
 2153                        request_id,
 2154                    } => {
 2155                        editor.refresh_semantic_tokens(
 2156                            None,
 2157                            Some(RefreshForServer {
 2158                                server_id: *server_id,
 2159                                request_id: *request_id,
 2160                            }),
 2161                            cx,
 2162                        );
 2163                    }
 2164                    project::Event::LanguageServerRemoved(_) => {
 2165                        editor.registered_buffers.clear();
 2166                        editor.register_visible_buffers(cx);
 2167                        editor.invalidate_semantic_tokens(None);
 2168                        editor.refresh_runnables(None, window, cx);
 2169                        editor.update_lsp_data(None, window, cx);
 2170                        editor.refresh_inlay_hints(InlayHintRefreshReason::ServerRemoved, cx);
 2171                    }
 2172                    project::Event::SnippetEdit(id, snippet_edits) => {
 2173                        // todo(lw): Non singletons
 2174                        if let Some(buffer) = editor.buffer.read(cx).as_singleton() {
 2175                            let snapshot = buffer.read(cx).snapshot();
 2176                            let focus_handle = editor.focus_handle(cx);
 2177                            if snapshot.remote_id() == *id && focus_handle.is_focused(window) {
 2178                                for (range, snippet) in snippet_edits {
 2179                                    let buffer_range =
 2180                                        language::range_from_lsp(*range).to_offset(&snapshot);
 2181                                    editor
 2182                                        .insert_snippet(
 2183                                            &[MultiBufferOffset(buffer_range.start)
 2184                                                ..MultiBufferOffset(buffer_range.end)],
 2185                                            snippet.clone(),
 2186                                            window,
 2187                                            cx,
 2188                                        )
 2189                                        .ok();
 2190                                }
 2191                            }
 2192                        }
 2193                    }
 2194                    project::Event::LanguageServerBufferRegistered { buffer_id, .. } => {
 2195                        let buffer_id = *buffer_id;
 2196                        if editor.buffer().read(cx).buffer(buffer_id).is_some() {
 2197                            editor.register_buffer(buffer_id, cx);
 2198                            editor.refresh_runnables(Some(buffer_id), window, cx);
 2199                            editor.update_lsp_data(Some(buffer_id), window, cx);
 2200                            editor.refresh_inlay_hints(InlayHintRefreshReason::NewLinesShown, cx);
 2201                            refresh_linked_ranges(editor, window, cx);
 2202                            editor.refresh_code_actions(window, cx);
 2203                            editor.refresh_document_highlights(cx);
 2204                        }
 2205                    }
 2206
 2207                    project::Event::EntryRenamed(transaction, project_path, abs_path) => {
 2208                        let Some(workspace) = editor.workspace() else {
 2209                            return;
 2210                        };
 2211                        let Some(active_editor) = workspace.read(cx).active_item_as::<Self>(cx)
 2212                        else {
 2213                            return;
 2214                        };
 2215
 2216                        if active_editor.entity_id() == cx.entity_id() {
 2217                            let entity_id = cx.entity_id();
 2218                            workspace.update(cx, |this, cx| {
 2219                                this.panes_mut()
 2220                                    .iter_mut()
 2221                                    .filter(|pane| pane.entity_id() != entity_id)
 2222                                    .for_each(|p| {
 2223                                        p.update(cx, |pane, _| {
 2224                                            pane.nav_history_mut().rename_item(
 2225                                                entity_id,
 2226                                                project_path.clone(),
 2227                                                abs_path.clone().into(),
 2228                                            );
 2229                                        })
 2230                                    });
 2231                            });
 2232
 2233                            Self::open_transaction_for_hidden_buffers(
 2234                                workspace,
 2235                                transaction.clone(),
 2236                                "Rename".to_string(),
 2237                                window,
 2238                                cx,
 2239                            );
 2240                        }
 2241                    }
 2242
 2243                    project::Event::WorkspaceEditApplied(transaction) => {
 2244                        let Some(workspace) = editor.workspace() else {
 2245                            return;
 2246                        };
 2247                        let Some(active_editor) = workspace.read(cx).active_item_as::<Self>(cx)
 2248                        else {
 2249                            return;
 2250                        };
 2251
 2252                        if active_editor.entity_id() == cx.entity_id() {
 2253                            Self::open_transaction_for_hidden_buffers(
 2254                                workspace,
 2255                                transaction.clone(),
 2256                                "LSP Edit".to_string(),
 2257                                window,
 2258                                cx,
 2259                            );
 2260                        }
 2261                    }
 2262
 2263                    _ => {}
 2264                },
 2265            ));
 2266            if let Some(task_inventory) = project
 2267                .read(cx)
 2268                .task_store()
 2269                .read(cx)
 2270                .task_inventory()
 2271                .cloned()
 2272            {
 2273                project_subscriptions.push(cx.observe_in(
 2274                    &task_inventory,
 2275                    window,
 2276                    |editor, _, window, cx| {
 2277                        editor.refresh_runnables(None, window, cx);
 2278                    },
 2279                ));
 2280            };
 2281
 2282            project_subscriptions.push(cx.subscribe_in(
 2283                &project.read(cx).breakpoint_store(),
 2284                window,
 2285                |editor, _, event, window, cx| match event {
 2286                    BreakpointStoreEvent::ClearDebugLines => {
 2287                        editor.clear_row_highlights::<ActiveDebugLine>();
 2288                        editor.refresh_inline_values(cx);
 2289                    }
 2290                    BreakpointStoreEvent::SetDebugLine => {
 2291                        if editor.go_to_active_debug_line(window, cx) {
 2292                            cx.stop_propagation();
 2293                        }
 2294
 2295                        editor.refresh_inline_values(cx);
 2296                    }
 2297                    _ => {}
 2298                },
 2299            ));
 2300            let git_store = project.read(cx).git_store().clone();
 2301            let project = project.clone();
 2302            project_subscriptions.push(cx.subscribe(&git_store, move |this, _, event, cx| {
 2303                if let GitStoreEvent::RepositoryAdded = event {
 2304                    this.load_diff_task = Some(
 2305                        update_uncommitted_diff_for_buffer(
 2306                            cx.entity(),
 2307                            &project,
 2308                            this.buffer.read(cx).all_buffers(),
 2309                            this.buffer.clone(),
 2310                            cx,
 2311                        )
 2312                        .shared(),
 2313                    );
 2314                }
 2315            }));
 2316        }
 2317
 2318        let buffer_snapshot = multi_buffer.read(cx).snapshot(cx);
 2319
 2320        let inlay_hint_settings =
 2321            inlay_hint_settings(selections.newest_anchor().head(), &buffer_snapshot, cx);
 2322        let focus_handle = cx.focus_handle();
 2323        if !is_minimap {
 2324            cx.on_focus(&focus_handle, window, Self::handle_focus)
 2325                .detach();
 2326            cx.on_focus_in(&focus_handle, window, Self::handle_focus_in)
 2327                .detach();
 2328            cx.on_focus_out(&focus_handle, window, Self::handle_focus_out)
 2329                .detach();
 2330            cx.on_blur(&focus_handle, window, Self::handle_blur)
 2331                .detach();
 2332            cx.observe_pending_input(window, Self::observe_pending_input)
 2333                .detach();
 2334        }
 2335
 2336        let show_indent_guides =
 2337            if matches!(mode, EditorMode::SingleLine | EditorMode::Minimap { .. }) {
 2338                Some(false)
 2339            } else {
 2340                None
 2341            };
 2342
 2343        let breakpoint_store = match (&mode, project.as_ref()) {
 2344            (EditorMode::Full { .. }, Some(project)) => Some(project.read(cx).breakpoint_store()),
 2345            _ => None,
 2346        };
 2347
 2348        let mut code_action_providers = Vec::new();
 2349        let mut load_uncommitted_diff = None;
 2350        if let Some(project) = project.clone() {
 2351            load_uncommitted_diff = Some(
 2352                update_uncommitted_diff_for_buffer(
 2353                    cx.entity(),
 2354                    &project,
 2355                    multi_buffer.read(cx).all_buffers(),
 2356                    multi_buffer.clone(),
 2357                    cx,
 2358                )
 2359                .shared(),
 2360            );
 2361            code_action_providers.push(Rc::new(project) as Rc<_>);
 2362        }
 2363
 2364        let mut editor = Self {
 2365            focus_handle,
 2366            show_cursor_when_unfocused: false,
 2367            last_focused_descendant: None,
 2368            buffer: multi_buffer.clone(),
 2369            display_map: display_map.clone(),
 2370            placeholder_display_map: None,
 2371            selections,
 2372            scroll_manager: ScrollManager::new(cx),
 2373            columnar_selection_state: None,
 2374            add_selections_state: None,
 2375            select_next_state: None,
 2376            select_prev_state: None,
 2377            selection_history: SelectionHistory::default(),
 2378            defer_selection_effects: false,
 2379            deferred_selection_effects_state: None,
 2380            autoclose_regions: Vec::new(),
 2381            snippet_stack: InvalidationStack::default(),
 2382            select_syntax_node_history: SelectSyntaxNodeHistory::default(),
 2383            ime_transaction: None,
 2384            active_diagnostics: ActiveDiagnostic::None,
 2385            show_inline_diagnostics: ProjectSettings::get_global(cx).diagnostics.inline.enabled,
 2386            inline_diagnostics_update: Task::ready(()),
 2387            inline_diagnostics: Vec::new(),
 2388            soft_wrap_mode_override,
 2389            diagnostics_max_severity,
 2390            hard_wrap: None,
 2391            completion_provider: project.clone().map(|project| Rc::new(project) as _),
 2392            semantics_provider: project
 2393                .as_ref()
 2394                .map(|project| Rc::new(project.downgrade()) as _),
 2395            collaboration_hub: project.clone().map(|project| Box::new(project) as _),
 2396            project,
 2397            blink_manager: blink_manager.clone(),
 2398            show_local_selections: true,
 2399            show_scrollbars: ScrollbarAxes {
 2400                horizontal: full_mode,
 2401                vertical: full_mode,
 2402            },
 2403            minimap_visibility: MinimapVisibility::for_mode(&mode, cx),
 2404            offset_content: !matches!(mode, EditorMode::SingleLine),
 2405            show_breadcrumbs: EditorSettings::get_global(cx).toolbar.breadcrumbs,
 2406            show_gutter: full_mode,
 2407            show_line_numbers: (!full_mode).then_some(false),
 2408            use_relative_line_numbers: None,
 2409            disable_expand_excerpt_buttons: !full_mode,
 2410            delegate_expand_excerpts: false,
 2411            delegate_stage_and_restore: false,
 2412            delegate_open_excerpts: false,
 2413            enable_lsp_data: true,
 2414            enable_runnables: true,
 2415            show_git_diff_gutter: None,
 2416            show_code_actions: None,
 2417            show_runnables: None,
 2418            show_breakpoints: None,
 2419            show_diff_review_button: false,
 2420            show_wrap_guides: None,
 2421            show_indent_guides,
 2422            buffers_with_disabled_indent_guides: HashSet::default(),
 2423            highlight_order: 0,
 2424            highlighted_rows: HashMap::default(),
 2425            background_highlights: HashMap::default(),
 2426            gutter_highlights: HashMap::default(),
 2427            scrollbar_marker_state: ScrollbarMarkerState::default(),
 2428            active_indent_guides_state: ActiveIndentGuidesState::default(),
 2429            nav_history: None,
 2430            context_menu: RefCell::new(None),
 2431            context_menu_options: None,
 2432            mouse_context_menu: None,
 2433            completion_tasks: Vec::new(),
 2434            inline_blame_popover: None,
 2435            inline_blame_popover_show_task: None,
 2436            signature_help_state: SignatureHelpState::default(),
 2437            auto_signature_help: None,
 2438            find_all_references_task_sources: Vec::new(),
 2439            next_completion_id: 0,
 2440            next_inlay_id: 0,
 2441            code_action_providers,
 2442            available_code_actions: None,
 2443            code_actions_task: None,
 2444            quick_selection_highlight_task: None,
 2445            debounced_selection_highlight_task: None,
 2446            debounced_selection_highlight_complete: false,
 2447            document_highlights_task: None,
 2448            linked_editing_range_task: None,
 2449            pending_rename: None,
 2450            searchable: !is_minimap,
 2451            cursor_shape: EditorSettings::get_global(cx)
 2452                .cursor_shape
 2453                .unwrap_or_default(),
 2454            cursor_offset_on_selection: false,
 2455            current_line_highlight: None,
 2456            autoindent_mode: Some(AutoindentMode::EachLine),
 2457            collapse_matches: false,
 2458            workspace: None,
 2459            input_enabled: !is_minimap,
 2460            expects_character_input: !is_minimap,
 2461            use_modal_editing: full_mode,
 2462            read_only: is_minimap,
 2463            use_autoclose: true,
 2464            use_auto_surround: true,
 2465            auto_replace_emoji_shortcode: false,
 2466            jsx_tag_auto_close_enabled_in_any_buffer: false,
 2467            leader_id: None,
 2468            remote_id: None,
 2469            hover_state: HoverState::default(),
 2470            pending_mouse_down: None,
 2471            prev_pressure_stage: None,
 2472            hovered_link_state: None,
 2473            edit_prediction_provider: None,
 2474            active_edit_prediction: None,
 2475            stale_edit_prediction_in_menu: None,
 2476            edit_prediction_preview: EditPredictionPreview::Inactive {
 2477                released_too_fast: false,
 2478            },
 2479            inline_diagnostics_enabled: full_mode,
 2480            diagnostics_enabled: full_mode,
 2481            word_completions_enabled: full_mode,
 2482            inline_value_cache: InlineValueCache::new(inlay_hint_settings.show_value_hints),
 2483            gutter_hovered: false,
 2484            pixel_position_of_newest_cursor: None,
 2485            last_bounds: None,
 2486            last_position_map: None,
 2487            expect_bounds_change: None,
 2488            gutter_dimensions: GutterDimensions::default(),
 2489            style: None,
 2490            show_cursor_names: false,
 2491            hovered_cursors: HashMap::default(),
 2492            next_editor_action_id: EditorActionId::default(),
 2493            editor_actions: Rc::default(),
 2494            edit_predictions_hidden_for_vim_mode: false,
 2495            show_edit_predictions_override: None,
 2496            show_completions_on_input_override: None,
 2497            menu_edit_predictions_policy: MenuEditPredictionsPolicy::ByProvider,
 2498            edit_prediction_settings: EditPredictionSettings::Disabled,
 2499            in_leading_whitespace: false,
 2500            custom_context_menu: None,
 2501            show_git_blame_gutter: false,
 2502            show_git_blame_inline: false,
 2503            show_selection_menu: None,
 2504            show_git_blame_inline_delay_task: None,
 2505            git_blame_inline_enabled: full_mode
 2506                && ProjectSettings::get_global(cx).git.inline_blame.enabled,
 2507            render_diff_hunk_controls: Arc::new(render_diff_hunk_controls),
 2508            buffer_serialization: is_minimap.not().then(|| {
 2509                BufferSerialization::new(
 2510                    ProjectSettings::get_global(cx)
 2511                        .session
 2512                        .restore_unsaved_buffers,
 2513                )
 2514            }),
 2515            blame: None,
 2516            blame_subscription: None,
 2517
 2518            breakpoint_store,
 2519            gutter_breakpoint_indicator: (None, None),
 2520            gutter_diff_review_indicator: (None, None),
 2521            diff_review_drag_state: None,
 2522            diff_review_overlays: Vec::new(),
 2523            stored_review_comments: Vec::new(),
 2524            next_review_comment_id: 0,
 2525            hovered_diff_hunk_row: None,
 2526            _subscriptions: (!is_minimap)
 2527                .then(|| {
 2528                    vec![
 2529                        cx.observe(&multi_buffer, Self::on_buffer_changed),
 2530                        cx.subscribe_in(&multi_buffer, window, Self::on_buffer_event),
 2531                        cx.observe_in(&display_map, window, Self::on_display_map_changed),
 2532                        cx.observe(&blink_manager, |_, _, cx| cx.notify()),
 2533                        cx.observe_global_in::<SettingsStore>(window, Self::settings_changed),
 2534                        cx.observe_global_in::<GlobalTheme>(window, Self::theme_changed),
 2535                        observe_buffer_font_size_adjustment(cx, |_, cx| cx.notify()),
 2536                        cx.observe_window_activation(window, |editor, window, cx| {
 2537                            let active = window.is_window_active();
 2538                            editor.blink_manager.update(cx, |blink_manager, cx| {
 2539                                if active {
 2540                                    blink_manager.enable(cx);
 2541                                } else {
 2542                                    blink_manager.disable(cx);
 2543                                }
 2544                            });
 2545                            if active {
 2546                                editor.show_mouse_cursor(cx);
 2547                            }
 2548                        }),
 2549                    ]
 2550                })
 2551                .unwrap_or_default(),
 2552            runnables: RunnableData::new(),
 2553            pull_diagnostics_task: Task::ready(()),
 2554            colors: None,
 2555            refresh_colors_task: Task::ready(()),
 2556            use_document_folding_ranges: false,
 2557            refresh_folding_ranges_task: Task::ready(()),
 2558            inlay_hints: None,
 2559            next_color_inlay_id: 0,
 2560            post_scroll_update: Task::ready(()),
 2561            linked_edit_ranges: Default::default(),
 2562            in_project_search: false,
 2563            previous_search_ranges: None,
 2564            breadcrumb_header: None,
 2565            focused_block: None,
 2566            next_scroll_position: NextScrollCursorCenterTopBottom::default(),
 2567            addons: HashMap::default(),
 2568            registered_buffers: HashMap::default(),
 2569            _scroll_cursor_center_top_bottom_task: Task::ready(()),
 2570            selection_mark_mode: false,
 2571            toggle_fold_multiple_buffers: Task::ready(()),
 2572            serialize_selections: Task::ready(()),
 2573            serialize_folds: Task::ready(()),
 2574            text_style_refinement: None,
 2575            load_diff_task: load_uncommitted_diff,
 2576            temporary_diff_override: false,
 2577            mouse_cursor_hidden: false,
 2578            minimap: None,
 2579            hide_mouse_mode: EditorSettings::get_global(cx)
 2580                .hide_mouse
 2581                .unwrap_or_default(),
 2582            change_list: ChangeList::new(),
 2583            mode,
 2584            selection_drag_state: SelectionDragState::None,
 2585            folding_newlines: Task::ready(()),
 2586            lookup_key: None,
 2587            select_next_is_case_sensitive: None,
 2588            on_local_selections_changed: None,
 2589            suppress_selection_callback: false,
 2590            applicable_language_settings: HashMap::default(),
 2591            semantic_token_state: SemanticTokenState::new(cx, full_mode),
 2592            accent_data: None,
 2593            bracket_fetched_tree_sitter_chunks: HashMap::default(),
 2594            number_deleted_lines: false,
 2595            refresh_matching_bracket_highlights_task: Task::ready(()),
 2596            refresh_document_symbols_task: Task::ready(()).shared(),
 2597            lsp_document_symbols: HashMap::default(),
 2598            refresh_outline_symbols_at_cursor_at_cursor_task: Task::ready(()),
 2599            outline_symbols_at_cursor: None,
 2600            sticky_headers_task: Task::ready(()),
 2601            sticky_headers: None,
 2602            colorize_brackets_task: Task::ready(()),
 2603        };
 2604
 2605        if is_minimap {
 2606            return editor;
 2607        }
 2608
 2609        editor.applicable_language_settings = editor.fetch_applicable_language_settings(cx);
 2610        editor.accent_data = editor.fetch_accent_data(cx);
 2611
 2612        if let Some(breakpoints) = editor.breakpoint_store.as_ref() {
 2613            editor
 2614                ._subscriptions
 2615                .push(cx.observe(breakpoints, |_, _, cx| {
 2616                    cx.notify();
 2617                }));
 2618        }
 2619        editor._subscriptions.extend(project_subscriptions);
 2620
 2621        editor._subscriptions.push(cx.subscribe_in(
 2622            &cx.entity(),
 2623            window,
 2624            |editor, _, e: &EditorEvent, window, cx| match e {
 2625                EditorEvent::ScrollPositionChanged { local, .. } => {
 2626                    if *local {
 2627                        editor.hide_signature_help(cx, SignatureHelpHiddenBy::Escape);
 2628                        editor.inline_blame_popover.take();
 2629                        let snapshot = editor.snapshot(window, cx);
 2630                        let new_anchor = editor
 2631                            .scroll_manager
 2632                            .native_anchor(&snapshot.display_snapshot, cx);
 2633                        editor.update_restoration_data(cx, move |data| {
 2634                            data.scroll_position = (
 2635                                new_anchor.top_row(snapshot.buffer_snapshot()),
 2636                                new_anchor.offset,
 2637                            );
 2638                        });
 2639
 2640                        editor.post_scroll_update = cx.spawn_in(window, async move |editor, cx| {
 2641                            cx.background_executor()
 2642                                .timer(Duration::from_millis(50))
 2643                                .await;
 2644                            editor
 2645                                .update_in(cx, |editor, window, cx| {
 2646                                    editor.update_data_on_scroll(window, cx)
 2647                                })
 2648                                .ok();
 2649                        });
 2650                    }
 2651                    editor.refresh_sticky_headers(&editor.snapshot(window, cx), cx);
 2652                }
 2653                EditorEvent::Edited { .. } => {
 2654                    let vim_mode = vim_mode_setting::VimModeSetting::try_get(cx)
 2655                        .map(|vim_mode| vim_mode.0)
 2656                        .unwrap_or(false);
 2657                    if !vim_mode {
 2658                        let display_map = editor.display_snapshot(cx);
 2659                        let selections = editor.selections.all_adjusted_display(&display_map);
 2660                        let pop_state = editor
 2661                            .change_list
 2662                            .last()
 2663                            .map(|previous| {
 2664                                previous.len() == selections.len()
 2665                                    && previous.iter().enumerate().all(|(ix, p)| {
 2666                                        p.to_display_point(&display_map).row()
 2667                                            == selections[ix].head().row()
 2668                                    })
 2669                            })
 2670                            .unwrap_or(false);
 2671                        let new_positions = selections
 2672                            .into_iter()
 2673                            .map(|s| display_map.display_point_to_anchor(s.head(), Bias::Left))
 2674                            .collect();
 2675                        editor
 2676                            .change_list
 2677                            .push_to_change_list(pop_state, new_positions);
 2678                    }
 2679                }
 2680                _ => (),
 2681            },
 2682        ));
 2683
 2684        if let Some(dap_store) = editor
 2685            .project
 2686            .as_ref()
 2687            .map(|project| project.read(cx).dap_store())
 2688        {
 2689            let weak_editor = cx.weak_entity();
 2690
 2691            editor
 2692                ._subscriptions
 2693                .push(
 2694                    cx.observe_new::<project::debugger::session::Session>(move |_, _, cx| {
 2695                        let session_entity = cx.entity();
 2696                        weak_editor
 2697                            .update(cx, |editor, cx| {
 2698                                editor._subscriptions.push(
 2699                                    cx.subscribe(&session_entity, Self::on_debug_session_event),
 2700                                );
 2701                            })
 2702                            .ok();
 2703                    }),
 2704                );
 2705
 2706            for session in dap_store.read(cx).sessions().cloned().collect::<Vec<_>>() {
 2707                editor
 2708                    ._subscriptions
 2709                    .push(cx.subscribe(&session, Self::on_debug_session_event));
 2710            }
 2711        }
 2712
 2713        // skip adding the initial selection to selection history
 2714        editor.selection_history.mode = SelectionHistoryMode::Skipping;
 2715        editor.end_selection(window, cx);
 2716        editor.selection_history.mode = SelectionHistoryMode::Normal;
 2717
 2718        editor.scroll_manager.show_scrollbars(window, cx);
 2719        jsx_tag_auto_close::refresh_enabled_in_any_buffer(&mut editor, &multi_buffer, cx);
 2720
 2721        if full_mode {
 2722            let should_auto_hide_scrollbars = cx.should_auto_hide_scrollbars();
 2723            cx.set_global(ScrollbarAutoHide(should_auto_hide_scrollbars));
 2724
 2725            if editor.git_blame_inline_enabled {
 2726                editor.start_git_blame_inline(false, window, cx);
 2727            }
 2728
 2729            editor.go_to_active_debug_line(window, cx);
 2730
 2731            editor.minimap =
 2732                editor.create_minimap(EditorSettings::get_global(cx).minimap, window, cx);
 2733            editor.colors = Some(LspColorData::new(cx));
 2734            editor.use_document_folding_ranges = true;
 2735            editor.inlay_hints = Some(LspInlayHintData::new(inlay_hint_settings));
 2736
 2737            if let Some(buffer) = multi_buffer.read(cx).as_singleton() {
 2738                editor.register_buffer(buffer.read(cx).remote_id(), cx);
 2739            }
 2740            editor.report_editor_event(ReportEditorEvent::EditorOpened, None, cx);
 2741        }
 2742
 2743        editor
 2744    }
 2745
 2746    pub fn display_snapshot(&self, cx: &mut App) -> DisplaySnapshot {
 2747        self.display_map.update(cx, |map, cx| map.snapshot(cx))
 2748    }
 2749
 2750    pub fn deploy_mouse_context_menu(
 2751        &mut self,
 2752        position: gpui::Point<Pixels>,
 2753        context_menu: Entity<ContextMenu>,
 2754        window: &mut Window,
 2755        cx: &mut Context<Self>,
 2756    ) {
 2757        self.mouse_context_menu = Some(MouseContextMenu::new(
 2758            self,
 2759            crate::mouse_context_menu::MenuPosition::PinnedToScreen(position),
 2760            context_menu,
 2761            window,
 2762            cx,
 2763        ));
 2764    }
 2765
 2766    pub fn mouse_menu_is_focused(&self, window: &Window, cx: &App) -> bool {
 2767        self.mouse_context_menu
 2768            .as_ref()
 2769            .is_some_and(|menu| menu.context_menu.focus_handle(cx).is_focused(window))
 2770    }
 2771
 2772    pub fn is_range_selected(&mut self, range: &Range<Anchor>, cx: &mut Context<Self>) -> bool {
 2773        if self
 2774            .selections
 2775            .pending_anchor()
 2776            .is_some_and(|pending_selection| {
 2777                let snapshot = self.buffer().read(cx).snapshot(cx);
 2778                pending_selection.range().includes(range, &snapshot)
 2779            })
 2780        {
 2781            return true;
 2782        }
 2783
 2784        self.selections
 2785            .disjoint_in_range::<MultiBufferOffset>(range.clone(), &self.display_snapshot(cx))
 2786            .into_iter()
 2787            .any(|selection| {
 2788                // This is needed to cover a corner case, if we just check for an existing
 2789                // selection in the fold range, having a cursor at the start of the fold
 2790                // marks it as selected. Non-empty selections don't cause this.
 2791                let length = selection.end - selection.start;
 2792                length > 0
 2793            })
 2794    }
 2795
 2796    pub fn key_context(&self, window: &mut Window, cx: &mut App) -> KeyContext {
 2797        self.key_context_internal(self.has_active_edit_prediction(), window, cx)
 2798    }
 2799
 2800    fn key_context_internal(
 2801        &self,
 2802        has_active_edit_prediction: bool,
 2803        window: &mut Window,
 2804        cx: &mut App,
 2805    ) -> KeyContext {
 2806        let mut key_context = KeyContext::new_with_defaults();
 2807        key_context.add("Editor");
 2808        let mode = match self.mode {
 2809            EditorMode::SingleLine => "single_line",
 2810            EditorMode::AutoHeight { .. } => "auto_height",
 2811            EditorMode::Minimap { .. } => "minimap",
 2812            EditorMode::Full { .. } => "full",
 2813        };
 2814
 2815        if EditorSettings::jupyter_enabled(cx) {
 2816            key_context.add("jupyter");
 2817        }
 2818
 2819        key_context.set("mode", mode);
 2820        if self.pending_rename.is_some() {
 2821            key_context.add("renaming");
 2822        }
 2823
 2824        if let Some(snippet_stack) = self.snippet_stack.last() {
 2825            key_context.add("in_snippet");
 2826
 2827            if snippet_stack.active_index > 0 {
 2828                key_context.add("has_previous_tabstop");
 2829            }
 2830
 2831            if snippet_stack.active_index < snippet_stack.ranges.len().saturating_sub(1) {
 2832                key_context.add("has_next_tabstop");
 2833            }
 2834        }
 2835
 2836        match self.context_menu.borrow().as_ref() {
 2837            Some(CodeContextMenu::Completions(menu)) => {
 2838                if menu.visible() {
 2839                    key_context.add("menu");
 2840                    key_context.add("showing_completions");
 2841                }
 2842            }
 2843            Some(CodeContextMenu::CodeActions(menu)) => {
 2844                if menu.visible() {
 2845                    key_context.add("menu");
 2846                    key_context.add("showing_code_actions")
 2847                }
 2848            }
 2849            None => {}
 2850        }
 2851
 2852        if self.signature_help_state.has_multiple_signatures() {
 2853            key_context.add("showing_signature_help");
 2854        }
 2855
 2856        // Disable vim contexts when a sub-editor (e.g. rename/inline assistant) is focused.
 2857        if !self.focus_handle(cx).contains_focused(window, cx)
 2858            || (self.is_focused(window) || self.mouse_menu_is_focused(window, cx))
 2859        {
 2860            for addon in self.addons.values() {
 2861                addon.extend_key_context(&mut key_context, cx)
 2862            }
 2863        }
 2864
 2865        if let Some(singleton_buffer) = self.buffer.read(cx).as_singleton() {
 2866            if let Some(extension) = singleton_buffer.read(cx).file().and_then(|file| {
 2867                Some(
 2868                    file.full_path(cx)
 2869                        .extension()?
 2870                        .to_string_lossy()
 2871                        .to_lowercase(),
 2872                )
 2873            }) {
 2874                key_context.set("extension", extension);
 2875            }
 2876        } else {
 2877            key_context.add("multibuffer");
 2878        }
 2879
 2880        if has_active_edit_prediction {
 2881            key_context.add(EDIT_PREDICTION_KEY_CONTEXT);
 2882            key_context.add("copilot_suggestion");
 2883        }
 2884
 2885        if self.in_leading_whitespace {
 2886            key_context.add("in_leading_whitespace");
 2887        }
 2888        if self.edit_prediction_requires_modifier() {
 2889            key_context.set("edit_prediction_mode", "subtle")
 2890        } else {
 2891            key_context.set("edit_prediction_mode", "eager");
 2892        }
 2893
 2894        if self.selection_mark_mode {
 2895            key_context.add("selection_mode");
 2896        }
 2897
 2898        let disjoint = self.selections.disjoint_anchors();
 2899        if matches!(
 2900            &self.mode,
 2901            EditorMode::SingleLine | EditorMode::AutoHeight { .. }
 2902        ) && let [selection] = disjoint
 2903            && selection.start == selection.end
 2904        {
 2905            let snapshot = self.snapshot(window, cx);
 2906            let snapshot = snapshot.buffer_snapshot();
 2907            let caret_offset = selection.end.to_offset(snapshot);
 2908
 2909            if caret_offset == MultiBufferOffset(0) {
 2910                key_context.add("start_of_input");
 2911            }
 2912
 2913            if caret_offset == snapshot.len() {
 2914                key_context.add("end_of_input");
 2915            }
 2916        }
 2917
 2918        if self.has_any_expanded_diff_hunks(cx) {
 2919            key_context.add("diffs_expanded");
 2920        }
 2921
 2922        key_context
 2923    }
 2924
 2925    pub fn last_bounds(&self) -> Option<&Bounds<Pixels>> {
 2926        self.last_bounds.as_ref()
 2927    }
 2928
 2929    fn show_mouse_cursor(&mut self, cx: &mut Context<Self>) {
 2930        if self.mouse_cursor_hidden {
 2931            self.mouse_cursor_hidden = false;
 2932            cx.notify();
 2933        }
 2934    }
 2935
 2936    pub fn hide_mouse_cursor(&mut self, origin: HideMouseCursorOrigin, cx: &mut Context<Self>) {
 2937        let hide_mouse_cursor = match origin {
 2938            HideMouseCursorOrigin::TypingAction => {
 2939                matches!(
 2940                    self.hide_mouse_mode,
 2941                    HideMouseMode::OnTyping | HideMouseMode::OnTypingAndMovement
 2942                )
 2943            }
 2944            HideMouseCursorOrigin::MovementAction => {
 2945                matches!(self.hide_mouse_mode, HideMouseMode::OnTypingAndMovement)
 2946            }
 2947        };
 2948        if self.mouse_cursor_hidden != hide_mouse_cursor {
 2949            self.mouse_cursor_hidden = hide_mouse_cursor;
 2950            cx.notify();
 2951        }
 2952    }
 2953
 2954    fn accept_edit_prediction_keystroke(
 2955        &self,
 2956        granularity: EditPredictionGranularity,
 2957        window: &mut Window,
 2958        cx: &mut App,
 2959    ) -> Option<gpui::KeybindingKeystroke> {
 2960        let key_context = self.key_context_internal(true, window, cx);
 2961
 2962        let bindings =
 2963            match granularity {
 2964                EditPredictionGranularity::Word => window
 2965                    .bindings_for_action_in_context(&AcceptNextWordEditPrediction, key_context),
 2966                EditPredictionGranularity::Line => window
 2967                    .bindings_for_action_in_context(&AcceptNextLineEditPrediction, key_context),
 2968                EditPredictionGranularity::Full => {
 2969                    window.bindings_for_action_in_context(&AcceptEditPrediction, key_context)
 2970                }
 2971            };
 2972
 2973        bindings
 2974            .into_iter()
 2975            .rev()
 2976            .find_map(|binding| match binding.keystrokes() {
 2977                [keystroke, ..] => Some(keystroke.clone()),
 2978                _ => None,
 2979            })
 2980    }
 2981
 2982    fn preview_edit_prediction_keystroke(
 2983        &self,
 2984        window: &mut Window,
 2985        cx: &mut App,
 2986    ) -> Option<gpui::KeybindingKeystroke> {
 2987        let key_context = self.key_context_internal(true, window, cx);
 2988        let bindings = window.bindings_for_action_in_context(&AcceptEditPrediction, key_context);
 2989        bindings
 2990            .into_iter()
 2991            .rev()
 2992            .find_map(|binding| match binding.keystrokes() {
 2993                [keystroke, ..] if keystroke.modifiers().modified() => Some(keystroke.clone()),
 2994                _ => None,
 2995            })
 2996    }
 2997
 2998    fn edit_prediction_preview_modifiers_held(
 2999        &self,
 3000        modifiers: &Modifiers,
 3001        window: &mut Window,
 3002        cx: &mut App,
 3003    ) -> bool {
 3004        let key_context = self.key_context_internal(true, window, cx);
 3005        let actions: [&dyn Action; 3] = [
 3006            &AcceptEditPrediction,
 3007            &AcceptNextWordEditPrediction,
 3008            &AcceptNextLineEditPrediction,
 3009        ];
 3010
 3011        actions.into_iter().any(|action| {
 3012            window
 3013                .bindings_for_action_in_context(action, key_context.clone())
 3014                .into_iter()
 3015                .rev()
 3016                .any(|binding| {
 3017                    binding.keystrokes().first().is_some_and(|keystroke| {
 3018                        keystroke.modifiers().modified() && keystroke.modifiers() == modifiers
 3019                    })
 3020                })
 3021        })
 3022    }
 3023
 3024    fn edit_prediction_cursor_popover_prefers_preview(
 3025        &self,
 3026        completion: &EditPredictionState,
 3027    ) -> bool {
 3028        match &completion.completion {
 3029            EditPrediction::Edit {
 3030                edits, snapshot, ..
 3031            } => {
 3032                let mut start_row: Option<u32> = None;
 3033                let mut end_row: Option<u32> = None;
 3034
 3035                for (range, text) in edits {
 3036                    let edit_start_row = range.start.text_anchor.to_point(snapshot).row;
 3037                    let old_end_row = range.end.text_anchor.to_point(snapshot).row;
 3038                    let inserted_newline_count = text
 3039                        .as_ref()
 3040                        .chars()
 3041                        .filter(|character| *character == '\n')
 3042                        .count() as u32;
 3043                    let deleted_newline_count = old_end_row - edit_start_row;
 3044                    let preview_end_row = edit_start_row + inserted_newline_count;
 3045
 3046                    start_row =
 3047                        Some(start_row.map_or(edit_start_row, |row| row.min(edit_start_row)));
 3048                    end_row = Some(end_row.map_or(preview_end_row, |row| row.max(preview_end_row)));
 3049
 3050                    if deleted_newline_count > 1 {
 3051                        end_row = Some(end_row.map_or(old_end_row, |row| row.max(old_end_row)));
 3052                    }
 3053                }
 3054
 3055                start_row
 3056                    .zip(end_row)
 3057                    .is_some_and(|(start_row, end_row)| end_row > start_row)
 3058            }
 3059            EditPrediction::MoveWithin { .. } | EditPrediction::MoveOutside { .. } => false,
 3060        }
 3061    }
 3062
 3063    fn edit_prediction_keybind_display(
 3064        &self,
 3065        surface: EditPredictionKeybindSurface,
 3066        window: &mut Window,
 3067        cx: &mut App,
 3068    ) -> EditPredictionKeybindDisplay {
 3069        let accept_keystroke =
 3070            self.accept_edit_prediction_keystroke(EditPredictionGranularity::Full, window, cx);
 3071        let preview_keystroke = self.preview_edit_prediction_keystroke(window, cx);
 3072
 3073        let action = match surface {
 3074            EditPredictionKeybindSurface::Inline
 3075            | EditPredictionKeybindSurface::CursorPopoverCompact => {
 3076                if self.edit_prediction_requires_modifier() {
 3077                    EditPredictionKeybindAction::Preview
 3078                } else {
 3079                    EditPredictionKeybindAction::Accept
 3080                }
 3081            }
 3082            EditPredictionKeybindSurface::CursorPopoverExpanded => self
 3083                .active_edit_prediction
 3084                .as_ref()
 3085                .filter(|completion| {
 3086                    self.edit_prediction_cursor_popover_prefers_preview(completion)
 3087                })
 3088                .map_or(EditPredictionKeybindAction::Accept, |_| {
 3089                    EditPredictionKeybindAction::Preview
 3090                }),
 3091        };
 3092        #[cfg(test)]
 3093        let preview_copy = preview_keystroke.clone();
 3094        #[cfg(test)]
 3095        let accept_copy = accept_keystroke.clone();
 3096
 3097        let displayed_keystroke = match surface {
 3098            EditPredictionKeybindSurface::Inline => match action {
 3099                EditPredictionKeybindAction::Accept => accept_keystroke,
 3100                EditPredictionKeybindAction::Preview => preview_keystroke,
 3101            },
 3102            EditPredictionKeybindSurface::CursorPopoverCompact
 3103            | EditPredictionKeybindSurface::CursorPopoverExpanded => match action {
 3104                EditPredictionKeybindAction::Accept => accept_keystroke,
 3105                EditPredictionKeybindAction::Preview => {
 3106                    preview_keystroke.or_else(|| accept_keystroke.clone())
 3107                }
 3108            },
 3109        };
 3110
 3111        let missing_accept_keystroke = displayed_keystroke.is_none();
 3112
 3113        EditPredictionKeybindDisplay {
 3114            #[cfg(test)]
 3115            accept_keystroke: accept_copy,
 3116            #[cfg(test)]
 3117            preview_keystroke: preview_copy,
 3118            displayed_keystroke,
 3119            action,
 3120            missing_accept_keystroke,
 3121            show_hold_label: matches!(surface, EditPredictionKeybindSurface::CursorPopoverCompact)
 3122                && self.edit_prediction_preview.released_too_fast(),
 3123        }
 3124    }
 3125
 3126    pub fn new_file(
 3127        workspace: &mut Workspace,
 3128        _: &workspace::NewFile,
 3129        window: &mut Window,
 3130        cx: &mut Context<Workspace>,
 3131    ) {
 3132        Self::new_in_workspace(workspace, window, cx).detach_and_prompt_err(
 3133            "Failed to create buffer",
 3134            window,
 3135            cx,
 3136            |e, _, _| match e.error_code() {
 3137                ErrorCode::RemoteUpgradeRequired => Some(format!(
 3138                "The remote instance of Zed does not support this yet. It must be upgraded to {}",
 3139                e.error_tag("required").unwrap_or("the latest version")
 3140            )),
 3141                _ => None,
 3142            },
 3143        );
 3144    }
 3145
 3146    pub fn new_in_workspace(
 3147        workspace: &mut Workspace,
 3148        window: &mut Window,
 3149        cx: &mut Context<Workspace>,
 3150    ) -> Task<Result<Entity<Editor>>> {
 3151        let project = workspace.project().clone();
 3152        let create = project.update(cx, |project, cx| project.create_buffer(None, true, cx));
 3153
 3154        cx.spawn_in(window, async move |workspace, cx| {
 3155            let buffer = create.await?;
 3156            workspace.update_in(cx, |workspace, window, cx| {
 3157                let editor =
 3158                    cx.new(|cx| Editor::for_buffer(buffer, Some(project.clone()), window, cx));
 3159                workspace.add_item_to_active_pane(Box::new(editor.clone()), None, true, window, cx);
 3160                editor
 3161            })
 3162        })
 3163    }
 3164
 3165    fn new_file_vertical(
 3166        workspace: &mut Workspace,
 3167        _: &workspace::NewFileSplitVertical,
 3168        window: &mut Window,
 3169        cx: &mut Context<Workspace>,
 3170    ) {
 3171        Self::new_file_in_direction(workspace, SplitDirection::vertical(cx), window, cx)
 3172    }
 3173
 3174    fn new_file_horizontal(
 3175        workspace: &mut Workspace,
 3176        _: &workspace::NewFileSplitHorizontal,
 3177        window: &mut Window,
 3178        cx: &mut Context<Workspace>,
 3179    ) {
 3180        Self::new_file_in_direction(workspace, SplitDirection::horizontal(cx), window, cx)
 3181    }
 3182
 3183    fn new_file_split(
 3184        workspace: &mut Workspace,
 3185        action: &workspace::NewFileSplit,
 3186        window: &mut Window,
 3187        cx: &mut Context<Workspace>,
 3188    ) {
 3189        Self::new_file_in_direction(workspace, action.0, window, cx)
 3190    }
 3191
 3192    fn new_file_in_direction(
 3193        workspace: &mut Workspace,
 3194        direction: SplitDirection,
 3195        window: &mut Window,
 3196        cx: &mut Context<Workspace>,
 3197    ) {
 3198        let project = workspace.project().clone();
 3199        let create = project.update(cx, |project, cx| project.create_buffer(None, true, cx));
 3200
 3201        cx.spawn_in(window, async move |workspace, cx| {
 3202            let buffer = create.await?;
 3203            workspace.update_in(cx, move |workspace, window, cx| {
 3204                workspace.split_item(
 3205                    direction,
 3206                    Box::new(
 3207                        cx.new(|cx| Editor::for_buffer(buffer, Some(project.clone()), window, cx)),
 3208                    ),
 3209                    window,
 3210                    cx,
 3211                )
 3212            })?;
 3213            anyhow::Ok(())
 3214        })
 3215        .detach_and_prompt_err("Failed to create buffer", window, cx, |e, _, _| {
 3216            match e.error_code() {
 3217                ErrorCode::RemoteUpgradeRequired => Some(format!(
 3218                "The remote instance of Zed does not support this yet. It must be upgraded to {}",
 3219                e.error_tag("required").unwrap_or("the latest version")
 3220            )),
 3221                _ => None,
 3222            }
 3223        });
 3224    }
 3225
 3226    pub fn leader_id(&self) -> Option<CollaboratorId> {
 3227        self.leader_id
 3228    }
 3229
 3230    pub fn buffer(&self) -> &Entity<MultiBuffer> {
 3231        &self.buffer
 3232    }
 3233
 3234    pub fn project(&self) -> Option<&Entity<Project>> {
 3235        self.project.as_ref()
 3236    }
 3237
 3238    pub fn workspace(&self) -> Option<Entity<Workspace>> {
 3239        self.workspace.as_ref()?.0.upgrade()
 3240    }
 3241
 3242    /// Detaches a task and shows an error notification in the workspace if available,
 3243    /// otherwise just logs the error.
 3244    pub fn detach_and_notify_err<R, E>(
 3245        &self,
 3246        task: Task<Result<R, E>>,
 3247        window: &mut Window,
 3248        cx: &mut App,
 3249    ) where
 3250        E: std::fmt::Debug + std::fmt::Display + 'static,
 3251        R: 'static,
 3252    {
 3253        if let Some(workspace) = self.workspace() {
 3254            task.detach_and_notify_err(workspace.downgrade(), window, cx);
 3255        } else {
 3256            task.detach_and_log_err(cx);
 3257        }
 3258    }
 3259
 3260    /// Returns the workspace serialization ID if this editor should be serialized.
 3261    fn workspace_serialization_id(&self, _cx: &App) -> Option<WorkspaceId> {
 3262        self.workspace
 3263            .as_ref()
 3264            .filter(|_| self.should_serialize_buffer())
 3265            .and_then(|workspace| workspace.1)
 3266    }
 3267
 3268    pub fn title<'a>(&self, cx: &'a App) -> Cow<'a, str> {
 3269        self.buffer().read(cx).title(cx)
 3270    }
 3271
 3272    pub fn snapshot(&self, window: &Window, cx: &mut App) -> EditorSnapshot {
 3273        let git_blame_gutter_max_author_length = self
 3274            .render_git_blame_gutter(cx)
 3275            .then(|| {
 3276                if let Some(blame) = self.blame.as_ref() {
 3277                    let max_author_length =
 3278                        blame.update(cx, |blame, cx| blame.max_author_length(cx));
 3279                    Some(max_author_length)
 3280                } else {
 3281                    None
 3282                }
 3283            })
 3284            .flatten();
 3285
 3286        let display_snapshot = self.display_map.update(cx, |map, cx| map.snapshot(cx));
 3287
 3288        EditorSnapshot {
 3289            mode: self.mode.clone(),
 3290            show_gutter: self.show_gutter,
 3291            offset_content: self.offset_content,
 3292            show_line_numbers: self.show_line_numbers,
 3293            number_deleted_lines: self.number_deleted_lines,
 3294            show_git_diff_gutter: self.show_git_diff_gutter,
 3295            semantic_tokens_enabled: self.semantic_token_state.enabled(),
 3296            show_code_actions: self.show_code_actions,
 3297            show_runnables: self.show_runnables,
 3298            show_breakpoints: self.show_breakpoints,
 3299            git_blame_gutter_max_author_length,
 3300            scroll_anchor: self.scroll_manager.shared_scroll_anchor(cx),
 3301            display_snapshot,
 3302            placeholder_display_snapshot: self
 3303                .placeholder_display_map
 3304                .as_ref()
 3305                .map(|display_map| display_map.update(cx, |map, cx| map.snapshot(cx))),
 3306            ongoing_scroll: self.scroll_manager.ongoing_scroll(),
 3307            is_focused: self.focus_handle.is_focused(window),
 3308            current_line_highlight: self
 3309                .current_line_highlight
 3310                .unwrap_or_else(|| EditorSettings::get_global(cx).current_line_highlight),
 3311            gutter_hovered: self.gutter_hovered,
 3312        }
 3313    }
 3314
 3315    pub fn language_at<T: ToOffset>(&self, point: T, cx: &App) -> Option<Arc<Language>> {
 3316        self.buffer.read(cx).language_at(point, cx)
 3317    }
 3318
 3319    pub fn file_at<T: ToOffset>(&self, point: T, cx: &App) -> Option<Arc<dyn language::File>> {
 3320        self.buffer.read(cx).read(cx).file_at(point).cloned()
 3321    }
 3322
 3323    pub fn active_excerpt(
 3324        &self,
 3325        cx: &App,
 3326    ) -> Option<(ExcerptId, Entity<Buffer>, Range<text::Anchor>)> {
 3327        self.buffer
 3328            .read(cx)
 3329            .excerpt_containing(self.selections.newest_anchor().head(), cx)
 3330    }
 3331
 3332    pub fn mode(&self) -> &EditorMode {
 3333        &self.mode
 3334    }
 3335
 3336    pub fn set_mode(&mut self, mode: EditorMode) {
 3337        self.mode = mode;
 3338    }
 3339
 3340    pub fn collaboration_hub(&self) -> Option<&dyn CollaborationHub> {
 3341        self.collaboration_hub.as_deref()
 3342    }
 3343
 3344    pub fn set_collaboration_hub(&mut self, hub: Box<dyn CollaborationHub>) {
 3345        self.collaboration_hub = Some(hub);
 3346    }
 3347
 3348    pub fn set_in_project_search(&mut self, in_project_search: bool) {
 3349        self.in_project_search = in_project_search;
 3350    }
 3351
 3352    pub fn set_custom_context_menu(
 3353        &mut self,
 3354        f: impl 'static
 3355        + Fn(
 3356            &mut Self,
 3357            DisplayPoint,
 3358            &mut Window,
 3359            &mut Context<Self>,
 3360        ) -> Option<Entity<ui::ContextMenu>>,
 3361    ) {
 3362        self.custom_context_menu = Some(Box::new(f))
 3363    }
 3364
 3365    pub fn set_completion_provider(&mut self, provider: Option<Rc<dyn CompletionProvider>>) {
 3366        self.completion_provider = provider;
 3367    }
 3368
 3369    #[cfg(any(test, feature = "test-support"))]
 3370    pub fn completion_provider(&self) -> Option<Rc<dyn CompletionProvider>> {
 3371        self.completion_provider.clone()
 3372    }
 3373
 3374    pub fn semantics_provider(&self) -> Option<Rc<dyn SemanticsProvider>> {
 3375        self.semantics_provider.clone()
 3376    }
 3377
 3378    pub fn set_semantics_provider(&mut self, provider: Option<Rc<dyn SemanticsProvider>>) {
 3379        self.semantics_provider = provider;
 3380    }
 3381
 3382    pub fn set_edit_prediction_provider<T>(
 3383        &mut self,
 3384        provider: Option<Entity<T>>,
 3385        window: &mut Window,
 3386        cx: &mut Context<Self>,
 3387    ) where
 3388        T: EditPredictionDelegate,
 3389    {
 3390        self.edit_prediction_provider = provider.map(|provider| RegisteredEditPredictionDelegate {
 3391            _subscription: cx.observe_in(&provider, window, |this, _, window, cx| {
 3392                if this.focus_handle.is_focused(window) {
 3393                    this.update_visible_edit_prediction(window, cx);
 3394                }
 3395            }),
 3396            provider: Arc::new(provider),
 3397        });
 3398        self.update_edit_prediction_settings(cx);
 3399        self.refresh_edit_prediction(false, false, window, cx);
 3400    }
 3401
 3402    pub fn placeholder_text(&self, cx: &mut App) -> Option<String> {
 3403        self.placeholder_display_map
 3404            .as_ref()
 3405            .map(|display_map| display_map.update(cx, |map, cx| map.snapshot(cx)).text())
 3406    }
 3407
 3408    pub fn set_placeholder_text(
 3409        &mut self,
 3410        placeholder_text: &str,
 3411        window: &mut Window,
 3412        cx: &mut Context<Self>,
 3413    ) {
 3414        let multibuffer = cx
 3415            .new(|cx| MultiBuffer::singleton(cx.new(|cx| Buffer::local(placeholder_text, cx)), cx));
 3416
 3417        let style = window.text_style();
 3418
 3419        self.placeholder_display_map = Some(cx.new(|cx| {
 3420            DisplayMap::new(
 3421                multibuffer,
 3422                style.font(),
 3423                style.font_size.to_pixels(window.rem_size()),
 3424                None,
 3425                FILE_HEADER_HEIGHT,
 3426                MULTI_BUFFER_EXCERPT_HEADER_HEIGHT,
 3427                Default::default(),
 3428                DiagnosticSeverity::Off,
 3429                cx,
 3430            )
 3431        }));
 3432        cx.notify();
 3433    }
 3434
 3435    pub fn set_cursor_shape(&mut self, cursor_shape: CursorShape, cx: &mut Context<Self>) {
 3436        self.cursor_shape = cursor_shape;
 3437
 3438        // Disrupt blink for immediate user feedback that the cursor shape has changed
 3439        self.blink_manager.update(cx, BlinkManager::show_cursor);
 3440
 3441        cx.notify();
 3442    }
 3443
 3444    pub fn cursor_shape(&self) -> CursorShape {
 3445        self.cursor_shape
 3446    }
 3447
 3448    pub fn set_cursor_offset_on_selection(&mut self, set_cursor_offset_on_selection: bool) {
 3449        self.cursor_offset_on_selection = set_cursor_offset_on_selection;
 3450    }
 3451
 3452    pub fn set_current_line_highlight(
 3453        &mut self,
 3454        current_line_highlight: Option<CurrentLineHighlight>,
 3455    ) {
 3456        self.current_line_highlight = current_line_highlight;
 3457    }
 3458
 3459    pub fn set_collapse_matches(&mut self, collapse_matches: bool) {
 3460        self.collapse_matches = collapse_matches;
 3461    }
 3462
 3463    pub fn range_for_match<T: std::marker::Copy>(&self, range: &Range<T>) -> Range<T> {
 3464        if self.collapse_matches {
 3465            return range.start..range.start;
 3466        }
 3467        range.clone()
 3468    }
 3469
 3470    pub fn clip_at_line_ends(&mut self, cx: &mut Context<Self>) -> bool {
 3471        self.display_map.read(cx).clip_at_line_ends
 3472    }
 3473
 3474    pub fn set_clip_at_line_ends(&mut self, clip: bool, cx: &mut Context<Self>) {
 3475        if self.display_map.read(cx).clip_at_line_ends != clip {
 3476            self.display_map
 3477                .update(cx, |map, _| map.clip_at_line_ends = clip);
 3478        }
 3479    }
 3480
 3481    pub fn set_input_enabled(&mut self, input_enabled: bool) {
 3482        self.input_enabled = input_enabled;
 3483    }
 3484
 3485    pub fn set_expects_character_input(&mut self, expects_character_input: bool) {
 3486        self.expects_character_input = expects_character_input;
 3487    }
 3488
 3489    pub fn set_edit_predictions_hidden_for_vim_mode(
 3490        &mut self,
 3491        hidden: bool,
 3492        window: &mut Window,
 3493        cx: &mut Context<Self>,
 3494    ) {
 3495        if hidden != self.edit_predictions_hidden_for_vim_mode {
 3496            self.edit_predictions_hidden_for_vim_mode = hidden;
 3497            if hidden {
 3498                self.update_visible_edit_prediction(window, cx);
 3499            } else {
 3500                self.refresh_edit_prediction(true, false, window, cx);
 3501            }
 3502        }
 3503    }
 3504
 3505    pub fn set_menu_edit_predictions_policy(&mut self, value: MenuEditPredictionsPolicy) {
 3506        self.menu_edit_predictions_policy = value;
 3507    }
 3508
 3509    pub fn set_autoindent(&mut self, autoindent: bool) {
 3510        if autoindent {
 3511            self.autoindent_mode = Some(AutoindentMode::EachLine);
 3512        } else {
 3513            self.autoindent_mode = None;
 3514        }
 3515    }
 3516
 3517    pub fn capability(&self, cx: &App) -> Capability {
 3518        if self.read_only {
 3519            Capability::ReadOnly
 3520        } else {
 3521            self.buffer.read(cx).capability()
 3522        }
 3523    }
 3524
 3525    pub fn read_only(&self, cx: &App) -> bool {
 3526        self.read_only || self.buffer.read(cx).read_only()
 3527    }
 3528
 3529    pub fn set_read_only(&mut self, read_only: bool) {
 3530        self.read_only = read_only;
 3531    }
 3532
 3533    pub fn set_use_autoclose(&mut self, autoclose: bool) {
 3534        self.use_autoclose = autoclose;
 3535    }
 3536
 3537    pub fn set_use_auto_surround(&mut self, auto_surround: bool) {
 3538        self.use_auto_surround = auto_surround;
 3539    }
 3540
 3541    pub fn set_auto_replace_emoji_shortcode(&mut self, auto_replace: bool) {
 3542        self.auto_replace_emoji_shortcode = auto_replace;
 3543    }
 3544
 3545    pub fn set_should_serialize(&mut self, should_serialize: bool, cx: &App) {
 3546        self.buffer_serialization = should_serialize.then(|| {
 3547            BufferSerialization::new(
 3548                ProjectSettings::get_global(cx)
 3549                    .session
 3550                    .restore_unsaved_buffers,
 3551            )
 3552        })
 3553    }
 3554
 3555    fn should_serialize_buffer(&self) -> bool {
 3556        self.buffer_serialization.is_some()
 3557    }
 3558
 3559    pub fn toggle_edit_predictions(
 3560        &mut self,
 3561        _: &ToggleEditPrediction,
 3562        window: &mut Window,
 3563        cx: &mut Context<Self>,
 3564    ) {
 3565        if self.show_edit_predictions_override.is_some() {
 3566            self.set_show_edit_predictions(None, window, cx);
 3567        } else {
 3568            let show_edit_predictions = !self.edit_predictions_enabled();
 3569            self.set_show_edit_predictions(Some(show_edit_predictions), window, cx);
 3570        }
 3571    }
 3572
 3573    pub fn set_show_completions_on_input(&mut self, show_completions_on_input: Option<bool>) {
 3574        self.show_completions_on_input_override = show_completions_on_input;
 3575    }
 3576
 3577    pub fn set_show_edit_predictions(
 3578        &mut self,
 3579        show_edit_predictions: Option<bool>,
 3580        window: &mut Window,
 3581        cx: &mut Context<Self>,
 3582    ) {
 3583        self.show_edit_predictions_override = show_edit_predictions;
 3584        self.update_edit_prediction_settings(cx);
 3585
 3586        if let Some(false) = show_edit_predictions {
 3587            self.discard_edit_prediction(EditPredictionDiscardReason::Ignored, cx);
 3588        } else {
 3589            self.refresh_edit_prediction(false, true, window, cx);
 3590        }
 3591    }
 3592
 3593    fn edit_predictions_disabled_in_scope(
 3594        &self,
 3595        buffer: &Entity<Buffer>,
 3596        buffer_position: language::Anchor,
 3597        cx: &App,
 3598    ) -> bool {
 3599        let snapshot = buffer.read(cx).snapshot();
 3600        let settings = snapshot.settings_at(buffer_position, cx);
 3601
 3602        let Some(scope) = snapshot.language_scope_at(buffer_position) else {
 3603            return false;
 3604        };
 3605
 3606        scope.override_name().is_some_and(|scope_name| {
 3607            settings
 3608                .edit_predictions_disabled_in
 3609                .iter()
 3610                .any(|s| s == scope_name)
 3611        })
 3612    }
 3613
 3614    pub fn set_use_modal_editing(&mut self, to: bool) {
 3615        self.use_modal_editing = to;
 3616    }
 3617
 3618    pub fn use_modal_editing(&self) -> bool {
 3619        self.use_modal_editing
 3620    }
 3621
 3622    fn selections_did_change(
 3623        &mut self,
 3624        local: bool,
 3625        old_cursor_position: &Anchor,
 3626        effects: SelectionEffects,
 3627        window: &mut Window,
 3628        cx: &mut Context<Self>,
 3629    ) {
 3630        window.invalidate_character_coordinates();
 3631
 3632        // Copy selections to primary selection buffer
 3633        #[cfg(any(target_os = "linux", target_os = "freebsd"))]
 3634        if local {
 3635            let selections = self
 3636                .selections
 3637                .all::<MultiBufferOffset>(&self.display_snapshot(cx));
 3638            let buffer_handle = self.buffer.read(cx).read(cx);
 3639
 3640            let mut text = String::new();
 3641            for (index, selection) in selections.iter().enumerate() {
 3642                let text_for_selection = buffer_handle
 3643                    .text_for_range(selection.start..selection.end)
 3644                    .collect::<String>();
 3645
 3646                text.push_str(&text_for_selection);
 3647                if index != selections.len() - 1 {
 3648                    text.push('\n');
 3649                }
 3650            }
 3651
 3652            if !text.is_empty() {
 3653                cx.write_to_primary(ClipboardItem::new_string(text));
 3654            }
 3655        }
 3656
 3657        let selection_anchors = self.selections.disjoint_anchors_arc();
 3658
 3659        if self.focus_handle.is_focused(window) && self.leader_id.is_none() {
 3660            self.buffer.update(cx, |buffer, cx| {
 3661                buffer.set_active_selections(
 3662                    &selection_anchors,
 3663                    self.selections.line_mode(),
 3664                    self.cursor_shape,
 3665                    cx,
 3666                )
 3667            });
 3668        }
 3669        let display_map = self
 3670            .display_map
 3671            .update(cx, |display_map, cx| display_map.snapshot(cx));
 3672        let buffer = display_map.buffer_snapshot();
 3673        if self.selections.count() == 1 {
 3674            self.add_selections_state = None;
 3675        }
 3676        self.select_next_state = None;
 3677        self.select_prev_state = None;
 3678        self.select_syntax_node_history.try_clear();
 3679        self.invalidate_autoclose_regions(&selection_anchors, buffer);
 3680        self.snippet_stack.invalidate(&selection_anchors, buffer);
 3681        self.take_rename(false, window, cx);
 3682
 3683        let newest_selection = self.selections.newest_anchor();
 3684        let new_cursor_position = newest_selection.head();
 3685        let selection_start = newest_selection.start;
 3686
 3687        if effects.nav_history.is_none() || effects.nav_history == Some(true) {
 3688            self.push_to_nav_history(
 3689                *old_cursor_position,
 3690                Some(new_cursor_position.to_point(buffer)),
 3691                false,
 3692                effects.nav_history == Some(true),
 3693                cx,
 3694            );
 3695        }
 3696
 3697        if local {
 3698            if let Some(buffer_id) = new_cursor_position.text_anchor.buffer_id {
 3699                self.register_buffer(buffer_id, cx);
 3700            }
 3701
 3702            let mut context_menu = self.context_menu.borrow_mut();
 3703            let completion_menu = match context_menu.as_ref() {
 3704                Some(CodeContextMenu::Completions(menu)) => Some(menu),
 3705                Some(CodeContextMenu::CodeActions(_)) => {
 3706                    *context_menu = None;
 3707                    None
 3708                }
 3709                None => None,
 3710            };
 3711            let completion_position = completion_menu.map(|menu| menu.initial_position);
 3712            drop(context_menu);
 3713
 3714            if effects.completions
 3715                && let Some(completion_position) = completion_position
 3716            {
 3717                let start_offset = selection_start.to_offset(buffer);
 3718                let position_matches = start_offset == completion_position.to_offset(buffer);
 3719                let continue_showing = if let Some((snap, ..)) =
 3720                    buffer.point_to_buffer_offset(completion_position)
 3721                    && !snap.capability.editable()
 3722                {
 3723                    false
 3724                } else if position_matches {
 3725                    if self.snippet_stack.is_empty() {
 3726                        buffer.char_kind_before(start_offset, Some(CharScopeContext::Completion))
 3727                            == Some(CharKind::Word)
 3728                    } else {
 3729                        // Snippet choices can be shown even when the cursor is in whitespace.
 3730                        // Dismissing the menu with actions like backspace is handled by
 3731                        // invalidation regions.
 3732                        true
 3733                    }
 3734                } else {
 3735                    false
 3736                };
 3737
 3738                if continue_showing {
 3739                    self.open_or_update_completions_menu(None, None, false, window, cx);
 3740                } else {
 3741                    self.hide_context_menu(window, cx);
 3742                }
 3743            }
 3744
 3745            hide_hover(self, cx);
 3746
 3747            if old_cursor_position.to_display_point(&display_map).row()
 3748                != new_cursor_position.to_display_point(&display_map).row()
 3749            {
 3750                self.available_code_actions.take();
 3751            }
 3752            self.refresh_code_actions(window, cx);
 3753            self.refresh_document_highlights(cx);
 3754            refresh_linked_ranges(self, window, cx);
 3755
 3756            self.refresh_selected_text_highlights(&display_map, false, window, cx);
 3757            self.refresh_matching_bracket_highlights(&display_map, cx);
 3758            self.refresh_outline_symbols_at_cursor(cx);
 3759            self.update_visible_edit_prediction(window, cx);
 3760            self.inline_blame_popover.take();
 3761            if self.git_blame_inline_enabled {
 3762                self.start_inline_blame_timer(window, cx);
 3763            }
 3764        }
 3765
 3766        self.blink_manager.update(cx, BlinkManager::pause_blinking);
 3767
 3768        if local && !self.suppress_selection_callback {
 3769            if let Some(callback) = self.on_local_selections_changed.as_ref() {
 3770                let cursor_position = self.selections.newest::<Point>(&display_map).head();
 3771                callback(cursor_position, window, cx);
 3772            }
 3773        }
 3774
 3775        cx.emit(EditorEvent::SelectionsChanged { local });
 3776
 3777        let selections = &self.selections.disjoint_anchors_arc();
 3778        if selections.len() == 1 {
 3779            cx.emit(SearchEvent::ActiveMatchChanged)
 3780        }
 3781        if local && let Some((_, _, buffer_snapshot)) = buffer.as_singleton() {
 3782            let inmemory_selections = selections
 3783                .iter()
 3784                .map(|s| {
 3785                    text::ToPoint::to_point(&s.range().start.text_anchor, buffer_snapshot)
 3786                        ..text::ToPoint::to_point(&s.range().end.text_anchor, buffer_snapshot)
 3787                })
 3788                .collect();
 3789            self.update_restoration_data(cx, |data| {
 3790                data.selections = inmemory_selections;
 3791            });
 3792
 3793            if WorkspaceSettings::get(None, cx).restore_on_startup
 3794                != RestoreOnStartupBehavior::EmptyTab
 3795                && let Some(workspace_id) = self.workspace_serialization_id(cx)
 3796            {
 3797                let snapshot = self.buffer().read(cx).snapshot(cx);
 3798                let selections = selections.clone();
 3799                let background_executor = cx.background_executor().clone();
 3800                let editor_id = cx.entity().entity_id().as_u64() as ItemId;
 3801                let db = EditorDb::global(cx);
 3802                self.serialize_selections = cx.background_spawn(async move {
 3803                    background_executor.timer(SERIALIZATION_THROTTLE_TIME).await;
 3804                    let db_selections = selections
 3805                        .iter()
 3806                        .map(|selection| {
 3807                            (
 3808                                selection.start.to_offset(&snapshot).0,
 3809                                selection.end.to_offset(&snapshot).0,
 3810                            )
 3811                        })
 3812                        .collect();
 3813
 3814                    db.save_editor_selections(editor_id, workspace_id, db_selections)
 3815                        .await
 3816                        .with_context(|| {
 3817                            format!(
 3818                                "persisting editor selections for editor {editor_id}, \
 3819                                workspace {workspace_id:?}"
 3820                            )
 3821                        })
 3822                        .log_err();
 3823                });
 3824            }
 3825        }
 3826
 3827        cx.notify();
 3828    }
 3829
 3830    fn folds_did_change(&mut self, cx: &mut Context<Self>) {
 3831        use text::ToOffset as _;
 3832        use text::ToPoint as _;
 3833
 3834        if self.mode.is_minimap()
 3835            || WorkspaceSettings::get(None, cx).restore_on_startup
 3836                == RestoreOnStartupBehavior::EmptyTab
 3837        {
 3838            return;
 3839        }
 3840
 3841        if !self.buffer().read(cx).is_singleton() {
 3842            return;
 3843        }
 3844
 3845        let display_snapshot = self
 3846            .display_map
 3847            .update(cx, |display_map, cx| display_map.snapshot(cx));
 3848        let Some((.., snapshot)) = display_snapshot.buffer_snapshot().as_singleton() else {
 3849            return;
 3850        };
 3851        let inmemory_folds = display_snapshot
 3852            .folds_in_range(MultiBufferOffset(0)..display_snapshot.buffer_snapshot().len())
 3853            .map(|fold| {
 3854                fold.range.start.text_anchor.to_point(&snapshot)
 3855                    ..fold.range.end.text_anchor.to_point(&snapshot)
 3856            })
 3857            .collect();
 3858        self.update_restoration_data(cx, |data| {
 3859            data.folds = inmemory_folds;
 3860        });
 3861
 3862        let Some(workspace_id) = self.workspace_serialization_id(cx) else {
 3863            return;
 3864        };
 3865
 3866        // Get file path for path-based fold storage (survives tab close)
 3867        let Some(file_path) = self.buffer().read(cx).as_singleton().and_then(|buffer| {
 3868            project::File::from_dyn(buffer.read(cx).file())
 3869                .map(|file| Arc::<Path>::from(file.abs_path(cx)))
 3870        }) else {
 3871            return;
 3872        };
 3873
 3874        let background_executor = cx.background_executor().clone();
 3875        const FINGERPRINT_LEN: usize = 32;
 3876        let db_folds = display_snapshot
 3877            .folds_in_range(MultiBufferOffset(0)..display_snapshot.buffer_snapshot().len())
 3878            .map(|fold| {
 3879                let start = fold.range.start.text_anchor.to_offset(&snapshot);
 3880                let end = fold.range.end.text_anchor.to_offset(&snapshot);
 3881
 3882                // Extract fingerprints - content at fold boundaries for validation on restore
 3883                // Both fingerprints must be INSIDE the fold to avoid capturing surrounding
 3884                // content that might change independently.
 3885                // start_fp: first min(32, fold_len) bytes of fold content
 3886                // end_fp: last min(32, fold_len) bytes of fold content
 3887                // Clip to character boundaries to handle multibyte UTF-8 characters.
 3888                let fold_len = end - start;
 3889                let start_fp_end = snapshot
 3890                    .clip_offset(start + std::cmp::min(FINGERPRINT_LEN, fold_len), Bias::Left);
 3891                let start_fp: String = snapshot.text_for_range(start..start_fp_end).collect();
 3892                let end_fp_start = snapshot
 3893                    .clip_offset(end.saturating_sub(FINGERPRINT_LEN).max(start), Bias::Right);
 3894                let end_fp: String = snapshot.text_for_range(end_fp_start..end).collect();
 3895
 3896                (start, end, start_fp, end_fp)
 3897            })
 3898            .collect::<Vec<_>>();
 3899        let db = EditorDb::global(cx);
 3900        self.serialize_folds = cx.background_spawn(async move {
 3901            background_executor.timer(SERIALIZATION_THROTTLE_TIME).await;
 3902            if db_folds.is_empty() {
 3903                // No folds - delete any persisted folds for this file
 3904                db.delete_file_folds(workspace_id, file_path)
 3905                    .await
 3906                    .with_context(|| format!("deleting file folds for workspace {workspace_id:?}"))
 3907                    .log_err();
 3908            } else {
 3909                db.save_file_folds(workspace_id, file_path, db_folds)
 3910                    .await
 3911                    .with_context(|| {
 3912                        format!("persisting file folds for workspace {workspace_id:?}")
 3913                    })
 3914                    .log_err();
 3915            }
 3916        });
 3917    }
 3918
 3919    pub fn sync_selections(
 3920        &mut self,
 3921        other: Entity<Editor>,
 3922        cx: &mut Context<Self>,
 3923    ) -> gpui::Subscription {
 3924        let other_selections = other.read(cx).selections.disjoint_anchors().to_vec();
 3925        if !other_selections.is_empty() {
 3926            self.selections
 3927                .change_with(&self.display_snapshot(cx), |selections| {
 3928                    selections.select_anchors(other_selections);
 3929                });
 3930        }
 3931
 3932        let other_subscription = cx.subscribe(&other, |this, other, other_evt, cx| {
 3933            if let EditorEvent::SelectionsChanged { local: true } = other_evt {
 3934                let other_selections = other.read(cx).selections.disjoint_anchors().to_vec();
 3935                if other_selections.is_empty() {
 3936                    return;
 3937                }
 3938                let snapshot = this.display_snapshot(cx);
 3939                this.selections.change_with(&snapshot, |selections| {
 3940                    selections.select_anchors(other_selections);
 3941                });
 3942            }
 3943        });
 3944
 3945        let this_subscription = cx.subscribe_self::<EditorEvent>(move |this, this_evt, cx| {
 3946            if let EditorEvent::SelectionsChanged { local: true } = this_evt {
 3947                let these_selections = this.selections.disjoint_anchors().to_vec();
 3948                if these_selections.is_empty() {
 3949                    return;
 3950                }
 3951                other.update(cx, |other_editor, cx| {
 3952                    let snapshot = other_editor.display_snapshot(cx);
 3953                    other_editor
 3954                        .selections
 3955                        .change_with(&snapshot, |selections| {
 3956                            selections.select_anchors(these_selections);
 3957                        })
 3958                });
 3959            }
 3960        });
 3961
 3962        Subscription::join(other_subscription, this_subscription)
 3963    }
 3964
 3965    fn unfold_buffers_with_selections(&mut self, cx: &mut Context<Self>) {
 3966        if self.buffer().read(cx).is_singleton() {
 3967            return;
 3968        }
 3969        let snapshot = self.buffer.read(cx).snapshot(cx);
 3970        let buffer_ids: HashSet<BufferId> = self
 3971            .selections
 3972            .disjoint_anchor_ranges()
 3973            .flat_map(|range| snapshot.buffer_ids_for_range(range))
 3974            .collect();
 3975        for buffer_id in buffer_ids {
 3976            self.unfold_buffer(buffer_id, cx);
 3977        }
 3978    }
 3979
 3980    /// Changes selections using the provided mutation function. Changes to `self.selections` occur
 3981    /// immediately, but when run within `transact` or `with_selection_effects_deferred` other
 3982    /// effects of selection change occur at the end of the transaction.
 3983    pub fn change_selections<R>(
 3984        &mut self,
 3985        effects: SelectionEffects,
 3986        window: &mut Window,
 3987        cx: &mut Context<Self>,
 3988        change: impl FnOnce(&mut MutableSelectionsCollection<'_, '_>) -> R,
 3989    ) -> R {
 3990        let snapshot = self.display_snapshot(cx);
 3991        if let Some(state) = &mut self.deferred_selection_effects_state {
 3992            state.effects.scroll = effects.scroll.or(state.effects.scroll);
 3993            state.effects.completions = effects.completions;
 3994            state.effects.nav_history = effects.nav_history.or(state.effects.nav_history);
 3995            let (changed, result) = self.selections.change_with(&snapshot, change);
 3996            state.changed |= changed;
 3997            return result;
 3998        }
 3999        let mut state = DeferredSelectionEffectsState {
 4000            changed: false,
 4001            effects,
 4002            old_cursor_position: self.selections.newest_anchor().head(),
 4003            history_entry: SelectionHistoryEntry {
 4004                selections: self.selections.disjoint_anchors_arc(),
 4005                select_next_state: self.select_next_state.clone(),
 4006                select_prev_state: self.select_prev_state.clone(),
 4007                add_selections_state: self.add_selections_state.clone(),
 4008            },
 4009        };
 4010        let (changed, result) = self.selections.change_with(&snapshot, change);
 4011        state.changed = state.changed || changed;
 4012        if self.defer_selection_effects {
 4013            self.deferred_selection_effects_state = Some(state);
 4014        } else {
 4015            self.apply_selection_effects(state, window, cx);
 4016        }
 4017        result
 4018    }
 4019
 4020    /// Defers the effects of selection change, so that the effects of multiple calls to
 4021    /// `change_selections` are applied at the end. This way these intermediate states aren't added
 4022    /// to selection history and the state of popovers based on selection position aren't
 4023    /// erroneously updated.
 4024    pub fn with_selection_effects_deferred<R>(
 4025        &mut self,
 4026        window: &mut Window,
 4027        cx: &mut Context<Self>,
 4028        update: impl FnOnce(&mut Self, &mut Window, &mut Context<Self>) -> R,
 4029    ) -> R {
 4030        let already_deferred = self.defer_selection_effects;
 4031        self.defer_selection_effects = true;
 4032        let result = update(self, window, cx);
 4033        if !already_deferred {
 4034            self.defer_selection_effects = false;
 4035            if let Some(state) = self.deferred_selection_effects_state.take() {
 4036                self.apply_selection_effects(state, window, cx);
 4037            }
 4038        }
 4039        result
 4040    }
 4041
 4042    fn apply_selection_effects(
 4043        &mut self,
 4044        state: DeferredSelectionEffectsState,
 4045        window: &mut Window,
 4046        cx: &mut Context<Self>,
 4047    ) {
 4048        if state.changed {
 4049            self.selection_history.push(state.history_entry);
 4050
 4051            if let Some(autoscroll) = state.effects.scroll {
 4052                self.request_autoscroll(autoscroll, cx);
 4053            }
 4054
 4055            let old_cursor_position = &state.old_cursor_position;
 4056
 4057            self.selections_did_change(true, old_cursor_position, state.effects, window, cx);
 4058
 4059            if self.should_open_signature_help_automatically(old_cursor_position, cx) {
 4060                self.show_signature_help_auto(window, cx);
 4061            }
 4062        }
 4063    }
 4064
 4065    pub fn edit<I, S, T>(&mut self, edits: I, cx: &mut Context<Self>)
 4066    where
 4067        I: IntoIterator<Item = (Range<S>, T)>,
 4068        S: ToOffset,
 4069        T: Into<Arc<str>>,
 4070    {
 4071        if self.read_only(cx) {
 4072            return;
 4073        }
 4074
 4075        self.buffer
 4076            .update(cx, |buffer, cx| buffer.edit(edits, None, cx));
 4077    }
 4078
 4079    pub fn edit_with_autoindent<I, S, T>(&mut self, edits: I, cx: &mut Context<Self>)
 4080    where
 4081        I: IntoIterator<Item = (Range<S>, T)>,
 4082        S: ToOffset,
 4083        T: Into<Arc<str>>,
 4084    {
 4085        if self.read_only(cx) {
 4086            return;
 4087        }
 4088
 4089        self.buffer.update(cx, |buffer, cx| {
 4090            buffer.edit(edits, self.autoindent_mode.clone(), cx)
 4091        });
 4092    }
 4093
 4094    pub fn edit_with_block_indent<I, S, T>(
 4095        &mut self,
 4096        edits: I,
 4097        original_indent_columns: Vec<Option<u32>>,
 4098        cx: &mut Context<Self>,
 4099    ) where
 4100        I: IntoIterator<Item = (Range<S>, T)>,
 4101        S: ToOffset,
 4102        T: Into<Arc<str>>,
 4103    {
 4104        if self.read_only(cx) {
 4105            return;
 4106        }
 4107
 4108        self.buffer.update(cx, |buffer, cx| {
 4109            buffer.edit(
 4110                edits,
 4111                Some(AutoindentMode::Block {
 4112                    original_indent_columns,
 4113                }),
 4114                cx,
 4115            )
 4116        });
 4117    }
 4118
 4119    fn select(&mut self, phase: SelectPhase, window: &mut Window, cx: &mut Context<Self>) {
 4120        self.hide_context_menu(window, cx);
 4121
 4122        match phase {
 4123            SelectPhase::Begin {
 4124                position,
 4125                add,
 4126                click_count,
 4127            } => self.begin_selection(position, add, click_count, window, cx),
 4128            SelectPhase::BeginColumnar {
 4129                position,
 4130                goal_column,
 4131                reset,
 4132                mode,
 4133            } => self.begin_columnar_selection(position, goal_column, reset, mode, window, cx),
 4134            SelectPhase::Extend {
 4135                position,
 4136                click_count,
 4137            } => self.extend_selection(position, click_count, window, cx),
 4138            SelectPhase::Update {
 4139                position,
 4140                goal_column,
 4141                scroll_delta,
 4142            } => self.update_selection(position, goal_column, scroll_delta, window, cx),
 4143            SelectPhase::End => self.end_selection(window, cx),
 4144        }
 4145    }
 4146
 4147    fn extend_selection(
 4148        &mut self,
 4149        position: DisplayPoint,
 4150        click_count: usize,
 4151        window: &mut Window,
 4152        cx: &mut Context<Self>,
 4153    ) {
 4154        let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
 4155        let tail = self
 4156            .selections
 4157            .newest::<MultiBufferOffset>(&display_map)
 4158            .tail();
 4159        let click_count = click_count.max(match self.selections.select_mode() {
 4160            SelectMode::Character => 1,
 4161            SelectMode::Word(_) => 2,
 4162            SelectMode::Line(_) => 3,
 4163            SelectMode::All => 4,
 4164        });
 4165        self.begin_selection(position, false, click_count, window, cx);
 4166
 4167        let tail_anchor = display_map.buffer_snapshot().anchor_before(tail);
 4168
 4169        let current_selection = match self.selections.select_mode() {
 4170            SelectMode::Character | SelectMode::All => tail_anchor..tail_anchor,
 4171            SelectMode::Word(range) | SelectMode::Line(range) => range.clone(),
 4172        };
 4173
 4174        let mut pending_selection = self
 4175            .selections
 4176            .pending_anchor()
 4177            .cloned()
 4178            .expect("extend_selection not called with pending selection");
 4179
 4180        if pending_selection
 4181            .start
 4182            .cmp(&current_selection.start, display_map.buffer_snapshot())
 4183            == Ordering::Greater
 4184        {
 4185            pending_selection.start = current_selection.start;
 4186        }
 4187        if pending_selection
 4188            .end
 4189            .cmp(&current_selection.end, display_map.buffer_snapshot())
 4190            == Ordering::Less
 4191        {
 4192            pending_selection.end = current_selection.end;
 4193            pending_selection.reversed = true;
 4194        }
 4195
 4196        let mut pending_mode = self.selections.pending_mode().unwrap();
 4197        match &mut pending_mode {
 4198            SelectMode::Word(range) | SelectMode::Line(range) => *range = current_selection,
 4199            _ => {}
 4200        }
 4201
 4202        let effects = if EditorSettings::get_global(cx).autoscroll_on_clicks {
 4203            SelectionEffects::scroll(Autoscroll::fit())
 4204        } else {
 4205            SelectionEffects::no_scroll()
 4206        };
 4207
 4208        self.change_selections(effects, window, cx, |s| {
 4209            s.set_pending(pending_selection.clone(), pending_mode);
 4210            s.set_is_extending(true);
 4211        });
 4212    }
 4213
 4214    fn begin_selection(
 4215        &mut self,
 4216        position: DisplayPoint,
 4217        add: bool,
 4218        click_count: usize,
 4219        window: &mut Window,
 4220        cx: &mut Context<Self>,
 4221    ) {
 4222        if !self.focus_handle.is_focused(window) {
 4223            self.last_focused_descendant = None;
 4224            window.focus(&self.focus_handle, cx);
 4225        }
 4226
 4227        let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
 4228        let buffer = display_map.buffer_snapshot();
 4229        let position = display_map.clip_point(position, Bias::Left);
 4230
 4231        let start;
 4232        let end;
 4233        let mode;
 4234        let mut auto_scroll;
 4235        match click_count {
 4236            1 => {
 4237                start = buffer.anchor_before(position.to_point(&display_map));
 4238                end = start;
 4239                mode = SelectMode::Character;
 4240                auto_scroll = true;
 4241            }
 4242            2 => {
 4243                let position = display_map
 4244                    .clip_point(position, Bias::Left)
 4245                    .to_offset(&display_map, Bias::Left);
 4246                let (range, _) = buffer.surrounding_word(position, None);
 4247                start = buffer.anchor_before(range.start);
 4248                end = buffer.anchor_before(range.end);
 4249                mode = SelectMode::Word(start..end);
 4250                auto_scroll = true;
 4251            }
 4252            3 => {
 4253                let position = display_map
 4254                    .clip_point(position, Bias::Left)
 4255                    .to_point(&display_map);
 4256                let line_start = display_map.prev_line_boundary(position).0;
 4257                let next_line_start = buffer.clip_point(
 4258                    display_map.next_line_boundary(position).0 + Point::new(1, 0),
 4259                    Bias::Left,
 4260                );
 4261                start = buffer.anchor_before(line_start);
 4262                end = buffer.anchor_before(next_line_start);
 4263                mode = SelectMode::Line(start..end);
 4264                auto_scroll = true;
 4265            }
 4266            _ => {
 4267                start = buffer.anchor_before(MultiBufferOffset(0));
 4268                end = buffer.anchor_before(buffer.len());
 4269                mode = SelectMode::All;
 4270                auto_scroll = false;
 4271            }
 4272        }
 4273        auto_scroll &= EditorSettings::get_global(cx).autoscroll_on_clicks;
 4274
 4275        let point_to_delete: Option<usize> = {
 4276            let selected_points: Vec<Selection<Point>> =
 4277                self.selections.disjoint_in_range(start..end, &display_map);
 4278
 4279            if !add || click_count > 1 {
 4280                None
 4281            } else if !selected_points.is_empty() {
 4282                Some(selected_points[0].id)
 4283            } else {
 4284                let clicked_point_already_selected =
 4285                    self.selections.disjoint_anchors().iter().find(|selection| {
 4286                        selection.start.to_point(buffer) == start.to_point(buffer)
 4287                            || selection.end.to_point(buffer) == end.to_point(buffer)
 4288                    });
 4289
 4290                clicked_point_already_selected.map(|selection| selection.id)
 4291            }
 4292        };
 4293
 4294        let selections_count = self.selections.count();
 4295        let effects = if auto_scroll {
 4296            SelectionEffects::default()
 4297        } else {
 4298            SelectionEffects::no_scroll()
 4299        };
 4300
 4301        self.change_selections(effects, window, cx, |s| {
 4302            if let Some(point_to_delete) = point_to_delete {
 4303                s.delete(point_to_delete);
 4304
 4305                if selections_count == 1 {
 4306                    s.set_pending_anchor_range(start..end, mode);
 4307                }
 4308            } else {
 4309                if !add {
 4310                    s.clear_disjoint();
 4311                }
 4312
 4313                s.set_pending_anchor_range(start..end, mode);
 4314            }
 4315        });
 4316    }
 4317
 4318    fn begin_columnar_selection(
 4319        &mut self,
 4320        position: DisplayPoint,
 4321        goal_column: u32,
 4322        reset: bool,
 4323        mode: ColumnarMode,
 4324        window: &mut Window,
 4325        cx: &mut Context<Self>,
 4326    ) {
 4327        if !self.focus_handle.is_focused(window) {
 4328            self.last_focused_descendant = None;
 4329            window.focus(&self.focus_handle, cx);
 4330        }
 4331
 4332        let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
 4333
 4334        if reset {
 4335            let pointer_position = display_map
 4336                .buffer_snapshot()
 4337                .anchor_before(position.to_point(&display_map));
 4338
 4339            self.change_selections(
 4340                SelectionEffects::scroll(Autoscroll::newest()),
 4341                window,
 4342                cx,
 4343                |s| {
 4344                    s.clear_disjoint();
 4345                    s.set_pending_anchor_range(
 4346                        pointer_position..pointer_position,
 4347                        SelectMode::Character,
 4348                    );
 4349                },
 4350            );
 4351        };
 4352
 4353        let tail = self.selections.newest::<Point>(&display_map).tail();
 4354        let selection_anchor = display_map.buffer_snapshot().anchor_before(tail);
 4355        self.columnar_selection_state = match mode {
 4356            ColumnarMode::FromMouse => Some(ColumnarSelectionState::FromMouse {
 4357                selection_tail: selection_anchor,
 4358                display_point: if reset {
 4359                    if position.column() != goal_column {
 4360                        Some(DisplayPoint::new(position.row(), goal_column))
 4361                    } else {
 4362                        None
 4363                    }
 4364                } else {
 4365                    None
 4366                },
 4367            }),
 4368            ColumnarMode::FromSelection => Some(ColumnarSelectionState::FromSelection {
 4369                selection_tail: selection_anchor,
 4370            }),
 4371        };
 4372
 4373        if !reset {
 4374            self.select_columns(position, goal_column, &display_map, window, cx);
 4375        }
 4376    }
 4377
 4378    fn update_selection(
 4379        &mut self,
 4380        position: DisplayPoint,
 4381        goal_column: u32,
 4382        scroll_delta: gpui::Point<f32>,
 4383        window: &mut Window,
 4384        cx: &mut Context<Self>,
 4385    ) {
 4386        let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
 4387
 4388        if self.columnar_selection_state.is_some() {
 4389            self.select_columns(position, goal_column, &display_map, window, cx);
 4390        } else if let Some(mut pending) = self.selections.pending_anchor().cloned() {
 4391            let buffer = display_map.buffer_snapshot();
 4392            let head;
 4393            let tail;
 4394            let mode = self.selections.pending_mode().unwrap();
 4395            match &mode {
 4396                SelectMode::Character => {
 4397                    head = position.to_point(&display_map);
 4398                    tail = pending.tail().to_point(buffer);
 4399                }
 4400                SelectMode::Word(original_range) => {
 4401                    let offset = display_map
 4402                        .clip_point(position, Bias::Left)
 4403                        .to_offset(&display_map, Bias::Left);
 4404                    let original_range = original_range.to_offset(buffer);
 4405
 4406                    let head_offset = if buffer.is_inside_word(offset, None)
 4407                        || original_range.contains(&offset)
 4408                    {
 4409                        let (word_range, _) = buffer.surrounding_word(offset, None);
 4410                        if word_range.start < original_range.start {
 4411                            word_range.start
 4412                        } else {
 4413                            word_range.end
 4414                        }
 4415                    } else {
 4416                        offset
 4417                    };
 4418
 4419                    head = head_offset.to_point(buffer);
 4420                    if head_offset <= original_range.start {
 4421                        tail = original_range.end.to_point(buffer);
 4422                    } else {
 4423                        tail = original_range.start.to_point(buffer);
 4424                    }
 4425                }
 4426                SelectMode::Line(original_range) => {
 4427                    let original_range = original_range.to_point(display_map.buffer_snapshot());
 4428
 4429                    let position = display_map
 4430                        .clip_point(position, Bias::Left)
 4431                        .to_point(&display_map);
 4432                    let line_start = display_map.prev_line_boundary(position).0;
 4433                    let next_line_start = buffer.clip_point(
 4434                        display_map.next_line_boundary(position).0 + Point::new(1, 0),
 4435                        Bias::Left,
 4436                    );
 4437
 4438                    if line_start < original_range.start {
 4439                        head = line_start
 4440                    } else {
 4441                        head = next_line_start
 4442                    }
 4443
 4444                    if head <= original_range.start {
 4445                        tail = original_range.end;
 4446                    } else {
 4447                        tail = original_range.start;
 4448                    }
 4449                }
 4450                SelectMode::All => {
 4451                    return;
 4452                }
 4453            };
 4454
 4455            if head < tail {
 4456                pending.start = buffer.anchor_before(head);
 4457                pending.end = buffer.anchor_before(tail);
 4458                pending.reversed = true;
 4459            } else {
 4460                pending.start = buffer.anchor_before(tail);
 4461                pending.end = buffer.anchor_before(head);
 4462                pending.reversed = false;
 4463            }
 4464
 4465            self.change_selections(SelectionEffects::no_scroll(), window, cx, |s| {
 4466                s.set_pending(pending.clone(), mode);
 4467            });
 4468        } else {
 4469            log::error!("update_selection dispatched with no pending selection");
 4470            return;
 4471        }
 4472
 4473        self.apply_scroll_delta(scroll_delta, window, cx);
 4474        cx.notify();
 4475    }
 4476
 4477    fn end_selection(&mut self, window: &mut Window, cx: &mut Context<Self>) {
 4478        self.columnar_selection_state.take();
 4479        if let Some(pending_mode) = self.selections.pending_mode() {
 4480            let selections = self
 4481                .selections
 4482                .all::<MultiBufferOffset>(&self.display_snapshot(cx));
 4483            self.change_selections(SelectionEffects::no_scroll(), window, cx, |s| {
 4484                s.select(selections);
 4485                s.clear_pending();
 4486                if s.is_extending() {
 4487                    s.set_is_extending(false);
 4488                } else {
 4489                    s.set_select_mode(pending_mode);
 4490                }
 4491            });
 4492        }
 4493    }
 4494
 4495    fn select_columns(
 4496        &mut self,
 4497        head: DisplayPoint,
 4498        goal_column: u32,
 4499        display_map: &DisplaySnapshot,
 4500        window: &mut Window,
 4501        cx: &mut Context<Self>,
 4502    ) {
 4503        let Some(columnar_state) = self.columnar_selection_state.as_ref() else {
 4504            return;
 4505        };
 4506
 4507        let tail = match columnar_state {
 4508            ColumnarSelectionState::FromMouse {
 4509                selection_tail,
 4510                display_point,
 4511            } => display_point.unwrap_or_else(|| selection_tail.to_display_point(display_map)),
 4512            ColumnarSelectionState::FromSelection { selection_tail } => {
 4513                selection_tail.to_display_point(display_map)
 4514            }
 4515        };
 4516
 4517        let start_row = cmp::min(tail.row(), head.row());
 4518        let end_row = cmp::max(tail.row(), head.row());
 4519        let start_column = cmp::min(tail.column(), goal_column);
 4520        let end_column = cmp::max(tail.column(), goal_column);
 4521        let reversed = start_column < tail.column();
 4522
 4523        let selection_ranges = (start_row.0..=end_row.0)
 4524            .map(DisplayRow)
 4525            .filter_map(|row| {
 4526                if (matches!(columnar_state, ColumnarSelectionState::FromMouse { .. })
 4527                    || start_column <= display_map.line_len(row))
 4528                    && !display_map.is_block_line(row)
 4529                {
 4530                    let start = display_map
 4531                        .clip_point(DisplayPoint::new(row, start_column), Bias::Left)
 4532                        .to_point(display_map);
 4533                    let end = display_map
 4534                        .clip_point(DisplayPoint::new(row, end_column), Bias::Right)
 4535                        .to_point(display_map);
 4536                    if reversed {
 4537                        Some(end..start)
 4538                    } else {
 4539                        Some(start..end)
 4540                    }
 4541                } else {
 4542                    None
 4543                }
 4544            })
 4545            .collect::<Vec<_>>();
 4546        if selection_ranges.is_empty() {
 4547            return;
 4548        }
 4549
 4550        let ranges = match columnar_state {
 4551            ColumnarSelectionState::FromMouse { .. } => {
 4552                let mut non_empty_ranges = selection_ranges
 4553                    .iter()
 4554                    .filter(|selection_range| selection_range.start != selection_range.end)
 4555                    .peekable();
 4556                if non_empty_ranges.peek().is_some() {
 4557                    non_empty_ranges.cloned().collect()
 4558                } else {
 4559                    selection_ranges
 4560                }
 4561            }
 4562            _ => selection_ranges,
 4563        };
 4564
 4565        self.change_selections(SelectionEffects::no_scroll(), window, cx, |s| {
 4566            s.select_ranges(ranges);
 4567        });
 4568        cx.notify();
 4569    }
 4570
 4571    pub fn has_non_empty_selection(&self, snapshot: &DisplaySnapshot) -> bool {
 4572        self.selections
 4573            .all_adjusted(snapshot)
 4574            .iter()
 4575            .any(|selection| !selection.is_empty())
 4576    }
 4577
 4578    pub fn has_pending_nonempty_selection(&self) -> bool {
 4579        let pending_nonempty_selection = match self.selections.pending_anchor() {
 4580            Some(Selection { start, end, .. }) => start != end,
 4581            None => false,
 4582        };
 4583
 4584        pending_nonempty_selection
 4585            || (self.columnar_selection_state.is_some()
 4586                && self.selections.disjoint_anchors().len() > 1)
 4587    }
 4588
 4589    pub fn has_pending_selection(&self) -> bool {
 4590        self.selections.pending_anchor().is_some() || self.columnar_selection_state.is_some()
 4591    }
 4592
 4593    pub fn cancel(&mut self, _: &Cancel, window: &mut Window, cx: &mut Context<Self>) {
 4594        self.selection_mark_mode = false;
 4595        self.selection_drag_state = SelectionDragState::None;
 4596
 4597        if self.dismiss_menus_and_popups(true, window, cx) {
 4598            cx.notify();
 4599            return;
 4600        }
 4601        if self.clear_expanded_diff_hunks(cx) {
 4602            cx.notify();
 4603            return;
 4604        }
 4605        if self.show_git_blame_gutter {
 4606            self.show_git_blame_gutter = false;
 4607            cx.notify();
 4608            return;
 4609        }
 4610
 4611        if self.mode.is_full()
 4612            && self.change_selections(Default::default(), window, cx, |s| s.try_cancel())
 4613        {
 4614            cx.notify();
 4615            return;
 4616        }
 4617
 4618        cx.propagate();
 4619    }
 4620
 4621    pub fn dismiss_menus_and_popups(
 4622        &mut self,
 4623        is_user_requested: bool,
 4624        window: &mut Window,
 4625        cx: &mut Context<Self>,
 4626    ) -> bool {
 4627        let mut dismissed = false;
 4628
 4629        dismissed |= self.take_rename(false, window, cx).is_some();
 4630        dismissed |= self.hide_blame_popover(true, cx);
 4631        dismissed |= hide_hover(self, cx);
 4632        dismissed |= self.hide_signature_help(cx, SignatureHelpHiddenBy::Escape);
 4633        dismissed |= self.hide_context_menu(window, cx).is_some();
 4634        dismissed |= self.mouse_context_menu.take().is_some();
 4635        dismissed |= is_user_requested
 4636            && self.discard_edit_prediction(EditPredictionDiscardReason::Rejected, cx);
 4637        dismissed |= self.snippet_stack.pop().is_some();
 4638        if self.diff_review_drag_state.is_some() {
 4639            self.cancel_diff_review_drag(cx);
 4640            dismissed = true;
 4641        }
 4642        if !self.diff_review_overlays.is_empty() {
 4643            self.dismiss_all_diff_review_overlays(cx);
 4644            dismissed = true;
 4645        }
 4646
 4647        if self.mode.is_full() && matches!(self.active_diagnostics, ActiveDiagnostic::Group(_)) {
 4648            self.dismiss_diagnostics(cx);
 4649            dismissed = true;
 4650        }
 4651
 4652        dismissed
 4653    }
 4654
 4655    fn linked_editing_ranges_for(
 4656        &self,
 4657        selection: Range<text::Anchor>,
 4658        cx: &App,
 4659    ) -> Option<HashMap<Entity<Buffer>, Vec<Range<text::Anchor>>>> {
 4660        if self.linked_edit_ranges.is_empty() {
 4661            return None;
 4662        }
 4663        let ((base_range, linked_ranges), buffer_snapshot, buffer) =
 4664            selection.end.buffer_id.and_then(|end_buffer_id| {
 4665                if selection.start.buffer_id != Some(end_buffer_id) {
 4666                    return None;
 4667                }
 4668                let buffer = self.buffer.read(cx).buffer(end_buffer_id)?;
 4669                let snapshot = buffer.read(cx).snapshot();
 4670                self.linked_edit_ranges
 4671                    .get(end_buffer_id, selection.start..selection.end, &snapshot)
 4672                    .map(|ranges| (ranges, snapshot, buffer))
 4673            })?;
 4674        use text::ToOffset as TO;
 4675        // find offset from the start of current range to current cursor position
 4676        let start_byte_offset = TO::to_offset(&base_range.start, &buffer_snapshot);
 4677
 4678        let start_offset = TO::to_offset(&selection.start, &buffer_snapshot);
 4679        let start_difference = start_offset - start_byte_offset;
 4680        let end_offset = TO::to_offset(&selection.end, &buffer_snapshot);
 4681        let end_difference = end_offset - start_byte_offset;
 4682
 4683        // Current range has associated linked ranges.
 4684        let mut linked_edits = HashMap::<_, Vec<_>>::default();
 4685        for range in linked_ranges.iter() {
 4686            let start_offset = TO::to_offset(&range.start, &buffer_snapshot);
 4687            let end_offset = start_offset + end_difference;
 4688            let start_offset = start_offset + start_difference;
 4689            if start_offset > buffer_snapshot.len() || end_offset > buffer_snapshot.len() {
 4690                continue;
 4691            }
 4692            if self.selections.disjoint_anchor_ranges().any(|s| {
 4693                if s.start.text_anchor.buffer_id != selection.start.buffer_id
 4694                    || s.end.text_anchor.buffer_id != selection.end.buffer_id
 4695                {
 4696                    return false;
 4697                }
 4698                TO::to_offset(&s.start.text_anchor, &buffer_snapshot) <= end_offset
 4699                    && TO::to_offset(&s.end.text_anchor, &buffer_snapshot) >= start_offset
 4700            }) {
 4701                continue;
 4702            }
 4703            let start = buffer_snapshot.anchor_after(start_offset);
 4704            let end = buffer_snapshot.anchor_after(end_offset);
 4705            linked_edits
 4706                .entry(buffer.clone())
 4707                .or_default()
 4708                .push(start..end);
 4709        }
 4710        Some(linked_edits)
 4711    }
 4712
 4713    pub fn handle_input(&mut self, text: &str, window: &mut Window, cx: &mut Context<Self>) {
 4714        let text: Arc<str> = text.into();
 4715
 4716        if self.read_only(cx) {
 4717            return;
 4718        }
 4719
 4720        self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
 4721
 4722        self.unfold_buffers_with_selections(cx);
 4723
 4724        let selections = self.selections.all_adjusted(&self.display_snapshot(cx));
 4725        let mut bracket_inserted = false;
 4726        let mut edits = Vec::new();
 4727        let mut linked_edits = LinkedEdits::new();
 4728        let mut new_selections = Vec::with_capacity(selections.len());
 4729        let mut new_autoclose_regions = Vec::new();
 4730        let snapshot = self.buffer.read(cx).read(cx);
 4731        let mut clear_linked_edit_ranges = false;
 4732        let mut all_selections_read_only = true;
 4733        let mut has_adjacent_edits = false;
 4734        let mut in_adjacent_group = false;
 4735
 4736        let mut regions = self
 4737            .selections_with_autoclose_regions(selections, &snapshot)
 4738            .peekable();
 4739
 4740        while let Some((selection, autoclose_region)) = regions.next() {
 4741            if snapshot
 4742                .point_to_buffer_point(selection.head())
 4743                .is_none_or(|(snapshot, ..)| !snapshot.capability.editable())
 4744            {
 4745                continue;
 4746            }
 4747            if snapshot
 4748                .point_to_buffer_point(selection.tail())
 4749                .is_none_or(|(snapshot, ..)| !snapshot.capability.editable())
 4750            {
 4751                // note, ideally we'd clip the tail to the closest writeable region towards the head
 4752                continue;
 4753            }
 4754            all_selections_read_only = false;
 4755
 4756            if let Some(scope) = snapshot.language_scope_at(selection.head()) {
 4757                // Determine if the inserted text matches the opening or closing
 4758                // bracket of any of this language's bracket pairs.
 4759                let mut bracket_pair = None;
 4760                let mut is_bracket_pair_start = false;
 4761                let mut is_bracket_pair_end = false;
 4762                if !text.is_empty() {
 4763                    let mut bracket_pair_matching_end = None;
 4764                    // `text` can be empty when a user is using IME (e.g. Chinese Wubi Simplified)
 4765                    //  and they are removing the character that triggered IME popup.
 4766                    for (pair, enabled) in scope.brackets() {
 4767                        if !pair.close && !pair.surround {
 4768                            continue;
 4769                        }
 4770
 4771                        if enabled && pair.start.ends_with(text.as_ref()) {
 4772                            let prefix_len = pair.start.len() - text.len();
 4773                            let preceding_text_matches_prefix = prefix_len == 0
 4774                                || (selection.start.column >= (prefix_len as u32)
 4775                                    && snapshot.contains_str_at(
 4776                                        Point::new(
 4777                                            selection.start.row,
 4778                                            selection.start.column - (prefix_len as u32),
 4779                                        ),
 4780                                        &pair.start[..prefix_len],
 4781                                    ));
 4782                            if preceding_text_matches_prefix {
 4783                                bracket_pair = Some(pair.clone());
 4784                                is_bracket_pair_start = true;
 4785                                break;
 4786                            }
 4787                        }
 4788                        if pair.end.as_str() == text.as_ref() && bracket_pair_matching_end.is_none()
 4789                        {
 4790                            // take first bracket pair matching end, but don't break in case a later bracket
 4791                            // pair matches start
 4792                            bracket_pair_matching_end = Some(pair.clone());
 4793                        }
 4794                    }
 4795                    if let Some(end) = bracket_pair_matching_end
 4796                        && bracket_pair.is_none()
 4797                    {
 4798                        bracket_pair = Some(end);
 4799                        is_bracket_pair_end = true;
 4800                    }
 4801                }
 4802
 4803                if let Some(bracket_pair) = bracket_pair {
 4804                    let snapshot_settings = snapshot.language_settings_at(selection.start, cx);
 4805                    let autoclose = self.use_autoclose && snapshot_settings.use_autoclose;
 4806                    let auto_surround =
 4807                        self.use_auto_surround && snapshot_settings.use_auto_surround;
 4808                    if selection.is_empty() {
 4809                        if is_bracket_pair_start {
 4810                            // If the inserted text is a suffix of an opening bracket and the
 4811                            // selection is preceded by the rest of the opening bracket, then
 4812                            // insert the closing bracket.
 4813                            let following_text_allows_autoclose = snapshot
 4814                                .chars_at(selection.start)
 4815                                .next()
 4816                                .is_none_or(|c| scope.should_autoclose_before(c));
 4817
 4818                            let preceding_text_allows_autoclose = selection.start.column == 0
 4819                                || snapshot
 4820                                    .reversed_chars_at(selection.start)
 4821                                    .next()
 4822                                    .is_none_or(|c| {
 4823                                        bracket_pair.start != bracket_pair.end
 4824                                            || !snapshot
 4825                                                .char_classifier_at(selection.start)
 4826                                                .is_word(c)
 4827                                    });
 4828
 4829                            let is_closing_quote = if bracket_pair.end == bracket_pair.start
 4830                                && bracket_pair.start.len() == 1
 4831                            {
 4832                                let target = bracket_pair.start.chars().next().unwrap();
 4833                                let mut byte_offset = 0u32;
 4834                                let current_line_count = snapshot
 4835                                    .reversed_chars_at(selection.start)
 4836                                    .take_while(|&c| c != '\n')
 4837                                    .filter(|c| {
 4838                                        byte_offset += c.len_utf8() as u32;
 4839                                        if *c != target {
 4840                                            return false;
 4841                                        }
 4842
 4843                                        let point = Point::new(
 4844                                            selection.start.row,
 4845                                            selection.start.column.saturating_sub(byte_offset),
 4846                                        );
 4847
 4848                                        let is_enabled = snapshot
 4849                                            .language_scope_at(point)
 4850                                            .and_then(|scope| {
 4851                                                scope
 4852                                                    .brackets()
 4853                                                    .find(|(pair, _)| {
 4854                                                        pair.start == bracket_pair.start
 4855                                                    })
 4856                                                    .map(|(_, enabled)| enabled)
 4857                                            })
 4858                                            .unwrap_or(true);
 4859
 4860                                        let is_delimiter = snapshot
 4861                                            .language_scope_at(Point::new(
 4862                                                point.row,
 4863                                                point.column + 1,
 4864                                            ))
 4865                                            .and_then(|scope| {
 4866                                                scope
 4867                                                    .brackets()
 4868                                                    .find(|(pair, _)| {
 4869                                                        pair.start == bracket_pair.start
 4870                                                    })
 4871                                                    .map(|(_, enabled)| !enabled)
 4872                                            })
 4873                                            .unwrap_or(false);
 4874
 4875                                        is_enabled && !is_delimiter
 4876                                    })
 4877                                    .count();
 4878                                current_line_count % 2 == 1
 4879                            } else {
 4880                                false
 4881                            };
 4882
 4883                            if autoclose
 4884                                && bracket_pair.close
 4885                                && following_text_allows_autoclose
 4886                                && preceding_text_allows_autoclose
 4887                                && !is_closing_quote
 4888                            {
 4889                                let anchor = snapshot.anchor_before(selection.end);
 4890                                new_selections.push((selection.map(|_| anchor), text.len()));
 4891                                new_autoclose_regions.push((
 4892                                    anchor,
 4893                                    text.len(),
 4894                                    selection.id,
 4895                                    bracket_pair.clone(),
 4896                                ));
 4897                                edits.push((
 4898                                    selection.range(),
 4899                                    format!("{}{}", text, bracket_pair.end).into(),
 4900                                ));
 4901                                bracket_inserted = true;
 4902                                continue;
 4903                            }
 4904                        }
 4905
 4906                        if let Some(region) = autoclose_region {
 4907                            // If the selection is followed by an auto-inserted closing bracket,
 4908                            // then don't insert that closing bracket again; just move the selection
 4909                            // past the closing bracket.
 4910                            let should_skip = selection.end == region.range.end.to_point(&snapshot)
 4911                                && text.as_ref() == region.pair.end.as_str()
 4912                                && snapshot.contains_str_at(region.range.end, text.as_ref());
 4913                            if should_skip {
 4914                                let anchor = snapshot.anchor_after(selection.end);
 4915                                new_selections
 4916                                    .push((selection.map(|_| anchor), region.pair.end.len()));
 4917                                continue;
 4918                            }
 4919                        }
 4920
 4921                        let always_treat_brackets_as_autoclosed = snapshot
 4922                            .language_settings_at(selection.start, cx)
 4923                            .always_treat_brackets_as_autoclosed;
 4924                        if always_treat_brackets_as_autoclosed
 4925                            && is_bracket_pair_end
 4926                            && snapshot.contains_str_at(selection.end, text.as_ref())
 4927                        {
 4928                            // Otherwise, when `always_treat_brackets_as_autoclosed` is set to `true
 4929                            // and the inserted text is a closing bracket and the selection is followed
 4930                            // by the closing bracket then move the selection past the closing bracket.
 4931                            let anchor = snapshot.anchor_after(selection.end);
 4932                            new_selections.push((selection.map(|_| anchor), text.len()));
 4933                            continue;
 4934                        }
 4935                    }
 4936                    // If an opening bracket is 1 character long and is typed while
 4937                    // text is selected, then surround that text with the bracket pair.
 4938                    else if auto_surround
 4939                        && bracket_pair.surround
 4940                        && is_bracket_pair_start
 4941                        && bracket_pair.start.chars().count() == 1
 4942                    {
 4943                        edits.push((selection.start..selection.start, text.clone()));
 4944                        edits.push((
 4945                            selection.end..selection.end,
 4946                            bracket_pair.end.as_str().into(),
 4947                        ));
 4948                        bracket_inserted = true;
 4949                        new_selections.push((
 4950                            Selection {
 4951                                id: selection.id,
 4952                                start: snapshot.anchor_after(selection.start),
 4953                                end: snapshot.anchor_before(selection.end),
 4954                                reversed: selection.reversed,
 4955                                goal: selection.goal,
 4956                            },
 4957                            0,
 4958                        ));
 4959                        continue;
 4960                    }
 4961                }
 4962            }
 4963
 4964            if self.auto_replace_emoji_shortcode
 4965                && selection.is_empty()
 4966                && text.as_ref().ends_with(':')
 4967                && let Some(possible_emoji_short_code) =
 4968                    Self::find_possible_emoji_shortcode_at_position(&snapshot, selection.start)
 4969                && !possible_emoji_short_code.is_empty()
 4970                && let Some(emoji) = emojis::get_by_shortcode(&possible_emoji_short_code)
 4971            {
 4972                let emoji_shortcode_start = Point::new(
 4973                    selection.start.row,
 4974                    selection.start.column - possible_emoji_short_code.len() as u32 - 1,
 4975                );
 4976
 4977                // Remove shortcode from buffer
 4978                edits.push((
 4979                    emoji_shortcode_start..selection.start,
 4980                    "".to_string().into(),
 4981                ));
 4982                new_selections.push((
 4983                    Selection {
 4984                        id: selection.id,
 4985                        start: snapshot.anchor_after(emoji_shortcode_start),
 4986                        end: snapshot.anchor_before(selection.start),
 4987                        reversed: selection.reversed,
 4988                        goal: selection.goal,
 4989                    },
 4990                    0,
 4991                ));
 4992
 4993                // Insert emoji
 4994                let selection_start_anchor = snapshot.anchor_after(selection.start);
 4995                new_selections.push((selection.map(|_| selection_start_anchor), 0));
 4996                edits.push((selection.start..selection.end, emoji.to_string().into()));
 4997
 4998                continue;
 4999            }
 5000
 5001            let next_is_adjacent = regions
 5002                .peek()
 5003                .is_some_and(|(next, _)| selection.end == next.start);
 5004
 5005            // If not handling any auto-close operation, then just replace the selected
 5006            // text with the given input and move the selection to the end of the
 5007            // newly inserted text.
 5008            let anchor = if in_adjacent_group || next_is_adjacent {
 5009                // After edits the right bias would shift those anchor to the next visible fragment
 5010                // but we want to resolve to the previous one
 5011                snapshot.anchor_before(selection.end)
 5012            } else {
 5013                snapshot.anchor_after(selection.end)
 5014            };
 5015
 5016            if !self.linked_edit_ranges.is_empty() {
 5017                let start_anchor = snapshot.anchor_before(selection.start);
 5018
 5019                let is_word_char = text.chars().next().is_none_or(|char| {
 5020                    let classifier = snapshot
 5021                        .char_classifier_at(start_anchor.to_offset(&snapshot))
 5022                        .scope_context(Some(CharScopeContext::LinkedEdit));
 5023                    classifier.is_word(char)
 5024                });
 5025                let is_dot = text.as_ref() == ".";
 5026                let should_apply_linked_edit = is_word_char || is_dot;
 5027
 5028                if should_apply_linked_edit {
 5029                    let anchor_range = start_anchor.text_anchor..anchor.text_anchor;
 5030                    linked_edits.push(&self, anchor_range, text.clone(), cx);
 5031                } else {
 5032                    clear_linked_edit_ranges = true;
 5033                }
 5034            }
 5035
 5036            new_selections.push((selection.map(|_| anchor), 0));
 5037            edits.push((selection.start..selection.end, text.clone()));
 5038
 5039            has_adjacent_edits |= next_is_adjacent;
 5040            in_adjacent_group = next_is_adjacent;
 5041        }
 5042
 5043        if all_selections_read_only {
 5044            return;
 5045        }
 5046
 5047        drop(regions);
 5048        drop(snapshot);
 5049
 5050        self.transact(window, cx, |this, window, cx| {
 5051            if clear_linked_edit_ranges {
 5052                this.linked_edit_ranges.clear();
 5053            }
 5054            let initial_buffer_versions =
 5055                jsx_tag_auto_close::construct_initial_buffer_versions_map(this, &edits, cx);
 5056
 5057            this.buffer.update(cx, |buffer, cx| {
 5058                if has_adjacent_edits {
 5059                    buffer.edit_non_coalesce(edits, this.autoindent_mode.clone(), cx);
 5060                } else {
 5061                    buffer.edit(edits, this.autoindent_mode.clone(), cx);
 5062                }
 5063            });
 5064            linked_edits.apply(cx);
 5065            let new_anchor_selections = new_selections.iter().map(|e| &e.0);
 5066            let new_selection_deltas = new_selections.iter().map(|e| e.1);
 5067            let map = this.display_map.update(cx, |map, cx| map.snapshot(cx));
 5068            let new_selections = resolve_selections_wrapping_blocks::<MultiBufferOffset, _>(
 5069                new_anchor_selections,
 5070                &map,
 5071            )
 5072            .zip(new_selection_deltas)
 5073            .map(|(selection, delta)| Selection {
 5074                id: selection.id,
 5075                start: selection.start + delta,
 5076                end: selection.end + delta,
 5077                reversed: selection.reversed,
 5078                goal: SelectionGoal::None,
 5079            })
 5080            .collect::<Vec<_>>();
 5081
 5082            let mut i = 0;
 5083            for (position, delta, selection_id, pair) in new_autoclose_regions {
 5084                let position = position.to_offset(map.buffer_snapshot()) + delta;
 5085                let start = map.buffer_snapshot().anchor_before(position);
 5086                let end = map.buffer_snapshot().anchor_after(position);
 5087                while let Some(existing_state) = this.autoclose_regions.get(i) {
 5088                    match existing_state
 5089                        .range
 5090                        .start
 5091                        .cmp(&start, map.buffer_snapshot())
 5092                    {
 5093                        Ordering::Less => i += 1,
 5094                        Ordering::Greater => break,
 5095                        Ordering::Equal => {
 5096                            match end.cmp(&existing_state.range.end, map.buffer_snapshot()) {
 5097                                Ordering::Less => i += 1,
 5098                                Ordering::Equal => break,
 5099                                Ordering::Greater => break,
 5100                            }
 5101                        }
 5102                    }
 5103                }
 5104                this.autoclose_regions.insert(
 5105                    i,
 5106                    AutocloseRegion {
 5107                        selection_id,
 5108                        range: start..end,
 5109                        pair,
 5110                    },
 5111                );
 5112            }
 5113
 5114            let had_active_edit_prediction = this.has_active_edit_prediction();
 5115            this.change_selections(
 5116                SelectionEffects::scroll(Autoscroll::fit()).completions(false),
 5117                window,
 5118                cx,
 5119                |s| s.select(new_selections),
 5120            );
 5121
 5122            if !bracket_inserted
 5123                && let Some(on_type_format_task) =
 5124                    this.trigger_on_type_formatting(text.to_string(), window, cx)
 5125            {
 5126                on_type_format_task.detach_and_log_err(cx);
 5127            }
 5128
 5129            let editor_settings = EditorSettings::get_global(cx);
 5130            if bracket_inserted
 5131                && (editor_settings.auto_signature_help
 5132                    || editor_settings.show_signature_help_after_edits)
 5133            {
 5134                this.show_signature_help(&ShowSignatureHelp, window, cx);
 5135            }
 5136
 5137            let trigger_in_words =
 5138                this.show_edit_predictions_in_menu() || !had_active_edit_prediction;
 5139            if this.hard_wrap.is_some() {
 5140                let latest: Range<Point> = this.selections.newest(&map).range();
 5141                if latest.is_empty()
 5142                    && this
 5143                        .buffer()
 5144                        .read(cx)
 5145                        .snapshot(cx)
 5146                        .line_len(MultiBufferRow(latest.start.row))
 5147                        == latest.start.column
 5148                {
 5149                    this.rewrap_impl(
 5150                        RewrapOptions {
 5151                            override_language_settings: true,
 5152                            preserve_existing_whitespace: true,
 5153                            line_length: None,
 5154                        },
 5155                        cx,
 5156                    )
 5157                }
 5158            }
 5159            this.trigger_completion_on_input(&text, trigger_in_words, window, cx);
 5160            refresh_linked_ranges(this, window, cx);
 5161            this.refresh_edit_prediction(true, false, window, cx);
 5162            jsx_tag_auto_close::handle_from(this, initial_buffer_versions, window, cx);
 5163        });
 5164    }
 5165
 5166    fn find_possible_emoji_shortcode_at_position(
 5167        snapshot: &MultiBufferSnapshot,
 5168        position: Point,
 5169    ) -> Option<String> {
 5170        let mut chars = Vec::new();
 5171        let mut found_colon = false;
 5172        for char in snapshot.reversed_chars_at(position).take(100) {
 5173            // Found a possible emoji shortcode in the middle of the buffer
 5174            if found_colon {
 5175                if char.is_whitespace() {
 5176                    chars.reverse();
 5177                    return Some(chars.iter().collect());
 5178                }
 5179                // If the previous character is not a whitespace, we are in the middle of a word
 5180                // and we only want to complete the shortcode if the word is made up of other emojis
 5181                let mut containing_word = String::new();
 5182                for ch in snapshot
 5183                    .reversed_chars_at(position)
 5184                    .skip(chars.len() + 1)
 5185                    .take(100)
 5186                {
 5187                    if ch.is_whitespace() {
 5188                        break;
 5189                    }
 5190                    containing_word.push(ch);
 5191                }
 5192                let containing_word = containing_word.chars().rev().collect::<String>();
 5193                if util::word_consists_of_emojis(containing_word.as_str()) {
 5194                    chars.reverse();
 5195                    return Some(chars.iter().collect());
 5196                }
 5197            }
 5198
 5199            if char.is_whitespace() || !char.is_ascii() {
 5200                return None;
 5201            }
 5202            if char == ':' {
 5203                found_colon = true;
 5204            } else {
 5205                chars.push(char);
 5206            }
 5207        }
 5208        // Found a possible emoji shortcode at the beginning of the buffer
 5209        chars.reverse();
 5210        Some(chars.iter().collect())
 5211    }
 5212
 5213    pub fn newline(&mut self, _: &Newline, window: &mut Window, cx: &mut Context<Self>) {
 5214        if self.read_only(cx) {
 5215            return;
 5216        }
 5217
 5218        self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
 5219        self.transact(window, cx, |this, window, cx| {
 5220            let (edits_with_flags, selection_info): (Vec<_>, Vec<_>) = {
 5221                let selections = this
 5222                    .selections
 5223                    .all::<MultiBufferOffset>(&this.display_snapshot(cx));
 5224                let multi_buffer = this.buffer.read(cx);
 5225                let buffer = multi_buffer.snapshot(cx);
 5226                selections
 5227                    .iter()
 5228                    .map(|selection| {
 5229                        let start_point = selection.start.to_point(&buffer);
 5230                        let mut existing_indent =
 5231                            buffer.indent_size_for_line(MultiBufferRow(start_point.row));
 5232                        existing_indent.len = cmp::min(existing_indent.len, start_point.column);
 5233                        let start = selection.start;
 5234                        let end = selection.end;
 5235                        let selection_is_empty = start == end;
 5236                        let language_scope = buffer.language_scope_at(start);
 5237                        let (delimiter, newline_config) = if let Some(language) = &language_scope {
 5238                            let needs_extra_newline = NewlineConfig::insert_extra_newline_brackets(
 5239                                &buffer,
 5240                                start..end,
 5241                                language,
 5242                            )
 5243                                || NewlineConfig::insert_extra_newline_tree_sitter(
 5244                                    &buffer,
 5245                                    start..end,
 5246                                );
 5247
 5248                            let mut newline_config = NewlineConfig::Newline {
 5249                                additional_indent: IndentSize::spaces(0),
 5250                                extra_line_additional_indent: if needs_extra_newline {
 5251                                    Some(IndentSize::spaces(0))
 5252                                } else {
 5253                                    None
 5254                                },
 5255                                prevent_auto_indent: false,
 5256                            };
 5257
 5258                            let comment_delimiter = maybe!({
 5259                                if !selection_is_empty {
 5260                                    return None;
 5261                                }
 5262
 5263                                if !multi_buffer.language_settings(cx).extend_comment_on_newline {
 5264                                    return None;
 5265                                }
 5266
 5267                                return comment_delimiter_for_newline(
 5268                                    &start_point,
 5269                                    &buffer,
 5270                                    language,
 5271                                );
 5272                            });
 5273
 5274                            let doc_delimiter = maybe!({
 5275                                if !selection_is_empty {
 5276                                    return None;
 5277                                }
 5278
 5279                                if !multi_buffer.language_settings(cx).extend_comment_on_newline {
 5280                                    return None;
 5281                                }
 5282
 5283                                return documentation_delimiter_for_newline(
 5284                                    &start_point,
 5285                                    &buffer,
 5286                                    language,
 5287                                    &mut newline_config,
 5288                                );
 5289                            });
 5290
 5291                            let list_delimiter = maybe!({
 5292                                if !selection_is_empty {
 5293                                    return None;
 5294                                }
 5295
 5296                                if !multi_buffer.language_settings(cx).extend_list_on_newline {
 5297                                    return None;
 5298                                }
 5299
 5300                                return list_delimiter_for_newline(
 5301                                    &start_point,
 5302                                    &buffer,
 5303                                    language,
 5304                                    &mut newline_config,
 5305                                );
 5306                            });
 5307
 5308                            (
 5309                                comment_delimiter.or(doc_delimiter).or(list_delimiter),
 5310                                newline_config,
 5311                            )
 5312                        } else {
 5313                            (
 5314                                None,
 5315                                NewlineConfig::Newline {
 5316                                    additional_indent: IndentSize::spaces(0),
 5317                                    extra_line_additional_indent: None,
 5318                                    prevent_auto_indent: false,
 5319                                },
 5320                            )
 5321                        };
 5322
 5323                        let (edit_start, new_text, prevent_auto_indent) = match &newline_config {
 5324                            NewlineConfig::ClearCurrentLine => {
 5325                                let row_start =
 5326                                    buffer.point_to_offset(Point::new(start_point.row, 0));
 5327                                (row_start, String::new(), false)
 5328                            }
 5329                            NewlineConfig::UnindentCurrentLine { continuation } => {
 5330                                let row_start =
 5331                                    buffer.point_to_offset(Point::new(start_point.row, 0));
 5332                                let tab_size = buffer.language_settings_at(start, cx).tab_size;
 5333                                let tab_size_indent = IndentSize::spaces(tab_size.get());
 5334                                let reduced_indent =
 5335                                    existing_indent.with_delta(Ordering::Less, tab_size_indent);
 5336                                let mut new_text = String::new();
 5337                                new_text.extend(reduced_indent.chars());
 5338                                new_text.push_str(continuation);
 5339                                (row_start, new_text, true)
 5340                            }
 5341                            NewlineConfig::Newline {
 5342                                additional_indent,
 5343                                extra_line_additional_indent,
 5344                                prevent_auto_indent,
 5345                            } => {
 5346                                let auto_indent_mode =
 5347                                    buffer.language_settings_at(start, cx).auto_indent;
 5348                                let preserve_indent =
 5349                                    auto_indent_mode != language::AutoIndentMode::None;
 5350                                let apply_syntax_indent =
 5351                                    auto_indent_mode == language::AutoIndentMode::SyntaxAware;
 5352                                let capacity_for_delimiter =
 5353                                    delimiter.as_deref().map(str::len).unwrap_or_default();
 5354                                let existing_indent_len = if preserve_indent {
 5355                                    existing_indent.len as usize
 5356                                } else {
 5357                                    0
 5358                                };
 5359                                let extra_line_len = extra_line_additional_indent
 5360                                    .map(|i| 1 + existing_indent_len + i.len as usize)
 5361                                    .unwrap_or(0);
 5362                                let mut new_text = String::with_capacity(
 5363                                    1 + capacity_for_delimiter
 5364                                        + existing_indent_len
 5365                                        + additional_indent.len as usize
 5366                                        + extra_line_len,
 5367                                );
 5368                                new_text.push('\n');
 5369                                if preserve_indent {
 5370                                    new_text.extend(existing_indent.chars());
 5371                                }
 5372                                new_text.extend(additional_indent.chars());
 5373                                if let Some(delimiter) = &delimiter {
 5374                                    new_text.push_str(delimiter);
 5375                                }
 5376                                if let Some(extra_indent) = extra_line_additional_indent {
 5377                                    new_text.push('\n');
 5378                                    if preserve_indent {
 5379                                        new_text.extend(existing_indent.chars());
 5380                                    }
 5381                                    new_text.extend(extra_indent.chars());
 5382                                }
 5383                                (
 5384                                    start,
 5385                                    new_text,
 5386                                    *prevent_auto_indent || !apply_syntax_indent,
 5387                                )
 5388                            }
 5389                        };
 5390
 5391                        let anchor = buffer.anchor_after(end);
 5392                        let new_selection = selection.map(|_| anchor);
 5393                        (
 5394                            ((edit_start..end, new_text), prevent_auto_indent),
 5395                            (newline_config.has_extra_line(), new_selection),
 5396                        )
 5397                    })
 5398                    .unzip()
 5399            };
 5400
 5401            let mut auto_indent_edits = Vec::new();
 5402            let mut edits = Vec::new();
 5403            for (edit, prevent_auto_indent) in edits_with_flags {
 5404                if prevent_auto_indent {
 5405                    edits.push(edit);
 5406                } else {
 5407                    auto_indent_edits.push(edit);
 5408                }
 5409            }
 5410            if !edits.is_empty() {
 5411                this.edit(edits, cx);
 5412            }
 5413            if !auto_indent_edits.is_empty() {
 5414                this.edit_with_autoindent(auto_indent_edits, cx);
 5415            }
 5416
 5417            let buffer = this.buffer.read(cx).snapshot(cx);
 5418            let new_selections = selection_info
 5419                .into_iter()
 5420                .map(|(extra_newline_inserted, new_selection)| {
 5421                    let mut cursor = new_selection.end.to_point(&buffer);
 5422                    if extra_newline_inserted {
 5423                        cursor.row -= 1;
 5424                        cursor.column = buffer.line_len(MultiBufferRow(cursor.row));
 5425                    }
 5426                    new_selection.map(|_| cursor)
 5427                })
 5428                .collect();
 5429
 5430            this.change_selections(Default::default(), window, cx, |s| s.select(new_selections));
 5431            this.refresh_edit_prediction(true, false, window, cx);
 5432            if let Some(task) = this.trigger_on_type_formatting("\n".to_owned(), window, cx) {
 5433                task.detach_and_log_err(cx);
 5434            }
 5435        });
 5436    }
 5437
 5438    pub fn newline_above(&mut self, _: &NewlineAbove, window: &mut Window, cx: &mut Context<Self>) {
 5439        if self.read_only(cx) {
 5440            return;
 5441        }
 5442
 5443        self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
 5444
 5445        let buffer = self.buffer.read(cx);
 5446        let snapshot = buffer.snapshot(cx);
 5447
 5448        let mut edits = Vec::new();
 5449        let mut rows = Vec::new();
 5450
 5451        for (rows_inserted, selection) in self
 5452            .selections
 5453            .all_adjusted(&self.display_snapshot(cx))
 5454            .into_iter()
 5455            .enumerate()
 5456        {
 5457            let cursor = selection.head();
 5458            let row = cursor.row;
 5459
 5460            let start_of_line = snapshot.clip_point(Point::new(row, 0), Bias::Left);
 5461
 5462            let newline = "\n".to_string();
 5463            edits.push((start_of_line..start_of_line, newline));
 5464
 5465            rows.push(row + rows_inserted as u32);
 5466        }
 5467
 5468        self.transact(window, cx, |editor, window, cx| {
 5469            editor.edit(edits, cx);
 5470
 5471            editor.change_selections(Default::default(), window, cx, |s| {
 5472                let mut index = 0;
 5473                s.move_cursors_with(&mut |map, _, _| {
 5474                    let row = rows[index];
 5475                    index += 1;
 5476
 5477                    let point = Point::new(row, 0);
 5478                    let boundary = map.next_line_boundary(point).1;
 5479                    let clipped = map.clip_point(boundary, Bias::Left);
 5480
 5481                    (clipped, SelectionGoal::None)
 5482                });
 5483            });
 5484
 5485            let mut indent_edits = Vec::new();
 5486            let multibuffer_snapshot = editor.buffer.read(cx).snapshot(cx);
 5487            for row in rows {
 5488                let indents = multibuffer_snapshot.suggested_indents(row..row + 1, cx);
 5489                for (row, indent) in indents {
 5490                    if indent.len == 0 {
 5491                        continue;
 5492                    }
 5493
 5494                    let text = match indent.kind {
 5495                        IndentKind::Space => " ".repeat(indent.len as usize),
 5496                        IndentKind::Tab => "\t".repeat(indent.len as usize),
 5497                    };
 5498                    let point = Point::new(row.0, 0);
 5499                    indent_edits.push((point..point, text));
 5500                }
 5501            }
 5502            editor.edit(indent_edits, cx);
 5503            if let Some(format) = editor.trigger_on_type_formatting("\n".to_owned(), window, cx) {
 5504                format.detach_and_log_err(cx);
 5505            }
 5506        });
 5507    }
 5508
 5509    pub fn newline_below(&mut self, _: &NewlineBelow, window: &mut Window, cx: &mut Context<Self>) {
 5510        if self.read_only(cx) {
 5511            return;
 5512        }
 5513
 5514        self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
 5515
 5516        let mut buffer_edits: HashMap<EntityId, (Entity<Buffer>, Vec<Point>)> = HashMap::default();
 5517        let mut rows = Vec::new();
 5518        let mut rows_inserted = 0;
 5519
 5520        for selection in self.selections.all_adjusted(&self.display_snapshot(cx)) {
 5521            let cursor = selection.head();
 5522            let row = cursor.row;
 5523
 5524            let point = Point::new(row, 0);
 5525            let Some((buffer_handle, buffer_point, _)) =
 5526                self.buffer.read(cx).point_to_buffer_point(point, cx)
 5527            else {
 5528                continue;
 5529            };
 5530
 5531            buffer_edits
 5532                .entry(buffer_handle.entity_id())
 5533                .or_insert_with(|| (buffer_handle, Vec::new()))
 5534                .1
 5535                .push(buffer_point);
 5536
 5537            rows_inserted += 1;
 5538            rows.push(row + rows_inserted);
 5539        }
 5540
 5541        self.transact(window, cx, |editor, window, cx| {
 5542            for (_, (buffer_handle, points)) in &buffer_edits {
 5543                buffer_handle.update(cx, |buffer, cx| {
 5544                    let edits: Vec<_> = points
 5545                        .iter()
 5546                        .map(|point| {
 5547                            let target = Point::new(point.row + 1, 0);
 5548                            let start_of_line = buffer.point_to_offset(target).min(buffer.len());
 5549                            (start_of_line..start_of_line, "\n")
 5550                        })
 5551                        .collect();
 5552                    buffer.edit(edits, None, cx);
 5553                });
 5554            }
 5555
 5556            editor.change_selections(Default::default(), window, cx, |s| {
 5557                let mut index = 0;
 5558                s.move_cursors_with(&mut |map, _, _| {
 5559                    let row = rows[index];
 5560                    index += 1;
 5561
 5562                    let point = Point::new(row, 0);
 5563                    let boundary = map.next_line_boundary(point).1;
 5564                    let clipped = map.clip_point(boundary, Bias::Left);
 5565
 5566                    (clipped, SelectionGoal::None)
 5567                });
 5568            });
 5569
 5570            let mut indent_edits = Vec::new();
 5571            let multibuffer_snapshot = editor.buffer.read(cx).snapshot(cx);
 5572            for row in rows {
 5573                let indents = multibuffer_snapshot.suggested_indents(row..row + 1, cx);
 5574                for (row, indent) in indents {
 5575                    if indent.len == 0 {
 5576                        continue;
 5577                    }
 5578
 5579                    let text = match indent.kind {
 5580                        IndentKind::Space => " ".repeat(indent.len as usize),
 5581                        IndentKind::Tab => "\t".repeat(indent.len as usize),
 5582                    };
 5583                    let point = Point::new(row.0, 0);
 5584                    indent_edits.push((point..point, text));
 5585                }
 5586            }
 5587            editor.edit(indent_edits, cx);
 5588            if let Some(format) = editor.trigger_on_type_formatting("\n".to_owned(), window, cx) {
 5589                format.detach_and_log_err(cx);
 5590            }
 5591        });
 5592    }
 5593
 5594    pub fn insert(&mut self, text: &str, window: &mut Window, cx: &mut Context<Self>) {
 5595        let autoindent = text.is_empty().not().then(|| AutoindentMode::Block {
 5596            original_indent_columns: Vec::new(),
 5597        });
 5598        self.replace_selections(text, autoindent, window, cx, false);
 5599    }
 5600
 5601    /// Replaces the editor's selections with the provided `text`, applying the
 5602    /// given `autoindent_mode` (`None` will skip autoindentation).
 5603    ///
 5604    /// Early returns if the editor is in read-only mode, without applying any
 5605    /// edits.
 5606    fn replace_selections(
 5607        &mut self,
 5608        text: &str,
 5609        autoindent_mode: Option<AutoindentMode>,
 5610        window: &mut Window,
 5611        cx: &mut Context<Self>,
 5612        apply_linked_edits: bool,
 5613    ) {
 5614        if self.read_only(cx) {
 5615            return;
 5616        }
 5617
 5618        let text: Arc<str> = text.into();
 5619        self.transact(window, cx, |this, window, cx| {
 5620            let old_selections = this.selections.all_adjusted(&this.display_snapshot(cx));
 5621            let linked_edits = if apply_linked_edits {
 5622                this.linked_edits_for_selections(text.clone(), cx)
 5623            } else {
 5624                LinkedEdits::new()
 5625            };
 5626
 5627            let selection_anchors = this.buffer.update(cx, |buffer, cx| {
 5628                let anchors = {
 5629                    let snapshot = buffer.read(cx);
 5630                    old_selections
 5631                        .iter()
 5632                        .map(|s| {
 5633                            let anchor = snapshot.anchor_after(s.head());
 5634                            s.map(|_| anchor)
 5635                        })
 5636                        .collect::<Vec<_>>()
 5637                };
 5638                buffer.edit(
 5639                    old_selections
 5640                        .iter()
 5641                        .map(|s| (s.start..s.end, text.clone())),
 5642                    autoindent_mode,
 5643                    cx,
 5644                );
 5645                anchors
 5646            });
 5647
 5648            linked_edits.apply(cx);
 5649
 5650            this.change_selections(Default::default(), window, cx, |s| {
 5651                s.select_anchors(selection_anchors);
 5652            });
 5653
 5654            if apply_linked_edits {
 5655                refresh_linked_ranges(this, window, cx);
 5656            }
 5657
 5658            cx.notify();
 5659        });
 5660    }
 5661
 5662    /// Collects linked edits for the current selections, pairing each linked
 5663    /// range with `text`.
 5664    pub fn linked_edits_for_selections(&self, text: Arc<str>, cx: &App) -> LinkedEdits {
 5665        let mut linked_edits = LinkedEdits::new();
 5666        if !self.linked_edit_ranges.is_empty() {
 5667            for selection in self.selections.disjoint_anchors() {
 5668                let start = selection.start.text_anchor;
 5669                let end = selection.end.text_anchor;
 5670                linked_edits.push(self, start..end, text.clone(), cx);
 5671            }
 5672        }
 5673        linked_edits
 5674    }
 5675
 5676    /// Deletes the content covered by the current selections and applies
 5677    /// linked edits.
 5678    pub fn delete_selections_with_linked_edits(
 5679        &mut self,
 5680        window: &mut Window,
 5681        cx: &mut Context<Self>,
 5682    ) {
 5683        self.replace_selections("", None, window, cx, true);
 5684    }
 5685
 5686    #[cfg(any(test, feature = "test-support"))]
 5687    pub fn set_linked_edit_ranges_for_testing(
 5688        &mut self,
 5689        ranges: Vec<(Range<Point>, Vec<Range<Point>>)>,
 5690        cx: &mut Context<Self>,
 5691    ) -> Option<()> {
 5692        let Some((buffer, _)) = self
 5693            .buffer
 5694            .read(cx)
 5695            .text_anchor_for_position(self.selections.newest_anchor().start, cx)
 5696        else {
 5697            return None;
 5698        };
 5699        let buffer = buffer.read(cx);
 5700        let buffer_id = buffer.remote_id();
 5701        let mut linked_ranges = Vec::with_capacity(ranges.len());
 5702        for (base_range, linked_ranges_points) in ranges {
 5703            let base_anchor =
 5704                buffer.anchor_before(base_range.start)..buffer.anchor_after(base_range.end);
 5705            let linked_anchors = linked_ranges_points
 5706                .into_iter()
 5707                .map(|range| buffer.anchor_before(range.start)..buffer.anchor_after(range.end))
 5708                .collect();
 5709            linked_ranges.push((base_anchor, linked_anchors));
 5710        }
 5711        let mut map = HashMap::default();
 5712        map.insert(buffer_id, linked_ranges);
 5713        self.linked_edit_ranges = linked_editing_ranges::LinkedEditingRanges(map);
 5714        Some(())
 5715    }
 5716
 5717    fn trigger_completion_on_input(
 5718        &mut self,
 5719        text: &str,
 5720        trigger_in_words: bool,
 5721        window: &mut Window,
 5722        cx: &mut Context<Self>,
 5723    ) {
 5724        let completions_source = self
 5725            .context_menu
 5726            .borrow()
 5727            .as_ref()
 5728            .and_then(|menu| match menu {
 5729                CodeContextMenu::Completions(completions_menu) => Some(completions_menu.source),
 5730                CodeContextMenu::CodeActions(_) => None,
 5731            });
 5732
 5733        match completions_source {
 5734            Some(CompletionsMenuSource::Words { .. }) => {
 5735                self.open_or_update_completions_menu(
 5736                    Some(CompletionsMenuSource::Words {
 5737                        ignore_threshold: false,
 5738                    }),
 5739                    None,
 5740                    trigger_in_words,
 5741                    window,
 5742                    cx,
 5743                );
 5744            }
 5745            _ => self.open_or_update_completions_menu(
 5746                None,
 5747                Some(text.to_owned()).filter(|x| !x.is_empty()),
 5748                true,
 5749                window,
 5750                cx,
 5751            ),
 5752        }
 5753    }
 5754
 5755    /// If any empty selections is touching the start of its innermost containing autoclose
 5756    /// region, expand it to select the brackets.
 5757    fn select_autoclose_pair(&mut self, window: &mut Window, cx: &mut Context<Self>) {
 5758        let selections = self
 5759            .selections
 5760            .all::<MultiBufferOffset>(&self.display_snapshot(cx));
 5761        let buffer = self.buffer.read(cx).read(cx);
 5762        let new_selections = self
 5763            .selections_with_autoclose_regions(selections, &buffer)
 5764            .map(|(mut selection, region)| {
 5765                if !selection.is_empty() {
 5766                    return selection;
 5767                }
 5768
 5769                if let Some(region) = region {
 5770                    let mut range = region.range.to_offset(&buffer);
 5771                    if selection.start == range.start && range.start.0 >= region.pair.start.len() {
 5772                        range.start -= region.pair.start.len();
 5773                        if buffer.contains_str_at(range.start, &region.pair.start)
 5774                            && buffer.contains_str_at(range.end, &region.pair.end)
 5775                        {
 5776                            range.end += region.pair.end.len();
 5777                            selection.start = range.start;
 5778                            selection.end = range.end;
 5779
 5780                            return selection;
 5781                        }
 5782                    }
 5783                }
 5784
 5785                let always_treat_brackets_as_autoclosed = buffer
 5786                    .language_settings_at(selection.start, cx)
 5787                    .always_treat_brackets_as_autoclosed;
 5788
 5789                if !always_treat_brackets_as_autoclosed {
 5790                    return selection;
 5791                }
 5792
 5793                if let Some(scope) = buffer.language_scope_at(selection.start) {
 5794                    for (pair, enabled) in scope.brackets() {
 5795                        if !enabled || !pair.close {
 5796                            continue;
 5797                        }
 5798
 5799                        if buffer.contains_str_at(selection.start, &pair.end) {
 5800                            let pair_start_len = pair.start.len();
 5801                            if buffer.contains_str_at(
 5802                                selection.start.saturating_sub_usize(pair_start_len),
 5803                                &pair.start,
 5804                            ) {
 5805                                selection.start -= pair_start_len;
 5806                                selection.end += pair.end.len();
 5807
 5808                                return selection;
 5809                            }
 5810                        }
 5811                    }
 5812                }
 5813
 5814                selection
 5815            })
 5816            .collect();
 5817
 5818        drop(buffer);
 5819        self.change_selections(SelectionEffects::no_scroll(), window, cx, |selections| {
 5820            selections.select(new_selections)
 5821        });
 5822    }
 5823
 5824    /// Iterate the given selections, and for each one, find the smallest surrounding
 5825    /// autoclose region. This uses the ordering of the selections and the autoclose
 5826    /// regions to avoid repeated comparisons.
 5827    fn selections_with_autoclose_regions<'a, D: ToOffset + Clone>(
 5828        &'a self,
 5829        selections: impl IntoIterator<Item = Selection<D>>,
 5830        buffer: &'a MultiBufferSnapshot,
 5831    ) -> impl Iterator<Item = (Selection<D>, Option<&'a AutocloseRegion>)> {
 5832        let mut i = 0;
 5833        let mut regions = self.autoclose_regions.as_slice();
 5834        selections.into_iter().map(move |selection| {
 5835            let range = selection.start.to_offset(buffer)..selection.end.to_offset(buffer);
 5836
 5837            let mut enclosing = None;
 5838            while let Some(pair_state) = regions.get(i) {
 5839                if pair_state.range.end.to_offset(buffer) < range.start {
 5840                    regions = &regions[i + 1..];
 5841                    i = 0;
 5842                } else if pair_state.range.start.to_offset(buffer) > range.end {
 5843                    break;
 5844                } else {
 5845                    if pair_state.selection_id == selection.id {
 5846                        enclosing = Some(pair_state);
 5847                    }
 5848                    i += 1;
 5849                }
 5850            }
 5851
 5852            (selection, enclosing)
 5853        })
 5854    }
 5855
 5856    /// Remove any autoclose regions that no longer contain their selection or have invalid anchors in ranges.
 5857    fn invalidate_autoclose_regions(
 5858        &mut self,
 5859        mut selections: &[Selection<Anchor>],
 5860        buffer: &MultiBufferSnapshot,
 5861    ) {
 5862        self.autoclose_regions.retain(|state| {
 5863            if !state.range.start.is_valid(buffer) || !state.range.end.is_valid(buffer) {
 5864                return false;
 5865            }
 5866
 5867            let mut i = 0;
 5868            while let Some(selection) = selections.get(i) {
 5869                if selection.end.cmp(&state.range.start, buffer).is_lt() {
 5870                    selections = &selections[1..];
 5871                    continue;
 5872                }
 5873                if selection.start.cmp(&state.range.end, buffer).is_gt() {
 5874                    break;
 5875                }
 5876                if selection.id == state.selection_id {
 5877                    return true;
 5878                } else {
 5879                    i += 1;
 5880                }
 5881            }
 5882            false
 5883        });
 5884    }
 5885
 5886    fn completion_query(buffer: &MultiBufferSnapshot, position: impl ToOffset) -> Option<String> {
 5887        let offset = position.to_offset(buffer);
 5888        let (word_range, kind) =
 5889            buffer.surrounding_word(offset, Some(CharScopeContext::Completion));
 5890        if offset > word_range.start && kind == Some(CharKind::Word) {
 5891            Some(
 5892                buffer
 5893                    .text_for_range(word_range.start..offset)
 5894                    .collect::<String>(),
 5895            )
 5896        } else {
 5897            None
 5898        }
 5899    }
 5900
 5901    pub fn visible_excerpts(
 5902        &self,
 5903        lsp_related_only: bool,
 5904        cx: &mut Context<Editor>,
 5905    ) -> HashMap<ExcerptId, (Entity<Buffer>, clock::Global, Range<usize>)> {
 5906        let project = self.project().cloned();
 5907        let display_snapshot = self.display_map.update(cx, |map, cx| map.snapshot(cx));
 5908        let multi_buffer = self.buffer().read(cx);
 5909        let multi_buffer_snapshot = multi_buffer.snapshot(cx);
 5910        multi_buffer_snapshot
 5911            .range_to_buffer_ranges(
 5912                self.multi_buffer_visible_range(&display_snapshot, cx)
 5913                    .to_inclusive(),
 5914            )
 5915            .into_iter()
 5916            .filter(|(_, excerpt_visible_range, _)| !excerpt_visible_range.is_empty())
 5917            .filter_map(|(buffer, excerpt_visible_range, excerpt_id)| {
 5918                if !lsp_related_only {
 5919                    return Some((
 5920                        excerpt_id,
 5921                        (
 5922                            multi_buffer.buffer(buffer.remote_id()).unwrap(),
 5923                            buffer.version().clone(),
 5924                            excerpt_visible_range.start.0..excerpt_visible_range.end.0,
 5925                        ),
 5926                    ));
 5927                }
 5928
 5929                let project = project.as_ref()?.read(cx);
 5930                let buffer_file = project::File::from_dyn(buffer.file())?;
 5931                let buffer_worktree = project.worktree_for_id(buffer_file.worktree_id(cx), cx)?;
 5932                let worktree_entry = buffer_worktree
 5933                    .read(cx)
 5934                    .entry_for_id(buffer_file.project_entry_id()?)?;
 5935                if worktree_entry.is_ignored {
 5936                    None
 5937                } else {
 5938                    Some((
 5939                        excerpt_id,
 5940                        (
 5941                            multi_buffer.buffer(buffer.remote_id()).unwrap(),
 5942                            buffer.version().clone(),
 5943                            excerpt_visible_range.start.0..excerpt_visible_range.end.0,
 5944                        ),
 5945                    ))
 5946                }
 5947            })
 5948            .collect()
 5949    }
 5950
 5951    pub fn text_layout_details(&self, window: &mut Window, cx: &mut App) -> TextLayoutDetails {
 5952        TextLayoutDetails {
 5953            text_system: window.text_system().clone(),
 5954            editor_style: self.style.clone().unwrap(),
 5955            rem_size: window.rem_size(),
 5956            scroll_anchor: self.scroll_manager.shared_scroll_anchor(cx),
 5957            visible_rows: self.visible_line_count(),
 5958            vertical_scroll_margin: self.scroll_manager.vertical_scroll_margin,
 5959        }
 5960    }
 5961
 5962    fn trigger_on_type_formatting(
 5963        &self,
 5964        input: String,
 5965        window: &mut Window,
 5966        cx: &mut Context<Self>,
 5967    ) -> Option<Task<Result<()>>> {
 5968        if input.chars().count() != 1 {
 5969            return None;
 5970        }
 5971
 5972        let project = self.project()?;
 5973        let position = self.selections.newest_anchor().head();
 5974        let (buffer, buffer_position) = self
 5975            .buffer
 5976            .read(cx)
 5977            .text_anchor_for_position(position, cx)?;
 5978
 5979        let settings = LanguageSettings::for_buffer_at(&buffer.read(cx), buffer_position, cx);
 5980        if !settings.use_on_type_format {
 5981            return None;
 5982        }
 5983
 5984        // OnTypeFormatting returns a list of edits, no need to pass them between Zed instances,
 5985        // hence we do LSP request & edit on host side only — add formats to host's history.
 5986        let push_to_lsp_host_history = true;
 5987        // If this is not the host, append its history with new edits.
 5988        let push_to_client_history = project.read(cx).is_via_collab();
 5989
 5990        let on_type_formatting = project.update(cx, |project, cx| {
 5991            project.on_type_format(
 5992                buffer.clone(),
 5993                buffer_position,
 5994                input,
 5995                push_to_lsp_host_history,
 5996                cx,
 5997            )
 5998        });
 5999        Some(cx.spawn_in(window, async move |editor, cx| {
 6000            if let Some(transaction) = on_type_formatting.await? {
 6001                if push_to_client_history {
 6002                    buffer.update(cx, |buffer, _| {
 6003                        buffer.push_transaction(transaction, Instant::now());
 6004                        buffer.finalize_last_transaction();
 6005                    });
 6006                }
 6007                editor.update(cx, |editor, cx| {
 6008                    editor.refresh_document_highlights(cx);
 6009                })?;
 6010            }
 6011            Ok(())
 6012        }))
 6013    }
 6014
 6015    pub fn show_word_completions(
 6016        &mut self,
 6017        _: &ShowWordCompletions,
 6018        window: &mut Window,
 6019        cx: &mut Context<Self>,
 6020    ) {
 6021        self.open_or_update_completions_menu(
 6022            Some(CompletionsMenuSource::Words {
 6023                ignore_threshold: true,
 6024            }),
 6025            None,
 6026            false,
 6027            window,
 6028            cx,
 6029        );
 6030    }
 6031
 6032    pub fn show_completions(
 6033        &mut self,
 6034        _: &ShowCompletions,
 6035        window: &mut Window,
 6036        cx: &mut Context<Self>,
 6037    ) {
 6038        self.open_or_update_completions_menu(None, None, false, window, cx);
 6039    }
 6040
 6041    fn open_or_update_completions_menu(
 6042        &mut self,
 6043        requested_source: Option<CompletionsMenuSource>,
 6044        trigger: Option<String>,
 6045        trigger_in_words: bool,
 6046        window: &mut Window,
 6047        cx: &mut Context<Self>,
 6048    ) {
 6049        if self.pending_rename.is_some() {
 6050            return;
 6051        }
 6052
 6053        let completions_source = self
 6054            .context_menu
 6055            .borrow()
 6056            .as_ref()
 6057            .and_then(|menu| match menu {
 6058                CodeContextMenu::Completions(completions_menu) => Some(completions_menu.source),
 6059                CodeContextMenu::CodeActions(_) => None,
 6060            });
 6061
 6062        let multibuffer_snapshot = self.buffer.read(cx).read(cx);
 6063
 6064        // Typically `start` == `end`, but with snippet tabstop choices the default choice is
 6065        // inserted and selected. To handle that case, the start of the selection is used so that
 6066        // the menu starts with all choices.
 6067        let position = self
 6068            .selections
 6069            .newest_anchor()
 6070            .start
 6071            .bias_right(&multibuffer_snapshot);
 6072        if position.diff_base_anchor.is_some() {
 6073            return;
 6074        }
 6075        let buffer_position = multibuffer_snapshot.anchor_before(position);
 6076        let Some(buffer) = buffer_position
 6077            .text_anchor
 6078            .buffer_id
 6079            .and_then(|buffer_id| self.buffer.read(cx).buffer(buffer_id))
 6080        else {
 6081            return;
 6082        };
 6083        let buffer_snapshot = buffer.read(cx).snapshot();
 6084
 6085        let menu_is_open = matches!(
 6086            self.context_menu.borrow().as_ref(),
 6087            Some(CodeContextMenu::Completions(_))
 6088        );
 6089
 6090        let language = buffer_snapshot
 6091            .language_at(buffer_position.text_anchor)
 6092            .map(|language| language.name());
 6093        let language_settings = multibuffer_snapshot.language_settings_at(buffer_position, cx);
 6094        let completion_settings = language_settings.completions.clone();
 6095
 6096        let show_completions_on_input = self
 6097            .show_completions_on_input_override
 6098            .unwrap_or(language_settings.show_completions_on_input);
 6099        if !menu_is_open && trigger.is_some() && !show_completions_on_input {
 6100            return;
 6101        }
 6102
 6103        let query: Option<Arc<String>> =
 6104            Self::completion_query(&multibuffer_snapshot, buffer_position)
 6105                .map(|query| query.into());
 6106
 6107        drop(multibuffer_snapshot);
 6108
 6109        // Hide the current completions menu when query is empty. Without this, cached
 6110        // completions from before the trigger char may be reused (#32774).
 6111        if query.is_none() && menu_is_open {
 6112            self.hide_context_menu(window, cx);
 6113        }
 6114
 6115        let mut ignore_word_threshold = false;
 6116        let provider = match requested_source {
 6117            Some(CompletionsMenuSource::Normal) | None => self.completion_provider.clone(),
 6118            Some(CompletionsMenuSource::Words { ignore_threshold }) => {
 6119                ignore_word_threshold = ignore_threshold;
 6120                None
 6121            }
 6122            Some(CompletionsMenuSource::SnippetChoices)
 6123            | Some(CompletionsMenuSource::SnippetsOnly) => {
 6124                log::error!("bug: SnippetChoices requested_source is not handled");
 6125                None
 6126            }
 6127        };
 6128
 6129        let sort_completions = provider
 6130            .as_ref()
 6131            .is_some_and(|provider| provider.sort_completions());
 6132
 6133        let filter_completions = provider
 6134            .as_ref()
 6135            .is_none_or(|provider| provider.filter_completions());
 6136
 6137        let was_snippets_only = matches!(
 6138            completions_source,
 6139            Some(CompletionsMenuSource::SnippetsOnly)
 6140        );
 6141
 6142        if let Some(CodeContextMenu::Completions(menu)) = self.context_menu.borrow_mut().as_mut() {
 6143            if filter_completions {
 6144                menu.filter(
 6145                    query.clone().unwrap_or_default(),
 6146                    buffer_position.text_anchor,
 6147                    &buffer,
 6148                    provider.clone(),
 6149                    window,
 6150                    cx,
 6151                );
 6152            }
 6153            // When `is_incomplete` is false, no need to re-query completions when the current query
 6154            // is a suffix of the initial query.
 6155            let was_complete = !menu.is_incomplete;
 6156            if was_complete && !was_snippets_only {
 6157                // If the new query is a suffix of the old query (typing more characters) and
 6158                // the previous result was complete, the existing completions can be filtered.
 6159                //
 6160                // Note that snippet completions are always complete.
 6161                let query_matches = match (&menu.initial_query, &query) {
 6162                    (Some(initial_query), Some(query)) => query.starts_with(initial_query.as_ref()),
 6163                    (None, _) => true,
 6164                    _ => false,
 6165                };
 6166                if query_matches {
 6167                    let position_matches = if menu.initial_position == position {
 6168                        true
 6169                    } else {
 6170                        let snapshot = self.buffer.read(cx).read(cx);
 6171                        menu.initial_position.to_offset(&snapshot) == position.to_offset(&snapshot)
 6172                    };
 6173                    if position_matches {
 6174                        return;
 6175                    }
 6176                }
 6177            }
 6178        };
 6179
 6180        let Anchor {
 6181            excerpt_id: buffer_excerpt_id,
 6182            text_anchor: buffer_position,
 6183            ..
 6184        } = buffer_position;
 6185
 6186        let (word_replace_range, word_to_exclude) = if let (word_range, Some(CharKind::Word)) =
 6187            buffer_snapshot.surrounding_word(buffer_position, None)
 6188        {
 6189            let word_to_exclude = buffer_snapshot
 6190                .text_for_range(word_range.clone())
 6191                .collect::<String>();
 6192            (
 6193                buffer_snapshot.anchor_before(word_range.start)
 6194                    ..buffer_snapshot.anchor_after(buffer_position),
 6195                Some(word_to_exclude),
 6196            )
 6197        } else {
 6198            (buffer_position..buffer_position, None)
 6199        };
 6200
 6201        let show_completion_documentation = buffer_snapshot
 6202            .settings_at(buffer_position, cx)
 6203            .show_completion_documentation;
 6204
 6205        // The document can be large, so stay in reasonable bounds when searching for words,
 6206        // otherwise completion pop-up might be slow to appear.
 6207        const WORD_LOOKUP_ROWS: u32 = 5_000;
 6208        let buffer_row = text::ToPoint::to_point(&buffer_position, &buffer_snapshot).row;
 6209        let min_word_search = buffer_snapshot.clip_point(
 6210            Point::new(buffer_row.saturating_sub(WORD_LOOKUP_ROWS), 0),
 6211            Bias::Left,
 6212        );
 6213        let max_word_search = buffer_snapshot.clip_point(
 6214            Point::new(buffer_row + WORD_LOOKUP_ROWS, 0).min(buffer_snapshot.max_point()),
 6215            Bias::Right,
 6216        );
 6217        let word_search_range = buffer_snapshot.point_to_offset(min_word_search)
 6218            ..buffer_snapshot.point_to_offset(max_word_search);
 6219
 6220        let skip_digits = query
 6221            .as_ref()
 6222            .is_none_or(|query| !query.chars().any(|c| c.is_digit(10)));
 6223
 6224        let load_provider_completions = provider.as_ref().is_some_and(|provider| {
 6225            trigger.as_ref().is_none_or(|trigger| {
 6226                provider.is_completion_trigger(
 6227                    &buffer,
 6228                    position.text_anchor,
 6229                    trigger,
 6230                    trigger_in_words,
 6231                    cx,
 6232                )
 6233            })
 6234        });
 6235
 6236        let provider_responses = if let Some(provider) = &provider
 6237            && load_provider_completions
 6238        {
 6239            let trigger_character =
 6240                trigger.filter(|trigger| buffer.read(cx).completion_triggers().contains(trigger));
 6241            let completion_context = CompletionContext {
 6242                trigger_kind: match &trigger_character {
 6243                    Some(_) => CompletionTriggerKind::TRIGGER_CHARACTER,
 6244                    None => CompletionTriggerKind::INVOKED,
 6245                },
 6246                trigger_character,
 6247            };
 6248
 6249            provider.completions(
 6250                buffer_excerpt_id,
 6251                &buffer,
 6252                buffer_position,
 6253                completion_context,
 6254                window,
 6255                cx,
 6256            )
 6257        } else {
 6258            Task::ready(Ok(Vec::new()))
 6259        };
 6260
 6261        let load_word_completions = if !self.word_completions_enabled {
 6262            false
 6263        } else if requested_source
 6264            == Some(CompletionsMenuSource::Words {
 6265                ignore_threshold: true,
 6266            })
 6267        {
 6268            true
 6269        } else {
 6270            load_provider_completions
 6271                && completion_settings.words != WordsCompletionMode::Disabled
 6272                && (ignore_word_threshold || {
 6273                    let words_min_length = completion_settings.words_min_length;
 6274                    // check whether word has at least `words_min_length` characters
 6275                    let query_chars = query.iter().flat_map(|q| q.chars());
 6276                    query_chars.take(words_min_length).count() == words_min_length
 6277                })
 6278        };
 6279
 6280        let mut words = if load_word_completions {
 6281            cx.background_spawn({
 6282                let buffer_snapshot = buffer_snapshot.clone();
 6283                async move {
 6284                    buffer_snapshot.words_in_range(WordsQuery {
 6285                        fuzzy_contents: None,
 6286                        range: word_search_range,
 6287                        skip_digits,
 6288                    })
 6289                }
 6290            })
 6291        } else {
 6292            Task::ready(BTreeMap::default())
 6293        };
 6294
 6295        let snippets = if let Some(provider) = &provider
 6296            && provider.show_snippets()
 6297            && let Some(project) = self.project()
 6298        {
 6299            let char_classifier = buffer_snapshot
 6300                .char_classifier_at(buffer_position)
 6301                .scope_context(Some(CharScopeContext::Completion));
 6302            project.update(cx, |project, cx| {
 6303                snippet_completions(project, &buffer, buffer_position, char_classifier, cx)
 6304            })
 6305        } else {
 6306            Task::ready(Ok(CompletionResponse {
 6307                completions: Vec::new(),
 6308                display_options: Default::default(),
 6309                is_incomplete: false,
 6310            }))
 6311        };
 6312
 6313        let snippet_sort_order = EditorSettings::get_global(cx).snippet_sort_order;
 6314
 6315        let id = post_inc(&mut self.next_completion_id);
 6316        let task = cx.spawn_in(window, async move |editor, cx| {
 6317            let Ok(()) = editor.update(cx, |this, _| {
 6318                this.completion_tasks.retain(|(task_id, _)| *task_id >= id);
 6319            }) else {
 6320                return;
 6321            };
 6322
 6323            // TODO: Ideally completions from different sources would be selectively re-queried, so
 6324            // that having one source with `is_incomplete: true` doesn't cause all to be re-queried.
 6325            let mut completions = Vec::new();
 6326            let mut is_incomplete = false;
 6327            let mut display_options: Option<CompletionDisplayOptions> = None;
 6328            if let Some(provider_responses) = provider_responses.await.log_err()
 6329                && !provider_responses.is_empty()
 6330            {
 6331                for response in provider_responses {
 6332                    completions.extend(response.completions);
 6333                    is_incomplete = is_incomplete || response.is_incomplete;
 6334                    match display_options.as_mut() {
 6335                        None => {
 6336                            display_options = Some(response.display_options);
 6337                        }
 6338                        Some(options) => options.merge(&response.display_options),
 6339                    }
 6340                }
 6341                if completion_settings.words == WordsCompletionMode::Fallback {
 6342                    words = Task::ready(BTreeMap::default());
 6343                }
 6344            }
 6345            let display_options = display_options.unwrap_or_default();
 6346
 6347            let mut words = words.await;
 6348            if let Some(word_to_exclude) = &word_to_exclude {
 6349                words.remove(word_to_exclude);
 6350            }
 6351            for lsp_completion in &completions {
 6352                words.remove(&lsp_completion.new_text);
 6353            }
 6354            completions.extend(words.into_iter().map(|(word, word_range)| Completion {
 6355                replace_range: word_replace_range.clone(),
 6356                new_text: word.clone(),
 6357                label: CodeLabel::plain(word, None),
 6358                match_start: None,
 6359                snippet_deduplication_key: None,
 6360                icon_path: None,
 6361                documentation: None,
 6362                source: CompletionSource::BufferWord {
 6363                    word_range,
 6364                    resolved: false,
 6365                },
 6366                insert_text_mode: Some(InsertTextMode::AS_IS),
 6367                confirm: None,
 6368            }));
 6369
 6370            completions.extend(
 6371                snippets
 6372                    .await
 6373                    .into_iter()
 6374                    .flat_map(|response| response.completions),
 6375            );
 6376
 6377            let menu = if completions.is_empty() {
 6378                None
 6379            } else {
 6380                let Ok((mut menu, matches_task)) = editor.update(cx, |editor, cx| {
 6381                    let languages = editor
 6382                        .workspace
 6383                        .as_ref()
 6384                        .and_then(|(workspace, _)| workspace.upgrade())
 6385                        .map(|workspace| workspace.read(cx).app_state().languages.clone());
 6386                    let menu = CompletionsMenu::new(
 6387                        id,
 6388                        requested_source.unwrap_or(if load_provider_completions {
 6389                            CompletionsMenuSource::Normal
 6390                        } else {
 6391                            CompletionsMenuSource::SnippetsOnly
 6392                        }),
 6393                        sort_completions,
 6394                        show_completion_documentation,
 6395                        position,
 6396                        query.clone(),
 6397                        is_incomplete,
 6398                        buffer.clone(),
 6399                        completions.into(),
 6400                        editor
 6401                            .context_menu()
 6402                            .borrow_mut()
 6403                            .as_ref()
 6404                            .map(|menu| menu.primary_scroll_handle()),
 6405                        display_options,
 6406                        snippet_sort_order,
 6407                        languages,
 6408                        language,
 6409                        cx,
 6410                    );
 6411
 6412                    let query = if filter_completions { query } else { None };
 6413                    let matches_task = menu.do_async_filtering(
 6414                        query.unwrap_or_default(),
 6415                        buffer_position,
 6416                        &buffer,
 6417                        cx,
 6418                    );
 6419                    (menu, matches_task)
 6420                }) else {
 6421                    return;
 6422                };
 6423
 6424                let matches = matches_task.await;
 6425
 6426                let Ok(()) = editor.update_in(cx, |editor, window, cx| {
 6427                    // Newer menu already set, so exit.
 6428                    if let Some(CodeContextMenu::Completions(prev_menu)) =
 6429                        editor.context_menu.borrow().as_ref()
 6430                        && prev_menu.id > id
 6431                    {
 6432                        return;
 6433                    };
 6434
 6435                    // Only valid to take prev_menu because either the new menu is immediately set
 6436                    // below, or the menu is hidden.
 6437                    if let Some(CodeContextMenu::Completions(prev_menu)) =
 6438                        editor.context_menu.borrow_mut().take()
 6439                    {
 6440                        let position_matches =
 6441                            if prev_menu.initial_position == menu.initial_position {
 6442                                true
 6443                            } else {
 6444                                let snapshot = editor.buffer.read(cx).read(cx);
 6445                                prev_menu.initial_position.to_offset(&snapshot)
 6446                                    == menu.initial_position.to_offset(&snapshot)
 6447                            };
 6448                        if position_matches {
 6449                            // Preserve markdown cache before `set_filter_results` because it will
 6450                            // try to populate the documentation cache.
 6451                            menu.preserve_markdown_cache(prev_menu);
 6452                        }
 6453                    };
 6454
 6455                    menu.set_filter_results(matches, provider, window, cx);
 6456                }) else {
 6457                    return;
 6458                };
 6459
 6460                menu.visible().then_some(menu)
 6461            };
 6462
 6463            editor
 6464                .update_in(cx, |editor, window, cx| {
 6465                    if editor.focus_handle.is_focused(window)
 6466                        && let Some(menu) = menu
 6467                    {
 6468                        *editor.context_menu.borrow_mut() =
 6469                            Some(CodeContextMenu::Completions(menu));
 6470
 6471                        crate::hover_popover::hide_hover(editor, cx);
 6472                        if editor.show_edit_predictions_in_menu() {
 6473                            editor.update_visible_edit_prediction(window, cx);
 6474                        } else {
 6475                            editor
 6476                                .discard_edit_prediction(EditPredictionDiscardReason::Ignored, cx);
 6477                        }
 6478
 6479                        cx.notify();
 6480                        return;
 6481                    }
 6482
 6483                    if editor.completion_tasks.len() <= 1 {
 6484                        // If there are no more completion tasks and the last menu was empty, we should hide it.
 6485                        let was_hidden = editor.hide_context_menu(window, cx).is_none();
 6486                        // If it was already hidden and we don't show edit predictions in the menu,
 6487                        // we should also show the edit prediction when available.
 6488                        if was_hidden && editor.show_edit_predictions_in_menu() {
 6489                            editor.update_visible_edit_prediction(window, cx);
 6490                        }
 6491                    }
 6492                })
 6493                .ok();
 6494        });
 6495
 6496        self.completion_tasks.push((id, task));
 6497    }
 6498
 6499    #[cfg(any(test, feature = "test-support"))]
 6500    pub fn current_completions(&self) -> Option<Vec<project::Completion>> {
 6501        let menu = self.context_menu.borrow();
 6502        if let CodeContextMenu::Completions(menu) = menu.as_ref()? {
 6503            let completions = menu.completions.borrow();
 6504            Some(completions.to_vec())
 6505        } else {
 6506            None
 6507        }
 6508    }
 6509
 6510    pub fn with_completions_menu_matching_id<R>(
 6511        &self,
 6512        id: CompletionId,
 6513        f: impl FnOnce(Option<&mut CompletionsMenu>) -> R,
 6514    ) -> R {
 6515        let mut context_menu = self.context_menu.borrow_mut();
 6516        let Some(CodeContextMenu::Completions(completions_menu)) = &mut *context_menu else {
 6517            return f(None);
 6518        };
 6519        if completions_menu.id != id {
 6520            return f(None);
 6521        }
 6522        f(Some(completions_menu))
 6523    }
 6524
 6525    pub fn confirm_completion(
 6526        &mut self,
 6527        action: &ConfirmCompletion,
 6528        window: &mut Window,
 6529        cx: &mut Context<Self>,
 6530    ) -> Option<Task<Result<()>>> {
 6531        self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
 6532        self.do_completion(action.item_ix, CompletionIntent::Complete, window, cx)
 6533    }
 6534
 6535    pub fn confirm_completion_insert(
 6536        &mut self,
 6537        _: &ConfirmCompletionInsert,
 6538        window: &mut Window,
 6539        cx: &mut Context<Self>,
 6540    ) -> Option<Task<Result<()>>> {
 6541        self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
 6542        self.do_completion(None, CompletionIntent::CompleteWithInsert, window, cx)
 6543    }
 6544
 6545    pub fn confirm_completion_replace(
 6546        &mut self,
 6547        _: &ConfirmCompletionReplace,
 6548        window: &mut Window,
 6549        cx: &mut Context<Self>,
 6550    ) -> Option<Task<Result<()>>> {
 6551        self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
 6552        self.do_completion(None, CompletionIntent::CompleteWithReplace, window, cx)
 6553    }
 6554
 6555    pub fn compose_completion(
 6556        &mut self,
 6557        action: &ComposeCompletion,
 6558        window: &mut Window,
 6559        cx: &mut Context<Self>,
 6560    ) -> Option<Task<Result<()>>> {
 6561        self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
 6562        self.do_completion(action.item_ix, CompletionIntent::Compose, window, cx)
 6563    }
 6564
 6565    fn do_completion(
 6566        &mut self,
 6567        item_ix: Option<usize>,
 6568        intent: CompletionIntent,
 6569        window: &mut Window,
 6570        cx: &mut Context<Editor>,
 6571    ) -> Option<Task<Result<()>>> {
 6572        use language::ToOffset as _;
 6573
 6574        let CodeContextMenu::Completions(completions_menu) = self.hide_context_menu(window, cx)?
 6575        else {
 6576            return None;
 6577        };
 6578
 6579        let candidate_id = {
 6580            let entries = completions_menu.entries.borrow();
 6581            let mat = entries.get(item_ix.unwrap_or(completions_menu.selected_item))?;
 6582            if self.show_edit_predictions_in_menu() {
 6583                self.discard_edit_prediction(EditPredictionDiscardReason::Rejected, cx);
 6584            }
 6585            mat.candidate_id
 6586        };
 6587
 6588        let completion = completions_menu
 6589            .completions
 6590            .borrow()
 6591            .get(candidate_id)?
 6592            .clone();
 6593        cx.stop_propagation();
 6594
 6595        let buffer_handle = completions_menu.buffer.clone();
 6596
 6597        let CompletionEdit {
 6598            new_text,
 6599            snippet,
 6600            replace_range,
 6601        } = process_completion_for_edit(
 6602            &completion,
 6603            intent,
 6604            &buffer_handle,
 6605            &completions_menu.initial_position.text_anchor,
 6606            cx,
 6607        );
 6608
 6609        let buffer = buffer_handle.read(cx);
 6610        let snapshot = self.buffer.read(cx).snapshot(cx);
 6611        let newest_anchor = self.selections.newest_anchor();
 6612        let replace_range_multibuffer = {
 6613            let mut excerpt = snapshot.excerpt_containing(newest_anchor.range()).unwrap();
 6614            excerpt.map_range_from_buffer(replace_range.clone())
 6615        };
 6616        if snapshot.buffer_id_for_anchor(newest_anchor.head()) != Some(buffer.remote_id()) {
 6617            return None;
 6618        }
 6619
 6620        let old_text = buffer
 6621            .text_for_range(replace_range.clone())
 6622            .collect::<String>();
 6623        let lookbehind = newest_anchor
 6624            .start
 6625            .text_anchor
 6626            .to_offset(buffer)
 6627            .saturating_sub(replace_range.start.0);
 6628        let lookahead = replace_range
 6629            .end
 6630            .0
 6631            .saturating_sub(newest_anchor.end.text_anchor.to_offset(buffer));
 6632        let prefix = &old_text[..old_text.len().saturating_sub(lookahead)];
 6633        let suffix = &old_text[lookbehind.min(old_text.len())..];
 6634
 6635        let selections = self
 6636            .selections
 6637            .all::<MultiBufferOffset>(&self.display_snapshot(cx));
 6638        let mut ranges = Vec::new();
 6639        let mut all_commit_ranges = Vec::new();
 6640        let mut linked_edits = LinkedEdits::new();
 6641
 6642        let text: Arc<str> = new_text.clone().into();
 6643        for selection in &selections {
 6644            let range = if selection.id == newest_anchor.id {
 6645                replace_range_multibuffer.clone()
 6646            } else {
 6647                let mut range = selection.range();
 6648
 6649                // if prefix is present, don't duplicate it
 6650                if snapshot.contains_str_at(range.start.saturating_sub_usize(lookbehind), prefix) {
 6651                    range.start = range.start.saturating_sub_usize(lookbehind);
 6652
 6653                    // if suffix is also present, mimic the newest cursor and replace it
 6654                    if selection.id != newest_anchor.id
 6655                        && snapshot.contains_str_at(range.end, suffix)
 6656                    {
 6657                        range.end += lookahead;
 6658                    }
 6659                }
 6660                range
 6661            };
 6662
 6663            ranges.push(range.clone());
 6664
 6665            let start_anchor = snapshot.anchor_before(range.start);
 6666            let end_anchor = snapshot.anchor_after(range.end);
 6667            let anchor_range = start_anchor.text_anchor..end_anchor.text_anchor;
 6668            all_commit_ranges.push(anchor_range.clone());
 6669
 6670            if !self.linked_edit_ranges.is_empty() {
 6671                linked_edits.push(&self, anchor_range, text.clone(), cx);
 6672            }
 6673        }
 6674
 6675        let common_prefix_len = old_text
 6676            .chars()
 6677            .zip(new_text.chars())
 6678            .take_while(|(a, b)| a == b)
 6679            .map(|(a, _)| a.len_utf8())
 6680            .sum::<usize>();
 6681
 6682        cx.emit(EditorEvent::InputHandled {
 6683            utf16_range_to_replace: None,
 6684            text: new_text[common_prefix_len..].into(),
 6685        });
 6686
 6687        self.transact(window, cx, |editor, window, cx| {
 6688            if let Some(mut snippet) = snippet {
 6689                snippet.text = new_text.to_string();
 6690                editor
 6691                    .insert_snippet(&ranges, snippet, window, cx)
 6692                    .log_err();
 6693            } else {
 6694                editor.buffer.update(cx, |multi_buffer, cx| {
 6695                    let auto_indent = match completion.insert_text_mode {
 6696                        Some(InsertTextMode::AS_IS) => None,
 6697                        _ => editor.autoindent_mode.clone(),
 6698                    };
 6699                    let edits = ranges.into_iter().map(|range| (range, new_text.as_str()));
 6700                    multi_buffer.edit(edits, auto_indent, cx);
 6701                });
 6702            }
 6703            linked_edits.apply(cx);
 6704            editor.refresh_edit_prediction(true, false, window, cx);
 6705        });
 6706        self.invalidate_autoclose_regions(&self.selections.disjoint_anchors_arc(), &snapshot);
 6707
 6708        let show_new_completions_on_confirm = completion
 6709            .confirm
 6710            .as_ref()
 6711            .is_some_and(|confirm| confirm(intent, window, cx));
 6712        if show_new_completions_on_confirm {
 6713            self.open_or_update_completions_menu(None, None, false, window, cx);
 6714        }
 6715
 6716        let provider = self.completion_provider.as_ref()?;
 6717
 6718        let lsp_store = self.project().map(|project| project.read(cx).lsp_store());
 6719        let command = lsp_store.as_ref().and_then(|lsp_store| {
 6720            let CompletionSource::Lsp {
 6721                lsp_completion,
 6722                server_id,
 6723                ..
 6724            } = &completion.source
 6725            else {
 6726                return None;
 6727            };
 6728            let lsp_command = lsp_completion.command.as_ref()?;
 6729            let available_commands = lsp_store
 6730                .read(cx)
 6731                .lsp_server_capabilities
 6732                .get(server_id)
 6733                .and_then(|server_capabilities| {
 6734                    server_capabilities
 6735                        .execute_command_provider
 6736                        .as_ref()
 6737                        .map(|options| options.commands.as_slice())
 6738                })?;
 6739            if available_commands.contains(&lsp_command.command) {
 6740                Some(CodeAction {
 6741                    server_id: *server_id,
 6742                    range: language::Anchor::MIN..language::Anchor::MIN,
 6743                    lsp_action: LspAction::Command(lsp_command.clone()),
 6744                    resolved: false,
 6745                })
 6746            } else {
 6747                None
 6748            }
 6749        });
 6750
 6751        drop(completion);
 6752        let apply_edits = provider.apply_additional_edits_for_completion(
 6753            buffer_handle.clone(),
 6754            completions_menu.completions.clone(),
 6755            candidate_id,
 6756            true,
 6757            all_commit_ranges,
 6758            cx,
 6759        );
 6760
 6761        let editor_settings = EditorSettings::get_global(cx);
 6762        if editor_settings.show_signature_help_after_edits || editor_settings.auto_signature_help {
 6763            // After the code completion is finished, users often want to know what signatures are needed.
 6764            // so we should automatically call signature_help
 6765            self.show_signature_help(&ShowSignatureHelp, window, cx);
 6766        }
 6767
 6768        Some(cx.spawn_in(window, async move |editor, cx| {
 6769            apply_edits.await?;
 6770
 6771            if let Some((lsp_store, command)) = lsp_store.zip(command) {
 6772                let title = command.lsp_action.title().to_owned();
 6773                let project_transaction = lsp_store
 6774                    .update(cx, |lsp_store, cx| {
 6775                        lsp_store.apply_code_action(buffer_handle, command, false, cx)
 6776                    })
 6777                    .await
 6778                    .context("applying post-completion command")?;
 6779                if let Some(workspace) = editor.read_with(cx, |editor, _| editor.workspace())? {
 6780                    Self::open_project_transaction(
 6781                        &editor,
 6782                        workspace.downgrade(),
 6783                        project_transaction,
 6784                        title,
 6785                        cx,
 6786                    )
 6787                    .await?;
 6788                }
 6789            }
 6790
 6791            Ok(())
 6792        }))
 6793    }
 6794
 6795    pub fn toggle_code_actions(
 6796        &mut self,
 6797        action: &ToggleCodeActions,
 6798        window: &mut Window,
 6799        cx: &mut Context<Self>,
 6800    ) {
 6801        let quick_launch = action.quick_launch;
 6802        let mut context_menu = self.context_menu.borrow_mut();
 6803        if let Some(CodeContextMenu::CodeActions(code_actions)) = context_menu.as_ref() {
 6804            if code_actions.deployed_from == action.deployed_from {
 6805                // Toggle if we're selecting the same one
 6806                *context_menu = None;
 6807                cx.notify();
 6808                return;
 6809            } else {
 6810                // Otherwise, clear it and start a new one
 6811                *context_menu = None;
 6812                cx.notify();
 6813            }
 6814        }
 6815        drop(context_menu);
 6816        let snapshot = self.snapshot(window, cx);
 6817        let deployed_from = action.deployed_from.clone();
 6818        let action = action.clone();
 6819        self.completion_tasks.clear();
 6820        self.discard_edit_prediction(EditPredictionDiscardReason::Ignored, cx);
 6821
 6822        let multibuffer_point = match &action.deployed_from {
 6823            Some(CodeActionSource::Indicator(row)) | Some(CodeActionSource::RunMenu(row)) => {
 6824                DisplayPoint::new(*row, 0).to_point(&snapshot)
 6825            }
 6826            _ => self
 6827                .selections
 6828                .newest::<Point>(&snapshot.display_snapshot)
 6829                .head(),
 6830        };
 6831        let Some((buffer, buffer_row)) = snapshot
 6832            .buffer_snapshot()
 6833            .buffer_line_for_row(MultiBufferRow(multibuffer_point.row))
 6834            .and_then(|(buffer_snapshot, range)| {
 6835                self.buffer()
 6836                    .read(cx)
 6837                    .buffer(buffer_snapshot.remote_id())
 6838                    .map(|buffer| (buffer, range.start.row))
 6839            })
 6840        else {
 6841            return;
 6842        };
 6843        let buffer_id = buffer.read(cx).remote_id();
 6844        let tasks = self
 6845            .runnables
 6846            .runnables((buffer_id, buffer_row))
 6847            .map(|t| Arc::new(t.to_owned()));
 6848
 6849        if !self.focus_handle.is_focused(window) {
 6850            return;
 6851        }
 6852        let project = self.project.clone();
 6853
 6854        let code_actions_task = match deployed_from {
 6855            Some(CodeActionSource::RunMenu(_)) => Task::ready(None),
 6856            _ => self.code_actions(buffer_row, window, cx),
 6857        };
 6858
 6859        let runnable_task = match deployed_from {
 6860            Some(CodeActionSource::Indicator(_)) => Task::ready(Ok(Default::default())),
 6861            _ => {
 6862                let mut task_context_task = Task::ready(None);
 6863                if let Some(tasks) = &tasks
 6864                    && let Some(project) = project
 6865                {
 6866                    task_context_task =
 6867                        Self::build_tasks_context(&project, &buffer, buffer_row, tasks, cx);
 6868                }
 6869
 6870                cx.spawn_in(window, {
 6871                    let buffer = buffer.clone();
 6872                    async move |editor, cx| {
 6873                        let task_context = task_context_task.await;
 6874
 6875                        let resolved_tasks =
 6876                            tasks
 6877                                .zip(task_context.clone())
 6878                                .map(|(tasks, task_context)| ResolvedTasks {
 6879                                    templates: tasks.resolve(&task_context).collect(),
 6880                                    position: snapshot.buffer_snapshot().anchor_before(Point::new(
 6881                                        multibuffer_point.row,
 6882                                        tasks.column,
 6883                                    )),
 6884                                });
 6885                        let debug_scenarios = editor
 6886                            .update(cx, |editor, cx| {
 6887                                editor.debug_scenarios(&resolved_tasks, &buffer, cx)
 6888                            })?
 6889                            .await;
 6890                        anyhow::Ok((resolved_tasks, debug_scenarios, task_context))
 6891                    }
 6892                })
 6893            }
 6894        };
 6895
 6896        cx.spawn_in(window, async move |editor, cx| {
 6897            let (resolved_tasks, debug_scenarios, task_context) = runnable_task.await?;
 6898            let code_actions = code_actions_task.await;
 6899            let spawn_straight_away = quick_launch
 6900                && resolved_tasks
 6901                    .as_ref()
 6902                    .is_some_and(|tasks| tasks.templates.len() == 1)
 6903                && code_actions
 6904                    .as_ref()
 6905                    .is_none_or(|actions| actions.is_empty())
 6906                && debug_scenarios.is_empty();
 6907
 6908            editor.update_in(cx, |editor, window, cx| {
 6909                crate::hover_popover::hide_hover(editor, cx);
 6910                let actions = CodeActionContents::new(
 6911                    resolved_tasks,
 6912                    code_actions,
 6913                    debug_scenarios,
 6914                    task_context.unwrap_or_default(),
 6915                );
 6916
 6917                // Don't show the menu if there are no actions available
 6918                if actions.is_empty() {
 6919                    cx.notify();
 6920                    return Task::ready(Ok(()));
 6921                }
 6922
 6923                *editor.context_menu.borrow_mut() =
 6924                    Some(CodeContextMenu::CodeActions(CodeActionsMenu {
 6925                        buffer,
 6926                        actions,
 6927                        selected_item: Default::default(),
 6928                        scroll_handle: UniformListScrollHandle::default(),
 6929                        deployed_from,
 6930                    }));
 6931                cx.notify();
 6932                if spawn_straight_away
 6933                    && let Some(task) = editor.confirm_code_action(
 6934                        &ConfirmCodeAction { item_ix: Some(0) },
 6935                        window,
 6936                        cx,
 6937                    )
 6938                {
 6939                    return task;
 6940                }
 6941
 6942                Task::ready(Ok(()))
 6943            })
 6944        })
 6945        .detach_and_log_err(cx);
 6946    }
 6947
 6948    fn debug_scenarios(
 6949        &mut self,
 6950        resolved_tasks: &Option<ResolvedTasks>,
 6951        buffer: &Entity<Buffer>,
 6952        cx: &mut App,
 6953    ) -> Task<Vec<task::DebugScenario>> {
 6954        maybe!({
 6955            let project = self.project()?;
 6956            let dap_store = project.read(cx).dap_store();
 6957            let mut scenarios = vec![];
 6958            let resolved_tasks = resolved_tasks.as_ref()?;
 6959            let buffer = buffer.read(cx);
 6960            let language = buffer.language()?;
 6961            let debug_adapter = LanguageSettings::for_buffer(&buffer, cx)
 6962                .debuggers
 6963                .first()
 6964                .map(SharedString::from)
 6965                .or_else(|| language.config().debuggers.first().map(SharedString::from))?;
 6966
 6967            dap_store.update(cx, |dap_store, cx| {
 6968                for (_, task) in &resolved_tasks.templates {
 6969                    let maybe_scenario = dap_store.debug_scenario_for_build_task(
 6970                        task.original_task().clone(),
 6971                        debug_adapter.clone().into(),
 6972                        task.display_label().to_owned().into(),
 6973                        cx,
 6974                    );
 6975                    scenarios.push(maybe_scenario);
 6976                }
 6977            });
 6978            Some(cx.background_spawn(async move {
 6979                futures::future::join_all(scenarios)
 6980                    .await
 6981                    .into_iter()
 6982                    .flatten()
 6983                    .collect::<Vec<_>>()
 6984            }))
 6985        })
 6986        .unwrap_or_else(|| Task::ready(vec![]))
 6987    }
 6988
 6989    fn code_actions(
 6990        &mut self,
 6991        buffer_row: u32,
 6992        window: &mut Window,
 6993        cx: &mut Context<Self>,
 6994    ) -> Task<Option<Rc<[AvailableCodeAction]>>> {
 6995        let mut task = self.code_actions_task.take();
 6996        cx.spawn_in(window, async move |editor, cx| {
 6997            while let Some(prev_task) = task {
 6998                prev_task.await.log_err();
 6999                task = editor
 7000                    .update(cx, |this, _| this.code_actions_task.take())
 7001                    .ok()?;
 7002            }
 7003
 7004            editor
 7005                .update(cx, |editor, cx| {
 7006                    editor
 7007                        .available_code_actions
 7008                        .clone()
 7009                        .and_then(|(location, code_actions)| {
 7010                            let snapshot = location.buffer.read(cx).snapshot();
 7011                            let point_range = location.range.to_point(&snapshot);
 7012                            let point_range = point_range.start.row..=point_range.end.row;
 7013                            if point_range.contains(&buffer_row) {
 7014                                Some(code_actions)
 7015                            } else {
 7016                                None
 7017                            }
 7018                        })
 7019                })
 7020                .ok()
 7021                .flatten()
 7022        })
 7023    }
 7024
 7025    pub fn confirm_code_action(
 7026        &mut self,
 7027        action: &ConfirmCodeAction,
 7028        window: &mut Window,
 7029        cx: &mut Context<Self>,
 7030    ) -> Option<Task<Result<()>>> {
 7031        self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
 7032
 7033        let actions_menu =
 7034            if let CodeContextMenu::CodeActions(menu) = self.hide_context_menu(window, cx)? {
 7035                menu
 7036            } else {
 7037                return None;
 7038            };
 7039
 7040        let action_ix = action.item_ix.unwrap_or(actions_menu.selected_item);
 7041        let action = actions_menu.actions.get(action_ix)?;
 7042        let title = action.label();
 7043        let buffer = actions_menu.buffer;
 7044        let workspace = self.workspace()?;
 7045
 7046        match action {
 7047            CodeActionsItem::Task(task_source_kind, resolved_task) => {
 7048                workspace.update(cx, |workspace, cx| {
 7049                    workspace.schedule_resolved_task(
 7050                        task_source_kind,
 7051                        resolved_task,
 7052                        false,
 7053                        window,
 7054                        cx,
 7055                    );
 7056
 7057                    Some(Task::ready(Ok(())))
 7058                })
 7059            }
 7060            CodeActionsItem::CodeAction {
 7061                excerpt_id,
 7062                action,
 7063                provider,
 7064            } => {
 7065                let apply_code_action =
 7066                    provider.apply_code_action(buffer, action, excerpt_id, true, window, cx);
 7067                let workspace = workspace.downgrade();
 7068                Some(cx.spawn_in(window, async move |editor, cx| {
 7069                    let project_transaction = apply_code_action.await?;
 7070                    Self::open_project_transaction(
 7071                        &editor,
 7072                        workspace,
 7073                        project_transaction,
 7074                        title,
 7075                        cx,
 7076                    )
 7077                    .await
 7078                }))
 7079            }
 7080            CodeActionsItem::DebugScenario(scenario) => {
 7081                let context = actions_menu.actions.context.into();
 7082
 7083                workspace.update(cx, |workspace, cx| {
 7084                    dap::send_telemetry(&scenario, TelemetrySpawnLocation::Gutter, cx);
 7085                    workspace.start_debug_session(
 7086                        scenario,
 7087                        context,
 7088                        Some(buffer),
 7089                        None,
 7090                        window,
 7091                        cx,
 7092                    );
 7093                });
 7094                Some(Task::ready(Ok(())))
 7095            }
 7096        }
 7097    }
 7098
 7099    fn open_transaction_for_hidden_buffers(
 7100        workspace: Entity<Workspace>,
 7101        transaction: ProjectTransaction,
 7102        title: String,
 7103        window: &mut Window,
 7104        cx: &mut Context<Self>,
 7105    ) {
 7106        if transaction.0.is_empty() {
 7107            return;
 7108        }
 7109
 7110        let edited_buffers_already_open = {
 7111            let other_editors: Vec<Entity<Editor>> = workspace
 7112                .read(cx)
 7113                .panes()
 7114                .iter()
 7115                .flat_map(|pane| pane.read(cx).items_of_type::<Editor>())
 7116                .filter(|editor| editor.entity_id() != cx.entity_id())
 7117                .collect();
 7118
 7119            transaction.0.keys().all(|buffer| {
 7120                other_editors.iter().any(|editor| {
 7121                    let multi_buffer = editor.read(cx).buffer();
 7122                    multi_buffer.read(cx).is_singleton()
 7123                        && multi_buffer
 7124                            .read(cx)
 7125                            .as_singleton()
 7126                            .map_or(false, |singleton| {
 7127                                singleton.entity_id() == buffer.entity_id()
 7128                            })
 7129                })
 7130            })
 7131        };
 7132        if !edited_buffers_already_open {
 7133            let workspace = workspace.downgrade();
 7134            cx.defer_in(window, move |_, window, cx| {
 7135                cx.spawn_in(window, async move |editor, cx| {
 7136                    Self::open_project_transaction(&editor, workspace, transaction, title, cx)
 7137                        .await
 7138                        .ok()
 7139                })
 7140                .detach();
 7141            });
 7142        }
 7143    }
 7144
 7145    pub async fn open_project_transaction(
 7146        editor: &WeakEntity<Editor>,
 7147        workspace: WeakEntity<Workspace>,
 7148        transaction: ProjectTransaction,
 7149        title: String,
 7150        cx: &mut AsyncWindowContext,
 7151    ) -> Result<()> {
 7152        let mut entries = transaction.0.into_iter().collect::<Vec<_>>();
 7153        cx.update(|_, cx| {
 7154            entries.sort_unstable_by_key(|(buffer, _)| {
 7155                buffer.read(cx).file().map(|f| f.path().clone())
 7156            });
 7157        })?;
 7158        if entries.is_empty() {
 7159            return Ok(());
 7160        }
 7161
 7162        // If the project transaction's edits are all contained within this editor, then
 7163        // avoid opening a new editor to display them.
 7164
 7165        if let [(buffer, transaction)] = &*entries {
 7166            let excerpt = editor.update(cx, |editor, cx| {
 7167                editor
 7168                    .buffer()
 7169                    .read(cx)
 7170                    .excerpt_containing(editor.selections.newest_anchor().head(), cx)
 7171            })?;
 7172            if let Some((_, excerpted_buffer, excerpt_range)) = excerpt
 7173                && excerpted_buffer == *buffer
 7174            {
 7175                let all_edits_within_excerpt = buffer.read_with(cx, |buffer, _| {
 7176                    let excerpt_range = excerpt_range.to_offset(buffer);
 7177                    buffer
 7178                        .edited_ranges_for_transaction::<usize>(transaction)
 7179                        .all(|range| {
 7180                            excerpt_range.start <= range.start && excerpt_range.end >= range.end
 7181                        })
 7182                });
 7183
 7184                if all_edits_within_excerpt {
 7185                    return Ok(());
 7186                }
 7187            }
 7188        }
 7189
 7190        let mut ranges_to_highlight = Vec::new();
 7191        let excerpt_buffer = cx.new(|cx| {
 7192            let mut multibuffer = MultiBuffer::new(Capability::ReadWrite).with_title(title);
 7193            for (buffer_handle, transaction) in &entries {
 7194                let edited_ranges = buffer_handle
 7195                    .read(cx)
 7196                    .edited_ranges_for_transaction::<Point>(transaction)
 7197                    .collect::<Vec<_>>();
 7198                let (ranges, _) = multibuffer.set_excerpts_for_path(
 7199                    PathKey::for_buffer(buffer_handle, cx),
 7200                    buffer_handle.clone(),
 7201                    edited_ranges,
 7202                    multibuffer_context_lines(cx),
 7203                    cx,
 7204                );
 7205
 7206                ranges_to_highlight.extend(ranges);
 7207            }
 7208            multibuffer.push_transaction(entries.iter().map(|(b, t)| (b, t)), cx);
 7209            multibuffer
 7210        });
 7211
 7212        workspace.update_in(cx, |workspace, window, cx| {
 7213            let project = workspace.project().clone();
 7214            let editor =
 7215                cx.new(|cx| Editor::for_multibuffer(excerpt_buffer, Some(project), window, cx));
 7216            workspace.add_item_to_active_pane(Box::new(editor.clone()), None, true, window, cx);
 7217            editor.update(cx, |editor, cx| {
 7218                editor.highlight_background(
 7219                    HighlightKey::Editor,
 7220                    &ranges_to_highlight,
 7221                    |_, theme| theme.colors().editor_highlighted_line_background,
 7222                    cx,
 7223                );
 7224            });
 7225        })?;
 7226
 7227        Ok(())
 7228    }
 7229
 7230    pub fn clear_code_action_providers(&mut self) {
 7231        self.code_action_providers.clear();
 7232        self.available_code_actions.take();
 7233    }
 7234
 7235    pub fn add_code_action_provider(
 7236        &mut self,
 7237        provider: Rc<dyn CodeActionProvider>,
 7238        window: &mut Window,
 7239        cx: &mut Context<Self>,
 7240    ) {
 7241        if self
 7242            .code_action_providers
 7243            .iter()
 7244            .any(|existing_provider| existing_provider.id() == provider.id())
 7245        {
 7246            return;
 7247        }
 7248
 7249        self.code_action_providers.push(provider);
 7250        self.refresh_code_actions(window, cx);
 7251    }
 7252
 7253    pub fn remove_code_action_provider(
 7254        &mut self,
 7255        id: Arc<str>,
 7256        window: &mut Window,
 7257        cx: &mut Context<Self>,
 7258    ) {
 7259        self.code_action_providers
 7260            .retain(|provider| provider.id() != id);
 7261        self.refresh_code_actions(window, cx);
 7262    }
 7263
 7264    pub fn code_actions_enabled_for_toolbar(&self, cx: &App) -> bool {
 7265        !self.code_action_providers.is_empty()
 7266            && EditorSettings::get_global(cx).toolbar.code_actions
 7267    }
 7268
 7269    pub fn has_available_code_actions(&self) -> bool {
 7270        self.available_code_actions
 7271            .as_ref()
 7272            .is_some_and(|(_, actions)| !actions.is_empty())
 7273    }
 7274
 7275    fn render_inline_code_actions(
 7276        &self,
 7277        icon_size: ui::IconSize,
 7278        display_row: DisplayRow,
 7279        is_active: bool,
 7280        cx: &mut Context<Self>,
 7281    ) -> AnyElement {
 7282        let show_tooltip = !self.context_menu_visible();
 7283        IconButton::new("inline_code_actions", ui::IconName::BoltFilled)
 7284            .icon_size(icon_size)
 7285            .shape(ui::IconButtonShape::Square)
 7286            .icon_color(ui::Color::Hidden)
 7287            .toggle_state(is_active)
 7288            .when(show_tooltip, |this| {
 7289                this.tooltip({
 7290                    let focus_handle = self.focus_handle.clone();
 7291                    move |_window, cx| {
 7292                        Tooltip::for_action_in(
 7293                            "Toggle Code Actions",
 7294                            &ToggleCodeActions {
 7295                                deployed_from: None,
 7296                                quick_launch: false,
 7297                            },
 7298                            &focus_handle,
 7299                            cx,
 7300                        )
 7301                    }
 7302                })
 7303            })
 7304            .on_click(cx.listener(move |editor, _: &ClickEvent, window, cx| {
 7305                window.focus(&editor.focus_handle(cx), cx);
 7306                editor.toggle_code_actions(
 7307                    &crate::actions::ToggleCodeActions {
 7308                        deployed_from: Some(crate::actions::CodeActionSource::Indicator(
 7309                            display_row,
 7310                        )),
 7311                        quick_launch: false,
 7312                    },
 7313                    window,
 7314                    cx,
 7315                );
 7316            }))
 7317            .into_any_element()
 7318    }
 7319
 7320    pub fn context_menu(&self) -> &RefCell<Option<CodeContextMenu>> {
 7321        &self.context_menu
 7322    }
 7323
 7324    fn refresh_code_actions(&mut self, window: &mut Window, cx: &mut Context<Self>) {
 7325        self.code_actions_task = Some(cx.spawn_in(window, async move |this, cx| {
 7326            cx.background_executor()
 7327                .timer(CODE_ACTIONS_DEBOUNCE_TIMEOUT)
 7328                .await;
 7329
 7330            let (start_buffer, start, _, end, newest_selection) = this
 7331                .update(cx, |this, cx| {
 7332                    let newest_selection = this.selections.newest_anchor().clone();
 7333                    if newest_selection.head().diff_base_anchor.is_some() {
 7334                        return None;
 7335                    }
 7336                    let display_snapshot = this.display_snapshot(cx);
 7337                    let newest_selection_adjusted =
 7338                        this.selections.newest_adjusted(&display_snapshot);
 7339                    let buffer = this.buffer.read(cx);
 7340
 7341                    let (start_buffer, start) =
 7342                        buffer.text_anchor_for_position(newest_selection_adjusted.start, cx)?;
 7343                    let (end_buffer, end) =
 7344                        buffer.text_anchor_for_position(newest_selection_adjusted.end, cx)?;
 7345
 7346                    Some((start_buffer, start, end_buffer, end, newest_selection))
 7347                })?
 7348                .filter(|(start_buffer, _, end_buffer, _, _)| start_buffer == end_buffer)
 7349                .context(
 7350                    "Expected selection to lie in a single buffer when refreshing code actions",
 7351                )?;
 7352            let (providers, tasks) = this.update_in(cx, |this, window, cx| {
 7353                let providers = this.code_action_providers.clone();
 7354                let tasks = this
 7355                    .code_action_providers
 7356                    .iter()
 7357                    .map(|provider| provider.code_actions(&start_buffer, start..end, window, cx))
 7358                    .collect::<Vec<_>>();
 7359                (providers, tasks)
 7360            })?;
 7361
 7362            let mut actions = Vec::new();
 7363            for (provider, provider_actions) in
 7364                providers.into_iter().zip(future::join_all(tasks).await)
 7365            {
 7366                if let Some(provider_actions) = provider_actions.log_err() {
 7367                    actions.extend(provider_actions.into_iter().map(|action| {
 7368                        AvailableCodeAction {
 7369                            excerpt_id: newest_selection.start.excerpt_id,
 7370                            action,
 7371                            provider: provider.clone(),
 7372                        }
 7373                    }));
 7374                }
 7375            }
 7376
 7377            this.update(cx, |this, cx| {
 7378                this.available_code_actions = if actions.is_empty() {
 7379                    None
 7380                } else {
 7381                    Some((
 7382                        Location {
 7383                            buffer: start_buffer,
 7384                            range: start..end,
 7385                        },
 7386                        actions.into(),
 7387                    ))
 7388                };
 7389                cx.notify();
 7390            })
 7391        }));
 7392    }
 7393
 7394    fn start_inline_blame_timer(&mut self, window: &mut Window, cx: &mut Context<Self>) {
 7395        if let Some(delay) = ProjectSettings::get_global(cx).git.inline_blame_delay() {
 7396            self.show_git_blame_inline = false;
 7397
 7398            self.show_git_blame_inline_delay_task =
 7399                Some(cx.spawn_in(window, async move |this, cx| {
 7400                    cx.background_executor().timer(delay).await;
 7401
 7402                    this.update(cx, |this, cx| {
 7403                        this.show_git_blame_inline = true;
 7404                        cx.notify();
 7405                    })
 7406                    .log_err();
 7407                }));
 7408        }
 7409    }
 7410
 7411    pub fn blame_hover(&mut self, _: &BlameHover, window: &mut Window, cx: &mut Context<Self>) {
 7412        let snapshot = self.snapshot(window, cx);
 7413        let cursor = self
 7414            .selections
 7415            .newest::<Point>(&snapshot.display_snapshot)
 7416            .head();
 7417        let Some((buffer, point, _)) = snapshot.buffer_snapshot().point_to_buffer_point(cursor)
 7418        else {
 7419            return;
 7420        };
 7421
 7422        if self.blame.is_none() {
 7423            self.start_git_blame(true, window, cx);
 7424        }
 7425        let Some(blame) = self.blame.as_ref() else {
 7426            return;
 7427        };
 7428
 7429        let row_info = RowInfo {
 7430            buffer_id: Some(buffer.remote_id()),
 7431            buffer_row: Some(point.row),
 7432            ..Default::default()
 7433        };
 7434        let Some((buffer, blame_entry)) = blame
 7435            .update(cx, |blame, cx| blame.blame_for_rows(&[row_info], cx).next())
 7436            .flatten()
 7437        else {
 7438            return;
 7439        };
 7440
 7441        let anchor = self.selections.newest_anchor().head();
 7442        let position = self.to_pixel_point(anchor, &snapshot, window, cx);
 7443        if let (Some(position), Some(last_bounds)) = (position, self.last_bounds) {
 7444            self.show_blame_popover(
 7445                buffer,
 7446                &blame_entry,
 7447                position + last_bounds.origin,
 7448                true,
 7449                cx,
 7450            );
 7451        };
 7452    }
 7453
 7454    fn show_blame_popover(
 7455        &mut self,
 7456        buffer: BufferId,
 7457        blame_entry: &BlameEntry,
 7458        position: gpui::Point<Pixels>,
 7459        ignore_timeout: bool,
 7460        cx: &mut Context<Self>,
 7461    ) {
 7462        if let Some(state) = &mut self.inline_blame_popover {
 7463            state.hide_task.take();
 7464        } else {
 7465            let blame_popover_delay = EditorSettings::get_global(cx).hover_popover_delay.0;
 7466            let blame_entry = blame_entry.clone();
 7467            let show_task = cx.spawn(async move |editor, cx| {
 7468                if !ignore_timeout {
 7469                    cx.background_executor()
 7470                        .timer(std::time::Duration::from_millis(blame_popover_delay))
 7471                        .await;
 7472                }
 7473                editor
 7474                    .update(cx, |editor, cx| {
 7475                        editor.inline_blame_popover_show_task.take();
 7476                        let Some(blame) = editor.blame.as_ref() else {
 7477                            return;
 7478                        };
 7479                        let blame = blame.read(cx);
 7480                        let details = blame.details_for_entry(buffer, &blame_entry);
 7481                        let markdown = cx.new(|cx| {
 7482                            Markdown::new(
 7483                                details
 7484                                    .as_ref()
 7485                                    .map(|message| message.message.clone())
 7486                                    .unwrap_or_default(),
 7487                                None,
 7488                                None,
 7489                                cx,
 7490                            )
 7491                        });
 7492                        editor.inline_blame_popover = Some(InlineBlamePopover {
 7493                            position,
 7494                            hide_task: None,
 7495                            popover_bounds: None,
 7496                            popover_state: InlineBlamePopoverState {
 7497                                scroll_handle: ScrollHandle::new(),
 7498                                commit_message: details,
 7499                                markdown,
 7500                            },
 7501                            keyboard_grace: ignore_timeout,
 7502                        });
 7503                        cx.notify();
 7504                    })
 7505                    .ok();
 7506            });
 7507            self.inline_blame_popover_show_task = Some(show_task);
 7508        }
 7509    }
 7510
 7511    pub fn has_mouse_context_menu(&self) -> bool {
 7512        self.mouse_context_menu.is_some()
 7513    }
 7514
 7515    pub fn hide_blame_popover(&mut self, ignore_timeout: bool, cx: &mut Context<Self>) -> bool {
 7516        self.inline_blame_popover_show_task.take();
 7517        if let Some(state) = &mut self.inline_blame_popover {
 7518            let hide_task = cx.spawn(async move |editor, cx| {
 7519                if !ignore_timeout {
 7520                    cx.background_executor()
 7521                        .timer(std::time::Duration::from_millis(100))
 7522                        .await;
 7523                }
 7524                editor
 7525                    .update(cx, |editor, cx| {
 7526                        editor.inline_blame_popover.take();
 7527                        cx.notify();
 7528                    })
 7529                    .ok();
 7530            });
 7531            state.hide_task = Some(hide_task);
 7532            true
 7533        } else {
 7534            false
 7535        }
 7536    }
 7537
 7538    fn refresh_document_highlights(&mut self, cx: &mut Context<Self>) -> Option<()> {
 7539        if self.pending_rename.is_some() {
 7540            return None;
 7541        }
 7542
 7543        let provider = self.semantics_provider.clone()?;
 7544        let buffer = self.buffer.read(cx);
 7545        let newest_selection = self.selections.newest_anchor().clone();
 7546        let cursor_position = newest_selection.head();
 7547        let (cursor_buffer, cursor_buffer_position) =
 7548            buffer.text_anchor_for_position(cursor_position, cx)?;
 7549        let (tail_buffer, tail_buffer_position) =
 7550            buffer.text_anchor_for_position(newest_selection.tail(), cx)?;
 7551        if cursor_buffer != tail_buffer {
 7552            return None;
 7553        }
 7554
 7555        let snapshot = cursor_buffer.read(cx).snapshot();
 7556        let word_ranges = cx.background_spawn(async move {
 7557            // this might look odd to put on the background thread, but
 7558            // `surrounding_word` can be quite expensive as it calls into
 7559            // tree-sitter language scopes
 7560            let (start_word_range, _) = snapshot.surrounding_word(cursor_buffer_position, None);
 7561            let (end_word_range, _) = snapshot.surrounding_word(tail_buffer_position, None);
 7562            (start_word_range, end_word_range)
 7563        });
 7564
 7565        let debounce = EditorSettings::get_global(cx).lsp_highlight_debounce.0;
 7566        self.document_highlights_task = Some(cx.spawn(async move |this, cx| {
 7567            let (start_word_range, end_word_range) = word_ranges.await;
 7568            if start_word_range != end_word_range {
 7569                this.update(cx, |this, cx| {
 7570                    this.document_highlights_task.take();
 7571                    this.clear_background_highlights(HighlightKey::DocumentHighlightRead, cx);
 7572                    this.clear_background_highlights(HighlightKey::DocumentHighlightWrite, cx);
 7573                })
 7574                .ok();
 7575                return;
 7576            }
 7577            cx.background_executor()
 7578                .timer(Duration::from_millis(debounce))
 7579                .await;
 7580
 7581            let highlights = if let Some(highlights) = cx.update(|cx| {
 7582                provider.document_highlights(&cursor_buffer, cursor_buffer_position, cx)
 7583            }) {
 7584                highlights.await.log_err()
 7585            } else {
 7586                None
 7587            };
 7588
 7589            if let Some(highlights) = highlights {
 7590                this.update(cx, |this, cx| {
 7591                    if this.pending_rename.is_some() {
 7592                        return;
 7593                    }
 7594
 7595                    let buffer = this.buffer.read(cx);
 7596                    if buffer
 7597                        .text_anchor_for_position(cursor_position, cx)
 7598                        .is_none_or(|(buffer, _)| buffer != cursor_buffer)
 7599                    {
 7600                        return;
 7601                    }
 7602
 7603                    let cursor_buffer_snapshot = cursor_buffer.read(cx);
 7604                    let mut write_ranges = Vec::new();
 7605                    let mut read_ranges = Vec::new();
 7606                    for highlight in highlights {
 7607                        let buffer_id = cursor_buffer.read(cx).remote_id();
 7608                        for (excerpt_id, _, excerpt_range) in
 7609                            buffer.excerpts_for_buffer(buffer_id, cx)
 7610                        {
 7611                            let start = highlight
 7612                                .range
 7613                                .start
 7614                                .max(&excerpt_range.context.start, cursor_buffer_snapshot);
 7615                            let end = highlight
 7616                                .range
 7617                                .end
 7618                                .min(&excerpt_range.context.end, cursor_buffer_snapshot);
 7619                            if start.cmp(&end, cursor_buffer_snapshot).is_ge() {
 7620                                continue;
 7621                            }
 7622
 7623                            let range = Anchor::range_in_buffer(excerpt_id, *start..*end);
 7624                            if highlight.kind == lsp::DocumentHighlightKind::WRITE {
 7625                                write_ranges.push(range);
 7626                            } else {
 7627                                read_ranges.push(range);
 7628                            }
 7629                        }
 7630                    }
 7631
 7632                    this.highlight_background(
 7633                        HighlightKey::DocumentHighlightRead,
 7634                        &read_ranges,
 7635                        |_, theme| theme.colors().editor_document_highlight_read_background,
 7636                        cx,
 7637                    );
 7638                    this.highlight_background(
 7639                        HighlightKey::DocumentHighlightWrite,
 7640                        &write_ranges,
 7641                        |_, theme| theme.colors().editor_document_highlight_write_background,
 7642                        cx,
 7643                    );
 7644                    cx.notify();
 7645                })
 7646                .log_err();
 7647            }
 7648        }));
 7649        None
 7650    }
 7651
 7652    fn prepare_highlight_query_from_selection(
 7653        &mut self,
 7654        snapshot: &DisplaySnapshot,
 7655        cx: &mut Context<Editor>,
 7656    ) -> Option<(String, Range<Anchor>)> {
 7657        if matches!(self.mode, EditorMode::SingleLine) {
 7658            return None;
 7659        }
 7660        if !EditorSettings::get_global(cx).selection_highlight {
 7661            return None;
 7662        }
 7663        if self.selections.count() != 1 || self.selections.line_mode() {
 7664            return None;
 7665        }
 7666        let selection = self.selections.newest::<Point>(&snapshot);
 7667        // If the selection spans multiple rows OR it is empty
 7668        if selection.start.row != selection.end.row
 7669            || selection.start.column == selection.end.column
 7670        {
 7671            return None;
 7672        }
 7673        let selection_anchor_range = selection.range().to_anchors(snapshot.buffer_snapshot());
 7674        let query = snapshot
 7675            .buffer_snapshot()
 7676            .text_for_range(selection_anchor_range.clone())
 7677            .collect::<String>();
 7678        if query.trim().is_empty() {
 7679            return None;
 7680        }
 7681        Some((query, selection_anchor_range))
 7682    }
 7683
 7684    #[ztracing::instrument(skip_all)]
 7685    fn update_selection_occurrence_highlights(
 7686        &mut self,
 7687        multi_buffer_snapshot: MultiBufferSnapshot,
 7688        query_text: String,
 7689        query_range: Range<Anchor>,
 7690        multi_buffer_range_to_query: Range<Point>,
 7691        use_debounce: bool,
 7692        window: &mut Window,
 7693        cx: &mut Context<Editor>,
 7694    ) -> Task<()> {
 7695        cx.spawn_in(window, async move |editor, cx| {
 7696            if use_debounce {
 7697                cx.background_executor()
 7698                    .timer(SELECTION_HIGHLIGHT_DEBOUNCE_TIMEOUT)
 7699                    .await;
 7700            }
 7701            let match_task = cx.background_spawn(async move {
 7702                let buffer_ranges = multi_buffer_snapshot
 7703                    .range_to_buffer_ranges(
 7704                        multi_buffer_range_to_query.start..=multi_buffer_range_to_query.end,
 7705                    )
 7706                    .into_iter()
 7707                    .filter(|(_, excerpt_visible_range, _)| !excerpt_visible_range.is_empty());
 7708                let mut match_ranges = Vec::new();
 7709                let Ok(regex) = project::search::SearchQuery::text(
 7710                    query_text,
 7711                    false,
 7712                    false,
 7713                    false,
 7714                    Default::default(),
 7715                    Default::default(),
 7716                    false,
 7717                    None,
 7718                ) else {
 7719                    return Vec::default();
 7720                };
 7721                let query_range = query_range.to_anchors(&multi_buffer_snapshot);
 7722                for (buffer_snapshot, search_range, excerpt_id) in buffer_ranges {
 7723                    match_ranges.extend(
 7724                        regex
 7725                            .search(
 7726                                buffer_snapshot,
 7727                                Some(search_range.start.0..search_range.end.0),
 7728                            )
 7729                            .await
 7730                            .into_iter()
 7731                            .filter_map(|match_range| {
 7732                                let match_start = buffer_snapshot
 7733                                    .anchor_after(search_range.start + match_range.start);
 7734                                let match_end = buffer_snapshot
 7735                                    .anchor_before(search_range.start + match_range.end);
 7736                                let match_anchor_range =
 7737                                    Anchor::range_in_buffer(excerpt_id, match_start..match_end);
 7738                                (match_anchor_range != query_range).then_some(match_anchor_range)
 7739                            }),
 7740                    );
 7741                }
 7742                match_ranges
 7743            });
 7744            let match_ranges = match_task.await;
 7745            editor
 7746                .update_in(cx, |editor, _, cx| {
 7747                    if use_debounce {
 7748                        editor.clear_background_highlights(HighlightKey::SelectedTextHighlight, cx);
 7749                        editor.debounced_selection_highlight_complete = true;
 7750                    } else if editor.debounced_selection_highlight_complete {
 7751                        return;
 7752                    }
 7753                    if !match_ranges.is_empty() {
 7754                        editor.highlight_background(
 7755                            HighlightKey::SelectedTextHighlight,
 7756                            &match_ranges,
 7757                            |_, theme| theme.colors().editor_document_highlight_bracket_background,
 7758                            cx,
 7759                        )
 7760                    }
 7761                })
 7762                .log_err();
 7763        })
 7764    }
 7765
 7766    fn refresh_single_line_folds(&mut self, window: &mut Window, cx: &mut Context<Editor>) {
 7767        struct NewlineFold;
 7768        let type_id = std::any::TypeId::of::<NewlineFold>();
 7769        if !self.mode.is_single_line() {
 7770            return;
 7771        }
 7772        let snapshot = self.snapshot(window, cx);
 7773        if snapshot.buffer_snapshot().max_point().row == 0 {
 7774            return;
 7775        }
 7776        let task = cx.background_spawn(async move {
 7777            let new_newlines = snapshot
 7778                .buffer_chars_at(MultiBufferOffset(0))
 7779                .filter_map(|(c, i)| {
 7780                    if c == '\n' {
 7781                        Some(
 7782                            snapshot.buffer_snapshot().anchor_after(i)
 7783                                ..snapshot.buffer_snapshot().anchor_before(i + 1usize),
 7784                        )
 7785                    } else {
 7786                        None
 7787                    }
 7788                })
 7789                .collect::<Vec<_>>();
 7790            let existing_newlines = snapshot
 7791                .folds_in_range(MultiBufferOffset(0)..snapshot.buffer_snapshot().len())
 7792                .filter_map(|fold| {
 7793                    if fold.placeholder.type_tag == Some(type_id) {
 7794                        Some(fold.range.start..fold.range.end)
 7795                    } else {
 7796                        None
 7797                    }
 7798                })
 7799                .collect::<Vec<_>>();
 7800
 7801            (new_newlines, existing_newlines)
 7802        });
 7803        self.folding_newlines = cx.spawn(async move |this, cx| {
 7804            let (new_newlines, existing_newlines) = task.await;
 7805            if new_newlines == existing_newlines {
 7806                return;
 7807            }
 7808            let placeholder = FoldPlaceholder {
 7809                render: Arc::new(move |_, _, cx| {
 7810                    div()
 7811                        .bg(cx.theme().status().hint_background)
 7812                        .border_b_1()
 7813                        .size_full()
 7814                        .font(ThemeSettings::get_global(cx).buffer_font.clone())
 7815                        .border_color(cx.theme().status().hint)
 7816                        .child("\\n")
 7817                        .into_any()
 7818                }),
 7819                constrain_width: false,
 7820                merge_adjacent: false,
 7821                type_tag: Some(type_id),
 7822                collapsed_text: None,
 7823            };
 7824            let creases = new_newlines
 7825                .into_iter()
 7826                .map(|range| Crease::simple(range, placeholder.clone()))
 7827                .collect();
 7828            this.update(cx, |this, cx| {
 7829                this.display_map.update(cx, |display_map, cx| {
 7830                    display_map.remove_folds_with_type(existing_newlines, type_id, cx);
 7831                    display_map.fold(creases, cx);
 7832                });
 7833            })
 7834            .ok();
 7835        });
 7836    }
 7837
 7838    #[ztracing::instrument(skip_all)]
 7839    fn refresh_outline_symbols_at_cursor(&mut self, cx: &mut Context<Editor>) {
 7840        if !self.lsp_data_enabled() {
 7841            return;
 7842        }
 7843        let cursor = self.selections.newest_anchor().head();
 7844        let multi_buffer_snapshot = self.buffer().read(cx).snapshot(cx);
 7845
 7846        if self.uses_lsp_document_symbols(cursor, &multi_buffer_snapshot, cx) {
 7847            self.outline_symbols_at_cursor =
 7848                self.lsp_symbols_at_cursor(cursor, &multi_buffer_snapshot, cx);
 7849            cx.emit(EditorEvent::OutlineSymbolsChanged);
 7850            cx.notify();
 7851        } else {
 7852            let syntax = cx.theme().syntax().clone();
 7853            let background_task = cx.background_spawn(async move {
 7854                multi_buffer_snapshot.symbols_containing(cursor, Some(&syntax))
 7855            });
 7856            self.refresh_outline_symbols_at_cursor_at_cursor_task =
 7857                cx.spawn(async move |this, cx| {
 7858                    let symbols = background_task.await;
 7859                    this.update(cx, |this, cx| {
 7860                        this.outline_symbols_at_cursor = symbols;
 7861                        cx.emit(EditorEvent::OutlineSymbolsChanged);
 7862                        cx.notify();
 7863                    })
 7864                    .ok();
 7865                });
 7866        }
 7867    }
 7868
 7869    #[ztracing::instrument(skip_all)]
 7870    fn refresh_selected_text_highlights(
 7871        &mut self,
 7872        snapshot: &DisplaySnapshot,
 7873        on_buffer_edit: bool,
 7874        window: &mut Window,
 7875        cx: &mut Context<Editor>,
 7876    ) {
 7877        let Some((query_text, query_range)) =
 7878            self.prepare_highlight_query_from_selection(snapshot, cx)
 7879        else {
 7880            self.clear_background_highlights(HighlightKey::SelectedTextHighlight, cx);
 7881            self.quick_selection_highlight_task.take();
 7882            self.debounced_selection_highlight_task.take();
 7883            self.debounced_selection_highlight_complete = false;
 7884            return;
 7885        };
 7886        let display_snapshot = self.display_map.update(cx, |map, cx| map.snapshot(cx));
 7887        let multi_buffer_snapshot = self.buffer().read(cx).snapshot(cx);
 7888        let query_changed = self
 7889            .quick_selection_highlight_task
 7890            .as_ref()
 7891            .is_none_or(|(prev_anchor_range, _)| prev_anchor_range != &query_range);
 7892        if query_changed {
 7893            self.debounced_selection_highlight_complete = false;
 7894        }
 7895        if on_buffer_edit || query_changed {
 7896            self.quick_selection_highlight_task = Some((
 7897                query_range.clone(),
 7898                self.update_selection_occurrence_highlights(
 7899                    snapshot.buffer.clone(),
 7900                    query_text.clone(),
 7901                    query_range.clone(),
 7902                    self.multi_buffer_visible_range(&display_snapshot, cx),
 7903                    false,
 7904                    window,
 7905                    cx,
 7906                ),
 7907            ));
 7908        }
 7909        if on_buffer_edit
 7910            || self
 7911                .debounced_selection_highlight_task
 7912                .as_ref()
 7913                .is_none_or(|(prev_anchor_range, _)| prev_anchor_range != &query_range)
 7914        {
 7915            let multi_buffer_start = multi_buffer_snapshot
 7916                .anchor_before(MultiBufferOffset(0))
 7917                .to_point(&multi_buffer_snapshot);
 7918            let multi_buffer_end = multi_buffer_snapshot
 7919                .anchor_after(multi_buffer_snapshot.len())
 7920                .to_point(&multi_buffer_snapshot);
 7921            let multi_buffer_full_range = multi_buffer_start..multi_buffer_end;
 7922            self.debounced_selection_highlight_task = Some((
 7923                query_range.clone(),
 7924                self.update_selection_occurrence_highlights(
 7925                    snapshot.buffer.clone(),
 7926                    query_text,
 7927                    query_range,
 7928                    multi_buffer_full_range,
 7929                    true,
 7930                    window,
 7931                    cx,
 7932                ),
 7933            ));
 7934        }
 7935    }
 7936
 7937    pub fn multi_buffer_visible_range(
 7938        &self,
 7939        display_snapshot: &DisplaySnapshot,
 7940        cx: &App,
 7941    ) -> Range<Point> {
 7942        let visible_start = self
 7943            .scroll_manager
 7944            .native_anchor(display_snapshot, cx)
 7945            .anchor
 7946            .to_point(display_snapshot.buffer_snapshot())
 7947            .to_display_point(display_snapshot);
 7948
 7949        let mut target_end = visible_start;
 7950        *target_end.row_mut() += self.visible_line_count().unwrap_or(0.).ceil() as u32;
 7951
 7952        visible_start.to_point(display_snapshot)
 7953            ..display_snapshot
 7954                .clip_point(target_end, Bias::Right)
 7955                .to_point(display_snapshot)
 7956    }
 7957
 7958    pub fn refresh_edit_prediction(
 7959        &mut self,
 7960        debounce: bool,
 7961        user_requested: bool,
 7962        window: &mut Window,
 7963        cx: &mut Context<Self>,
 7964    ) -> Option<()> {
 7965        if self.leader_id.is_some() {
 7966            self.discard_edit_prediction(EditPredictionDiscardReason::Ignored, cx);
 7967            return None;
 7968        }
 7969
 7970        let cursor = self.selections.newest_anchor().head();
 7971        let (buffer, cursor_buffer_position) =
 7972            self.buffer.read(cx).text_anchor_for_position(cursor, cx)?;
 7973
 7974        if DisableAiSettings::is_ai_disabled_for_buffer(Some(&buffer), cx) {
 7975            return None;
 7976        }
 7977
 7978        if !self.edit_predictions_enabled_in_buffer(&buffer, cursor_buffer_position, cx) {
 7979            self.discard_edit_prediction(EditPredictionDiscardReason::Ignored, cx);
 7980            return None;
 7981        }
 7982
 7983        self.update_visible_edit_prediction(window, cx);
 7984
 7985        if !user_requested
 7986            && (!self.should_show_edit_predictions()
 7987                || !self.is_focused(window)
 7988                || buffer.read(cx).is_empty())
 7989        {
 7990            self.discard_edit_prediction(EditPredictionDiscardReason::Ignored, cx);
 7991            return None;
 7992        }
 7993
 7994        self.edit_prediction_provider()?
 7995            .refresh(buffer, cursor_buffer_position, debounce, cx);
 7996        Some(())
 7997    }
 7998
 7999    fn show_edit_predictions_in_menu(&self) -> bool {
 8000        match self.edit_prediction_settings {
 8001            EditPredictionSettings::Disabled => false,
 8002            EditPredictionSettings::Enabled { show_in_menu, .. } => show_in_menu,
 8003        }
 8004    }
 8005
 8006    pub fn edit_predictions_enabled(&self) -> bool {
 8007        match self.edit_prediction_settings {
 8008            EditPredictionSettings::Disabled => false,
 8009            EditPredictionSettings::Enabled { .. } => true,
 8010        }
 8011    }
 8012
 8013    fn edit_prediction_requires_modifier(&self) -> bool {
 8014        match self.edit_prediction_settings {
 8015            EditPredictionSettings::Disabled => false,
 8016            EditPredictionSettings::Enabled {
 8017                preview_requires_modifier,
 8018                ..
 8019            } => preview_requires_modifier,
 8020        }
 8021    }
 8022
 8023    pub fn update_edit_prediction_settings(&mut self, cx: &mut Context<Self>) {
 8024        if self.edit_prediction_provider.is_none() {
 8025            self.edit_prediction_settings = EditPredictionSettings::Disabled;
 8026            self.discard_edit_prediction(EditPredictionDiscardReason::Ignored, cx);
 8027            return;
 8028        }
 8029
 8030        let selection = self.selections.newest_anchor();
 8031        let cursor = selection.head();
 8032
 8033        if let Some((buffer, cursor_buffer_position)) =
 8034            self.buffer.read(cx).text_anchor_for_position(cursor, cx)
 8035        {
 8036            if DisableAiSettings::is_ai_disabled_for_buffer(Some(&buffer), cx) {
 8037                self.edit_prediction_settings = EditPredictionSettings::Disabled;
 8038                self.discard_edit_prediction(EditPredictionDiscardReason::Ignored, cx);
 8039                return;
 8040            }
 8041            self.edit_prediction_settings =
 8042                self.edit_prediction_settings_at_position(&buffer, cursor_buffer_position, cx);
 8043        }
 8044    }
 8045
 8046    fn edit_prediction_settings_at_position(
 8047        &self,
 8048        buffer: &Entity<Buffer>,
 8049        buffer_position: language::Anchor,
 8050        cx: &App,
 8051    ) -> EditPredictionSettings {
 8052        if !self.mode.is_full()
 8053            || !self.show_edit_predictions_override.unwrap_or(true)
 8054            || self.edit_predictions_disabled_in_scope(buffer, buffer_position, cx)
 8055        {
 8056            return EditPredictionSettings::Disabled;
 8057        }
 8058
 8059        if !LanguageSettings::for_buffer(&buffer.read(cx), cx).show_edit_predictions {
 8060            return EditPredictionSettings::Disabled;
 8061        };
 8062
 8063        let by_provider = matches!(
 8064            self.menu_edit_predictions_policy,
 8065            MenuEditPredictionsPolicy::ByProvider
 8066        );
 8067
 8068        let show_in_menu = by_provider
 8069            && self
 8070                .edit_prediction_provider
 8071                .as_ref()
 8072                .is_some_and(|provider| provider.provider.show_predictions_in_menu());
 8073
 8074        let file = buffer.read(cx).file();
 8075        let preview_requires_modifier =
 8076            all_language_settings(file, cx).edit_predictions_mode() == EditPredictionsMode::Subtle;
 8077
 8078        EditPredictionSettings::Enabled {
 8079            show_in_menu,
 8080            preview_requires_modifier,
 8081        }
 8082    }
 8083
 8084    fn should_show_edit_predictions(&self) -> bool {
 8085        self.snippet_stack.is_empty() && self.edit_predictions_enabled()
 8086    }
 8087
 8088    pub fn edit_prediction_preview_is_active(&self) -> bool {
 8089        matches!(
 8090            self.edit_prediction_preview,
 8091            EditPredictionPreview::Active { .. }
 8092        )
 8093    }
 8094
 8095    pub fn edit_predictions_enabled_at_cursor(&self, cx: &App) -> bool {
 8096        let cursor = self.selections.newest_anchor().head();
 8097        if let Some((buffer, cursor_position)) =
 8098            self.buffer.read(cx).text_anchor_for_position(cursor, cx)
 8099        {
 8100            self.edit_predictions_enabled_in_buffer(&buffer, cursor_position, cx)
 8101        } else {
 8102            false
 8103        }
 8104    }
 8105
 8106    pub fn supports_minimap(&self, cx: &App) -> bool {
 8107        !self.minimap_visibility.disabled() && self.buffer_kind(cx) == ItemBufferKind::Singleton
 8108    }
 8109
 8110    fn edit_predictions_enabled_in_buffer(
 8111        &self,
 8112        buffer: &Entity<Buffer>,
 8113        buffer_position: language::Anchor,
 8114        cx: &App,
 8115    ) -> bool {
 8116        maybe!({
 8117            if self.read_only(cx) || self.leader_id.is_some() {
 8118                return Some(false);
 8119            }
 8120            let provider = self.edit_prediction_provider()?;
 8121            if !provider.is_enabled(buffer, buffer_position, cx) {
 8122                return Some(false);
 8123            }
 8124            let buffer = buffer.read(cx);
 8125            let Some(file) = buffer.file() else {
 8126                return Some(true);
 8127            };
 8128            let settings = all_language_settings(Some(file), cx);
 8129            Some(settings.edit_predictions_enabled_for_file(file, cx))
 8130        })
 8131        .unwrap_or(false)
 8132    }
 8133
 8134    pub fn show_edit_prediction(
 8135        &mut self,
 8136        _: &ShowEditPrediction,
 8137        window: &mut Window,
 8138        cx: &mut Context<Self>,
 8139    ) {
 8140        if !self.has_active_edit_prediction() {
 8141            self.refresh_edit_prediction(false, true, window, cx);
 8142            return;
 8143        }
 8144
 8145        self.update_visible_edit_prediction(window, cx);
 8146    }
 8147
 8148    pub fn display_cursor_names(
 8149        &mut self,
 8150        _: &DisplayCursorNames,
 8151        window: &mut Window,
 8152        cx: &mut Context<Self>,
 8153    ) {
 8154        self.show_cursor_names(window, cx);
 8155    }
 8156
 8157    fn show_cursor_names(&mut self, window: &mut Window, cx: &mut Context<Self>) {
 8158        self.show_cursor_names = true;
 8159        cx.notify();
 8160        cx.spawn_in(window, async move |this, cx| {
 8161            cx.background_executor().timer(CURSORS_VISIBLE_FOR).await;
 8162            this.update(cx, |this, cx| {
 8163                this.show_cursor_names = false;
 8164                cx.notify()
 8165            })
 8166            .ok()
 8167        })
 8168        .detach();
 8169    }
 8170
 8171    pub fn accept_partial_edit_prediction(
 8172        &mut self,
 8173        granularity: EditPredictionGranularity,
 8174        window: &mut Window,
 8175        cx: &mut Context<Self>,
 8176    ) {
 8177        if self.show_edit_predictions_in_menu() {
 8178            self.hide_context_menu(window, cx);
 8179        }
 8180
 8181        let Some(active_edit_prediction) = self.active_edit_prediction.as_ref() else {
 8182            return;
 8183        };
 8184
 8185        if !matches!(granularity, EditPredictionGranularity::Full) && self.selections.count() != 1 {
 8186            return;
 8187        }
 8188
 8189        match &active_edit_prediction.completion {
 8190            EditPrediction::MoveWithin { target, .. } => {
 8191                let target = *target;
 8192
 8193                if matches!(granularity, EditPredictionGranularity::Full) {
 8194                    if let Some(position_map) = &self.last_position_map {
 8195                        let target_row = target.to_display_point(&position_map.snapshot).row();
 8196                        let is_visible = position_map.visible_row_range.contains(&target_row);
 8197
 8198                        if is_visible || !self.edit_prediction_requires_modifier() {
 8199                            self.unfold_ranges(&[target..target], true, false, cx);
 8200                            self.change_selections(
 8201                                SelectionEffects::scroll(Autoscroll::newest()),
 8202                                window,
 8203                                cx,
 8204                                |selections| {
 8205                                    selections.select_anchor_ranges([target..target]);
 8206                                },
 8207                            );
 8208                            self.clear_row_highlights::<EditPredictionPreview>();
 8209                            self.edit_prediction_preview
 8210                                .set_previous_scroll_position(None);
 8211                        } else {
 8212                            // Highlight and request scroll
 8213                            self.edit_prediction_preview
 8214                                .set_previous_scroll_position(Some(
 8215                                    position_map.snapshot.scroll_anchor,
 8216                                ));
 8217                            self.highlight_rows::<EditPredictionPreview>(
 8218                                target..target,
 8219                                cx.theme().colors().editor_highlighted_line_background,
 8220                                RowHighlightOptions {
 8221                                    autoscroll: true,
 8222                                    ..Default::default()
 8223                                },
 8224                                cx,
 8225                            );
 8226                            self.request_autoscroll(Autoscroll::fit(), cx);
 8227                        }
 8228                    }
 8229                } else {
 8230                    self.change_selections(
 8231                        SelectionEffects::scroll(Autoscroll::newest()),
 8232                        window,
 8233                        cx,
 8234                        |selections| {
 8235                            selections.select_anchor_ranges([target..target]);
 8236                        },
 8237                    );
 8238                }
 8239            }
 8240            EditPrediction::MoveOutside { snapshot, target } => {
 8241                if let Some(workspace) = self.workspace() {
 8242                    Self::open_editor_at_anchor(snapshot, *target, &workspace, window, cx)
 8243                        .detach_and_log_err(cx);
 8244                }
 8245            }
 8246            EditPrediction::Edit {
 8247                edits,
 8248                cursor_position,
 8249                ..
 8250            } => {
 8251                self.report_edit_prediction_event(
 8252                    active_edit_prediction.completion_id.clone(),
 8253                    true,
 8254                    cx,
 8255                );
 8256
 8257                match granularity {
 8258                    EditPredictionGranularity::Full => {
 8259                        let transaction_id_prev = self.buffer.read(cx).last_transaction_id(cx);
 8260
 8261                        // Compute fallback cursor position BEFORE applying the edit,
 8262                        // so the anchor tracks through the edit correctly
 8263                        let fallback_cursor_target = {
 8264                            let snapshot = self.buffer.read(cx).snapshot(cx);
 8265                            edits.last().unwrap().0.end.bias_right(&snapshot)
 8266                        };
 8267
 8268                        self.buffer.update(cx, |buffer, cx| {
 8269                            buffer.edit(edits.iter().cloned(), None, cx)
 8270                        });
 8271
 8272                        if let Some(provider) = self.edit_prediction_provider() {
 8273                            provider.accept(cx);
 8274                        }
 8275
 8276                        // Resolve cursor position after the edit is applied
 8277                        let cursor_target = if let Some((anchor, offset)) = cursor_position {
 8278                            // The anchor tracks through the edit, then we add the offset
 8279                            let snapshot = self.buffer.read(cx).snapshot(cx);
 8280                            let base_offset = anchor.to_offset(&snapshot).0;
 8281                            let target_offset =
 8282                                MultiBufferOffset((base_offset + offset).min(snapshot.len().0));
 8283                            snapshot.anchor_after(target_offset)
 8284                        } else {
 8285                            fallback_cursor_target
 8286                        };
 8287
 8288                        self.change_selections(SelectionEffects::no_scroll(), window, cx, |s| {
 8289                            s.select_anchor_ranges([cursor_target..cursor_target]);
 8290                        });
 8291
 8292                        let selections = self.selections.disjoint_anchors_arc();
 8293                        if let Some(transaction_id_now) =
 8294                            self.buffer.read(cx).last_transaction_id(cx)
 8295                        {
 8296                            if transaction_id_prev != Some(transaction_id_now) {
 8297                                self.selection_history
 8298                                    .insert_transaction(transaction_id_now, selections);
 8299                            }
 8300                        }
 8301
 8302                        self.update_visible_edit_prediction(window, cx);
 8303                        if self.active_edit_prediction.is_none() {
 8304                            self.refresh_edit_prediction(true, true, window, cx);
 8305                        }
 8306                        cx.notify();
 8307                    }
 8308                    _ => {
 8309                        let snapshot = self.buffer.read(cx).snapshot(cx);
 8310                        let cursor_offset = self
 8311                            .selections
 8312                            .newest::<MultiBufferOffset>(&self.display_snapshot(cx))
 8313                            .head();
 8314
 8315                        let insertion = edits.iter().find_map(|(range, text)| {
 8316                            let range = range.to_offset(&snapshot);
 8317                            if range.is_empty() && range.start == cursor_offset {
 8318                                Some(text)
 8319                            } else {
 8320                                None
 8321                            }
 8322                        });
 8323
 8324                        if let Some(text) = insertion {
 8325                            let text_to_insert = match granularity {
 8326                                EditPredictionGranularity::Word => {
 8327                                    let mut partial = text
 8328                                        .chars()
 8329                                        .by_ref()
 8330                                        .take_while(|c| c.is_alphabetic())
 8331                                        .collect::<String>();
 8332                                    if partial.is_empty() {
 8333                                        partial = text
 8334                                            .chars()
 8335                                            .by_ref()
 8336                                            .take_while(|c| c.is_whitespace() || !c.is_alphabetic())
 8337                                            .collect::<String>();
 8338                                    }
 8339                                    partial
 8340                                }
 8341                                EditPredictionGranularity::Line => {
 8342                                    if let Some(line) = text.split_inclusive('\n').next() {
 8343                                        line.to_string()
 8344                                    } else {
 8345                                        text.to_string()
 8346                                    }
 8347                                }
 8348                                EditPredictionGranularity::Full => unreachable!(),
 8349                            };
 8350
 8351                            cx.emit(EditorEvent::InputHandled {
 8352                                utf16_range_to_replace: None,
 8353                                text: text_to_insert.clone().into(),
 8354                            });
 8355
 8356                            self.replace_selections(&text_to_insert, None, window, cx, false);
 8357                            self.refresh_edit_prediction(true, true, window, cx);
 8358                            cx.notify();
 8359                        } else {
 8360                            self.accept_partial_edit_prediction(
 8361                                EditPredictionGranularity::Full,
 8362                                window,
 8363                                cx,
 8364                            );
 8365                        }
 8366                    }
 8367                }
 8368            }
 8369        }
 8370    }
 8371
 8372    pub fn accept_next_word_edit_prediction(
 8373        &mut self,
 8374        _: &AcceptNextWordEditPrediction,
 8375        window: &mut Window,
 8376        cx: &mut Context<Self>,
 8377    ) {
 8378        self.accept_partial_edit_prediction(EditPredictionGranularity::Word, window, cx);
 8379    }
 8380
 8381    pub fn accept_next_line_edit_prediction(
 8382        &mut self,
 8383        _: &AcceptNextLineEditPrediction,
 8384        window: &mut Window,
 8385        cx: &mut Context<Self>,
 8386    ) {
 8387        self.accept_partial_edit_prediction(EditPredictionGranularity::Line, window, cx);
 8388    }
 8389
 8390    pub fn accept_edit_prediction(
 8391        &mut self,
 8392        _: &AcceptEditPrediction,
 8393        window: &mut Window,
 8394        cx: &mut Context<Self>,
 8395    ) {
 8396        self.accept_partial_edit_prediction(EditPredictionGranularity::Full, window, cx);
 8397    }
 8398
 8399    fn discard_edit_prediction(
 8400        &mut self,
 8401        reason: EditPredictionDiscardReason,
 8402        cx: &mut Context<Self>,
 8403    ) -> bool {
 8404        if reason == EditPredictionDiscardReason::Rejected {
 8405            let completion_id = self
 8406                .active_edit_prediction
 8407                .as_ref()
 8408                .and_then(|active_completion| active_completion.completion_id.clone());
 8409
 8410            self.report_edit_prediction_event(completion_id, false, cx);
 8411        }
 8412
 8413        if let Some(provider) = self.edit_prediction_provider() {
 8414            provider.discard(reason, cx);
 8415        }
 8416
 8417        self.take_active_edit_prediction(reason == EditPredictionDiscardReason::Ignored, cx)
 8418    }
 8419
 8420    fn report_edit_prediction_event(&self, id: Option<SharedString>, accepted: bool, cx: &App) {
 8421        let Some(provider) = self.edit_prediction_provider() else {
 8422            return;
 8423        };
 8424
 8425        let Some((_, buffer, _)) = self
 8426            .buffer
 8427            .read(cx)
 8428            .excerpt_containing(self.selections.newest_anchor().head(), cx)
 8429        else {
 8430            return;
 8431        };
 8432
 8433        let extension = buffer
 8434            .read(cx)
 8435            .file()
 8436            .and_then(|file| Some(file.path().extension()?.to_string()));
 8437
 8438        let event_type = match accepted {
 8439            true => "Edit Prediction Accepted",
 8440            false => "Edit Prediction Discarded",
 8441        };
 8442        telemetry::event!(
 8443            event_type,
 8444            provider = provider.name(),
 8445            prediction_id = id,
 8446            suggestion_accepted = accepted,
 8447            file_extension = extension,
 8448        );
 8449    }
 8450
 8451    fn open_editor_at_anchor(
 8452        snapshot: &language::BufferSnapshot,
 8453        target: language::Anchor,
 8454        workspace: &Entity<Workspace>,
 8455        window: &mut Window,
 8456        cx: &mut App,
 8457    ) -> Task<Result<()>> {
 8458        workspace.update(cx, |workspace, cx| {
 8459            let path = snapshot.file().map(|file| file.full_path(cx));
 8460            let Some(path) =
 8461                path.and_then(|path| workspace.project().read(cx).find_project_path(path, cx))
 8462            else {
 8463                return Task::ready(Err(anyhow::anyhow!("Project path not found")));
 8464            };
 8465            let target = text::ToPoint::to_point(&target, snapshot);
 8466            let item = workspace.open_path(path, None, true, window, cx);
 8467            window.spawn(cx, async move |cx| {
 8468                let Some(editor) = item.await?.downcast::<Editor>() else {
 8469                    return Ok(());
 8470                };
 8471                editor
 8472                    .update_in(cx, |editor, window, cx| {
 8473                        editor.go_to_singleton_buffer_point(target, window, cx);
 8474                    })
 8475                    .ok();
 8476                anyhow::Ok(())
 8477            })
 8478        })
 8479    }
 8480
 8481    pub fn has_active_edit_prediction(&self) -> bool {
 8482        self.active_edit_prediction.is_some()
 8483    }
 8484
 8485    fn take_active_edit_prediction(
 8486        &mut self,
 8487        preserve_stale_in_menu: bool,
 8488        cx: &mut Context<Self>,
 8489    ) -> bool {
 8490        let Some(active_edit_prediction) = self.active_edit_prediction.take() else {
 8491            if !preserve_stale_in_menu {
 8492                self.stale_edit_prediction_in_menu = None;
 8493            }
 8494            return false;
 8495        };
 8496
 8497        self.splice_inlays(&active_edit_prediction.inlay_ids, Default::default(), cx);
 8498        self.clear_highlights(HighlightKey::EditPredictionHighlight, cx);
 8499        self.stale_edit_prediction_in_menu =
 8500            preserve_stale_in_menu.then_some(active_edit_prediction);
 8501        true
 8502    }
 8503
 8504    /// Returns true when we're displaying the edit prediction popover below the cursor
 8505    /// like we are not previewing and the LSP autocomplete menu is visible
 8506    /// or we are in `when_holding_modifier` mode.
 8507    pub fn edit_prediction_visible_in_cursor_popover(&self, has_completion: bool) -> bool {
 8508        if self.edit_prediction_preview_is_active()
 8509            || !self.show_edit_predictions_in_menu()
 8510            || !self.edit_predictions_enabled()
 8511        {
 8512            return false;
 8513        }
 8514
 8515        if self.has_visible_completions_menu() {
 8516            return true;
 8517        }
 8518
 8519        has_completion && self.edit_prediction_requires_modifier()
 8520    }
 8521
 8522    fn handle_modifiers_changed(
 8523        &mut self,
 8524        modifiers: Modifiers,
 8525        position_map: &PositionMap,
 8526        window: &mut Window,
 8527        cx: &mut Context<Self>,
 8528    ) {
 8529        self.update_edit_prediction_settings(cx);
 8530
 8531        // Ensure that the edit prediction preview is updated, even when not
 8532        // enabled, if there's an active edit prediction preview.
 8533        if self.show_edit_predictions_in_menu()
 8534            || self.edit_prediction_requires_modifier()
 8535            || matches!(
 8536                self.edit_prediction_preview,
 8537                EditPredictionPreview::Active { .. }
 8538            )
 8539        {
 8540            self.update_edit_prediction_preview(&modifiers, window, cx);
 8541        }
 8542
 8543        self.update_selection_mode(&modifiers, position_map, window, cx);
 8544
 8545        let mouse_position = window.mouse_position();
 8546        if !position_map.text_hitbox.is_hovered(window) {
 8547            return;
 8548        }
 8549
 8550        self.update_hovered_link(
 8551            position_map.point_for_position(mouse_position),
 8552            Some(mouse_position),
 8553            &position_map.snapshot,
 8554            modifiers,
 8555            window,
 8556            cx,
 8557        )
 8558    }
 8559
 8560    fn is_cmd_or_ctrl_pressed(modifiers: &Modifiers, cx: &mut Context<Self>) -> bool {
 8561        match EditorSettings::get_global(cx).multi_cursor_modifier {
 8562            MultiCursorModifier::Alt => modifiers.secondary(),
 8563            MultiCursorModifier::CmdOrCtrl => modifiers.alt,
 8564        }
 8565    }
 8566
 8567    fn is_alt_pressed(modifiers: &Modifiers, cx: &mut Context<Self>) -> bool {
 8568        match EditorSettings::get_global(cx).multi_cursor_modifier {
 8569            MultiCursorModifier::Alt => modifiers.alt,
 8570            MultiCursorModifier::CmdOrCtrl => modifiers.secondary(),
 8571        }
 8572    }
 8573
 8574    fn columnar_selection_mode(
 8575        modifiers: &Modifiers,
 8576        cx: &mut Context<Self>,
 8577    ) -> Option<ColumnarMode> {
 8578        if modifiers.shift && modifiers.number_of_modifiers() == 2 {
 8579            if Self::is_cmd_or_ctrl_pressed(modifiers, cx) {
 8580                Some(ColumnarMode::FromMouse)
 8581            } else if Self::is_alt_pressed(modifiers, cx) {
 8582                Some(ColumnarMode::FromSelection)
 8583            } else {
 8584                None
 8585            }
 8586        } else {
 8587            None
 8588        }
 8589    }
 8590
 8591    fn update_selection_mode(
 8592        &mut self,
 8593        modifiers: &Modifiers,
 8594        position_map: &PositionMap,
 8595        window: &mut Window,
 8596        cx: &mut Context<Self>,
 8597    ) {
 8598        let Some(mode) = Self::columnar_selection_mode(modifiers, cx) else {
 8599            return;
 8600        };
 8601        if self.selections.pending_anchor().is_none() {
 8602            return;
 8603        }
 8604
 8605        let mouse_position = window.mouse_position();
 8606        let point_for_position = position_map.point_for_position(mouse_position);
 8607        let position = point_for_position.previous_valid;
 8608
 8609        self.select(
 8610            SelectPhase::BeginColumnar {
 8611                position,
 8612                reset: false,
 8613                mode,
 8614                goal_column: point_for_position.exact_unclipped.column(),
 8615            },
 8616            window,
 8617            cx,
 8618        );
 8619    }
 8620
 8621    fn update_edit_prediction_preview(
 8622        &mut self,
 8623        modifiers: &Modifiers,
 8624        window: &mut Window,
 8625        cx: &mut Context<Self>,
 8626    ) {
 8627        let modifiers_held = self.edit_prediction_preview_modifiers_held(modifiers, window, cx);
 8628
 8629        if modifiers_held {
 8630            if matches!(
 8631                self.edit_prediction_preview,
 8632                EditPredictionPreview::Inactive { .. }
 8633            ) {
 8634                self.edit_prediction_preview = EditPredictionPreview::Active {
 8635                    previous_scroll_position: None,
 8636                    since: Instant::now(),
 8637                };
 8638
 8639                self.update_visible_edit_prediction(window, cx);
 8640                cx.notify();
 8641            }
 8642        } else if let EditPredictionPreview::Active {
 8643            previous_scroll_position,
 8644            since,
 8645        } = self.edit_prediction_preview
 8646        {
 8647            if let (Some(previous_scroll_position), Some(position_map)) =
 8648                (previous_scroll_position, self.last_position_map.as_ref())
 8649            {
 8650                self.set_scroll_position(
 8651                    previous_scroll_position
 8652                        .scroll_position(&position_map.snapshot.display_snapshot),
 8653                    window,
 8654                    cx,
 8655                );
 8656            }
 8657
 8658            self.edit_prediction_preview = EditPredictionPreview::Inactive {
 8659                released_too_fast: since.elapsed() < Duration::from_millis(200),
 8660            };
 8661            self.clear_row_highlights::<EditPredictionPreview>();
 8662            self.update_visible_edit_prediction(window, cx);
 8663            cx.notify();
 8664        }
 8665    }
 8666
 8667    fn update_visible_edit_prediction(
 8668        &mut self,
 8669        _window: &mut Window,
 8670        cx: &mut Context<Self>,
 8671    ) -> Option<()> {
 8672        if self.ime_transaction.is_some() {
 8673            self.discard_edit_prediction(EditPredictionDiscardReason::Ignored, cx);
 8674            return None;
 8675        }
 8676
 8677        let selection = self.selections.newest_anchor();
 8678        let cursor = selection.head();
 8679        let multibuffer = self.buffer.read(cx).snapshot(cx);
 8680
 8681        // Check project-level disable_ai setting for the current buffer
 8682        if let Some((buffer, _)) = self.buffer.read(cx).text_anchor_for_position(cursor, cx) {
 8683            if DisableAiSettings::is_ai_disabled_for_buffer(Some(&buffer), cx) {
 8684                return None;
 8685            }
 8686        }
 8687        let offset_selection = selection.map(|endpoint| endpoint.to_offset(&multibuffer));
 8688        let excerpt_id = cursor.excerpt_id;
 8689
 8690        let show_in_menu = self.show_edit_predictions_in_menu();
 8691        let completions_menu_has_precedence = !show_in_menu
 8692            && (self.context_menu.borrow().is_some()
 8693                || (!self.completion_tasks.is_empty() && !self.has_active_edit_prediction()));
 8694
 8695        if completions_menu_has_precedence
 8696            || !offset_selection.is_empty()
 8697            || self
 8698                .active_edit_prediction
 8699                .as_ref()
 8700                .is_some_and(|completion| {
 8701                    let Some(invalidation_range) = completion.invalidation_range.as_ref() else {
 8702                        return false;
 8703                    };
 8704                    let invalidation_range = invalidation_range.to_offset(&multibuffer);
 8705                    let invalidation_range = invalidation_range.start..=invalidation_range.end;
 8706                    !invalidation_range.contains(&offset_selection.head())
 8707                })
 8708        {
 8709            self.discard_edit_prediction(EditPredictionDiscardReason::Ignored, cx);
 8710            return None;
 8711        }
 8712
 8713        self.take_active_edit_prediction(true, cx);
 8714        let Some(provider) = self.edit_prediction_provider() else {
 8715            self.edit_prediction_settings = EditPredictionSettings::Disabled;
 8716            return None;
 8717        };
 8718
 8719        let (buffer, cursor_buffer_position) =
 8720            self.buffer.read(cx).text_anchor_for_position(cursor, cx)?;
 8721
 8722        self.edit_prediction_settings =
 8723            self.edit_prediction_settings_at_position(&buffer, cursor_buffer_position, cx);
 8724
 8725        self.in_leading_whitespace = multibuffer.is_line_whitespace_upto(cursor);
 8726
 8727        if self.in_leading_whitespace {
 8728            let cursor_point = cursor.to_point(&multibuffer);
 8729            let mut suggested_indent = None;
 8730            multibuffer.suggested_indents_callback(
 8731                cursor_point.row..cursor_point.row + 1,
 8732                &mut |_, indent| {
 8733                    suggested_indent = Some(indent);
 8734                    ControlFlow::Break(())
 8735                },
 8736                cx,
 8737            );
 8738
 8739            if let Some(indent) = suggested_indent
 8740                && indent.len == cursor_point.column
 8741            {
 8742                self.in_leading_whitespace = false;
 8743            }
 8744        }
 8745
 8746        let edit_prediction = provider.suggest(&buffer, cursor_buffer_position, cx)?;
 8747
 8748        let (completion_id, edits, predicted_cursor_position, edit_preview) = match edit_prediction
 8749        {
 8750            edit_prediction_types::EditPrediction::Local {
 8751                id,
 8752                edits,
 8753                cursor_position,
 8754                edit_preview,
 8755            } => (id, edits, cursor_position, edit_preview),
 8756            edit_prediction_types::EditPrediction::Jump {
 8757                id,
 8758                snapshot,
 8759                target,
 8760            } => {
 8761                if let Some(provider) = &self.edit_prediction_provider {
 8762                    provider.provider.did_show(SuggestionDisplayType::Jump, cx);
 8763                }
 8764                self.stale_edit_prediction_in_menu = None;
 8765                self.active_edit_prediction = Some(EditPredictionState {
 8766                    inlay_ids: vec![],
 8767                    completion: EditPrediction::MoveOutside { snapshot, target },
 8768                    completion_id: id,
 8769                    invalidation_range: None,
 8770                });
 8771                cx.notify();
 8772                return Some(());
 8773            }
 8774        };
 8775
 8776        let edits = edits
 8777            .into_iter()
 8778            .flat_map(|(range, new_text)| {
 8779                Some((
 8780                    multibuffer.anchor_range_in_excerpt(excerpt_id, range)?,
 8781                    new_text,
 8782                ))
 8783            })
 8784            .collect::<Vec<_>>();
 8785        if edits.is_empty() {
 8786            return None;
 8787        }
 8788
 8789        let cursor_position = predicted_cursor_position.and_then(|predicted| {
 8790            let anchor = multibuffer.anchor_in_excerpt(excerpt_id, predicted.anchor)?;
 8791            Some((anchor, predicted.offset))
 8792        });
 8793
 8794        let first_edit_start = edits.first().unwrap().0.start;
 8795        let first_edit_start_point = first_edit_start.to_point(&multibuffer);
 8796        let edit_start_row = first_edit_start_point.row.saturating_sub(2);
 8797
 8798        let last_edit_end = edits.last().unwrap().0.end;
 8799        let last_edit_end_point = last_edit_end.to_point(&multibuffer);
 8800        let edit_end_row = cmp::min(multibuffer.max_point().row, last_edit_end_point.row + 2);
 8801
 8802        let cursor_row = cursor.to_point(&multibuffer).row;
 8803
 8804        let snapshot = multibuffer.buffer_for_excerpt(excerpt_id).cloned()?;
 8805
 8806        let mut inlay_ids = Vec::new();
 8807        let invalidation_row_range;
 8808        let move_invalidation_row_range = if cursor_row < edit_start_row {
 8809            Some(cursor_row..edit_end_row)
 8810        } else if cursor_row > edit_end_row {
 8811            Some(edit_start_row..cursor_row)
 8812        } else {
 8813            None
 8814        };
 8815        let supports_jump = self
 8816            .edit_prediction_provider
 8817            .as_ref()
 8818            .map(|provider| provider.provider.supports_jump_to_edit())
 8819            .unwrap_or(true);
 8820
 8821        let is_move = supports_jump
 8822            && (move_invalidation_row_range.is_some() || self.edit_predictions_hidden_for_vim_mode);
 8823        let completion = if is_move {
 8824            if let Some(provider) = &self.edit_prediction_provider {
 8825                provider.provider.did_show(SuggestionDisplayType::Jump, cx);
 8826            }
 8827            invalidation_row_range =
 8828                move_invalidation_row_range.unwrap_or(edit_start_row..edit_end_row);
 8829            let target = first_edit_start;
 8830            EditPrediction::MoveWithin { target, snapshot }
 8831        } else {
 8832            let show_completions_in_menu = self.has_visible_completions_menu();
 8833            let show_completions_in_buffer = !self.edit_prediction_visible_in_cursor_popover(true)
 8834                && !self.edit_predictions_hidden_for_vim_mode;
 8835
 8836            let display_mode = if all_edits_insertions_or_deletions(&edits, &multibuffer) {
 8837                if provider.show_tab_accept_marker() {
 8838                    EditDisplayMode::TabAccept
 8839                } else {
 8840                    EditDisplayMode::Inline
 8841                }
 8842            } else {
 8843                EditDisplayMode::DiffPopover
 8844            };
 8845
 8846            let report_shown = match display_mode {
 8847                EditDisplayMode::DiffPopover | EditDisplayMode::Inline => {
 8848                    show_completions_in_buffer || show_completions_in_menu
 8849                }
 8850                EditDisplayMode::TabAccept => {
 8851                    show_completions_in_menu || self.edit_prediction_preview_is_active()
 8852                }
 8853            };
 8854
 8855            if report_shown && let Some(provider) = &self.edit_prediction_provider {
 8856                let suggestion_display_type = match display_mode {
 8857                    EditDisplayMode::DiffPopover => SuggestionDisplayType::DiffPopover,
 8858                    EditDisplayMode::Inline | EditDisplayMode::TabAccept => {
 8859                        SuggestionDisplayType::GhostText
 8860                    }
 8861                };
 8862                provider.provider.did_show(suggestion_display_type, cx);
 8863            }
 8864
 8865            if show_completions_in_buffer {
 8866                if edits
 8867                    .iter()
 8868                    .all(|(range, _)| range.to_offset(&multibuffer).is_empty())
 8869                {
 8870                    let mut inlays = Vec::new();
 8871                    for (range, new_text) in &edits {
 8872                        let inlay = Inlay::edit_prediction(
 8873                            post_inc(&mut self.next_inlay_id),
 8874                            range.start,
 8875                            new_text.as_ref(),
 8876                        );
 8877                        inlay_ids.push(inlay.id);
 8878                        inlays.push(inlay);
 8879                    }
 8880
 8881                    self.splice_inlays(&[], inlays, cx);
 8882                } else {
 8883                    let background_color = cx.theme().status().deleted_background;
 8884                    self.highlight_text(
 8885                        HighlightKey::EditPredictionHighlight,
 8886                        edits.iter().map(|(range, _)| range.clone()).collect(),
 8887                        HighlightStyle {
 8888                            background_color: Some(background_color),
 8889                            ..Default::default()
 8890                        },
 8891                        cx,
 8892                    );
 8893                }
 8894            }
 8895
 8896            invalidation_row_range = edit_start_row..edit_end_row;
 8897
 8898            EditPrediction::Edit {
 8899                edits,
 8900                cursor_position,
 8901                edit_preview,
 8902                display_mode,
 8903                snapshot,
 8904            }
 8905        };
 8906
 8907        let invalidation_range = multibuffer
 8908            .anchor_before(Point::new(invalidation_row_range.start, 0))
 8909            ..multibuffer.anchor_after(Point::new(
 8910                invalidation_row_range.end,
 8911                multibuffer.line_len(MultiBufferRow(invalidation_row_range.end)),
 8912            ));
 8913
 8914        self.stale_edit_prediction_in_menu = None;
 8915        self.active_edit_prediction = Some(EditPredictionState {
 8916            inlay_ids,
 8917            completion,
 8918            completion_id,
 8919            invalidation_range: Some(invalidation_range),
 8920        });
 8921
 8922        cx.notify();
 8923
 8924        Some(())
 8925    }
 8926
 8927    pub fn edit_prediction_provider(&self) -> Option<Arc<dyn EditPredictionDelegateHandle>> {
 8928        Some(self.edit_prediction_provider.as_ref()?.provider.clone())
 8929    }
 8930
 8931    /// Get all display points of breakpoints that will be rendered within editor
 8932    ///
 8933    /// This function is used to handle overlaps between breakpoints and Code action/runner symbol.
 8934    /// It's also used to set the color of line numbers with breakpoints to the breakpoint color.
 8935    /// TODO debugger: Use this function to color toggle symbols that house nested breakpoints
 8936    fn active_breakpoints(
 8937        &self,
 8938        range: Range<DisplayRow>,
 8939        window: &mut Window,
 8940        cx: &mut Context<Self>,
 8941    ) -> HashMap<DisplayRow, (Anchor, Breakpoint, Option<BreakpointSessionState>)> {
 8942        let mut breakpoint_display_points = HashMap::default();
 8943
 8944        let Some(breakpoint_store) = self.breakpoint_store.clone() else {
 8945            return breakpoint_display_points;
 8946        };
 8947
 8948        let snapshot = self.snapshot(window, cx);
 8949
 8950        let multi_buffer_snapshot = snapshot.buffer_snapshot();
 8951        let Some(project) = self.project() else {
 8952            return breakpoint_display_points;
 8953        };
 8954
 8955        let range = snapshot.display_point_to_point(DisplayPoint::new(range.start, 0), Bias::Left)
 8956            ..snapshot.display_point_to_point(DisplayPoint::new(range.end, 0), Bias::Right);
 8957
 8958        for (buffer_snapshot, range, excerpt_id) in
 8959            multi_buffer_snapshot.range_to_buffer_ranges(range.start..=range.end)
 8960        {
 8961            let Some(buffer) = project
 8962                .read(cx)
 8963                .buffer_for_id(buffer_snapshot.remote_id(), cx)
 8964            else {
 8965                continue;
 8966            };
 8967            let breakpoints = breakpoint_store.read(cx).breakpoints(
 8968                &buffer,
 8969                Some(
 8970                    buffer_snapshot.anchor_before(range.start)
 8971                        ..buffer_snapshot.anchor_after(range.end),
 8972                ),
 8973                buffer_snapshot,
 8974                cx,
 8975            );
 8976            for (breakpoint, state) in breakpoints {
 8977                let multi_buffer_anchor = Anchor::in_buffer(excerpt_id, breakpoint.position);
 8978                let position = multi_buffer_anchor
 8979                    .to_point(&multi_buffer_snapshot)
 8980                    .to_display_point(&snapshot);
 8981
 8982                breakpoint_display_points.insert(
 8983                    position.row(),
 8984                    (multi_buffer_anchor, breakpoint.bp.clone(), state),
 8985                );
 8986            }
 8987        }
 8988
 8989        breakpoint_display_points
 8990    }
 8991
 8992    fn breakpoint_context_menu(
 8993        &self,
 8994        anchor: Anchor,
 8995        window: &mut Window,
 8996        cx: &mut Context<Self>,
 8997    ) -> Entity<ui::ContextMenu> {
 8998        let weak_editor = cx.weak_entity();
 8999        let focus_handle = self.focus_handle(cx);
 9000
 9001        let row = self
 9002            .buffer
 9003            .read(cx)
 9004            .snapshot(cx)
 9005            .summary_for_anchor::<Point>(&anchor)
 9006            .row;
 9007
 9008        let breakpoint = self
 9009            .breakpoint_at_row(row, window, cx)
 9010            .map(|(anchor, bp)| (anchor, Arc::from(bp)));
 9011
 9012        let log_breakpoint_msg = if breakpoint.as_ref().is_some_and(|bp| bp.1.message.is_some()) {
 9013            "Edit Log Breakpoint"
 9014        } else {
 9015            "Set Log Breakpoint"
 9016        };
 9017
 9018        let condition_breakpoint_msg = if breakpoint
 9019            .as_ref()
 9020            .is_some_and(|bp| bp.1.condition.is_some())
 9021        {
 9022            "Edit Condition Breakpoint"
 9023        } else {
 9024            "Set Condition Breakpoint"
 9025        };
 9026
 9027        let hit_condition_breakpoint_msg = if breakpoint
 9028            .as_ref()
 9029            .is_some_and(|bp| bp.1.hit_condition.is_some())
 9030        {
 9031            "Edit Hit Condition Breakpoint"
 9032        } else {
 9033            "Set Hit Condition Breakpoint"
 9034        };
 9035
 9036        let set_breakpoint_msg = if breakpoint.as_ref().is_some() {
 9037            "Unset Breakpoint"
 9038        } else {
 9039            "Set Breakpoint"
 9040        };
 9041
 9042        let run_to_cursor = window.is_action_available(&RunToCursor, cx);
 9043
 9044        let toggle_state_msg = breakpoint.as_ref().map_or(None, |bp| match bp.1.state {
 9045            BreakpointState::Enabled => Some("Disable"),
 9046            BreakpointState::Disabled => Some("Enable"),
 9047        });
 9048
 9049        let (anchor, breakpoint) =
 9050            breakpoint.unwrap_or_else(|| (anchor, Arc::new(Breakpoint::new_standard())));
 9051
 9052        ui::ContextMenu::build(window, cx, |menu, _, _cx| {
 9053            menu.on_blur_subscription(Subscription::new(|| {}))
 9054                .context(focus_handle)
 9055                .when(run_to_cursor, |this| {
 9056                    let weak_editor = weak_editor.clone();
 9057                    this.entry("Run to Cursor", None, move |window, cx| {
 9058                        weak_editor
 9059                            .update(cx, |editor, cx| {
 9060                                editor.change_selections(
 9061                                    SelectionEffects::no_scroll(),
 9062                                    window,
 9063                                    cx,
 9064                                    |s| s.select_ranges([Point::new(row, 0)..Point::new(row, 0)]),
 9065                                );
 9066                            })
 9067                            .ok();
 9068
 9069                        window.dispatch_action(Box::new(RunToCursor), cx);
 9070                    })
 9071                    .separator()
 9072                })
 9073                .when_some(toggle_state_msg, |this, msg| {
 9074                    this.entry(msg, None, {
 9075                        let weak_editor = weak_editor.clone();
 9076                        let breakpoint = breakpoint.clone();
 9077                        move |_window, cx| {
 9078                            weak_editor
 9079                                .update(cx, |this, cx| {
 9080                                    this.edit_breakpoint_at_anchor(
 9081                                        anchor,
 9082                                        breakpoint.as_ref().clone(),
 9083                                        BreakpointEditAction::InvertState,
 9084                                        cx,
 9085                                    );
 9086                                })
 9087                                .log_err();
 9088                        }
 9089                    })
 9090                })
 9091                .entry(set_breakpoint_msg, None, {
 9092                    let weak_editor = weak_editor.clone();
 9093                    let breakpoint = breakpoint.clone();
 9094                    move |_window, cx| {
 9095                        weak_editor
 9096                            .update(cx, |this, cx| {
 9097                                this.edit_breakpoint_at_anchor(
 9098                                    anchor,
 9099                                    breakpoint.as_ref().clone(),
 9100                                    BreakpointEditAction::Toggle,
 9101                                    cx,
 9102                                );
 9103                            })
 9104                            .log_err();
 9105                    }
 9106                })
 9107                .entry(log_breakpoint_msg, None, {
 9108                    let breakpoint = breakpoint.clone();
 9109                    let weak_editor = weak_editor.clone();
 9110                    move |window, cx| {
 9111                        weak_editor
 9112                            .update(cx, |this, cx| {
 9113                                this.add_edit_breakpoint_block(
 9114                                    anchor,
 9115                                    breakpoint.as_ref(),
 9116                                    BreakpointPromptEditAction::Log,
 9117                                    window,
 9118                                    cx,
 9119                                );
 9120                            })
 9121                            .log_err();
 9122                    }
 9123                })
 9124                .entry(condition_breakpoint_msg, None, {
 9125                    let breakpoint = breakpoint.clone();
 9126                    let weak_editor = weak_editor.clone();
 9127                    move |window, cx| {
 9128                        weak_editor
 9129                            .update(cx, |this, cx| {
 9130                                this.add_edit_breakpoint_block(
 9131                                    anchor,
 9132                                    breakpoint.as_ref(),
 9133                                    BreakpointPromptEditAction::Condition,
 9134                                    window,
 9135                                    cx,
 9136                                );
 9137                            })
 9138                            .log_err();
 9139                    }
 9140                })
 9141                .entry(hit_condition_breakpoint_msg, None, move |window, cx| {
 9142                    weak_editor
 9143                        .update(cx, |this, cx| {
 9144                            this.add_edit_breakpoint_block(
 9145                                anchor,
 9146                                breakpoint.as_ref(),
 9147                                BreakpointPromptEditAction::HitCondition,
 9148                                window,
 9149                                cx,
 9150                            );
 9151                        })
 9152                        .log_err();
 9153                })
 9154        })
 9155    }
 9156
 9157    fn render_breakpoint(
 9158        &self,
 9159        position: Anchor,
 9160        row: DisplayRow,
 9161        breakpoint: &Breakpoint,
 9162        state: Option<BreakpointSessionState>,
 9163        cx: &mut Context<Self>,
 9164    ) -> IconButton {
 9165        let is_rejected = state.is_some_and(|s| !s.verified);
 9166        // Is it a breakpoint that shows up when hovering over gutter?
 9167        let (is_phantom, collides_with_existing) = self.gutter_breakpoint_indicator.0.map_or(
 9168            (false, false),
 9169            |PhantomBreakpointIndicator {
 9170                 is_active,
 9171                 display_row,
 9172                 collides_with_existing_breakpoint,
 9173             }| {
 9174                (
 9175                    is_active && display_row == row,
 9176                    collides_with_existing_breakpoint,
 9177                )
 9178            },
 9179        );
 9180
 9181        let (color, icon) = {
 9182            let icon = match (&breakpoint.message.is_some(), breakpoint.is_disabled()) {
 9183                (false, false) => ui::IconName::DebugBreakpoint,
 9184                (true, false) => ui::IconName::DebugLogBreakpoint,
 9185                (false, true) => ui::IconName::DebugDisabledBreakpoint,
 9186                (true, true) => ui::IconName::DebugDisabledLogBreakpoint,
 9187            };
 9188
 9189            let theme_colors = cx.theme().colors();
 9190
 9191            let color = if is_phantom {
 9192                if collides_with_existing {
 9193                    Color::Custom(
 9194                        theme_colors
 9195                            .debugger_accent
 9196                            .blend(theme_colors.text.opacity(0.6)),
 9197                    )
 9198                } else {
 9199                    Color::Hint
 9200                }
 9201            } else if is_rejected {
 9202                Color::Disabled
 9203            } else {
 9204                Color::Debugger
 9205            };
 9206
 9207            (color, icon)
 9208        };
 9209
 9210        let breakpoint = Arc::from(breakpoint.clone());
 9211
 9212        let alt_as_text = gpui::Keystroke {
 9213            modifiers: Modifiers::secondary_key(),
 9214            ..Default::default()
 9215        };
 9216        let primary_action_text = if breakpoint.is_disabled() {
 9217            "Enable breakpoint"
 9218        } else if is_phantom && !collides_with_existing {
 9219            "Set breakpoint"
 9220        } else {
 9221            "Unset breakpoint"
 9222        };
 9223        let focus_handle = self.focus_handle.clone();
 9224
 9225        let meta = if is_rejected {
 9226            SharedString::from("No executable code is associated with this line.")
 9227        } else if collides_with_existing && !breakpoint.is_disabled() {
 9228            SharedString::from(format!(
 9229                "{alt_as_text}-click to disable,\nright-click for more options."
 9230            ))
 9231        } else {
 9232            SharedString::from("Right-click for more options.")
 9233        };
 9234        IconButton::new(("breakpoint_indicator", row.0 as usize), icon)
 9235            .icon_size(IconSize::XSmall)
 9236            .size(ui::ButtonSize::None)
 9237            .when(is_rejected, |this| {
 9238                this.indicator(Indicator::icon(Icon::new(IconName::Warning)).color(Color::Warning))
 9239            })
 9240            .icon_color(color)
 9241            .style(ButtonStyle::Transparent)
 9242            .on_click(cx.listener({
 9243                move |editor, event: &ClickEvent, window, cx| {
 9244                    let edit_action = if event.modifiers().platform || breakpoint.is_disabled() {
 9245                        BreakpointEditAction::InvertState
 9246                    } else {
 9247                        BreakpointEditAction::Toggle
 9248                    };
 9249
 9250                    window.focus(&editor.focus_handle(cx), cx);
 9251                    editor.update_breakpoint_collision_on_toggle(row, &edit_action);
 9252                    editor.edit_breakpoint_at_anchor(
 9253                        position,
 9254                        breakpoint.as_ref().clone(),
 9255                        edit_action,
 9256                        cx,
 9257                    );
 9258                }
 9259            }))
 9260            .on_right_click(cx.listener(move |editor, event: &ClickEvent, window, cx| {
 9261                editor.set_breakpoint_context_menu(
 9262                    row,
 9263                    Some(position),
 9264                    event.position(),
 9265                    window,
 9266                    cx,
 9267                );
 9268            }))
 9269            .tooltip(move |_window, cx| {
 9270                Tooltip::with_meta_in(
 9271                    primary_action_text,
 9272                    Some(&ToggleBreakpoint),
 9273                    meta.clone(),
 9274                    &focus_handle,
 9275                    cx,
 9276                )
 9277            })
 9278    }
 9279
 9280    fn build_tasks_context(
 9281        project: &Entity<Project>,
 9282        buffer: &Entity<Buffer>,
 9283        buffer_row: u32,
 9284        tasks: &Arc<RunnableTasks>,
 9285        cx: &mut Context<Self>,
 9286    ) -> Task<Option<task::TaskContext>> {
 9287        let position = Point::new(buffer_row, tasks.column);
 9288        let range_start = buffer.read(cx).anchor_at(position, Bias::Right);
 9289        let location = Location {
 9290            buffer: buffer.clone(),
 9291            range: range_start..range_start,
 9292        };
 9293        // Fill in the environmental variables from the tree-sitter captures
 9294        let mut captured_task_variables = TaskVariables::default();
 9295        for (capture_name, value) in tasks.extra_variables.clone() {
 9296            captured_task_variables.insert(
 9297                task::VariableName::Custom(capture_name.into()),
 9298                value.clone(),
 9299            );
 9300        }
 9301        project.update(cx, |project, cx| {
 9302            project.task_store().update(cx, |task_store, cx| {
 9303                task_store.task_context_for_location(captured_task_variables, location, cx)
 9304            })
 9305        })
 9306    }
 9307
 9308    pub fn context_menu_visible(&self) -> bool {
 9309        !self.edit_prediction_preview_is_active()
 9310            && self
 9311                .context_menu
 9312                .borrow()
 9313                .as_ref()
 9314                .is_some_and(|menu| menu.visible())
 9315    }
 9316
 9317    pub fn context_menu_origin(&self) -> Option<ContextMenuOrigin> {
 9318        self.context_menu
 9319            .borrow()
 9320            .as_ref()
 9321            .map(|menu| menu.origin())
 9322    }
 9323
 9324    pub fn set_context_menu_options(&mut self, options: ContextMenuOptions) {
 9325        self.context_menu_options = Some(options);
 9326    }
 9327
 9328    const EDIT_PREDICTION_POPOVER_PADDING_X: Pixels = px(24.);
 9329    const EDIT_PREDICTION_POPOVER_PADDING_Y: Pixels = px(2.);
 9330
 9331    fn render_edit_prediction_popover(
 9332        &mut self,
 9333        text_bounds: &Bounds<Pixels>,
 9334        content_origin: gpui::Point<Pixels>,
 9335        right_margin: Pixels,
 9336        editor_snapshot: &EditorSnapshot,
 9337        visible_row_range: Range<DisplayRow>,
 9338        scroll_top: ScrollOffset,
 9339        scroll_bottom: ScrollOffset,
 9340        line_layouts: &[LineWithInvisibles],
 9341        line_height: Pixels,
 9342        scroll_position: gpui::Point<ScrollOffset>,
 9343        scroll_pixel_position: gpui::Point<ScrollPixelOffset>,
 9344        newest_selection_head: Option<DisplayPoint>,
 9345        editor_width: Pixels,
 9346        style: &EditorStyle,
 9347        window: &mut Window,
 9348        cx: &mut App,
 9349    ) -> Option<(AnyElement, gpui::Point<Pixels>)> {
 9350        if self.mode().is_minimap() {
 9351            return None;
 9352        }
 9353        let active_edit_prediction = self.active_edit_prediction.as_ref()?;
 9354
 9355        if self.edit_prediction_visible_in_cursor_popover(true) {
 9356            return None;
 9357        }
 9358
 9359        match &active_edit_prediction.completion {
 9360            EditPrediction::MoveWithin { target, .. } => {
 9361                let target_display_point = target.to_display_point(editor_snapshot);
 9362
 9363                if self.edit_prediction_requires_modifier() {
 9364                    if !self.edit_prediction_preview_is_active() {
 9365                        return None;
 9366                    }
 9367
 9368                    self.render_edit_prediction_modifier_jump_popover(
 9369                        text_bounds,
 9370                        content_origin,
 9371                        visible_row_range,
 9372                        line_layouts,
 9373                        line_height,
 9374                        scroll_pixel_position,
 9375                        newest_selection_head,
 9376                        target_display_point,
 9377                        window,
 9378                        cx,
 9379                    )
 9380                } else {
 9381                    self.render_edit_prediction_eager_jump_popover(
 9382                        text_bounds,
 9383                        content_origin,
 9384                        editor_snapshot,
 9385                        visible_row_range,
 9386                        scroll_top,
 9387                        scroll_bottom,
 9388                        line_height,
 9389                        scroll_pixel_position,
 9390                        target_display_point,
 9391                        editor_width,
 9392                        window,
 9393                        cx,
 9394                    )
 9395                }
 9396            }
 9397            EditPrediction::Edit {
 9398                display_mode: EditDisplayMode::Inline,
 9399                ..
 9400            } => None,
 9401            EditPrediction::Edit {
 9402                display_mode: EditDisplayMode::TabAccept,
 9403                edits,
 9404                ..
 9405            } => {
 9406                let range = &edits.first()?.0;
 9407                let target_display_point = range.end.to_display_point(editor_snapshot);
 9408
 9409                self.render_edit_prediction_end_of_line_popover(
 9410                    "Accept",
 9411                    editor_snapshot,
 9412                    visible_row_range,
 9413                    target_display_point,
 9414                    line_height,
 9415                    scroll_pixel_position,
 9416                    content_origin,
 9417                    editor_width,
 9418                    window,
 9419                    cx,
 9420                )
 9421            }
 9422            EditPrediction::Edit {
 9423                edits,
 9424                edit_preview,
 9425                display_mode: EditDisplayMode::DiffPopover,
 9426                snapshot,
 9427                ..
 9428            } => self.render_edit_prediction_diff_popover(
 9429                text_bounds,
 9430                content_origin,
 9431                right_margin,
 9432                editor_snapshot,
 9433                visible_row_range,
 9434                line_layouts,
 9435                line_height,
 9436                scroll_position,
 9437                scroll_pixel_position,
 9438                newest_selection_head,
 9439                editor_width,
 9440                style,
 9441                edits,
 9442                edit_preview,
 9443                snapshot,
 9444                window,
 9445                cx,
 9446            ),
 9447            EditPrediction::MoveOutside { snapshot, .. } => {
 9448                let mut element = self
 9449                    .render_edit_prediction_jump_outside_popover(snapshot, window, cx)
 9450                    .into_any();
 9451
 9452                let size = element.layout_as_root(AvailableSpace::min_size(), window, cx);
 9453                let origin_x = text_bounds.size.width - size.width - px(30.);
 9454                let origin = text_bounds.origin + gpui::Point::new(origin_x, px(16.));
 9455                element.prepaint_at(origin, window, cx);
 9456
 9457                Some((element, origin))
 9458            }
 9459        }
 9460    }
 9461
 9462    fn render_edit_prediction_modifier_jump_popover(
 9463        &mut self,
 9464        text_bounds: &Bounds<Pixels>,
 9465        content_origin: gpui::Point<Pixels>,
 9466        visible_row_range: Range<DisplayRow>,
 9467        line_layouts: &[LineWithInvisibles],
 9468        line_height: Pixels,
 9469        scroll_pixel_position: gpui::Point<ScrollPixelOffset>,
 9470        newest_selection_head: Option<DisplayPoint>,
 9471        target_display_point: DisplayPoint,
 9472        window: &mut Window,
 9473        cx: &mut App,
 9474    ) -> Option<(AnyElement, gpui::Point<Pixels>)> {
 9475        let scrolled_content_origin =
 9476            content_origin - gpui::Point::new(scroll_pixel_position.x.into(), Pixels::ZERO);
 9477
 9478        const SCROLL_PADDING_Y: Pixels = px(12.);
 9479
 9480        if target_display_point.row() < visible_row_range.start {
 9481            return self.render_edit_prediction_scroll_popover(
 9482                &|_| SCROLL_PADDING_Y,
 9483                IconName::ArrowUp,
 9484                visible_row_range,
 9485                line_layouts,
 9486                newest_selection_head,
 9487                scrolled_content_origin,
 9488                window,
 9489                cx,
 9490            );
 9491        } else if target_display_point.row() >= visible_row_range.end {
 9492            return self.render_edit_prediction_scroll_popover(
 9493                &|size| text_bounds.size.height - size.height - SCROLL_PADDING_Y,
 9494                IconName::ArrowDown,
 9495                visible_row_range,
 9496                line_layouts,
 9497                newest_selection_head,
 9498                scrolled_content_origin,
 9499                window,
 9500                cx,
 9501            );
 9502        }
 9503
 9504        const POLE_WIDTH: Pixels = px(2.);
 9505
 9506        let line_layout =
 9507            line_layouts.get(target_display_point.row().minus(visible_row_range.start) as usize)?;
 9508        let target_column = target_display_point.column() as usize;
 9509
 9510        let target_x = line_layout.x_for_index(target_column);
 9511        let target_y = (target_display_point.row().as_f64() * f64::from(line_height))
 9512            - scroll_pixel_position.y;
 9513
 9514        let flag_on_right = target_x < text_bounds.size.width / 2.;
 9515
 9516        let mut border_color = Self::edit_prediction_callout_popover_border_color(cx);
 9517        border_color.l += 0.001;
 9518
 9519        let mut element = v_flex()
 9520            .items_end()
 9521            .when(flag_on_right, |el| el.items_start())
 9522            .child(if flag_on_right {
 9523                self.render_edit_prediction_line_popover("Jump", None, window, cx)
 9524                    .rounded_bl(px(0.))
 9525                    .rounded_tl(px(0.))
 9526                    .border_l_2()
 9527                    .border_color(border_color)
 9528            } else {
 9529                self.render_edit_prediction_line_popover("Jump", None, window, cx)
 9530                    .rounded_br(px(0.))
 9531                    .rounded_tr(px(0.))
 9532                    .border_r_2()
 9533                    .border_color(border_color)
 9534            })
 9535            .child(div().w(POLE_WIDTH).bg(border_color).h(line_height))
 9536            .into_any();
 9537
 9538        let size = element.layout_as_root(AvailableSpace::min_size(), window, cx);
 9539
 9540        let mut origin = scrolled_content_origin + point(target_x, target_y.into())
 9541            - point(
 9542                if flag_on_right {
 9543                    POLE_WIDTH
 9544                } else {
 9545                    size.width - POLE_WIDTH
 9546                },
 9547                size.height - line_height,
 9548            );
 9549
 9550        origin.x = origin.x.max(content_origin.x);
 9551
 9552        element.prepaint_at(origin, window, cx);
 9553
 9554        Some((element, origin))
 9555    }
 9556
 9557    fn render_edit_prediction_scroll_popover(
 9558        &mut self,
 9559        to_y: &dyn Fn(Size<Pixels>) -> Pixels,
 9560        scroll_icon: IconName,
 9561        visible_row_range: Range<DisplayRow>,
 9562        line_layouts: &[LineWithInvisibles],
 9563        newest_selection_head: Option<DisplayPoint>,
 9564        scrolled_content_origin: gpui::Point<Pixels>,
 9565        window: &mut Window,
 9566        cx: &mut App,
 9567    ) -> Option<(AnyElement, gpui::Point<Pixels>)> {
 9568        let mut element = self
 9569            .render_edit_prediction_line_popover("Scroll", Some(scroll_icon), window, cx)
 9570            .into_any();
 9571
 9572        let size = element.layout_as_root(AvailableSpace::min_size(), window, cx);
 9573
 9574        let cursor = newest_selection_head?;
 9575        let cursor_row_layout =
 9576            line_layouts.get(cursor.row().minus(visible_row_range.start) as usize)?;
 9577        let cursor_column = cursor.column() as usize;
 9578
 9579        let cursor_character_x = cursor_row_layout.x_for_index(cursor_column);
 9580
 9581        let origin = scrolled_content_origin + point(cursor_character_x, to_y(size));
 9582
 9583        element.prepaint_at(origin, window, cx);
 9584        Some((element, origin))
 9585    }
 9586
 9587    fn render_edit_prediction_eager_jump_popover(
 9588        &mut self,
 9589        text_bounds: &Bounds<Pixels>,
 9590        content_origin: gpui::Point<Pixels>,
 9591        editor_snapshot: &EditorSnapshot,
 9592        visible_row_range: Range<DisplayRow>,
 9593        scroll_top: ScrollOffset,
 9594        scroll_bottom: ScrollOffset,
 9595        line_height: Pixels,
 9596        scroll_pixel_position: gpui::Point<ScrollPixelOffset>,
 9597        target_display_point: DisplayPoint,
 9598        editor_width: Pixels,
 9599        window: &mut Window,
 9600        cx: &mut App,
 9601    ) -> Option<(AnyElement, gpui::Point<Pixels>)> {
 9602        if target_display_point.row().as_f64() < scroll_top {
 9603            let mut element = self
 9604                .render_edit_prediction_line_popover(
 9605                    "Jump to Edit",
 9606                    Some(IconName::ArrowUp),
 9607                    window,
 9608                    cx,
 9609                )
 9610                .into_any();
 9611
 9612            let size = element.layout_as_root(AvailableSpace::min_size(), window, cx);
 9613            let offset = point(
 9614                (text_bounds.size.width - size.width) / 2.,
 9615                Self::EDIT_PREDICTION_POPOVER_PADDING_Y,
 9616            );
 9617
 9618            let origin = text_bounds.origin + offset;
 9619            element.prepaint_at(origin, window, cx);
 9620            Some((element, origin))
 9621        } else if (target_display_point.row().as_f64() + 1.) > scroll_bottom {
 9622            let mut element = self
 9623                .render_edit_prediction_line_popover(
 9624                    "Jump to Edit",
 9625                    Some(IconName::ArrowDown),
 9626                    window,
 9627                    cx,
 9628                )
 9629                .into_any();
 9630
 9631            let size = element.layout_as_root(AvailableSpace::min_size(), window, cx);
 9632            let offset = point(
 9633                (text_bounds.size.width - size.width) / 2.,
 9634                text_bounds.size.height - size.height - Self::EDIT_PREDICTION_POPOVER_PADDING_Y,
 9635            );
 9636
 9637            let origin = text_bounds.origin + offset;
 9638            element.prepaint_at(origin, window, cx);
 9639            Some((element, origin))
 9640        } else {
 9641            self.render_edit_prediction_end_of_line_popover(
 9642                "Jump to Edit",
 9643                editor_snapshot,
 9644                visible_row_range,
 9645                target_display_point,
 9646                line_height,
 9647                scroll_pixel_position,
 9648                content_origin,
 9649                editor_width,
 9650                window,
 9651                cx,
 9652            )
 9653        }
 9654    }
 9655
 9656    fn render_edit_prediction_end_of_line_popover(
 9657        self: &mut Editor,
 9658        label: &'static str,
 9659        editor_snapshot: &EditorSnapshot,
 9660        visible_row_range: Range<DisplayRow>,
 9661        target_display_point: DisplayPoint,
 9662        line_height: Pixels,
 9663        scroll_pixel_position: gpui::Point<ScrollPixelOffset>,
 9664        content_origin: gpui::Point<Pixels>,
 9665        editor_width: Pixels,
 9666        window: &mut Window,
 9667        cx: &mut App,
 9668    ) -> Option<(AnyElement, gpui::Point<Pixels>)> {
 9669        let target_line_end = DisplayPoint::new(
 9670            target_display_point.row(),
 9671            editor_snapshot.line_len(target_display_point.row()),
 9672        );
 9673
 9674        let mut element = self
 9675            .render_edit_prediction_line_popover(label, None, window, cx)
 9676            .into_any();
 9677
 9678        let size = element.layout_as_root(AvailableSpace::min_size(), window, cx);
 9679
 9680        let line_origin =
 9681            self.display_to_pixel_point(target_line_end, editor_snapshot, window, cx)?;
 9682
 9683        let start_point = content_origin - point(scroll_pixel_position.x.into(), Pixels::ZERO);
 9684        let mut origin = start_point
 9685            + line_origin
 9686            + point(Self::EDIT_PREDICTION_POPOVER_PADDING_X, Pixels::ZERO);
 9687        origin.x = origin.x.max(content_origin.x);
 9688
 9689        let max_x = content_origin.x + editor_width - size.width;
 9690
 9691        if origin.x > max_x {
 9692            let offset = line_height + Self::EDIT_PREDICTION_POPOVER_PADDING_Y;
 9693
 9694            let icon = if visible_row_range.contains(&(target_display_point.row() + 2)) {
 9695                origin.y += offset;
 9696                IconName::ArrowUp
 9697            } else {
 9698                origin.y -= offset;
 9699                IconName::ArrowDown
 9700            };
 9701
 9702            element = self
 9703                .render_edit_prediction_line_popover(label, Some(icon), window, cx)
 9704                .into_any();
 9705
 9706            let size = element.layout_as_root(AvailableSpace::min_size(), window, cx);
 9707
 9708            origin.x = content_origin.x + editor_width - size.width - px(2.);
 9709        }
 9710
 9711        element.prepaint_at(origin, window, cx);
 9712        Some((element, origin))
 9713    }
 9714
 9715    fn render_edit_prediction_diff_popover(
 9716        self: &Editor,
 9717        text_bounds: &Bounds<Pixels>,
 9718        content_origin: gpui::Point<Pixels>,
 9719        right_margin: Pixels,
 9720        editor_snapshot: &EditorSnapshot,
 9721        visible_row_range: Range<DisplayRow>,
 9722        line_layouts: &[LineWithInvisibles],
 9723        line_height: Pixels,
 9724        scroll_position: gpui::Point<ScrollOffset>,
 9725        scroll_pixel_position: gpui::Point<ScrollPixelOffset>,
 9726        newest_selection_head: Option<DisplayPoint>,
 9727        editor_width: Pixels,
 9728        style: &EditorStyle,
 9729        edits: &Vec<(Range<Anchor>, Arc<str>)>,
 9730        edit_preview: &Option<language::EditPreview>,
 9731        snapshot: &language::BufferSnapshot,
 9732        window: &mut Window,
 9733        cx: &mut App,
 9734    ) -> Option<(AnyElement, gpui::Point<Pixels>)> {
 9735        let edit_start = edits
 9736            .first()
 9737            .unwrap()
 9738            .0
 9739            .start
 9740            .to_display_point(editor_snapshot);
 9741        let edit_end = edits
 9742            .last()
 9743            .unwrap()
 9744            .0
 9745            .end
 9746            .to_display_point(editor_snapshot);
 9747
 9748        let is_visible = visible_row_range.contains(&edit_start.row())
 9749            || visible_row_range.contains(&edit_end.row());
 9750        if !is_visible {
 9751            return None;
 9752        }
 9753
 9754        let highlighted_edits = if let Some(edit_preview) = edit_preview.as_ref() {
 9755            crate::edit_prediction_edit_text(snapshot, edits, edit_preview, false, cx)
 9756        } else {
 9757            // Fallback for providers without edit_preview
 9758            crate::edit_prediction_fallback_text(edits, cx)
 9759        };
 9760
 9761        let styled_text = highlighted_edits.to_styled_text(&style.text);
 9762        let line_count = highlighted_edits.text.lines().count();
 9763
 9764        const BORDER_WIDTH: Pixels = px(1.);
 9765
 9766        let keybind = self.render_edit_prediction_keybind(window, cx);
 9767        let has_keybind = keybind.is_some();
 9768
 9769        let mut element = h_flex()
 9770            .items_start()
 9771            .child(
 9772                h_flex()
 9773                    .bg(cx.theme().colors().editor_background)
 9774                    .border(BORDER_WIDTH)
 9775                    .shadow_xs()
 9776                    .border_color(cx.theme().colors().border)
 9777                    .rounded_l_lg()
 9778                    .when(line_count > 1, |el| el.rounded_br_lg())
 9779                    .pr_1()
 9780                    .child(styled_text),
 9781            )
 9782            .child(
 9783                h_flex()
 9784                    .h(line_height + BORDER_WIDTH * 2.)
 9785                    .px_1p5()
 9786                    .gap_1()
 9787                    // Workaround: For some reason, there's a gap if we don't do this
 9788                    .ml(-BORDER_WIDTH)
 9789                    .shadow(vec![gpui::BoxShadow {
 9790                        color: gpui::black().opacity(0.05),
 9791                        offset: point(px(1.), px(1.)),
 9792                        blur_radius: px(2.),
 9793                        spread_radius: px(0.),
 9794                    }])
 9795                    .bg(Editor::edit_prediction_line_popover_bg_color(cx))
 9796                    .border(BORDER_WIDTH)
 9797                    .border_color(cx.theme().colors().border)
 9798                    .rounded_r_lg()
 9799                    .id("edit_prediction_diff_popover_keybind")
 9800                    .when(!has_keybind, |el| {
 9801                        let status_colors = cx.theme().status();
 9802
 9803                        el.bg(status_colors.error_background)
 9804                            .border_color(status_colors.error.opacity(0.6))
 9805                            .child(Icon::new(IconName::Info).color(Color::Error))
 9806                            .cursor_default()
 9807                            .hoverable_tooltip(move |_window, cx| {
 9808                                cx.new(|_| MissingEditPredictionKeybindingTooltip).into()
 9809                            })
 9810                    })
 9811                    .children(keybind),
 9812            )
 9813            .into_any();
 9814
 9815        let longest_row =
 9816            editor_snapshot.longest_row_in_range(edit_start.row()..edit_end.row() + 1);
 9817        let longest_line_width = if visible_row_range.contains(&longest_row) {
 9818            line_layouts[(longest_row.0 - visible_row_range.start.0) as usize].width
 9819        } else {
 9820            layout_line(
 9821                longest_row,
 9822                editor_snapshot,
 9823                style,
 9824                editor_width,
 9825                |_| false,
 9826                window,
 9827                cx,
 9828            )
 9829            .width
 9830        };
 9831
 9832        let viewport_bounds =
 9833            Bounds::new(Default::default(), window.viewport_size()).extend(Edges {
 9834                right: -right_margin,
 9835                ..Default::default()
 9836            });
 9837
 9838        let x_after_longest = Pixels::from(
 9839            ScrollPixelOffset::from(
 9840                text_bounds.origin.x + longest_line_width + Self::EDIT_PREDICTION_POPOVER_PADDING_X,
 9841            ) - scroll_pixel_position.x,
 9842        );
 9843
 9844        let element_bounds = element.layout_as_root(AvailableSpace::min_size(), window, cx);
 9845
 9846        // Fully visible if it can be displayed within the window (allow overlapping other
 9847        // panes). However, this is only allowed if the popover starts within text_bounds.
 9848        let can_position_to_the_right = x_after_longest < text_bounds.right()
 9849            && x_after_longest + element_bounds.width < viewport_bounds.right();
 9850
 9851        let mut origin = if can_position_to_the_right {
 9852            point(
 9853                x_after_longest,
 9854                text_bounds.origin.y
 9855                    + Pixels::from(
 9856                        edit_start.row().as_f64() * ScrollPixelOffset::from(line_height)
 9857                            - scroll_pixel_position.y,
 9858                    ),
 9859            )
 9860        } else {
 9861            let cursor_row = newest_selection_head.map(|head| head.row());
 9862            let above_edit = edit_start
 9863                .row()
 9864                .0
 9865                .checked_sub(line_count as u32)
 9866                .map(DisplayRow);
 9867            let below_edit = Some(edit_end.row() + 1);
 9868            let above_cursor =
 9869                cursor_row.and_then(|row| row.0.checked_sub(line_count as u32).map(DisplayRow));
 9870            let below_cursor = cursor_row.map(|cursor_row| cursor_row + 1);
 9871
 9872            // Place the edit popover adjacent to the edit if there is a location
 9873            // available that is onscreen and does not obscure the cursor. Otherwise,
 9874            // place it adjacent to the cursor.
 9875            let row_target = [above_edit, below_edit, above_cursor, below_cursor]
 9876                .into_iter()
 9877                .flatten()
 9878                .find(|&start_row| {
 9879                    let end_row = start_row + line_count as u32;
 9880                    visible_row_range.contains(&start_row)
 9881                        && visible_row_range.contains(&end_row)
 9882                        && cursor_row
 9883                            .is_none_or(|cursor_row| !((start_row..end_row).contains(&cursor_row)))
 9884                })?;
 9885
 9886            content_origin
 9887                + point(
 9888                    Pixels::from(-scroll_pixel_position.x),
 9889                    Pixels::from(
 9890                        (row_target.as_f64() - scroll_position.y) * f64::from(line_height),
 9891                    ),
 9892                )
 9893        };
 9894
 9895        origin.x -= BORDER_WIDTH;
 9896
 9897        window.with_content_mask(
 9898            Some(gpui::ContentMask {
 9899                bounds: *text_bounds,
 9900            }),
 9901            |window| {
 9902                window.defer_draw(element, origin, 1, Some(window.content_mask()));
 9903            },
 9904        );
 9905
 9906        // Do not return an element, since it will already be drawn due to defer_draw.
 9907        None
 9908    }
 9909
 9910    fn edit_prediction_cursor_popover_height(&self) -> Pixels {
 9911        px(30.)
 9912    }
 9913
 9914    fn current_user_player_color(&self, cx: &mut App) -> PlayerColor {
 9915        if self.read_only(cx) {
 9916            cx.theme().players().read_only()
 9917        } else {
 9918            self.style.as_ref().unwrap().local_player
 9919        }
 9920    }
 9921
 9922    fn render_edit_prediction_inline_keystroke(
 9923        &self,
 9924        keystroke: &gpui::KeybindingKeystroke,
 9925        modifiers_color: Color,
 9926        cx: &App,
 9927    ) -> AnyElement {
 9928        let is_platform_style_mac = PlatformStyle::platform() == PlatformStyle::Mac;
 9929
 9930        h_flex()
 9931            .px_0p5()
 9932            .when(is_platform_style_mac, |parent| parent.gap_0p5())
 9933            .font(
 9934                theme_settings::ThemeSettings::get_global(cx)
 9935                    .buffer_font
 9936                    .clone(),
 9937            )
 9938            .text_size(TextSize::XSmall.rems(cx))
 9939            .child(h_flex().children(ui::render_modifiers(
 9940                keystroke.modifiers(),
 9941                PlatformStyle::platform(),
 9942                Some(modifiers_color),
 9943                Some(IconSize::XSmall.rems().into()),
 9944                true,
 9945            )))
 9946            .when(is_platform_style_mac, |parent| {
 9947                parent.child(keystroke.key().to_string())
 9948            })
 9949            .when(!is_platform_style_mac, |parent| {
 9950                parent.child(
 9951                    Key::new(ui::utils::capitalize(keystroke.key()), Some(Color::Default))
 9952                        .size(Some(IconSize::XSmall.rems().into())),
 9953                )
 9954            })
 9955            .into_any()
 9956    }
 9957
 9958    fn render_edit_prediction_popover_keystroke(
 9959        &self,
 9960        keystroke: &gpui::KeybindingKeystroke,
 9961        color: Color,
 9962        cx: &App,
 9963    ) -> AnyElement {
 9964        let is_platform_style_mac = PlatformStyle::platform() == PlatformStyle::Mac;
 9965
 9966        if keystroke.modifiers().modified() {
 9967            h_flex()
 9968                .font(
 9969                    theme_settings::ThemeSettings::get_global(cx)
 9970                        .buffer_font
 9971                        .clone(),
 9972                )
 9973                .when(is_platform_style_mac, |parent| parent.gap_1())
 9974                .child(h_flex().children(ui::render_modifiers(
 9975                    keystroke.modifiers(),
 9976                    PlatformStyle::platform(),
 9977                    Some(color),
 9978                    None,
 9979                    false,
 9980                )))
 9981                .into_any()
 9982        } else {
 9983            Key::new(ui::utils::capitalize(keystroke.key()), Some(color))
 9984                .size(Some(IconSize::XSmall.rems().into()))
 9985                .into_any_element()
 9986        }
 9987    }
 9988
 9989    fn render_edit_prediction_keybind(
 9990        &self,
 9991        window: &mut Window,
 9992        cx: &mut App,
 9993    ) -> Option<AnyElement> {
 9994        let keybind_display =
 9995            self.edit_prediction_keybind_display(EditPredictionKeybindSurface::Inline, window, cx);
 9996        let keystroke = keybind_display.displayed_keystroke.as_ref()?;
 9997
 9998        let modifiers_color = if *keystroke.modifiers() == window.modifiers() {
 9999            Color::Accent
10000        } else {
10001            Color::Muted
10002        };
10003
10004        Some(self.render_edit_prediction_inline_keystroke(keystroke, modifiers_color, cx))
10005    }
10006
10007    fn render_edit_prediction_line_popover(
10008        &self,
10009        label: impl Into<SharedString>,
10010        icon: Option<IconName>,
10011        window: &mut Window,
10012        cx: &mut App,
10013    ) -> Stateful<Div> {
10014        let padding_right = if icon.is_some() { px(4.) } else { px(8.) };
10015
10016        let keybind = self.render_edit_prediction_keybind(window, cx);
10017        let has_keybind = keybind.is_some();
10018        let icons = Self::get_prediction_provider_icons(&self.edit_prediction_provider, cx);
10019
10020        h_flex()
10021            .id("ep-line-popover")
10022            .py_0p5()
10023            .pl_1()
10024            .pr(padding_right)
10025            .gap_1()
10026            .rounded_md()
10027            .border_1()
10028            .bg(Self::edit_prediction_line_popover_bg_color(cx))
10029            .border_color(Self::edit_prediction_callout_popover_border_color(cx))
10030            .shadow_xs()
10031            .when(!has_keybind, |el| {
10032                let status_colors = cx.theme().status();
10033
10034                el.bg(status_colors.error_background)
10035                    .border_color(status_colors.error.opacity(0.6))
10036                    .pl_2()
10037                    .child(Icon::new(icons.error).color(Color::Error))
10038                    .cursor_default()
10039                    .hoverable_tooltip(move |_window, cx| {
10040                        cx.new(|_| MissingEditPredictionKeybindingTooltip).into()
10041                    })
10042            })
10043            .children(keybind)
10044            .child(
10045                Label::new(label)
10046                    .size(LabelSize::Small)
10047                    .when(!has_keybind, |el| {
10048                        el.color(cx.theme().status().error.into()).strikethrough()
10049                    }),
10050            )
10051            .when(!has_keybind, |el| {
10052                el.child(
10053                    h_flex().ml_1().child(
10054                        Icon::new(IconName::Info)
10055                            .size(IconSize::Small)
10056                            .color(cx.theme().status().error.into()),
10057                    ),
10058                )
10059            })
10060            .when_some(icon, |element, icon| {
10061                element.child(
10062                    div()
10063                        .mt(px(1.5))
10064                        .child(Icon::new(icon).size(IconSize::Small)),
10065                )
10066            })
10067    }
10068
10069    fn render_edit_prediction_jump_outside_popover(
10070        &self,
10071        snapshot: &BufferSnapshot,
10072        window: &mut Window,
10073        cx: &mut App,
10074    ) -> Stateful<Div> {
10075        let keybind = self.render_edit_prediction_keybind(window, cx);
10076        let has_keybind = keybind.is_some();
10077        let icons = Self::get_prediction_provider_icons(&self.edit_prediction_provider, cx);
10078
10079        let file_name = snapshot
10080            .file()
10081            .map(|file| SharedString::new(file.file_name(cx)))
10082            .unwrap_or(SharedString::new_static("untitled"));
10083
10084        h_flex()
10085            .id("ep-jump-outside-popover")
10086            .py_1()
10087            .px_2()
10088            .gap_1()
10089            .rounded_md()
10090            .border_1()
10091            .bg(Self::edit_prediction_line_popover_bg_color(cx))
10092            .border_color(Self::edit_prediction_callout_popover_border_color(cx))
10093            .shadow_xs()
10094            .when(!has_keybind, |el| {
10095                let status_colors = cx.theme().status();
10096
10097                el.bg(status_colors.error_background)
10098                    .border_color(status_colors.error.opacity(0.6))
10099                    .pl_2()
10100                    .child(Icon::new(icons.error).color(Color::Error))
10101                    .cursor_default()
10102                    .hoverable_tooltip(move |_window, cx| {
10103                        cx.new(|_| MissingEditPredictionKeybindingTooltip).into()
10104                    })
10105            })
10106            .children(keybind)
10107            .child(
10108                Label::new(file_name)
10109                    .size(LabelSize::Small)
10110                    .buffer_font(cx)
10111                    .when(!has_keybind, |el| {
10112                        el.color(cx.theme().status().error.into()).strikethrough()
10113                    }),
10114            )
10115            .when(!has_keybind, |el| {
10116                el.child(
10117                    h_flex().ml_1().child(
10118                        Icon::new(IconName::Info)
10119                            .size(IconSize::Small)
10120                            .color(cx.theme().status().error.into()),
10121                    ),
10122                )
10123            })
10124            .child(
10125                div()
10126                    .mt(px(1.5))
10127                    .child(Icon::new(IconName::ArrowUpRight).size(IconSize::Small)),
10128            )
10129    }
10130
10131    fn edit_prediction_line_popover_bg_color(cx: &App) -> Hsla {
10132        let accent_color = cx.theme().colors().text_accent;
10133        let editor_bg_color = cx.theme().colors().editor_background;
10134        editor_bg_color.blend(accent_color.opacity(0.1))
10135    }
10136
10137    fn edit_prediction_callout_popover_border_color(cx: &App) -> Hsla {
10138        let accent_color = cx.theme().colors().text_accent;
10139        let editor_bg_color = cx.theme().colors().editor_background;
10140        editor_bg_color.blend(accent_color.opacity(0.6))
10141    }
10142    fn get_prediction_provider_icons(
10143        provider: &Option<RegisteredEditPredictionDelegate>,
10144        cx: &App,
10145    ) -> edit_prediction_types::EditPredictionIconSet {
10146        match provider {
10147            Some(provider) => provider.provider.icons(cx),
10148            None => edit_prediction_types::EditPredictionIconSet::new(IconName::ZedPredict),
10149        }
10150    }
10151
10152    fn render_edit_prediction_cursor_popover(
10153        &self,
10154        min_width: Pixels,
10155        max_width: Pixels,
10156        cursor_point: Point,
10157        style: &EditorStyle,
10158        window: &mut Window,
10159        cx: &mut Context<Editor>,
10160    ) -> Option<AnyElement> {
10161        let provider = self.edit_prediction_provider.as_ref()?;
10162        let icons = Self::get_prediction_provider_icons(&self.edit_prediction_provider, cx);
10163
10164        let is_refreshing = provider.provider.is_refreshing(cx);
10165
10166        fn pending_completion_container(icon: IconName) -> Div {
10167            h_flex().h_full().flex_1().gap_2().child(Icon::new(icon))
10168        }
10169
10170        let completion = match &self.active_edit_prediction {
10171            Some(prediction) => {
10172                if !self.has_visible_completions_menu() {
10173                    const RADIUS: Pixels = px(6.);
10174                    const BORDER_WIDTH: Pixels = px(1.);
10175                    let keybind_display = self.edit_prediction_keybind_display(
10176                        EditPredictionKeybindSurface::CursorPopoverCompact,
10177                        window,
10178                        cx,
10179                    );
10180
10181                    return Some(
10182                        h_flex()
10183                            .elevation_2(cx)
10184                            .border(BORDER_WIDTH)
10185                            .border_color(cx.theme().colors().border)
10186                            .when(keybind_display.missing_accept_keystroke, |el| {
10187                                el.border_color(cx.theme().status().error)
10188                            })
10189                            .rounded(RADIUS)
10190                            .rounded_tl(px(0.))
10191                            .overflow_hidden()
10192                            .child(div().px_1p5().child(match &prediction.completion {
10193                                EditPrediction::MoveWithin { target, snapshot } => {
10194                                    use text::ToPoint as _;
10195                                    if target.text_anchor.to_point(snapshot).row > cursor_point.row
10196                                    {
10197                                        Icon::new(icons.down)
10198                                    } else {
10199                                        Icon::new(icons.up)
10200                                    }
10201                                }
10202                                EditPrediction::MoveOutside { .. } => {
10203                                    // TODO [zeta2] custom icon for external jump?
10204                                    Icon::new(icons.base)
10205                                }
10206                                EditPrediction::Edit { .. } => Icon::new(icons.base),
10207                            }))
10208                            .child(
10209                                h_flex()
10210                                    .gap_1()
10211                                    .py_1()
10212                                    .px_2()
10213                                    .rounded_r(RADIUS - BORDER_WIDTH)
10214                                    .border_l_1()
10215                                    .border_color(cx.theme().colors().border)
10216                                    .bg(Self::edit_prediction_line_popover_bg_color(cx))
10217                                    .when(keybind_display.show_hold_label, |el| {
10218                                        el.child(
10219                                            Label::new("Hold")
10220                                                .size(LabelSize::Small)
10221                                                .when(
10222                                                    keybind_display.missing_accept_keystroke,
10223                                                    |el| el.strikethrough(),
10224                                                )
10225                                                .line_height_style(LineHeightStyle::UiLabel),
10226                                        )
10227                                    })
10228                                    .id("edit_prediction_cursor_popover_keybind")
10229                                    .when(keybind_display.missing_accept_keystroke, |el| {
10230                                        let status_colors = cx.theme().status();
10231
10232                                        el.bg(status_colors.error_background)
10233                                            .border_color(status_colors.error.opacity(0.6))
10234                                            .child(Icon::new(IconName::Info).color(Color::Error))
10235                                            .cursor_default()
10236                                            .hoverable_tooltip(move |_window, cx| {
10237                                                cx.new(|_| MissingEditPredictionKeybindingTooltip)
10238                                                    .into()
10239                                            })
10240                                    })
10241                                    .when_some(
10242                                        keybind_display.displayed_keystroke.as_ref(),
10243                                        |el, compact_keystroke| {
10244                                            el.child(self.render_edit_prediction_popover_keystroke(
10245                                                compact_keystroke,
10246                                                Color::Default,
10247                                                cx,
10248                                            ))
10249                                        },
10250                                    ),
10251                            )
10252                            .into_any(),
10253                    );
10254                }
10255
10256                self.render_edit_prediction_cursor_popover_preview(
10257                    prediction,
10258                    cursor_point,
10259                    style,
10260                    cx,
10261                )?
10262            }
10263
10264            None if is_refreshing => match &self.stale_edit_prediction_in_menu {
10265                Some(stale_completion) => self.render_edit_prediction_cursor_popover_preview(
10266                    stale_completion,
10267                    cursor_point,
10268                    style,
10269                    cx,
10270                )?,
10271
10272                None => pending_completion_container(icons.base)
10273                    .child(Label::new("...").size(LabelSize::Small)),
10274            },
10275
10276            None => pending_completion_container(icons.base)
10277                .child(Label::new("...").size(LabelSize::Small)),
10278        };
10279
10280        let completion = if is_refreshing || self.active_edit_prediction.is_none() {
10281            completion
10282                .with_animation(
10283                    "loading-completion",
10284                    Animation::new(Duration::from_secs(2))
10285                        .repeat()
10286                        .with_easing(pulsating_between(0.4, 0.8)),
10287                    |label, delta| label.opacity(delta),
10288                )
10289                .into_any_element()
10290        } else {
10291            completion.into_any_element()
10292        };
10293
10294        let has_completion = self.active_edit_prediction.is_some();
10295        let keybind_display = self.edit_prediction_keybind_display(
10296            EditPredictionKeybindSurface::CursorPopoverExpanded,
10297            window,
10298            cx,
10299        );
10300
10301        Some(
10302            h_flex()
10303                .min_w(min_width)
10304                .max_w(max_width)
10305                .flex_1()
10306                .elevation_2(cx)
10307                .border_color(cx.theme().colors().border)
10308                .child(
10309                    div()
10310                        .flex_1()
10311                        .py_1()
10312                        .px_2()
10313                        .overflow_hidden()
10314                        .child(completion),
10315                )
10316                .when_some(
10317                    keybind_display.displayed_keystroke.as_ref(),
10318                    |el, keystroke| {
10319                        let key_color = if !has_completion {
10320                            Color::Muted
10321                        } else {
10322                            Color::Default
10323                        };
10324
10325                        if keybind_display.action == EditPredictionKeybindAction::Preview {
10326                            el.child(
10327                                h_flex()
10328                                    .h_full()
10329                                    .border_l_1()
10330                                    .rounded_r_lg()
10331                                    .border_color(cx.theme().colors().border)
10332                                    .bg(Self::edit_prediction_line_popover_bg_color(cx))
10333                                    .gap_1()
10334                                    .py_1()
10335                                    .px_2()
10336                                    .child(self.render_edit_prediction_popover_keystroke(
10337                                        keystroke, key_color, cx,
10338                                    ))
10339                                    .child(Label::new("Preview").into_any_element())
10340                                    .opacity(if has_completion { 1.0 } else { 0.4 }),
10341                            )
10342                        } else {
10343                            el.child(
10344                                h_flex()
10345                                    .h_full()
10346                                    .border_l_1()
10347                                    .rounded_r_lg()
10348                                    .border_color(cx.theme().colors().border)
10349                                    .bg(Self::edit_prediction_line_popover_bg_color(cx))
10350                                    .gap_1()
10351                                    .py_1()
10352                                    .px_2()
10353                                    .child(self.render_edit_prediction_popover_keystroke(
10354                                        keystroke, key_color, cx,
10355                                    ))
10356                                    .opacity(if has_completion { 1.0 } else { 0.4 }),
10357                            )
10358                        }
10359                    },
10360                )
10361                .into_any(),
10362        )
10363    }
10364
10365    fn render_edit_prediction_cursor_popover_preview(
10366        &self,
10367        completion: &EditPredictionState,
10368        cursor_point: Point,
10369        style: &EditorStyle,
10370        cx: &mut Context<Editor>,
10371    ) -> Option<Div> {
10372        use text::ToPoint as _;
10373
10374        fn render_relative_row_jump(
10375            prefix: impl Into<String>,
10376            current_row: u32,
10377            target_row: u32,
10378        ) -> Div {
10379            let (row_diff, arrow) = if target_row < current_row {
10380                (current_row - target_row, IconName::ArrowUp)
10381            } else {
10382                (target_row - current_row, IconName::ArrowDown)
10383            };
10384
10385            h_flex()
10386                .child(
10387                    Label::new(format!("{}{}", prefix.into(), row_diff))
10388                        .color(Color::Muted)
10389                        .size(LabelSize::Small),
10390                )
10391                .child(Icon::new(arrow).color(Color::Muted).size(IconSize::Small))
10392        }
10393
10394        let supports_jump = self
10395            .edit_prediction_provider
10396            .as_ref()
10397            .map(|provider| provider.provider.supports_jump_to_edit())
10398            .unwrap_or(true);
10399
10400        let icons = Self::get_prediction_provider_icons(&self.edit_prediction_provider, cx);
10401
10402        match &completion.completion {
10403            EditPrediction::MoveWithin {
10404                target, snapshot, ..
10405            } => {
10406                if !supports_jump {
10407                    return None;
10408                }
10409
10410                Some(
10411                    h_flex()
10412                        .px_2()
10413                        .gap_2()
10414                        .flex_1()
10415                        .child(
10416                            if target.text_anchor.to_point(snapshot).row > cursor_point.row {
10417                                Icon::new(icons.down)
10418                            } else {
10419                                Icon::new(icons.up)
10420                            },
10421                        )
10422                        .child(Label::new("Jump to Edit")),
10423                )
10424            }
10425            EditPrediction::MoveOutside { snapshot, .. } => {
10426                let file_name = snapshot
10427                    .file()
10428                    .map(|file| file.file_name(cx))
10429                    .unwrap_or("untitled");
10430                Some(
10431                    h_flex()
10432                        .px_2()
10433                        .gap_2()
10434                        .flex_1()
10435                        .child(Icon::new(icons.base))
10436                        .child(Label::new(format!("Jump to {file_name}"))),
10437                )
10438            }
10439            EditPrediction::Edit {
10440                edits,
10441                edit_preview,
10442                snapshot,
10443                ..
10444            } => {
10445                let first_edit_row = edits.first()?.0.start.text_anchor.to_point(snapshot).row;
10446
10447                let (highlighted_edits, has_more_lines) =
10448                    if let Some(edit_preview) = edit_preview.as_ref() {
10449                        crate::edit_prediction_edit_text(snapshot, edits, edit_preview, true, cx)
10450                            .first_line_preview()
10451                    } else {
10452                        crate::edit_prediction_fallback_text(edits, cx).first_line_preview()
10453                    };
10454
10455                let styled_text = gpui::StyledText::new(highlighted_edits.text)
10456                    .with_default_highlights(&style.text, highlighted_edits.highlights);
10457
10458                let preview = h_flex()
10459                    .gap_1()
10460                    .min_w_16()
10461                    .child(styled_text)
10462                    .when(has_more_lines, |parent| parent.child(""));
10463
10464                let left = if supports_jump && first_edit_row != cursor_point.row {
10465                    render_relative_row_jump("", cursor_point.row, first_edit_row)
10466                        .into_any_element()
10467                } else {
10468                    Icon::new(icons.base).into_any_element()
10469                };
10470
10471                Some(
10472                    h_flex()
10473                        .h_full()
10474                        .flex_1()
10475                        .gap_2()
10476                        .pr_1()
10477                        .overflow_x_hidden()
10478                        .font(
10479                            theme_settings::ThemeSettings::get_global(cx)
10480                                .buffer_font
10481                                .clone(),
10482                        )
10483                        .child(left)
10484                        .child(preview),
10485                )
10486            }
10487        }
10488    }
10489
10490    pub fn render_context_menu(
10491        &mut self,
10492        max_height_in_lines: u32,
10493        window: &mut Window,
10494        cx: &mut Context<Editor>,
10495    ) -> Option<AnyElement> {
10496        let menu = self.context_menu.borrow();
10497        let menu = menu.as_ref()?;
10498        if !menu.visible() {
10499            return None;
10500        };
10501        self.style
10502            .as_ref()
10503            .map(|style| menu.render(style, max_height_in_lines, window, cx))
10504    }
10505
10506    fn render_context_menu_aside(
10507        &mut self,
10508        max_size: Size<Pixels>,
10509        window: &mut Window,
10510        cx: &mut Context<Editor>,
10511    ) -> Option<AnyElement> {
10512        self.context_menu.borrow_mut().as_mut().and_then(|menu| {
10513            if menu.visible() {
10514                menu.render_aside(max_size, window, cx)
10515            } else {
10516                None
10517            }
10518        })
10519    }
10520
10521    fn hide_context_menu(
10522        &mut self,
10523        window: &mut Window,
10524        cx: &mut Context<Self>,
10525    ) -> Option<CodeContextMenu> {
10526        cx.notify();
10527        self.completion_tasks.clear();
10528        let context_menu = self.context_menu.borrow_mut().take();
10529        self.stale_edit_prediction_in_menu.take();
10530        self.update_visible_edit_prediction(window, cx);
10531        if let Some(CodeContextMenu::Completions(_)) = &context_menu
10532            && let Some(completion_provider) = &self.completion_provider
10533        {
10534            completion_provider.selection_changed(None, window, cx);
10535        }
10536        context_menu
10537    }
10538
10539    fn show_snippet_choices(
10540        &mut self,
10541        choices: &Vec<String>,
10542        selection: Range<Anchor>,
10543        cx: &mut Context<Self>,
10544    ) {
10545        let Some((_, buffer, _)) = self
10546            .buffer()
10547            .read(cx)
10548            .excerpt_containing(selection.start, cx)
10549        else {
10550            return;
10551        };
10552        let Some((_, end_buffer, _)) = self.buffer().read(cx).excerpt_containing(selection.end, cx)
10553        else {
10554            return;
10555        };
10556        if buffer != end_buffer {
10557            log::error!("expected anchor range to have matching buffer IDs");
10558            return;
10559        }
10560
10561        let id = post_inc(&mut self.next_completion_id);
10562        let snippet_sort_order = EditorSettings::get_global(cx).snippet_sort_order;
10563        let mut context_menu = self.context_menu.borrow_mut();
10564        let old_menu = context_menu.take();
10565        *context_menu = Some(CodeContextMenu::Completions(
10566            CompletionsMenu::new_snippet_choices(
10567                id,
10568                true,
10569                choices,
10570                selection,
10571                buffer,
10572                old_menu.map(|menu| menu.primary_scroll_handle()),
10573                snippet_sort_order,
10574            ),
10575        ));
10576    }
10577
10578    pub fn insert_snippet(
10579        &mut self,
10580        insertion_ranges: &[Range<MultiBufferOffset>],
10581        snippet: Snippet,
10582        window: &mut Window,
10583        cx: &mut Context<Self>,
10584    ) -> Result<()> {
10585        struct Tabstop<T> {
10586            is_end_tabstop: bool,
10587            ranges: Vec<Range<T>>,
10588            choices: Option<Vec<String>>,
10589        }
10590
10591        let tabstops = self.buffer.update(cx, |buffer, cx| {
10592            let snippet_text: Arc<str> = snippet.text.clone().into();
10593            let edits = insertion_ranges
10594                .iter()
10595                .cloned()
10596                .map(|range| (range, snippet_text.clone()));
10597            let autoindent_mode = AutoindentMode::Block {
10598                original_indent_columns: Vec::new(),
10599            };
10600            buffer.edit(edits, Some(autoindent_mode), cx);
10601
10602            let snapshot = &*buffer.read(cx);
10603            let snippet = &snippet;
10604            snippet
10605                .tabstops
10606                .iter()
10607                .map(|tabstop| {
10608                    let is_end_tabstop = tabstop.ranges.first().is_some_and(|tabstop| {
10609                        tabstop.is_empty() && tabstop.start == snippet.text.len() as isize
10610                    });
10611                    let mut tabstop_ranges = tabstop
10612                        .ranges
10613                        .iter()
10614                        .flat_map(|tabstop_range| {
10615                            let mut delta = 0_isize;
10616                            insertion_ranges.iter().map(move |insertion_range| {
10617                                let insertion_start = insertion_range.start + delta;
10618                                delta += snippet.text.len() as isize
10619                                    - (insertion_range.end - insertion_range.start) as isize;
10620
10621                                let start =
10622                                    (insertion_start + tabstop_range.start).min(snapshot.len());
10623                                let end = (insertion_start + tabstop_range.end).min(snapshot.len());
10624                                snapshot.anchor_before(start)..snapshot.anchor_after(end)
10625                            })
10626                        })
10627                        .collect::<Vec<_>>();
10628                    tabstop_ranges.sort_unstable_by(|a, b| a.start.cmp(&b.start, snapshot));
10629
10630                    Tabstop {
10631                        is_end_tabstop,
10632                        ranges: tabstop_ranges,
10633                        choices: tabstop.choices.clone(),
10634                    }
10635                })
10636                .collect::<Vec<_>>()
10637        });
10638        if let Some(tabstop) = tabstops.first() {
10639            self.change_selections(Default::default(), window, cx, |s| {
10640                // Reverse order so that the first range is the newest created selection.
10641                // Completions will use it and autoscroll will prioritize it.
10642                s.select_ranges(tabstop.ranges.iter().rev().cloned());
10643            });
10644
10645            if let Some(choices) = &tabstop.choices
10646                && let Some(selection) = tabstop.ranges.first()
10647            {
10648                self.show_snippet_choices(choices, selection.clone(), cx)
10649            }
10650
10651            // If we're already at the last tabstop and it's at the end of the snippet,
10652            // we're done, we don't need to keep the state around.
10653            if !tabstop.is_end_tabstop {
10654                let choices = tabstops
10655                    .iter()
10656                    .map(|tabstop| tabstop.choices.clone())
10657                    .collect();
10658
10659                let ranges = tabstops
10660                    .into_iter()
10661                    .map(|tabstop| tabstop.ranges)
10662                    .collect::<Vec<_>>();
10663
10664                self.snippet_stack.push(SnippetState {
10665                    active_index: 0,
10666                    ranges,
10667                    choices,
10668                });
10669            }
10670
10671            // Check whether the just-entered snippet ends with an auto-closable bracket.
10672            if self.autoclose_regions.is_empty() {
10673                let snapshot = self.buffer.read(cx).snapshot(cx);
10674                for selection in &mut self.selections.all::<Point>(&self.display_snapshot(cx)) {
10675                    let selection_head = selection.head();
10676                    let Some(scope) = snapshot.language_scope_at(selection_head) else {
10677                        continue;
10678                    };
10679
10680                    let mut bracket_pair = None;
10681                    let max_lookup_length = scope
10682                        .brackets()
10683                        .map(|(pair, _)| {
10684                            pair.start
10685                                .as_str()
10686                                .chars()
10687                                .count()
10688                                .max(pair.end.as_str().chars().count())
10689                        })
10690                        .max();
10691                    if let Some(max_lookup_length) = max_lookup_length {
10692                        let next_text = snapshot
10693                            .chars_at(selection_head)
10694                            .take(max_lookup_length)
10695                            .collect::<String>();
10696                        let prev_text = snapshot
10697                            .reversed_chars_at(selection_head)
10698                            .take(max_lookup_length)
10699                            .collect::<String>();
10700
10701                        for (pair, enabled) in scope.brackets() {
10702                            if enabled
10703                                && pair.close
10704                                && prev_text.starts_with(pair.start.as_str())
10705                                && next_text.starts_with(pair.end.as_str())
10706                            {
10707                                bracket_pair = Some(pair.clone());
10708                                break;
10709                            }
10710                        }
10711                    }
10712
10713                    if let Some(pair) = bracket_pair {
10714                        let snapshot_settings = snapshot.language_settings_at(selection_head, cx);
10715                        let autoclose_enabled =
10716                            self.use_autoclose && snapshot_settings.use_autoclose;
10717                        if autoclose_enabled {
10718                            let start = snapshot.anchor_after(selection_head);
10719                            let end = snapshot.anchor_after(selection_head);
10720                            self.autoclose_regions.push(AutocloseRegion {
10721                                selection_id: selection.id,
10722                                range: start..end,
10723                                pair,
10724                            });
10725                        }
10726                    }
10727                }
10728            }
10729        }
10730        Ok(())
10731    }
10732
10733    pub fn move_to_next_snippet_tabstop(
10734        &mut self,
10735        window: &mut Window,
10736        cx: &mut Context<Self>,
10737    ) -> bool {
10738        self.move_to_snippet_tabstop(Bias::Right, window, cx)
10739    }
10740
10741    pub fn move_to_prev_snippet_tabstop(
10742        &mut self,
10743        window: &mut Window,
10744        cx: &mut Context<Self>,
10745    ) -> bool {
10746        self.move_to_snippet_tabstop(Bias::Left, window, cx)
10747    }
10748
10749    pub fn move_to_snippet_tabstop(
10750        &mut self,
10751        bias: Bias,
10752        window: &mut Window,
10753        cx: &mut Context<Self>,
10754    ) -> bool {
10755        if let Some(mut snippet) = self.snippet_stack.pop() {
10756            match bias {
10757                Bias::Left => {
10758                    if snippet.active_index > 0 {
10759                        snippet.active_index -= 1;
10760                    } else {
10761                        self.snippet_stack.push(snippet);
10762                        return false;
10763                    }
10764                }
10765                Bias::Right => {
10766                    if snippet.active_index + 1 < snippet.ranges.len() {
10767                        snippet.active_index += 1;
10768                    } else {
10769                        self.snippet_stack.push(snippet);
10770                        return false;
10771                    }
10772                }
10773            }
10774            if let Some(current_ranges) = snippet.ranges.get(snippet.active_index) {
10775                self.change_selections(Default::default(), window, cx, |s| {
10776                    // Reverse order so that the first range is the newest created selection.
10777                    // Completions will use it and autoscroll will prioritize it.
10778                    s.select_ranges(current_ranges.iter().rev().cloned())
10779                });
10780
10781                if let Some(choices) = &snippet.choices[snippet.active_index]
10782                    && let Some(selection) = current_ranges.first()
10783                {
10784                    self.show_snippet_choices(choices, selection.clone(), cx);
10785                }
10786
10787                // If snippet state is not at the last tabstop, push it back on the stack
10788                if snippet.active_index + 1 < snippet.ranges.len() {
10789                    self.snippet_stack.push(snippet);
10790                }
10791                return true;
10792            }
10793        }
10794
10795        false
10796    }
10797
10798    pub fn clear(&mut self, window: &mut Window, cx: &mut Context<Self>) {
10799        self.transact(window, cx, |this, window, cx| {
10800            this.select_all(&SelectAll, window, cx);
10801            this.insert("", window, cx);
10802        });
10803    }
10804
10805    pub fn backspace(&mut self, _: &Backspace, window: &mut Window, cx: &mut Context<Self>) {
10806        if self.read_only(cx) {
10807            return;
10808        }
10809        self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
10810        self.transact(window, cx, |this, window, cx| {
10811            this.select_autoclose_pair(window, cx);
10812
10813            let linked_edits = this.linked_edits_for_selections(Arc::from(""), cx);
10814
10815            let display_map = this.display_map.update(cx, |map, cx| map.snapshot(cx));
10816            let mut selections = this.selections.all::<MultiBufferPoint>(&display_map);
10817            for selection in &mut selections {
10818                if selection.is_empty() {
10819                    let old_head = selection.head();
10820                    let mut new_head =
10821                        movement::left(&display_map, old_head.to_display_point(&display_map))
10822                            .to_point(&display_map);
10823                    if let Some((buffer, line_buffer_range)) = display_map
10824                        .buffer_snapshot()
10825                        .buffer_line_for_row(MultiBufferRow(old_head.row))
10826                    {
10827                        let indent_size = buffer.indent_size_for_line(line_buffer_range.start.row);
10828                        let indent_len = match indent_size.kind {
10829                            IndentKind::Space => {
10830                                buffer.settings_at(line_buffer_range.start, cx).tab_size
10831                            }
10832                            IndentKind::Tab => NonZeroU32::new(1).unwrap(),
10833                        };
10834                        if old_head.column <= indent_size.len && old_head.column > 0 {
10835                            let indent_len = indent_len.get();
10836                            new_head = cmp::min(
10837                                new_head,
10838                                MultiBufferPoint::new(
10839                                    old_head.row,
10840                                    ((old_head.column - 1) / indent_len) * indent_len,
10841                                ),
10842                            );
10843                        }
10844                    }
10845
10846                    selection.set_head(new_head, SelectionGoal::None);
10847                }
10848            }
10849
10850            this.change_selections(Default::default(), window, cx, |s| s.select(selections));
10851            this.insert("", window, cx);
10852            linked_edits.apply_with_left_expansion(cx);
10853            this.refresh_edit_prediction(true, false, window, cx);
10854            refresh_linked_ranges(this, window, cx);
10855        });
10856    }
10857
10858    pub fn delete(&mut self, _: &Delete, window: &mut Window, cx: &mut Context<Self>) {
10859        if self.read_only(cx) {
10860            return;
10861        }
10862        self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
10863        self.transact(window, cx, |this, window, cx| {
10864            this.change_selections(Default::default(), window, cx, |s| {
10865                s.move_with(&mut |map, selection| {
10866                    if selection.is_empty() {
10867                        let cursor = movement::right(map, selection.head());
10868                        selection.end = cursor;
10869                        selection.reversed = true;
10870                        selection.goal = SelectionGoal::None;
10871                    }
10872                })
10873            });
10874            let linked_edits = this.linked_edits_for_selections(Arc::from(""), cx);
10875            this.insert("", window, cx);
10876            linked_edits.apply(cx);
10877            this.refresh_edit_prediction(true, false, window, cx);
10878            refresh_linked_ranges(this, window, cx);
10879        });
10880    }
10881
10882    pub fn backtab(&mut self, _: &Backtab, window: &mut Window, cx: &mut Context<Self>) {
10883        if self.mode.is_single_line() {
10884            cx.propagate();
10885            return;
10886        }
10887
10888        self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
10889        if self.move_to_prev_snippet_tabstop(window, cx) {
10890            return;
10891        }
10892        self.outdent(&Outdent, window, cx);
10893    }
10894
10895    pub fn next_snippet_tabstop(
10896        &mut self,
10897        _: &NextSnippetTabstop,
10898        window: &mut Window,
10899        cx: &mut Context<Self>,
10900    ) {
10901        if self.mode.is_single_line() || self.snippet_stack.is_empty() {
10902            cx.propagate();
10903            return;
10904        }
10905
10906        if self.move_to_next_snippet_tabstop(window, cx) {
10907            self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
10908            return;
10909        }
10910        cx.propagate();
10911    }
10912
10913    pub fn previous_snippet_tabstop(
10914        &mut self,
10915        _: &PreviousSnippetTabstop,
10916        window: &mut Window,
10917        cx: &mut Context<Self>,
10918    ) {
10919        if self.mode.is_single_line() || self.snippet_stack.is_empty() {
10920            cx.propagate();
10921            return;
10922        }
10923
10924        if self.move_to_prev_snippet_tabstop(window, cx) {
10925            self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
10926            return;
10927        }
10928        cx.propagate();
10929    }
10930
10931    pub fn tab(&mut self, _: &Tab, window: &mut Window, cx: &mut Context<Self>) {
10932        if self.mode.is_single_line() {
10933            cx.propagate();
10934            return;
10935        }
10936
10937        if self.move_to_next_snippet_tabstop(window, cx) {
10938            self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
10939            return;
10940        }
10941        if self.read_only(cx) {
10942            return;
10943        }
10944        self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
10945        let mut selections = self.selections.all_adjusted(&self.display_snapshot(cx));
10946        let buffer = self.buffer.read(cx);
10947        let snapshot = buffer.snapshot(cx);
10948        let rows_iter = selections.iter().map(|s| s.head().row);
10949        let suggested_indents = snapshot.suggested_indents(rows_iter, cx);
10950
10951        let has_some_cursor_in_whitespace = selections
10952            .iter()
10953            .filter(|selection| selection.is_empty())
10954            .any(|selection| {
10955                let cursor = selection.head();
10956                let current_indent = snapshot.indent_size_for_line(MultiBufferRow(cursor.row));
10957                cursor.column < current_indent.len
10958            });
10959
10960        let mut edits = Vec::new();
10961        let mut prev_edited_row = 0;
10962        let mut row_delta = 0;
10963        for selection in &mut selections {
10964            if selection.start.row != prev_edited_row {
10965                row_delta = 0;
10966            }
10967            prev_edited_row = selection.end.row;
10968
10969            // If cursor is after a list prefix, make selection non-empty to trigger line indent
10970            if selection.is_empty() {
10971                let cursor = selection.head();
10972                let settings = buffer.language_settings_at(cursor, cx);
10973                if settings.indent_list_on_tab {
10974                    if let Some(language) = snapshot.language_scope_at(Point::new(cursor.row, 0)) {
10975                        if is_list_prefix_row(MultiBufferRow(cursor.row), &snapshot, &language) {
10976                            row_delta = Self::indent_selection(
10977                                buffer, &snapshot, selection, &mut edits, row_delta, cx,
10978                            );
10979                            continue;
10980                        }
10981                    }
10982                }
10983            }
10984
10985            // If the selection is non-empty, then increase the indentation of the selected lines.
10986            if !selection.is_empty() {
10987                row_delta =
10988                    Self::indent_selection(buffer, &snapshot, selection, &mut edits, row_delta, cx);
10989                continue;
10990            }
10991
10992            let cursor = selection.head();
10993            let current_indent = snapshot.indent_size_for_line(MultiBufferRow(cursor.row));
10994            if let Some(suggested_indent) =
10995                suggested_indents.get(&MultiBufferRow(cursor.row)).copied()
10996            {
10997                // Don't do anything if already at suggested indent
10998                // and there is any other cursor which is not
10999                if has_some_cursor_in_whitespace
11000                    && cursor.column == current_indent.len
11001                    && current_indent.len == suggested_indent.len
11002                {
11003                    continue;
11004                }
11005
11006                // Adjust line and move cursor to suggested indent
11007                // if cursor is not at suggested indent
11008                if cursor.column < suggested_indent.len
11009                    && cursor.column <= current_indent.len
11010                    && current_indent.len <= suggested_indent.len
11011                {
11012                    selection.start = Point::new(cursor.row, suggested_indent.len);
11013                    selection.end = selection.start;
11014                    if row_delta == 0 {
11015                        edits.extend(Buffer::edit_for_indent_size_adjustment(
11016                            cursor.row,
11017                            current_indent,
11018                            suggested_indent,
11019                        ));
11020                        row_delta = suggested_indent.len - current_indent.len;
11021                    }
11022                    continue;
11023                }
11024
11025                // If current indent is more than suggested indent
11026                // only move cursor to current indent and skip indent
11027                if cursor.column < current_indent.len && current_indent.len > suggested_indent.len {
11028                    selection.start = Point::new(cursor.row, current_indent.len);
11029                    selection.end = selection.start;
11030                    continue;
11031                }
11032            }
11033
11034            // Otherwise, insert a hard or soft tab.
11035            let settings = buffer.language_settings_at(cursor, cx);
11036            let tab_size = if settings.hard_tabs {
11037                IndentSize::tab()
11038            } else {
11039                let tab_size = settings.tab_size.get();
11040                let indent_remainder = snapshot
11041                    .text_for_range(Point::new(cursor.row, 0)..cursor)
11042                    .flat_map(str::chars)
11043                    .fold(row_delta % tab_size, |counter: u32, c| {
11044                        if c == '\t' {
11045                            0
11046                        } else {
11047                            (counter + 1) % tab_size
11048                        }
11049                    });
11050
11051                let chars_to_next_tab_stop = tab_size - indent_remainder;
11052                IndentSize::spaces(chars_to_next_tab_stop)
11053            };
11054            selection.start = Point::new(cursor.row, cursor.column + row_delta + tab_size.len);
11055            selection.end = selection.start;
11056            edits.push((cursor..cursor, tab_size.chars().collect::<String>()));
11057            row_delta += tab_size.len;
11058        }
11059
11060        self.transact(window, cx, |this, window, cx| {
11061            this.buffer.update(cx, |b, cx| b.edit(edits, None, cx));
11062            this.change_selections(Default::default(), window, cx, |s| s.select(selections));
11063            this.refresh_edit_prediction(true, false, window, cx);
11064        });
11065    }
11066
11067    pub fn indent(&mut self, _: &Indent, window: &mut Window, cx: &mut Context<Self>) {
11068        if self.read_only(cx) {
11069            return;
11070        }
11071        if self.mode.is_single_line() {
11072            cx.propagate();
11073            return;
11074        }
11075
11076        self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
11077        let mut selections = self.selections.all::<Point>(&self.display_snapshot(cx));
11078        let mut prev_edited_row = 0;
11079        let mut row_delta = 0;
11080        let mut edits = Vec::new();
11081        let buffer = self.buffer.read(cx);
11082        let snapshot = buffer.snapshot(cx);
11083        for selection in &mut selections {
11084            if selection.start.row != prev_edited_row {
11085                row_delta = 0;
11086            }
11087            prev_edited_row = selection.end.row;
11088
11089            row_delta =
11090                Self::indent_selection(buffer, &snapshot, selection, &mut edits, row_delta, cx);
11091        }
11092
11093        self.transact(window, cx, |this, window, cx| {
11094            this.buffer.update(cx, |b, cx| b.edit(edits, None, cx));
11095            this.change_selections(Default::default(), window, cx, |s| s.select(selections));
11096        });
11097    }
11098
11099    fn indent_selection(
11100        buffer: &MultiBuffer,
11101        snapshot: &MultiBufferSnapshot,
11102        selection: &mut Selection<Point>,
11103        edits: &mut Vec<(Range<Point>, String)>,
11104        delta_for_start_row: u32,
11105        cx: &App,
11106    ) -> u32 {
11107        let settings = buffer.language_settings_at(selection.start, cx);
11108        let tab_size = settings.tab_size.get();
11109        let indent_kind = if settings.hard_tabs {
11110            IndentKind::Tab
11111        } else {
11112            IndentKind::Space
11113        };
11114        let mut start_row = selection.start.row;
11115        let mut end_row = selection.end.row + 1;
11116
11117        // If a selection ends at the beginning of a line, don't indent
11118        // that last line.
11119        if selection.end.column == 0 && selection.end.row > selection.start.row {
11120            end_row -= 1;
11121        }
11122
11123        // Avoid re-indenting a row that has already been indented by a
11124        // previous selection, but still update this selection's column
11125        // to reflect that indentation.
11126        if delta_for_start_row > 0 {
11127            start_row += 1;
11128            selection.start.column += delta_for_start_row;
11129            if selection.end.row == selection.start.row {
11130                selection.end.column += delta_for_start_row;
11131            }
11132        }
11133
11134        let mut delta_for_end_row = 0;
11135        let has_multiple_rows = start_row + 1 != end_row;
11136        for row in start_row..end_row {
11137            let current_indent = snapshot.indent_size_for_line(MultiBufferRow(row));
11138            let indent_delta = match (current_indent.kind, indent_kind) {
11139                (IndentKind::Space, IndentKind::Space) => {
11140                    let columns_to_next_tab_stop = tab_size - (current_indent.len % tab_size);
11141                    IndentSize::spaces(columns_to_next_tab_stop)
11142                }
11143                (IndentKind::Tab, IndentKind::Space) => IndentSize::spaces(tab_size),
11144                (_, IndentKind::Tab) => IndentSize::tab(),
11145            };
11146
11147            let start = if has_multiple_rows || current_indent.len < selection.start.column {
11148                0
11149            } else {
11150                selection.start.column
11151            };
11152            let row_start = Point::new(row, start);
11153            edits.push((
11154                row_start..row_start,
11155                indent_delta.chars().collect::<String>(),
11156            ));
11157
11158            // Update this selection's endpoints to reflect the indentation.
11159            if row == selection.start.row {
11160                selection.start.column += indent_delta.len;
11161            }
11162            if row == selection.end.row {
11163                selection.end.column += indent_delta.len;
11164                delta_for_end_row = indent_delta.len;
11165            }
11166        }
11167
11168        if selection.start.row == selection.end.row {
11169            delta_for_start_row + delta_for_end_row
11170        } else {
11171            delta_for_end_row
11172        }
11173    }
11174
11175    pub fn outdent(&mut self, _: &Outdent, window: &mut Window, cx: &mut Context<Self>) {
11176        if self.read_only(cx) {
11177            return;
11178        }
11179        if self.mode.is_single_line() {
11180            cx.propagate();
11181            return;
11182        }
11183
11184        self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
11185        let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
11186        let selections = self.selections.all::<Point>(&display_map);
11187        let mut deletion_ranges = Vec::new();
11188        let mut last_outdent = None;
11189        {
11190            let buffer = self.buffer.read(cx);
11191            let snapshot = buffer.snapshot(cx);
11192            for selection in &selections {
11193                let settings = buffer.language_settings_at(selection.start, cx);
11194                let tab_size = settings.tab_size.get();
11195                let mut rows = selection.spanned_rows(false, &display_map);
11196
11197                // Avoid re-outdenting a row that has already been outdented by a
11198                // previous selection.
11199                if let Some(last_row) = last_outdent
11200                    && last_row == rows.start
11201                {
11202                    rows.start = rows.start.next_row();
11203                }
11204                let has_multiple_rows = rows.len() > 1;
11205                for row in rows.iter_rows() {
11206                    let indent_size = snapshot.indent_size_for_line(row);
11207                    if indent_size.len > 0 {
11208                        let deletion_len = match indent_size.kind {
11209                            IndentKind::Space => {
11210                                let columns_to_prev_tab_stop = indent_size.len % tab_size;
11211                                if columns_to_prev_tab_stop == 0 {
11212                                    tab_size
11213                                } else {
11214                                    columns_to_prev_tab_stop
11215                                }
11216                            }
11217                            IndentKind::Tab => 1,
11218                        };
11219                        let start = if has_multiple_rows
11220                            || deletion_len > selection.start.column
11221                            || indent_size.len < selection.start.column
11222                        {
11223                            0
11224                        } else {
11225                            selection.start.column - deletion_len
11226                        };
11227                        deletion_ranges.push(
11228                            Point::new(row.0, start)..Point::new(row.0, start + deletion_len),
11229                        );
11230                        last_outdent = Some(row);
11231                    }
11232                }
11233            }
11234        }
11235
11236        self.transact(window, cx, |this, window, cx| {
11237            this.buffer.update(cx, |buffer, cx| {
11238                let empty_str: Arc<str> = Arc::default();
11239                buffer.edit(
11240                    deletion_ranges
11241                        .into_iter()
11242                        .map(|range| (range, empty_str.clone())),
11243                    None,
11244                    cx,
11245                );
11246            });
11247            let selections = this
11248                .selections
11249                .all::<MultiBufferOffset>(&this.display_snapshot(cx));
11250            this.change_selections(Default::default(), window, cx, |s| s.select(selections));
11251        });
11252    }
11253
11254    pub fn autoindent(&mut self, _: &AutoIndent, window: &mut Window, cx: &mut Context<Self>) {
11255        if self.read_only(cx) {
11256            return;
11257        }
11258        if self.mode.is_single_line() {
11259            cx.propagate();
11260            return;
11261        }
11262
11263        self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
11264        let selections = self
11265            .selections
11266            .all::<MultiBufferOffset>(&self.display_snapshot(cx))
11267            .into_iter()
11268            .map(|s| s.range());
11269
11270        self.transact(window, cx, |this, window, cx| {
11271            this.buffer.update(cx, |buffer, cx| {
11272                buffer.autoindent_ranges(selections, cx);
11273            });
11274            let selections = this
11275                .selections
11276                .all::<MultiBufferOffset>(&this.display_snapshot(cx));
11277            this.change_selections(Default::default(), window, cx, |s| s.select(selections));
11278        });
11279    }
11280
11281    pub fn delete_line(&mut self, _: &DeleteLine, window: &mut Window, cx: &mut Context<Self>) {
11282        self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
11283        let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
11284        let selections = self.selections.all::<Point>(&display_map);
11285
11286        let mut new_cursors = Vec::new();
11287        let mut edit_ranges = Vec::new();
11288        let mut selections = selections.iter().peekable();
11289        while let Some(selection) = selections.next() {
11290            let mut rows = selection.spanned_rows(false, &display_map);
11291
11292            // Accumulate contiguous regions of rows that we want to delete.
11293            while let Some(next_selection) = selections.peek() {
11294                let next_rows = next_selection.spanned_rows(false, &display_map);
11295                if next_rows.start <= rows.end {
11296                    rows.end = next_rows.end;
11297                    selections.next().unwrap();
11298                } else {
11299                    break;
11300                }
11301            }
11302
11303            let buffer = display_map.buffer_snapshot();
11304            let mut edit_start = ToOffset::to_offset(&Point::new(rows.start.0, 0), buffer);
11305            let (edit_end, target_row) = if buffer.max_point().row >= rows.end.0 {
11306                // If there's a line after the range, delete the \n from the end of the row range
11307                (
11308                    ToOffset::to_offset(&Point::new(rows.end.0, 0), buffer),
11309                    rows.end,
11310                )
11311            } else {
11312                // If there isn't a line after the range, delete the \n from the line before the
11313                // start of the row range
11314                edit_start = edit_start.saturating_sub_usize(1);
11315                (buffer.len(), rows.start.previous_row())
11316            };
11317
11318            let text_layout_details = self.text_layout_details(window, cx);
11319            let x = display_map.x_for_display_point(
11320                selection.head().to_display_point(&display_map),
11321                &text_layout_details,
11322            );
11323            let row = Point::new(target_row.0, 0)
11324                .to_display_point(&display_map)
11325                .row();
11326            let column = display_map.display_column_for_x(row, x, &text_layout_details);
11327
11328            new_cursors.push((
11329                selection.id,
11330                buffer.anchor_after(DisplayPoint::new(row, column).to_point(&display_map)),
11331                SelectionGoal::None,
11332            ));
11333            edit_ranges.push(edit_start..edit_end);
11334        }
11335
11336        self.transact(window, cx, |this, window, cx| {
11337            let buffer = this.buffer.update(cx, |buffer, cx| {
11338                let empty_str: Arc<str> = Arc::default();
11339                buffer.edit(
11340                    edit_ranges
11341                        .into_iter()
11342                        .map(|range| (range, empty_str.clone())),
11343                    None,
11344                    cx,
11345                );
11346                buffer.snapshot(cx)
11347            });
11348            let new_selections = new_cursors
11349                .into_iter()
11350                .map(|(id, cursor, goal)| {
11351                    let cursor = cursor.to_point(&buffer);
11352                    Selection {
11353                        id,
11354                        start: cursor,
11355                        end: cursor,
11356                        reversed: false,
11357                        goal,
11358                    }
11359                })
11360                .collect();
11361
11362            this.change_selections(Default::default(), window, cx, |s| {
11363                s.select(new_selections);
11364            });
11365        });
11366    }
11367
11368    pub fn join_lines_impl(
11369        &mut self,
11370        insert_whitespace: bool,
11371        window: &mut Window,
11372        cx: &mut Context<Self>,
11373    ) {
11374        if self.read_only(cx) {
11375            return;
11376        }
11377        let mut row_ranges = Vec::<Range<MultiBufferRow>>::new();
11378        for selection in self.selections.all::<Point>(&self.display_snapshot(cx)) {
11379            let start = MultiBufferRow(selection.start.row);
11380            // Treat single line selections as if they include the next line. Otherwise this action
11381            // would do nothing for single line selections individual cursors.
11382            let end = if selection.start.row == selection.end.row {
11383                MultiBufferRow(selection.start.row + 1)
11384            } else if selection.end.column == 0 {
11385                // If the selection ends at the start of a line, it's logically at the end of the
11386                // previous line (plus its newline).
11387                // Don't include the end line unless there's only one line selected.
11388                if selection.start.row + 1 == selection.end.row {
11389                    MultiBufferRow(selection.end.row)
11390                } else {
11391                    MultiBufferRow(selection.end.row - 1)
11392                }
11393            } else {
11394                MultiBufferRow(selection.end.row)
11395            };
11396
11397            if let Some(last_row_range) = row_ranges.last_mut()
11398                && start <= last_row_range.end
11399            {
11400                last_row_range.end = end;
11401                continue;
11402            }
11403            row_ranges.push(start..end);
11404        }
11405
11406        let snapshot = self.buffer.read(cx).snapshot(cx);
11407        let mut cursor_positions = Vec::new();
11408        for row_range in &row_ranges {
11409            let anchor = snapshot.anchor_before(Point::new(
11410                row_range.end.previous_row().0,
11411                snapshot.line_len(row_range.end.previous_row()),
11412            ));
11413            cursor_positions.push(anchor..anchor);
11414        }
11415
11416        self.transact(window, cx, |this, window, cx| {
11417            for row_range in row_ranges.into_iter().rev() {
11418                for row in row_range.iter_rows().rev() {
11419                    let end_of_line = Point::new(row.0, snapshot.line_len(row));
11420                    let next_line_row = row.next_row();
11421                    let indent = snapshot.indent_size_for_line(next_line_row);
11422                    let mut join_start_column = indent.len;
11423
11424                    if let Some(language_scope) =
11425                        snapshot.language_scope_at(Point::new(next_line_row.0, indent.len))
11426                    {
11427                        let line_end =
11428                            Point::new(next_line_row.0, snapshot.line_len(next_line_row));
11429                        let line_text_after_indent = snapshot
11430                            .text_for_range(Point::new(next_line_row.0, indent.len)..line_end)
11431                            .collect::<String>();
11432
11433                        if !line_text_after_indent.is_empty() {
11434                            let block_prefix = language_scope
11435                                .block_comment()
11436                                .map(|c| c.prefix.as_ref())
11437                                .filter(|p| !p.is_empty());
11438                            let doc_prefix = language_scope
11439                                .documentation_comment()
11440                                .map(|c| c.prefix.as_ref())
11441                                .filter(|p| !p.is_empty());
11442                            let all_prefixes = language_scope
11443                                .line_comment_prefixes()
11444                                .iter()
11445                                .map(|p| p.as_ref())
11446                                .chain(block_prefix)
11447                                .chain(doc_prefix)
11448                                .chain(language_scope.unordered_list().iter().map(|p| p.as_ref()));
11449
11450                            let mut longest_prefix_len = None;
11451                            for prefix in all_prefixes {
11452                                let trimmed = prefix.trim_end();
11453                                if line_text_after_indent.starts_with(trimmed) {
11454                                    let candidate_len =
11455                                        if line_text_after_indent.starts_with(prefix) {
11456                                            prefix.len()
11457                                        } else {
11458                                            trimmed.len()
11459                                        };
11460                                    if longest_prefix_len.map_or(true, |len| candidate_len > len) {
11461                                        longest_prefix_len = Some(candidate_len);
11462                                    }
11463                                }
11464                            }
11465
11466                            if let Some(prefix_len) = longest_prefix_len {
11467                                join_start_column =
11468                                    join_start_column.saturating_add(prefix_len as u32);
11469                            }
11470                        }
11471                    }
11472
11473                    let start_of_next_line = Point::new(next_line_row.0, join_start_column);
11474
11475                    let replace = if snapshot.line_len(next_line_row) > join_start_column
11476                        && insert_whitespace
11477                    {
11478                        " "
11479                    } else {
11480                        ""
11481                    };
11482
11483                    this.buffer.update(cx, |buffer, cx| {
11484                        buffer.edit([(end_of_line..start_of_next_line, replace)], None, cx)
11485                    });
11486                }
11487            }
11488
11489            this.change_selections(Default::default(), window, cx, |s| {
11490                s.select_anchor_ranges(cursor_positions)
11491            });
11492        });
11493    }
11494
11495    pub fn join_lines(&mut self, _: &JoinLines, window: &mut Window, cx: &mut Context<Self>) {
11496        self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
11497        self.join_lines_impl(true, window, cx);
11498    }
11499
11500    pub fn sort_lines_case_sensitive(
11501        &mut self,
11502        _: &SortLinesCaseSensitive,
11503        window: &mut Window,
11504        cx: &mut Context<Self>,
11505    ) {
11506        self.manipulate_immutable_lines(window, cx, |lines| lines.sort())
11507    }
11508
11509    pub fn sort_lines_by_length(
11510        &mut self,
11511        _: &SortLinesByLength,
11512        window: &mut Window,
11513        cx: &mut Context<Self>,
11514    ) {
11515        self.manipulate_immutable_lines(window, cx, |lines| {
11516            lines.sort_by_key(|&line| line.chars().count())
11517        })
11518    }
11519
11520    pub fn sort_lines_case_insensitive(
11521        &mut self,
11522        _: &SortLinesCaseInsensitive,
11523        window: &mut Window,
11524        cx: &mut Context<Self>,
11525    ) {
11526        self.manipulate_immutable_lines(window, cx, |lines| {
11527            lines.sort_by_key(|line| line.to_lowercase())
11528        })
11529    }
11530
11531    pub fn unique_lines_case_insensitive(
11532        &mut self,
11533        _: &UniqueLinesCaseInsensitive,
11534        window: &mut Window,
11535        cx: &mut Context<Self>,
11536    ) {
11537        self.manipulate_immutable_lines(window, cx, |lines| {
11538            let mut seen = HashSet::default();
11539            lines.retain(|line| seen.insert(line.to_lowercase()));
11540        })
11541    }
11542
11543    pub fn unique_lines_case_sensitive(
11544        &mut self,
11545        _: &UniqueLinesCaseSensitive,
11546        window: &mut Window,
11547        cx: &mut Context<Self>,
11548    ) {
11549        self.manipulate_immutable_lines(window, cx, |lines| {
11550            let mut seen = HashSet::default();
11551            lines.retain(|line| seen.insert(*line));
11552        })
11553    }
11554
11555    fn enable_wrap_selections_in_tag(&self, cx: &App) -> bool {
11556        let snapshot = self.buffer.read(cx).snapshot(cx);
11557        for selection in self.selections.disjoint_anchors_arc().iter() {
11558            if snapshot
11559                .language_at(selection.start)
11560                .and_then(|lang| lang.config().wrap_characters.as_ref())
11561                .is_some()
11562            {
11563                return true;
11564            }
11565        }
11566        false
11567    }
11568
11569    fn wrap_selections_in_tag(
11570        &mut self,
11571        _: &WrapSelectionsInTag,
11572        window: &mut Window,
11573        cx: &mut Context<Self>,
11574    ) {
11575        self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
11576
11577        let snapshot = self.buffer.read(cx).snapshot(cx);
11578
11579        let mut edits = Vec::new();
11580        let mut boundaries = Vec::new();
11581
11582        for selection in self
11583            .selections
11584            .all_adjusted(&self.display_snapshot(cx))
11585            .iter()
11586        {
11587            let Some(wrap_config) = snapshot
11588                .language_at(selection.start)
11589                .and_then(|lang| lang.config().wrap_characters.clone())
11590            else {
11591                continue;
11592            };
11593
11594            let open_tag = format!("{}{}", wrap_config.start_prefix, wrap_config.start_suffix);
11595            let close_tag = format!("{}{}", wrap_config.end_prefix, wrap_config.end_suffix);
11596
11597            let start_before = snapshot.anchor_before(selection.start);
11598            let end_after = snapshot.anchor_after(selection.end);
11599
11600            edits.push((start_before..start_before, open_tag));
11601            edits.push((end_after..end_after, close_tag));
11602
11603            boundaries.push((
11604                start_before,
11605                end_after,
11606                wrap_config.start_prefix.len(),
11607                wrap_config.end_suffix.len(),
11608            ));
11609        }
11610
11611        if edits.is_empty() {
11612            return;
11613        }
11614
11615        self.transact(window, cx, |this, window, cx| {
11616            let buffer = this.buffer.update(cx, |buffer, cx| {
11617                buffer.edit(edits, None, cx);
11618                buffer.snapshot(cx)
11619            });
11620
11621            let mut new_selections = Vec::with_capacity(boundaries.len() * 2);
11622            for (start_before, end_after, start_prefix_len, end_suffix_len) in
11623                boundaries.into_iter()
11624            {
11625                let open_offset = start_before.to_offset(&buffer) + start_prefix_len;
11626                let close_offset = end_after
11627                    .to_offset(&buffer)
11628                    .saturating_sub_usize(end_suffix_len);
11629                new_selections.push(open_offset..open_offset);
11630                new_selections.push(close_offset..close_offset);
11631            }
11632
11633            this.change_selections(Default::default(), window, cx, |s| {
11634                s.select_ranges(new_selections);
11635            });
11636
11637            this.request_autoscroll(Autoscroll::fit(), cx);
11638        });
11639    }
11640
11641    pub fn toggle_read_only(
11642        &mut self,
11643        _: &workspace::ToggleReadOnlyFile,
11644        _: &mut Window,
11645        cx: &mut Context<Self>,
11646    ) {
11647        if let Some(buffer) = self.buffer.read(cx).as_singleton() {
11648            buffer.update(cx, |buffer, cx| {
11649                buffer.set_capability(
11650                    match buffer.capability() {
11651                        Capability::ReadWrite => Capability::Read,
11652                        Capability::Read => Capability::ReadWrite,
11653                        Capability::ReadOnly => Capability::ReadOnly,
11654                    },
11655                    cx,
11656                );
11657            })
11658        }
11659    }
11660
11661    pub fn reload_file(&mut self, _: &ReloadFile, window: &mut Window, cx: &mut Context<Self>) {
11662        let Some(project) = self.project.clone() else {
11663            return;
11664        };
11665        let task = self.reload(project, window, cx);
11666        self.detach_and_notify_err(task, window, cx);
11667    }
11668
11669    pub fn restore_file(
11670        &mut self,
11671        _: &::git::RestoreFile,
11672        window: &mut Window,
11673        cx: &mut Context<Self>,
11674    ) {
11675        self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
11676        let mut buffer_ids = HashSet::default();
11677        let snapshot = self.buffer().read(cx).snapshot(cx);
11678        for selection in self
11679            .selections
11680            .all::<MultiBufferOffset>(&self.display_snapshot(cx))
11681        {
11682            buffer_ids.extend(snapshot.buffer_ids_for_range(selection.range()))
11683        }
11684
11685        let buffer = self.buffer().read(cx);
11686        let ranges = buffer_ids
11687            .into_iter()
11688            .flat_map(|buffer_id| buffer.excerpt_ranges_for_buffer(buffer_id, cx))
11689            .collect::<Vec<_>>();
11690
11691        self.restore_hunks_in_ranges(ranges, window, cx);
11692    }
11693
11694    pub fn git_restore(&mut self, _: &Restore, window: &mut Window, cx: &mut Context<Self>) {
11695        self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
11696        let selections = self
11697            .selections
11698            .all(&self.display_snapshot(cx))
11699            .into_iter()
11700            .map(|s| s.range())
11701            .collect();
11702        self.restore_hunks_in_ranges(selections, window, cx);
11703    }
11704
11705    /// Restores the diff hunks in the editor's selections and moves the cursor
11706    /// to the next diff hunk. Wraps around to the beginning of the buffer if
11707    /// not all diff hunks are expanded.
11708    pub fn restore_and_next(
11709        &mut self,
11710        _: &::git::RestoreAndNext,
11711        window: &mut Window,
11712        cx: &mut Context<Self>,
11713    ) {
11714        let selections = self
11715            .selections
11716            .all(&self.display_snapshot(cx))
11717            .into_iter()
11718            .map(|selection| selection.range())
11719            .collect();
11720
11721        self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
11722        self.restore_hunks_in_ranges(selections, window, cx);
11723
11724        let all_diff_hunks_expanded = self.buffer().read(cx).all_diff_hunks_expanded();
11725        let wrap_around = !all_diff_hunks_expanded;
11726        let snapshot = self.snapshot(window, cx);
11727        let position = self
11728            .selections
11729            .newest::<Point>(&snapshot.display_snapshot)
11730            .head();
11731
11732        self.go_to_hunk_before_or_after_position(
11733            &snapshot,
11734            position,
11735            Direction::Next,
11736            wrap_around,
11737            window,
11738            cx,
11739        );
11740    }
11741
11742    pub fn restore_hunks_in_ranges(
11743        &mut self,
11744        ranges: Vec<Range<Point>>,
11745        window: &mut Window,
11746        cx: &mut Context<Editor>,
11747    ) {
11748        if self.delegate_stage_and_restore {
11749            let hunks = self.snapshot(window, cx).hunks_for_ranges(ranges);
11750            if !hunks.is_empty() {
11751                cx.emit(EditorEvent::RestoreRequested { hunks });
11752            }
11753            return;
11754        }
11755        let hunks = self.snapshot(window, cx).hunks_for_ranges(ranges);
11756        self.transact(window, cx, |editor, window, cx| {
11757            editor.restore_diff_hunks(hunks, cx);
11758            editor.change_selections(SelectionEffects::no_scroll(), window, cx, |selections| {
11759                selections.refresh()
11760            });
11761        });
11762    }
11763
11764    pub(crate) fn restore_diff_hunks(&self, hunks: Vec<MultiBufferDiffHunk>, cx: &mut App) {
11765        let mut revert_changes = HashMap::default();
11766        let chunk_by = hunks.into_iter().chunk_by(|hunk| hunk.buffer_id);
11767        for (buffer_id, hunks) in &chunk_by {
11768            let hunks = hunks.collect::<Vec<_>>();
11769            for hunk in &hunks {
11770                self.prepare_restore_change(&mut revert_changes, hunk, cx);
11771            }
11772            self.do_stage_or_unstage(false, buffer_id, hunks.into_iter(), cx);
11773        }
11774        if !revert_changes.is_empty() {
11775            self.buffer().update(cx, |multi_buffer, cx| {
11776                for (buffer_id, changes) in revert_changes {
11777                    if let Some(buffer) = multi_buffer.buffer(buffer_id) {
11778                        buffer.update(cx, |buffer, cx| {
11779                            buffer.edit(
11780                                changes
11781                                    .into_iter()
11782                                    .map(|(range, text)| (range, text.to_string())),
11783                                None,
11784                                cx,
11785                            );
11786                        });
11787                    }
11788                }
11789            });
11790        }
11791    }
11792
11793    pub fn status_for_buffer_id(&self, buffer_id: BufferId, cx: &App) -> Option<FileStatus> {
11794        if let Some(status) = self
11795            .addons
11796            .iter()
11797            .find_map(|(_, addon)| addon.override_status_for_buffer_id(buffer_id, cx))
11798        {
11799            return Some(status);
11800        }
11801        self.project
11802            .as_ref()?
11803            .read(cx)
11804            .status_for_buffer_id(buffer_id, cx)
11805    }
11806
11807    pub fn open_active_item_in_terminal(
11808        &mut self,
11809        _: &OpenInTerminal,
11810        window: &mut Window,
11811        cx: &mut Context<Self>,
11812    ) {
11813        if let Some(working_directory) = self.active_excerpt(cx).and_then(|(_, buffer, _)| {
11814            let project_path = buffer.read(cx).project_path(cx)?;
11815            let project = self.project()?.read(cx);
11816            let entry = project.entry_for_path(&project_path, cx)?;
11817            let parent = match &entry.canonical_path {
11818                Some(canonical_path) => canonical_path.to_path_buf(),
11819                None => project.absolute_path(&project_path, cx)?,
11820            }
11821            .parent()?
11822            .to_path_buf();
11823            Some(parent)
11824        }) {
11825            window.dispatch_action(
11826                OpenTerminal {
11827                    working_directory,
11828                    local: false,
11829                }
11830                .boxed_clone(),
11831                cx,
11832            );
11833        }
11834    }
11835
11836    fn set_breakpoint_context_menu(
11837        &mut self,
11838        display_row: DisplayRow,
11839        position: Option<Anchor>,
11840        clicked_point: gpui::Point<Pixels>,
11841        window: &mut Window,
11842        cx: &mut Context<Self>,
11843    ) {
11844        let source = self
11845            .buffer
11846            .read(cx)
11847            .snapshot(cx)
11848            .anchor_before(Point::new(display_row.0, 0u32));
11849
11850        let context_menu = self.breakpoint_context_menu(position.unwrap_or(source), window, cx);
11851
11852        self.mouse_context_menu = MouseContextMenu::pinned_to_editor(
11853            self,
11854            source,
11855            clicked_point,
11856            context_menu,
11857            window,
11858            cx,
11859        );
11860    }
11861
11862    fn add_edit_breakpoint_block(
11863        &mut self,
11864        anchor: Anchor,
11865        breakpoint: &Breakpoint,
11866        edit_action: BreakpointPromptEditAction,
11867        window: &mut Window,
11868        cx: &mut Context<Self>,
11869    ) {
11870        let weak_editor = cx.weak_entity();
11871        let bp_prompt = cx.new(|cx| {
11872            BreakpointPromptEditor::new(
11873                weak_editor,
11874                anchor,
11875                breakpoint.clone(),
11876                edit_action,
11877                window,
11878                cx,
11879            )
11880        });
11881
11882        let height = bp_prompt.update(cx, |this, cx| {
11883            this.prompt
11884                .update(cx, |prompt, cx| prompt.max_point(cx).row().0 + 1 + 2)
11885        });
11886        let cloned_prompt = bp_prompt.clone();
11887        let blocks = vec![BlockProperties {
11888            style: BlockStyle::Sticky,
11889            placement: BlockPlacement::Above(anchor),
11890            height: Some(height),
11891            render: Arc::new(move |cx| {
11892                *cloned_prompt.read(cx).editor_margins.lock() = *cx.margins;
11893                cloned_prompt.clone().into_any_element()
11894            }),
11895            priority: 0,
11896        }];
11897
11898        let focus_handle = bp_prompt.focus_handle(cx);
11899        window.focus(&focus_handle, cx);
11900
11901        let block_ids = self.insert_blocks(blocks, None, cx);
11902        bp_prompt.update(cx, |prompt, _| {
11903            prompt.add_block_ids(block_ids);
11904        });
11905    }
11906
11907    pub(crate) fn breakpoint_at_row(
11908        &self,
11909        row: u32,
11910        window: &mut Window,
11911        cx: &mut Context<Self>,
11912    ) -> Option<(Anchor, Breakpoint)> {
11913        let snapshot = self.snapshot(window, cx);
11914        let breakpoint_position = snapshot.buffer_snapshot().anchor_before(Point::new(row, 0));
11915
11916        self.breakpoint_at_anchor(breakpoint_position, &snapshot, cx)
11917    }
11918
11919    pub(crate) fn breakpoint_at_anchor(
11920        &self,
11921        breakpoint_position: Anchor,
11922        snapshot: &EditorSnapshot,
11923        cx: &mut Context<Self>,
11924    ) -> Option<(Anchor, Breakpoint)> {
11925        let buffer = self
11926            .buffer
11927            .read(cx)
11928            .buffer_for_anchor(breakpoint_position, cx)?;
11929
11930        let enclosing_excerpt = breakpoint_position.excerpt_id;
11931        let buffer_snapshot = buffer.read(cx).snapshot();
11932
11933        let row = buffer_snapshot
11934            .summary_for_anchor::<text::PointUtf16>(&breakpoint_position.text_anchor)
11935            .row;
11936
11937        let line_len = snapshot.buffer_snapshot().line_len(MultiBufferRow(row));
11938        let anchor_end = snapshot
11939            .buffer_snapshot()
11940            .anchor_after(Point::new(row, line_len));
11941
11942        self.breakpoint_store
11943            .as_ref()?
11944            .read_with(cx, |breakpoint_store, cx| {
11945                breakpoint_store
11946                    .breakpoints(
11947                        &buffer,
11948                        Some(breakpoint_position.text_anchor..anchor_end.text_anchor),
11949                        &buffer_snapshot,
11950                        cx,
11951                    )
11952                    .next()
11953                    .and_then(|(bp, _)| {
11954                        let breakpoint_row = buffer_snapshot
11955                            .summary_for_anchor::<text::PointUtf16>(&bp.position)
11956                            .row;
11957
11958                        if breakpoint_row == row {
11959                            snapshot
11960                                .buffer_snapshot()
11961                                .anchor_in_excerpt(enclosing_excerpt, bp.position)
11962                                .map(|position| (position, bp.bp.clone()))
11963                        } else {
11964                            None
11965                        }
11966                    })
11967            })
11968    }
11969
11970    pub fn edit_log_breakpoint(
11971        &mut self,
11972        _: &EditLogBreakpoint,
11973        window: &mut Window,
11974        cx: &mut Context<Self>,
11975    ) {
11976        if self.breakpoint_store.is_none() {
11977            return;
11978        }
11979
11980        for (anchor, breakpoint) in self.breakpoints_at_cursors(window, cx) {
11981            let breakpoint = breakpoint.unwrap_or_else(|| Breakpoint {
11982                message: None,
11983                state: BreakpointState::Enabled,
11984                condition: None,
11985                hit_condition: None,
11986            });
11987
11988            self.add_edit_breakpoint_block(
11989                anchor,
11990                &breakpoint,
11991                BreakpointPromptEditAction::Log,
11992                window,
11993                cx,
11994            );
11995        }
11996    }
11997
11998    fn breakpoints_at_cursors(
11999        &self,
12000        window: &mut Window,
12001        cx: &mut Context<Self>,
12002    ) -> Vec<(Anchor, Option<Breakpoint>)> {
12003        let snapshot = self.snapshot(window, cx);
12004        let cursors = self
12005            .selections
12006            .disjoint_anchors_arc()
12007            .iter()
12008            .map(|selection| {
12009                let cursor_position: Point = selection.head().to_point(&snapshot.buffer_snapshot());
12010
12011                let breakpoint_position = self
12012                    .breakpoint_at_row(cursor_position.row, window, cx)
12013                    .map(|bp| bp.0)
12014                    .unwrap_or_else(|| {
12015                        snapshot
12016                            .display_snapshot
12017                            .buffer_snapshot()
12018                            .anchor_after(Point::new(cursor_position.row, 0))
12019                    });
12020
12021                let breakpoint = self
12022                    .breakpoint_at_anchor(breakpoint_position, &snapshot, cx)
12023                    .map(|(anchor, breakpoint)| (anchor, Some(breakpoint)));
12024
12025                breakpoint.unwrap_or_else(|| (breakpoint_position, None))
12026            })
12027            // 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.
12028            .collect::<HashMap<Anchor, _>>();
12029
12030        cursors.into_iter().collect()
12031    }
12032
12033    pub fn enable_breakpoint(
12034        &mut self,
12035        _: &crate::actions::EnableBreakpoint,
12036        window: &mut Window,
12037        cx: &mut Context<Self>,
12038    ) {
12039        if self.breakpoint_store.is_none() {
12040            return;
12041        }
12042
12043        for (anchor, breakpoint) in self.breakpoints_at_cursors(window, cx) {
12044            let Some(breakpoint) = breakpoint.filter(|breakpoint| breakpoint.is_disabled()) else {
12045                continue;
12046            };
12047            self.edit_breakpoint_at_anchor(
12048                anchor,
12049                breakpoint,
12050                BreakpointEditAction::InvertState,
12051                cx,
12052            );
12053        }
12054    }
12055
12056    pub fn align_selections(
12057        &mut self,
12058        _: &crate::actions::AlignSelections,
12059        window: &mut Window,
12060        cx: &mut Context<Self>,
12061    ) {
12062        self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
12063
12064        let display_snapshot = self.display_snapshot(cx);
12065
12066        struct CursorData {
12067            anchor: Anchor,
12068            point: Point,
12069        }
12070        let cursor_data: Vec<CursorData> = self
12071            .selections
12072            .disjoint_anchors()
12073            .iter()
12074            .map(|selection| {
12075                let anchor = if selection.reversed {
12076                    selection.head()
12077                } else {
12078                    selection.tail()
12079                };
12080                CursorData {
12081                    anchor: anchor,
12082                    point: anchor.to_point(&display_snapshot.buffer_snapshot()),
12083                }
12084            })
12085            .collect();
12086
12087        let rows_anchors_count: Vec<usize> = cursor_data
12088            .iter()
12089            .map(|cursor| cursor.point.row)
12090            .chunk_by(|&row| row)
12091            .into_iter()
12092            .map(|(_, group)| group.count())
12093            .collect();
12094        let max_columns = rows_anchors_count.iter().max().copied().unwrap_or(0);
12095        let mut rows_column_offset = vec![0; rows_anchors_count.len()];
12096        let mut edits = Vec::new();
12097
12098        for column_idx in 0..max_columns {
12099            let mut cursor_index = 0;
12100
12101            // Calculate target_column => position that the selections will go
12102            let mut target_column = 0;
12103            for (row_idx, cursor_count) in rows_anchors_count.iter().enumerate() {
12104                // Skip rows that don't have this column
12105                if column_idx >= *cursor_count {
12106                    cursor_index += cursor_count;
12107                    continue;
12108                }
12109
12110                let point = &cursor_data[cursor_index + column_idx].point;
12111                let adjusted_column = point.column + rows_column_offset[row_idx];
12112                if adjusted_column > target_column {
12113                    target_column = adjusted_column;
12114                }
12115                cursor_index += cursor_count;
12116            }
12117
12118            // Collect edits for this column
12119            cursor_index = 0;
12120            for (row_idx, cursor_count) in rows_anchors_count.iter().enumerate() {
12121                // Skip rows that don't have this column
12122                if column_idx >= *cursor_count {
12123                    cursor_index += *cursor_count;
12124                    continue;
12125                }
12126
12127                let point = &cursor_data[cursor_index + column_idx].point;
12128                let spaces_needed = target_column - point.column - rows_column_offset[row_idx];
12129                if spaces_needed > 0 {
12130                    let anchor = cursor_data[cursor_index + column_idx]
12131                        .anchor
12132                        .bias_left(&display_snapshot);
12133                    edits.push((anchor..anchor, " ".repeat(spaces_needed as usize)));
12134                }
12135                rows_column_offset[row_idx] += spaces_needed;
12136
12137                cursor_index += *cursor_count;
12138            }
12139        }
12140
12141        if !edits.is_empty() {
12142            self.transact(window, cx, |editor, _window, cx| {
12143                editor.edit(edits, cx);
12144            });
12145        }
12146    }
12147
12148    pub fn disable_breakpoint(
12149        &mut self,
12150        _: &crate::actions::DisableBreakpoint,
12151        window: &mut Window,
12152        cx: &mut Context<Self>,
12153    ) {
12154        if self.breakpoint_store.is_none() {
12155            return;
12156        }
12157
12158        for (anchor, breakpoint) in self.breakpoints_at_cursors(window, cx) {
12159            let Some(breakpoint) = breakpoint.filter(|breakpoint| breakpoint.is_enabled()) else {
12160                continue;
12161            };
12162            self.edit_breakpoint_at_anchor(
12163                anchor,
12164                breakpoint,
12165                BreakpointEditAction::InvertState,
12166                cx,
12167            );
12168        }
12169    }
12170
12171    pub fn toggle_breakpoint(
12172        &mut self,
12173        _: &crate::actions::ToggleBreakpoint,
12174        window: &mut Window,
12175        cx: &mut Context<Self>,
12176    ) {
12177        if self.breakpoint_store.is_none() {
12178            return;
12179        }
12180
12181        let snapshot = self.snapshot(window, cx);
12182        for (anchor, breakpoint) in self.breakpoints_at_cursors(window, cx) {
12183            if self.gutter_breakpoint_indicator.0.is_some() {
12184                let display_row = anchor
12185                    .to_point(snapshot.buffer_snapshot())
12186                    .to_display_point(&snapshot.display_snapshot)
12187                    .row();
12188                self.update_breakpoint_collision_on_toggle(
12189                    display_row,
12190                    &BreakpointEditAction::Toggle,
12191                );
12192            }
12193
12194            if let Some(breakpoint) = breakpoint {
12195                self.edit_breakpoint_at_anchor(
12196                    anchor,
12197                    breakpoint,
12198                    BreakpointEditAction::Toggle,
12199                    cx,
12200                );
12201            } else {
12202                self.edit_breakpoint_at_anchor(
12203                    anchor,
12204                    Breakpoint::new_standard(),
12205                    BreakpointEditAction::Toggle,
12206                    cx,
12207                );
12208            }
12209        }
12210    }
12211
12212    fn update_breakpoint_collision_on_toggle(
12213        &mut self,
12214        display_row: DisplayRow,
12215        edit_action: &BreakpointEditAction,
12216    ) {
12217        if let Some(ref mut breakpoint_indicator) = self.gutter_breakpoint_indicator.0 {
12218            if breakpoint_indicator.display_row == display_row
12219                && matches!(edit_action, BreakpointEditAction::Toggle)
12220            {
12221                breakpoint_indicator.collides_with_existing_breakpoint =
12222                    !breakpoint_indicator.collides_with_existing_breakpoint;
12223            }
12224        }
12225    }
12226
12227    pub fn edit_breakpoint_at_anchor(
12228        &mut self,
12229        breakpoint_position: Anchor,
12230        breakpoint: Breakpoint,
12231        edit_action: BreakpointEditAction,
12232        cx: &mut Context<Self>,
12233    ) {
12234        let Some(breakpoint_store) = &self.breakpoint_store else {
12235            return;
12236        };
12237
12238        let Some(buffer) = self
12239            .buffer
12240            .read(cx)
12241            .buffer_for_anchor(breakpoint_position, cx)
12242        else {
12243            return;
12244        };
12245
12246        breakpoint_store.update(cx, |breakpoint_store, cx| {
12247            breakpoint_store.toggle_breakpoint(
12248                buffer,
12249                BreakpointWithPosition {
12250                    position: breakpoint_position.text_anchor,
12251                    bp: breakpoint,
12252                },
12253                edit_action,
12254                cx,
12255            );
12256        });
12257
12258        cx.notify();
12259    }
12260
12261    #[cfg(any(test, feature = "test-support"))]
12262    pub fn breakpoint_store(&self) -> Option<Entity<BreakpointStore>> {
12263        self.breakpoint_store.clone()
12264    }
12265
12266    pub fn prepare_restore_change(
12267        &self,
12268        revert_changes: &mut HashMap<BufferId, Vec<(Range<text::Anchor>, Rope)>>,
12269        hunk: &MultiBufferDiffHunk,
12270        cx: &mut App,
12271    ) -> Option<()> {
12272        if hunk.is_created_file() {
12273            return None;
12274        }
12275        let buffer = self.buffer.read(cx);
12276        let diff = buffer.diff_for(hunk.buffer_id)?;
12277        let buffer = buffer.buffer(hunk.buffer_id)?;
12278        let buffer = buffer.read(cx);
12279        let original_text = diff
12280            .read(cx)
12281            .base_text(cx)
12282            .as_rope()
12283            .slice(hunk.diff_base_byte_range.start.0..hunk.diff_base_byte_range.end.0);
12284        let buffer_snapshot = buffer.snapshot();
12285        let buffer_revert_changes = revert_changes.entry(buffer.remote_id()).or_default();
12286        if let Err(i) = buffer_revert_changes.binary_search_by(|probe| {
12287            probe
12288                .0
12289                .start
12290                .cmp(&hunk.buffer_range.start, &buffer_snapshot)
12291                .then(probe.0.end.cmp(&hunk.buffer_range.end, &buffer_snapshot))
12292        }) {
12293            buffer_revert_changes.insert(i, (hunk.buffer_range.clone(), original_text));
12294            Some(())
12295        } else {
12296            None
12297        }
12298    }
12299
12300    pub fn reverse_lines(&mut self, _: &ReverseLines, window: &mut Window, cx: &mut Context<Self>) {
12301        self.manipulate_immutable_lines(window, cx, |lines| lines.reverse())
12302    }
12303
12304    pub fn shuffle_lines(&mut self, _: &ShuffleLines, window: &mut Window, cx: &mut Context<Self>) {
12305        self.manipulate_immutable_lines(window, cx, |lines| lines.shuffle(&mut rand::rng()))
12306    }
12307
12308    pub fn rotate_selections_forward(
12309        &mut self,
12310        _: &RotateSelectionsForward,
12311        window: &mut Window,
12312        cx: &mut Context<Self>,
12313    ) {
12314        self.rotate_selections(window, cx, false)
12315    }
12316
12317    pub fn rotate_selections_backward(
12318        &mut self,
12319        _: &RotateSelectionsBackward,
12320        window: &mut Window,
12321        cx: &mut Context<Self>,
12322    ) {
12323        self.rotate_selections(window, cx, true)
12324    }
12325
12326    fn rotate_selections(&mut self, window: &mut Window, cx: &mut Context<Self>, reverse: bool) {
12327        self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
12328        let display_snapshot = self.display_snapshot(cx);
12329        let selections = self.selections.all::<MultiBufferOffset>(&display_snapshot);
12330
12331        if selections.len() < 2 {
12332            return;
12333        }
12334
12335        let (edits, new_selections) = {
12336            let buffer = self.buffer.read(cx).read(cx);
12337            let has_selections = selections.iter().any(|s| !s.is_empty());
12338            if has_selections {
12339                let mut selected_texts: Vec<String> = selections
12340                    .iter()
12341                    .map(|selection| {
12342                        buffer
12343                            .text_for_range(selection.start..selection.end)
12344                            .collect()
12345                    })
12346                    .collect();
12347
12348                if reverse {
12349                    selected_texts.rotate_left(1);
12350                } else {
12351                    selected_texts.rotate_right(1);
12352                }
12353
12354                let mut offset_delta: i64 = 0;
12355                let mut new_selections = Vec::new();
12356                let edits: Vec<_> = selections
12357                    .iter()
12358                    .zip(selected_texts.iter())
12359                    .map(|(selection, new_text)| {
12360                        let old_len = (selection.end.0 - selection.start.0) as i64;
12361                        let new_len = new_text.len() as i64;
12362                        let adjusted_start =
12363                            MultiBufferOffset((selection.start.0 as i64 + offset_delta) as usize);
12364                        let adjusted_end =
12365                            MultiBufferOffset((adjusted_start.0 as i64 + new_len) as usize);
12366
12367                        new_selections.push(Selection {
12368                            id: selection.id,
12369                            start: adjusted_start,
12370                            end: adjusted_end,
12371                            reversed: selection.reversed,
12372                            goal: selection.goal,
12373                        });
12374
12375                        offset_delta += new_len - old_len;
12376                        (selection.start..selection.end, new_text.clone())
12377                    })
12378                    .collect();
12379                (edits, new_selections)
12380            } else {
12381                let mut all_rows: Vec<u32> = selections
12382                    .iter()
12383                    .map(|selection| buffer.offset_to_point(selection.start).row)
12384                    .collect();
12385                all_rows.sort_unstable();
12386                all_rows.dedup();
12387
12388                if all_rows.len() < 2 {
12389                    return;
12390                }
12391
12392                let line_ranges: Vec<Range<MultiBufferOffset>> = all_rows
12393                    .iter()
12394                    .map(|&row| {
12395                        let start = Point::new(row, 0);
12396                        let end = Point::new(row, buffer.line_len(MultiBufferRow(row)));
12397                        buffer.point_to_offset(start)..buffer.point_to_offset(end)
12398                    })
12399                    .collect();
12400
12401                let mut line_texts: Vec<String> = line_ranges
12402                    .iter()
12403                    .map(|range| buffer.text_for_range(range.clone()).collect())
12404                    .collect();
12405
12406                if reverse {
12407                    line_texts.rotate_left(1);
12408                } else {
12409                    line_texts.rotate_right(1);
12410                }
12411
12412                let edits = line_ranges
12413                    .iter()
12414                    .zip(line_texts.iter())
12415                    .map(|(range, new_text)| (range.clone(), new_text.clone()))
12416                    .collect();
12417
12418                let num_rows = all_rows.len();
12419                let row_to_index: std::collections::HashMap<u32, usize> = all_rows
12420                    .iter()
12421                    .enumerate()
12422                    .map(|(i, &row)| (row, i))
12423                    .collect();
12424
12425                // Compute new line start offsets after rotation (handles CRLF)
12426                let newline_len = line_ranges[1].start.0 - line_ranges[0].end.0;
12427                let first_line_start = line_ranges[0].start.0;
12428                let mut new_line_starts: Vec<usize> = vec![first_line_start];
12429                for text in line_texts.iter().take(num_rows - 1) {
12430                    let prev_start = *new_line_starts.last().unwrap();
12431                    new_line_starts.push(prev_start + text.len() + newline_len);
12432                }
12433
12434                let new_selections = selections
12435                    .iter()
12436                    .map(|selection| {
12437                        let point = buffer.offset_to_point(selection.start);
12438                        let old_index = row_to_index[&point.row];
12439                        let new_index = if reverse {
12440                            (old_index + num_rows - 1) % num_rows
12441                        } else {
12442                            (old_index + 1) % num_rows
12443                        };
12444                        let new_offset =
12445                            MultiBufferOffset(new_line_starts[new_index] + point.column as usize);
12446                        Selection {
12447                            id: selection.id,
12448                            start: new_offset,
12449                            end: new_offset,
12450                            reversed: selection.reversed,
12451                            goal: selection.goal,
12452                        }
12453                    })
12454                    .collect();
12455
12456                (edits, new_selections)
12457            }
12458        };
12459
12460        self.transact(window, cx, |this, window, cx| {
12461            this.buffer.update(cx, |buffer, cx| {
12462                buffer.edit(edits, None, cx);
12463            });
12464            this.change_selections(Default::default(), window, cx, |s| {
12465                s.select(new_selections);
12466            });
12467        });
12468    }
12469
12470    fn manipulate_lines<M>(
12471        &mut self,
12472        window: &mut Window,
12473        cx: &mut Context<Self>,
12474        mut manipulate: M,
12475    ) where
12476        M: FnMut(&str) -> LineManipulationResult,
12477    {
12478        self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
12479
12480        let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
12481        let buffer = self.buffer.read(cx).snapshot(cx);
12482
12483        let mut edits = Vec::new();
12484
12485        let selections = self.selections.all::<Point>(&display_map);
12486        let mut selections = selections.iter().peekable();
12487        let mut contiguous_row_selections = Vec::new();
12488        let mut new_selections = Vec::new();
12489        let mut added_lines = 0;
12490        let mut removed_lines = 0;
12491
12492        while let Some(selection) = selections.next() {
12493            let (start_row, end_row) = consume_contiguous_rows(
12494                &mut contiguous_row_selections,
12495                selection,
12496                &display_map,
12497                &mut selections,
12498            );
12499
12500            let start_point = Point::new(start_row.0, 0);
12501            let end_point = Point::new(
12502                end_row.previous_row().0,
12503                buffer.line_len(end_row.previous_row()),
12504            );
12505            let text = buffer
12506                .text_for_range(start_point..end_point)
12507                .collect::<String>();
12508
12509            let LineManipulationResult {
12510                new_text,
12511                line_count_before,
12512                line_count_after,
12513            } = manipulate(&text);
12514
12515            edits.push((start_point..end_point, new_text));
12516
12517            // Selections must change based on added and removed line count
12518            let start_row =
12519                MultiBufferRow(start_point.row + added_lines as u32 - removed_lines as u32);
12520            let end_row = MultiBufferRow(start_row.0 + line_count_after.saturating_sub(1) as u32);
12521            new_selections.push(Selection {
12522                id: selection.id,
12523                start: start_row,
12524                end: end_row,
12525                goal: SelectionGoal::None,
12526                reversed: selection.reversed,
12527            });
12528
12529            if line_count_after > line_count_before {
12530                added_lines += line_count_after - line_count_before;
12531            } else if line_count_before > line_count_after {
12532                removed_lines += line_count_before - line_count_after;
12533            }
12534        }
12535
12536        self.transact(window, cx, |this, window, cx| {
12537            let buffer = this.buffer.update(cx, |buffer, cx| {
12538                buffer.edit(edits, None, cx);
12539                buffer.snapshot(cx)
12540            });
12541
12542            // Recalculate offsets on newly edited buffer
12543            let new_selections = new_selections
12544                .iter()
12545                .map(|s| {
12546                    let start_point = Point::new(s.start.0, 0);
12547                    let end_point = Point::new(s.end.0, buffer.line_len(s.end));
12548                    Selection {
12549                        id: s.id,
12550                        start: buffer.point_to_offset(start_point),
12551                        end: buffer.point_to_offset(end_point),
12552                        goal: s.goal,
12553                        reversed: s.reversed,
12554                    }
12555                })
12556                .collect();
12557
12558            this.change_selections(Default::default(), window, cx, |s| {
12559                s.select(new_selections);
12560            });
12561
12562            this.request_autoscroll(Autoscroll::fit(), cx);
12563        });
12564    }
12565
12566    fn manipulate_immutable_lines<Fn>(
12567        &mut self,
12568        window: &mut Window,
12569        cx: &mut Context<Self>,
12570        mut callback: Fn,
12571    ) where
12572        Fn: FnMut(&mut Vec<&str>),
12573    {
12574        self.manipulate_lines(window, cx, |text| {
12575            let mut lines: Vec<&str> = text.split('\n').collect();
12576            let line_count_before = lines.len();
12577
12578            callback(&mut lines);
12579
12580            LineManipulationResult {
12581                new_text: lines.join("\n"),
12582                line_count_before,
12583                line_count_after: lines.len(),
12584            }
12585        });
12586    }
12587
12588    fn manipulate_mutable_lines<Fn>(
12589        &mut self,
12590        window: &mut Window,
12591        cx: &mut Context<Self>,
12592        mut callback: Fn,
12593    ) where
12594        Fn: FnMut(&mut Vec<Cow<'_, str>>),
12595    {
12596        self.manipulate_lines(window, cx, |text| {
12597            let mut lines: Vec<Cow<str>> = text.split('\n').map(Cow::from).collect();
12598            let line_count_before = lines.len();
12599
12600            callback(&mut lines);
12601
12602            LineManipulationResult {
12603                new_text: lines.join("\n"),
12604                line_count_before,
12605                line_count_after: lines.len(),
12606            }
12607        });
12608    }
12609
12610    pub fn convert_indentation_to_spaces(
12611        &mut self,
12612        _: &ConvertIndentationToSpaces,
12613        window: &mut Window,
12614        cx: &mut Context<Self>,
12615    ) {
12616        let settings = self.buffer.read(cx).language_settings(cx);
12617        let tab_size = settings.tab_size.get() as usize;
12618
12619        self.manipulate_mutable_lines(window, cx, |lines| {
12620            // Allocates a reasonably sized scratch buffer once for the whole loop
12621            let mut reindented_line = String::with_capacity(MAX_LINE_LEN);
12622            // Avoids recomputing spaces that could be inserted many times
12623            let space_cache: Vec<Vec<char>> = (1..=tab_size)
12624                .map(|n| IndentSize::spaces(n as u32).chars().collect())
12625                .collect();
12626
12627            for line in lines.iter_mut().filter(|line| !line.is_empty()) {
12628                let mut chars = line.as_ref().chars();
12629                let mut col = 0;
12630                let mut changed = false;
12631
12632                for ch in chars.by_ref() {
12633                    match ch {
12634                        ' ' => {
12635                            reindented_line.push(' ');
12636                            col += 1;
12637                        }
12638                        '\t' => {
12639                            // \t are converted to spaces depending on the current column
12640                            let spaces_len = tab_size - (col % tab_size);
12641                            reindented_line.extend(&space_cache[spaces_len - 1]);
12642                            col += spaces_len;
12643                            changed = true;
12644                        }
12645                        _ => {
12646                            // If we dont append before break, the character is consumed
12647                            reindented_line.push(ch);
12648                            break;
12649                        }
12650                    }
12651                }
12652
12653                if !changed {
12654                    reindented_line.clear();
12655                    continue;
12656                }
12657                // Append the rest of the line and replace old reference with new one
12658                reindented_line.extend(chars);
12659                *line = Cow::Owned(reindented_line.clone());
12660                reindented_line.clear();
12661            }
12662        });
12663    }
12664
12665    pub fn convert_indentation_to_tabs(
12666        &mut self,
12667        _: &ConvertIndentationToTabs,
12668        window: &mut Window,
12669        cx: &mut Context<Self>,
12670    ) {
12671        let settings = self.buffer.read(cx).language_settings(cx);
12672        let tab_size = settings.tab_size.get() as usize;
12673
12674        self.manipulate_mutable_lines(window, cx, |lines| {
12675            // Allocates a reasonably sized buffer once for the whole loop
12676            let mut reindented_line = String::with_capacity(MAX_LINE_LEN);
12677            // Avoids recomputing spaces that could be inserted many times
12678            let space_cache: Vec<Vec<char>> = (1..=tab_size)
12679                .map(|n| IndentSize::spaces(n as u32).chars().collect())
12680                .collect();
12681
12682            for line in lines.iter_mut().filter(|line| !line.is_empty()) {
12683                let mut chars = line.chars();
12684                let mut spaces_count = 0;
12685                let mut first_non_indent_char = None;
12686                let mut changed = false;
12687
12688                for ch in chars.by_ref() {
12689                    match ch {
12690                        ' ' => {
12691                            // Keep track of spaces. Append \t when we reach tab_size
12692                            spaces_count += 1;
12693                            changed = true;
12694                            if spaces_count == tab_size {
12695                                reindented_line.push('\t');
12696                                spaces_count = 0;
12697                            }
12698                        }
12699                        '\t' => {
12700                            reindented_line.push('\t');
12701                            spaces_count = 0;
12702                        }
12703                        _ => {
12704                            // Dont append it yet, we might have remaining spaces
12705                            first_non_indent_char = Some(ch);
12706                            break;
12707                        }
12708                    }
12709                }
12710
12711                if !changed {
12712                    reindented_line.clear();
12713                    continue;
12714                }
12715                // Remaining spaces that didn't make a full tab stop
12716                if spaces_count > 0 {
12717                    reindented_line.extend(&space_cache[spaces_count - 1]);
12718                }
12719                // If we consume an extra character that was not indentation, add it back
12720                if let Some(extra_char) = first_non_indent_char {
12721                    reindented_line.push(extra_char);
12722                }
12723                // Append the rest of the line and replace old reference with new one
12724                reindented_line.extend(chars);
12725                *line = Cow::Owned(reindented_line.clone());
12726                reindented_line.clear();
12727            }
12728        });
12729    }
12730
12731    pub fn convert_to_upper_case(
12732        &mut self,
12733        _: &ConvertToUpperCase,
12734        window: &mut Window,
12735        cx: &mut Context<Self>,
12736    ) {
12737        self.manipulate_text(window, cx, |text| text.to_uppercase())
12738    }
12739
12740    pub fn convert_to_lower_case(
12741        &mut self,
12742        _: &ConvertToLowerCase,
12743        window: &mut Window,
12744        cx: &mut Context<Self>,
12745    ) {
12746        self.manipulate_text(window, cx, |text| text.to_lowercase())
12747    }
12748
12749    pub fn convert_to_title_case(
12750        &mut self,
12751        _: &ConvertToTitleCase,
12752        window: &mut Window,
12753        cx: &mut Context<Self>,
12754    ) {
12755        self.manipulate_text(window, cx, |text| {
12756            Self::convert_text_case(text, Case::Title)
12757        })
12758    }
12759
12760    pub fn convert_to_snake_case(
12761        &mut self,
12762        _: &ConvertToSnakeCase,
12763        window: &mut Window,
12764        cx: &mut Context<Self>,
12765    ) {
12766        self.manipulate_text(window, cx, |text| {
12767            Self::convert_text_case(text, Case::Snake)
12768        })
12769    }
12770
12771    pub fn convert_to_kebab_case(
12772        &mut self,
12773        _: &ConvertToKebabCase,
12774        window: &mut Window,
12775        cx: &mut Context<Self>,
12776    ) {
12777        self.manipulate_text(window, cx, |text| {
12778            Self::convert_text_case(text, Case::Kebab)
12779        })
12780    }
12781
12782    pub fn convert_to_upper_camel_case(
12783        &mut self,
12784        _: &ConvertToUpperCamelCase,
12785        window: &mut Window,
12786        cx: &mut Context<Self>,
12787    ) {
12788        self.manipulate_text(window, cx, |text| {
12789            Self::convert_text_case(text, Case::UpperCamel)
12790        })
12791    }
12792
12793    pub fn convert_to_lower_camel_case(
12794        &mut self,
12795        _: &ConvertToLowerCamelCase,
12796        window: &mut Window,
12797        cx: &mut Context<Self>,
12798    ) {
12799        self.manipulate_text(window, cx, |text| {
12800            Self::convert_text_case(text, Case::Camel)
12801        })
12802    }
12803
12804    pub fn convert_to_opposite_case(
12805        &mut self,
12806        _: &ConvertToOppositeCase,
12807        window: &mut Window,
12808        cx: &mut Context<Self>,
12809    ) {
12810        self.manipulate_text(window, cx, |text| {
12811            text.chars()
12812                .fold(String::with_capacity(text.len()), |mut t, c| {
12813                    if c.is_uppercase() {
12814                        t.extend(c.to_lowercase());
12815                    } else {
12816                        t.extend(c.to_uppercase());
12817                    }
12818                    t
12819                })
12820        })
12821    }
12822
12823    pub fn convert_to_sentence_case(
12824        &mut self,
12825        _: &ConvertToSentenceCase,
12826        window: &mut Window,
12827        cx: &mut Context<Self>,
12828    ) {
12829        self.manipulate_text(window, cx, |text| {
12830            Self::convert_text_case(text, Case::Sentence)
12831        })
12832    }
12833
12834    pub fn toggle_case(&mut self, _: &ToggleCase, window: &mut Window, cx: &mut Context<Self>) {
12835        self.manipulate_text(window, cx, |text| {
12836            let has_upper_case_characters = text.chars().any(|c| c.is_uppercase());
12837            if has_upper_case_characters {
12838                text.to_lowercase()
12839            } else {
12840                text.to_uppercase()
12841            }
12842        })
12843    }
12844
12845    pub fn convert_to_rot13(
12846        &mut self,
12847        _: &ConvertToRot13,
12848        window: &mut Window,
12849        cx: &mut Context<Self>,
12850    ) {
12851        self.manipulate_text(window, cx, |text| {
12852            text.chars()
12853                .map(|c| match c {
12854                    'A'..='M' | 'a'..='m' => ((c as u8) + 13) as char,
12855                    'N'..='Z' | 'n'..='z' => ((c as u8) - 13) as char,
12856                    _ => c,
12857                })
12858                .collect()
12859        })
12860    }
12861
12862    fn convert_text_case(text: &str, case: Case) -> String {
12863        text.lines()
12864            .map(|line| {
12865                let trimmed_start = line.trim_start();
12866                let leading = &line[..line.len() - trimmed_start.len()];
12867                let trimmed = trimmed_start.trim_end();
12868                let trailing = &trimmed_start[trimmed.len()..];
12869                format!("{}{}{}", leading, trimmed.to_case(case), trailing)
12870            })
12871            .join("\n")
12872    }
12873
12874    pub fn convert_to_rot47(
12875        &mut self,
12876        _: &ConvertToRot47,
12877        window: &mut Window,
12878        cx: &mut Context<Self>,
12879    ) {
12880        self.manipulate_text(window, cx, |text| {
12881            text.chars()
12882                .map(|c| {
12883                    let code_point = c as u32;
12884                    if code_point >= 33 && code_point <= 126 {
12885                        return char::from_u32(33 + ((code_point + 14) % 94)).unwrap();
12886                    }
12887                    c
12888                })
12889                .collect()
12890        })
12891    }
12892
12893    fn manipulate_text<Fn>(&mut self, window: &mut Window, cx: &mut Context<Self>, mut callback: Fn)
12894    where
12895        Fn: FnMut(&str) -> String,
12896    {
12897        let buffer = self.buffer.read(cx).snapshot(cx);
12898
12899        let mut new_selections = Vec::new();
12900        let mut edits = Vec::new();
12901        let mut selection_adjustment = 0isize;
12902
12903        for selection in self.selections.all_adjusted(&self.display_snapshot(cx)) {
12904            let selection_is_empty = selection.is_empty();
12905
12906            let (start, end) = if selection_is_empty {
12907                let (word_range, _) = buffer.surrounding_word(selection.start, None);
12908                (word_range.start, word_range.end)
12909            } else {
12910                (
12911                    buffer.point_to_offset(selection.start),
12912                    buffer.point_to_offset(selection.end),
12913                )
12914            };
12915
12916            let text = buffer.text_for_range(start..end).collect::<String>();
12917            let old_length = text.len() as isize;
12918            let text = callback(&text);
12919
12920            new_selections.push(Selection {
12921                start: MultiBufferOffset((start.0 as isize - selection_adjustment) as usize),
12922                end: MultiBufferOffset(
12923                    ((start.0 + text.len()) as isize - selection_adjustment) as usize,
12924                ),
12925                goal: SelectionGoal::None,
12926                id: selection.id,
12927                reversed: selection.reversed,
12928            });
12929
12930            selection_adjustment += old_length - text.len() as isize;
12931
12932            edits.push((start..end, text));
12933        }
12934
12935        self.transact(window, cx, |this, window, cx| {
12936            this.buffer.update(cx, |buffer, cx| {
12937                buffer.edit(edits, None, cx);
12938            });
12939
12940            this.change_selections(Default::default(), window, cx, |s| {
12941                s.select(new_selections);
12942            });
12943
12944            this.request_autoscroll(Autoscroll::fit(), cx);
12945        });
12946    }
12947
12948    pub fn move_selection_on_drop(
12949        &mut self,
12950        selection: &Selection<Anchor>,
12951        target: DisplayPoint,
12952        is_cut: bool,
12953        window: &mut Window,
12954        cx: &mut Context<Self>,
12955    ) {
12956        let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
12957        let buffer = display_map.buffer_snapshot();
12958        let mut edits = Vec::new();
12959        let insert_point = display_map
12960            .clip_point(target, Bias::Left)
12961            .to_point(&display_map);
12962        let text = buffer
12963            .text_for_range(selection.start..selection.end)
12964            .collect::<String>();
12965        if is_cut {
12966            edits.push(((selection.start..selection.end), String::new()));
12967        }
12968        let insert_anchor = buffer.anchor_before(insert_point);
12969        edits.push(((insert_anchor..insert_anchor), text));
12970        let last_edit_start = insert_anchor.bias_left(buffer);
12971        let last_edit_end = insert_anchor.bias_right(buffer);
12972        self.transact(window, cx, |this, window, cx| {
12973            this.buffer.update(cx, |buffer, cx| {
12974                buffer.edit(edits, None, cx);
12975            });
12976            this.change_selections(Default::default(), window, cx, |s| {
12977                s.select_anchor_ranges([last_edit_start..last_edit_end]);
12978            });
12979        });
12980    }
12981
12982    pub fn clear_selection_drag_state(&mut self) {
12983        self.selection_drag_state = SelectionDragState::None;
12984    }
12985
12986    pub fn duplicate(
12987        &mut self,
12988        upwards: bool,
12989        whole_lines: bool,
12990        window: &mut Window,
12991        cx: &mut Context<Self>,
12992    ) {
12993        self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
12994
12995        let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
12996        let buffer = display_map.buffer_snapshot();
12997        let selections = self.selections.all::<Point>(&display_map);
12998
12999        let mut edits = Vec::new();
13000        let mut selections_iter = selections.iter().peekable();
13001        while let Some(selection) = selections_iter.next() {
13002            let mut rows = selection.spanned_rows(false, &display_map);
13003            // duplicate line-wise
13004            if whole_lines || selection.start == selection.end {
13005                // Avoid duplicating the same lines twice.
13006                while let Some(next_selection) = selections_iter.peek() {
13007                    let next_rows = next_selection.spanned_rows(false, &display_map);
13008                    if next_rows.start < rows.end {
13009                        rows.end = next_rows.end;
13010                        selections_iter.next().unwrap();
13011                    } else {
13012                        break;
13013                    }
13014                }
13015
13016                // Copy the text from the selected row region and splice it either at the start
13017                // or end of the region.
13018                let start = Point::new(rows.start.0, 0);
13019                let end = Point::new(
13020                    rows.end.previous_row().0,
13021                    buffer.line_len(rows.end.previous_row()),
13022                );
13023
13024                let mut text = buffer.text_for_range(start..end).collect::<String>();
13025
13026                let insert_location = if upwards {
13027                    // When duplicating upward, we need to insert before the current line.
13028                    // If we're on the last line and it doesn't end with a newline,
13029                    // we need to add a newline before the duplicated content.
13030                    let needs_leading_newline = rows.end.0 >= buffer.max_point().row
13031                        && buffer.max_point().column > 0
13032                        && !text.ends_with('\n');
13033
13034                    if needs_leading_newline {
13035                        text.insert(0, '\n');
13036                        end
13037                    } else {
13038                        text.push('\n');
13039                        Point::new(rows.start.0, 0)
13040                    }
13041                } else {
13042                    text.push('\n');
13043                    start
13044                };
13045                edits.push((insert_location..insert_location, text));
13046            } else {
13047                // duplicate character-wise
13048                let start = selection.start;
13049                let end = selection.end;
13050                let text = buffer.text_for_range(start..end).collect::<String>();
13051                edits.push((selection.end..selection.end, text));
13052            }
13053        }
13054
13055        self.transact(window, cx, |this, window, cx| {
13056            this.buffer.update(cx, |buffer, cx| {
13057                buffer.edit(edits, None, cx);
13058            });
13059
13060            // When duplicating upward with whole lines, move the cursor to the duplicated line
13061            if upwards && whole_lines {
13062                let display_map = this.display_map.update(cx, |map, cx| map.snapshot(cx));
13063
13064                this.change_selections(SelectionEffects::no_scroll(), window, cx, |s| {
13065                    let mut new_ranges = Vec::new();
13066                    let selections = s.all::<Point>(&display_map);
13067                    let mut selections_iter = selections.iter().peekable();
13068
13069                    while let Some(first_selection) = selections_iter.next() {
13070                        // Group contiguous selections together to find the total row span
13071                        let mut group_selections = vec![first_selection];
13072                        let mut rows = first_selection.spanned_rows(false, &display_map);
13073
13074                        while let Some(next_selection) = selections_iter.peek() {
13075                            let next_rows = next_selection.spanned_rows(false, &display_map);
13076                            if next_rows.start < rows.end {
13077                                rows.end = next_rows.end;
13078                                group_selections.push(selections_iter.next().unwrap());
13079                            } else {
13080                                break;
13081                            }
13082                        }
13083
13084                        let row_count = rows.end.0 - rows.start.0;
13085
13086                        // Move all selections in this group up by the total number of duplicated rows
13087                        for selection in group_selections {
13088                            let new_start = Point::new(
13089                                selection.start.row.saturating_sub(row_count),
13090                                selection.start.column,
13091                            );
13092
13093                            let new_end = Point::new(
13094                                selection.end.row.saturating_sub(row_count),
13095                                selection.end.column,
13096                            );
13097
13098                            new_ranges.push(new_start..new_end);
13099                        }
13100                    }
13101
13102                    s.select_ranges(new_ranges);
13103                });
13104            }
13105
13106            this.request_autoscroll(Autoscroll::fit(), cx);
13107        });
13108    }
13109
13110    pub fn duplicate_line_up(
13111        &mut self,
13112        _: &DuplicateLineUp,
13113        window: &mut Window,
13114        cx: &mut Context<Self>,
13115    ) {
13116        self.duplicate(true, true, window, cx);
13117    }
13118
13119    pub fn duplicate_line_down(
13120        &mut self,
13121        _: &DuplicateLineDown,
13122        window: &mut Window,
13123        cx: &mut Context<Self>,
13124    ) {
13125        self.duplicate(false, true, window, cx);
13126    }
13127
13128    pub fn duplicate_selection(
13129        &mut self,
13130        _: &DuplicateSelection,
13131        window: &mut Window,
13132        cx: &mut Context<Self>,
13133    ) {
13134        self.duplicate(false, false, window, cx);
13135    }
13136
13137    pub fn move_line_up(&mut self, _: &MoveLineUp, window: &mut Window, cx: &mut Context<Self>) {
13138        self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
13139        if self.mode.is_single_line() {
13140            cx.propagate();
13141            return;
13142        }
13143
13144        let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
13145        let buffer = self.buffer.read(cx).snapshot(cx);
13146
13147        let mut edits = Vec::new();
13148        let mut unfold_ranges = Vec::new();
13149        let mut refold_creases = Vec::new();
13150
13151        let selections = self.selections.all::<Point>(&display_map);
13152        let mut selections = selections.iter().peekable();
13153        let mut contiguous_row_selections = Vec::new();
13154        let mut new_selections = Vec::new();
13155
13156        while let Some(selection) = selections.next() {
13157            // Find all the selections that span a contiguous row range
13158            let (start_row, end_row) = consume_contiguous_rows(
13159                &mut contiguous_row_selections,
13160                selection,
13161                &display_map,
13162                &mut selections,
13163            );
13164
13165            // Move the text spanned by the row range to be before the line preceding the row range
13166            if start_row.0 > 0 {
13167                let range_to_move = Point::new(
13168                    start_row.previous_row().0,
13169                    buffer.line_len(start_row.previous_row()),
13170                )
13171                    ..Point::new(
13172                        end_row.previous_row().0,
13173                        buffer.line_len(end_row.previous_row()),
13174                    );
13175                let insertion_point = display_map
13176                    .prev_line_boundary(Point::new(start_row.previous_row().0, 0))
13177                    .0;
13178
13179                // Don't move lines across excerpts
13180                if buffer
13181                    .excerpt_containing(insertion_point..range_to_move.end)
13182                    .is_some()
13183                {
13184                    let text = buffer
13185                        .text_for_range(range_to_move.clone())
13186                        .flat_map(|s| s.chars())
13187                        .skip(1)
13188                        .chain(['\n'])
13189                        .collect::<String>();
13190
13191                    edits.push((
13192                        buffer.anchor_after(range_to_move.start)
13193                            ..buffer.anchor_before(range_to_move.end),
13194                        String::new(),
13195                    ));
13196                    let insertion_anchor = buffer.anchor_after(insertion_point);
13197                    edits.push((insertion_anchor..insertion_anchor, text));
13198
13199                    let row_delta = range_to_move.start.row - insertion_point.row + 1;
13200
13201                    // Move selections up
13202                    new_selections.extend(contiguous_row_selections.drain(..).map(
13203                        |mut selection| {
13204                            selection.start.row -= row_delta;
13205                            selection.end.row -= row_delta;
13206                            selection
13207                        },
13208                    ));
13209
13210                    // Move folds up
13211                    unfold_ranges.push(range_to_move.clone());
13212                    for fold in display_map.folds_in_range(
13213                        buffer.anchor_before(range_to_move.start)
13214                            ..buffer.anchor_after(range_to_move.end),
13215                    ) {
13216                        let mut start = fold.range.start.to_point(&buffer);
13217                        let mut end = fold.range.end.to_point(&buffer);
13218                        start.row -= row_delta;
13219                        end.row -= row_delta;
13220                        refold_creases.push(Crease::simple(start..end, fold.placeholder.clone()));
13221                    }
13222                }
13223            }
13224
13225            // If we didn't move line(s), preserve the existing selections
13226            new_selections.append(&mut contiguous_row_selections);
13227        }
13228
13229        self.transact(window, cx, |this, window, cx| {
13230            this.unfold_ranges(&unfold_ranges, true, true, cx);
13231            this.buffer.update(cx, |buffer, cx| {
13232                for (range, text) in edits {
13233                    buffer.edit([(range, text)], None, cx);
13234                }
13235            });
13236            this.fold_creases(refold_creases, true, window, cx);
13237            this.change_selections(Default::default(), window, cx, |s| {
13238                s.select(new_selections);
13239            })
13240        });
13241    }
13242
13243    pub fn move_line_down(
13244        &mut self,
13245        _: &MoveLineDown,
13246        window: &mut Window,
13247        cx: &mut Context<Self>,
13248    ) {
13249        self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
13250        if self.mode.is_single_line() {
13251            cx.propagate();
13252            return;
13253        }
13254
13255        let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
13256        let buffer = self.buffer.read(cx).snapshot(cx);
13257
13258        let mut edits = Vec::new();
13259        let mut unfold_ranges = Vec::new();
13260        let mut refold_creases = Vec::new();
13261
13262        let selections = self.selections.all::<Point>(&display_map);
13263        let mut selections = selections.iter().peekable();
13264        let mut contiguous_row_selections = Vec::new();
13265        let mut new_selections = Vec::new();
13266
13267        while let Some(selection) = selections.next() {
13268            // Find all the selections that span a contiguous row range
13269            let (start_row, end_row) = consume_contiguous_rows(
13270                &mut contiguous_row_selections,
13271                selection,
13272                &display_map,
13273                &mut selections,
13274            );
13275
13276            // Move the text spanned by the row range to be after the last line of the row range
13277            if end_row.0 <= buffer.max_point().row {
13278                let range_to_move =
13279                    MultiBufferPoint::new(start_row.0, 0)..MultiBufferPoint::new(end_row.0, 0);
13280                let insertion_point = display_map
13281                    .next_line_boundary(MultiBufferPoint::new(end_row.0, 0))
13282                    .0;
13283
13284                // Don't move lines across excerpt boundaries
13285                if buffer
13286                    .excerpt_containing(range_to_move.start..insertion_point)
13287                    .is_some()
13288                {
13289                    let mut text = String::from("\n");
13290                    text.extend(buffer.text_for_range(range_to_move.clone()));
13291                    text.pop(); // Drop trailing newline
13292                    edits.push((
13293                        buffer.anchor_after(range_to_move.start)
13294                            ..buffer.anchor_before(range_to_move.end),
13295                        String::new(),
13296                    ));
13297                    let insertion_anchor = buffer.anchor_after(insertion_point);
13298                    edits.push((insertion_anchor..insertion_anchor, text));
13299
13300                    let row_delta = insertion_point.row - range_to_move.end.row + 1;
13301
13302                    // Move selections down
13303                    new_selections.extend(contiguous_row_selections.drain(..).map(
13304                        |mut selection| {
13305                            selection.start.row += row_delta;
13306                            selection.end.row += row_delta;
13307                            selection
13308                        },
13309                    ));
13310
13311                    // Move folds down
13312                    unfold_ranges.push(range_to_move.clone());
13313                    for fold in display_map.folds_in_range(
13314                        buffer.anchor_before(range_to_move.start)
13315                            ..buffer.anchor_after(range_to_move.end),
13316                    ) {
13317                        let mut start = fold.range.start.to_point(&buffer);
13318                        let mut end = fold.range.end.to_point(&buffer);
13319                        start.row += row_delta;
13320                        end.row += row_delta;
13321                        refold_creases.push(Crease::simple(start..end, fold.placeholder.clone()));
13322                    }
13323                }
13324            }
13325
13326            // If we didn't move line(s), preserve the existing selections
13327            new_selections.append(&mut contiguous_row_selections);
13328        }
13329
13330        self.transact(window, cx, |this, window, cx| {
13331            this.unfold_ranges(&unfold_ranges, true, true, cx);
13332            this.buffer.update(cx, |buffer, cx| {
13333                for (range, text) in edits {
13334                    buffer.edit([(range, text)], None, cx);
13335                }
13336            });
13337            this.fold_creases(refold_creases, true, window, cx);
13338            this.change_selections(Default::default(), window, cx, |s| s.select(new_selections));
13339        });
13340    }
13341
13342    pub fn transpose(&mut self, _: &Transpose, window: &mut Window, cx: &mut Context<Self>) {
13343        self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
13344        let text_layout_details = &self.text_layout_details(window, cx);
13345        self.transact(window, cx, |this, window, cx| {
13346            let edits = this.change_selections(Default::default(), window, cx, |s| {
13347                let mut edits: Vec<(Range<MultiBufferOffset>, String)> = Default::default();
13348                s.move_with(&mut |display_map, selection| {
13349                    if !selection.is_empty() {
13350                        return;
13351                    }
13352
13353                    let mut head = selection.head();
13354                    let mut transpose_offset = head.to_offset(display_map, Bias::Right);
13355                    if head.column() == display_map.line_len(head.row()) {
13356                        transpose_offset = display_map
13357                            .buffer_snapshot()
13358                            .clip_offset(transpose_offset.saturating_sub_usize(1), Bias::Left);
13359                    }
13360
13361                    if transpose_offset == MultiBufferOffset(0) {
13362                        return;
13363                    }
13364
13365                    *head.column_mut() += 1;
13366                    head = display_map.clip_point(head, Bias::Right);
13367                    let goal = SelectionGoal::HorizontalPosition(
13368                        display_map
13369                            .x_for_display_point(head, text_layout_details)
13370                            .into(),
13371                    );
13372                    selection.collapse_to(head, goal);
13373
13374                    let transpose_start = display_map
13375                        .buffer_snapshot()
13376                        .clip_offset(transpose_offset.saturating_sub_usize(1), Bias::Left);
13377                    if edits.last().is_none_or(|e| e.0.end <= transpose_start) {
13378                        let transpose_end = display_map
13379                            .buffer_snapshot()
13380                            .clip_offset(transpose_offset + 1usize, Bias::Right);
13381                        if let Some(ch) = display_map
13382                            .buffer_snapshot()
13383                            .chars_at(transpose_start)
13384                            .next()
13385                        {
13386                            edits.push((transpose_start..transpose_offset, String::new()));
13387                            edits.push((transpose_end..transpose_end, ch.to_string()));
13388                        }
13389                    }
13390                });
13391                edits
13392            });
13393            this.buffer
13394                .update(cx, |buffer, cx| buffer.edit(edits, None, cx));
13395            let selections = this
13396                .selections
13397                .all::<MultiBufferOffset>(&this.display_snapshot(cx));
13398            this.change_selections(Default::default(), window, cx, |s| {
13399                s.select(selections);
13400            });
13401        });
13402    }
13403
13404    pub fn rewrap(&mut self, _: &Rewrap, _: &mut Window, cx: &mut Context<Self>) {
13405        self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
13406        if self.mode.is_single_line() {
13407            cx.propagate();
13408            return;
13409        }
13410
13411        self.rewrap_impl(RewrapOptions::default(), cx)
13412    }
13413
13414    pub fn rewrap_impl(&mut self, options: RewrapOptions, cx: &mut Context<Self>) {
13415        let buffer = self.buffer.read(cx).snapshot(cx);
13416        let selections = self.selections.all::<Point>(&self.display_snapshot(cx));
13417
13418        #[derive(Clone, Debug, PartialEq)]
13419        enum CommentFormat {
13420            /// single line comment, with prefix for line
13421            Line(String),
13422            /// single line within a block comment, with prefix for line
13423            BlockLine(String),
13424            /// a single line of a block comment that includes the initial delimiter
13425            BlockCommentWithStart(BlockCommentConfig),
13426            /// a single line of a block comment that includes the ending delimiter
13427            BlockCommentWithEnd(BlockCommentConfig),
13428        }
13429
13430        // Split selections to respect paragraph, indent, and comment prefix boundaries.
13431        let wrap_ranges = selections.into_iter().flat_map(|selection| {
13432            let language_settings = buffer.language_settings_at(selection.head(), cx);
13433            let language_scope = buffer.language_scope_at(selection.head());
13434
13435            let indent_and_prefix_for_row =
13436                |row: u32| -> (IndentSize, Option<CommentFormat>, Option<String>) {
13437                    let indent = buffer.indent_size_for_line(MultiBufferRow(row));
13438                    let (comment_prefix, rewrap_prefix) = if let Some(language_scope) =
13439                        &language_scope
13440                    {
13441                        let indent_end = Point::new(row, indent.len);
13442                        let line_end = Point::new(row, buffer.line_len(MultiBufferRow(row)));
13443                        let line_text_after_indent = buffer
13444                            .text_for_range(indent_end..line_end)
13445                            .collect::<String>();
13446
13447                        let is_within_comment_override = buffer
13448                            .language_scope_at(indent_end)
13449                            .is_some_and(|scope| scope.override_name() == Some("comment"));
13450                        let comment_delimiters = if is_within_comment_override {
13451                            // we are within a comment syntax node, but we don't
13452                            // yet know what kind of comment: block, doc or line
13453                            match (
13454                                language_scope.documentation_comment(),
13455                                language_scope.block_comment(),
13456                            ) {
13457                                (Some(config), _) | (_, Some(config))
13458                                    if buffer.contains_str_at(indent_end, &config.start) =>
13459                                {
13460                                    Some(CommentFormat::BlockCommentWithStart(config.clone()))
13461                                }
13462                                (Some(config), _) | (_, Some(config))
13463                                    if line_text_after_indent.ends_with(config.end.as_ref()) =>
13464                                {
13465                                    Some(CommentFormat::BlockCommentWithEnd(config.clone()))
13466                                }
13467                                (Some(config), _) | (_, Some(config))
13468                                    if buffer.contains_str_at(indent_end, &config.prefix) =>
13469                                {
13470                                    Some(CommentFormat::BlockLine(config.prefix.to_string()))
13471                                }
13472                                (_, _) => language_scope
13473                                    .line_comment_prefixes()
13474                                    .iter()
13475                                    .find(|prefix| buffer.contains_str_at(indent_end, prefix))
13476                                    .map(|prefix| CommentFormat::Line(prefix.to_string())),
13477                            }
13478                        } else {
13479                            // we not in an overridden comment node, but we may
13480                            // be within a non-overridden line comment node
13481                            language_scope
13482                                .line_comment_prefixes()
13483                                .iter()
13484                                .find(|prefix| buffer.contains_str_at(indent_end, prefix))
13485                                .map(|prefix| CommentFormat::Line(prefix.to_string()))
13486                        };
13487
13488                        let rewrap_prefix = language_scope
13489                            .rewrap_prefixes()
13490                            .iter()
13491                            .find_map(|prefix_regex| {
13492                                prefix_regex.find(&line_text_after_indent).map(|mat| {
13493                                    if mat.start() == 0 {
13494                                        Some(mat.as_str().to_string())
13495                                    } else {
13496                                        None
13497                                    }
13498                                })
13499                            })
13500                            .flatten();
13501                        (comment_delimiters, rewrap_prefix)
13502                    } else {
13503                        (None, None)
13504                    };
13505                    (indent, comment_prefix, rewrap_prefix)
13506                };
13507
13508            let mut start_row = selection.start.row;
13509            let mut end_row = selection.end.row;
13510
13511            if selection.is_empty() {
13512                let cursor_row = selection.start.row;
13513
13514                let (mut indent_size, comment_prefix, _) = indent_and_prefix_for_row(cursor_row);
13515                let line_prefix = match &comment_prefix {
13516                    Some(CommentFormat::Line(prefix) | CommentFormat::BlockLine(prefix)) => {
13517                        Some(prefix.as_str())
13518                    }
13519                    Some(CommentFormat::BlockCommentWithEnd(BlockCommentConfig {
13520                        prefix, ..
13521                    })) => Some(prefix.as_ref()),
13522                    Some(CommentFormat::BlockCommentWithStart(BlockCommentConfig {
13523                        start: _,
13524                        end: _,
13525                        prefix,
13526                        tab_size,
13527                    })) => {
13528                        indent_size.len += tab_size;
13529                        Some(prefix.as_ref())
13530                    }
13531                    None => None,
13532                };
13533                let indent_prefix = indent_size.chars().collect::<String>();
13534                let line_prefix = format!("{indent_prefix}{}", line_prefix.unwrap_or(""));
13535
13536                'expand_upwards: while start_row > 0 {
13537                    let prev_row = start_row - 1;
13538                    if buffer.contains_str_at(Point::new(prev_row, 0), &line_prefix)
13539                        && buffer.line_len(MultiBufferRow(prev_row)) as usize > line_prefix.len()
13540                        && !buffer.is_line_blank(MultiBufferRow(prev_row))
13541                    {
13542                        start_row = prev_row;
13543                    } else {
13544                        break 'expand_upwards;
13545                    }
13546                }
13547
13548                'expand_downwards: while end_row < buffer.max_point().row {
13549                    let next_row = end_row + 1;
13550                    if buffer.contains_str_at(Point::new(next_row, 0), &line_prefix)
13551                        && buffer.line_len(MultiBufferRow(next_row)) as usize > line_prefix.len()
13552                        && !buffer.is_line_blank(MultiBufferRow(next_row))
13553                    {
13554                        end_row = next_row;
13555                    } else {
13556                        break 'expand_downwards;
13557                    }
13558                }
13559            }
13560
13561            let mut non_blank_rows_iter = (start_row..=end_row)
13562                .filter(|row| !buffer.is_line_blank(MultiBufferRow(*row)))
13563                .peekable();
13564
13565            let first_row = if let Some(&row) = non_blank_rows_iter.peek() {
13566                row
13567            } else {
13568                return Vec::new();
13569            };
13570
13571            let mut ranges = Vec::new();
13572
13573            let mut current_range_start = first_row;
13574            let mut prev_row = first_row;
13575            let (
13576                mut current_range_indent,
13577                mut current_range_comment_delimiters,
13578                mut current_range_rewrap_prefix,
13579            ) = indent_and_prefix_for_row(first_row);
13580
13581            for row in non_blank_rows_iter.skip(1) {
13582                let has_paragraph_break = row > prev_row + 1;
13583
13584                let (row_indent, row_comment_delimiters, row_rewrap_prefix) =
13585                    indent_and_prefix_for_row(row);
13586
13587                let has_indent_change = row_indent != current_range_indent;
13588                let has_comment_change = row_comment_delimiters != current_range_comment_delimiters;
13589
13590                let has_boundary_change = has_comment_change
13591                    || row_rewrap_prefix.is_some()
13592                    || (has_indent_change && current_range_comment_delimiters.is_some());
13593
13594                if has_paragraph_break || has_boundary_change {
13595                    ranges.push((
13596                        language_settings.clone(),
13597                        Point::new(current_range_start, 0)
13598                            ..Point::new(prev_row, buffer.line_len(MultiBufferRow(prev_row))),
13599                        current_range_indent,
13600                        current_range_comment_delimiters.clone(),
13601                        current_range_rewrap_prefix.clone(),
13602                    ));
13603                    current_range_start = row;
13604                    current_range_indent = row_indent;
13605                    current_range_comment_delimiters = row_comment_delimiters;
13606                    current_range_rewrap_prefix = row_rewrap_prefix;
13607                }
13608                prev_row = row;
13609            }
13610
13611            ranges.push((
13612                language_settings.clone(),
13613                Point::new(current_range_start, 0)
13614                    ..Point::new(prev_row, buffer.line_len(MultiBufferRow(prev_row))),
13615                current_range_indent,
13616                current_range_comment_delimiters,
13617                current_range_rewrap_prefix,
13618            ));
13619
13620            ranges
13621        });
13622
13623        let mut edits = Vec::new();
13624        let mut rewrapped_row_ranges = Vec::<RangeInclusive<u32>>::new();
13625
13626        for (language_settings, wrap_range, mut indent_size, comment_prefix, rewrap_prefix) in
13627            wrap_ranges
13628        {
13629            let start_row = wrap_range.start.row;
13630            let end_row = wrap_range.end.row;
13631
13632            // Skip selections that overlap with a range that has already been rewrapped.
13633            let selection_range = start_row..end_row;
13634            if rewrapped_row_ranges
13635                .iter()
13636                .any(|range| range.overlaps(&selection_range))
13637            {
13638                continue;
13639            }
13640
13641            let tab_size = language_settings.tab_size;
13642
13643            let (line_prefix, inside_comment) = match &comment_prefix {
13644                Some(CommentFormat::Line(prefix) | CommentFormat::BlockLine(prefix)) => {
13645                    (Some(prefix.as_str()), true)
13646                }
13647                Some(CommentFormat::BlockCommentWithEnd(BlockCommentConfig { prefix, .. })) => {
13648                    (Some(prefix.as_ref()), true)
13649                }
13650                Some(CommentFormat::BlockCommentWithStart(BlockCommentConfig {
13651                    start: _,
13652                    end: _,
13653                    prefix,
13654                    tab_size,
13655                })) => {
13656                    indent_size.len += tab_size;
13657                    (Some(prefix.as_ref()), true)
13658                }
13659                None => (None, false),
13660            };
13661            let indent_prefix = indent_size.chars().collect::<String>();
13662            let line_prefix = format!("{indent_prefix}{}", line_prefix.unwrap_or(""));
13663
13664            let allow_rewrap_based_on_language = match language_settings.allow_rewrap {
13665                RewrapBehavior::InComments => inside_comment,
13666                RewrapBehavior::InSelections => !wrap_range.is_empty(),
13667                RewrapBehavior::Anywhere => true,
13668            };
13669
13670            let should_rewrap = options.override_language_settings
13671                || allow_rewrap_based_on_language
13672                || self.hard_wrap.is_some();
13673            if !should_rewrap {
13674                continue;
13675            }
13676
13677            let start = Point::new(start_row, 0);
13678            let start_offset = ToOffset::to_offset(&start, &buffer);
13679            let end = Point::new(end_row, buffer.line_len(MultiBufferRow(end_row)));
13680            let selection_text = buffer.text_for_range(start..end).collect::<String>();
13681            let mut first_line_delimiter = None;
13682            let mut last_line_delimiter = None;
13683            let Some(lines_without_prefixes) = selection_text
13684                .lines()
13685                .enumerate()
13686                .map(|(ix, line)| {
13687                    let line_trimmed = line.trim_start();
13688                    if rewrap_prefix.is_some() && ix > 0 {
13689                        Ok(line_trimmed)
13690                    } else if let Some(
13691                        CommentFormat::BlockCommentWithStart(BlockCommentConfig {
13692                            start,
13693                            prefix,
13694                            end,
13695                            tab_size,
13696                        })
13697                        | CommentFormat::BlockCommentWithEnd(BlockCommentConfig {
13698                            start,
13699                            prefix,
13700                            end,
13701                            tab_size,
13702                        }),
13703                    ) = &comment_prefix
13704                    {
13705                        let line_trimmed = line_trimmed
13706                            .strip_prefix(start.as_ref())
13707                            .map(|s| {
13708                                let mut indent_size = indent_size;
13709                                indent_size.len -= tab_size;
13710                                let indent_prefix: String = indent_size.chars().collect();
13711                                first_line_delimiter = Some((indent_prefix, start));
13712                                s.trim_start()
13713                            })
13714                            .unwrap_or(line_trimmed);
13715                        let line_trimmed = line_trimmed
13716                            .strip_suffix(end.as_ref())
13717                            .map(|s| {
13718                                last_line_delimiter = Some(end);
13719                                s.trim_end()
13720                            })
13721                            .unwrap_or(line_trimmed);
13722                        let line_trimmed = line_trimmed
13723                            .strip_prefix(prefix.as_ref())
13724                            .unwrap_or(line_trimmed);
13725                        Ok(line_trimmed)
13726                    } else if let Some(CommentFormat::BlockLine(prefix)) = &comment_prefix {
13727                        line_trimmed.strip_prefix(prefix).with_context(|| {
13728                            format!("line did not start with prefix {prefix:?}: {line:?}")
13729                        })
13730                    } else {
13731                        line_trimmed
13732                            .strip_prefix(&line_prefix.trim_start())
13733                            .with_context(|| {
13734                                format!("line did not start with prefix {line_prefix:?}: {line:?}")
13735                            })
13736                    }
13737                })
13738                .collect::<Result<Vec<_>, _>>()
13739                .log_err()
13740            else {
13741                continue;
13742            };
13743
13744            let wrap_column = options.line_length.or(self.hard_wrap).unwrap_or_else(|| {
13745                buffer
13746                    .language_settings_at(Point::new(start_row, 0), cx)
13747                    .preferred_line_length as usize
13748            });
13749
13750            let subsequent_lines_prefix = if let Some(rewrap_prefix_str) = &rewrap_prefix {
13751                format!("{}{}", indent_prefix, " ".repeat(rewrap_prefix_str.len()))
13752            } else {
13753                line_prefix.clone()
13754            };
13755
13756            let wrapped_text = {
13757                let mut wrapped_text = wrap_with_prefix(
13758                    line_prefix,
13759                    subsequent_lines_prefix,
13760                    lines_without_prefixes.join("\n"),
13761                    wrap_column,
13762                    tab_size,
13763                    options.preserve_existing_whitespace,
13764                );
13765
13766                if let Some((indent, delimiter)) = first_line_delimiter {
13767                    wrapped_text = format!("{indent}{delimiter}\n{wrapped_text}");
13768                }
13769                if let Some(last_line) = last_line_delimiter {
13770                    wrapped_text = format!("{wrapped_text}\n{indent_prefix}{last_line}");
13771                }
13772
13773                wrapped_text
13774            };
13775
13776            // TODO: should always use char-based diff while still supporting cursor behavior that
13777            // matches vim.
13778            let mut diff_options = DiffOptions::default();
13779            if options.override_language_settings {
13780                diff_options.max_word_diff_len = 0;
13781                diff_options.max_word_diff_line_count = 0;
13782            } else {
13783                diff_options.max_word_diff_len = usize::MAX;
13784                diff_options.max_word_diff_line_count = usize::MAX;
13785            }
13786
13787            for (old_range, new_text) in
13788                text_diff_with_options(&selection_text, &wrapped_text, diff_options)
13789            {
13790                let edit_start = buffer.anchor_after(start_offset + old_range.start);
13791                let edit_end = buffer.anchor_after(start_offset + old_range.end);
13792                edits.push((edit_start..edit_end, new_text));
13793            }
13794
13795            rewrapped_row_ranges.push(start_row..=end_row);
13796        }
13797
13798        self.buffer
13799            .update(cx, |buffer, cx| buffer.edit(edits, None, cx));
13800    }
13801
13802    pub fn cut_common(
13803        &mut self,
13804        cut_no_selection_line: bool,
13805        window: &mut Window,
13806        cx: &mut Context<Self>,
13807    ) -> ClipboardItem {
13808        let mut text = String::new();
13809        let buffer = self.buffer.read(cx).snapshot(cx);
13810        let mut selections = self.selections.all::<Point>(&self.display_snapshot(cx));
13811        let mut clipboard_selections = Vec::with_capacity(selections.len());
13812        {
13813            let max_point = buffer.max_point();
13814            let mut is_first = true;
13815            let mut prev_selection_was_entire_line = false;
13816            for selection in &mut selections {
13817                let is_entire_line =
13818                    (selection.is_empty() && cut_no_selection_line) || self.selections.line_mode();
13819                if is_entire_line {
13820                    selection.start = Point::new(selection.start.row, 0);
13821                    if !selection.is_empty() && selection.end.column == 0 {
13822                        selection.end = cmp::min(max_point, selection.end);
13823                    } else {
13824                        selection.end = cmp::min(max_point, Point::new(selection.end.row + 1, 0));
13825                    }
13826                    selection.goal = SelectionGoal::None;
13827                }
13828                if is_first {
13829                    is_first = false;
13830                } else if !prev_selection_was_entire_line {
13831                    text += "\n";
13832                }
13833                prev_selection_was_entire_line = is_entire_line;
13834                let mut len = 0;
13835                for chunk in buffer.text_for_range(selection.start..selection.end) {
13836                    text.push_str(chunk);
13837                    len += chunk.len();
13838                }
13839
13840                clipboard_selections.push(ClipboardSelection::for_buffer(
13841                    len,
13842                    is_entire_line,
13843                    selection.range(),
13844                    &buffer,
13845                    self.project.as_ref(),
13846                    cx,
13847                ));
13848            }
13849        }
13850
13851        self.transact(window, cx, |this, window, cx| {
13852            this.change_selections(Default::default(), window, cx, |s| {
13853                s.select(selections);
13854            });
13855            this.insert("", window, cx);
13856        });
13857        ClipboardItem::new_string_with_json_metadata(text, clipboard_selections)
13858    }
13859
13860    pub fn cut(&mut self, _: &Cut, window: &mut Window, cx: &mut Context<Self>) {
13861        self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
13862        let item = self.cut_common(true, window, cx);
13863        cx.write_to_clipboard(item);
13864    }
13865
13866    pub fn kill_ring_cut(&mut self, _: &KillRingCut, window: &mut Window, cx: &mut Context<Self>) {
13867        self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
13868        self.change_selections(SelectionEffects::no_scroll(), window, cx, |s| {
13869            s.move_with(&mut |snapshot, sel| {
13870                if sel.is_empty() {
13871                    sel.end = DisplayPoint::new(sel.end.row(), snapshot.line_len(sel.end.row()));
13872                }
13873                if sel.is_empty() {
13874                    sel.end = DisplayPoint::new(sel.end.row() + 1_u32, 0);
13875                }
13876            });
13877        });
13878        let item = self.cut_common(false, window, cx);
13879        cx.set_global(KillRing(item))
13880    }
13881
13882    pub fn kill_ring_yank(
13883        &mut self,
13884        _: &KillRingYank,
13885        window: &mut Window,
13886        cx: &mut Context<Self>,
13887    ) {
13888        self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
13889        let (text, metadata) = if let Some(KillRing(item)) = cx.try_global() {
13890            if let Some(ClipboardEntry::String(kill_ring)) = item.entries().first() {
13891                (kill_ring.text().to_string(), kill_ring.metadata_json())
13892            } else {
13893                return;
13894            }
13895        } else {
13896            return;
13897        };
13898        self.do_paste(&text, metadata, false, window, cx);
13899    }
13900
13901    pub fn copy_and_trim(&mut self, _: &CopyAndTrim, _: &mut Window, cx: &mut Context<Self>) {
13902        self.do_copy(true, cx);
13903    }
13904
13905    pub fn copy(&mut self, _: &Copy, _: &mut Window, cx: &mut Context<Self>) {
13906        self.do_copy(false, cx);
13907    }
13908
13909    fn do_copy(&self, strip_leading_indents: bool, cx: &mut Context<Self>) {
13910        let selections = self.selections.all::<Point>(&self.display_snapshot(cx));
13911        let buffer = self.buffer.read(cx).read(cx);
13912        let mut text = String::new();
13913        let mut clipboard_selections = Vec::with_capacity(selections.len());
13914
13915        let max_point = buffer.max_point();
13916        let mut is_first = true;
13917        for selection in &selections {
13918            let mut start = selection.start;
13919            let mut end = selection.end;
13920            let is_entire_line = selection.is_empty() || self.selections.line_mode();
13921            let mut add_trailing_newline = false;
13922            if is_entire_line {
13923                start = Point::new(start.row, 0);
13924                let next_line_start = Point::new(end.row + 1, 0);
13925                if next_line_start <= max_point {
13926                    end = next_line_start;
13927                } else {
13928                    // We're on the last line without a trailing newline.
13929                    // Copy to the end of the line and add a newline afterwards.
13930                    end = Point::new(end.row, buffer.line_len(MultiBufferRow(end.row)));
13931                    add_trailing_newline = true;
13932                }
13933            }
13934
13935            let mut trimmed_selections = Vec::new();
13936            if strip_leading_indents && end.row.saturating_sub(start.row) > 0 {
13937                let row = MultiBufferRow(start.row);
13938                let first_indent = buffer.indent_size_for_line(row);
13939                if first_indent.len == 0 || start.column > first_indent.len {
13940                    trimmed_selections.push(start..end);
13941                } else {
13942                    trimmed_selections.push(
13943                        Point::new(row.0, first_indent.len)
13944                            ..Point::new(row.0, buffer.line_len(row)),
13945                    );
13946                    for row in start.row + 1..=end.row {
13947                        let mut line_len = buffer.line_len(MultiBufferRow(row));
13948                        if row == end.row {
13949                            line_len = end.column;
13950                        }
13951                        if line_len == 0 {
13952                            trimmed_selections.push(Point::new(row, 0)..Point::new(row, line_len));
13953                            continue;
13954                        }
13955                        let row_indent_size = buffer.indent_size_for_line(MultiBufferRow(row));
13956                        if row_indent_size.len >= first_indent.len {
13957                            trimmed_selections
13958                                .push(Point::new(row, first_indent.len)..Point::new(row, line_len));
13959                        } else {
13960                            trimmed_selections.clear();
13961                            trimmed_selections.push(start..end);
13962                            break;
13963                        }
13964                    }
13965                }
13966            } else {
13967                trimmed_selections.push(start..end);
13968            }
13969
13970            let is_multiline_trim = trimmed_selections.len() > 1;
13971            let mut selection_len: usize = 0;
13972            let prev_selection_was_entire_line = is_entire_line && !is_multiline_trim;
13973
13974            for trimmed_range in trimmed_selections {
13975                if is_first {
13976                    is_first = false;
13977                } else if is_multiline_trim || !prev_selection_was_entire_line {
13978                    text.push('\n');
13979                    if is_multiline_trim {
13980                        selection_len += 1;
13981                    }
13982                }
13983                for chunk in buffer.text_for_range(trimmed_range.start..trimmed_range.end) {
13984                    text.push_str(chunk);
13985                    selection_len += chunk.len();
13986                }
13987                if add_trailing_newline {
13988                    text.push('\n');
13989                    selection_len += 1;
13990                }
13991            }
13992
13993            clipboard_selections.push(ClipboardSelection::for_buffer(
13994                selection_len,
13995                is_entire_line,
13996                start..end,
13997                &buffer,
13998                self.project.as_ref(),
13999                cx,
14000            ));
14001        }
14002
14003        cx.write_to_clipboard(ClipboardItem::new_string_with_json_metadata(
14004            text,
14005            clipboard_selections,
14006        ));
14007    }
14008
14009    pub fn do_paste(
14010        &mut self,
14011        text: &String,
14012        clipboard_selections: Option<Vec<ClipboardSelection>>,
14013        handle_entire_lines: bool,
14014        window: &mut Window,
14015        cx: &mut Context<Self>,
14016    ) {
14017        if self.read_only(cx) {
14018            return;
14019        }
14020
14021        self.finalize_last_transaction(cx);
14022
14023        let clipboard_text = Cow::Borrowed(text.as_str());
14024
14025        self.transact(window, cx, |this, window, cx| {
14026            let had_active_edit_prediction = this.has_active_edit_prediction();
14027            let display_map = this.display_snapshot(cx);
14028            let old_selections = this.selections.all::<MultiBufferOffset>(&display_map);
14029            let cursor_offset = this
14030                .selections
14031                .last::<MultiBufferOffset>(&display_map)
14032                .head();
14033
14034            if let Some(mut clipboard_selections) = clipboard_selections {
14035                let all_selections_were_entire_line =
14036                    clipboard_selections.iter().all(|s| s.is_entire_line);
14037                let first_selection_indent_column =
14038                    clipboard_selections.first().map(|s| s.first_line_indent);
14039                if clipboard_selections.len() != old_selections.len() {
14040                    clipboard_selections.drain(..);
14041                }
14042                let mut auto_indent_on_paste = true;
14043
14044                this.buffer.update(cx, |buffer, cx| {
14045                    let snapshot = buffer.read(cx);
14046                    auto_indent_on_paste = snapshot
14047                        .language_settings_at(cursor_offset, cx)
14048                        .auto_indent_on_paste;
14049
14050                    let mut start_offset = 0;
14051                    let mut edits = Vec::new();
14052                    let mut original_indent_columns = Vec::new();
14053                    for (ix, selection) in old_selections.iter().enumerate() {
14054                        let to_insert;
14055                        let entire_line;
14056                        let original_indent_column;
14057                        if let Some(clipboard_selection) = clipboard_selections.get(ix) {
14058                            let end_offset = start_offset + clipboard_selection.len;
14059                            to_insert = &clipboard_text[start_offset..end_offset];
14060                            entire_line = clipboard_selection.is_entire_line;
14061                            start_offset = if entire_line {
14062                                end_offset
14063                            } else {
14064                                end_offset + 1
14065                            };
14066                            original_indent_column = Some(clipboard_selection.first_line_indent);
14067                        } else {
14068                            to_insert = &*clipboard_text;
14069                            entire_line = all_selections_were_entire_line;
14070                            original_indent_column = first_selection_indent_column
14071                        }
14072
14073                        let (range, to_insert) =
14074                            if selection.is_empty() && handle_entire_lines && entire_line {
14075                                // If the corresponding selection was empty when this slice of the
14076                                // clipboard text was written, then the entire line containing the
14077                                // selection was copied. If this selection is also currently empty,
14078                                // then paste the line before the current line of the buffer.
14079                                let column = selection.start.to_point(&snapshot).column as usize;
14080                                let line_start = selection.start - column;
14081                                (line_start..line_start, Cow::Borrowed(to_insert))
14082                            } else {
14083                                let language = snapshot.language_at(selection.head());
14084                                let range = selection.range();
14085                                if let Some(language) = language
14086                                    && language.name() == "Markdown"
14087                                {
14088                                    edit_for_markdown_paste(
14089                                        &snapshot,
14090                                        range,
14091                                        to_insert,
14092                                        url::Url::parse(to_insert).ok(),
14093                                    )
14094                                } else {
14095                                    (range, Cow::Borrowed(to_insert))
14096                                }
14097                            };
14098
14099                        edits.push((range, to_insert));
14100                        original_indent_columns.push(original_indent_column);
14101                    }
14102                    drop(snapshot);
14103
14104                    buffer.edit(
14105                        edits,
14106                        if auto_indent_on_paste {
14107                            Some(AutoindentMode::Block {
14108                                original_indent_columns,
14109                            })
14110                        } else {
14111                            None
14112                        },
14113                        cx,
14114                    );
14115                });
14116
14117                let selections = this
14118                    .selections
14119                    .all::<MultiBufferOffset>(&this.display_snapshot(cx));
14120                this.change_selections(Default::default(), window, cx, |s| s.select(selections));
14121            } else {
14122                let url = url::Url::parse(&clipboard_text).ok();
14123
14124                let auto_indent_mode = if !clipboard_text.is_empty() {
14125                    Some(AutoindentMode::Block {
14126                        original_indent_columns: Vec::new(),
14127                    })
14128                } else {
14129                    None
14130                };
14131
14132                let selection_anchors = this.buffer.update(cx, |buffer, cx| {
14133                    let snapshot = buffer.snapshot(cx);
14134
14135                    let anchors = old_selections
14136                        .iter()
14137                        .map(|s| {
14138                            let anchor = snapshot.anchor_after(s.head());
14139                            s.map(|_| anchor)
14140                        })
14141                        .collect::<Vec<_>>();
14142
14143                    let mut edits = Vec::new();
14144
14145                    // When pasting text without metadata (e.g. copied from an
14146                    // external editor using multiple cursors) and the number of
14147                    // lines matches the number of selections, distribute one
14148                    // line per cursor instead of pasting the whole text at each.
14149                    let lines: Vec<&str> = clipboard_text.split('\n').collect();
14150                    let distribute_lines =
14151                        old_selections.len() > 1 && lines.len() == old_selections.len();
14152
14153                    for (ix, selection) in old_selections.iter().enumerate() {
14154                        let language = snapshot.language_at(selection.head());
14155                        let range = selection.range();
14156
14157                        let text_for_cursor: &str = if distribute_lines {
14158                            lines[ix]
14159                        } else {
14160                            &clipboard_text
14161                        };
14162
14163                        let (edit_range, edit_text) = if let Some(language) = language
14164                            && language.name() == "Markdown"
14165                        {
14166                            edit_for_markdown_paste(&snapshot, range, text_for_cursor, url.clone())
14167                        } else {
14168                            (range, Cow::Borrowed(text_for_cursor))
14169                        };
14170
14171                        edits.push((edit_range, edit_text));
14172                    }
14173
14174                    drop(snapshot);
14175                    buffer.edit(edits, auto_indent_mode, cx);
14176
14177                    anchors
14178                });
14179
14180                this.change_selections(Default::default(), window, cx, |s| {
14181                    s.select_anchors(selection_anchors);
14182                });
14183            }
14184
14185            //   🤔                 |    ..     | show_in_menu |
14186            // | ..                  |   true        true
14187            // | had_edit_prediction |   false       true
14188
14189            let trigger_in_words =
14190                this.show_edit_predictions_in_menu() || !had_active_edit_prediction;
14191
14192            this.trigger_completion_on_input(text, trigger_in_words, window, cx);
14193        });
14194    }
14195
14196    pub fn diff_clipboard_with_selection(
14197        &mut self,
14198        _: &DiffClipboardWithSelection,
14199        window: &mut Window,
14200        cx: &mut Context<Self>,
14201    ) {
14202        let selections = self
14203            .selections
14204            .all::<MultiBufferOffset>(&self.display_snapshot(cx));
14205
14206        if selections.is_empty() {
14207            log::warn!("There should always be at least one selection in Zed. This is a bug.");
14208            return;
14209        };
14210
14211        let clipboard_text = cx.read_from_clipboard().and_then(|item| {
14212            item.entries().iter().find_map(|entry| match entry {
14213                ClipboardEntry::String(text) => Some(text.text().to_string()),
14214                _ => None,
14215            })
14216        });
14217
14218        let Some(clipboard_text) = clipboard_text else {
14219            log::warn!("Clipboard doesn't contain text.");
14220            return;
14221        };
14222
14223        window.dispatch_action(
14224            Box::new(DiffClipboardWithSelectionData {
14225                clipboard_text,
14226                editor: cx.entity(),
14227            }),
14228            cx,
14229        );
14230    }
14231
14232    pub fn paste(&mut self, _: &Paste, window: &mut Window, cx: &mut Context<Self>) {
14233        self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
14234        if let Some(item) = cx.read_from_clipboard() {
14235            let clipboard_string = item.entries().iter().find_map(|entry| match entry {
14236                ClipboardEntry::String(s) => Some(s),
14237                _ => None,
14238            });
14239            match clipboard_string {
14240                Some(clipboard_string) => self.do_paste(
14241                    clipboard_string.text(),
14242                    clipboard_string.metadata_json::<Vec<ClipboardSelection>>(),
14243                    true,
14244                    window,
14245                    cx,
14246                ),
14247                _ => self.do_paste(&item.text().unwrap_or_default(), None, true, window, cx),
14248            }
14249        }
14250    }
14251
14252    pub fn undo(&mut self, _: &Undo, window: &mut Window, cx: &mut Context<Self>) {
14253        if self.read_only(cx) {
14254            return;
14255        }
14256
14257        self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
14258
14259        if let Some(transaction_id) = self.buffer.update(cx, |buffer, cx| buffer.undo(cx)) {
14260            if let Some((selections, _)) =
14261                self.selection_history.transaction(transaction_id).cloned()
14262            {
14263                self.change_selections(SelectionEffects::no_scroll(), window, cx, |s| {
14264                    s.select_anchors(selections.to_vec());
14265                });
14266            } else {
14267                log::error!(
14268                    "No entry in selection_history found for undo. \
14269                     This may correspond to a bug where undo does not update the selection. \
14270                     If this is occurring, please add details to \
14271                     https://github.com/zed-industries/zed/issues/22692"
14272                );
14273            }
14274            self.request_autoscroll(Autoscroll::fit(), cx);
14275            self.unmark_text(window, cx);
14276            self.refresh_edit_prediction(true, false, window, cx);
14277            cx.emit(EditorEvent::Edited { transaction_id });
14278            cx.emit(EditorEvent::TransactionUndone { transaction_id });
14279        }
14280    }
14281
14282    pub fn redo(&mut self, _: &Redo, window: &mut Window, cx: &mut Context<Self>) {
14283        if self.read_only(cx) {
14284            return;
14285        }
14286
14287        self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
14288
14289        if let Some(transaction_id) = self.buffer.update(cx, |buffer, cx| buffer.redo(cx)) {
14290            if let Some((_, Some(selections))) =
14291                self.selection_history.transaction(transaction_id).cloned()
14292            {
14293                self.change_selections(SelectionEffects::no_scroll(), window, cx, |s| {
14294                    s.select_anchors(selections.to_vec());
14295                });
14296            } else {
14297                log::error!(
14298                    "No entry in selection_history found for redo. \
14299                     This may correspond to a bug where undo does not update the selection. \
14300                     If this is occurring, please add details to \
14301                     https://github.com/zed-industries/zed/issues/22692"
14302                );
14303            }
14304            self.request_autoscroll(Autoscroll::fit(), cx);
14305            self.unmark_text(window, cx);
14306            self.refresh_edit_prediction(true, false, window, cx);
14307            cx.emit(EditorEvent::Edited { transaction_id });
14308        }
14309    }
14310
14311    pub fn finalize_last_transaction(&mut self, cx: &mut Context<Self>) {
14312        self.buffer
14313            .update(cx, |buffer, cx| buffer.finalize_last_transaction(cx));
14314    }
14315
14316    pub fn group_until_transaction(&mut self, tx_id: TransactionId, cx: &mut Context<Self>) {
14317        self.buffer
14318            .update(cx, |buffer, cx| buffer.group_until_transaction(tx_id, cx));
14319    }
14320
14321    pub fn move_left(&mut self, _: &MoveLeft, window: &mut Window, cx: &mut Context<Self>) {
14322        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
14323        self.change_selections(Default::default(), window, cx, |s| {
14324            s.move_with(&mut |map, selection| {
14325                let cursor = if selection.is_empty() {
14326                    movement::left(map, selection.start)
14327                } else {
14328                    selection.start
14329                };
14330                selection.collapse_to(cursor, SelectionGoal::None);
14331            });
14332        })
14333    }
14334
14335    pub fn select_left(&mut self, _: &SelectLeft, window: &mut Window, cx: &mut Context<Self>) {
14336        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
14337        self.change_selections(Default::default(), window, cx, |s| {
14338            s.move_heads_with(&mut |map, head, _| (movement::left(map, head), SelectionGoal::None));
14339        })
14340    }
14341
14342    pub fn move_right(&mut self, _: &MoveRight, window: &mut Window, cx: &mut Context<Self>) {
14343        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
14344        self.change_selections(Default::default(), window, cx, |s| {
14345            s.move_with(&mut |map, selection| {
14346                let cursor = if selection.is_empty() {
14347                    movement::right(map, selection.end)
14348                } else {
14349                    selection.end
14350                };
14351                selection.collapse_to(cursor, SelectionGoal::None)
14352            });
14353        })
14354    }
14355
14356    pub fn select_right(&mut self, _: &SelectRight, window: &mut Window, cx: &mut Context<Self>) {
14357        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
14358        self.change_selections(Default::default(), window, cx, |s| {
14359            s.move_heads_with(&mut |map, head, _| {
14360                (movement::right(map, head), SelectionGoal::None)
14361            });
14362        });
14363    }
14364
14365    pub fn move_up(&mut self, _: &MoveUp, window: &mut Window, cx: &mut Context<Self>) {
14366        if self.take_rename(true, window, cx).is_some() {
14367            return;
14368        }
14369
14370        if self.mode.is_single_line() {
14371            cx.propagate();
14372            return;
14373        }
14374
14375        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
14376
14377        let text_layout_details = &self.text_layout_details(window, cx);
14378        let selection_count = self.selections.count();
14379        let first_selection = self.selections.first_anchor();
14380
14381        self.change_selections(Default::default(), window, cx, |s| {
14382            s.move_with(&mut |map, selection| {
14383                if !selection.is_empty() {
14384                    selection.goal = SelectionGoal::None;
14385                }
14386                let (cursor, goal) = movement::up(
14387                    map,
14388                    selection.start,
14389                    selection.goal,
14390                    false,
14391                    text_layout_details,
14392                );
14393                selection.collapse_to(cursor, goal);
14394            });
14395        });
14396
14397        if selection_count == 1 && first_selection.range() == self.selections.first_anchor().range()
14398        {
14399            cx.propagate();
14400        }
14401    }
14402
14403    pub fn move_up_by_lines(
14404        &mut self,
14405        action: &MoveUpByLines,
14406        window: &mut Window,
14407        cx: &mut Context<Self>,
14408    ) {
14409        if self.take_rename(true, window, cx).is_some() {
14410            return;
14411        }
14412
14413        if self.mode.is_single_line() {
14414            cx.propagate();
14415            return;
14416        }
14417
14418        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
14419
14420        let text_layout_details = &self.text_layout_details(window, cx);
14421
14422        self.change_selections(Default::default(), window, cx, |s| {
14423            s.move_with(&mut |map, selection| {
14424                if !selection.is_empty() {
14425                    selection.goal = SelectionGoal::None;
14426                }
14427                let (cursor, goal) = movement::up_by_rows(
14428                    map,
14429                    selection.start,
14430                    action.lines,
14431                    selection.goal,
14432                    false,
14433                    text_layout_details,
14434                );
14435                selection.collapse_to(cursor, goal);
14436            });
14437        })
14438    }
14439
14440    pub fn move_down_by_lines(
14441        &mut self,
14442        action: &MoveDownByLines,
14443        window: &mut Window,
14444        cx: &mut Context<Self>,
14445    ) {
14446        if self.take_rename(true, window, cx).is_some() {
14447            return;
14448        }
14449
14450        if self.mode.is_single_line() {
14451            cx.propagate();
14452            return;
14453        }
14454
14455        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
14456
14457        let text_layout_details = &self.text_layout_details(window, cx);
14458
14459        self.change_selections(Default::default(), window, cx, |s| {
14460            s.move_with(&mut |map, selection| {
14461                if !selection.is_empty() {
14462                    selection.goal = SelectionGoal::None;
14463                }
14464                let (cursor, goal) = movement::down_by_rows(
14465                    map,
14466                    selection.start,
14467                    action.lines,
14468                    selection.goal,
14469                    false,
14470                    text_layout_details,
14471                );
14472                selection.collapse_to(cursor, goal);
14473            });
14474        })
14475    }
14476
14477    pub fn select_down_by_lines(
14478        &mut self,
14479        action: &SelectDownByLines,
14480        window: &mut Window,
14481        cx: &mut Context<Self>,
14482    ) {
14483        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
14484        let text_layout_details = &self.text_layout_details(window, cx);
14485        self.change_selections(Default::default(), window, cx, |s| {
14486            s.move_heads_with(&mut |map, head, goal| {
14487                movement::down_by_rows(map, head, action.lines, goal, false, text_layout_details)
14488            })
14489        })
14490    }
14491
14492    pub fn select_up_by_lines(
14493        &mut self,
14494        action: &SelectUpByLines,
14495        window: &mut Window,
14496        cx: &mut Context<Self>,
14497    ) {
14498        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
14499        let text_layout_details = &self.text_layout_details(window, cx);
14500        self.change_selections(Default::default(), window, cx, |s| {
14501            s.move_heads_with(&mut |map, head, goal| {
14502                movement::up_by_rows(map, head, action.lines, goal, false, text_layout_details)
14503            })
14504        })
14505    }
14506
14507    pub fn select_page_up(
14508        &mut self,
14509        _: &SelectPageUp,
14510        window: &mut Window,
14511        cx: &mut Context<Self>,
14512    ) {
14513        let Some(row_count) = self.visible_row_count() else {
14514            return;
14515        };
14516
14517        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
14518
14519        let text_layout_details = &self.text_layout_details(window, cx);
14520
14521        self.change_selections(Default::default(), window, cx, |s| {
14522            s.move_heads_with(&mut |map, head, goal| {
14523                movement::up_by_rows(map, head, row_count, goal, false, text_layout_details)
14524            })
14525        })
14526    }
14527
14528    pub fn move_page_up(
14529        &mut self,
14530        action: &MovePageUp,
14531        window: &mut Window,
14532        cx: &mut Context<Self>,
14533    ) {
14534        if self.take_rename(true, window, cx).is_some() {
14535            return;
14536        }
14537
14538        if self
14539            .context_menu
14540            .borrow_mut()
14541            .as_mut()
14542            .map(|menu| menu.select_first(self.completion_provider.as_deref(), window, cx))
14543            .unwrap_or(false)
14544        {
14545            return;
14546        }
14547
14548        if matches!(self.mode, EditorMode::SingleLine) {
14549            cx.propagate();
14550            return;
14551        }
14552
14553        let Some(row_count) = self.visible_row_count() else {
14554            return;
14555        };
14556
14557        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
14558
14559        let effects = if action.center_cursor {
14560            SelectionEffects::scroll(Autoscroll::center())
14561        } else {
14562            SelectionEffects::default()
14563        };
14564
14565        let text_layout_details = &self.text_layout_details(window, cx);
14566
14567        self.change_selections(effects, window, cx, |s| {
14568            s.move_with(&mut |map, selection| {
14569                if !selection.is_empty() {
14570                    selection.goal = SelectionGoal::None;
14571                }
14572                let (cursor, goal) = movement::up_by_rows(
14573                    map,
14574                    selection.end,
14575                    row_count,
14576                    selection.goal,
14577                    false,
14578                    text_layout_details,
14579                );
14580                selection.collapse_to(cursor, goal);
14581            });
14582        });
14583    }
14584
14585    pub fn select_up(&mut self, _: &SelectUp, window: &mut Window, cx: &mut Context<Self>) {
14586        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
14587        let text_layout_details = &self.text_layout_details(window, cx);
14588        self.change_selections(Default::default(), window, cx, |s| {
14589            s.move_heads_with(&mut |map, head, goal| {
14590                movement::up(map, head, goal, false, text_layout_details)
14591            })
14592        })
14593    }
14594
14595    pub fn move_down(&mut self, _: &MoveDown, window: &mut Window, cx: &mut Context<Self>) {
14596        self.take_rename(true, window, cx);
14597
14598        if self.mode.is_single_line() {
14599            cx.propagate();
14600            return;
14601        }
14602
14603        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
14604
14605        let text_layout_details = &self.text_layout_details(window, cx);
14606        let selection_count = self.selections.count();
14607        let first_selection = self.selections.first_anchor();
14608
14609        self.change_selections(Default::default(), window, cx, |s| {
14610            s.move_with(&mut |map, selection| {
14611                if !selection.is_empty() {
14612                    selection.goal = SelectionGoal::None;
14613                }
14614                let (cursor, goal) = movement::down(
14615                    map,
14616                    selection.end,
14617                    selection.goal,
14618                    false,
14619                    text_layout_details,
14620                );
14621                selection.collapse_to(cursor, goal);
14622            });
14623        });
14624
14625        if selection_count == 1 && first_selection.range() == self.selections.first_anchor().range()
14626        {
14627            cx.propagate();
14628        }
14629    }
14630
14631    pub fn select_page_down(
14632        &mut self,
14633        _: &SelectPageDown,
14634        window: &mut Window,
14635        cx: &mut Context<Self>,
14636    ) {
14637        let Some(row_count) = self.visible_row_count() else {
14638            return;
14639        };
14640
14641        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
14642
14643        let text_layout_details = &self.text_layout_details(window, cx);
14644
14645        self.change_selections(Default::default(), window, cx, |s| {
14646            s.move_heads_with(&mut |map, head, goal| {
14647                movement::down_by_rows(map, head, row_count, goal, false, text_layout_details)
14648            })
14649        })
14650    }
14651
14652    pub fn move_page_down(
14653        &mut self,
14654        action: &MovePageDown,
14655        window: &mut Window,
14656        cx: &mut Context<Self>,
14657    ) {
14658        if self.take_rename(true, window, cx).is_some() {
14659            return;
14660        }
14661
14662        if self
14663            .context_menu
14664            .borrow_mut()
14665            .as_mut()
14666            .map(|menu| menu.select_last(self.completion_provider.as_deref(), window, cx))
14667            .unwrap_or(false)
14668        {
14669            return;
14670        }
14671
14672        if matches!(self.mode, EditorMode::SingleLine) {
14673            cx.propagate();
14674            return;
14675        }
14676
14677        let Some(row_count) = self.visible_row_count() else {
14678            return;
14679        };
14680
14681        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
14682
14683        let effects = if action.center_cursor {
14684            SelectionEffects::scroll(Autoscroll::center())
14685        } else {
14686            SelectionEffects::default()
14687        };
14688
14689        let text_layout_details = &self.text_layout_details(window, cx);
14690        self.change_selections(effects, window, cx, |s| {
14691            s.move_with(&mut |map, selection| {
14692                if !selection.is_empty() {
14693                    selection.goal = SelectionGoal::None;
14694                }
14695                let (cursor, goal) = movement::down_by_rows(
14696                    map,
14697                    selection.end,
14698                    row_count,
14699                    selection.goal,
14700                    false,
14701                    text_layout_details,
14702                );
14703                selection.collapse_to(cursor, goal);
14704            });
14705        });
14706    }
14707
14708    pub fn select_down(&mut self, _: &SelectDown, window: &mut Window, cx: &mut Context<Self>) {
14709        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
14710        let text_layout_details = &self.text_layout_details(window, cx);
14711        self.change_selections(Default::default(), window, cx, |s| {
14712            s.move_heads_with(&mut |map, head, goal| {
14713                movement::down(map, head, goal, false, text_layout_details)
14714            })
14715        });
14716    }
14717
14718    pub fn context_menu_first(
14719        &mut self,
14720        _: &ContextMenuFirst,
14721        window: &mut Window,
14722        cx: &mut Context<Self>,
14723    ) {
14724        if let Some(context_menu) = self.context_menu.borrow_mut().as_mut() {
14725            context_menu.select_first(self.completion_provider.as_deref(), window, cx);
14726        }
14727    }
14728
14729    pub fn context_menu_prev(
14730        &mut self,
14731        _: &ContextMenuPrevious,
14732        window: &mut Window,
14733        cx: &mut Context<Self>,
14734    ) {
14735        if let Some(context_menu) = self.context_menu.borrow_mut().as_mut() {
14736            context_menu.select_prev(self.completion_provider.as_deref(), window, cx);
14737        }
14738    }
14739
14740    pub fn context_menu_next(
14741        &mut self,
14742        _: &ContextMenuNext,
14743        window: &mut Window,
14744        cx: &mut Context<Self>,
14745    ) {
14746        if let Some(context_menu) = self.context_menu.borrow_mut().as_mut() {
14747            context_menu.select_next(self.completion_provider.as_deref(), window, cx);
14748        }
14749    }
14750
14751    pub fn context_menu_last(
14752        &mut self,
14753        _: &ContextMenuLast,
14754        window: &mut Window,
14755        cx: &mut Context<Self>,
14756    ) {
14757        if let Some(context_menu) = self.context_menu.borrow_mut().as_mut() {
14758            context_menu.select_last(self.completion_provider.as_deref(), window, cx);
14759        }
14760    }
14761
14762    pub fn signature_help_prev(
14763        &mut self,
14764        _: &SignatureHelpPrevious,
14765        _: &mut Window,
14766        cx: &mut Context<Self>,
14767    ) {
14768        if let Some(popover) = self.signature_help_state.popover_mut() {
14769            if popover.current_signature == 0 {
14770                popover.current_signature = popover.signatures.len() - 1;
14771            } else {
14772                popover.current_signature -= 1;
14773            }
14774            cx.notify();
14775        }
14776    }
14777
14778    pub fn signature_help_next(
14779        &mut self,
14780        _: &SignatureHelpNext,
14781        _: &mut Window,
14782        cx: &mut Context<Self>,
14783    ) {
14784        if let Some(popover) = self.signature_help_state.popover_mut() {
14785            if popover.current_signature + 1 == popover.signatures.len() {
14786                popover.current_signature = 0;
14787            } else {
14788                popover.current_signature += 1;
14789            }
14790            cx.notify();
14791        }
14792    }
14793
14794    pub fn move_to_previous_word_start(
14795        &mut self,
14796        _: &MoveToPreviousWordStart,
14797        window: &mut Window,
14798        cx: &mut Context<Self>,
14799    ) {
14800        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
14801        self.change_selections(Default::default(), window, cx, |s| {
14802            s.move_cursors_with(&mut |map, head, _| {
14803                (
14804                    movement::previous_word_start(map, head),
14805                    SelectionGoal::None,
14806                )
14807            });
14808        })
14809    }
14810
14811    pub fn move_to_previous_subword_start(
14812        &mut self,
14813        _: &MoveToPreviousSubwordStart,
14814        window: &mut Window,
14815        cx: &mut Context<Self>,
14816    ) {
14817        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
14818        self.change_selections(Default::default(), window, cx, |s| {
14819            s.move_cursors_with(&mut |map, head, _| {
14820                (
14821                    movement::previous_subword_start(map, head),
14822                    SelectionGoal::None,
14823                )
14824            });
14825        })
14826    }
14827
14828    pub fn select_to_previous_word_start(
14829        &mut self,
14830        _: &SelectToPreviousWordStart,
14831        window: &mut Window,
14832        cx: &mut Context<Self>,
14833    ) {
14834        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
14835        self.change_selections(Default::default(), window, cx, |s| {
14836            s.move_heads_with(&mut |map, head, _| {
14837                (
14838                    movement::previous_word_start(map, head),
14839                    SelectionGoal::None,
14840                )
14841            });
14842        })
14843    }
14844
14845    pub fn select_to_previous_subword_start(
14846        &mut self,
14847        _: &SelectToPreviousSubwordStart,
14848        window: &mut Window,
14849        cx: &mut Context<Self>,
14850    ) {
14851        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
14852        self.change_selections(Default::default(), window, cx, |s| {
14853            s.move_heads_with(&mut |map, head, _| {
14854                (
14855                    movement::previous_subword_start(map, head),
14856                    SelectionGoal::None,
14857                )
14858            });
14859        })
14860    }
14861
14862    pub fn delete_to_previous_word_start(
14863        &mut self,
14864        action: &DeleteToPreviousWordStart,
14865        window: &mut Window,
14866        cx: &mut Context<Self>,
14867    ) {
14868        self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
14869        self.transact(window, cx, |this, window, cx| {
14870            this.select_autoclose_pair(window, cx);
14871            this.change_selections(Default::default(), window, cx, |s| {
14872                s.move_with(&mut |map, selection| {
14873                    if selection.is_empty() {
14874                        let mut cursor = if action.ignore_newlines {
14875                            movement::previous_word_start(map, selection.head())
14876                        } else {
14877                            movement::previous_word_start_or_newline(map, selection.head())
14878                        };
14879                        cursor = movement::adjust_greedy_deletion(
14880                            map,
14881                            selection.head(),
14882                            cursor,
14883                            action.ignore_brackets,
14884                        );
14885                        selection.set_head(cursor, SelectionGoal::None);
14886                    }
14887                });
14888            });
14889            this.insert("", window, cx);
14890        });
14891    }
14892
14893    pub fn delete_to_previous_subword_start(
14894        &mut self,
14895        action: &DeleteToPreviousSubwordStart,
14896        window: &mut Window,
14897        cx: &mut Context<Self>,
14898    ) {
14899        self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
14900        self.transact(window, cx, |this, window, cx| {
14901            this.select_autoclose_pair(window, cx);
14902            this.change_selections(Default::default(), window, cx, |s| {
14903                s.move_with(&mut |map, selection| {
14904                    if selection.is_empty() {
14905                        let mut cursor = if action.ignore_newlines {
14906                            movement::previous_subword_start(map, selection.head())
14907                        } else {
14908                            movement::previous_subword_start_or_newline(map, selection.head())
14909                        };
14910                        cursor = movement::adjust_greedy_deletion(
14911                            map,
14912                            selection.head(),
14913                            cursor,
14914                            action.ignore_brackets,
14915                        );
14916                        selection.set_head(cursor, SelectionGoal::None);
14917                    }
14918                });
14919            });
14920            this.insert("", window, cx);
14921        });
14922    }
14923
14924    pub fn move_to_next_word_end(
14925        &mut self,
14926        _: &MoveToNextWordEnd,
14927        window: &mut Window,
14928        cx: &mut Context<Self>,
14929    ) {
14930        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
14931        self.change_selections(Default::default(), window, cx, |s| {
14932            s.move_cursors_with(&mut |map, head, _| {
14933                (movement::next_word_end(map, head), SelectionGoal::None)
14934            });
14935        })
14936    }
14937
14938    pub fn move_to_next_subword_end(
14939        &mut self,
14940        _: &MoveToNextSubwordEnd,
14941        window: &mut Window,
14942        cx: &mut Context<Self>,
14943    ) {
14944        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
14945        self.change_selections(Default::default(), window, cx, |s| {
14946            s.move_cursors_with(&mut |map, head, _| {
14947                (movement::next_subword_end(map, head), SelectionGoal::None)
14948            });
14949        })
14950    }
14951
14952    pub fn select_to_next_word_end(
14953        &mut self,
14954        _: &SelectToNextWordEnd,
14955        window: &mut Window,
14956        cx: &mut Context<Self>,
14957    ) {
14958        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
14959        self.change_selections(Default::default(), window, cx, |s| {
14960            s.move_heads_with(&mut |map, head, _| {
14961                (movement::next_word_end(map, head), SelectionGoal::None)
14962            });
14963        })
14964    }
14965
14966    pub fn select_to_next_subword_end(
14967        &mut self,
14968        _: &SelectToNextSubwordEnd,
14969        window: &mut Window,
14970        cx: &mut Context<Self>,
14971    ) {
14972        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
14973        self.change_selections(Default::default(), window, cx, |s| {
14974            s.move_heads_with(&mut |map, head, _| {
14975                (movement::next_subword_end(map, head), SelectionGoal::None)
14976            });
14977        })
14978    }
14979
14980    pub fn delete_to_next_word_end(
14981        &mut self,
14982        action: &DeleteToNextWordEnd,
14983        window: &mut Window,
14984        cx: &mut Context<Self>,
14985    ) {
14986        self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
14987        self.transact(window, cx, |this, window, cx| {
14988            this.change_selections(Default::default(), window, cx, |s| {
14989                s.move_with(&mut |map, selection| {
14990                    if selection.is_empty() {
14991                        let mut cursor = if action.ignore_newlines {
14992                            movement::next_word_end(map, selection.head())
14993                        } else {
14994                            movement::next_word_end_or_newline(map, selection.head())
14995                        };
14996                        cursor = movement::adjust_greedy_deletion(
14997                            map,
14998                            selection.head(),
14999                            cursor,
15000                            action.ignore_brackets,
15001                        );
15002                        selection.set_head(cursor, SelectionGoal::None);
15003                    }
15004                });
15005            });
15006            this.insert("", window, cx);
15007        });
15008    }
15009
15010    pub fn delete_to_next_subword_end(
15011        &mut self,
15012        action: &DeleteToNextSubwordEnd,
15013        window: &mut Window,
15014        cx: &mut Context<Self>,
15015    ) {
15016        self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
15017        self.transact(window, cx, |this, window, cx| {
15018            this.change_selections(Default::default(), window, cx, |s| {
15019                s.move_with(&mut |map, selection| {
15020                    if selection.is_empty() {
15021                        let mut cursor = if action.ignore_newlines {
15022                            movement::next_subword_end(map, selection.head())
15023                        } else {
15024                            movement::next_subword_end_or_newline(map, selection.head())
15025                        };
15026                        cursor = movement::adjust_greedy_deletion(
15027                            map,
15028                            selection.head(),
15029                            cursor,
15030                            action.ignore_brackets,
15031                        );
15032                        selection.set_head(cursor, SelectionGoal::None);
15033                    }
15034                });
15035            });
15036            this.insert("", window, cx);
15037        });
15038    }
15039
15040    pub fn move_to_beginning_of_line(
15041        &mut self,
15042        action: &MoveToBeginningOfLine,
15043        window: &mut Window,
15044        cx: &mut Context<Self>,
15045    ) {
15046        let stop_at_indent = action.stop_at_indent && !self.mode.is_single_line();
15047        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
15048        self.change_selections(Default::default(), window, cx, |s| {
15049            s.move_cursors_with(&mut |map, head, _| {
15050                (
15051                    movement::indented_line_beginning(
15052                        map,
15053                        head,
15054                        action.stop_at_soft_wraps,
15055                        stop_at_indent,
15056                    ),
15057                    SelectionGoal::None,
15058                )
15059            });
15060        })
15061    }
15062
15063    pub fn select_to_beginning_of_line(
15064        &mut self,
15065        action: &SelectToBeginningOfLine,
15066        window: &mut Window,
15067        cx: &mut Context<Self>,
15068    ) {
15069        let stop_at_indent = action.stop_at_indent && !self.mode.is_single_line();
15070        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
15071        self.change_selections(Default::default(), window, cx, |s| {
15072            s.move_heads_with(&mut |map, head, _| {
15073                (
15074                    movement::indented_line_beginning(
15075                        map,
15076                        head,
15077                        action.stop_at_soft_wraps,
15078                        stop_at_indent,
15079                    ),
15080                    SelectionGoal::None,
15081                )
15082            });
15083        });
15084    }
15085
15086    pub fn delete_to_beginning_of_line(
15087        &mut self,
15088        action: &DeleteToBeginningOfLine,
15089        window: &mut Window,
15090        cx: &mut Context<Self>,
15091    ) {
15092        self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
15093        self.transact(window, cx, |this, window, cx| {
15094            this.change_selections(Default::default(), window, cx, |s| {
15095                s.move_with(&mut |_, selection| {
15096                    selection.reversed = true;
15097                });
15098            });
15099
15100            this.select_to_beginning_of_line(
15101                &SelectToBeginningOfLine {
15102                    stop_at_soft_wraps: false,
15103                    stop_at_indent: action.stop_at_indent,
15104                },
15105                window,
15106                cx,
15107            );
15108            this.backspace(&Backspace, window, cx);
15109        });
15110    }
15111
15112    pub fn move_to_end_of_line(
15113        &mut self,
15114        action: &MoveToEndOfLine,
15115        window: &mut Window,
15116        cx: &mut Context<Self>,
15117    ) {
15118        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
15119        self.change_selections(Default::default(), window, cx, |s| {
15120            s.move_cursors_with(&mut |map, head, _| {
15121                (
15122                    movement::line_end(map, head, action.stop_at_soft_wraps),
15123                    SelectionGoal::None,
15124                )
15125            });
15126        })
15127    }
15128
15129    pub fn select_to_end_of_line(
15130        &mut self,
15131        action: &SelectToEndOfLine,
15132        window: &mut Window,
15133        cx: &mut Context<Self>,
15134    ) {
15135        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
15136        self.change_selections(Default::default(), window, cx, |s| {
15137            s.move_heads_with(&mut |map, head, _| {
15138                (
15139                    movement::line_end(map, head, action.stop_at_soft_wraps),
15140                    SelectionGoal::None,
15141                )
15142            });
15143        })
15144    }
15145
15146    pub fn delete_to_end_of_line(
15147        &mut self,
15148        _: &DeleteToEndOfLine,
15149        window: &mut Window,
15150        cx: &mut Context<Self>,
15151    ) {
15152        self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
15153        self.transact(window, cx, |this, window, cx| {
15154            this.select_to_end_of_line(
15155                &SelectToEndOfLine {
15156                    stop_at_soft_wraps: false,
15157                },
15158                window,
15159                cx,
15160            );
15161            this.delete(&Delete, window, cx);
15162        });
15163    }
15164
15165    pub fn cut_to_end_of_line(
15166        &mut self,
15167        action: &CutToEndOfLine,
15168        window: &mut Window,
15169        cx: &mut Context<Self>,
15170    ) {
15171        self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
15172        self.transact(window, cx, |this, window, cx| {
15173            this.select_to_end_of_line(
15174                &SelectToEndOfLine {
15175                    stop_at_soft_wraps: false,
15176                },
15177                window,
15178                cx,
15179            );
15180            if !action.stop_at_newlines {
15181                this.change_selections(Default::default(), window, cx, |s| {
15182                    s.move_with(&mut |_, sel| {
15183                        if sel.is_empty() {
15184                            sel.end = DisplayPoint::new(sel.end.row() + 1_u32, 0);
15185                        }
15186                    });
15187                });
15188            }
15189            this.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
15190            let item = this.cut_common(false, window, cx);
15191            cx.write_to_clipboard(item);
15192        });
15193    }
15194
15195    pub fn move_to_start_of_paragraph(
15196        &mut self,
15197        _: &MoveToStartOfParagraph,
15198        window: &mut Window,
15199        cx: &mut Context<Self>,
15200    ) {
15201        if matches!(self.mode, EditorMode::SingleLine) {
15202            cx.propagate();
15203            return;
15204        }
15205        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
15206        self.change_selections(Default::default(), window, cx, |s| {
15207            s.move_with(&mut |map, selection| {
15208                selection.collapse_to(
15209                    movement::start_of_paragraph(map, selection.head(), 1),
15210                    SelectionGoal::None,
15211                )
15212            });
15213        })
15214    }
15215
15216    pub fn move_to_end_of_paragraph(
15217        &mut self,
15218        _: &MoveToEndOfParagraph,
15219        window: &mut Window,
15220        cx: &mut Context<Self>,
15221    ) {
15222        if matches!(self.mode, EditorMode::SingleLine) {
15223            cx.propagate();
15224            return;
15225        }
15226        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
15227        self.change_selections(Default::default(), window, cx, |s| {
15228            s.move_with(&mut |map, selection| {
15229                selection.collapse_to(
15230                    movement::end_of_paragraph(map, selection.head(), 1),
15231                    SelectionGoal::None,
15232                )
15233            });
15234        })
15235    }
15236
15237    pub fn select_to_start_of_paragraph(
15238        &mut self,
15239        _: &SelectToStartOfParagraph,
15240        window: &mut Window,
15241        cx: &mut Context<Self>,
15242    ) {
15243        if matches!(self.mode, EditorMode::SingleLine) {
15244            cx.propagate();
15245            return;
15246        }
15247        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
15248        self.change_selections(Default::default(), window, cx, |s| {
15249            s.move_heads_with(&mut |map, head, _| {
15250                (
15251                    movement::start_of_paragraph(map, head, 1),
15252                    SelectionGoal::None,
15253                )
15254            });
15255        })
15256    }
15257
15258    pub fn select_to_end_of_paragraph(
15259        &mut self,
15260        _: &SelectToEndOfParagraph,
15261        window: &mut Window,
15262        cx: &mut Context<Self>,
15263    ) {
15264        if matches!(self.mode, EditorMode::SingleLine) {
15265            cx.propagate();
15266            return;
15267        }
15268        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
15269        self.change_selections(Default::default(), window, cx, |s| {
15270            s.move_heads_with(&mut |map, head, _| {
15271                (
15272                    movement::end_of_paragraph(map, head, 1),
15273                    SelectionGoal::None,
15274                )
15275            });
15276        })
15277    }
15278
15279    pub fn move_to_start_of_excerpt(
15280        &mut self,
15281        _: &MoveToStartOfExcerpt,
15282        window: &mut Window,
15283        cx: &mut Context<Self>,
15284    ) {
15285        if matches!(self.mode, EditorMode::SingleLine) {
15286            cx.propagate();
15287            return;
15288        }
15289        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
15290        self.change_selections(Default::default(), window, cx, |s| {
15291            s.move_with(&mut |map, selection| {
15292                selection.collapse_to(
15293                    movement::start_of_excerpt(
15294                        map,
15295                        selection.head(),
15296                        workspace::searchable::Direction::Prev,
15297                    ),
15298                    SelectionGoal::None,
15299                )
15300            });
15301        })
15302    }
15303
15304    pub fn move_to_start_of_next_excerpt(
15305        &mut self,
15306        _: &MoveToStartOfNextExcerpt,
15307        window: &mut Window,
15308        cx: &mut Context<Self>,
15309    ) {
15310        if matches!(self.mode, EditorMode::SingleLine) {
15311            cx.propagate();
15312            return;
15313        }
15314
15315        self.change_selections(Default::default(), window, cx, |s| {
15316            s.move_with(&mut |map, selection| {
15317                selection.collapse_to(
15318                    movement::start_of_excerpt(
15319                        map,
15320                        selection.head(),
15321                        workspace::searchable::Direction::Next,
15322                    ),
15323                    SelectionGoal::None,
15324                )
15325            });
15326        })
15327    }
15328
15329    pub fn move_to_end_of_excerpt(
15330        &mut self,
15331        _: &MoveToEndOfExcerpt,
15332        window: &mut Window,
15333        cx: &mut Context<Self>,
15334    ) {
15335        if matches!(self.mode, EditorMode::SingleLine) {
15336            cx.propagate();
15337            return;
15338        }
15339        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
15340        self.change_selections(Default::default(), window, cx, |s| {
15341            s.move_with(&mut |map, selection| {
15342                selection.collapse_to(
15343                    movement::end_of_excerpt(
15344                        map,
15345                        selection.head(),
15346                        workspace::searchable::Direction::Next,
15347                    ),
15348                    SelectionGoal::None,
15349                )
15350            });
15351        })
15352    }
15353
15354    pub fn move_to_end_of_previous_excerpt(
15355        &mut self,
15356        _: &MoveToEndOfPreviousExcerpt,
15357        window: &mut Window,
15358        cx: &mut Context<Self>,
15359    ) {
15360        if matches!(self.mode, EditorMode::SingleLine) {
15361            cx.propagate();
15362            return;
15363        }
15364        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
15365        self.change_selections(Default::default(), window, cx, |s| {
15366            s.move_with(&mut |map, selection| {
15367                selection.collapse_to(
15368                    movement::end_of_excerpt(
15369                        map,
15370                        selection.head(),
15371                        workspace::searchable::Direction::Prev,
15372                    ),
15373                    SelectionGoal::None,
15374                )
15375            });
15376        })
15377    }
15378
15379    pub fn select_to_start_of_excerpt(
15380        &mut self,
15381        _: &SelectToStartOfExcerpt,
15382        window: &mut Window,
15383        cx: &mut Context<Self>,
15384    ) {
15385        if matches!(self.mode, EditorMode::SingleLine) {
15386            cx.propagate();
15387            return;
15388        }
15389        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
15390        self.change_selections(Default::default(), window, cx, |s| {
15391            s.move_heads_with(&mut |map, head, _| {
15392                (
15393                    movement::start_of_excerpt(map, head, workspace::searchable::Direction::Prev),
15394                    SelectionGoal::None,
15395                )
15396            });
15397        })
15398    }
15399
15400    pub fn select_to_start_of_next_excerpt(
15401        &mut self,
15402        _: &SelectToStartOfNextExcerpt,
15403        window: &mut Window,
15404        cx: &mut Context<Self>,
15405    ) {
15406        if matches!(self.mode, EditorMode::SingleLine) {
15407            cx.propagate();
15408            return;
15409        }
15410        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
15411        self.change_selections(Default::default(), window, cx, |s| {
15412            s.move_heads_with(&mut |map, head, _| {
15413                (
15414                    movement::start_of_excerpt(map, head, workspace::searchable::Direction::Next),
15415                    SelectionGoal::None,
15416                )
15417            });
15418        })
15419    }
15420
15421    pub fn select_to_end_of_excerpt(
15422        &mut self,
15423        _: &SelectToEndOfExcerpt,
15424        window: &mut Window,
15425        cx: &mut Context<Self>,
15426    ) {
15427        if matches!(self.mode, EditorMode::SingleLine) {
15428            cx.propagate();
15429            return;
15430        }
15431        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
15432        self.change_selections(Default::default(), window, cx, |s| {
15433            s.move_heads_with(&mut |map, head, _| {
15434                (
15435                    movement::end_of_excerpt(map, head, workspace::searchable::Direction::Next),
15436                    SelectionGoal::None,
15437                )
15438            });
15439        })
15440    }
15441
15442    pub fn select_to_end_of_previous_excerpt(
15443        &mut self,
15444        _: &SelectToEndOfPreviousExcerpt,
15445        window: &mut Window,
15446        cx: &mut Context<Self>,
15447    ) {
15448        if matches!(self.mode, EditorMode::SingleLine) {
15449            cx.propagate();
15450            return;
15451        }
15452        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
15453        self.change_selections(Default::default(), window, cx, |s| {
15454            s.move_heads_with(&mut |map, head, _| {
15455                (
15456                    movement::end_of_excerpt(map, head, workspace::searchable::Direction::Prev),
15457                    SelectionGoal::None,
15458                )
15459            });
15460        })
15461    }
15462
15463    pub fn move_to_beginning(
15464        &mut self,
15465        _: &MoveToBeginning,
15466        window: &mut Window,
15467        cx: &mut Context<Self>,
15468    ) {
15469        if matches!(self.mode, EditorMode::SingleLine) {
15470            cx.propagate();
15471            return;
15472        }
15473        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
15474        self.change_selections(Default::default(), window, cx, |s| {
15475            s.select_ranges(vec![Anchor::min()..Anchor::min()]);
15476        });
15477    }
15478
15479    pub fn select_to_beginning(
15480        &mut self,
15481        _: &SelectToBeginning,
15482        window: &mut Window,
15483        cx: &mut Context<Self>,
15484    ) {
15485        let mut selection = self.selections.last::<Point>(&self.display_snapshot(cx));
15486        selection.set_head(Point::zero(), SelectionGoal::None);
15487        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
15488        self.change_selections(Default::default(), window, cx, |s| {
15489            s.select(vec![selection]);
15490        });
15491    }
15492
15493    pub fn move_to_end(&mut self, _: &MoveToEnd, window: &mut Window, cx: &mut Context<Self>) {
15494        if matches!(self.mode, EditorMode::SingleLine) {
15495            cx.propagate();
15496            return;
15497        }
15498        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
15499        let cursor = self.buffer.read(cx).read(cx).len();
15500        self.change_selections(Default::default(), window, cx, |s| {
15501            s.select_ranges(vec![cursor..cursor])
15502        });
15503    }
15504
15505    pub fn set_nav_history(&mut self, nav_history: Option<ItemNavHistory>) {
15506        self.nav_history = nav_history;
15507    }
15508
15509    pub fn nav_history(&self) -> Option<&ItemNavHistory> {
15510        self.nav_history.as_ref()
15511    }
15512
15513    pub fn create_nav_history_entry(&mut self, cx: &mut Context<Self>) {
15514        self.push_to_nav_history(
15515            self.selections.newest_anchor().head(),
15516            None,
15517            false,
15518            true,
15519            cx,
15520        );
15521    }
15522
15523    fn navigation_data(&self, cursor_anchor: Anchor, cx: &mut Context<Self>) -> NavigationData {
15524        let display_snapshot = self.display_map.update(cx, |map, cx| map.snapshot(cx));
15525        let buffer = self.buffer.read(cx).read(cx);
15526        let cursor_position = cursor_anchor.to_point(&buffer);
15527        let scroll_anchor = self.scroll_manager.native_anchor(&display_snapshot, cx);
15528        let scroll_top_row = scroll_anchor.top_row(&buffer);
15529        drop(buffer);
15530
15531        NavigationData {
15532            cursor_anchor,
15533            cursor_position,
15534            scroll_anchor,
15535            scroll_top_row,
15536        }
15537    }
15538
15539    fn navigation_entry(
15540        &self,
15541        cursor_anchor: Anchor,
15542        cx: &mut Context<Self>,
15543    ) -> Option<NavigationEntry> {
15544        let Some(history) = self.nav_history.clone() else {
15545            return None;
15546        };
15547        let data = self.navigation_data(cursor_anchor, cx);
15548        Some(history.navigation_entry(Some(Arc::new(data) as Arc<dyn Any + Send + Sync>)))
15549    }
15550
15551    fn push_to_nav_history(
15552        &mut self,
15553        cursor_anchor: Anchor,
15554        new_position: Option<Point>,
15555        is_deactivate: bool,
15556        always: bool,
15557        cx: &mut Context<Self>,
15558    ) {
15559        let data = self.navigation_data(cursor_anchor, cx);
15560        if let Some(nav_history) = self.nav_history.as_mut() {
15561            if let Some(new_position) = new_position {
15562                let row_delta = (new_position.row as i64 - data.cursor_position.row as i64).abs();
15563                if row_delta == 0 || (row_delta < MIN_NAVIGATION_HISTORY_ROW_DELTA && !always) {
15564                    return;
15565                }
15566            }
15567
15568            let cursor_row = data.cursor_position.row;
15569            nav_history.push(Some(data), Some(cursor_row), cx);
15570            cx.emit(EditorEvent::PushedToNavHistory {
15571                anchor: cursor_anchor,
15572                is_deactivate,
15573            })
15574        }
15575    }
15576
15577    pub fn select_to_end(&mut self, _: &SelectToEnd, window: &mut Window, cx: &mut Context<Self>) {
15578        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
15579        let buffer = self.buffer.read(cx).snapshot(cx);
15580        let mut selection = self
15581            .selections
15582            .first::<MultiBufferOffset>(&self.display_snapshot(cx));
15583        selection.set_head(buffer.len(), SelectionGoal::None);
15584        self.change_selections(Default::default(), window, cx, |s| {
15585            s.select(vec![selection]);
15586        });
15587    }
15588
15589    pub fn select_all(&mut self, _: &SelectAll, window: &mut Window, cx: &mut Context<Self>) {
15590        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
15591        self.change_selections(SelectionEffects::no_scroll(), window, cx, |s| {
15592            s.select_ranges([Anchor::min()..Anchor::max()]);
15593        });
15594    }
15595
15596    pub fn select_line(&mut self, _: &SelectLine, window: &mut Window, cx: &mut Context<Self>) {
15597        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
15598        let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
15599        let mut selections = self.selections.all::<Point>(&display_map);
15600        let max_point = display_map.buffer_snapshot().max_point();
15601        for selection in &mut selections {
15602            let rows = selection.spanned_rows(true, &display_map);
15603            selection.start = Point::new(rows.start.0, 0);
15604            selection.end = cmp::min(max_point, Point::new(rows.end.0, 0));
15605            selection.reversed = false;
15606        }
15607        self.change_selections(Default::default(), window, cx, |s| {
15608            s.select(selections);
15609        });
15610    }
15611
15612    pub fn split_selection_into_lines(
15613        &mut self,
15614        action: &SplitSelectionIntoLines,
15615        window: &mut Window,
15616        cx: &mut Context<Self>,
15617    ) {
15618        let selections = self
15619            .selections
15620            .all::<Point>(&self.display_snapshot(cx))
15621            .into_iter()
15622            .map(|selection| selection.start..selection.end)
15623            .collect::<Vec<_>>();
15624        self.unfold_ranges(&selections, true, false, cx);
15625
15626        let mut new_selection_ranges = Vec::new();
15627        {
15628            let buffer = self.buffer.read(cx).read(cx);
15629            for selection in selections {
15630                for row in selection.start.row..selection.end.row {
15631                    let line_start = Point::new(row, 0);
15632                    let line_end = Point::new(row, buffer.line_len(MultiBufferRow(row)));
15633
15634                    if action.keep_selections {
15635                        // Keep the selection range for each line
15636                        let selection_start = if row == selection.start.row {
15637                            selection.start
15638                        } else {
15639                            line_start
15640                        };
15641                        new_selection_ranges.push(selection_start..line_end);
15642                    } else {
15643                        // Collapse to cursor at end of line
15644                        new_selection_ranges.push(line_end..line_end);
15645                    }
15646                }
15647
15648                let is_multiline_selection = selection.start.row != selection.end.row;
15649                // Don't insert last one if it's a multi-line selection ending at the start of a line,
15650                // so this action feels more ergonomic when paired with other selection operations
15651                let should_skip_last = is_multiline_selection && selection.end.column == 0;
15652                if !should_skip_last {
15653                    if action.keep_selections {
15654                        if is_multiline_selection {
15655                            let line_start = Point::new(selection.end.row, 0);
15656                            new_selection_ranges.push(line_start..selection.end);
15657                        } else {
15658                            new_selection_ranges.push(selection.start..selection.end);
15659                        }
15660                    } else {
15661                        new_selection_ranges.push(selection.end..selection.end);
15662                    }
15663                }
15664            }
15665        }
15666        self.change_selections(SelectionEffects::no_scroll(), window, cx, |s| {
15667            s.select_ranges(new_selection_ranges);
15668        });
15669    }
15670
15671    pub fn add_selection_above(
15672        &mut self,
15673        action: &AddSelectionAbove,
15674        window: &mut Window,
15675        cx: &mut Context<Self>,
15676    ) {
15677        self.add_selection(true, action.skip_soft_wrap, window, cx);
15678    }
15679
15680    pub fn add_selection_below(
15681        &mut self,
15682        action: &AddSelectionBelow,
15683        window: &mut Window,
15684        cx: &mut Context<Self>,
15685    ) {
15686        self.add_selection(false, action.skip_soft_wrap, window, cx);
15687    }
15688
15689    fn add_selection(
15690        &mut self,
15691        above: bool,
15692        skip_soft_wrap: bool,
15693        window: &mut Window,
15694        cx: &mut Context<Self>,
15695    ) {
15696        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
15697
15698        let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
15699        let all_selections = self.selections.all::<Point>(&display_map);
15700        let text_layout_details = self.text_layout_details(window, cx);
15701
15702        let (mut columnar_selections, new_selections_to_columnarize) = {
15703            if let Some(state) = self.add_selections_state.as_ref() {
15704                let columnar_selection_ids: HashSet<_> = state
15705                    .groups
15706                    .iter()
15707                    .flat_map(|group| group.stack.iter())
15708                    .copied()
15709                    .collect();
15710
15711                all_selections
15712                    .into_iter()
15713                    .partition(|s| columnar_selection_ids.contains(&s.id))
15714            } else {
15715                (Vec::new(), all_selections)
15716            }
15717        };
15718
15719        let mut state = self
15720            .add_selections_state
15721            .take()
15722            .unwrap_or_else(|| AddSelectionsState { groups: Vec::new() });
15723
15724        for selection in new_selections_to_columnarize {
15725            let range = selection.display_range(&display_map).sorted();
15726            let start_x = display_map.x_for_display_point(range.start, &text_layout_details);
15727            let end_x = display_map.x_for_display_point(range.end, &text_layout_details);
15728            let positions = start_x.min(end_x)..start_x.max(end_x);
15729            let mut stack = Vec::new();
15730            for row in range.start.row().0..=range.end.row().0 {
15731                if let Some(selection) = self.selections.build_columnar_selection(
15732                    &display_map,
15733                    DisplayRow(row),
15734                    &positions,
15735                    selection.reversed,
15736                    &text_layout_details,
15737                ) {
15738                    stack.push(selection.id);
15739                    columnar_selections.push(selection);
15740                }
15741            }
15742            if !stack.is_empty() {
15743                if above {
15744                    stack.reverse();
15745                }
15746                state.groups.push(AddSelectionsGroup { above, stack });
15747            }
15748        }
15749
15750        let mut final_selections = Vec::new();
15751        let end_row = if above {
15752            DisplayRow(0)
15753        } else {
15754            display_map.max_point().row()
15755        };
15756
15757        // When `skip_soft_wrap` is true, we use UTF-16 columns instead of pixel
15758        // positions to place new selections, so we need to keep track of the
15759        // column range of the oldest selection in each group, because
15760        // intermediate selections may have been clamped to shorter lines.
15761        let mut goal_columns_by_selection_id = if skip_soft_wrap {
15762            let mut map = HashMap::default();
15763            for group in state.groups.iter() {
15764                if let Some(oldest_id) = group.stack.first() {
15765                    if let Some(oldest_selection) =
15766                        columnar_selections.iter().find(|s| s.id == *oldest_id)
15767                    {
15768                        let snapshot = display_map.buffer_snapshot();
15769                        let start_col =
15770                            snapshot.point_to_point_utf16(oldest_selection.start).column;
15771                        let end_col = snapshot.point_to_point_utf16(oldest_selection.end).column;
15772                        let goal_columns = start_col.min(end_col)..start_col.max(end_col);
15773                        for id in &group.stack {
15774                            map.insert(*id, goal_columns.clone());
15775                        }
15776                    }
15777                }
15778            }
15779            map
15780        } else {
15781            HashMap::default()
15782        };
15783
15784        let mut last_added_item_per_group = HashMap::default();
15785        for group in state.groups.iter_mut() {
15786            if let Some(last_id) = group.stack.last() {
15787                last_added_item_per_group.insert(*last_id, group);
15788            }
15789        }
15790
15791        for selection in columnar_selections {
15792            if let Some(group) = last_added_item_per_group.get_mut(&selection.id) {
15793                if above == group.above {
15794                    let range = selection.display_range(&display_map).sorted();
15795                    debug_assert_eq!(range.start.row(), range.end.row());
15796                    let row = range.start.row();
15797                    let positions =
15798                        if let SelectionGoal::HorizontalRange { start, end } = selection.goal {
15799                            Pixels::from(start)..Pixels::from(end)
15800                        } else {
15801                            let start_x =
15802                                display_map.x_for_display_point(range.start, &text_layout_details);
15803                            let end_x =
15804                                display_map.x_for_display_point(range.end, &text_layout_details);
15805                            start_x.min(end_x)..start_x.max(end_x)
15806                        };
15807
15808                    let maybe_new_selection = if skip_soft_wrap {
15809                        let goal_columns = goal_columns_by_selection_id
15810                            .remove(&selection.id)
15811                            .unwrap_or_else(|| {
15812                                let snapshot = display_map.buffer_snapshot();
15813                                let start_col =
15814                                    snapshot.point_to_point_utf16(selection.start).column;
15815                                let end_col = snapshot.point_to_point_utf16(selection.end).column;
15816                                start_col.min(end_col)..start_col.max(end_col)
15817                            });
15818                        self.selections.find_next_columnar_selection_by_buffer_row(
15819                            &display_map,
15820                            row,
15821                            end_row,
15822                            above,
15823                            &goal_columns,
15824                            selection.reversed,
15825                            &text_layout_details,
15826                        )
15827                    } else {
15828                        self.selections.find_next_columnar_selection_by_display_row(
15829                            &display_map,
15830                            row,
15831                            end_row,
15832                            above,
15833                            &positions,
15834                            selection.reversed,
15835                            &text_layout_details,
15836                        )
15837                    };
15838
15839                    if let Some(new_selection) = maybe_new_selection {
15840                        group.stack.push(new_selection.id);
15841                        if above {
15842                            final_selections.push(new_selection);
15843                            final_selections.push(selection);
15844                        } else {
15845                            final_selections.push(selection);
15846                            final_selections.push(new_selection);
15847                        }
15848                    } else {
15849                        final_selections.push(selection);
15850                    }
15851                } else {
15852                    group.stack.pop();
15853                }
15854            } else {
15855                final_selections.push(selection);
15856            }
15857        }
15858
15859        self.change_selections(Default::default(), window, cx, |s| {
15860            s.select(final_selections);
15861        });
15862
15863        let final_selection_ids: HashSet<_> = self
15864            .selections
15865            .all::<Point>(&display_map)
15866            .iter()
15867            .map(|s| s.id)
15868            .collect();
15869        state.groups.retain_mut(|group| {
15870            // selections might get merged above so we remove invalid items from stacks
15871            group.stack.retain(|id| final_selection_ids.contains(id));
15872
15873            // single selection in stack can be treated as initial state
15874            group.stack.len() > 1
15875        });
15876
15877        if !state.groups.is_empty() {
15878            self.add_selections_state = Some(state);
15879        }
15880    }
15881
15882    pub fn insert_snippet_at_selections(
15883        &mut self,
15884        action: &InsertSnippet,
15885        window: &mut Window,
15886        cx: &mut Context<Self>,
15887    ) {
15888        self.try_insert_snippet_at_selections(action, window, cx)
15889            .log_err();
15890    }
15891
15892    fn try_insert_snippet_at_selections(
15893        &mut self,
15894        action: &InsertSnippet,
15895        window: &mut Window,
15896        cx: &mut Context<Self>,
15897    ) -> Result<()> {
15898        let insertion_ranges = self
15899            .selections
15900            .all::<MultiBufferOffset>(&self.display_snapshot(cx))
15901            .into_iter()
15902            .map(|selection| selection.range())
15903            .collect_vec();
15904
15905        let snippet = if let Some(snippet_body) = &action.snippet {
15906            if action.language.is_none() && action.name.is_none() {
15907                Snippet::parse(snippet_body)?
15908            } else {
15909                bail!("`snippet` is mutually exclusive with `language` and `name`")
15910            }
15911        } else if let Some(name) = &action.name {
15912            let project = self.project().context("no project")?;
15913            let snippet_store = project.read(cx).snippets().read(cx);
15914            let snippet = snippet_store
15915                .snippets_for(action.language.clone(), cx)
15916                .into_iter()
15917                .find(|snippet| snippet.name == *name)
15918                .context("snippet not found")?;
15919            Snippet::parse(&snippet.body)?
15920        } else {
15921            // todo(andrew): open modal to select snippet
15922            bail!("`name` or `snippet` is required")
15923        };
15924
15925        self.insert_snippet(&insertion_ranges, snippet, window, cx)
15926    }
15927
15928    fn select_match_ranges(
15929        &mut self,
15930        range: Range<MultiBufferOffset>,
15931        reversed: bool,
15932        replace_newest: bool,
15933        auto_scroll: Option<Autoscroll>,
15934        window: &mut Window,
15935        cx: &mut Context<Editor>,
15936    ) {
15937        self.unfold_ranges(
15938            std::slice::from_ref(&range),
15939            false,
15940            auto_scroll.is_some(),
15941            cx,
15942        );
15943        let effects = if let Some(scroll) = auto_scroll {
15944            SelectionEffects::scroll(scroll)
15945        } else {
15946            SelectionEffects::no_scroll()
15947        };
15948        self.change_selections(effects, window, cx, |s| {
15949            if replace_newest {
15950                s.delete(s.newest_anchor().id);
15951            }
15952            if reversed {
15953                s.insert_range(range.end..range.start);
15954            } else {
15955                s.insert_range(range);
15956            }
15957        });
15958    }
15959
15960    pub fn select_next_match_internal(
15961        &mut self,
15962        display_map: &DisplaySnapshot,
15963        replace_newest: bool,
15964        autoscroll: Option<Autoscroll>,
15965        window: &mut Window,
15966        cx: &mut Context<Self>,
15967    ) -> Result<()> {
15968        let buffer = display_map.buffer_snapshot();
15969        let mut selections = self.selections.all::<MultiBufferOffset>(&display_map);
15970        if let Some(mut select_next_state) = self.select_next_state.take() {
15971            let query = &select_next_state.query;
15972            if !select_next_state.done {
15973                let first_selection = selections.iter().min_by_key(|s| s.id).unwrap();
15974                let last_selection = selections.iter().max_by_key(|s| s.id).unwrap();
15975                let mut next_selected_range = None;
15976
15977                let bytes_after_last_selection =
15978                    buffer.bytes_in_range(last_selection.end..buffer.len());
15979                let bytes_before_first_selection =
15980                    buffer.bytes_in_range(MultiBufferOffset(0)..first_selection.start);
15981                let query_matches = query
15982                    .stream_find_iter(bytes_after_last_selection)
15983                    .map(|result| (last_selection.end, result))
15984                    .chain(
15985                        query
15986                            .stream_find_iter(bytes_before_first_selection)
15987                            .map(|result| (MultiBufferOffset(0), result)),
15988                    );
15989
15990                for (start_offset, query_match) in query_matches {
15991                    let query_match = query_match.unwrap(); // can only fail due to I/O
15992                    let offset_range =
15993                        start_offset + query_match.start()..start_offset + query_match.end();
15994
15995                    if !select_next_state.wordwise
15996                        || (!buffer.is_inside_word(offset_range.start, None)
15997                            && !buffer.is_inside_word(offset_range.end, None))
15998                    {
15999                        let idx = selections
16000                            .partition_point(|selection| selection.end <= offset_range.start);
16001                        let overlaps = selections
16002                            .get(idx)
16003                            .map_or(false, |selection| selection.start < offset_range.end);
16004
16005                        if !overlaps {
16006                            next_selected_range = Some(offset_range);
16007                            break;
16008                        }
16009                    }
16010                }
16011
16012                if let Some(next_selected_range) = next_selected_range {
16013                    self.select_match_ranges(
16014                        next_selected_range,
16015                        last_selection.reversed,
16016                        replace_newest,
16017                        autoscroll,
16018                        window,
16019                        cx,
16020                    );
16021                } else {
16022                    select_next_state.done = true;
16023                }
16024            }
16025
16026            self.select_next_state = Some(select_next_state);
16027        } else {
16028            let mut only_carets = true;
16029            let mut same_text_selected = true;
16030            let mut selected_text = None;
16031
16032            let mut selections_iter = selections.iter().peekable();
16033            while let Some(selection) = selections_iter.next() {
16034                if selection.start != selection.end {
16035                    only_carets = false;
16036                }
16037
16038                if same_text_selected {
16039                    if selected_text.is_none() {
16040                        selected_text =
16041                            Some(buffer.text_for_range(selection.range()).collect::<String>());
16042                    }
16043
16044                    if let Some(next_selection) = selections_iter.peek() {
16045                        if next_selection.len() == selection.len() {
16046                            let next_selected_text = buffer
16047                                .text_for_range(next_selection.range())
16048                                .collect::<String>();
16049                            if Some(next_selected_text) != selected_text {
16050                                same_text_selected = false;
16051                                selected_text = None;
16052                            }
16053                        } else {
16054                            same_text_selected = false;
16055                            selected_text = None;
16056                        }
16057                    }
16058                }
16059            }
16060
16061            if only_carets {
16062                for selection in &mut selections {
16063                    let (word_range, _) = buffer.surrounding_word(selection.start, None);
16064                    selection.start = word_range.start;
16065                    selection.end = word_range.end;
16066                    selection.goal = SelectionGoal::None;
16067                    selection.reversed = false;
16068                    self.select_match_ranges(
16069                        selection.start..selection.end,
16070                        selection.reversed,
16071                        replace_newest,
16072                        autoscroll,
16073                        window,
16074                        cx,
16075                    );
16076                }
16077
16078                if selections.len() == 1 {
16079                    let selection = selections
16080                        .last()
16081                        .expect("ensured that there's only one selection");
16082                    let query = buffer
16083                        .text_for_range(selection.start..selection.end)
16084                        .collect::<String>();
16085                    let is_empty = query.is_empty();
16086                    let select_state = SelectNextState {
16087                        query: self.build_query(&[query], cx)?,
16088                        wordwise: true,
16089                        done: is_empty,
16090                    };
16091                    self.select_next_state = Some(select_state);
16092                } else {
16093                    self.select_next_state = None;
16094                }
16095            } else if let Some(selected_text) = selected_text {
16096                self.select_next_state = Some(SelectNextState {
16097                    query: self.build_query(&[selected_text], cx)?,
16098                    wordwise: false,
16099                    done: false,
16100                });
16101                self.select_next_match_internal(
16102                    display_map,
16103                    replace_newest,
16104                    autoscroll,
16105                    window,
16106                    cx,
16107                )?;
16108            }
16109        }
16110        Ok(())
16111    }
16112
16113    pub fn select_all_matches(
16114        &mut self,
16115        _action: &SelectAllMatches,
16116        window: &mut Window,
16117        cx: &mut Context<Self>,
16118    ) -> Result<()> {
16119        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
16120
16121        let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
16122
16123        self.select_next_match_internal(&display_map, false, None, window, cx)?;
16124        let Some(select_next_state) = self.select_next_state.as_mut().filter(|state| !state.done)
16125        else {
16126            return Ok(());
16127        };
16128
16129        let mut new_selections = Vec::new();
16130        let initial_selection = self.selections.oldest::<MultiBufferOffset>(&display_map);
16131        let reversed = initial_selection.reversed;
16132        let buffer = display_map.buffer_snapshot();
16133        let query_matches = select_next_state
16134            .query
16135            .stream_find_iter(buffer.bytes_in_range(MultiBufferOffset(0)..buffer.len()));
16136
16137        for query_match in query_matches.into_iter() {
16138            let query_match = query_match.context("query match for select all action")?; // can only fail due to I/O
16139            let offset_range = if reversed {
16140                MultiBufferOffset(query_match.end())..MultiBufferOffset(query_match.start())
16141            } else {
16142                MultiBufferOffset(query_match.start())..MultiBufferOffset(query_match.end())
16143            };
16144
16145            let is_partial_word_match = select_next_state.wordwise
16146                && (buffer.is_inside_word(offset_range.start, None)
16147                    || buffer.is_inside_word(offset_range.end, None));
16148
16149            let is_initial_selection = MultiBufferOffset(query_match.start())
16150                == initial_selection.start
16151                && MultiBufferOffset(query_match.end()) == initial_selection.end;
16152
16153            if !is_partial_word_match && !is_initial_selection {
16154                new_selections.push(offset_range);
16155            }
16156        }
16157
16158        // Ensure that the initial range is the last selection, as
16159        // `MutableSelectionsCollection::select_ranges` makes the last selection
16160        // the newest selection, which the editor then relies on as the primary
16161        // cursor for scroll targeting. Without this, the last match would then
16162        // be automatically focused when the user started editing the selected
16163        // matches.
16164        let initial_directed_range = if reversed {
16165            initial_selection.end..initial_selection.start
16166        } else {
16167            initial_selection.start..initial_selection.end
16168        };
16169        new_selections.push(initial_directed_range);
16170
16171        select_next_state.done = true;
16172        self.unfold_ranges(&new_selections, false, false, cx);
16173        self.change_selections(SelectionEffects::no_scroll(), window, cx, |selections| {
16174            selections.select_ranges(new_selections)
16175        });
16176
16177        Ok(())
16178    }
16179
16180    pub fn select_next(
16181        &mut self,
16182        action: &SelectNext,
16183        window: &mut Window,
16184        cx: &mut Context<Self>,
16185    ) -> Result<()> {
16186        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
16187        let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
16188        self.select_next_match_internal(
16189            &display_map,
16190            action.replace_newest,
16191            Some(Autoscroll::newest()),
16192            window,
16193            cx,
16194        )
16195    }
16196
16197    pub fn select_previous(
16198        &mut self,
16199        action: &SelectPrevious,
16200        window: &mut Window,
16201        cx: &mut Context<Self>,
16202    ) -> Result<()> {
16203        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
16204        let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
16205        let buffer = display_map.buffer_snapshot();
16206        let mut selections = self.selections.all::<MultiBufferOffset>(&display_map);
16207        if let Some(mut select_prev_state) = self.select_prev_state.take() {
16208            let query = &select_prev_state.query;
16209            if !select_prev_state.done {
16210                let first_selection = selections.iter().min_by_key(|s| s.id).unwrap();
16211                let last_selection = selections.iter().max_by_key(|s| s.id).unwrap();
16212                let mut next_selected_range = None;
16213                // When we're iterating matches backwards, the oldest match will actually be the furthest one in the buffer.
16214                let bytes_before_last_selection =
16215                    buffer.reversed_bytes_in_range(MultiBufferOffset(0)..last_selection.start);
16216                let bytes_after_first_selection =
16217                    buffer.reversed_bytes_in_range(first_selection.end..buffer.len());
16218                let query_matches = query
16219                    .stream_find_iter(bytes_before_last_selection)
16220                    .map(|result| (last_selection.start, result))
16221                    .chain(
16222                        query
16223                            .stream_find_iter(bytes_after_first_selection)
16224                            .map(|result| (buffer.len(), result)),
16225                    );
16226                for (end_offset, query_match) in query_matches {
16227                    let query_match = query_match.unwrap(); // can only fail due to I/O
16228                    let offset_range =
16229                        end_offset - query_match.end()..end_offset - query_match.start();
16230
16231                    if !select_prev_state.wordwise
16232                        || (!buffer.is_inside_word(offset_range.start, None)
16233                            && !buffer.is_inside_word(offset_range.end, None))
16234                    {
16235                        next_selected_range = Some(offset_range);
16236                        break;
16237                    }
16238                }
16239
16240                if let Some(next_selected_range) = next_selected_range {
16241                    self.select_match_ranges(
16242                        next_selected_range,
16243                        last_selection.reversed,
16244                        action.replace_newest,
16245                        Some(Autoscroll::newest()),
16246                        window,
16247                        cx,
16248                    );
16249                } else {
16250                    select_prev_state.done = true;
16251                }
16252            }
16253
16254            self.select_prev_state = Some(select_prev_state);
16255        } else {
16256            let mut only_carets = true;
16257            let mut same_text_selected = true;
16258            let mut selected_text = None;
16259
16260            let mut selections_iter = selections.iter().peekable();
16261            while let Some(selection) = selections_iter.next() {
16262                if selection.start != selection.end {
16263                    only_carets = false;
16264                }
16265
16266                if same_text_selected {
16267                    if selected_text.is_none() {
16268                        selected_text =
16269                            Some(buffer.text_for_range(selection.range()).collect::<String>());
16270                    }
16271
16272                    if let Some(next_selection) = selections_iter.peek() {
16273                        if next_selection.len() == selection.len() {
16274                            let next_selected_text = buffer
16275                                .text_for_range(next_selection.range())
16276                                .collect::<String>();
16277                            if Some(next_selected_text) != selected_text {
16278                                same_text_selected = false;
16279                                selected_text = None;
16280                            }
16281                        } else {
16282                            same_text_selected = false;
16283                            selected_text = None;
16284                        }
16285                    }
16286                }
16287            }
16288
16289            if only_carets {
16290                for selection in &mut selections {
16291                    let (word_range, _) = buffer.surrounding_word(selection.start, None);
16292                    selection.start = word_range.start;
16293                    selection.end = word_range.end;
16294                    selection.goal = SelectionGoal::None;
16295                    selection.reversed = false;
16296                    self.select_match_ranges(
16297                        selection.start..selection.end,
16298                        selection.reversed,
16299                        action.replace_newest,
16300                        Some(Autoscroll::newest()),
16301                        window,
16302                        cx,
16303                    );
16304                }
16305                if selections.len() == 1 {
16306                    let selection = selections
16307                        .last()
16308                        .expect("ensured that there's only one selection");
16309                    let query = buffer
16310                        .text_for_range(selection.start..selection.end)
16311                        .collect::<String>();
16312                    let is_empty = query.is_empty();
16313                    let select_state = SelectNextState {
16314                        query: self.build_query(&[query.chars().rev().collect::<String>()], cx)?,
16315                        wordwise: true,
16316                        done: is_empty,
16317                    };
16318                    self.select_prev_state = Some(select_state);
16319                } else {
16320                    self.select_prev_state = None;
16321                }
16322            } else if let Some(selected_text) = selected_text {
16323                self.select_prev_state = Some(SelectNextState {
16324                    query: self
16325                        .build_query(&[selected_text.chars().rev().collect::<String>()], cx)?,
16326                    wordwise: false,
16327                    done: false,
16328                });
16329                self.select_previous(action, window, cx)?;
16330            }
16331        }
16332        Ok(())
16333    }
16334
16335    /// Builds an `AhoCorasick` automaton from the provided patterns, while
16336    /// setting the case sensitivity based on the global
16337    /// `SelectNextCaseSensitive` setting, if set, otherwise based on the
16338    /// editor's settings.
16339    fn build_query<I, P>(&self, patterns: I, cx: &Context<Self>) -> Result<AhoCorasick, BuildError>
16340    where
16341        I: IntoIterator<Item = P>,
16342        P: AsRef<[u8]>,
16343    {
16344        let case_sensitive = self
16345            .select_next_is_case_sensitive
16346            .unwrap_or_else(|| EditorSettings::get_global(cx).search.case_sensitive);
16347
16348        let mut builder = AhoCorasickBuilder::new();
16349        builder.ascii_case_insensitive(!case_sensitive);
16350        builder.build(patterns)
16351    }
16352
16353    pub fn find_next_match(
16354        &mut self,
16355        _: &FindNextMatch,
16356        window: &mut Window,
16357        cx: &mut Context<Self>,
16358    ) -> Result<()> {
16359        let selections = self.selections.disjoint_anchors_arc();
16360        match selections.first() {
16361            Some(first) if selections.len() >= 2 => {
16362                self.change_selections(Default::default(), window, cx, |s| {
16363                    s.select_ranges([first.range()]);
16364                });
16365            }
16366            _ => self.select_next(
16367                &SelectNext {
16368                    replace_newest: true,
16369                },
16370                window,
16371                cx,
16372            )?,
16373        }
16374        Ok(())
16375    }
16376
16377    pub fn find_previous_match(
16378        &mut self,
16379        _: &FindPreviousMatch,
16380        window: &mut Window,
16381        cx: &mut Context<Self>,
16382    ) -> Result<()> {
16383        let selections = self.selections.disjoint_anchors_arc();
16384        match selections.last() {
16385            Some(last) if selections.len() >= 2 => {
16386                self.change_selections(Default::default(), window, cx, |s| {
16387                    s.select_ranges([last.range()]);
16388                });
16389            }
16390            _ => self.select_previous(
16391                &SelectPrevious {
16392                    replace_newest: true,
16393                },
16394                window,
16395                cx,
16396            )?,
16397        }
16398        Ok(())
16399    }
16400
16401    pub fn toggle_comments(
16402        &mut self,
16403        action: &ToggleComments,
16404        window: &mut Window,
16405        cx: &mut Context<Self>,
16406    ) {
16407        if self.read_only(cx) {
16408            return;
16409        }
16410        self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
16411        let text_layout_details = &self.text_layout_details(window, cx);
16412        self.transact(window, cx, |this, window, cx| {
16413            let mut selections = this
16414                .selections
16415                .all::<MultiBufferPoint>(&this.display_snapshot(cx));
16416            let mut edits = Vec::new();
16417            let mut selection_edit_ranges = Vec::new();
16418            let mut last_toggled_row = None;
16419            let snapshot = this.buffer.read(cx).read(cx);
16420            let empty_str: Arc<str> = Arc::default();
16421            let mut suffixes_inserted = Vec::new();
16422            let ignore_indent = action.ignore_indent;
16423
16424            fn comment_prefix_range(
16425                snapshot: &MultiBufferSnapshot,
16426                row: MultiBufferRow,
16427                comment_prefix: &str,
16428                comment_prefix_whitespace: &str,
16429                ignore_indent: bool,
16430            ) -> Range<Point> {
16431                let indent_size = if ignore_indent {
16432                    0
16433                } else {
16434                    snapshot.indent_size_for_line(row).len
16435                };
16436
16437                let start = Point::new(row.0, indent_size);
16438
16439                let mut line_bytes = snapshot
16440                    .bytes_in_range(start..snapshot.max_point())
16441                    .flatten()
16442                    .copied();
16443
16444                // If this line currently begins with the line comment prefix, then record
16445                // the range containing the prefix.
16446                if line_bytes
16447                    .by_ref()
16448                    .take(comment_prefix.len())
16449                    .eq(comment_prefix.bytes())
16450                {
16451                    // Include any whitespace that matches the comment prefix.
16452                    let matching_whitespace_len = line_bytes
16453                        .zip(comment_prefix_whitespace.bytes())
16454                        .take_while(|(a, b)| a == b)
16455                        .count() as u32;
16456                    let end = Point::new(
16457                        start.row,
16458                        start.column + comment_prefix.len() as u32 + matching_whitespace_len,
16459                    );
16460                    start..end
16461                } else {
16462                    start..start
16463                }
16464            }
16465
16466            fn comment_suffix_range(
16467                snapshot: &MultiBufferSnapshot,
16468                row: MultiBufferRow,
16469                comment_suffix: &str,
16470                comment_suffix_has_leading_space: bool,
16471            ) -> Range<Point> {
16472                let end = Point::new(row.0, snapshot.line_len(row));
16473                let suffix_start_column = end.column.saturating_sub(comment_suffix.len() as u32);
16474
16475                let mut line_end_bytes = snapshot
16476                    .bytes_in_range(Point::new(end.row, suffix_start_column.saturating_sub(1))..end)
16477                    .flatten()
16478                    .copied();
16479
16480                let leading_space_len = if suffix_start_column > 0
16481                    && line_end_bytes.next() == Some(b' ')
16482                    && comment_suffix_has_leading_space
16483                {
16484                    1
16485                } else {
16486                    0
16487                };
16488
16489                // If this line currently begins with the line comment prefix, then record
16490                // the range containing the prefix.
16491                if line_end_bytes.by_ref().eq(comment_suffix.bytes()) {
16492                    let start = Point::new(end.row, suffix_start_column - leading_space_len);
16493                    start..end
16494                } else {
16495                    end..end
16496                }
16497            }
16498
16499            // TODO: Handle selections that cross excerpts
16500            for selection in &mut selections {
16501                let start_column = snapshot
16502                    .indent_size_for_line(MultiBufferRow(selection.start.row))
16503                    .len;
16504                let language = if let Some(language) =
16505                    snapshot.language_scope_at(Point::new(selection.start.row, start_column))
16506                {
16507                    language
16508                } else {
16509                    continue;
16510                };
16511
16512                selection_edit_ranges.clear();
16513
16514                // If multiple selections contain a given row, avoid processing that
16515                // row more than once.
16516                let mut start_row = MultiBufferRow(selection.start.row);
16517                if last_toggled_row == Some(start_row) {
16518                    start_row = start_row.next_row();
16519                }
16520                let end_row =
16521                    if selection.end.row > selection.start.row && selection.end.column == 0 {
16522                        MultiBufferRow(selection.end.row - 1)
16523                    } else {
16524                        MultiBufferRow(selection.end.row)
16525                    };
16526                last_toggled_row = Some(end_row);
16527
16528                if start_row > end_row {
16529                    continue;
16530                }
16531
16532                // If the language has line comments, toggle those.
16533                let mut full_comment_prefixes = language.line_comment_prefixes().to_vec();
16534
16535                // If ignore_indent is set, trim spaces from the right side of all full_comment_prefixes
16536                if ignore_indent {
16537                    full_comment_prefixes = full_comment_prefixes
16538                        .into_iter()
16539                        .map(|s| Arc::from(s.trim_end()))
16540                        .collect();
16541                }
16542
16543                if !full_comment_prefixes.is_empty() {
16544                    let first_prefix = full_comment_prefixes
16545                        .first()
16546                        .expect("prefixes is non-empty");
16547                    let prefix_trimmed_lengths = full_comment_prefixes
16548                        .iter()
16549                        .map(|p| p.trim_end_matches(' ').len())
16550                        .collect::<SmallVec<[usize; 4]>>();
16551
16552                    let mut all_selection_lines_are_comments = true;
16553
16554                    for row in start_row.0..=end_row.0 {
16555                        let row = MultiBufferRow(row);
16556                        if start_row < end_row && snapshot.is_line_blank(row) {
16557                            continue;
16558                        }
16559
16560                        let prefix_range = full_comment_prefixes
16561                            .iter()
16562                            .zip(prefix_trimmed_lengths.iter().copied())
16563                            .map(|(prefix, trimmed_prefix_len)| {
16564                                comment_prefix_range(
16565                                    snapshot.deref(),
16566                                    row,
16567                                    &prefix[..trimmed_prefix_len],
16568                                    &prefix[trimmed_prefix_len..],
16569                                    ignore_indent,
16570                                )
16571                            })
16572                            .max_by_key(|range| range.end.column - range.start.column)
16573                            .expect("prefixes is non-empty");
16574
16575                        if prefix_range.is_empty() {
16576                            all_selection_lines_are_comments = false;
16577                        }
16578
16579                        selection_edit_ranges.push(prefix_range);
16580                    }
16581
16582                    if all_selection_lines_are_comments {
16583                        edits.extend(
16584                            selection_edit_ranges
16585                                .iter()
16586                                .cloned()
16587                                .map(|range| (range, empty_str.clone())),
16588                        );
16589                    } else {
16590                        let min_column = selection_edit_ranges
16591                            .iter()
16592                            .map(|range| range.start.column)
16593                            .min()
16594                            .unwrap_or(0);
16595                        edits.extend(selection_edit_ranges.iter().map(|range| {
16596                            let position = Point::new(range.start.row, min_column);
16597                            (position..position, first_prefix.clone())
16598                        }));
16599                    }
16600                } else if let Some(BlockCommentConfig {
16601                    start: full_comment_prefix,
16602                    end: comment_suffix,
16603                    ..
16604                }) = language.block_comment()
16605                {
16606                    let comment_prefix = full_comment_prefix.trim_end_matches(' ');
16607                    let comment_prefix_whitespace = &full_comment_prefix[comment_prefix.len()..];
16608                    let prefix_range = comment_prefix_range(
16609                        snapshot.deref(),
16610                        start_row,
16611                        comment_prefix,
16612                        comment_prefix_whitespace,
16613                        ignore_indent,
16614                    );
16615                    let suffix_range = comment_suffix_range(
16616                        snapshot.deref(),
16617                        end_row,
16618                        comment_suffix.trim_start_matches(' '),
16619                        comment_suffix.starts_with(' '),
16620                    );
16621
16622                    if prefix_range.is_empty() || suffix_range.is_empty() {
16623                        edits.push((
16624                            prefix_range.start..prefix_range.start,
16625                            full_comment_prefix.clone(),
16626                        ));
16627                        edits.push((suffix_range.end..suffix_range.end, comment_suffix.clone()));
16628                        suffixes_inserted.push((end_row, comment_suffix.len()));
16629                    } else {
16630                        edits.push((prefix_range, empty_str.clone()));
16631                        edits.push((suffix_range, empty_str.clone()));
16632                    }
16633                } else {
16634                    continue;
16635                }
16636            }
16637
16638            drop(snapshot);
16639            this.buffer.update(cx, |buffer, cx| {
16640                buffer.edit(edits, None, cx);
16641            });
16642
16643            // Adjust selections so that they end before any comment suffixes that
16644            // were inserted.
16645            let mut suffixes_inserted = suffixes_inserted.into_iter().peekable();
16646            let mut selections = this.selections.all::<Point>(&this.display_snapshot(cx));
16647            let snapshot = this.buffer.read(cx).read(cx);
16648            for selection in &mut selections {
16649                while let Some((row, suffix_len)) = suffixes_inserted.peek().copied() {
16650                    match row.cmp(&MultiBufferRow(selection.end.row)) {
16651                        Ordering::Less => {
16652                            suffixes_inserted.next();
16653                            continue;
16654                        }
16655                        Ordering::Greater => break,
16656                        Ordering::Equal => {
16657                            if selection.end.column == snapshot.line_len(row) {
16658                                if selection.is_empty() {
16659                                    selection.start.column -= suffix_len as u32;
16660                                }
16661                                selection.end.column -= suffix_len as u32;
16662                            }
16663                            break;
16664                        }
16665                    }
16666                }
16667            }
16668
16669            drop(snapshot);
16670            this.change_selections(Default::default(), window, cx, |s| s.select(selections));
16671
16672            let selections = this.selections.all::<Point>(&this.display_snapshot(cx));
16673            let selections_on_single_row = selections.windows(2).all(|selections| {
16674                selections[0].start.row == selections[1].start.row
16675                    && selections[0].end.row == selections[1].end.row
16676                    && selections[0].start.row == selections[0].end.row
16677            });
16678            let selections_selecting = selections
16679                .iter()
16680                .any(|selection| selection.start != selection.end);
16681            let advance_downwards = action.advance_downwards
16682                && selections_on_single_row
16683                && !selections_selecting
16684                && !matches!(this.mode, EditorMode::SingleLine);
16685
16686            if advance_downwards {
16687                let snapshot = this.buffer.read(cx).snapshot(cx);
16688
16689                this.change_selections(Default::default(), window, cx, |s| {
16690                    s.move_cursors_with(&mut |display_snapshot, display_point, _| {
16691                        let mut point = display_point.to_point(display_snapshot);
16692                        point.row += 1;
16693                        point = snapshot.clip_point(point, Bias::Left);
16694                        let display_point = point.to_display_point(display_snapshot);
16695                        let goal = SelectionGoal::HorizontalPosition(
16696                            display_snapshot
16697                                .x_for_display_point(display_point, text_layout_details)
16698                                .into(),
16699                        );
16700                        (display_point, goal)
16701                    })
16702                });
16703            }
16704        });
16705    }
16706
16707    pub fn select_enclosing_symbol(
16708        &mut self,
16709        _: &SelectEnclosingSymbol,
16710        window: &mut Window,
16711        cx: &mut Context<Self>,
16712    ) {
16713        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
16714
16715        let buffer = self.buffer.read(cx).snapshot(cx);
16716        let old_selections = self
16717            .selections
16718            .all::<MultiBufferOffset>(&self.display_snapshot(cx))
16719            .into_boxed_slice();
16720
16721        fn update_selection(
16722            selection: &Selection<MultiBufferOffset>,
16723            buffer_snap: &MultiBufferSnapshot,
16724        ) -> Option<Selection<MultiBufferOffset>> {
16725            let cursor = selection.head();
16726            let (_buffer_id, symbols) = buffer_snap.symbols_containing(cursor, None)?;
16727            for symbol in symbols.iter().rev() {
16728                let start = symbol.range.start.to_offset(buffer_snap);
16729                let end = symbol.range.end.to_offset(buffer_snap);
16730                let new_range = start..end;
16731                if start < selection.start || end > selection.end {
16732                    return Some(Selection {
16733                        id: selection.id,
16734                        start: new_range.start,
16735                        end: new_range.end,
16736                        goal: SelectionGoal::None,
16737                        reversed: selection.reversed,
16738                    });
16739                }
16740            }
16741            None
16742        }
16743
16744        let mut selected_larger_symbol = false;
16745        let new_selections = old_selections
16746            .iter()
16747            .map(|selection| match update_selection(selection, &buffer) {
16748                Some(new_selection) => {
16749                    if new_selection.range() != selection.range() {
16750                        selected_larger_symbol = true;
16751                    }
16752                    new_selection
16753                }
16754                None => selection.clone(),
16755            })
16756            .collect::<Vec<_>>();
16757
16758        if selected_larger_symbol {
16759            self.change_selections(Default::default(), window, cx, |s| {
16760                s.select(new_selections);
16761            });
16762        }
16763    }
16764
16765    pub fn select_larger_syntax_node(
16766        &mut self,
16767        _: &SelectLargerSyntaxNode,
16768        window: &mut Window,
16769        cx: &mut Context<Self>,
16770    ) {
16771        let Some(visible_row_count) = self.visible_row_count() else {
16772            return;
16773        };
16774        let old_selections: Box<[_]> = self
16775            .selections
16776            .all::<MultiBufferOffset>(&self.display_snapshot(cx))
16777            .into();
16778        if old_selections.is_empty() {
16779            return;
16780        }
16781
16782        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
16783
16784        let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
16785        let buffer = self.buffer.read(cx).snapshot(cx);
16786
16787        let mut selected_larger_node = false;
16788        let mut new_selections = old_selections
16789            .iter()
16790            .map(|selection| {
16791                let old_range = selection.start..selection.end;
16792
16793                if let Some((node, _)) = buffer.syntax_ancestor(old_range.clone()) {
16794                    // manually select word at selection
16795                    if ["string_content", "inline"].contains(&node.kind()) {
16796                        let (word_range, _) = buffer.surrounding_word(old_range.start, None);
16797                        // ignore if word is already selected
16798                        if !word_range.is_empty() && old_range != word_range {
16799                            let (last_word_range, _) = buffer.surrounding_word(old_range.end, None);
16800                            // only select word if start and end point belongs to same word
16801                            if word_range == last_word_range {
16802                                selected_larger_node = true;
16803                                return Selection {
16804                                    id: selection.id,
16805                                    start: word_range.start,
16806                                    end: word_range.end,
16807                                    goal: SelectionGoal::None,
16808                                    reversed: selection.reversed,
16809                                };
16810                            }
16811                        }
16812                    }
16813                }
16814
16815                let mut new_range = old_range.clone();
16816                while let Some((node, range)) = buffer.syntax_ancestor(new_range.clone()) {
16817                    new_range = range;
16818                    if !node.is_named() {
16819                        continue;
16820                    }
16821                    if !display_map.intersects_fold(new_range.start)
16822                        && !display_map.intersects_fold(new_range.end)
16823                    {
16824                        break;
16825                    }
16826                }
16827
16828                selected_larger_node |= new_range != old_range;
16829                Selection {
16830                    id: selection.id,
16831                    start: new_range.start,
16832                    end: new_range.end,
16833                    goal: SelectionGoal::None,
16834                    reversed: selection.reversed,
16835                }
16836            })
16837            .collect::<Vec<_>>();
16838
16839        if !selected_larger_node {
16840            return; // don't put this call in the history
16841        }
16842
16843        // scroll based on transformation done to the last selection created by the user
16844        let (last_old, last_new) = old_selections
16845            .last()
16846            .zip(new_selections.last().cloned())
16847            .expect("old_selections isn't empty");
16848
16849        let is_selection_reversed = if new_selections.len() == 1 {
16850            let should_be_reversed = last_old.start != last_new.start;
16851            new_selections.last_mut().expect("checked above").reversed = should_be_reversed;
16852            should_be_reversed
16853        } else {
16854            last_new.reversed
16855        };
16856
16857        if selected_larger_node {
16858            self.select_syntax_node_history.disable_clearing = true;
16859            self.change_selections(SelectionEffects::no_scroll(), window, cx, |s| {
16860                s.select(new_selections.clone());
16861            });
16862            self.select_syntax_node_history.disable_clearing = false;
16863        }
16864
16865        let start_row = last_new.start.to_display_point(&display_map).row().0;
16866        let end_row = last_new.end.to_display_point(&display_map).row().0;
16867        let selection_height = end_row - start_row + 1;
16868        let scroll_margin_rows = self.vertical_scroll_margin() as u32;
16869
16870        let fits_on_the_screen = visible_row_count >= selection_height + scroll_margin_rows * 2;
16871        let scroll_behavior = if fits_on_the_screen {
16872            self.request_autoscroll(Autoscroll::fit(), cx);
16873            SelectSyntaxNodeScrollBehavior::FitSelection
16874        } else if is_selection_reversed {
16875            self.scroll_cursor_top(&ScrollCursorTop, window, cx);
16876            SelectSyntaxNodeScrollBehavior::CursorTop
16877        } else {
16878            self.scroll_cursor_bottom(&ScrollCursorBottom, window, cx);
16879            SelectSyntaxNodeScrollBehavior::CursorBottom
16880        };
16881
16882        let old_selections: Box<[Selection<Anchor>]> = old_selections
16883            .iter()
16884            .map(|s| s.map(|offset| buffer.anchor_before(offset)))
16885            .collect();
16886        self.select_syntax_node_history.push((
16887            old_selections,
16888            scroll_behavior,
16889            is_selection_reversed,
16890        ));
16891    }
16892
16893    pub fn select_smaller_syntax_node(
16894        &mut self,
16895        _: &SelectSmallerSyntaxNode,
16896        window: &mut Window,
16897        cx: &mut Context<Self>,
16898    ) {
16899        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
16900
16901        if let Some((mut selections, scroll_behavior, is_selection_reversed)) =
16902            self.select_syntax_node_history.pop()
16903        {
16904            if let Some(selection) = selections.last_mut() {
16905                selection.reversed = is_selection_reversed;
16906            }
16907
16908            let snapshot = self.buffer.read(cx).snapshot(cx);
16909            let selections: Vec<Selection<MultiBufferOffset>> = selections
16910                .iter()
16911                .map(|s| s.map(|anchor| anchor.to_offset(&snapshot)))
16912                .collect();
16913
16914            self.select_syntax_node_history.disable_clearing = true;
16915            self.change_selections(SelectionEffects::no_scroll(), window, cx, |s| {
16916                s.select(selections);
16917            });
16918            self.select_syntax_node_history.disable_clearing = false;
16919
16920            match scroll_behavior {
16921                SelectSyntaxNodeScrollBehavior::CursorTop => {
16922                    self.scroll_cursor_top(&ScrollCursorTop, window, cx);
16923                }
16924                SelectSyntaxNodeScrollBehavior::FitSelection => {
16925                    self.request_autoscroll(Autoscroll::fit(), cx);
16926                }
16927                SelectSyntaxNodeScrollBehavior::CursorBottom => {
16928                    self.scroll_cursor_bottom(&ScrollCursorBottom, window, cx);
16929                }
16930            }
16931        }
16932    }
16933
16934    pub fn unwrap_syntax_node(
16935        &mut self,
16936        _: &UnwrapSyntaxNode,
16937        window: &mut Window,
16938        cx: &mut Context<Self>,
16939    ) {
16940        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
16941
16942        let buffer = self.buffer.read(cx).snapshot(cx);
16943        let selections = self
16944            .selections
16945            .all::<MultiBufferOffset>(&self.display_snapshot(cx))
16946            .into_iter()
16947            // subtracting the offset requires sorting
16948            .sorted_by_key(|i| i.start);
16949
16950        let full_edits = selections
16951            .into_iter()
16952            .filter_map(|selection| {
16953                let child = if selection.is_empty()
16954                    && let Some((_, ancestor_range)) =
16955                        buffer.syntax_ancestor(selection.start..selection.end)
16956                {
16957                    ancestor_range
16958                } else {
16959                    selection.range()
16960                };
16961
16962                let mut parent = child.clone();
16963                while let Some((_, ancestor_range)) = buffer.syntax_ancestor(parent.clone()) {
16964                    parent = ancestor_range;
16965                    if parent.start < child.start || parent.end > child.end {
16966                        break;
16967                    }
16968                }
16969
16970                if parent == child {
16971                    return None;
16972                }
16973                let text = buffer.text_for_range(child).collect::<String>();
16974                Some((selection.id, parent, text))
16975            })
16976            .collect::<Vec<_>>();
16977        if full_edits.is_empty() {
16978            return;
16979        }
16980
16981        self.transact(window, cx, |this, window, cx| {
16982            this.buffer.update(cx, |buffer, cx| {
16983                buffer.edit(
16984                    full_edits
16985                        .iter()
16986                        .map(|(_, p, t)| (p.clone(), t.clone()))
16987                        .collect::<Vec<_>>(),
16988                    None,
16989                    cx,
16990                );
16991            });
16992            this.change_selections(Default::default(), window, cx, |s| {
16993                let mut offset = 0;
16994                let mut selections = vec![];
16995                for (id, parent, text) in full_edits {
16996                    let start = parent.start - offset;
16997                    offset += (parent.end - parent.start) - text.len();
16998                    selections.push(Selection {
16999                        id,
17000                        start,
17001                        end: start + text.len(),
17002                        reversed: false,
17003                        goal: Default::default(),
17004                    });
17005                }
17006                s.select(selections);
17007            });
17008        });
17009    }
17010
17011    pub fn select_next_syntax_node(
17012        &mut self,
17013        _: &SelectNextSyntaxNode,
17014        window: &mut Window,
17015        cx: &mut Context<Self>,
17016    ) {
17017        let old_selections: Box<[_]> = self
17018            .selections
17019            .all::<MultiBufferOffset>(&self.display_snapshot(cx))
17020            .into();
17021        if old_selections.is_empty() {
17022            return;
17023        }
17024
17025        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
17026
17027        let buffer = self.buffer.read(cx).snapshot(cx);
17028        let mut selected_sibling = false;
17029
17030        let new_selections = old_selections
17031            .iter()
17032            .map(|selection| {
17033                let old_range = selection.start..selection.end;
17034
17035                let old_range =
17036                    old_range.start.to_offset(&buffer)..old_range.end.to_offset(&buffer);
17037                let excerpt = buffer.excerpt_containing(old_range.clone());
17038
17039                if let Some(mut excerpt) = excerpt
17040                    && let Some(node) = excerpt
17041                        .buffer()
17042                        .syntax_next_sibling(excerpt.map_range_to_buffer(old_range))
17043                {
17044                    let new_range = excerpt.map_range_from_buffer(
17045                        BufferOffset(node.byte_range().start)..BufferOffset(node.byte_range().end),
17046                    );
17047                    selected_sibling = true;
17048                    Selection {
17049                        id: selection.id,
17050                        start: new_range.start,
17051                        end: new_range.end,
17052                        goal: SelectionGoal::None,
17053                        reversed: selection.reversed,
17054                    }
17055                } else {
17056                    selection.clone()
17057                }
17058            })
17059            .collect::<Vec<_>>();
17060
17061        if selected_sibling {
17062            self.change_selections(
17063                SelectionEffects::scroll(Autoscroll::fit()),
17064                window,
17065                cx,
17066                |s| {
17067                    s.select(new_selections);
17068                },
17069            );
17070        }
17071    }
17072
17073    pub fn select_prev_syntax_node(
17074        &mut self,
17075        _: &SelectPreviousSyntaxNode,
17076        window: &mut Window,
17077        cx: &mut Context<Self>,
17078    ) {
17079        let old_selections: Box<[_]> = self
17080            .selections
17081            .all::<MultiBufferOffset>(&self.display_snapshot(cx))
17082            .into();
17083        if old_selections.is_empty() {
17084            return;
17085        }
17086
17087        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
17088
17089        let buffer = self.buffer.read(cx).snapshot(cx);
17090        let mut selected_sibling = false;
17091
17092        let new_selections = old_selections
17093            .iter()
17094            .map(|selection| {
17095                let old_range = selection.start..selection.end;
17096                let old_range =
17097                    old_range.start.to_offset(&buffer)..old_range.end.to_offset(&buffer);
17098                let excerpt = buffer.excerpt_containing(old_range.clone());
17099
17100                if let Some(mut excerpt) = excerpt
17101                    && let Some(node) = excerpt
17102                        .buffer()
17103                        .syntax_prev_sibling(excerpt.map_range_to_buffer(old_range))
17104                {
17105                    let new_range = excerpt.map_range_from_buffer(
17106                        BufferOffset(node.byte_range().start)..BufferOffset(node.byte_range().end),
17107                    );
17108                    selected_sibling = true;
17109                    Selection {
17110                        id: selection.id,
17111                        start: new_range.start,
17112                        end: new_range.end,
17113                        goal: SelectionGoal::None,
17114                        reversed: selection.reversed,
17115                    }
17116                } else {
17117                    selection.clone()
17118                }
17119            })
17120            .collect::<Vec<_>>();
17121
17122        if selected_sibling {
17123            self.change_selections(
17124                SelectionEffects::scroll(Autoscroll::fit()),
17125                window,
17126                cx,
17127                |s| {
17128                    s.select(new_selections);
17129                },
17130            );
17131        }
17132    }
17133
17134    pub fn move_to_start_of_larger_syntax_node(
17135        &mut self,
17136        _: &MoveToStartOfLargerSyntaxNode,
17137        window: &mut Window,
17138        cx: &mut Context<Self>,
17139    ) {
17140        self.move_cursors_to_syntax_nodes(window, cx, false);
17141    }
17142
17143    pub fn move_to_end_of_larger_syntax_node(
17144        &mut self,
17145        _: &MoveToEndOfLargerSyntaxNode,
17146        window: &mut Window,
17147        cx: &mut Context<Self>,
17148    ) {
17149        self.move_cursors_to_syntax_nodes(window, cx, true);
17150    }
17151
17152    fn find_syntax_node_boundary(
17153        &self,
17154        selection_pos: MultiBufferOffset,
17155        move_to_end: bool,
17156        display_map: &DisplaySnapshot,
17157        buffer: &MultiBufferSnapshot,
17158    ) -> MultiBufferOffset {
17159        let old_range = selection_pos..selection_pos;
17160        let mut new_pos = selection_pos;
17161        let mut search_range = old_range;
17162        while let Some((node, range)) = buffer.syntax_ancestor(search_range.clone()) {
17163            search_range = range.clone();
17164            if !node.is_named()
17165                || display_map.intersects_fold(range.start)
17166                || display_map.intersects_fold(range.end)
17167                // If cursor is already at the end of the syntax node, continue searching
17168                || (move_to_end && range.end == selection_pos)
17169                // If cursor is already at the start of the syntax node, continue searching
17170                || (!move_to_end && range.start == selection_pos)
17171            {
17172                continue;
17173            }
17174
17175            // If we found a string_content node, find the largest parent that is still string_content
17176            // Enables us to skip to the end of strings without taking multiple steps inside the string
17177            let (_, final_range) = if node.kind() == "string_content" {
17178                let mut current_node = node;
17179                let mut current_range = range;
17180                while let Some((parent, parent_range)) =
17181                    buffer.syntax_ancestor(current_range.clone())
17182                {
17183                    if parent.kind() == "string_content" {
17184                        current_node = parent;
17185                        current_range = parent_range;
17186                    } else {
17187                        break;
17188                    }
17189                }
17190
17191                (current_node, current_range)
17192            } else {
17193                (node, range)
17194            };
17195
17196            new_pos = if move_to_end {
17197                final_range.end
17198            } else {
17199                final_range.start
17200            };
17201
17202            break;
17203        }
17204
17205        new_pos
17206    }
17207
17208    fn move_cursors_to_syntax_nodes(
17209        &mut self,
17210        window: &mut Window,
17211        cx: &mut Context<Self>,
17212        move_to_end: bool,
17213    ) -> bool {
17214        let old_selections: Box<[_]> = self
17215            .selections
17216            .all::<MultiBufferOffset>(&self.display_snapshot(cx))
17217            .into();
17218        if old_selections.is_empty() {
17219            return false;
17220        }
17221
17222        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
17223
17224        let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
17225        let buffer = self.buffer.read(cx).snapshot(cx);
17226
17227        let mut any_cursor_moved = false;
17228        let new_selections = old_selections
17229            .iter()
17230            .map(|selection| {
17231                if !selection.is_empty() {
17232                    return selection.clone();
17233                }
17234
17235                let selection_pos = selection.head();
17236                let new_pos = self.find_syntax_node_boundary(
17237                    selection_pos,
17238                    move_to_end,
17239                    &display_map,
17240                    &buffer,
17241                );
17242
17243                any_cursor_moved |= new_pos != selection_pos;
17244
17245                Selection {
17246                    id: selection.id,
17247                    start: new_pos,
17248                    end: new_pos,
17249                    goal: SelectionGoal::None,
17250                    reversed: false,
17251                }
17252            })
17253            .collect::<Vec<_>>();
17254
17255        self.change_selections(Default::default(), window, cx, |s| {
17256            s.select(new_selections);
17257        });
17258        self.request_autoscroll(Autoscroll::newest(), cx);
17259
17260        any_cursor_moved
17261    }
17262
17263    pub fn select_to_start_of_larger_syntax_node(
17264        &mut self,
17265        _: &SelectToStartOfLargerSyntaxNode,
17266        window: &mut Window,
17267        cx: &mut Context<Self>,
17268    ) {
17269        self.select_to_syntax_nodes(window, cx, false);
17270    }
17271
17272    pub fn select_to_end_of_larger_syntax_node(
17273        &mut self,
17274        _: &SelectToEndOfLargerSyntaxNode,
17275        window: &mut Window,
17276        cx: &mut Context<Self>,
17277    ) {
17278        self.select_to_syntax_nodes(window, cx, true);
17279    }
17280
17281    fn select_to_syntax_nodes(
17282        &mut self,
17283        window: &mut Window,
17284        cx: &mut Context<Self>,
17285        move_to_end: bool,
17286    ) {
17287        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
17288
17289        let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
17290        let buffer = self.buffer.read(cx).snapshot(cx);
17291        let old_selections = self.selections.all::<MultiBufferOffset>(&display_map);
17292
17293        let new_selections = old_selections
17294            .iter()
17295            .map(|selection| {
17296                let new_pos = self.find_syntax_node_boundary(
17297                    selection.head(),
17298                    move_to_end,
17299                    &display_map,
17300                    &buffer,
17301                );
17302
17303                let mut new_selection = selection.clone();
17304                new_selection.set_head(new_pos, SelectionGoal::None);
17305                new_selection
17306            })
17307            .collect::<Vec<_>>();
17308
17309        self.change_selections(Default::default(), window, cx, |s| {
17310            s.select(new_selections);
17311        });
17312    }
17313
17314    pub fn move_to_enclosing_bracket(
17315        &mut self,
17316        _: &MoveToEnclosingBracket,
17317        window: &mut Window,
17318        cx: &mut Context<Self>,
17319    ) {
17320        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
17321        self.change_selections(Default::default(), window, cx, |s| {
17322            s.move_offsets_with(&mut |snapshot, selection| {
17323                let Some(enclosing_bracket_ranges) =
17324                    snapshot.enclosing_bracket_ranges(selection.start..selection.end)
17325                else {
17326                    return;
17327                };
17328
17329                let mut best_length = usize::MAX;
17330                let mut best_inside = false;
17331                let mut best_in_bracket_range = false;
17332                let mut best_destination = None;
17333                for (open, close) in enclosing_bracket_ranges {
17334                    let close = close.to_inclusive();
17335                    let length = *close.end() - open.start;
17336                    let inside = selection.start >= open.end && selection.end <= *close.start();
17337                    let in_bracket_range = open.to_inclusive().contains(&selection.head())
17338                        || close.contains(&selection.head());
17339
17340                    // If best is next to a bracket and current isn't, skip
17341                    if !in_bracket_range && best_in_bracket_range {
17342                        continue;
17343                    }
17344
17345                    // Prefer smaller lengths unless best is inside and current isn't
17346                    if length > best_length && (best_inside || !inside) {
17347                        continue;
17348                    }
17349
17350                    best_length = length;
17351                    best_inside = inside;
17352                    best_in_bracket_range = in_bracket_range;
17353                    best_destination = Some(
17354                        if close.contains(&selection.start) && close.contains(&selection.end) {
17355                            if inside { open.end } else { open.start }
17356                        } else if inside {
17357                            *close.start()
17358                        } else {
17359                            *close.end()
17360                        },
17361                    );
17362                }
17363
17364                if let Some(destination) = best_destination {
17365                    selection.collapse_to(destination, SelectionGoal::None);
17366                }
17367            })
17368        });
17369    }
17370
17371    pub fn undo_selection(
17372        &mut self,
17373        _: &UndoSelection,
17374        window: &mut Window,
17375        cx: &mut Context<Self>,
17376    ) {
17377        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
17378        if let Some(entry) = self.selection_history.undo_stack.pop_back() {
17379            self.selection_history.mode = SelectionHistoryMode::Undoing;
17380            self.with_selection_effects_deferred(window, cx, |this, window, cx| {
17381                this.end_selection(window, cx);
17382                this.change_selections(
17383                    SelectionEffects::scroll(Autoscroll::newest()),
17384                    window,
17385                    cx,
17386                    |s| s.select_anchors(entry.selections.to_vec()),
17387                );
17388            });
17389            self.selection_history.mode = SelectionHistoryMode::Normal;
17390
17391            self.select_next_state = entry.select_next_state;
17392            self.select_prev_state = entry.select_prev_state;
17393            self.add_selections_state = entry.add_selections_state;
17394        }
17395    }
17396
17397    pub fn redo_selection(
17398        &mut self,
17399        _: &RedoSelection,
17400        window: &mut Window,
17401        cx: &mut Context<Self>,
17402    ) {
17403        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
17404        if let Some(entry) = self.selection_history.redo_stack.pop_back() {
17405            self.selection_history.mode = SelectionHistoryMode::Redoing;
17406            self.with_selection_effects_deferred(window, cx, |this, window, cx| {
17407                this.end_selection(window, cx);
17408                this.change_selections(
17409                    SelectionEffects::scroll(Autoscroll::newest()),
17410                    window,
17411                    cx,
17412                    |s| s.select_anchors(entry.selections.to_vec()),
17413                );
17414            });
17415            self.selection_history.mode = SelectionHistoryMode::Normal;
17416
17417            self.select_next_state = entry.select_next_state;
17418            self.select_prev_state = entry.select_prev_state;
17419            self.add_selections_state = entry.add_selections_state;
17420        }
17421    }
17422
17423    pub fn expand_excerpts(
17424        &mut self,
17425        action: &ExpandExcerpts,
17426        _: &mut Window,
17427        cx: &mut Context<Self>,
17428    ) {
17429        self.expand_excerpts_for_direction(action.lines, ExpandExcerptDirection::UpAndDown, cx)
17430    }
17431
17432    pub fn expand_excerpts_down(
17433        &mut self,
17434        action: &ExpandExcerptsDown,
17435        _: &mut Window,
17436        cx: &mut Context<Self>,
17437    ) {
17438        self.expand_excerpts_for_direction(action.lines, ExpandExcerptDirection::Down, cx)
17439    }
17440
17441    pub fn expand_excerpts_up(
17442        &mut self,
17443        action: &ExpandExcerptsUp,
17444        _: &mut Window,
17445        cx: &mut Context<Self>,
17446    ) {
17447        self.expand_excerpts_for_direction(action.lines, ExpandExcerptDirection::Up, cx)
17448    }
17449
17450    pub fn expand_excerpts_for_direction(
17451        &mut self,
17452        lines: u32,
17453        direction: ExpandExcerptDirection,
17454        cx: &mut Context<Self>,
17455    ) {
17456        let selections = self.selections.disjoint_anchors_arc();
17457
17458        let lines = if lines == 0 {
17459            EditorSettings::get_global(cx).expand_excerpt_lines
17460        } else {
17461            lines
17462        };
17463
17464        let snapshot = self.buffer.read(cx).snapshot(cx);
17465        let excerpt_ids = selections
17466            .iter()
17467            .flat_map(|selection| snapshot.excerpt_ids_for_range(selection.range()))
17468            .unique()
17469            .sorted()
17470            .collect::<Vec<_>>();
17471
17472        if self.delegate_expand_excerpts {
17473            cx.emit(EditorEvent::ExpandExcerptsRequested {
17474                excerpt_ids,
17475                lines,
17476                direction,
17477            });
17478            return;
17479        }
17480
17481        self.buffer.update(cx, |buffer, cx| {
17482            buffer.expand_excerpts(excerpt_ids, lines, direction, cx)
17483        })
17484    }
17485
17486    pub fn expand_excerpt(
17487        &mut self,
17488        excerpt: ExcerptId,
17489        direction: ExpandExcerptDirection,
17490        window: &mut Window,
17491        cx: &mut Context<Self>,
17492    ) {
17493        let lines_to_expand = EditorSettings::get_global(cx).expand_excerpt_lines;
17494
17495        if self.delegate_expand_excerpts {
17496            cx.emit(EditorEvent::ExpandExcerptsRequested {
17497                excerpt_ids: vec![excerpt],
17498                lines: lines_to_expand,
17499                direction,
17500            });
17501            return;
17502        }
17503
17504        let current_scroll_position = self.scroll_position(cx);
17505        let mut scroll = None;
17506
17507        if direction == ExpandExcerptDirection::Down {
17508            let multi_buffer = self.buffer.read(cx);
17509            let snapshot = multi_buffer.snapshot(cx);
17510            if let Some(buffer_id) = snapshot.buffer_id_for_excerpt(excerpt)
17511                && let Some(buffer) = multi_buffer.buffer(buffer_id)
17512                && let Some(excerpt_range) = snapshot.context_range_for_excerpt(excerpt)
17513            {
17514                let buffer_snapshot = buffer.read(cx).snapshot();
17515                let excerpt_end_row = Point::from_anchor(&excerpt_range.end, &buffer_snapshot).row;
17516                let last_row = buffer_snapshot.max_point().row;
17517                let lines_below = last_row.saturating_sub(excerpt_end_row);
17518                if lines_below >= lines_to_expand {
17519                    scroll = Some(
17520                        current_scroll_position
17521                            + gpui::Point::new(0.0, lines_to_expand as ScrollOffset),
17522                    );
17523                }
17524            }
17525        }
17526        if direction == ExpandExcerptDirection::Up
17527            && self
17528                .buffer
17529                .read(cx)
17530                .snapshot(cx)
17531                .excerpt_before(excerpt)
17532                .is_none()
17533        {
17534            scroll = Some(current_scroll_position);
17535        }
17536
17537        self.buffer.update(cx, |buffer, cx| {
17538            buffer.expand_excerpts([excerpt], lines_to_expand, direction, cx)
17539        });
17540
17541        if let Some(new_scroll_position) = scroll {
17542            self.set_scroll_position(new_scroll_position, window, cx);
17543        }
17544    }
17545
17546    pub fn go_to_singleton_buffer_point(
17547        &mut self,
17548        point: Point,
17549        window: &mut Window,
17550        cx: &mut Context<Self>,
17551    ) {
17552        self.go_to_singleton_buffer_range(point..point, window, cx);
17553    }
17554
17555    pub fn go_to_singleton_buffer_range(
17556        &mut self,
17557        range: Range<Point>,
17558        window: &mut Window,
17559        cx: &mut Context<Self>,
17560    ) {
17561        let multibuffer = self.buffer().read(cx);
17562        let Some(buffer) = multibuffer.as_singleton() else {
17563            return;
17564        };
17565        let Some(start) = multibuffer.buffer_point_to_anchor(&buffer, range.start, cx) else {
17566            return;
17567        };
17568        let Some(end) = multibuffer.buffer_point_to_anchor(&buffer, range.end, cx) else {
17569            return;
17570        };
17571        self.change_selections(
17572            SelectionEffects::default().nav_history(true),
17573            window,
17574            cx,
17575            |s| s.select_anchor_ranges([start..end]),
17576        );
17577    }
17578
17579    pub fn go_to_diagnostic(
17580        &mut self,
17581        action: &GoToDiagnostic,
17582        window: &mut Window,
17583        cx: &mut Context<Self>,
17584    ) {
17585        if !self.diagnostics_enabled() {
17586            return;
17587        }
17588        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
17589        self.go_to_diagnostic_impl(Direction::Next, action.severity, window, cx)
17590    }
17591
17592    pub fn go_to_prev_diagnostic(
17593        &mut self,
17594        action: &GoToPreviousDiagnostic,
17595        window: &mut Window,
17596        cx: &mut Context<Self>,
17597    ) {
17598        if !self.diagnostics_enabled() {
17599            return;
17600        }
17601        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
17602        self.go_to_diagnostic_impl(Direction::Prev, action.severity, window, cx)
17603    }
17604
17605    pub fn go_to_diagnostic_impl(
17606        &mut self,
17607        direction: Direction,
17608        severity: GoToDiagnosticSeverityFilter,
17609        window: &mut Window,
17610        cx: &mut Context<Self>,
17611    ) {
17612        let buffer = self.buffer.read(cx).snapshot(cx);
17613        let selection = self
17614            .selections
17615            .newest::<MultiBufferOffset>(&self.display_snapshot(cx));
17616
17617        let mut active_group_id = None;
17618        if let ActiveDiagnostic::Group(active_group) = &self.active_diagnostics
17619            && active_group.active_range.start.to_offset(&buffer) == selection.start
17620        {
17621            active_group_id = Some(active_group.group_id);
17622        }
17623
17624        fn filtered<'a>(
17625            severity: GoToDiagnosticSeverityFilter,
17626            diagnostics: impl Iterator<Item = DiagnosticEntryRef<'a, MultiBufferOffset>>,
17627        ) -> impl Iterator<Item = DiagnosticEntryRef<'a, MultiBufferOffset>> {
17628            diagnostics
17629                .filter(move |entry| severity.matches(entry.diagnostic.severity))
17630                .filter(|entry| entry.range.start != entry.range.end)
17631                .filter(|entry| !entry.diagnostic.is_unnecessary)
17632        }
17633
17634        let before = filtered(
17635            severity,
17636            buffer
17637                .diagnostics_in_range(MultiBufferOffset(0)..selection.start)
17638                .filter(|entry| entry.range.start <= selection.start),
17639        );
17640        let after = filtered(
17641            severity,
17642            buffer
17643                .diagnostics_in_range(selection.start..buffer.len())
17644                .filter(|entry| entry.range.start >= selection.start),
17645        );
17646
17647        let mut found: Option<DiagnosticEntryRef<MultiBufferOffset>> = None;
17648        if direction == Direction::Prev {
17649            'outer: for prev_diagnostics in [before.collect::<Vec<_>>(), after.collect::<Vec<_>>()]
17650            {
17651                for diagnostic in prev_diagnostics.into_iter().rev() {
17652                    if diagnostic.range.start != selection.start
17653                        || active_group_id
17654                            .is_some_and(|active| diagnostic.diagnostic.group_id < active)
17655                    {
17656                        found = Some(diagnostic);
17657                        break 'outer;
17658                    }
17659                }
17660            }
17661        } else {
17662            for diagnostic in after.chain(before) {
17663                if diagnostic.range.start != selection.start
17664                    || active_group_id.is_some_and(|active| diagnostic.diagnostic.group_id > active)
17665                {
17666                    found = Some(diagnostic);
17667                    break;
17668                }
17669            }
17670        }
17671        let Some(next_diagnostic) = found else {
17672            return;
17673        };
17674
17675        let next_diagnostic_start = buffer.anchor_after(next_diagnostic.range.start);
17676        let Some(buffer_id) = buffer.buffer_id_for_anchor(next_diagnostic_start) else {
17677            return;
17678        };
17679        let snapshot = self.snapshot(window, cx);
17680        if snapshot.intersects_fold(next_diagnostic.range.start) {
17681            self.unfold_ranges(
17682                std::slice::from_ref(&next_diagnostic.range),
17683                true,
17684                false,
17685                cx,
17686            );
17687        }
17688        self.change_selections(Default::default(), window, cx, |s| {
17689            s.select_ranges(vec![
17690                next_diagnostic.range.start..next_diagnostic.range.start,
17691            ])
17692        });
17693        self.activate_diagnostics(buffer_id, next_diagnostic, window, cx);
17694        self.refresh_edit_prediction(false, true, window, cx);
17695    }
17696
17697    pub fn go_to_next_hunk(&mut self, _: &GoToHunk, window: &mut Window, cx: &mut Context<Self>) {
17698        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
17699        let snapshot = self.snapshot(window, cx);
17700        let selection = self.selections.newest::<Point>(&self.display_snapshot(cx));
17701        self.go_to_hunk_before_or_after_position(
17702            &snapshot,
17703            selection.head(),
17704            Direction::Next,
17705            true,
17706            window,
17707            cx,
17708        );
17709    }
17710
17711    pub fn go_to_hunk_before_or_after_position(
17712        &mut self,
17713        snapshot: &EditorSnapshot,
17714        position: Point,
17715        direction: Direction,
17716        wrap_around: bool,
17717        window: &mut Window,
17718        cx: &mut Context<Editor>,
17719    ) {
17720        let row = if direction == Direction::Next {
17721            self.hunk_after_position(snapshot, position, wrap_around)
17722                .map(|hunk| hunk.row_range.start)
17723        } else {
17724            self.hunk_before_position(snapshot, position, wrap_around)
17725        };
17726
17727        if let Some(row) = row {
17728            let destination = Point::new(row.0, 0);
17729            let autoscroll = Autoscroll::center();
17730
17731            self.unfold_ranges(&[destination..destination], false, false, cx);
17732            self.change_selections(SelectionEffects::scroll(autoscroll), window, cx, |s| {
17733                s.select_ranges([destination..destination]);
17734            });
17735        }
17736    }
17737
17738    fn hunk_after_position(
17739        &mut self,
17740        snapshot: &EditorSnapshot,
17741        position: Point,
17742        wrap_around: bool,
17743    ) -> Option<MultiBufferDiffHunk> {
17744        let result = snapshot
17745            .buffer_snapshot()
17746            .diff_hunks_in_range(position..snapshot.buffer_snapshot().max_point())
17747            .find(|hunk| hunk.row_range.start.0 > position.row);
17748
17749        if wrap_around {
17750            result.or_else(|| {
17751                snapshot
17752                    .buffer_snapshot()
17753                    .diff_hunks_in_range(Point::zero()..position)
17754                    .find(|hunk| hunk.row_range.end.0 < position.row)
17755            })
17756        } else {
17757            result
17758        }
17759    }
17760
17761    fn go_to_prev_hunk(
17762        &mut self,
17763        _: &GoToPreviousHunk,
17764        window: &mut Window,
17765        cx: &mut Context<Self>,
17766    ) {
17767        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
17768        let snapshot = self.snapshot(window, cx);
17769        let selection = self.selections.newest::<Point>(&snapshot.display_snapshot);
17770        self.go_to_hunk_before_or_after_position(
17771            &snapshot,
17772            selection.head(),
17773            Direction::Prev,
17774            true,
17775            window,
17776            cx,
17777        );
17778    }
17779
17780    fn hunk_before_position(
17781        &mut self,
17782        snapshot: &EditorSnapshot,
17783        position: Point,
17784        wrap_around: bool,
17785    ) -> Option<MultiBufferRow> {
17786        let result = snapshot.buffer_snapshot().diff_hunk_before(position);
17787
17788        if wrap_around {
17789            result.or_else(|| snapshot.buffer_snapshot().diff_hunk_before(Point::MAX))
17790        } else {
17791            result
17792        }
17793    }
17794
17795    fn go_to_next_change(
17796        &mut self,
17797        _: &GoToNextChange,
17798        window: &mut Window,
17799        cx: &mut Context<Self>,
17800    ) {
17801        if let Some(selections) = self
17802            .change_list
17803            .next_change(1, Direction::Next)
17804            .map(|s| s.to_vec())
17805        {
17806            self.change_selections(Default::default(), window, cx, |s| {
17807                let map = s.display_snapshot();
17808                s.select_display_ranges(selections.iter().map(|a| {
17809                    let point = a.to_display_point(&map);
17810                    point..point
17811                }))
17812            })
17813        }
17814    }
17815
17816    fn go_to_previous_change(
17817        &mut self,
17818        _: &GoToPreviousChange,
17819        window: &mut Window,
17820        cx: &mut Context<Self>,
17821    ) {
17822        if let Some(selections) = self
17823            .change_list
17824            .next_change(1, Direction::Prev)
17825            .map(|s| s.to_vec())
17826        {
17827            self.change_selections(Default::default(), window, cx, |s| {
17828                let map = s.display_snapshot();
17829                s.select_display_ranges(selections.iter().map(|a| {
17830                    let point = a.to_display_point(&map);
17831                    point..point
17832                }))
17833            })
17834        }
17835    }
17836
17837    pub fn go_to_next_document_highlight(
17838        &mut self,
17839        _: &GoToNextDocumentHighlight,
17840        window: &mut Window,
17841        cx: &mut Context<Self>,
17842    ) {
17843        self.go_to_document_highlight_before_or_after_position(Direction::Next, window, cx);
17844    }
17845
17846    pub fn go_to_prev_document_highlight(
17847        &mut self,
17848        _: &GoToPreviousDocumentHighlight,
17849        window: &mut Window,
17850        cx: &mut Context<Self>,
17851    ) {
17852        self.go_to_document_highlight_before_or_after_position(Direction::Prev, window, cx);
17853    }
17854
17855    pub fn go_to_document_highlight_before_or_after_position(
17856        &mut self,
17857        direction: Direction,
17858        window: &mut Window,
17859        cx: &mut Context<Editor>,
17860    ) {
17861        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
17862        let snapshot = self.snapshot(window, cx);
17863        let buffer = &snapshot.buffer_snapshot();
17864        let position = self
17865            .selections
17866            .newest::<Point>(&snapshot.display_snapshot)
17867            .head();
17868        let anchor_position = buffer.anchor_after(position);
17869
17870        // Get all document highlights (both read and write)
17871        let mut all_highlights = Vec::new();
17872
17873        if let Some((_, read_highlights)) = self
17874            .background_highlights
17875            .get(&HighlightKey::DocumentHighlightRead)
17876        {
17877            all_highlights.extend(read_highlights.iter());
17878        }
17879
17880        if let Some((_, write_highlights)) = self
17881            .background_highlights
17882            .get(&HighlightKey::DocumentHighlightWrite)
17883        {
17884            all_highlights.extend(write_highlights.iter());
17885        }
17886
17887        if all_highlights.is_empty() {
17888            return;
17889        }
17890
17891        // Sort highlights by position
17892        all_highlights.sort_by(|a, b| a.start.cmp(&b.start, buffer));
17893
17894        let target_highlight = match direction {
17895            Direction::Next => {
17896                // Find the first highlight after the current position
17897                all_highlights
17898                    .iter()
17899                    .find(|highlight| highlight.start.cmp(&anchor_position, buffer).is_gt())
17900            }
17901            Direction::Prev => {
17902                // Find the last highlight before the current position
17903                all_highlights
17904                    .iter()
17905                    .rev()
17906                    .find(|highlight| highlight.end.cmp(&anchor_position, buffer).is_lt())
17907            }
17908        };
17909
17910        if let Some(highlight) = target_highlight {
17911            let destination = highlight.start.to_point(buffer);
17912            let autoscroll = Autoscroll::center();
17913
17914            self.unfold_ranges(&[destination..destination], false, false, cx);
17915            self.change_selections(SelectionEffects::scroll(autoscroll), window, cx, |s| {
17916                s.select_ranges([destination..destination]);
17917            });
17918        }
17919    }
17920
17921    fn go_to_line<T: 'static>(
17922        &mut self,
17923        position: Anchor,
17924        highlight_color: Option<Hsla>,
17925        window: &mut Window,
17926        cx: &mut Context<Self>,
17927    ) {
17928        let snapshot = self.snapshot(window, cx).display_snapshot;
17929        let position = position.to_point(&snapshot.buffer_snapshot());
17930        let start = snapshot
17931            .buffer_snapshot()
17932            .clip_point(Point::new(position.row, 0), Bias::Left);
17933        let end = start + Point::new(1, 0);
17934        let start = snapshot.buffer_snapshot().anchor_before(start);
17935        let end = snapshot.buffer_snapshot().anchor_before(end);
17936
17937        self.highlight_rows::<T>(
17938            start..end,
17939            highlight_color
17940                .unwrap_or_else(|| cx.theme().colors().editor_highlighted_line_background),
17941            Default::default(),
17942            cx,
17943        );
17944
17945        if self.buffer.read(cx).is_singleton() {
17946            self.request_autoscroll(Autoscroll::center().for_anchor(start), cx);
17947        }
17948    }
17949
17950    pub fn go_to_definition(
17951        &mut self,
17952        _: &GoToDefinition,
17953        window: &mut Window,
17954        cx: &mut Context<Self>,
17955    ) -> Task<Result<Navigated>> {
17956        let definition =
17957            self.go_to_definition_of_kind(GotoDefinitionKind::Symbol, false, window, cx);
17958        let fallback_strategy = EditorSettings::get_global(cx).go_to_definition_fallback;
17959        cx.spawn_in(window, async move |editor, cx| {
17960            if definition.await? == Navigated::Yes {
17961                return Ok(Navigated::Yes);
17962            }
17963            match fallback_strategy {
17964                GoToDefinitionFallback::None => Ok(Navigated::No),
17965                GoToDefinitionFallback::FindAllReferences => {
17966                    match editor.update_in(cx, |editor, window, cx| {
17967                        editor.find_all_references(&FindAllReferences::default(), window, cx)
17968                    })? {
17969                        Some(references) => references.await,
17970                        None => Ok(Navigated::No),
17971                    }
17972                }
17973            }
17974        })
17975    }
17976
17977    pub fn go_to_declaration(
17978        &mut self,
17979        _: &GoToDeclaration,
17980        window: &mut Window,
17981        cx: &mut Context<Self>,
17982    ) -> Task<Result<Navigated>> {
17983        self.go_to_definition_of_kind(GotoDefinitionKind::Declaration, false, window, cx)
17984    }
17985
17986    pub fn go_to_declaration_split(
17987        &mut self,
17988        _: &GoToDeclaration,
17989        window: &mut Window,
17990        cx: &mut Context<Self>,
17991    ) -> Task<Result<Navigated>> {
17992        self.go_to_definition_of_kind(GotoDefinitionKind::Declaration, true, window, cx)
17993    }
17994
17995    pub fn go_to_implementation(
17996        &mut self,
17997        _: &GoToImplementation,
17998        window: &mut Window,
17999        cx: &mut Context<Self>,
18000    ) -> Task<Result<Navigated>> {
18001        self.go_to_definition_of_kind(GotoDefinitionKind::Implementation, false, window, cx)
18002    }
18003
18004    pub fn go_to_implementation_split(
18005        &mut self,
18006        _: &GoToImplementationSplit,
18007        window: &mut Window,
18008        cx: &mut Context<Self>,
18009    ) -> Task<Result<Navigated>> {
18010        self.go_to_definition_of_kind(GotoDefinitionKind::Implementation, true, window, cx)
18011    }
18012
18013    pub fn go_to_type_definition(
18014        &mut self,
18015        _: &GoToTypeDefinition,
18016        window: &mut Window,
18017        cx: &mut Context<Self>,
18018    ) -> Task<Result<Navigated>> {
18019        self.go_to_definition_of_kind(GotoDefinitionKind::Type, false, window, cx)
18020    }
18021
18022    pub fn go_to_definition_split(
18023        &mut self,
18024        _: &GoToDefinitionSplit,
18025        window: &mut Window,
18026        cx: &mut Context<Self>,
18027    ) -> Task<Result<Navigated>> {
18028        self.go_to_definition_of_kind(GotoDefinitionKind::Symbol, true, window, cx)
18029    }
18030
18031    pub fn go_to_type_definition_split(
18032        &mut self,
18033        _: &GoToTypeDefinitionSplit,
18034        window: &mut Window,
18035        cx: &mut Context<Self>,
18036    ) -> Task<Result<Navigated>> {
18037        self.go_to_definition_of_kind(GotoDefinitionKind::Type, true, window, cx)
18038    }
18039
18040    fn go_to_definition_of_kind(
18041        &mut self,
18042        kind: GotoDefinitionKind,
18043        split: bool,
18044        window: &mut Window,
18045        cx: &mut Context<Self>,
18046    ) -> Task<Result<Navigated>> {
18047        let Some(provider) = self.semantics_provider.clone() else {
18048            return Task::ready(Ok(Navigated::No));
18049        };
18050        let head = self
18051            .selections
18052            .newest::<MultiBufferOffset>(&self.display_snapshot(cx))
18053            .head();
18054        let buffer = self.buffer.read(cx);
18055        let Some((buffer, head)) = buffer.text_anchor_for_position(head, cx) else {
18056            return Task::ready(Ok(Navigated::No));
18057        };
18058        let Some(definitions) = provider.definitions(&buffer, head, kind, cx) else {
18059            return Task::ready(Ok(Navigated::No));
18060        };
18061
18062        let nav_entry = self.navigation_entry(self.selections.newest_anchor().head(), cx);
18063
18064        cx.spawn_in(window, async move |editor, cx| {
18065            let Some(definitions) = definitions.await? else {
18066                return Ok(Navigated::No);
18067            };
18068            let navigated = editor
18069                .update_in(cx, |editor, window, cx| {
18070                    editor.navigate_to_hover_links(
18071                        Some(kind),
18072                        definitions
18073                            .into_iter()
18074                            .filter(|location| {
18075                                hover_links::exclude_link_to_position(&buffer, &head, location, cx)
18076                            })
18077                            .map(HoverLink::Text)
18078                            .collect::<Vec<_>>(),
18079                        nav_entry,
18080                        split,
18081                        window,
18082                        cx,
18083                    )
18084                })?
18085                .await?;
18086            anyhow::Ok(navigated)
18087        })
18088    }
18089
18090    pub fn open_url(&mut self, _: &OpenUrl, window: &mut Window, cx: &mut Context<Self>) {
18091        let selection = self.selections.newest_anchor();
18092        let head = selection.head();
18093        let tail = selection.tail();
18094
18095        let Some((buffer, start_position)) =
18096            self.buffer.read(cx).text_anchor_for_position(head, cx)
18097        else {
18098            return;
18099        };
18100
18101        let end_position = if head != tail {
18102            let Some((_, pos)) = self.buffer.read(cx).text_anchor_for_position(tail, cx) else {
18103                return;
18104            };
18105            Some(pos)
18106        } else {
18107            None
18108        };
18109
18110        let url_finder = cx.spawn_in(window, async move |_editor, cx| {
18111            let url = if let Some(end_pos) = end_position {
18112                find_url_from_range(&buffer, start_position..end_pos, cx.clone())
18113            } else {
18114                find_url(&buffer, start_position, cx.clone()).map(|(_, url)| url)
18115            };
18116
18117            if let Some(url) = url {
18118                cx.update(|window, cx| {
18119                    if parse_zed_link(&url, cx).is_some() {
18120                        window.dispatch_action(Box::new(zed_actions::OpenZedUrl { url }), cx);
18121                    } else {
18122                        cx.open_url(&url);
18123                    }
18124                })?;
18125            }
18126
18127            anyhow::Ok(())
18128        });
18129
18130        url_finder.detach();
18131    }
18132
18133    pub fn open_selected_filename(
18134        &mut self,
18135        _: &OpenSelectedFilename,
18136        window: &mut Window,
18137        cx: &mut Context<Self>,
18138    ) {
18139        let Some(workspace) = self.workspace() else {
18140            return;
18141        };
18142
18143        let position = self.selections.newest_anchor().head();
18144
18145        let Some((buffer, buffer_position)) =
18146            self.buffer.read(cx).text_anchor_for_position(position, cx)
18147        else {
18148            return;
18149        };
18150
18151        let project = self.project.clone();
18152
18153        cx.spawn_in(window, async move |_, cx| {
18154            let result = find_file(&buffer, project, buffer_position, cx).await;
18155
18156            if let Some((_, path)) = result {
18157                workspace
18158                    .update_in(cx, |workspace, window, cx| {
18159                        workspace.open_resolved_path(path, window, cx)
18160                    })?
18161                    .await?;
18162            }
18163            anyhow::Ok(())
18164        })
18165        .detach();
18166    }
18167
18168    pub(crate) fn navigate_to_hover_links(
18169        &mut self,
18170        kind: Option<GotoDefinitionKind>,
18171        definitions: Vec<HoverLink>,
18172        origin: Option<NavigationEntry>,
18173        split: bool,
18174        window: &mut Window,
18175        cx: &mut Context<Editor>,
18176    ) -> Task<Result<Navigated>> {
18177        // Separate out url and file links, we can only handle one of them at most or an arbitrary number of locations
18178        let mut first_url_or_file = None;
18179        let definitions: Vec<_> = definitions
18180            .into_iter()
18181            .filter_map(|def| match def {
18182                HoverLink::Text(link) => Some(Task::ready(anyhow::Ok(Some(link.target)))),
18183                HoverLink::InlayHint(lsp_location, server_id) => {
18184                    let computation =
18185                        self.compute_target_location(lsp_location, server_id, window, cx);
18186                    Some(cx.background_spawn(computation))
18187                }
18188                HoverLink::Url(url) => {
18189                    first_url_or_file = Some(Either::Left(url));
18190                    None
18191                }
18192                HoverLink::File(path) => {
18193                    first_url_or_file = Some(Either::Right(path));
18194                    None
18195                }
18196            })
18197            .collect();
18198
18199        let workspace = self.workspace();
18200
18201        let excerpt_context_lines = multi_buffer::excerpt_context_lines(cx);
18202        cx.spawn_in(window, async move |editor, cx| {
18203            let locations: Vec<Location> = future::join_all(definitions)
18204                .await
18205                .into_iter()
18206                .filter_map(|location| location.transpose())
18207                .collect::<Result<_>>()
18208                .context("location tasks")?;
18209            let mut locations = cx.update(|_, cx| {
18210                locations
18211                    .into_iter()
18212                    .map(|location| {
18213                        let buffer = location.buffer.read(cx);
18214                        (location.buffer, location.range.to_point(buffer))
18215                    })
18216                    .into_group_map()
18217            })?;
18218            let mut num_locations = 0;
18219            for ranges in locations.values_mut() {
18220                ranges.sort_by_key(|range| (range.start, Reverse(range.end)));
18221                ranges.dedup();
18222                let fits_in_one_excerpt = ranges
18223                    .iter()
18224                    .tuple_windows()
18225                    .all(|(a, b)| b.start.row - a.end.row <= 2 * excerpt_context_lines);
18226                num_locations += if fits_in_one_excerpt { 1 } else { ranges.len() };
18227            }
18228
18229            if num_locations > 1 {
18230                let tab_kind = match kind {
18231                    Some(GotoDefinitionKind::Implementation) => "Implementations",
18232                    Some(GotoDefinitionKind::Symbol) | None => "Definitions",
18233                    Some(GotoDefinitionKind::Declaration) => "Declarations",
18234                    Some(GotoDefinitionKind::Type) => "Types",
18235                };
18236                let title = editor
18237                    .update_in(cx, |_, _, cx| {
18238                        let target = locations
18239                            .iter()
18240                            .flat_map(|(k, v)| iter::repeat(k.clone()).zip(v))
18241                            .map(|(buffer, location)| {
18242                                buffer
18243                                    .read(cx)
18244                                    .text_for_range(location.clone())
18245                                    .collect::<String>()
18246                            })
18247                            .filter(|text| !text.contains('\n'))
18248                            .unique()
18249                            .take(3)
18250                            .join(", ");
18251                        if target.is_empty() {
18252                            tab_kind.to_owned()
18253                        } else {
18254                            format!("{tab_kind} for {target}")
18255                        }
18256                    })
18257                    .context("buffer title")?;
18258
18259                let Some(workspace) = workspace else {
18260                    return Ok(Navigated::No);
18261                };
18262
18263                let opened = workspace
18264                    .update_in(cx, |workspace, window, cx| {
18265                        let allow_preview = PreviewTabsSettings::get_global(cx)
18266                            .enable_preview_multibuffer_from_code_navigation;
18267                        if let Some((target_editor, target_pane)) =
18268                            Self::open_locations_in_multibuffer(
18269                                workspace,
18270                                locations,
18271                                title,
18272                                split,
18273                                allow_preview,
18274                                MultibufferSelectionMode::First,
18275                                window,
18276                                cx,
18277                            )
18278                        {
18279                            // We create our own nav history instead of using
18280                            // `target_editor.nav_history` because `nav_history`
18281                            // seems to be populated asynchronously when an item
18282                            // is added to a pane
18283                            let mut nav_history = target_pane
18284                                .update(cx, |pane, _| pane.nav_history_for_item(&target_editor));
18285                            target_editor.update(cx, |editor, cx| {
18286                                let nav_data = editor
18287                                    .navigation_data(editor.selections.newest_anchor().head(), cx);
18288                                let target =
18289                                    Some(nav_history.navigation_entry(Some(
18290                                        Arc::new(nav_data) as Arc<dyn Any + Send + Sync>
18291                                    )));
18292                                nav_history.push_tag(origin, target);
18293                            })
18294                        }
18295                    })
18296                    .is_ok();
18297
18298                anyhow::Ok(Navigated::from_bool(opened))
18299            } else if num_locations == 0 {
18300                // If there is one url or file, open it directly
18301                match first_url_or_file {
18302                    Some(Either::Left(url)) => {
18303                        cx.update(|window, cx| {
18304                            if parse_zed_link(&url, cx).is_some() {
18305                                window
18306                                    .dispatch_action(Box::new(zed_actions::OpenZedUrl { url }), cx);
18307                            } else {
18308                                cx.open_url(&url);
18309                            }
18310                        })?;
18311                        Ok(Navigated::Yes)
18312                    }
18313                    Some(Either::Right(path)) => {
18314                        // TODO(andrew): respect preview tab settings
18315                        //               `enable_keep_preview_on_code_navigation` and
18316                        //               `enable_preview_file_from_code_navigation`
18317                        let Some(workspace) = workspace else {
18318                            return Ok(Navigated::No);
18319                        };
18320                        workspace
18321                            .update_in(cx, |workspace, window, cx| {
18322                                workspace.open_resolved_path(path, window, cx)
18323                            })?
18324                            .await?;
18325                        Ok(Navigated::Yes)
18326                    }
18327                    None => Ok(Navigated::No),
18328                }
18329            } else {
18330                let (target_buffer, target_ranges) = locations.into_iter().next().unwrap();
18331
18332                editor.update_in(cx, |editor, window, cx| {
18333                    let target_ranges = target_ranges
18334                        .into_iter()
18335                        .map(|r| editor.range_for_match(&r))
18336                        .map(collapse_multiline_range)
18337                        .collect::<Vec<_>>();
18338                    if !split
18339                        && Some(&target_buffer) == editor.buffer.read(cx).as_singleton().as_ref()
18340                    {
18341                        let multibuffer = editor.buffer.read(cx);
18342                        let target_ranges = target_ranges
18343                            .into_iter()
18344                            .filter_map(|r| {
18345                                let start = multibuffer.buffer_point_to_anchor(
18346                                    &target_buffer,
18347                                    r.start,
18348                                    cx,
18349                                )?;
18350                                let end = multibuffer.buffer_point_to_anchor(
18351                                    &target_buffer,
18352                                    r.end,
18353                                    cx,
18354                                )?;
18355                                Some(start..end)
18356                            })
18357                            .collect::<Vec<_>>();
18358                        if target_ranges.is_empty() {
18359                            return Navigated::No;
18360                        }
18361
18362                        editor.change_selections(
18363                            SelectionEffects::default().nav_history(true),
18364                            window,
18365                            cx,
18366                            |s| s.select_anchor_ranges(target_ranges),
18367                        );
18368
18369                        let target =
18370                            editor.navigation_entry(editor.selections.newest_anchor().head(), cx);
18371                        if let Some(mut nav_history) = editor.nav_history.clone() {
18372                            nav_history.push_tag(origin, target);
18373                        }
18374                    } else {
18375                        let Some(workspace) = workspace else {
18376                            return Navigated::No;
18377                        };
18378                        let pane = workspace.read(cx).active_pane().clone();
18379                        window.defer(cx, move |window, cx| {
18380                            let (target_editor, target_pane): (Entity<Self>, Entity<Pane>) =
18381                                workspace.update(cx, |workspace, cx| {
18382                                    let pane = if split {
18383                                        workspace.adjacent_pane(window, cx)
18384                                    } else {
18385                                        workspace.active_pane().clone()
18386                                    };
18387
18388                                    let preview_tabs_settings = PreviewTabsSettings::get_global(cx);
18389                                    let keep_old_preview = preview_tabs_settings
18390                                        .enable_keep_preview_on_code_navigation;
18391                                    let allow_new_preview = preview_tabs_settings
18392                                        .enable_preview_file_from_code_navigation;
18393
18394                                    let editor = workspace.open_project_item(
18395                                        pane.clone(),
18396                                        target_buffer.clone(),
18397                                        true,
18398                                        true,
18399                                        keep_old_preview,
18400                                        allow_new_preview,
18401                                        window,
18402                                        cx,
18403                                    );
18404                                    (editor, pane)
18405                                });
18406                            // We create our own nav history instead of using
18407                            // `target_editor.nav_history` because `nav_history`
18408                            // seems to be populated asynchronously when an item
18409                            // is added to a pane
18410                            let mut nav_history = target_pane
18411                                .update(cx, |pane, _| pane.nav_history_for_item(&target_editor));
18412                            target_editor.update(cx, |target_editor, cx| {
18413                                // When selecting a definition in a different buffer, disable the nav history
18414                                // to avoid creating a history entry at the previous cursor location.
18415                                pane.update(cx, |pane, _| pane.disable_history());
18416
18417                                let multibuffer = target_editor.buffer.read(cx);
18418                                let Some(target_buffer) = multibuffer.as_singleton() else {
18419                                    return Navigated::No;
18420                                };
18421                                let target_ranges = target_ranges
18422                                    .into_iter()
18423                                    .filter_map(|r| {
18424                                        let start = multibuffer.buffer_point_to_anchor(
18425                                            &target_buffer,
18426                                            r.start,
18427                                            cx,
18428                                        )?;
18429                                        let end = multibuffer.buffer_point_to_anchor(
18430                                            &target_buffer,
18431                                            r.end,
18432                                            cx,
18433                                        )?;
18434                                        Some(start..end)
18435                                    })
18436                                    .collect::<Vec<_>>();
18437                                if target_ranges.is_empty() {
18438                                    return Navigated::No;
18439                                }
18440
18441                                target_editor.change_selections(
18442                                    SelectionEffects::default().nav_history(true),
18443                                    window,
18444                                    cx,
18445                                    |s| s.select_anchor_ranges(target_ranges),
18446                                );
18447
18448                                let nav_data = target_editor.navigation_data(
18449                                    target_editor.selections.newest_anchor().head(),
18450                                    cx,
18451                                );
18452                                let target =
18453                                    Some(nav_history.navigation_entry(Some(
18454                                        Arc::new(nav_data) as Arc<dyn Any + Send + Sync>
18455                                    )));
18456                                nav_history.push_tag(origin, target);
18457                                pane.update(cx, |pane, _| pane.enable_history());
18458                                Navigated::Yes
18459                            });
18460                        });
18461                    }
18462                    Navigated::Yes
18463                })
18464            }
18465        })
18466    }
18467
18468    fn compute_target_location(
18469        &self,
18470        lsp_location: lsp::Location,
18471        server_id: LanguageServerId,
18472        window: &mut Window,
18473        cx: &mut Context<Self>,
18474    ) -> Task<anyhow::Result<Option<Location>>> {
18475        let Some(project) = self.project.clone() else {
18476            return Task::ready(Ok(None));
18477        };
18478
18479        cx.spawn_in(window, async move |editor, cx| {
18480            let location_task = editor.update(cx, |_, cx| {
18481                project.update(cx, |project, cx| {
18482                    project.open_local_buffer_via_lsp(lsp_location.uri.clone(), server_id, cx)
18483                })
18484            })?;
18485            let location = Some({
18486                let target_buffer_handle = location_task.await.context("open local buffer")?;
18487                let range = target_buffer_handle.read_with(cx, |target_buffer, _| {
18488                    let target_start = target_buffer
18489                        .clip_point_utf16(point_from_lsp(lsp_location.range.start), Bias::Left);
18490                    let target_end = target_buffer
18491                        .clip_point_utf16(point_from_lsp(lsp_location.range.end), Bias::Left);
18492                    target_buffer.anchor_after(target_start)
18493                        ..target_buffer.anchor_before(target_end)
18494                });
18495                Location {
18496                    buffer: target_buffer_handle,
18497                    range,
18498                }
18499            });
18500            Ok(location)
18501        })
18502    }
18503
18504    fn go_to_next_reference(
18505        &mut self,
18506        _: &GoToNextReference,
18507        window: &mut Window,
18508        cx: &mut Context<Self>,
18509    ) {
18510        let task = self.go_to_reference_before_or_after_position(Direction::Next, 1, window, cx);
18511        if let Some(task) = task {
18512            task.detach();
18513        };
18514    }
18515
18516    fn go_to_prev_reference(
18517        &mut self,
18518        _: &GoToPreviousReference,
18519        window: &mut Window,
18520        cx: &mut Context<Self>,
18521    ) {
18522        let task = self.go_to_reference_before_or_after_position(Direction::Prev, 1, window, cx);
18523        if let Some(task) = task {
18524            task.detach();
18525        };
18526    }
18527
18528    fn go_to_symbol_by_offset(
18529        &mut self,
18530        window: &mut Window,
18531        cx: &mut Context<Self>,
18532        offset: i8,
18533    ) -> Task<Result<()>> {
18534        let editor_snapshot = self.snapshot(window, cx);
18535
18536        // We don't care about multi-buffer symbols
18537        let Some((excerpt_id, _, _)) = editor_snapshot.as_singleton() else {
18538            return Task::ready(Ok(()));
18539        };
18540
18541        let cursor_offset = self
18542            .selections
18543            .newest::<MultiBufferOffset>(&editor_snapshot.display_snapshot)
18544            .head();
18545
18546        cx.spawn_in(window, async move |editor, wcx| -> Result<()> {
18547            let Ok(Some(remote_id)) = editor.update(wcx, |ed, cx| {
18548                let buffer = ed.buffer.read(cx).as_singleton()?;
18549                Some(buffer.read(cx).remote_id())
18550            }) else {
18551                return Ok(());
18552            };
18553
18554            let task = editor.update(wcx, |ed, cx| ed.buffer_outline_items(remote_id, cx))?;
18555            let outline_items: Vec<OutlineItem<text::Anchor>> = task.await;
18556
18557            let multi_snapshot = editor_snapshot.buffer();
18558            let buffer_range = |range: &Range<_>| {
18559                Anchor::range_in_buffer(excerpt_id, range.clone()).to_offset(multi_snapshot)
18560            };
18561
18562            wcx.update_window(wcx.window_handle(), |_, window, acx| {
18563                let current_idx = outline_items
18564                    .iter()
18565                    .enumerate()
18566                    .filter_map(|(idx, item)| {
18567                        // Find the closest outline item by distance between outline text and cursor location
18568                        let source_range = buffer_range(&item.source_range_for_text);
18569                        let distance_to_closest_endpoint = cmp::min(
18570                            (source_range.start.0 as isize - cursor_offset.0 as isize).abs(),
18571                            (source_range.end.0 as isize - cursor_offset.0 as isize).abs(),
18572                        );
18573
18574                        let item_towards_offset =
18575                            (source_range.start.0 as isize - cursor_offset.0 as isize).signum()
18576                                == (offset as isize).signum();
18577
18578                        let source_range_contains_cursor = source_range.contains(&cursor_offset);
18579
18580                        // To pick the next outline to jump to, we should jump in the direction of the offset, and
18581                        // we should not already be within the outline's source range. We then pick the closest outline
18582                        // item.
18583                        (item_towards_offset && !source_range_contains_cursor)
18584                            .then_some((distance_to_closest_endpoint, idx))
18585                    })
18586                    .min()
18587                    .map(|(_, idx)| idx);
18588
18589                let Some(idx) = current_idx else {
18590                    return;
18591                };
18592
18593                let range = buffer_range(&outline_items[idx].source_range_for_text);
18594                let selection = [range.start..range.start];
18595
18596                let _ = editor
18597                    .update(acx, |editor, ecx| {
18598                        editor.change_selections(
18599                            SelectionEffects::scroll(Autoscroll::newest()),
18600                            window,
18601                            ecx,
18602                            |s| s.select_ranges(selection),
18603                        );
18604                    })
18605                    .ok();
18606            })?;
18607
18608            Ok(())
18609        })
18610    }
18611
18612    fn go_to_next_symbol(
18613        &mut self,
18614        _: &GoToNextSymbol,
18615        window: &mut Window,
18616        cx: &mut Context<Self>,
18617    ) {
18618        self.go_to_symbol_by_offset(window, cx, 1).detach();
18619    }
18620
18621    fn go_to_previous_symbol(
18622        &mut self,
18623        _: &GoToPreviousSymbol,
18624        window: &mut Window,
18625        cx: &mut Context<Self>,
18626    ) {
18627        self.go_to_symbol_by_offset(window, cx, -1).detach();
18628    }
18629
18630    pub fn go_to_reference_before_or_after_position(
18631        &mut self,
18632        direction: Direction,
18633        count: usize,
18634        window: &mut Window,
18635        cx: &mut Context<Self>,
18636    ) -> Option<Task<Result<()>>> {
18637        let selection = self.selections.newest_anchor();
18638        let head = selection.head();
18639
18640        let multi_buffer = self.buffer.read(cx);
18641
18642        let (buffer, text_head) = multi_buffer.text_anchor_for_position(head, cx)?;
18643        let workspace = self.workspace()?;
18644        let project = workspace.read(cx).project().clone();
18645        let references =
18646            project.update(cx, |project, cx| project.references(&buffer, text_head, cx));
18647        Some(cx.spawn_in(window, async move |editor, cx| -> Result<()> {
18648            let Some(locations) = references.await? else {
18649                return Ok(());
18650            };
18651
18652            if locations.is_empty() {
18653                // totally normal - the cursor may be on something which is not
18654                // a symbol (e.g. a keyword)
18655                log::info!("no references found under cursor");
18656                return Ok(());
18657            }
18658
18659            let multi_buffer = editor.read_with(cx, |editor, _| editor.buffer().clone())?;
18660
18661            let (locations, current_location_index) =
18662                multi_buffer.update(cx, |multi_buffer, cx| {
18663                    let mut locations = locations
18664                        .into_iter()
18665                        .filter_map(|loc| {
18666                            let start = multi_buffer.buffer_anchor_to_anchor(
18667                                &loc.buffer,
18668                                loc.range.start,
18669                                cx,
18670                            )?;
18671                            let end = multi_buffer.buffer_anchor_to_anchor(
18672                                &loc.buffer,
18673                                loc.range.end,
18674                                cx,
18675                            )?;
18676                            Some(start..end)
18677                        })
18678                        .collect::<Vec<_>>();
18679
18680                    let multi_buffer_snapshot = multi_buffer.snapshot(cx);
18681                    // There is an O(n) implementation, but given this list will be
18682                    // small (usually <100 items), the extra O(log(n)) factor isn't
18683                    // worth the (surprisingly large amount of) extra complexity.
18684                    locations
18685                        .sort_unstable_by(|l, r| l.start.cmp(&r.start, &multi_buffer_snapshot));
18686
18687                    let head_offset = head.to_offset(&multi_buffer_snapshot);
18688
18689                    let current_location_index = locations.iter().position(|loc| {
18690                        loc.start.to_offset(&multi_buffer_snapshot) <= head_offset
18691                            && loc.end.to_offset(&multi_buffer_snapshot) >= head_offset
18692                    });
18693
18694                    (locations, current_location_index)
18695                });
18696
18697            let Some(current_location_index) = current_location_index else {
18698                // This indicates something has gone wrong, because we already
18699                // handle the "no references" case above
18700                log::error!(
18701                    "failed to find current reference under cursor. Total references: {}",
18702                    locations.len()
18703                );
18704                return Ok(());
18705            };
18706
18707            let destination_location_index = match direction {
18708                Direction::Next => (current_location_index + count) % locations.len(),
18709                Direction::Prev => {
18710                    (current_location_index + locations.len() - count % locations.len())
18711                        % locations.len()
18712                }
18713            };
18714
18715            // TODO(cameron): is this needed?
18716            // the thinking is to avoid "jumping to the current location" (avoid
18717            // polluting "jumplist" in vim terms)
18718            if current_location_index == destination_location_index {
18719                return Ok(());
18720            }
18721
18722            let Range { start, end } = locations[destination_location_index];
18723
18724            editor.update_in(cx, |editor, window, cx| {
18725                let effects = SelectionEffects::default();
18726
18727                editor.unfold_ranges(&[start..end], false, false, cx);
18728                editor.change_selections(effects, window, cx, |s| {
18729                    s.select_ranges([start..start]);
18730                });
18731            })?;
18732
18733            Ok(())
18734        }))
18735    }
18736
18737    pub fn find_all_references(
18738        &mut self,
18739        action: &FindAllReferences,
18740        window: &mut Window,
18741        cx: &mut Context<Self>,
18742    ) -> Option<Task<Result<Navigated>>> {
18743        let always_open_multibuffer = action.always_open_multibuffer;
18744        let selection = self.selections.newest_anchor();
18745        let multi_buffer = self.buffer.read(cx);
18746        let multi_buffer_snapshot = multi_buffer.snapshot(cx);
18747        let selection_offset = selection.map(|anchor| anchor.to_offset(&multi_buffer_snapshot));
18748        let selection_point = selection.map(|anchor| anchor.to_point(&multi_buffer_snapshot));
18749        let head = selection_offset.head();
18750
18751        let head_anchor = multi_buffer_snapshot.anchor_at(
18752            head,
18753            if head < selection_offset.tail() {
18754                Bias::Right
18755            } else {
18756                Bias::Left
18757            },
18758        );
18759
18760        match self
18761            .find_all_references_task_sources
18762            .binary_search_by(|anchor| anchor.cmp(&head_anchor, &multi_buffer_snapshot))
18763        {
18764            Ok(_) => {
18765                log::info!(
18766                    "Ignoring repeated FindAllReferences invocation with the position of already running task"
18767                );
18768                return None;
18769            }
18770            Err(i) => {
18771                self.find_all_references_task_sources.insert(i, head_anchor);
18772            }
18773        }
18774
18775        let (buffer, head) = multi_buffer.text_anchor_for_position(head, cx)?;
18776        let workspace = self.workspace()?;
18777        let project = workspace.read(cx).project().clone();
18778        let references = project.update(cx, |project, cx| project.references(&buffer, head, cx));
18779        Some(cx.spawn_in(window, async move |editor, cx| {
18780            let _cleanup = cx.on_drop(&editor, move |editor, _| {
18781                if let Ok(i) = editor
18782                    .find_all_references_task_sources
18783                    .binary_search_by(|anchor| anchor.cmp(&head_anchor, &multi_buffer_snapshot))
18784                {
18785                    editor.find_all_references_task_sources.remove(i);
18786                }
18787            });
18788
18789            let Some(locations) = references.await? else {
18790                return anyhow::Ok(Navigated::No);
18791            };
18792            let mut locations = cx.update(|_, cx| {
18793                locations
18794                    .into_iter()
18795                    .map(|location| {
18796                        let buffer = location.buffer.read(cx);
18797                        (location.buffer, location.range.to_point(buffer))
18798                    })
18799                    // if special-casing the single-match case, remove ranges
18800                    // that intersect current selection
18801                    .filter(|(location_buffer, location)| {
18802                        if always_open_multibuffer || &buffer != location_buffer {
18803                            return true;
18804                        }
18805
18806                        !location.contains_inclusive(&selection_point.range())
18807                    })
18808                    .into_group_map()
18809            })?;
18810            if locations.is_empty() {
18811                return anyhow::Ok(Navigated::No);
18812            }
18813            for ranges in locations.values_mut() {
18814                ranges.sort_by_key(|range| (range.start, Reverse(range.end)));
18815                ranges.dedup();
18816            }
18817            let mut num_locations = 0;
18818            for ranges in locations.values_mut() {
18819                ranges.sort_by_key(|range| (range.start, Reverse(range.end)));
18820                ranges.dedup();
18821                num_locations += ranges.len();
18822            }
18823
18824            if num_locations == 1 && !always_open_multibuffer {
18825                let (target_buffer, target_ranges) = locations.into_iter().next().unwrap();
18826                let target_range = target_ranges.first().unwrap().clone();
18827
18828                return editor.update_in(cx, |editor, window, cx| {
18829                    let range = target_range.to_point(target_buffer.read(cx));
18830                    let range = editor.range_for_match(&range);
18831                    let range = range.start..range.start;
18832
18833                    if Some(&target_buffer) == editor.buffer.read(cx).as_singleton().as_ref() {
18834                        editor.go_to_singleton_buffer_range(range, window, cx);
18835                    } else {
18836                        let pane = workspace.read(cx).active_pane().clone();
18837                        window.defer(cx, move |window, cx| {
18838                            let target_editor: Entity<Self> =
18839                                workspace.update(cx, |workspace, cx| {
18840                                    let pane = workspace.active_pane().clone();
18841
18842                                    let preview_tabs_settings = PreviewTabsSettings::get_global(cx);
18843                                    let keep_old_preview = preview_tabs_settings
18844                                        .enable_keep_preview_on_code_navigation;
18845                                    let allow_new_preview = preview_tabs_settings
18846                                        .enable_preview_file_from_code_navigation;
18847
18848                                    workspace.open_project_item(
18849                                        pane,
18850                                        target_buffer.clone(),
18851                                        true,
18852                                        true,
18853                                        keep_old_preview,
18854                                        allow_new_preview,
18855                                        window,
18856                                        cx,
18857                                    )
18858                                });
18859                            target_editor.update(cx, |target_editor, cx| {
18860                                // When selecting a definition in a different buffer, disable the nav history
18861                                // to avoid creating a history entry at the previous cursor location.
18862                                pane.update(cx, |pane, _| pane.disable_history());
18863                                target_editor.go_to_singleton_buffer_range(range, window, cx);
18864                                pane.update(cx, |pane, _| pane.enable_history());
18865                            });
18866                        });
18867                    }
18868                    Navigated::No
18869                });
18870            }
18871
18872            workspace.update_in(cx, |workspace, window, cx| {
18873                let target = locations
18874                    .iter()
18875                    .flat_map(|(k, v)| iter::repeat(k.clone()).zip(v))
18876                    .map(|(buffer, location)| {
18877                        buffer
18878                            .read(cx)
18879                            .text_for_range(location.clone())
18880                            .collect::<String>()
18881                    })
18882                    .filter(|text| !text.contains('\n'))
18883                    .unique()
18884                    .take(3)
18885                    .join(", ");
18886                let title = if target.is_empty() {
18887                    "References".to_owned()
18888                } else {
18889                    format!("References to {target}")
18890                };
18891                let allow_preview = PreviewTabsSettings::get_global(cx)
18892                    .enable_preview_multibuffer_from_code_navigation;
18893                Self::open_locations_in_multibuffer(
18894                    workspace,
18895                    locations,
18896                    title,
18897                    false,
18898                    allow_preview,
18899                    MultibufferSelectionMode::First,
18900                    window,
18901                    cx,
18902                );
18903                Navigated::Yes
18904            })
18905        }))
18906    }
18907
18908    /// Opens a multibuffer with the given project locations in it.
18909    pub fn open_locations_in_multibuffer(
18910        workspace: &mut Workspace,
18911        locations: std::collections::HashMap<Entity<Buffer>, Vec<Range<Point>>>,
18912        title: String,
18913        split: bool,
18914        allow_preview: bool,
18915        multibuffer_selection_mode: MultibufferSelectionMode,
18916        window: &mut Window,
18917        cx: &mut Context<Workspace>,
18918    ) -> Option<(Entity<Editor>, Entity<Pane>)> {
18919        if locations.is_empty() {
18920            log::error!("bug: open_locations_in_multibuffer called with empty list of locations");
18921            return None;
18922        }
18923
18924        let capability = workspace.project().read(cx).capability();
18925        let mut ranges = <Vec<Range<Anchor>>>::new();
18926
18927        // a key to find existing multibuffer editors with the same set of locations
18928        // to prevent us from opening more and more multibuffer tabs for searches and the like
18929        let mut key = (title.clone(), vec![]);
18930        let excerpt_buffer = cx.new(|cx| {
18931            let key = &mut key.1;
18932            let mut multibuffer = MultiBuffer::new(capability);
18933            for (buffer, mut ranges_for_buffer) in locations {
18934                ranges_for_buffer.sort_by_key(|range| (range.start, Reverse(range.end)));
18935                key.push((buffer.read(cx).remote_id(), ranges_for_buffer.clone()));
18936                let (new_ranges, _) = multibuffer.set_excerpts_for_path(
18937                    PathKey::for_buffer(&buffer, cx),
18938                    buffer.clone(),
18939                    ranges_for_buffer,
18940                    multibuffer_context_lines(cx),
18941                    cx,
18942                );
18943                ranges.extend(new_ranges)
18944            }
18945
18946            multibuffer.with_title(title)
18947        });
18948        let existing = workspace.active_pane().update(cx, |pane, cx| {
18949            pane.items()
18950                .filter_map(|item| item.downcast::<Editor>())
18951                .find(|editor| {
18952                    editor
18953                        .read(cx)
18954                        .lookup_key
18955                        .as_ref()
18956                        .and_then(|it| {
18957                            it.downcast_ref::<(String, Vec<(BufferId, Vec<Range<Point>>)>)>()
18958                        })
18959                        .is_some_and(|it| *it == key)
18960                })
18961        });
18962        let was_existing = existing.is_some();
18963        let editor = existing.unwrap_or_else(|| {
18964            cx.new(|cx| {
18965                let mut editor = Editor::for_multibuffer(
18966                    excerpt_buffer,
18967                    Some(workspace.project().clone()),
18968                    window,
18969                    cx,
18970                );
18971                editor.lookup_key = Some(Box::new(key));
18972                editor
18973            })
18974        });
18975        editor.update(cx, |editor, cx| match multibuffer_selection_mode {
18976            MultibufferSelectionMode::First => {
18977                if let Some(first_range) = ranges.first() {
18978                    editor.change_selections(
18979                        SelectionEffects::no_scroll(),
18980                        window,
18981                        cx,
18982                        |selections| {
18983                            selections.clear_disjoint();
18984                            selections.select_anchor_ranges(std::iter::once(first_range.clone()));
18985                        },
18986                    );
18987                }
18988                editor.highlight_background(
18989                    HighlightKey::Editor,
18990                    &ranges,
18991                    |_, theme| theme.colors().editor_highlighted_line_background,
18992                    cx,
18993                );
18994            }
18995            MultibufferSelectionMode::All => {
18996                editor.change_selections(SelectionEffects::no_scroll(), window, cx, |selections| {
18997                    selections.clear_disjoint();
18998                    selections.select_anchor_ranges(ranges);
18999                });
19000            }
19001        });
19002
19003        let item = Box::new(editor.clone());
19004
19005        let pane = if split {
19006            workspace.adjacent_pane(window, cx)
19007        } else {
19008            workspace.active_pane().clone()
19009        };
19010        let activate_pane = split;
19011
19012        let mut destination_index = None;
19013        pane.update(cx, |pane, cx| {
19014            if allow_preview && !was_existing {
19015                destination_index = pane.replace_preview_item_id(item.item_id(), window, cx);
19016            }
19017            if was_existing && !allow_preview {
19018                pane.unpreview_item_if_preview(item.item_id());
19019            }
19020            pane.add_item(item, activate_pane, true, destination_index, window, cx);
19021        });
19022
19023        Some((editor, pane))
19024    }
19025
19026    pub fn rename(
19027        &mut self,
19028        _: &Rename,
19029        window: &mut Window,
19030        cx: &mut Context<Self>,
19031    ) -> Option<Task<Result<()>>> {
19032        use language::ToOffset as _;
19033
19034        let provider = self.semantics_provider.clone()?;
19035        let selection = self.selections.newest_anchor().clone();
19036        let (cursor_buffer, cursor_buffer_position) = self
19037            .buffer
19038            .read(cx)
19039            .text_anchor_for_position(selection.head(), cx)?;
19040        let (tail_buffer, cursor_buffer_position_end) = self
19041            .buffer
19042            .read(cx)
19043            .text_anchor_for_position(selection.tail(), cx)?;
19044        if tail_buffer != cursor_buffer {
19045            return None;
19046        }
19047
19048        let snapshot = cursor_buffer.read(cx).snapshot();
19049        let cursor_buffer_offset = cursor_buffer_position.to_offset(&snapshot);
19050        let cursor_buffer_offset_end = cursor_buffer_position_end.to_offset(&snapshot);
19051        let prepare_rename = provider
19052            .range_for_rename(&cursor_buffer, cursor_buffer_position, cx)
19053            .unwrap_or_else(|| Task::ready(Ok(None)));
19054        drop(snapshot);
19055
19056        Some(cx.spawn_in(window, async move |this, cx| {
19057            let rename_range = if let Some(range) = prepare_rename.await? {
19058                Some(range)
19059            } else {
19060                this.update(cx, |this, cx| {
19061                    let buffer = this.buffer.read(cx).snapshot(cx);
19062                    let mut buffer_highlights = this
19063                        .document_highlights_for_position(selection.head(), &buffer)
19064                        .filter(|highlight| {
19065                            highlight.start.excerpt_id == selection.head().excerpt_id
19066                                && highlight.end.excerpt_id == selection.head().excerpt_id
19067                        });
19068                    buffer_highlights
19069                        .next()
19070                        .map(|highlight| highlight.start.text_anchor..highlight.end.text_anchor)
19071                })?
19072            };
19073            if let Some(rename_range) = rename_range {
19074                this.update_in(cx, |this, window, cx| {
19075                    let snapshot = cursor_buffer.read(cx).snapshot();
19076                    let rename_buffer_range = rename_range.to_offset(&snapshot);
19077                    let cursor_offset_in_rename_range =
19078                        cursor_buffer_offset.saturating_sub(rename_buffer_range.start);
19079                    let cursor_offset_in_rename_range_end =
19080                        cursor_buffer_offset_end.saturating_sub(rename_buffer_range.start);
19081
19082                    this.take_rename(false, window, cx);
19083                    let buffer = this.buffer.read(cx).read(cx);
19084                    let cursor_offset = selection.head().to_offset(&buffer);
19085                    let rename_start =
19086                        cursor_offset.saturating_sub_usize(cursor_offset_in_rename_range);
19087                    let rename_end = rename_start + rename_buffer_range.len();
19088                    let range = buffer.anchor_before(rename_start)..buffer.anchor_after(rename_end);
19089                    let mut old_highlight_id = None;
19090                    let old_name: Arc<str> = buffer
19091                        .chunks(rename_start..rename_end, true)
19092                        .map(|chunk| {
19093                            if old_highlight_id.is_none() {
19094                                old_highlight_id = chunk.syntax_highlight_id;
19095                            }
19096                            chunk.text
19097                        })
19098                        .collect::<String>()
19099                        .into();
19100
19101                    drop(buffer);
19102
19103                    // Position the selection in the rename editor so that it matches the current selection.
19104                    this.show_local_selections = false;
19105                    let rename_editor = cx.new(|cx| {
19106                        let mut editor = Editor::single_line(window, cx);
19107                        editor.buffer.update(cx, |buffer, cx| {
19108                            buffer.edit(
19109                                [(MultiBufferOffset(0)..MultiBufferOffset(0), old_name.clone())],
19110                                None,
19111                                cx,
19112                            )
19113                        });
19114                        let cursor_offset_in_rename_range =
19115                            MultiBufferOffset(cursor_offset_in_rename_range);
19116                        let cursor_offset_in_rename_range_end =
19117                            MultiBufferOffset(cursor_offset_in_rename_range_end);
19118                        let rename_selection_range = match cursor_offset_in_rename_range
19119                            .cmp(&cursor_offset_in_rename_range_end)
19120                        {
19121                            Ordering::Equal => {
19122                                editor.select_all(&SelectAll, window, cx);
19123                                return editor;
19124                            }
19125                            Ordering::Less => {
19126                                cursor_offset_in_rename_range..cursor_offset_in_rename_range_end
19127                            }
19128                            Ordering::Greater => {
19129                                cursor_offset_in_rename_range_end..cursor_offset_in_rename_range
19130                            }
19131                        };
19132                        if rename_selection_range.end.0 > old_name.len() {
19133                            editor.select_all(&SelectAll, window, cx);
19134                        } else {
19135                            editor.change_selections(Default::default(), window, cx, |s| {
19136                                s.select_ranges([rename_selection_range]);
19137                            });
19138                        }
19139                        editor
19140                    });
19141                    cx.subscribe(&rename_editor, |_, _, e: &EditorEvent, cx| {
19142                        if e == &EditorEvent::Focused {
19143                            cx.emit(EditorEvent::FocusedIn)
19144                        }
19145                    })
19146                    .detach();
19147
19148                    let write_highlights =
19149                        this.clear_background_highlights(HighlightKey::DocumentHighlightWrite, cx);
19150                    let read_highlights =
19151                        this.clear_background_highlights(HighlightKey::DocumentHighlightRead, cx);
19152                    let ranges = write_highlights
19153                        .iter()
19154                        .flat_map(|(_, ranges)| ranges.iter())
19155                        .chain(read_highlights.iter().flat_map(|(_, ranges)| ranges.iter()))
19156                        .cloned()
19157                        .collect();
19158
19159                    this.highlight_text(
19160                        HighlightKey::Rename,
19161                        ranges,
19162                        HighlightStyle {
19163                            fade_out: Some(0.6),
19164                            ..Default::default()
19165                        },
19166                        cx,
19167                    );
19168                    let rename_focus_handle = rename_editor.focus_handle(cx);
19169                    window.focus(&rename_focus_handle, cx);
19170                    let block_id = this.insert_blocks(
19171                        [BlockProperties {
19172                            style: BlockStyle::Flex,
19173                            placement: BlockPlacement::Below(range.start),
19174                            height: Some(1),
19175                            render: Arc::new({
19176                                let rename_editor = rename_editor.clone();
19177                                move |cx: &mut BlockContext| {
19178                                    let mut text_style = cx.editor_style.text.clone();
19179                                    if let Some(highlight_style) = old_highlight_id
19180                                        .and_then(|h| cx.editor_style.syntax.get(h).cloned())
19181                                    {
19182                                        text_style = text_style.highlight(highlight_style);
19183                                    }
19184                                    div()
19185                                        .block_mouse_except_scroll()
19186                                        .pl(cx.anchor_x)
19187                                        .child(EditorElement::new(
19188                                            &rename_editor,
19189                                            EditorStyle {
19190                                                background: cx.theme().system().transparent,
19191                                                local_player: cx.editor_style.local_player,
19192                                                text: text_style,
19193                                                scrollbar_width: cx.editor_style.scrollbar_width,
19194                                                syntax: cx.editor_style.syntax.clone(),
19195                                                status: cx.editor_style.status.clone(),
19196                                                inlay_hints_style: HighlightStyle {
19197                                                    font_weight: Some(FontWeight::BOLD),
19198                                                    ..make_inlay_hints_style(cx.app)
19199                                                },
19200                                                edit_prediction_styles: make_suggestion_styles(
19201                                                    cx.app,
19202                                                ),
19203                                                ..EditorStyle::default()
19204                                            },
19205                                        ))
19206                                        .into_any_element()
19207                                }
19208                            }),
19209                            priority: 0,
19210                        }],
19211                        Some(Autoscroll::fit()),
19212                        cx,
19213                    )[0];
19214                    this.pending_rename = Some(RenameState {
19215                        range,
19216                        old_name,
19217                        editor: rename_editor,
19218                        block_id,
19219                    });
19220                })?;
19221            }
19222
19223            Ok(())
19224        }))
19225    }
19226
19227    pub fn confirm_rename(
19228        &mut self,
19229        _: &ConfirmRename,
19230        window: &mut Window,
19231        cx: &mut Context<Self>,
19232    ) -> Option<Task<Result<()>>> {
19233        let rename = self.take_rename(false, window, cx)?;
19234        let workspace = self.workspace()?.downgrade();
19235        let (buffer, start) = self
19236            .buffer
19237            .read(cx)
19238            .text_anchor_for_position(rename.range.start, cx)?;
19239        let (end_buffer, _) = self
19240            .buffer
19241            .read(cx)
19242            .text_anchor_for_position(rename.range.end, cx)?;
19243        if buffer != end_buffer {
19244            return None;
19245        }
19246
19247        let old_name = rename.old_name;
19248        let new_name = rename.editor.read(cx).text(cx);
19249
19250        let rename = self.semantics_provider.as_ref()?.perform_rename(
19251            &buffer,
19252            start,
19253            new_name.clone(),
19254            cx,
19255        )?;
19256
19257        Some(cx.spawn_in(window, async move |editor, cx| {
19258            let project_transaction = rename.await?;
19259            Self::open_project_transaction(
19260                &editor,
19261                workspace,
19262                project_transaction,
19263                format!("Rename: {}{}", old_name, new_name),
19264                cx,
19265            )
19266            .await?;
19267
19268            editor.update(cx, |editor, cx| {
19269                editor.refresh_document_highlights(cx);
19270            })?;
19271            Ok(())
19272        }))
19273    }
19274
19275    fn take_rename(
19276        &mut self,
19277        moving_cursor: bool,
19278        window: &mut Window,
19279        cx: &mut Context<Self>,
19280    ) -> Option<RenameState> {
19281        let rename = self.pending_rename.take()?;
19282        if rename.editor.focus_handle(cx).is_focused(window) {
19283            window.focus(&self.focus_handle, cx);
19284        }
19285
19286        self.remove_blocks(
19287            [rename.block_id].into_iter().collect(),
19288            Some(Autoscroll::fit()),
19289            cx,
19290        );
19291        self.clear_highlights(HighlightKey::Rename, cx);
19292        self.show_local_selections = true;
19293
19294        if moving_cursor {
19295            let cursor_in_rename_editor = rename.editor.update(cx, |editor, cx| {
19296                editor
19297                    .selections
19298                    .newest::<MultiBufferOffset>(&editor.display_snapshot(cx))
19299                    .head()
19300            });
19301
19302            // Update the selection to match the position of the selection inside
19303            // the rename editor.
19304            let snapshot = self.buffer.read(cx).read(cx);
19305            let rename_range = rename.range.to_offset(&snapshot);
19306            let cursor_in_editor = snapshot
19307                .clip_offset(rename_range.start + cursor_in_rename_editor, Bias::Left)
19308                .min(rename_range.end);
19309            drop(snapshot);
19310
19311            self.change_selections(SelectionEffects::no_scroll(), window, cx, |s| {
19312                s.select_ranges(vec![cursor_in_editor..cursor_in_editor])
19313            });
19314        } else {
19315            self.refresh_document_highlights(cx);
19316        }
19317
19318        Some(rename)
19319    }
19320
19321    pub fn pending_rename(&self) -> Option<&RenameState> {
19322        self.pending_rename.as_ref()
19323    }
19324
19325    fn format(
19326        &mut self,
19327        _: &Format,
19328        window: &mut Window,
19329        cx: &mut Context<Self>,
19330    ) -> Option<Task<Result<()>>> {
19331        self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
19332
19333        let project = match &self.project {
19334            Some(project) => project.clone(),
19335            None => return None,
19336        };
19337
19338        Some(self.perform_format(
19339            project,
19340            FormatTrigger::Manual,
19341            FormatTarget::Buffers(self.buffer.read(cx).all_buffers()),
19342            window,
19343            cx,
19344        ))
19345    }
19346
19347    fn format_selections(
19348        &mut self,
19349        _: &FormatSelections,
19350        window: &mut Window,
19351        cx: &mut Context<Self>,
19352    ) -> Option<Task<Result<()>>> {
19353        self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
19354
19355        let project = match &self.project {
19356            Some(project) => project.clone(),
19357            None => return None,
19358        };
19359
19360        let ranges = self
19361            .selections
19362            .all_adjusted(&self.display_snapshot(cx))
19363            .into_iter()
19364            .map(|selection| selection.range())
19365            .collect_vec();
19366
19367        Some(self.perform_format(
19368            project,
19369            FormatTrigger::Manual,
19370            FormatTarget::Ranges(ranges),
19371            window,
19372            cx,
19373        ))
19374    }
19375
19376    fn perform_format(
19377        &mut self,
19378        project: Entity<Project>,
19379        trigger: FormatTrigger,
19380        target: FormatTarget,
19381        window: &mut Window,
19382        cx: &mut Context<Self>,
19383    ) -> Task<Result<()>> {
19384        let buffer = self.buffer.clone();
19385        let (buffers, target) = match target {
19386            FormatTarget::Buffers(buffers) => (buffers, LspFormatTarget::Buffers),
19387            FormatTarget::Ranges(selection_ranges) => {
19388                let multi_buffer = buffer.read(cx);
19389                let snapshot = multi_buffer.read(cx);
19390                let mut buffers = HashSet::default();
19391                let mut buffer_id_to_ranges: BTreeMap<BufferId, Vec<Range<text::Anchor>>> =
19392                    BTreeMap::new();
19393                for selection_range in selection_ranges {
19394                    for (buffer, buffer_range, _) in
19395                        snapshot.range_to_buffer_ranges(selection_range.start..=selection_range.end)
19396                    {
19397                        let buffer_id = buffer.remote_id();
19398                        let start = buffer.anchor_before(buffer_range.start);
19399                        let end = buffer.anchor_after(buffer_range.end);
19400                        buffers.insert(multi_buffer.buffer(buffer_id).unwrap());
19401                        buffer_id_to_ranges
19402                            .entry(buffer_id)
19403                            .and_modify(|buffer_ranges| buffer_ranges.push(start..end))
19404                            .or_insert_with(|| vec![start..end]);
19405                    }
19406                }
19407                (buffers, LspFormatTarget::Ranges(buffer_id_to_ranges))
19408            }
19409        };
19410
19411        let transaction_id_prev = buffer.read(cx).last_transaction_id(cx);
19412        let selections_prev = transaction_id_prev
19413            .and_then(|transaction_id_prev| {
19414                // default to selections as they were after the last edit, if we have them,
19415                // instead of how they are now.
19416                // This will make it so that editing, moving somewhere else, formatting, then undoing the format
19417                // will take you back to where you made the last edit, instead of staying where you scrolled
19418                self.selection_history
19419                    .transaction(transaction_id_prev)
19420                    .map(|t| t.0.clone())
19421            })
19422            .unwrap_or_else(|| self.selections.disjoint_anchors_arc());
19423
19424        let mut timeout = cx.background_executor().timer(FORMAT_TIMEOUT).fuse();
19425        let format = project.update(cx, |project, cx| {
19426            project.format(buffers, target, true, trigger, cx)
19427        });
19428
19429        cx.spawn_in(window, async move |editor, cx| {
19430            let transaction = futures::select_biased! {
19431                transaction = format.log_err().fuse() => transaction,
19432                () = timeout => {
19433                    log::warn!("timed out waiting for formatting");
19434                    None
19435                }
19436            };
19437
19438            buffer.update(cx, |buffer, cx| {
19439                if let Some(transaction) = transaction
19440                    && !buffer.is_singleton()
19441                {
19442                    buffer.push_transaction(&transaction.0, cx);
19443                }
19444                cx.notify();
19445            });
19446
19447            if let Some(transaction_id_now) =
19448                buffer.read_with(cx, |b, cx| b.last_transaction_id(cx))
19449            {
19450                let has_new_transaction = transaction_id_prev != Some(transaction_id_now);
19451                if has_new_transaction {
19452                    editor
19453                        .update(cx, |editor, _| {
19454                            editor
19455                                .selection_history
19456                                .insert_transaction(transaction_id_now, selections_prev);
19457                        })
19458                        .ok();
19459                }
19460            }
19461
19462            Ok(())
19463        })
19464    }
19465
19466    fn organize_imports(
19467        &mut self,
19468        _: &OrganizeImports,
19469        window: &mut Window,
19470        cx: &mut Context<Self>,
19471    ) -> Option<Task<Result<()>>> {
19472        self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
19473        let project = match &self.project {
19474            Some(project) => project.clone(),
19475            None => return None,
19476        };
19477        Some(self.perform_code_action_kind(
19478            project,
19479            CodeActionKind::SOURCE_ORGANIZE_IMPORTS,
19480            window,
19481            cx,
19482        ))
19483    }
19484
19485    fn perform_code_action_kind(
19486        &mut self,
19487        project: Entity<Project>,
19488        kind: CodeActionKind,
19489        window: &mut Window,
19490        cx: &mut Context<Self>,
19491    ) -> Task<Result<()>> {
19492        let buffer = self.buffer.clone();
19493        let buffers = buffer.read(cx).all_buffers();
19494        let mut timeout = cx.background_executor().timer(CODE_ACTION_TIMEOUT).fuse();
19495        let apply_action = project.update(cx, |project, cx| {
19496            project.apply_code_action_kind(buffers, kind, true, cx)
19497        });
19498        cx.spawn_in(window, async move |_, cx| {
19499            let transaction = futures::select_biased! {
19500                () = timeout => {
19501                    log::warn!("timed out waiting for executing code action");
19502                    None
19503                }
19504                transaction = apply_action.log_err().fuse() => transaction,
19505            };
19506            buffer.update(cx, |buffer, cx| {
19507                // check if we need this
19508                if let Some(transaction) = transaction
19509                    && !buffer.is_singleton()
19510                {
19511                    buffer.push_transaction(&transaction.0, cx);
19512                }
19513                cx.notify();
19514            });
19515            Ok(())
19516        })
19517    }
19518
19519    pub fn restart_language_server(
19520        &mut self,
19521        _: &RestartLanguageServer,
19522        _: &mut Window,
19523        cx: &mut Context<Self>,
19524    ) {
19525        if let Some(project) = self.project.clone() {
19526            self.buffer.update(cx, |multi_buffer, cx| {
19527                project.update(cx, |project, cx| {
19528                    project.restart_language_servers_for_buffers(
19529                        multi_buffer.all_buffers().into_iter().collect(),
19530                        HashSet::default(),
19531                        cx,
19532                    );
19533                });
19534            })
19535        }
19536    }
19537
19538    pub fn stop_language_server(
19539        &mut self,
19540        _: &StopLanguageServer,
19541        _: &mut Window,
19542        cx: &mut Context<Self>,
19543    ) {
19544        if let Some(project) = self.project.clone() {
19545            self.buffer.update(cx, |multi_buffer, cx| {
19546                project.update(cx, |project, cx| {
19547                    project.stop_language_servers_for_buffers(
19548                        multi_buffer.all_buffers().into_iter().collect(),
19549                        HashSet::default(),
19550                        cx,
19551                    );
19552                });
19553            });
19554        }
19555    }
19556
19557    fn cancel_language_server_work(
19558        workspace: &mut Workspace,
19559        _: &actions::CancelLanguageServerWork,
19560        _: &mut Window,
19561        cx: &mut Context<Workspace>,
19562    ) {
19563        let project = workspace.project();
19564        let buffers = workspace
19565            .active_item(cx)
19566            .and_then(|item| item.act_as::<Editor>(cx))
19567            .map_or(HashSet::default(), |editor| {
19568                editor.read(cx).buffer.read(cx).all_buffers()
19569            });
19570        project.update(cx, |project, cx| {
19571            project.cancel_language_server_work_for_buffers(buffers, cx);
19572        });
19573    }
19574
19575    fn show_character_palette(
19576        &mut self,
19577        _: &ShowCharacterPalette,
19578        window: &mut Window,
19579        _: &mut Context<Self>,
19580    ) {
19581        window.show_character_palette();
19582    }
19583
19584    fn refresh_active_diagnostics(&mut self, cx: &mut Context<Editor>) {
19585        if !self.diagnostics_enabled() {
19586            return;
19587        }
19588
19589        if let ActiveDiagnostic::Group(active_diagnostics) = &mut self.active_diagnostics {
19590            let buffer = self.buffer.read(cx).snapshot(cx);
19591            let primary_range_start = active_diagnostics.active_range.start.to_offset(&buffer);
19592            let primary_range_end = active_diagnostics.active_range.end.to_offset(&buffer);
19593            let is_valid = buffer
19594                .diagnostics_in_range::<MultiBufferOffset>(primary_range_start..primary_range_end)
19595                .any(|entry| {
19596                    entry.diagnostic.is_primary
19597                        && !entry.range.is_empty()
19598                        && entry.range.start == primary_range_start
19599                        && entry.diagnostic.message == active_diagnostics.active_message
19600                });
19601
19602            if !is_valid {
19603                self.dismiss_diagnostics(cx);
19604            }
19605        }
19606    }
19607
19608    pub fn active_diagnostic_group(&self) -> Option<&ActiveDiagnosticGroup> {
19609        match &self.active_diagnostics {
19610            ActiveDiagnostic::Group(group) => Some(group),
19611            _ => None,
19612        }
19613    }
19614
19615    pub fn set_all_diagnostics_active(&mut self, cx: &mut Context<Self>) {
19616        if !self.diagnostics_enabled() {
19617            return;
19618        }
19619        self.dismiss_diagnostics(cx);
19620        self.active_diagnostics = ActiveDiagnostic::All;
19621    }
19622
19623    fn activate_diagnostics(
19624        &mut self,
19625        buffer_id: BufferId,
19626        diagnostic: DiagnosticEntryRef<'_, MultiBufferOffset>,
19627        window: &mut Window,
19628        cx: &mut Context<Self>,
19629    ) {
19630        if !self.diagnostics_enabled() || matches!(self.active_diagnostics, ActiveDiagnostic::All) {
19631            return;
19632        }
19633        self.dismiss_diagnostics(cx);
19634        let snapshot = self.snapshot(window, cx);
19635        let buffer = self.buffer.read(cx).snapshot(cx);
19636        let Some(renderer) = GlobalDiagnosticRenderer::global(cx) else {
19637            return;
19638        };
19639
19640        let diagnostic_group = buffer
19641            .diagnostic_group(buffer_id, diagnostic.diagnostic.group_id)
19642            .collect::<Vec<_>>();
19643
19644        let language_registry = self
19645            .project()
19646            .map(|project| project.read(cx).languages().clone());
19647
19648        let blocks = renderer.render_group(
19649            diagnostic_group,
19650            buffer_id,
19651            snapshot,
19652            cx.weak_entity(),
19653            language_registry,
19654            cx,
19655        );
19656
19657        let blocks = self.display_map.update(cx, |display_map, cx| {
19658            display_map.insert_blocks(blocks, cx).into_iter().collect()
19659        });
19660        self.active_diagnostics = ActiveDiagnostic::Group(ActiveDiagnosticGroup {
19661            active_range: buffer.anchor_before(diagnostic.range.start)
19662                ..buffer.anchor_after(diagnostic.range.end),
19663            active_message: diagnostic.diagnostic.message.clone(),
19664            group_id: diagnostic.diagnostic.group_id,
19665            blocks,
19666        });
19667        cx.notify();
19668    }
19669
19670    fn dismiss_diagnostics(&mut self, cx: &mut Context<Self>) {
19671        if matches!(self.active_diagnostics, ActiveDiagnostic::All) {
19672            return;
19673        };
19674
19675        let prev = mem::replace(&mut self.active_diagnostics, ActiveDiagnostic::None);
19676        if let ActiveDiagnostic::Group(group) = prev {
19677            self.display_map.update(cx, |display_map, cx| {
19678                display_map.remove_blocks(group.blocks, cx);
19679            });
19680            cx.notify();
19681        }
19682    }
19683
19684    /// Disable inline diagnostics rendering for this editor.
19685    pub fn disable_inline_diagnostics(&mut self) {
19686        self.inline_diagnostics_enabled = false;
19687        self.inline_diagnostics_update = Task::ready(());
19688        self.inline_diagnostics.clear();
19689    }
19690
19691    pub fn disable_diagnostics(&mut self, cx: &mut Context<Self>) {
19692        self.diagnostics_enabled = false;
19693        self.dismiss_diagnostics(cx);
19694        self.inline_diagnostics_update = Task::ready(());
19695        self.inline_diagnostics.clear();
19696    }
19697
19698    pub fn disable_word_completions(&mut self) {
19699        self.word_completions_enabled = false;
19700    }
19701
19702    pub fn diagnostics_enabled(&self) -> bool {
19703        self.diagnostics_enabled && self.lsp_data_enabled()
19704    }
19705
19706    pub fn inline_diagnostics_enabled(&self) -> bool {
19707        self.inline_diagnostics_enabled && self.diagnostics_enabled()
19708    }
19709
19710    pub fn show_inline_diagnostics(&self) -> bool {
19711        self.show_inline_diagnostics
19712    }
19713
19714    pub fn toggle_inline_diagnostics(
19715        &mut self,
19716        _: &ToggleInlineDiagnostics,
19717        window: &mut Window,
19718        cx: &mut Context<Editor>,
19719    ) {
19720        self.show_inline_diagnostics = !self.show_inline_diagnostics;
19721        self.refresh_inline_diagnostics(false, window, cx);
19722    }
19723
19724    pub fn set_max_diagnostics_severity(&mut self, severity: DiagnosticSeverity, cx: &mut App) {
19725        self.diagnostics_max_severity = severity;
19726        self.display_map.update(cx, |display_map, _| {
19727            display_map.diagnostics_max_severity = self.diagnostics_max_severity;
19728        });
19729    }
19730
19731    pub fn toggle_diagnostics(
19732        &mut self,
19733        _: &ToggleDiagnostics,
19734        window: &mut Window,
19735        cx: &mut Context<Editor>,
19736    ) {
19737        if !self.diagnostics_enabled() {
19738            return;
19739        }
19740
19741        let new_severity = if self.diagnostics_max_severity == DiagnosticSeverity::Off {
19742            EditorSettings::get_global(cx)
19743                .diagnostics_max_severity
19744                .filter(|severity| severity != &DiagnosticSeverity::Off)
19745                .unwrap_or(DiagnosticSeverity::Hint)
19746        } else {
19747            DiagnosticSeverity::Off
19748        };
19749        self.set_max_diagnostics_severity(new_severity, cx);
19750        if self.diagnostics_max_severity == DiagnosticSeverity::Off {
19751            self.active_diagnostics = ActiveDiagnostic::None;
19752            self.inline_diagnostics_update = Task::ready(());
19753            self.inline_diagnostics.clear();
19754        } else {
19755            self.refresh_inline_diagnostics(false, window, cx);
19756        }
19757
19758        cx.notify();
19759    }
19760
19761    pub fn toggle_minimap(
19762        &mut self,
19763        _: &ToggleMinimap,
19764        window: &mut Window,
19765        cx: &mut Context<Editor>,
19766    ) {
19767        if self.supports_minimap(cx) {
19768            self.set_minimap_visibility(self.minimap_visibility.toggle_visibility(), window, cx);
19769        }
19770    }
19771
19772    fn refresh_inline_diagnostics(
19773        &mut self,
19774        debounce: bool,
19775        window: &mut Window,
19776        cx: &mut Context<Self>,
19777    ) {
19778        let max_severity = ProjectSettings::get_global(cx)
19779            .diagnostics
19780            .inline
19781            .max_severity
19782            .unwrap_or(self.diagnostics_max_severity);
19783
19784        if !self.inline_diagnostics_enabled()
19785            || !self.diagnostics_enabled()
19786            || !self.show_inline_diagnostics
19787            || max_severity == DiagnosticSeverity::Off
19788        {
19789            self.inline_diagnostics_update = Task::ready(());
19790            self.inline_diagnostics.clear();
19791            return;
19792        }
19793
19794        let debounce_ms = ProjectSettings::get_global(cx)
19795            .diagnostics
19796            .inline
19797            .update_debounce_ms;
19798        let debounce = if debounce && debounce_ms > 0 {
19799            Some(Duration::from_millis(debounce_ms))
19800        } else {
19801            None
19802        };
19803        self.inline_diagnostics_update = cx.spawn_in(window, async move |editor, cx| {
19804            if let Some(debounce) = debounce {
19805                cx.background_executor().timer(debounce).await;
19806            }
19807            let Some(snapshot) = editor.upgrade().map(|editor| {
19808                editor.update(cx, |editor, cx| editor.buffer().read(cx).snapshot(cx))
19809            }) else {
19810                return;
19811            };
19812
19813            let new_inline_diagnostics = cx
19814                .background_spawn(async move {
19815                    let mut inline_diagnostics = Vec::<(Anchor, InlineDiagnostic)>::new();
19816                    for diagnostic_entry in
19817                        snapshot.diagnostics_in_range(MultiBufferOffset(0)..snapshot.len())
19818                    {
19819                        let message = diagnostic_entry
19820                            .diagnostic
19821                            .message
19822                            .split_once('\n')
19823                            .map(|(line, _)| line)
19824                            .map(SharedString::new)
19825                            .unwrap_or_else(|| {
19826                                SharedString::new(&*diagnostic_entry.diagnostic.message)
19827                            });
19828                        let start_anchor = snapshot.anchor_before(diagnostic_entry.range.start);
19829                        let (Ok(i) | Err(i)) = inline_diagnostics
19830                            .binary_search_by(|(probe, _)| probe.cmp(&start_anchor, &snapshot));
19831                        inline_diagnostics.insert(
19832                            i,
19833                            (
19834                                start_anchor,
19835                                InlineDiagnostic {
19836                                    message,
19837                                    group_id: diagnostic_entry.diagnostic.group_id,
19838                                    start: diagnostic_entry.range.start.to_point(&snapshot),
19839                                    is_primary: diagnostic_entry.diagnostic.is_primary,
19840                                    severity: diagnostic_entry.diagnostic.severity,
19841                                },
19842                            ),
19843                        );
19844                    }
19845                    inline_diagnostics
19846                })
19847                .await;
19848
19849            editor
19850                .update(cx, |editor, cx| {
19851                    editor.inline_diagnostics = new_inline_diagnostics;
19852                    cx.notify();
19853                })
19854                .ok();
19855        });
19856    }
19857
19858    fn pull_diagnostics(
19859        &mut self,
19860        buffer_id: BufferId,
19861        _window: &Window,
19862        cx: &mut Context<Self>,
19863    ) -> Option<()> {
19864        // `ActiveDiagnostic::All` is a special mode where editor's diagnostics are managed by the external view,
19865        // skip any LSP updates for it.
19866
19867        if self.active_diagnostics == ActiveDiagnostic::All || !self.diagnostics_enabled() {
19868            return None;
19869        }
19870        let pull_diagnostics_settings = ProjectSettings::get_global(cx)
19871            .diagnostics
19872            .lsp_pull_diagnostics;
19873        if !pull_diagnostics_settings.enabled {
19874            return None;
19875        }
19876        let debounce = Duration::from_millis(pull_diagnostics_settings.debounce_ms);
19877        let project = self.project()?.downgrade();
19878        let buffer = self.buffer().read(cx).buffer(buffer_id)?;
19879
19880        self.pull_diagnostics_task = cx.spawn(async move |_, cx| {
19881            cx.background_executor().timer(debounce).await;
19882            if let Ok(task) = project.update(cx, |project, cx| {
19883                project.lsp_store().update(cx, |lsp_store, cx| {
19884                    lsp_store.pull_diagnostics_for_buffer(buffer, cx)
19885                })
19886            }) {
19887                task.await.log_err();
19888            }
19889            project
19890                .update(cx, |project, cx| {
19891                    project.lsp_store().update(cx, |lsp_store, cx| {
19892                        lsp_store.pull_document_diagnostics_for_buffer_edit(buffer_id, cx);
19893                    })
19894                })
19895                .log_err();
19896        });
19897
19898        Some(())
19899    }
19900
19901    pub fn set_selections_from_remote(
19902        &mut self,
19903        selections: Vec<Selection<Anchor>>,
19904        pending_selection: Option<Selection<Anchor>>,
19905        window: &mut Window,
19906        cx: &mut Context<Self>,
19907    ) {
19908        let old_cursor_position = self.selections.newest_anchor().head();
19909        self.selections
19910            .change_with(&self.display_snapshot(cx), |s| {
19911                s.select_anchors(selections);
19912                if let Some(pending_selection) = pending_selection {
19913                    s.set_pending(pending_selection, SelectMode::Character);
19914                } else {
19915                    s.clear_pending();
19916                }
19917            });
19918        self.selections_did_change(
19919            false,
19920            &old_cursor_position,
19921            SelectionEffects::default(),
19922            window,
19923            cx,
19924        );
19925    }
19926
19927    pub fn transact(
19928        &mut self,
19929        window: &mut Window,
19930        cx: &mut Context<Self>,
19931        update: impl FnOnce(&mut Self, &mut Window, &mut Context<Self>),
19932    ) -> Option<TransactionId> {
19933        self.with_selection_effects_deferred(window, cx, |this, window, cx| {
19934            this.start_transaction_at(Instant::now(), window, cx);
19935            update(this, window, cx);
19936            this.end_transaction_at(Instant::now(), cx)
19937        })
19938    }
19939
19940    pub fn start_transaction_at(
19941        &mut self,
19942        now: Instant,
19943        window: &mut Window,
19944        cx: &mut Context<Self>,
19945    ) -> Option<TransactionId> {
19946        self.end_selection(window, cx);
19947        if let Some(tx_id) = self
19948            .buffer
19949            .update(cx, |buffer, cx| buffer.start_transaction_at(now, cx))
19950        {
19951            self.selection_history
19952                .insert_transaction(tx_id, self.selections.disjoint_anchors_arc());
19953            cx.emit(EditorEvent::TransactionBegun {
19954                transaction_id: tx_id,
19955            });
19956            Some(tx_id)
19957        } else {
19958            None
19959        }
19960    }
19961
19962    pub fn end_transaction_at(
19963        &mut self,
19964        now: Instant,
19965        cx: &mut Context<Self>,
19966    ) -> Option<TransactionId> {
19967        if let Some(transaction_id) = self
19968            .buffer
19969            .update(cx, |buffer, cx| buffer.end_transaction_at(now, cx))
19970        {
19971            if let Some((_, end_selections)) =
19972                self.selection_history.transaction_mut(transaction_id)
19973            {
19974                *end_selections = Some(self.selections.disjoint_anchors_arc());
19975            } else {
19976                log::error!("unexpectedly ended a transaction that wasn't started by this editor");
19977            }
19978
19979            cx.emit(EditorEvent::Edited { transaction_id });
19980            Some(transaction_id)
19981        } else {
19982            None
19983        }
19984    }
19985
19986    pub fn modify_transaction_selection_history(
19987        &mut self,
19988        transaction_id: TransactionId,
19989        modify: impl FnOnce(&mut (Arc<[Selection<Anchor>]>, Option<Arc<[Selection<Anchor>]>>)),
19990    ) -> bool {
19991        self.selection_history
19992            .transaction_mut(transaction_id)
19993            .map(modify)
19994            .is_some()
19995    }
19996
19997    pub fn set_mark(&mut self, _: &actions::SetMark, window: &mut Window, cx: &mut Context<Self>) {
19998        if self.selection_mark_mode {
19999            self.change_selections(SelectionEffects::no_scroll(), window, cx, |s| {
20000                s.move_with(&mut |_, sel| {
20001                    sel.collapse_to(sel.head(), SelectionGoal::None);
20002                });
20003            })
20004        }
20005        self.selection_mark_mode = true;
20006        cx.notify();
20007    }
20008
20009    pub fn swap_selection_ends(
20010        &mut self,
20011        _: &actions::SwapSelectionEnds,
20012        window: &mut Window,
20013        cx: &mut Context<Self>,
20014    ) {
20015        self.change_selections(SelectionEffects::no_scroll(), window, cx, |s| {
20016            s.move_with(&mut |_, sel| {
20017                if sel.start != sel.end {
20018                    sel.reversed = !sel.reversed
20019                }
20020            });
20021        });
20022        self.request_autoscroll(Autoscroll::newest(), cx);
20023        cx.notify();
20024    }
20025
20026    pub fn toggle_focus(
20027        workspace: &mut Workspace,
20028        _: &actions::ToggleFocus,
20029        window: &mut Window,
20030        cx: &mut Context<Workspace>,
20031    ) {
20032        let Some(item) = workspace.recent_active_item_by_type::<Self>(cx) else {
20033            return;
20034        };
20035        workspace.activate_item(&item, true, true, window, cx);
20036    }
20037
20038    pub fn toggle_fold(
20039        &mut self,
20040        _: &actions::ToggleFold,
20041        window: &mut Window,
20042        cx: &mut Context<Self>,
20043    ) {
20044        if self.buffer_kind(cx) == ItemBufferKind::Singleton {
20045            let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
20046            let selection = self.selections.newest::<Point>(&display_map);
20047
20048            let range = if selection.is_empty() {
20049                let point = selection.head().to_display_point(&display_map);
20050                let start = DisplayPoint::new(point.row(), 0).to_point(&display_map);
20051                let end = DisplayPoint::new(point.row(), display_map.line_len(point.row()))
20052                    .to_point(&display_map);
20053                start..end
20054            } else {
20055                selection.range()
20056            };
20057            if display_map.folds_in_range(range).next().is_some() {
20058                self.unfold_lines(&Default::default(), window, cx)
20059            } else {
20060                self.fold(&Default::default(), window, cx)
20061            }
20062        } else {
20063            let multi_buffer_snapshot = self.buffer.read(cx).snapshot(cx);
20064            let buffer_ids: HashSet<_> = self
20065                .selections
20066                .disjoint_anchor_ranges()
20067                .flat_map(|range| multi_buffer_snapshot.buffer_ids_for_range(range))
20068                .collect();
20069
20070            let should_unfold = buffer_ids
20071                .iter()
20072                .any(|buffer_id| self.is_buffer_folded(*buffer_id, cx));
20073
20074            for buffer_id in buffer_ids {
20075                if should_unfold {
20076                    self.unfold_buffer(buffer_id, cx);
20077                } else {
20078                    self.fold_buffer(buffer_id, cx);
20079                }
20080            }
20081        }
20082    }
20083
20084    pub fn toggle_fold_recursive(
20085        &mut self,
20086        _: &actions::ToggleFoldRecursive,
20087        window: &mut Window,
20088        cx: &mut Context<Self>,
20089    ) {
20090        let selection = self.selections.newest::<Point>(&self.display_snapshot(cx));
20091
20092        let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
20093        let range = if selection.is_empty() {
20094            let point = selection.head().to_display_point(&display_map);
20095            let start = DisplayPoint::new(point.row(), 0).to_point(&display_map);
20096            let end = DisplayPoint::new(point.row(), display_map.line_len(point.row()))
20097                .to_point(&display_map);
20098            start..end
20099        } else {
20100            selection.range()
20101        };
20102        if display_map.folds_in_range(range).next().is_some() {
20103            self.unfold_recursive(&Default::default(), window, cx)
20104        } else {
20105            self.fold_recursive(&Default::default(), window, cx)
20106        }
20107    }
20108
20109    pub fn fold(&mut self, _: &actions::Fold, window: &mut Window, cx: &mut Context<Self>) {
20110        if self.buffer_kind(cx) == ItemBufferKind::Singleton {
20111            let mut to_fold = Vec::new();
20112            let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
20113            let selections = self.selections.all_adjusted(&display_map);
20114
20115            for selection in selections {
20116                let range = selection.range().sorted();
20117                let buffer_start_row = range.start.row;
20118
20119                if range.start.row != range.end.row {
20120                    let mut found = false;
20121                    let mut row = range.start.row;
20122                    while row <= range.end.row {
20123                        if let Some(crease) = display_map.crease_for_buffer_row(MultiBufferRow(row))
20124                        {
20125                            found = true;
20126                            row = crease.range().end.row + 1;
20127                            to_fold.push(crease);
20128                        } else {
20129                            row += 1
20130                        }
20131                    }
20132                    if found {
20133                        continue;
20134                    }
20135                }
20136
20137                for row in (0..=range.start.row).rev() {
20138                    if let Some(crease) = display_map.crease_for_buffer_row(MultiBufferRow(row))
20139                        && crease.range().end.row >= buffer_start_row
20140                    {
20141                        to_fold.push(crease);
20142                        if row <= range.start.row {
20143                            break;
20144                        }
20145                    }
20146                }
20147            }
20148
20149            self.fold_creases(to_fold, true, window, cx);
20150        } else {
20151            let multi_buffer_snapshot = self.buffer.read(cx).snapshot(cx);
20152            let buffer_ids = self
20153                .selections
20154                .disjoint_anchor_ranges()
20155                .flat_map(|range| multi_buffer_snapshot.buffer_ids_for_range(range))
20156                .collect::<HashSet<_>>();
20157            for buffer_id in buffer_ids {
20158                self.fold_buffer(buffer_id, cx);
20159            }
20160        }
20161    }
20162
20163    pub fn toggle_fold_all(
20164        &mut self,
20165        _: &actions::ToggleFoldAll,
20166        window: &mut Window,
20167        cx: &mut Context<Self>,
20168    ) {
20169        let has_folds = if self.buffer.read(cx).is_singleton() {
20170            let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
20171            let has_folds = display_map
20172                .folds_in_range(MultiBufferOffset(0)..display_map.buffer_snapshot().len())
20173                .next()
20174                .is_some();
20175            has_folds
20176        } else {
20177            let buffer_ids = self.buffer.read(cx).excerpt_buffer_ids();
20178            let has_folds = buffer_ids
20179                .iter()
20180                .any(|buffer_id| self.is_buffer_folded(*buffer_id, cx));
20181            has_folds
20182        };
20183
20184        if has_folds {
20185            self.unfold_all(&actions::UnfoldAll, window, cx);
20186        } else {
20187            self.fold_all(&actions::FoldAll, window, cx);
20188        }
20189    }
20190
20191    fn fold_at_level(
20192        &mut self,
20193        fold_at: &FoldAtLevel,
20194        window: &mut Window,
20195        cx: &mut Context<Self>,
20196    ) {
20197        if !self.buffer.read(cx).is_singleton() {
20198            return;
20199        }
20200
20201        let fold_at_level = fold_at.0;
20202        let snapshot = self.buffer.read(cx).snapshot(cx);
20203        let mut to_fold = Vec::new();
20204        let mut stack = vec![(0, snapshot.max_row().0, 1)];
20205
20206        let row_ranges_to_keep: Vec<Range<u32>> = self
20207            .selections
20208            .all::<Point>(&self.display_snapshot(cx))
20209            .into_iter()
20210            .map(|sel| sel.start.row..sel.end.row)
20211            .collect();
20212
20213        while let Some((mut start_row, end_row, current_level)) = stack.pop() {
20214            while start_row < end_row {
20215                match self
20216                    .snapshot(window, cx)
20217                    .crease_for_buffer_row(MultiBufferRow(start_row))
20218                {
20219                    Some(crease) => {
20220                        let nested_start_row = crease.range().start.row + 1;
20221                        let nested_end_row = crease.range().end.row;
20222
20223                        if current_level < fold_at_level {
20224                            stack.push((nested_start_row, nested_end_row, current_level + 1));
20225                        } else if current_level == fold_at_level {
20226                            // Fold iff there is no selection completely contained within the fold region
20227                            if !row_ranges_to_keep.iter().any(|selection| {
20228                                selection.end >= nested_start_row
20229                                    && selection.start <= nested_end_row
20230                            }) {
20231                                to_fold.push(crease);
20232                            }
20233                        }
20234
20235                        start_row = nested_end_row + 1;
20236                    }
20237                    None => start_row += 1,
20238                }
20239            }
20240        }
20241
20242        self.fold_creases(to_fold, true, window, cx);
20243    }
20244
20245    pub fn fold_at_level_1(
20246        &mut self,
20247        _: &actions::FoldAtLevel1,
20248        window: &mut Window,
20249        cx: &mut Context<Self>,
20250    ) {
20251        self.fold_at_level(&actions::FoldAtLevel(1), window, cx);
20252    }
20253
20254    pub fn fold_at_level_2(
20255        &mut self,
20256        _: &actions::FoldAtLevel2,
20257        window: &mut Window,
20258        cx: &mut Context<Self>,
20259    ) {
20260        self.fold_at_level(&actions::FoldAtLevel(2), window, cx);
20261    }
20262
20263    pub fn fold_at_level_3(
20264        &mut self,
20265        _: &actions::FoldAtLevel3,
20266        window: &mut Window,
20267        cx: &mut Context<Self>,
20268    ) {
20269        self.fold_at_level(&actions::FoldAtLevel(3), window, cx);
20270    }
20271
20272    pub fn fold_at_level_4(
20273        &mut self,
20274        _: &actions::FoldAtLevel4,
20275        window: &mut Window,
20276        cx: &mut Context<Self>,
20277    ) {
20278        self.fold_at_level(&actions::FoldAtLevel(4), window, cx);
20279    }
20280
20281    pub fn fold_at_level_5(
20282        &mut self,
20283        _: &actions::FoldAtLevel5,
20284        window: &mut Window,
20285        cx: &mut Context<Self>,
20286    ) {
20287        self.fold_at_level(&actions::FoldAtLevel(5), window, cx);
20288    }
20289
20290    pub fn fold_at_level_6(
20291        &mut self,
20292        _: &actions::FoldAtLevel6,
20293        window: &mut Window,
20294        cx: &mut Context<Self>,
20295    ) {
20296        self.fold_at_level(&actions::FoldAtLevel(6), window, cx);
20297    }
20298
20299    pub fn fold_at_level_7(
20300        &mut self,
20301        _: &actions::FoldAtLevel7,
20302        window: &mut Window,
20303        cx: &mut Context<Self>,
20304    ) {
20305        self.fold_at_level(&actions::FoldAtLevel(7), window, cx);
20306    }
20307
20308    pub fn fold_at_level_8(
20309        &mut self,
20310        _: &actions::FoldAtLevel8,
20311        window: &mut Window,
20312        cx: &mut Context<Self>,
20313    ) {
20314        self.fold_at_level(&actions::FoldAtLevel(8), window, cx);
20315    }
20316
20317    pub fn fold_at_level_9(
20318        &mut self,
20319        _: &actions::FoldAtLevel9,
20320        window: &mut Window,
20321        cx: &mut Context<Self>,
20322    ) {
20323        self.fold_at_level(&actions::FoldAtLevel(9), window, cx);
20324    }
20325
20326    pub fn fold_all(&mut self, _: &actions::FoldAll, window: &mut Window, cx: &mut Context<Self>) {
20327        if self.buffer.read(cx).is_singleton() {
20328            let mut fold_ranges = Vec::new();
20329            let snapshot = self.buffer.read(cx).snapshot(cx);
20330
20331            for row in 0..snapshot.max_row().0 {
20332                if let Some(foldable_range) = self
20333                    .snapshot(window, cx)
20334                    .crease_for_buffer_row(MultiBufferRow(row))
20335                {
20336                    fold_ranges.push(foldable_range);
20337                }
20338            }
20339
20340            self.fold_creases(fold_ranges, true, window, cx);
20341        } else {
20342            self.toggle_fold_multiple_buffers = cx.spawn_in(window, async move |editor, cx| {
20343                editor
20344                    .update_in(cx, |editor, _, cx| {
20345                        for buffer_id in editor.buffer.read(cx).excerpt_buffer_ids() {
20346                            editor.fold_buffer(buffer_id, cx);
20347                        }
20348                    })
20349                    .ok();
20350            });
20351        }
20352    }
20353
20354    pub fn fold_function_bodies(
20355        &mut self,
20356        _: &actions::FoldFunctionBodies,
20357        window: &mut Window,
20358        cx: &mut Context<Self>,
20359    ) {
20360        let snapshot = self.buffer.read(cx).snapshot(cx);
20361
20362        let ranges = snapshot
20363            .text_object_ranges(
20364                MultiBufferOffset(0)..snapshot.len(),
20365                TreeSitterOptions::default(),
20366            )
20367            .filter_map(|(range, obj)| (obj == TextObject::InsideFunction).then_some(range))
20368            .collect::<Vec<_>>();
20369
20370        let creases = ranges
20371            .into_iter()
20372            .map(|range| Crease::simple(range, self.display_map.read(cx).fold_placeholder.clone()))
20373            .collect();
20374
20375        self.fold_creases(creases, true, window, cx);
20376    }
20377
20378    pub fn fold_recursive(
20379        &mut self,
20380        _: &actions::FoldRecursive,
20381        window: &mut Window,
20382        cx: &mut Context<Self>,
20383    ) {
20384        let mut to_fold = Vec::new();
20385        let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
20386        let selections = self.selections.all_adjusted(&display_map);
20387
20388        for selection in selections {
20389            let range = selection.range().sorted();
20390            let buffer_start_row = range.start.row;
20391
20392            if range.start.row != range.end.row {
20393                let mut found = false;
20394                for row in range.start.row..=range.end.row {
20395                    if let Some(crease) = display_map.crease_for_buffer_row(MultiBufferRow(row)) {
20396                        found = true;
20397                        to_fold.push(crease);
20398                    }
20399                }
20400                if found {
20401                    continue;
20402                }
20403            }
20404
20405            for row in (0..=range.start.row).rev() {
20406                if let Some(crease) = display_map.crease_for_buffer_row(MultiBufferRow(row)) {
20407                    if crease.range().end.row >= buffer_start_row {
20408                        to_fold.push(crease);
20409                    } else {
20410                        break;
20411                    }
20412                }
20413            }
20414        }
20415
20416        self.fold_creases(to_fold, true, window, cx);
20417    }
20418
20419    pub fn fold_at(
20420        &mut self,
20421        buffer_row: MultiBufferRow,
20422        window: &mut Window,
20423        cx: &mut Context<Self>,
20424    ) {
20425        let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
20426
20427        if let Some(crease) = display_map.crease_for_buffer_row(buffer_row) {
20428            let autoscroll = self
20429                .selections
20430                .all::<Point>(&display_map)
20431                .iter()
20432                .any(|selection| crease.range().overlaps(&selection.range()));
20433
20434            self.fold_creases(vec![crease], autoscroll, window, cx);
20435        }
20436    }
20437
20438    pub fn unfold_lines(&mut self, _: &UnfoldLines, _window: &mut Window, cx: &mut Context<Self>) {
20439        if self.buffer_kind(cx) == ItemBufferKind::Singleton {
20440            let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
20441            let buffer = display_map.buffer_snapshot();
20442            let selections = self.selections.all::<Point>(&display_map);
20443            let ranges = selections
20444                .iter()
20445                .map(|s| {
20446                    let range = s.display_range(&display_map).sorted();
20447                    let mut start = range.start.to_point(&display_map);
20448                    let mut end = range.end.to_point(&display_map);
20449                    start.column = 0;
20450                    end.column = buffer.line_len(MultiBufferRow(end.row));
20451                    start..end
20452                })
20453                .collect::<Vec<_>>();
20454
20455            self.unfold_ranges(&ranges, true, true, cx);
20456        } else {
20457            let multi_buffer_snapshot = self.buffer.read(cx).snapshot(cx);
20458            let buffer_ids = self
20459                .selections
20460                .disjoint_anchor_ranges()
20461                .flat_map(|range| multi_buffer_snapshot.buffer_ids_for_range(range))
20462                .collect::<HashSet<_>>();
20463            for buffer_id in buffer_ids {
20464                self.unfold_buffer(buffer_id, cx);
20465            }
20466        }
20467    }
20468
20469    pub fn unfold_recursive(
20470        &mut self,
20471        _: &UnfoldRecursive,
20472        _window: &mut Window,
20473        cx: &mut Context<Self>,
20474    ) {
20475        let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
20476        let selections = self.selections.all::<Point>(&display_map);
20477        let ranges = selections
20478            .iter()
20479            .map(|s| {
20480                let mut range = s.display_range(&display_map).sorted();
20481                *range.start.column_mut() = 0;
20482                *range.end.column_mut() = display_map.line_len(range.end.row());
20483                let start = range.start.to_point(&display_map);
20484                let end = range.end.to_point(&display_map);
20485                start..end
20486            })
20487            .collect::<Vec<_>>();
20488
20489        self.unfold_ranges(&ranges, true, true, cx);
20490    }
20491
20492    pub fn unfold_at(
20493        &mut self,
20494        buffer_row: MultiBufferRow,
20495        _window: &mut Window,
20496        cx: &mut Context<Self>,
20497    ) {
20498        let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
20499
20500        let intersection_range = Point::new(buffer_row.0, 0)
20501            ..Point::new(
20502                buffer_row.0,
20503                display_map.buffer_snapshot().line_len(buffer_row),
20504            );
20505
20506        let autoscroll = self
20507            .selections
20508            .all::<Point>(&display_map)
20509            .iter()
20510            .any(|selection| RangeExt::overlaps(&selection.range(), &intersection_range));
20511
20512        self.unfold_ranges(&[intersection_range], true, autoscroll, cx);
20513    }
20514
20515    pub fn unfold_all(
20516        &mut self,
20517        _: &actions::UnfoldAll,
20518        _window: &mut Window,
20519        cx: &mut Context<Self>,
20520    ) {
20521        if self.buffer.read(cx).is_singleton() {
20522            let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
20523            self.unfold_ranges(
20524                &[MultiBufferOffset(0)..display_map.buffer_snapshot().len()],
20525                true,
20526                true,
20527                cx,
20528            );
20529        } else {
20530            self.toggle_fold_multiple_buffers = cx.spawn(async move |editor, cx| {
20531                editor
20532                    .update(cx, |editor, cx| {
20533                        for buffer_id in editor.buffer.read(cx).excerpt_buffer_ids() {
20534                            editor.unfold_buffer(buffer_id, cx);
20535                        }
20536                    })
20537                    .ok();
20538            });
20539        }
20540    }
20541
20542    pub fn fold_selected_ranges(
20543        &mut self,
20544        _: &FoldSelectedRanges,
20545        window: &mut Window,
20546        cx: &mut Context<Self>,
20547    ) {
20548        let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
20549        let selections = self.selections.all_adjusted(&display_map);
20550        let ranges = selections
20551            .into_iter()
20552            .map(|s| Crease::simple(s.range(), display_map.fold_placeholder.clone()))
20553            .collect::<Vec<_>>();
20554        self.fold_creases(ranges, true, window, cx);
20555    }
20556
20557    pub fn fold_ranges<T: ToOffset + Clone>(
20558        &mut self,
20559        ranges: Vec<Range<T>>,
20560        auto_scroll: bool,
20561        window: &mut Window,
20562        cx: &mut Context<Self>,
20563    ) {
20564        let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
20565        let ranges = ranges
20566            .into_iter()
20567            .map(|r| Crease::simple(r, display_map.fold_placeholder.clone()))
20568            .collect::<Vec<_>>();
20569        self.fold_creases(ranges, auto_scroll, window, cx);
20570    }
20571
20572    pub fn fold_creases<T: ToOffset + Clone>(
20573        &mut self,
20574        creases: Vec<Crease<T>>,
20575        auto_scroll: bool,
20576        window: &mut Window,
20577        cx: &mut Context<Self>,
20578    ) {
20579        if creases.is_empty() {
20580            return;
20581        }
20582
20583        self.display_map.update(cx, |map, cx| map.fold(creases, cx));
20584
20585        if auto_scroll {
20586            self.request_autoscroll(Autoscroll::fit(), cx);
20587        }
20588
20589        cx.notify();
20590
20591        self.scrollbar_marker_state.dirty = true;
20592        self.update_data_on_scroll(window, cx);
20593        self.folds_did_change(cx);
20594    }
20595
20596    /// Removes any folds whose ranges intersect any of the given ranges.
20597    pub fn unfold_ranges<T: ToOffset + Clone>(
20598        &mut self,
20599        ranges: &[Range<T>],
20600        inclusive: bool,
20601        auto_scroll: bool,
20602        cx: &mut Context<Self>,
20603    ) {
20604        self.remove_folds_with(ranges, auto_scroll, cx, |map, cx| {
20605            map.unfold_intersecting(ranges.iter().cloned(), inclusive, cx);
20606        });
20607        self.folds_did_change(cx);
20608    }
20609
20610    pub fn fold_buffer(&mut self, buffer_id: BufferId, cx: &mut Context<Self>) {
20611        self.fold_buffers([buffer_id], cx);
20612    }
20613
20614    pub fn fold_buffers(
20615        &mut self,
20616        buffer_ids: impl IntoIterator<Item = BufferId>,
20617        cx: &mut Context<Self>,
20618    ) {
20619        if self.buffer().read(cx).is_singleton() {
20620            return;
20621        }
20622
20623        let ids_to_fold: Vec<BufferId> = buffer_ids
20624            .into_iter()
20625            .filter(|id| !self.is_buffer_folded(*id, cx))
20626            .collect();
20627
20628        if ids_to_fold.is_empty() {
20629            return;
20630        }
20631
20632        let mut all_folded_excerpt_ids = Vec::new();
20633        for buffer_id in &ids_to_fold {
20634            let folded_excerpts = self.buffer().read(cx).excerpts_for_buffer(*buffer_id, cx);
20635            all_folded_excerpt_ids.extend(folded_excerpts.into_iter().map(|(id, _, _)| id));
20636        }
20637
20638        self.display_map.update(cx, |display_map, cx| {
20639            display_map.fold_buffers(ids_to_fold.clone(), cx)
20640        });
20641
20642        let snapshot = self.display_snapshot(cx);
20643        self.selections.change_with(&snapshot, |selections| {
20644            for buffer_id in ids_to_fold {
20645                selections.remove_selections_from_buffer(buffer_id);
20646            }
20647        });
20648
20649        cx.emit(EditorEvent::BufferFoldToggled {
20650            ids: all_folded_excerpt_ids,
20651            folded: true,
20652        });
20653        cx.notify();
20654    }
20655
20656    pub fn unfold_buffer(&mut self, buffer_id: BufferId, cx: &mut Context<Self>) {
20657        if self.buffer().read(cx).is_singleton() || !self.is_buffer_folded(buffer_id, cx) {
20658            return;
20659        }
20660        let unfolded_excerpts = self.buffer().read(cx).excerpts_for_buffer(buffer_id, cx);
20661        self.display_map.update(cx, |display_map, cx| {
20662            display_map.unfold_buffers([buffer_id], cx);
20663        });
20664        cx.emit(EditorEvent::BufferFoldToggled {
20665            ids: unfolded_excerpts.iter().map(|&(id, _, _)| id).collect(),
20666            folded: false,
20667        });
20668        cx.notify();
20669    }
20670
20671    pub fn is_buffer_folded(&self, buffer: BufferId, cx: &App) -> bool {
20672        self.display_map.read(cx).is_buffer_folded(buffer)
20673    }
20674
20675    pub fn has_any_buffer_folded(&self, cx: &App) -> bool {
20676        if self.buffer().read(cx).is_singleton() {
20677            return false;
20678        }
20679        !self.folded_buffers(cx).is_empty()
20680    }
20681
20682    pub fn folded_buffers<'a>(&self, cx: &'a App) -> &'a HashSet<BufferId> {
20683        self.display_map.read(cx).folded_buffers()
20684    }
20685
20686    pub fn disable_header_for_buffer(&mut self, buffer_id: BufferId, cx: &mut Context<Self>) {
20687        self.display_map.update(cx, |display_map, cx| {
20688            display_map.disable_header_for_buffer(buffer_id, cx);
20689        });
20690        cx.notify();
20691    }
20692
20693    /// Removes any folds with the given ranges.
20694    pub fn remove_folds_with_type<T: ToOffset + Clone>(
20695        &mut self,
20696        ranges: &[Range<T>],
20697        type_id: TypeId,
20698        auto_scroll: bool,
20699        cx: &mut Context<Self>,
20700    ) {
20701        self.remove_folds_with(ranges, auto_scroll, cx, |map, cx| {
20702            map.remove_folds_with_type(ranges.iter().cloned(), type_id, cx)
20703        });
20704        self.folds_did_change(cx);
20705    }
20706
20707    fn remove_folds_with<T: ToOffset + Clone>(
20708        &mut self,
20709        ranges: &[Range<T>],
20710        auto_scroll: bool,
20711        cx: &mut Context<Self>,
20712        update: impl FnOnce(&mut DisplayMap, &mut Context<DisplayMap>),
20713    ) {
20714        if ranges.is_empty() {
20715            return;
20716        }
20717
20718        let mut buffers_affected = HashSet::default();
20719        let multi_buffer = self.buffer().read(cx);
20720        for range in ranges {
20721            if let Some((_, buffer, _)) = multi_buffer.excerpt_containing(range.start.clone(), cx) {
20722                buffers_affected.insert(buffer.read(cx).remote_id());
20723            };
20724        }
20725
20726        self.display_map.update(cx, update);
20727
20728        if auto_scroll {
20729            self.request_autoscroll(Autoscroll::fit(), cx);
20730        }
20731
20732        cx.notify();
20733        self.scrollbar_marker_state.dirty = true;
20734        self.active_indent_guides_state.dirty = true;
20735    }
20736
20737    pub fn update_renderer_widths(
20738        &mut self,
20739        widths: impl IntoIterator<Item = (ChunkRendererId, Pixels)>,
20740        cx: &mut Context<Self>,
20741    ) -> bool {
20742        self.display_map
20743            .update(cx, |map, cx| map.update_fold_widths(widths, cx))
20744    }
20745
20746    pub fn default_fold_placeholder(&self, cx: &App) -> FoldPlaceholder {
20747        self.display_map.read(cx).fold_placeholder.clone()
20748    }
20749
20750    pub fn set_expand_all_diff_hunks(&mut self, cx: &mut App) {
20751        self.buffer.update(cx, |buffer, cx| {
20752            buffer.set_all_diff_hunks_expanded(cx);
20753        });
20754    }
20755
20756    pub fn expand_all_diff_hunks(
20757        &mut self,
20758        _: &ExpandAllDiffHunks,
20759        _window: &mut Window,
20760        cx: &mut Context<Self>,
20761    ) {
20762        self.buffer.update(cx, |buffer, cx| {
20763            buffer.expand_diff_hunks(vec![Anchor::min()..Anchor::max()], cx)
20764        });
20765    }
20766
20767    pub fn collapse_all_diff_hunks(
20768        &mut self,
20769        _: &CollapseAllDiffHunks,
20770        _window: &mut Window,
20771        cx: &mut Context<Self>,
20772    ) {
20773        self.buffer.update(cx, |buffer, cx| {
20774            buffer.collapse_diff_hunks(vec![Anchor::min()..Anchor::max()], cx)
20775        });
20776    }
20777
20778    pub fn toggle_selected_diff_hunks(
20779        &mut self,
20780        _: &ToggleSelectedDiffHunks,
20781        _window: &mut Window,
20782        cx: &mut Context<Self>,
20783    ) {
20784        let ranges: Vec<_> = self
20785            .selections
20786            .disjoint_anchors()
20787            .iter()
20788            .map(|s| s.range())
20789            .collect();
20790        self.toggle_diff_hunks_in_ranges(ranges, cx);
20791    }
20792
20793    pub fn diff_hunks_in_ranges<'a>(
20794        &'a self,
20795        ranges: &'a [Range<Anchor>],
20796        buffer: &'a MultiBufferSnapshot,
20797    ) -> impl 'a + Iterator<Item = MultiBufferDiffHunk> {
20798        ranges.iter().flat_map(move |range| {
20799            let end_excerpt_id = range.end.excerpt_id;
20800            let range = range.to_point(buffer);
20801            let mut peek_end = range.end;
20802            if range.end.row < buffer.max_row().0 {
20803                peek_end = Point::new(range.end.row + 1, 0);
20804            }
20805            buffer
20806                .diff_hunks_in_range(range.start..peek_end)
20807                .filter(move |hunk| hunk.excerpt_id.cmp(&end_excerpt_id, buffer).is_le())
20808        })
20809    }
20810
20811    pub fn has_stageable_diff_hunks_in_ranges(
20812        &self,
20813        ranges: &[Range<Anchor>],
20814        snapshot: &MultiBufferSnapshot,
20815    ) -> bool {
20816        let mut hunks = self.diff_hunks_in_ranges(ranges, snapshot);
20817        hunks.any(|hunk| hunk.status().has_secondary_hunk())
20818    }
20819
20820    pub fn toggle_staged_selected_diff_hunks(
20821        &mut self,
20822        _: &::git::ToggleStaged,
20823        _: &mut Window,
20824        cx: &mut Context<Self>,
20825    ) {
20826        let snapshot = self.buffer.read(cx).snapshot(cx);
20827        let ranges: Vec<_> = self
20828            .selections
20829            .disjoint_anchors()
20830            .iter()
20831            .map(|s| s.range())
20832            .collect();
20833        let stage = self.has_stageable_diff_hunks_in_ranges(&ranges, &snapshot);
20834        self.stage_or_unstage_diff_hunks(stage, ranges, cx);
20835    }
20836
20837    pub fn set_render_diff_hunk_controls(
20838        &mut self,
20839        render_diff_hunk_controls: RenderDiffHunkControlsFn,
20840        cx: &mut Context<Self>,
20841    ) {
20842        self.render_diff_hunk_controls = render_diff_hunk_controls;
20843        cx.notify();
20844    }
20845
20846    pub fn stage_and_next(
20847        &mut self,
20848        _: &::git::StageAndNext,
20849        window: &mut Window,
20850        cx: &mut Context<Self>,
20851    ) {
20852        self.do_stage_or_unstage_and_next(true, window, cx);
20853    }
20854
20855    pub fn unstage_and_next(
20856        &mut self,
20857        _: &::git::UnstageAndNext,
20858        window: &mut Window,
20859        cx: &mut Context<Self>,
20860    ) {
20861        self.do_stage_or_unstage_and_next(false, window, cx);
20862    }
20863
20864    pub fn stage_or_unstage_diff_hunks(
20865        &mut self,
20866        stage: bool,
20867        ranges: Vec<Range<Anchor>>,
20868        cx: &mut Context<Self>,
20869    ) {
20870        if self.delegate_stage_and_restore {
20871            let snapshot = self.buffer.read(cx).snapshot(cx);
20872            let hunks: Vec<_> = self.diff_hunks_in_ranges(&ranges, &snapshot).collect();
20873            if !hunks.is_empty() {
20874                cx.emit(EditorEvent::StageOrUnstageRequested { stage, hunks });
20875            }
20876            return;
20877        }
20878        let task = self.save_buffers_for_ranges_if_needed(&ranges, cx);
20879        cx.spawn(async move |this, cx| {
20880            task.await?;
20881            this.update(cx, |this, cx| {
20882                let snapshot = this.buffer.read(cx).snapshot(cx);
20883                let chunk_by = this
20884                    .diff_hunks_in_ranges(&ranges, &snapshot)
20885                    .chunk_by(|hunk| hunk.buffer_id);
20886                for (buffer_id, hunks) in &chunk_by {
20887                    this.do_stage_or_unstage(stage, buffer_id, hunks, cx);
20888                }
20889            })
20890        })
20891        .detach_and_log_err(cx);
20892    }
20893
20894    fn save_buffers_for_ranges_if_needed(
20895        &mut self,
20896        ranges: &[Range<Anchor>],
20897        cx: &mut Context<Editor>,
20898    ) -> Task<Result<()>> {
20899        let multibuffer = self.buffer.read(cx);
20900        let snapshot = multibuffer.read(cx);
20901        let buffer_ids: HashSet<_> = ranges
20902            .iter()
20903            .flat_map(|range| snapshot.buffer_ids_for_range(range.clone()))
20904            .collect();
20905        drop(snapshot);
20906
20907        let mut buffers = HashSet::default();
20908        for buffer_id in buffer_ids {
20909            if let Some(buffer_entity) = multibuffer.buffer(buffer_id) {
20910                let buffer = buffer_entity.read(cx);
20911                if buffer.file().is_some_and(|file| file.disk_state().exists()) && buffer.is_dirty()
20912                {
20913                    buffers.insert(buffer_entity);
20914                }
20915            }
20916        }
20917
20918        if let Some(project) = &self.project {
20919            project.update(cx, |project, cx| project.save_buffers(buffers, cx))
20920        } else {
20921            Task::ready(Ok(()))
20922        }
20923    }
20924
20925    fn do_stage_or_unstage_and_next(
20926        &mut self,
20927        stage: bool,
20928        window: &mut Window,
20929        cx: &mut Context<Self>,
20930    ) {
20931        let ranges = self.selections.disjoint_anchor_ranges().collect::<Vec<_>>();
20932
20933        if ranges.iter().any(|range| range.start != range.end) {
20934            self.stage_or_unstage_diff_hunks(stage, ranges, cx);
20935            return;
20936        }
20937
20938        self.stage_or_unstage_diff_hunks(stage, ranges, cx);
20939
20940        let all_diff_hunks_expanded = self.buffer().read(cx).all_diff_hunks_expanded();
20941        let wrap_around = !all_diff_hunks_expanded;
20942        let snapshot = self.snapshot(window, cx);
20943        let position = self
20944            .selections
20945            .newest::<Point>(&snapshot.display_snapshot)
20946            .head();
20947
20948        self.go_to_hunk_before_or_after_position(
20949            &snapshot,
20950            position,
20951            Direction::Next,
20952            wrap_around,
20953            window,
20954            cx,
20955        );
20956    }
20957
20958    pub(crate) fn do_stage_or_unstage(
20959        &self,
20960        stage: bool,
20961        buffer_id: BufferId,
20962        hunks: impl Iterator<Item = MultiBufferDiffHunk>,
20963        cx: &mut App,
20964    ) -> Option<()> {
20965        let project = self.project()?;
20966        let buffer = project.read(cx).buffer_for_id(buffer_id, cx)?;
20967        let diff = self.buffer.read(cx).diff_for(buffer_id)?;
20968        let buffer_snapshot = buffer.read(cx).snapshot();
20969        let file_exists = buffer_snapshot
20970            .file()
20971            .is_some_and(|file| file.disk_state().exists());
20972        diff.update(cx, |diff, cx| {
20973            diff.stage_or_unstage_hunks(
20974                stage,
20975                &hunks
20976                    .map(|hunk| buffer_diff::DiffHunk {
20977                        buffer_range: hunk.buffer_range,
20978                        // We don't need to pass in word diffs here because they're only used for rendering and
20979                        // this function changes internal state
20980                        base_word_diffs: Vec::default(),
20981                        buffer_word_diffs: Vec::default(),
20982                        diff_base_byte_range: hunk.diff_base_byte_range.start.0
20983                            ..hunk.diff_base_byte_range.end.0,
20984                        secondary_status: hunk.status.secondary,
20985                        range: Point::zero()..Point::zero(), // unused
20986                    })
20987                    .collect::<Vec<_>>(),
20988                &buffer_snapshot,
20989                file_exists,
20990                cx,
20991            )
20992        });
20993        None
20994    }
20995
20996    pub fn expand_selected_diff_hunks(&mut self, cx: &mut Context<Self>) {
20997        let ranges: Vec<_> = self
20998            .selections
20999            .disjoint_anchors()
21000            .iter()
21001            .map(|s| s.range())
21002            .collect();
21003        self.buffer
21004            .update(cx, |buffer, cx| buffer.expand_diff_hunks(ranges, cx))
21005    }
21006
21007    pub fn clear_expanded_diff_hunks(&mut self, cx: &mut Context<Self>) -> bool {
21008        self.buffer.update(cx, |buffer, cx| {
21009            let ranges = vec![Anchor::min()..Anchor::max()];
21010            if !buffer.all_diff_hunks_expanded()
21011                && buffer.has_expanded_diff_hunks_in_ranges(&ranges, cx)
21012            {
21013                buffer.collapse_diff_hunks(ranges, cx);
21014                true
21015            } else {
21016                false
21017            }
21018        })
21019    }
21020
21021    fn has_any_expanded_diff_hunks(&self, cx: &App) -> bool {
21022        if self.buffer.read(cx).all_diff_hunks_expanded() {
21023            return true;
21024        }
21025        let ranges = vec![Anchor::min()..Anchor::max()];
21026        self.buffer
21027            .read(cx)
21028            .has_expanded_diff_hunks_in_ranges(&ranges, cx)
21029    }
21030
21031    fn toggle_diff_hunks_in_ranges(
21032        &mut self,
21033        ranges: Vec<Range<Anchor>>,
21034        cx: &mut Context<Editor>,
21035    ) {
21036        self.buffer.update(cx, |buffer, cx| {
21037            let expand = !buffer.has_expanded_diff_hunks_in_ranges(&ranges, cx);
21038            buffer.expand_or_collapse_diff_hunks(ranges, expand, cx);
21039        })
21040    }
21041
21042    fn toggle_single_diff_hunk(&mut self, range: Range<Anchor>, cx: &mut Context<Self>) {
21043        self.buffer.update(cx, |buffer, cx| {
21044            buffer.toggle_single_diff_hunk(range, cx);
21045        })
21046    }
21047
21048    pub(crate) fn apply_all_diff_hunks(
21049        &mut self,
21050        _: &ApplyAllDiffHunks,
21051        window: &mut Window,
21052        cx: &mut Context<Self>,
21053    ) {
21054        self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
21055
21056        let buffers = self.buffer.read(cx).all_buffers();
21057        for branch_buffer in buffers {
21058            branch_buffer.update(cx, |branch_buffer, cx| {
21059                branch_buffer.merge_into_base(Vec::new(), cx);
21060            });
21061        }
21062
21063        if let Some(project) = self.project.clone() {
21064            self.save(
21065                SaveOptions {
21066                    format: true,
21067                    autosave: false,
21068                },
21069                project,
21070                window,
21071                cx,
21072            )
21073            .detach_and_log_err(cx);
21074        }
21075    }
21076
21077    pub(crate) fn apply_selected_diff_hunks(
21078        &mut self,
21079        _: &ApplyDiffHunk,
21080        window: &mut Window,
21081        cx: &mut Context<Self>,
21082    ) {
21083        self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
21084        let snapshot = self.snapshot(window, cx);
21085        let hunks = snapshot.hunks_for_ranges(
21086            self.selections
21087                .all(&snapshot.display_snapshot)
21088                .into_iter()
21089                .map(|selection| selection.range()),
21090        );
21091        let mut ranges_by_buffer = HashMap::default();
21092        self.transact(window, cx, |editor, _window, cx| {
21093            for hunk in hunks {
21094                if let Some(buffer) = editor.buffer.read(cx).buffer(hunk.buffer_id) {
21095                    ranges_by_buffer
21096                        .entry(buffer.clone())
21097                        .or_insert_with(Vec::new)
21098                        .push(hunk.buffer_range.to_offset(buffer.read(cx)));
21099                }
21100            }
21101
21102            for (buffer, ranges) in ranges_by_buffer {
21103                buffer.update(cx, |buffer, cx| {
21104                    buffer.merge_into_base(ranges, cx);
21105                });
21106            }
21107        });
21108
21109        if let Some(project) = self.project.clone() {
21110            self.save(
21111                SaveOptions {
21112                    format: true,
21113                    autosave: false,
21114                },
21115                project,
21116                window,
21117                cx,
21118            )
21119            .detach_and_log_err(cx);
21120        }
21121    }
21122
21123    pub fn set_gutter_hovered(&mut self, hovered: bool, cx: &mut Context<Self>) {
21124        if hovered != self.gutter_hovered {
21125            self.gutter_hovered = hovered;
21126            cx.notify();
21127        }
21128    }
21129
21130    pub fn insert_blocks(
21131        &mut self,
21132        blocks: impl IntoIterator<Item = BlockProperties<Anchor>>,
21133        autoscroll: Option<Autoscroll>,
21134        cx: &mut Context<Self>,
21135    ) -> Vec<CustomBlockId> {
21136        let blocks = self
21137            .display_map
21138            .update(cx, |display_map, cx| display_map.insert_blocks(blocks, cx));
21139        if let Some(autoscroll) = autoscroll {
21140            self.request_autoscroll(autoscroll, cx);
21141        }
21142        cx.notify();
21143        blocks
21144    }
21145
21146    pub fn resize_blocks(
21147        &mut self,
21148        heights: HashMap<CustomBlockId, u32>,
21149        autoscroll: Option<Autoscroll>,
21150        cx: &mut Context<Self>,
21151    ) {
21152        self.display_map
21153            .update(cx, |display_map, cx| display_map.resize_blocks(heights, cx));
21154        if let Some(autoscroll) = autoscroll {
21155            self.request_autoscroll(autoscroll, cx);
21156        }
21157        cx.notify();
21158    }
21159
21160    pub fn replace_blocks(
21161        &mut self,
21162        renderers: HashMap<CustomBlockId, RenderBlock>,
21163        autoscroll: Option<Autoscroll>,
21164        cx: &mut Context<Self>,
21165    ) {
21166        self.display_map
21167            .update(cx, |display_map, _cx| display_map.replace_blocks(renderers));
21168        if let Some(autoscroll) = autoscroll {
21169            self.request_autoscroll(autoscroll, cx);
21170        }
21171        cx.notify();
21172    }
21173
21174    pub fn remove_blocks(
21175        &mut self,
21176        block_ids: HashSet<CustomBlockId>,
21177        autoscroll: Option<Autoscroll>,
21178        cx: &mut Context<Self>,
21179    ) {
21180        self.display_map.update(cx, |display_map, cx| {
21181            display_map.remove_blocks(block_ids, cx)
21182        });
21183        if let Some(autoscroll) = autoscroll {
21184            self.request_autoscroll(autoscroll, cx);
21185        }
21186        cx.notify();
21187    }
21188
21189    pub fn row_for_block(
21190        &self,
21191        block_id: CustomBlockId,
21192        cx: &mut Context<Self>,
21193    ) -> Option<DisplayRow> {
21194        self.display_map
21195            .update(cx, |map, cx| map.row_for_block(block_id, cx))
21196    }
21197
21198    pub(crate) fn set_focused_block(&mut self, focused_block: FocusedBlock) {
21199        self.focused_block = Some(focused_block);
21200    }
21201
21202    pub(crate) fn take_focused_block(&mut self) -> Option<FocusedBlock> {
21203        self.focused_block.take()
21204    }
21205
21206    pub fn insert_creases(
21207        &mut self,
21208        creases: impl IntoIterator<Item = Crease<Anchor>>,
21209        cx: &mut Context<Self>,
21210    ) -> Vec<CreaseId> {
21211        self.display_map
21212            .update(cx, |map, cx| map.insert_creases(creases, cx))
21213    }
21214
21215    pub fn remove_creases(
21216        &mut self,
21217        ids: impl IntoIterator<Item = CreaseId>,
21218        cx: &mut Context<Self>,
21219    ) -> Vec<(CreaseId, Range<Anchor>)> {
21220        self.display_map
21221            .update(cx, |map, cx| map.remove_creases(ids, cx))
21222    }
21223
21224    pub fn longest_row(&self, cx: &mut App) -> DisplayRow {
21225        self.display_map
21226            .update(cx, |map, cx| map.snapshot(cx))
21227            .longest_row()
21228    }
21229
21230    pub fn max_point(&self, cx: &mut App) -> DisplayPoint {
21231        self.display_map
21232            .update(cx, |map, cx| map.snapshot(cx))
21233            .max_point()
21234    }
21235
21236    pub fn text(&self, cx: &App) -> String {
21237        self.buffer.read(cx).read(cx).text()
21238    }
21239
21240    pub fn is_empty(&self, cx: &App) -> bool {
21241        self.buffer.read(cx).read(cx).is_empty()
21242    }
21243
21244    pub fn text_option(&self, cx: &App) -> Option<String> {
21245        let text = self.text(cx);
21246        let text = text.trim();
21247
21248        if text.is_empty() {
21249            return None;
21250        }
21251
21252        Some(text.to_string())
21253    }
21254
21255    pub fn set_text(
21256        &mut self,
21257        text: impl Into<Arc<str>>,
21258        window: &mut Window,
21259        cx: &mut Context<Self>,
21260    ) {
21261        self.transact(window, cx, |this, _, cx| {
21262            this.buffer
21263                .read(cx)
21264                .as_singleton()
21265                .expect("you can only call set_text on editors for singleton buffers")
21266                .update(cx, |buffer, cx| buffer.set_text(text, cx));
21267        });
21268    }
21269
21270    pub fn display_text(&self, cx: &mut App) -> String {
21271        self.display_map
21272            .update(cx, |map, cx| map.snapshot(cx))
21273            .text()
21274    }
21275
21276    fn create_minimap(
21277        &self,
21278        minimap_settings: MinimapSettings,
21279        window: &mut Window,
21280        cx: &mut Context<Self>,
21281    ) -> Option<Entity<Self>> {
21282        (minimap_settings.minimap_enabled() && self.buffer_kind(cx) == ItemBufferKind::Singleton)
21283            .then(|| self.initialize_new_minimap(minimap_settings, window, cx))
21284    }
21285
21286    fn initialize_new_minimap(
21287        &self,
21288        minimap_settings: MinimapSettings,
21289        window: &mut Window,
21290        cx: &mut Context<Self>,
21291    ) -> Entity<Self> {
21292        const MINIMAP_FONT_WEIGHT: gpui::FontWeight = gpui::FontWeight::BLACK;
21293        const MINIMAP_FONT_FAMILY: SharedString = SharedString::new_static(".ZedMono");
21294
21295        let mut minimap = Editor::new_internal(
21296            EditorMode::Minimap {
21297                parent: cx.weak_entity(),
21298            },
21299            self.buffer.clone(),
21300            None,
21301            Some(self.display_map.clone()),
21302            window,
21303            cx,
21304        );
21305        let my_snapshot = self.display_map.update(cx, |map, cx| map.snapshot(cx));
21306        let minimap_snapshot = minimap.display_map.update(cx, |map, cx| map.snapshot(cx));
21307        minimap.scroll_manager.clone_state(
21308            &self.scroll_manager,
21309            &my_snapshot,
21310            &minimap_snapshot,
21311            cx,
21312        );
21313        minimap.set_text_style_refinement(TextStyleRefinement {
21314            font_size: Some(MINIMAP_FONT_SIZE),
21315            font_weight: Some(MINIMAP_FONT_WEIGHT),
21316            font_family: Some(MINIMAP_FONT_FAMILY),
21317            ..Default::default()
21318        });
21319        minimap.update_minimap_configuration(minimap_settings, cx);
21320        cx.new(|_| minimap)
21321    }
21322
21323    fn update_minimap_configuration(&mut self, minimap_settings: MinimapSettings, cx: &App) {
21324        let current_line_highlight = minimap_settings
21325            .current_line_highlight
21326            .unwrap_or_else(|| EditorSettings::get_global(cx).current_line_highlight);
21327        self.set_current_line_highlight(Some(current_line_highlight));
21328    }
21329
21330    pub fn minimap(&self) -> Option<&Entity<Self>> {
21331        self.minimap
21332            .as_ref()
21333            .filter(|_| self.minimap_visibility.visible())
21334    }
21335
21336    pub fn wrap_guides(&self, cx: &App) -> SmallVec<[(usize, bool); 2]> {
21337        let mut wrap_guides = smallvec![];
21338
21339        if self.show_wrap_guides == Some(false) {
21340            return wrap_guides;
21341        }
21342
21343        let settings = self.buffer.read(cx).language_settings(cx);
21344        if settings.show_wrap_guides {
21345            match self.soft_wrap_mode(cx) {
21346                SoftWrap::Column(soft_wrap) => {
21347                    wrap_guides.push((soft_wrap as usize, true));
21348                }
21349                SoftWrap::Bounded(soft_wrap) => {
21350                    wrap_guides.push((soft_wrap as usize, true));
21351                }
21352                SoftWrap::GitDiff | SoftWrap::None | SoftWrap::EditorWidth => {}
21353            }
21354            wrap_guides.extend(settings.wrap_guides.iter().map(|guide| (*guide, false)))
21355        }
21356
21357        wrap_guides
21358    }
21359
21360    pub fn soft_wrap_mode(&self, cx: &App) -> SoftWrap {
21361        let settings = self.buffer.read(cx).language_settings(cx);
21362        let mode = self.soft_wrap_mode_override.unwrap_or(settings.soft_wrap);
21363        match mode {
21364            language_settings::SoftWrap::PreferLine | language_settings::SoftWrap::None => {
21365                SoftWrap::None
21366            }
21367            language_settings::SoftWrap::EditorWidth => SoftWrap::EditorWidth,
21368            language_settings::SoftWrap::PreferredLineLength => {
21369                SoftWrap::Column(settings.preferred_line_length)
21370            }
21371            language_settings::SoftWrap::Bounded => {
21372                SoftWrap::Bounded(settings.preferred_line_length)
21373            }
21374        }
21375    }
21376
21377    pub fn set_soft_wrap_mode(
21378        &mut self,
21379        mode: language_settings::SoftWrap,
21380        cx: &mut Context<Self>,
21381    ) {
21382        self.soft_wrap_mode_override = Some(mode);
21383        cx.notify();
21384    }
21385
21386    pub fn set_hard_wrap(&mut self, hard_wrap: Option<usize>, cx: &mut Context<Self>) {
21387        self.hard_wrap = hard_wrap;
21388        cx.notify();
21389    }
21390
21391    pub fn set_text_style_refinement(&mut self, style: TextStyleRefinement) {
21392        self.text_style_refinement = Some(style);
21393    }
21394
21395    /// called by the Element so we know what style we were most recently rendered with.
21396    pub fn set_style(&mut self, style: EditorStyle, window: &mut Window, cx: &mut Context<Self>) {
21397        // We intentionally do not inform the display map about the minimap style
21398        // so that wrapping is not recalculated and stays consistent for the editor
21399        // and its linked minimap.
21400        if !self.mode.is_minimap() {
21401            let font = style.text.font();
21402            let font_size = style.text.font_size.to_pixels(window.rem_size());
21403            let display_map = self
21404                .placeholder_display_map
21405                .as_ref()
21406                .filter(|_| self.is_empty(cx))
21407                .unwrap_or(&self.display_map);
21408
21409            display_map.update(cx, |map, cx| map.set_font(font, font_size, cx));
21410        }
21411        self.style = Some(style);
21412    }
21413
21414    pub fn style(&mut self, cx: &App) -> &EditorStyle {
21415        if self.style.is_none() {
21416            self.style = Some(self.create_style(cx));
21417        }
21418        self.style.as_ref().unwrap()
21419    }
21420
21421    // Called by the element. This method is not designed to be called outside of the editor
21422    // element's layout code because it does not notify when rewrapping is computed synchronously.
21423    pub(crate) fn set_wrap_width(&self, width: Option<Pixels>, cx: &mut App) -> bool {
21424        if self.is_empty(cx) {
21425            self.placeholder_display_map
21426                .as_ref()
21427                .map_or(false, |display_map| {
21428                    display_map.update(cx, |map, cx| map.set_wrap_width(width, cx))
21429                })
21430        } else {
21431            self.display_map
21432                .update(cx, |map, cx| map.set_wrap_width(width, cx))
21433        }
21434    }
21435
21436    pub fn set_soft_wrap(&mut self) {
21437        self.soft_wrap_mode_override = Some(language_settings::SoftWrap::EditorWidth)
21438    }
21439
21440    pub fn toggle_soft_wrap(&mut self, _: &ToggleSoftWrap, _: &mut Window, cx: &mut Context<Self>) {
21441        if self.soft_wrap_mode_override.is_some() {
21442            self.soft_wrap_mode_override.take();
21443        } else {
21444            let soft_wrap = match self.soft_wrap_mode(cx) {
21445                SoftWrap::GitDiff => return,
21446                SoftWrap::None => language_settings::SoftWrap::EditorWidth,
21447                SoftWrap::EditorWidth | SoftWrap::Column(_) | SoftWrap::Bounded(_) => {
21448                    language_settings::SoftWrap::None
21449                }
21450            };
21451            self.soft_wrap_mode_override = Some(soft_wrap);
21452        }
21453        cx.notify();
21454    }
21455
21456    pub fn toggle_tab_bar(&mut self, _: &ToggleTabBar, _: &mut Window, cx: &mut Context<Self>) {
21457        let Some(workspace) = self.workspace() else {
21458            return;
21459        };
21460        let fs = workspace.read(cx).app_state().fs.clone();
21461        let current_show = TabBarSettings::get_global(cx).show;
21462        update_settings_file(fs, cx, move |setting, _| {
21463            setting.tab_bar.get_or_insert_default().show = Some(!current_show);
21464        });
21465    }
21466
21467    pub fn toggle_indent_guides(
21468        &mut self,
21469        _: &ToggleIndentGuides,
21470        _: &mut Window,
21471        cx: &mut Context<Self>,
21472    ) {
21473        let currently_enabled = self.should_show_indent_guides().unwrap_or_else(|| {
21474            self.buffer
21475                .read(cx)
21476                .language_settings(cx)
21477                .indent_guides
21478                .enabled
21479        });
21480        self.show_indent_guides = Some(!currently_enabled);
21481        cx.notify();
21482    }
21483
21484    fn should_show_indent_guides(&self) -> Option<bool> {
21485        self.show_indent_guides
21486    }
21487
21488    pub fn disable_indent_guides_for_buffer(
21489        &mut self,
21490        buffer_id: BufferId,
21491        cx: &mut Context<Self>,
21492    ) {
21493        self.buffers_with_disabled_indent_guides.insert(buffer_id);
21494        cx.notify();
21495    }
21496
21497    pub fn has_indent_guides_disabled_for_buffer(&self, buffer_id: BufferId) -> bool {
21498        self.buffers_with_disabled_indent_guides
21499            .contains(&buffer_id)
21500    }
21501
21502    pub fn toggle_line_numbers(
21503        &mut self,
21504        _: &ToggleLineNumbers,
21505        _: &mut Window,
21506        cx: &mut Context<Self>,
21507    ) {
21508        let mut editor_settings = EditorSettings::get_global(cx).clone();
21509        editor_settings.gutter.line_numbers = !editor_settings.gutter.line_numbers;
21510        EditorSettings::override_global(editor_settings, cx);
21511    }
21512
21513    pub fn line_numbers_enabled(&self, cx: &App) -> bool {
21514        if let Some(show_line_numbers) = self.show_line_numbers {
21515            return show_line_numbers;
21516        }
21517        EditorSettings::get_global(cx).gutter.line_numbers
21518    }
21519
21520    pub fn relative_line_numbers(&self, cx: &App) -> RelativeLineNumbers {
21521        match (
21522            self.use_relative_line_numbers,
21523            EditorSettings::get_global(cx).relative_line_numbers,
21524        ) {
21525            (None, setting) => setting,
21526            (Some(false), _) => RelativeLineNumbers::Disabled,
21527            (Some(true), RelativeLineNumbers::Wrapped) => RelativeLineNumbers::Wrapped,
21528            (Some(true), _) => RelativeLineNumbers::Enabled,
21529        }
21530    }
21531
21532    pub fn toggle_relative_line_numbers(
21533        &mut self,
21534        _: &ToggleRelativeLineNumbers,
21535        _: &mut Window,
21536        cx: &mut Context<Self>,
21537    ) {
21538        let is_relative = self.relative_line_numbers(cx);
21539        self.set_relative_line_number(Some(!is_relative.enabled()), cx)
21540    }
21541
21542    pub fn set_relative_line_number(&mut self, is_relative: Option<bool>, cx: &mut Context<Self>) {
21543        self.use_relative_line_numbers = is_relative;
21544        cx.notify();
21545    }
21546
21547    pub fn set_show_gutter(&mut self, show_gutter: bool, cx: &mut Context<Self>) {
21548        self.show_gutter = show_gutter;
21549        cx.notify();
21550    }
21551
21552    pub fn set_show_scrollbars(&mut self, show: bool, cx: &mut Context<Self>) {
21553        self.show_scrollbars = ScrollbarAxes {
21554            horizontal: show,
21555            vertical: show,
21556        };
21557        cx.notify();
21558    }
21559
21560    pub fn set_show_vertical_scrollbar(&mut self, show: bool, cx: &mut Context<Self>) {
21561        self.show_scrollbars.vertical = show;
21562        cx.notify();
21563    }
21564
21565    pub fn set_show_horizontal_scrollbar(&mut self, show: bool, cx: &mut Context<Self>) {
21566        self.show_scrollbars.horizontal = show;
21567        cx.notify();
21568    }
21569
21570    pub fn set_minimap_visibility(
21571        &mut self,
21572        minimap_visibility: MinimapVisibility,
21573        window: &mut Window,
21574        cx: &mut Context<Self>,
21575    ) {
21576        if self.minimap_visibility != minimap_visibility {
21577            if minimap_visibility.visible() && self.minimap.is_none() {
21578                let minimap_settings = EditorSettings::get_global(cx).minimap;
21579                self.minimap =
21580                    self.create_minimap(minimap_settings.with_show_override(), window, cx);
21581            }
21582            self.minimap_visibility = minimap_visibility;
21583            cx.notify();
21584        }
21585    }
21586
21587    pub fn disable_scrollbars_and_minimap(&mut self, window: &mut Window, cx: &mut Context<Self>) {
21588        self.set_show_scrollbars(false, cx);
21589        self.set_minimap_visibility(MinimapVisibility::Disabled, window, cx);
21590    }
21591
21592    pub fn hide_minimap_by_default(&mut self, window: &mut Window, cx: &mut Context<Self>) {
21593        self.set_minimap_visibility(self.minimap_visibility.hidden(), window, cx);
21594    }
21595
21596    /// Normally the text in full mode and auto height editors is padded on the
21597    /// left side by roughly half a character width for improved hit testing.
21598    ///
21599    /// Use this method to disable this for cases where this is not wanted (e.g.
21600    /// if you want to align the editor text with some other text above or below)
21601    /// or if you want to add this padding to single-line editors.
21602    pub fn set_offset_content(&mut self, offset_content: bool, cx: &mut Context<Self>) {
21603        self.offset_content = offset_content;
21604        cx.notify();
21605    }
21606
21607    pub fn set_show_line_numbers(&mut self, show_line_numbers: bool, cx: &mut Context<Self>) {
21608        self.show_line_numbers = Some(show_line_numbers);
21609        cx.notify();
21610    }
21611
21612    pub fn disable_expand_excerpt_buttons(&mut self, cx: &mut Context<Self>) {
21613        self.disable_expand_excerpt_buttons = true;
21614        cx.notify();
21615    }
21616
21617    pub fn set_number_deleted_lines(&mut self, number: bool, cx: &mut Context<Self>) {
21618        self.number_deleted_lines = number;
21619        cx.notify();
21620    }
21621
21622    pub fn set_delegate_expand_excerpts(&mut self, delegate: bool) {
21623        self.delegate_expand_excerpts = delegate;
21624    }
21625
21626    pub fn set_delegate_stage_and_restore(&mut self, delegate: bool) {
21627        self.delegate_stage_and_restore = delegate;
21628    }
21629
21630    pub fn set_delegate_open_excerpts(&mut self, delegate: bool) {
21631        self.delegate_open_excerpts = delegate;
21632    }
21633
21634    pub fn set_on_local_selections_changed(
21635        &mut self,
21636        callback: Option<Box<dyn Fn(Point, &mut Window, &mut Context<Self>) + 'static>>,
21637    ) {
21638        self.on_local_selections_changed = callback;
21639    }
21640
21641    pub fn set_suppress_selection_callback(&mut self, suppress: bool) {
21642        self.suppress_selection_callback = suppress;
21643    }
21644
21645    pub fn set_show_git_diff_gutter(&mut self, show_git_diff_gutter: bool, cx: &mut Context<Self>) {
21646        self.show_git_diff_gutter = Some(show_git_diff_gutter);
21647        cx.notify();
21648    }
21649
21650    pub fn set_show_code_actions(&mut self, show_code_actions: bool, cx: &mut Context<Self>) {
21651        self.show_code_actions = Some(show_code_actions);
21652        cx.notify();
21653    }
21654
21655    pub fn set_show_runnables(&mut self, show_runnables: bool, cx: &mut Context<Self>) {
21656        self.show_runnables = Some(show_runnables);
21657        cx.notify();
21658    }
21659
21660    pub fn set_show_breakpoints(&mut self, show_breakpoints: bool, cx: &mut Context<Self>) {
21661        self.show_breakpoints = Some(show_breakpoints);
21662        cx.notify();
21663    }
21664
21665    pub fn set_show_diff_review_button(&mut self, show: bool, cx: &mut Context<Self>) {
21666        self.show_diff_review_button = show;
21667        cx.notify();
21668    }
21669
21670    pub fn show_diff_review_button(&self) -> bool {
21671        self.show_diff_review_button
21672    }
21673
21674    pub fn render_diff_review_button(
21675        &self,
21676        display_row: DisplayRow,
21677        width: Pixels,
21678        cx: &mut Context<Self>,
21679    ) -> impl IntoElement {
21680        let text_color = cx.theme().colors().text;
21681        let icon_color = cx.theme().colors().icon_accent;
21682
21683        h_flex()
21684            .id("diff_review_button")
21685            .cursor_pointer()
21686            .w(width - px(1.))
21687            .h(relative(0.9))
21688            .justify_center()
21689            .rounded_sm()
21690            .border_1()
21691            .border_color(text_color.opacity(0.1))
21692            .bg(text_color.opacity(0.15))
21693            .hover(|s| {
21694                s.bg(icon_color.opacity(0.4))
21695                    .border_color(icon_color.opacity(0.5))
21696            })
21697            .child(Icon::new(IconName::Plus).size(IconSize::Small))
21698            .tooltip(Tooltip::text("Add Review (drag to select multiple lines)"))
21699            .on_mouse_down(
21700                gpui::MouseButton::Left,
21701                cx.listener(move |editor, _event: &gpui::MouseDownEvent, window, cx| {
21702                    editor.start_diff_review_drag(display_row, window, cx);
21703                }),
21704            )
21705    }
21706
21707    pub fn start_diff_review_drag(
21708        &mut self,
21709        display_row: DisplayRow,
21710        window: &mut Window,
21711        cx: &mut Context<Self>,
21712    ) {
21713        let snapshot = self.snapshot(window, cx);
21714        let point = snapshot
21715            .display_snapshot
21716            .display_point_to_point(DisplayPoint::new(display_row, 0), Bias::Left);
21717        let anchor = snapshot.buffer_snapshot().anchor_before(point);
21718        self.diff_review_drag_state = Some(DiffReviewDragState {
21719            start_anchor: anchor,
21720            current_anchor: anchor,
21721        });
21722        cx.notify();
21723    }
21724
21725    pub fn update_diff_review_drag(
21726        &mut self,
21727        display_row: DisplayRow,
21728        window: &mut Window,
21729        cx: &mut Context<Self>,
21730    ) {
21731        if self.diff_review_drag_state.is_none() {
21732            return;
21733        }
21734        let snapshot = self.snapshot(window, cx);
21735        let point = snapshot
21736            .display_snapshot
21737            .display_point_to_point(display_row.as_display_point(), Bias::Left);
21738        let anchor = snapshot.buffer_snapshot().anchor_before(point);
21739        if let Some(drag_state) = &mut self.diff_review_drag_state {
21740            drag_state.current_anchor = anchor;
21741            cx.notify();
21742        }
21743    }
21744
21745    pub fn end_diff_review_drag(&mut self, window: &mut Window, cx: &mut Context<Self>) {
21746        if let Some(drag_state) = self.diff_review_drag_state.take() {
21747            let snapshot = self.snapshot(window, cx);
21748            let range = drag_state.row_range(&snapshot.display_snapshot);
21749            self.show_diff_review_overlay(*range.start()..*range.end(), window, cx);
21750        }
21751        cx.notify();
21752    }
21753
21754    pub fn cancel_diff_review_drag(&mut self, cx: &mut Context<Self>) {
21755        self.diff_review_drag_state = None;
21756        cx.notify();
21757    }
21758
21759    /// Calculates the appropriate block height for the diff review overlay.
21760    /// Height is in lines: 2 for input row, 1 for header when comments exist,
21761    /// and 2 lines per comment when expanded.
21762    fn calculate_overlay_height(
21763        &self,
21764        hunk_key: &DiffHunkKey,
21765        comments_expanded: bool,
21766        snapshot: &MultiBufferSnapshot,
21767    ) -> u32 {
21768        let comment_count = self.hunk_comment_count(hunk_key, snapshot);
21769        let base_height: u32 = 2; // Input row with avatar and buttons
21770
21771        if comment_count == 0 {
21772            base_height
21773        } else if comments_expanded {
21774            // Header (1 line) + 2 lines per comment
21775            base_height + 1 + (comment_count as u32 * 2)
21776        } else {
21777            // Just header when collapsed
21778            base_height + 1
21779        }
21780    }
21781
21782    pub fn show_diff_review_overlay(
21783        &mut self,
21784        display_range: Range<DisplayRow>,
21785        window: &mut Window,
21786        cx: &mut Context<Self>,
21787    ) {
21788        let Range { start, end } = display_range.sorted();
21789
21790        let buffer_snapshot = self.buffer.read(cx).snapshot(cx);
21791        let editor_snapshot = self.snapshot(window, cx);
21792
21793        // Convert display rows to multibuffer points
21794        let start_point = editor_snapshot
21795            .display_snapshot
21796            .display_point_to_point(start.as_display_point(), Bias::Left);
21797        let end_point = editor_snapshot
21798            .display_snapshot
21799            .display_point_to_point(end.as_display_point(), Bias::Left);
21800        let end_multi_buffer_row = MultiBufferRow(end_point.row);
21801
21802        // Create anchor range for the selected lines (start of first line to end of last line)
21803        let line_end = Point::new(
21804            end_point.row,
21805            buffer_snapshot.line_len(end_multi_buffer_row),
21806        );
21807        let anchor_range =
21808            buffer_snapshot.anchor_after(start_point)..buffer_snapshot.anchor_before(line_end);
21809
21810        // Compute the hunk key for this display row
21811        let file_path = buffer_snapshot
21812            .file_at(start_point)
21813            .map(|file: &Arc<dyn language::File>| file.path().clone())
21814            .unwrap_or_else(|| Arc::from(util::rel_path::RelPath::empty()));
21815        let hunk_start_anchor = buffer_snapshot.anchor_before(start_point);
21816        let new_hunk_key = DiffHunkKey {
21817            file_path,
21818            hunk_start_anchor,
21819        };
21820
21821        // Check if we already have an overlay for this hunk
21822        if let Some(existing_overlay) = self.diff_review_overlays.iter().find(|overlay| {
21823            Self::hunk_keys_match(&overlay.hunk_key, &new_hunk_key, &buffer_snapshot)
21824        }) {
21825            // Just focus the existing overlay's prompt editor
21826            let focus_handle = existing_overlay.prompt_editor.focus_handle(cx);
21827            window.focus(&focus_handle, cx);
21828            return;
21829        }
21830
21831        // Dismiss overlays that have no comments for their hunks
21832        self.dismiss_overlays_without_comments(cx);
21833
21834        // Get the current user's avatar URI from the project's user_store
21835        let user_avatar_uri = self.project.as_ref().and_then(|project| {
21836            let user_store = project.read(cx).user_store();
21837            user_store
21838                .read(cx)
21839                .current_user()
21840                .map(|user| user.avatar_uri.clone())
21841        });
21842
21843        // Create anchor at the end of the last row so the block appears immediately below it
21844        // Use multibuffer coordinates for anchor creation
21845        let line_len = buffer_snapshot.line_len(end_multi_buffer_row);
21846        let anchor = buffer_snapshot.anchor_after(Point::new(end_multi_buffer_row.0, line_len));
21847
21848        // Use the hunk key we already computed
21849        let hunk_key = new_hunk_key;
21850
21851        // Create the prompt editor for the review input
21852        let prompt_editor = cx.new(|cx| {
21853            let mut editor = Editor::single_line(window, cx);
21854            editor.set_placeholder_text("Add a review comment...", window, cx);
21855            editor
21856        });
21857
21858        // Register the Newline action on the prompt editor to submit the review
21859        let parent_editor = cx.entity().downgrade();
21860        let subscription = prompt_editor.update(cx, |prompt_editor, _cx| {
21861            prompt_editor.register_action({
21862                let parent_editor = parent_editor.clone();
21863                move |_: &crate::actions::Newline, window, cx| {
21864                    if let Some(editor) = parent_editor.upgrade() {
21865                        editor.update(cx, |editor, cx| {
21866                            editor.submit_diff_review_comment(window, cx);
21867                        });
21868                    }
21869                }
21870            })
21871        });
21872
21873        // Calculate initial height based on existing comments for this hunk
21874        let initial_height = self.calculate_overlay_height(&hunk_key, true, &buffer_snapshot);
21875
21876        // Create the overlay block
21877        let prompt_editor_for_render = prompt_editor.clone();
21878        let hunk_key_for_render = hunk_key.clone();
21879        let editor_handle = cx.entity().downgrade();
21880        let block = BlockProperties {
21881            style: BlockStyle::Sticky,
21882            placement: BlockPlacement::Below(anchor),
21883            height: Some(initial_height),
21884            render: Arc::new(move |cx| {
21885                Self::render_diff_review_overlay(
21886                    &prompt_editor_for_render,
21887                    &hunk_key_for_render,
21888                    &editor_handle,
21889                    cx,
21890                )
21891            }),
21892            priority: 0,
21893        };
21894
21895        let block_ids = self.insert_blocks([block], None, cx);
21896        let Some(block_id) = block_ids.into_iter().next() else {
21897            log::error!("Failed to insert diff review overlay block");
21898            return;
21899        };
21900
21901        self.diff_review_overlays.push(DiffReviewOverlay {
21902            anchor_range,
21903            block_id,
21904            prompt_editor: prompt_editor.clone(),
21905            hunk_key,
21906            comments_expanded: true,
21907            inline_edit_editors: HashMap::default(),
21908            inline_edit_subscriptions: HashMap::default(),
21909            user_avatar_uri,
21910            _subscription: subscription,
21911        });
21912
21913        // Focus the prompt editor
21914        let focus_handle = prompt_editor.focus_handle(cx);
21915        window.focus(&focus_handle, cx);
21916
21917        cx.notify();
21918    }
21919
21920    /// Dismisses all diff review overlays.
21921    pub fn dismiss_all_diff_review_overlays(&mut self, cx: &mut Context<Self>) {
21922        if self.diff_review_overlays.is_empty() {
21923            return;
21924        }
21925        let block_ids: HashSet<_> = self
21926            .diff_review_overlays
21927            .drain(..)
21928            .map(|overlay| overlay.block_id)
21929            .collect();
21930        self.remove_blocks(block_ids, None, cx);
21931        cx.notify();
21932    }
21933
21934    /// Dismisses overlays that have no comments stored for their hunks.
21935    /// Keeps overlays that have at least one comment.
21936    fn dismiss_overlays_without_comments(&mut self, cx: &mut Context<Self>) {
21937        let snapshot = self.buffer.read(cx).snapshot(cx);
21938
21939        // First, compute which overlays have comments (to avoid borrow issues with retain)
21940        let overlays_with_comments: Vec<bool> = self
21941            .diff_review_overlays
21942            .iter()
21943            .map(|overlay| self.hunk_comment_count(&overlay.hunk_key, &snapshot) > 0)
21944            .collect();
21945
21946        // Now collect block IDs to remove and retain overlays
21947        let mut block_ids_to_remove = HashSet::default();
21948        let mut index = 0;
21949        self.diff_review_overlays.retain(|overlay| {
21950            let has_comments = overlays_with_comments[index];
21951            index += 1;
21952            if !has_comments {
21953                block_ids_to_remove.insert(overlay.block_id);
21954            }
21955            has_comments
21956        });
21957
21958        if !block_ids_to_remove.is_empty() {
21959            self.remove_blocks(block_ids_to_remove, None, cx);
21960            cx.notify();
21961        }
21962    }
21963
21964    /// Refreshes the diff review overlay block to update its height and render function.
21965    /// Uses resize_blocks and replace_blocks to avoid visual flicker from remove+insert.
21966    fn refresh_diff_review_overlay_height(
21967        &mut self,
21968        hunk_key: &DiffHunkKey,
21969        _window: &mut Window,
21970        cx: &mut Context<Self>,
21971    ) {
21972        // Extract all needed data from overlay first to avoid borrow conflicts
21973        let snapshot = self.buffer.read(cx).snapshot(cx);
21974        let (comments_expanded, block_id, prompt_editor) = {
21975            let Some(overlay) = self
21976                .diff_review_overlays
21977                .iter()
21978                .find(|overlay| Self::hunk_keys_match(&overlay.hunk_key, hunk_key, &snapshot))
21979            else {
21980                return;
21981            };
21982
21983            (
21984                overlay.comments_expanded,
21985                overlay.block_id,
21986                overlay.prompt_editor.clone(),
21987            )
21988        };
21989
21990        // Calculate new height
21991        let snapshot = self.buffer.read(cx).snapshot(cx);
21992        let new_height = self.calculate_overlay_height(hunk_key, comments_expanded, &snapshot);
21993
21994        // Update the block height using resize_blocks (avoids flicker)
21995        let mut heights = HashMap::default();
21996        heights.insert(block_id, new_height);
21997        self.resize_blocks(heights, None, cx);
21998
21999        // Update the render function using replace_blocks (avoids flicker)
22000        let hunk_key_for_render = hunk_key.clone();
22001        let editor_handle = cx.entity().downgrade();
22002        let render: Arc<dyn Fn(&mut BlockContext) -> AnyElement + Send + Sync> =
22003            Arc::new(move |cx| {
22004                Self::render_diff_review_overlay(
22005                    &prompt_editor,
22006                    &hunk_key_for_render,
22007                    &editor_handle,
22008                    cx,
22009                )
22010            });
22011
22012        let mut renderers = HashMap::default();
22013        renderers.insert(block_id, render);
22014        self.replace_blocks(renderers, None, cx);
22015    }
22016
22017    /// Action handler for SubmitDiffReviewComment.
22018    pub fn submit_diff_review_comment_action(
22019        &mut self,
22020        _: &SubmitDiffReviewComment,
22021        window: &mut Window,
22022        cx: &mut Context<Self>,
22023    ) {
22024        self.submit_diff_review_comment(window, cx);
22025    }
22026
22027    /// Stores the diff review comment locally.
22028    /// Comments are stored per-hunk and can later be batch-submitted to the Agent panel.
22029    pub fn submit_diff_review_comment(&mut self, window: &mut Window, cx: &mut Context<Self>) {
22030        // Find the overlay that currently has focus
22031        let overlay_index = self
22032            .diff_review_overlays
22033            .iter()
22034            .position(|overlay| overlay.prompt_editor.focus_handle(cx).is_focused(window));
22035        let Some(overlay_index) = overlay_index else {
22036            return;
22037        };
22038        let overlay = &self.diff_review_overlays[overlay_index];
22039
22040        let comment_text = overlay.prompt_editor.read(cx).text(cx).trim().to_string();
22041        if comment_text.is_empty() {
22042            return;
22043        }
22044
22045        let anchor_range = overlay.anchor_range.clone();
22046        let hunk_key = overlay.hunk_key.clone();
22047
22048        self.add_review_comment(hunk_key.clone(), comment_text, anchor_range, cx);
22049
22050        // Clear the prompt editor but keep the overlay open
22051        if let Some(overlay) = self.diff_review_overlays.get(overlay_index) {
22052            overlay.prompt_editor.update(cx, |editor, cx| {
22053                editor.clear(window, cx);
22054            });
22055        }
22056
22057        // Refresh the overlay to update the block height for the new comment
22058        self.refresh_diff_review_overlay_height(&hunk_key, window, cx);
22059
22060        cx.notify();
22061    }
22062
22063    /// Returns the prompt editor for the diff review overlay, if one is active.
22064    /// This is primarily used for testing.
22065    pub fn diff_review_prompt_editor(&self) -> Option<&Entity<Editor>> {
22066        self.diff_review_overlays
22067            .first()
22068            .map(|overlay| &overlay.prompt_editor)
22069    }
22070
22071    /// Returns the line range for the first diff review overlay, if one is active.
22072    /// Returns (start_row, end_row) as physical line numbers in the underlying file.
22073    pub fn diff_review_line_range(&self, cx: &App) -> Option<(u32, u32)> {
22074        let overlay = self.diff_review_overlays.first()?;
22075        let snapshot = self.buffer.read(cx).snapshot(cx);
22076        let start_point = overlay.anchor_range.start.to_point(&snapshot);
22077        let end_point = overlay.anchor_range.end.to_point(&snapshot);
22078        let start_row = snapshot
22079            .point_to_buffer_point(start_point)
22080            .map(|(_, p, _)| p.row)
22081            .unwrap_or(start_point.row);
22082        let end_row = snapshot
22083            .point_to_buffer_point(end_point)
22084            .map(|(_, p, _)| p.row)
22085            .unwrap_or(end_point.row);
22086        Some((start_row, end_row))
22087    }
22088
22089    /// Sets whether the comments section is expanded in the diff review overlay.
22090    /// This is primarily used for testing.
22091    pub fn set_diff_review_comments_expanded(&mut self, expanded: bool, cx: &mut Context<Self>) {
22092        for overlay in &mut self.diff_review_overlays {
22093            overlay.comments_expanded = expanded;
22094        }
22095        cx.notify();
22096    }
22097
22098    /// Compares two DiffHunkKeys for equality by resolving their anchors.
22099    fn hunk_keys_match(a: &DiffHunkKey, b: &DiffHunkKey, snapshot: &MultiBufferSnapshot) -> bool {
22100        a.file_path == b.file_path
22101            && a.hunk_start_anchor.to_point(snapshot) == b.hunk_start_anchor.to_point(snapshot)
22102    }
22103
22104    /// Returns comments for a specific hunk, ordered by creation time.
22105    pub fn comments_for_hunk<'a>(
22106        &'a self,
22107        key: &DiffHunkKey,
22108        snapshot: &MultiBufferSnapshot,
22109    ) -> &'a [StoredReviewComment] {
22110        let key_point = key.hunk_start_anchor.to_point(snapshot);
22111        self.stored_review_comments
22112            .iter()
22113            .find(|(k, _)| {
22114                k.file_path == key.file_path && k.hunk_start_anchor.to_point(snapshot) == key_point
22115            })
22116            .map(|(_, comments)| comments.as_slice())
22117            .unwrap_or(&[])
22118    }
22119
22120    /// Returns the total count of stored review comments across all hunks.
22121    pub fn total_review_comment_count(&self) -> usize {
22122        self.stored_review_comments
22123            .iter()
22124            .map(|(_, v)| v.len())
22125            .sum()
22126    }
22127
22128    /// Returns the count of comments for a specific hunk.
22129    pub fn hunk_comment_count(&self, key: &DiffHunkKey, snapshot: &MultiBufferSnapshot) -> usize {
22130        let key_point = key.hunk_start_anchor.to_point(snapshot);
22131        self.stored_review_comments
22132            .iter()
22133            .find(|(k, _)| {
22134                k.file_path == key.file_path && k.hunk_start_anchor.to_point(snapshot) == key_point
22135            })
22136            .map(|(_, v)| v.len())
22137            .unwrap_or(0)
22138    }
22139
22140    /// Adds a new review comment to a specific hunk.
22141    pub fn add_review_comment(
22142        &mut self,
22143        hunk_key: DiffHunkKey,
22144        comment: String,
22145        anchor_range: Range<Anchor>,
22146        cx: &mut Context<Self>,
22147    ) -> usize {
22148        let id = self.next_review_comment_id;
22149        self.next_review_comment_id += 1;
22150
22151        let stored_comment = StoredReviewComment::new(id, comment, anchor_range);
22152
22153        let snapshot = self.buffer.read(cx).snapshot(cx);
22154        let key_point = hunk_key.hunk_start_anchor.to_point(&snapshot);
22155
22156        // Find existing entry for this hunk or add a new one
22157        if let Some((_, comments)) = self.stored_review_comments.iter_mut().find(|(k, _)| {
22158            k.file_path == hunk_key.file_path
22159                && k.hunk_start_anchor.to_point(&snapshot) == key_point
22160        }) {
22161            comments.push(stored_comment);
22162        } else {
22163            self.stored_review_comments
22164                .push((hunk_key, vec![stored_comment]));
22165        }
22166
22167        cx.emit(EditorEvent::ReviewCommentsChanged {
22168            total_count: self.total_review_comment_count(),
22169        });
22170        cx.notify();
22171        id
22172    }
22173
22174    /// Removes a review comment by ID from any hunk.
22175    pub fn remove_review_comment(&mut self, id: usize, cx: &mut Context<Self>) -> bool {
22176        for (_, comments) in self.stored_review_comments.iter_mut() {
22177            if let Some(index) = comments.iter().position(|c| c.id == id) {
22178                comments.remove(index);
22179                cx.emit(EditorEvent::ReviewCommentsChanged {
22180                    total_count: self.total_review_comment_count(),
22181                });
22182                cx.notify();
22183                return true;
22184            }
22185        }
22186        false
22187    }
22188
22189    /// Updates a review comment's text by ID.
22190    pub fn update_review_comment(
22191        &mut self,
22192        id: usize,
22193        new_comment: String,
22194        cx: &mut Context<Self>,
22195    ) -> bool {
22196        for (_, comments) in self.stored_review_comments.iter_mut() {
22197            if let Some(comment) = comments.iter_mut().find(|c| c.id == id) {
22198                comment.comment = new_comment;
22199                comment.is_editing = false;
22200                cx.emit(EditorEvent::ReviewCommentsChanged {
22201                    total_count: self.total_review_comment_count(),
22202                });
22203                cx.notify();
22204                return true;
22205            }
22206        }
22207        false
22208    }
22209
22210    /// Sets a comment's editing state.
22211    pub fn set_comment_editing(&mut self, id: usize, is_editing: bool, cx: &mut Context<Self>) {
22212        for (_, comments) in self.stored_review_comments.iter_mut() {
22213            if let Some(comment) = comments.iter_mut().find(|c| c.id == id) {
22214                comment.is_editing = is_editing;
22215                cx.notify();
22216                return;
22217            }
22218        }
22219    }
22220
22221    /// Takes all stored comments from all hunks, clearing the storage.
22222    /// Returns a Vec of (hunk_key, comments) pairs.
22223    pub fn take_all_review_comments(
22224        &mut self,
22225        cx: &mut Context<Self>,
22226    ) -> Vec<(DiffHunkKey, Vec<StoredReviewComment>)> {
22227        // Dismiss all overlays when taking comments (e.g., when sending to agent)
22228        self.dismiss_all_diff_review_overlays(cx);
22229        let comments = std::mem::take(&mut self.stored_review_comments);
22230        // Reset the ID counter since all comments have been taken
22231        self.next_review_comment_id = 0;
22232        cx.emit(EditorEvent::ReviewCommentsChanged { total_count: 0 });
22233        cx.notify();
22234        comments
22235    }
22236
22237    /// Removes review comments whose anchors are no longer valid or whose
22238    /// associated diff hunks no longer exist.
22239    ///
22240    /// This should be called when the buffer changes to prevent orphaned comments
22241    /// from accumulating.
22242    pub fn cleanup_orphaned_review_comments(&mut self, cx: &mut Context<Self>) {
22243        let snapshot = self.buffer.read(cx).snapshot(cx);
22244        let original_count = self.total_review_comment_count();
22245
22246        // Remove comments with invalid hunk anchors
22247        self.stored_review_comments
22248            .retain(|(hunk_key, _)| hunk_key.hunk_start_anchor.is_valid(&snapshot));
22249
22250        // Also clean up individual comments with invalid anchor ranges
22251        for (_, comments) in &mut self.stored_review_comments {
22252            comments.retain(|comment| {
22253                comment.range.start.is_valid(&snapshot) && comment.range.end.is_valid(&snapshot)
22254            });
22255        }
22256
22257        // Remove empty hunk entries
22258        self.stored_review_comments
22259            .retain(|(_, comments)| !comments.is_empty());
22260
22261        let new_count = self.total_review_comment_count();
22262        if new_count != original_count {
22263            cx.emit(EditorEvent::ReviewCommentsChanged {
22264                total_count: new_count,
22265            });
22266            cx.notify();
22267        }
22268    }
22269
22270    /// Toggles the expanded state of the comments section in the overlay.
22271    pub fn toggle_review_comments_expanded(
22272        &mut self,
22273        _: &ToggleReviewCommentsExpanded,
22274        window: &mut Window,
22275        cx: &mut Context<Self>,
22276    ) {
22277        // Find the overlay that currently has focus, or use the first one
22278        let overlay_info = self.diff_review_overlays.iter_mut().find_map(|overlay| {
22279            if overlay.prompt_editor.focus_handle(cx).is_focused(window) {
22280                overlay.comments_expanded = !overlay.comments_expanded;
22281                Some(overlay.hunk_key.clone())
22282            } else {
22283                None
22284            }
22285        });
22286
22287        // If no focused overlay found, toggle the first one
22288        let hunk_key = overlay_info.or_else(|| {
22289            self.diff_review_overlays.first_mut().map(|overlay| {
22290                overlay.comments_expanded = !overlay.comments_expanded;
22291                overlay.hunk_key.clone()
22292            })
22293        });
22294
22295        if let Some(hunk_key) = hunk_key {
22296            self.refresh_diff_review_overlay_height(&hunk_key, window, cx);
22297            cx.notify();
22298        }
22299    }
22300
22301    /// Handles the EditReviewComment action - sets a comment into editing mode.
22302    pub fn edit_review_comment(
22303        &mut self,
22304        action: &EditReviewComment,
22305        window: &mut Window,
22306        cx: &mut Context<Self>,
22307    ) {
22308        let comment_id = action.id;
22309
22310        // Set the comment to editing mode
22311        self.set_comment_editing(comment_id, true, cx);
22312
22313        // Find the overlay that contains this comment and create an inline editor if needed
22314        // First, find which hunk this comment belongs to
22315        let hunk_key = self
22316            .stored_review_comments
22317            .iter()
22318            .find_map(|(key, comments)| {
22319                if comments.iter().any(|c| c.id == comment_id) {
22320                    Some(key.clone())
22321                } else {
22322                    None
22323                }
22324            });
22325
22326        let snapshot = self.buffer.read(cx).snapshot(cx);
22327        if let Some(hunk_key) = hunk_key {
22328            if let Some(overlay) = self
22329                .diff_review_overlays
22330                .iter_mut()
22331                .find(|overlay| Self::hunk_keys_match(&overlay.hunk_key, &hunk_key, &snapshot))
22332            {
22333                if let std::collections::hash_map::Entry::Vacant(entry) =
22334                    overlay.inline_edit_editors.entry(comment_id)
22335                {
22336                    // Find the comment text
22337                    let comment_text = self
22338                        .stored_review_comments
22339                        .iter()
22340                        .flat_map(|(_, comments)| comments)
22341                        .find(|c| c.id == comment_id)
22342                        .map(|c| c.comment.clone())
22343                        .unwrap_or_default();
22344
22345                    // Create inline editor
22346                    let parent_editor = cx.entity().downgrade();
22347                    let inline_editor = cx.new(|cx| {
22348                        let mut editor = Editor::single_line(window, cx);
22349                        editor.set_text(&*comment_text, window, cx);
22350                        // Select all text for easy replacement
22351                        editor.select_all(&crate::actions::SelectAll, window, cx);
22352                        editor
22353                    });
22354
22355                    // Register the Newline action to confirm the edit
22356                    let subscription = inline_editor.update(cx, |inline_editor, _cx| {
22357                        inline_editor.register_action({
22358                            let parent_editor = parent_editor.clone();
22359                            move |_: &crate::actions::Newline, window, cx| {
22360                                if let Some(editor) = parent_editor.upgrade() {
22361                                    editor.update(cx, |editor, cx| {
22362                                        editor.confirm_edit_review_comment(comment_id, window, cx);
22363                                    });
22364                                }
22365                            }
22366                        })
22367                    });
22368
22369                    // Store the subscription to keep the action handler alive
22370                    overlay
22371                        .inline_edit_subscriptions
22372                        .insert(comment_id, subscription);
22373
22374                    // Focus the inline editor
22375                    let focus_handle = inline_editor.focus_handle(cx);
22376                    window.focus(&focus_handle, cx);
22377
22378                    entry.insert(inline_editor);
22379                }
22380            }
22381        }
22382
22383        cx.notify();
22384    }
22385
22386    /// Confirms an inline edit of a review comment.
22387    pub fn confirm_edit_review_comment(
22388        &mut self,
22389        comment_id: usize,
22390        _window: &mut Window,
22391        cx: &mut Context<Self>,
22392    ) {
22393        // Get the new text from the inline editor
22394        // Find the overlay containing this comment's inline editor
22395        let snapshot = self.buffer.read(cx).snapshot(cx);
22396        let hunk_key = self
22397            .stored_review_comments
22398            .iter()
22399            .find_map(|(key, comments)| {
22400                if comments.iter().any(|c| c.id == comment_id) {
22401                    Some(key.clone())
22402                } else {
22403                    None
22404                }
22405            });
22406
22407        let new_text = hunk_key
22408            .as_ref()
22409            .and_then(|hunk_key| {
22410                self.diff_review_overlays
22411                    .iter()
22412                    .find(|overlay| Self::hunk_keys_match(&overlay.hunk_key, hunk_key, &snapshot))
22413            })
22414            .as_ref()
22415            .and_then(|overlay| overlay.inline_edit_editors.get(&comment_id))
22416            .map(|editor| editor.read(cx).text(cx).trim().to_string());
22417
22418        if let Some(new_text) = new_text {
22419            if !new_text.is_empty() {
22420                self.update_review_comment(comment_id, new_text, cx);
22421            }
22422        }
22423
22424        // Remove the inline editor and its subscription
22425        if let Some(hunk_key) = hunk_key {
22426            if let Some(overlay) = self
22427                .diff_review_overlays
22428                .iter_mut()
22429                .find(|overlay| Self::hunk_keys_match(&overlay.hunk_key, &hunk_key, &snapshot))
22430            {
22431                overlay.inline_edit_editors.remove(&comment_id);
22432                overlay.inline_edit_subscriptions.remove(&comment_id);
22433            }
22434        }
22435
22436        // Clear editing state
22437        self.set_comment_editing(comment_id, false, cx);
22438    }
22439
22440    /// Cancels an inline edit of a review comment.
22441    pub fn cancel_edit_review_comment(
22442        &mut self,
22443        comment_id: usize,
22444        _window: &mut Window,
22445        cx: &mut Context<Self>,
22446    ) {
22447        // Find which hunk this comment belongs to
22448        let hunk_key = self
22449            .stored_review_comments
22450            .iter()
22451            .find_map(|(key, comments)| {
22452                if comments.iter().any(|c| c.id == comment_id) {
22453                    Some(key.clone())
22454                } else {
22455                    None
22456                }
22457            });
22458
22459        // Remove the inline editor and its subscription
22460        if let Some(hunk_key) = hunk_key {
22461            let snapshot = self.buffer.read(cx).snapshot(cx);
22462            if let Some(overlay) = self
22463                .diff_review_overlays
22464                .iter_mut()
22465                .find(|overlay| Self::hunk_keys_match(&overlay.hunk_key, &hunk_key, &snapshot))
22466            {
22467                overlay.inline_edit_editors.remove(&comment_id);
22468                overlay.inline_edit_subscriptions.remove(&comment_id);
22469            }
22470        }
22471
22472        // Clear editing state
22473        self.set_comment_editing(comment_id, false, cx);
22474    }
22475
22476    /// Action handler for ConfirmEditReviewComment.
22477    pub fn confirm_edit_review_comment_action(
22478        &mut self,
22479        action: &ConfirmEditReviewComment,
22480        window: &mut Window,
22481        cx: &mut Context<Self>,
22482    ) {
22483        self.confirm_edit_review_comment(action.id, window, cx);
22484    }
22485
22486    /// Action handler for CancelEditReviewComment.
22487    pub fn cancel_edit_review_comment_action(
22488        &mut self,
22489        action: &CancelEditReviewComment,
22490        window: &mut Window,
22491        cx: &mut Context<Self>,
22492    ) {
22493        self.cancel_edit_review_comment(action.id, window, cx);
22494    }
22495
22496    /// Handles the DeleteReviewComment action - removes a comment.
22497    pub fn delete_review_comment(
22498        &mut self,
22499        action: &DeleteReviewComment,
22500        window: &mut Window,
22501        cx: &mut Context<Self>,
22502    ) {
22503        // Get the hunk key before removing the comment
22504        // Find the hunk key from the comment itself
22505        let comment_id = action.id;
22506        let hunk_key = self
22507            .stored_review_comments
22508            .iter()
22509            .find_map(|(key, comments)| {
22510                if comments.iter().any(|c| c.id == comment_id) {
22511                    Some(key.clone())
22512                } else {
22513                    None
22514                }
22515            });
22516
22517        // Also get it from the overlay for refresh purposes
22518        let overlay_hunk_key = self
22519            .diff_review_overlays
22520            .first()
22521            .map(|o| o.hunk_key.clone());
22522
22523        self.remove_review_comment(action.id, cx);
22524
22525        // Refresh the overlay height after removing a comment
22526        if let Some(hunk_key) = hunk_key.or(overlay_hunk_key) {
22527            self.refresh_diff_review_overlay_height(&hunk_key, window, cx);
22528        }
22529    }
22530
22531    fn render_diff_review_overlay(
22532        prompt_editor: &Entity<Editor>,
22533        hunk_key: &DiffHunkKey,
22534        editor_handle: &WeakEntity<Editor>,
22535        cx: &mut BlockContext,
22536    ) -> AnyElement {
22537        fn format_line_ranges(ranges: &[(u32, u32)]) -> Option<String> {
22538            if ranges.is_empty() {
22539                return None;
22540            }
22541            let formatted: Vec<String> = ranges
22542                .iter()
22543                .map(|(start, end)| {
22544                    let start_line = start + 1;
22545                    let end_line = end + 1;
22546                    if start_line == end_line {
22547                        format!("Line {start_line}")
22548                    } else {
22549                        format!("Lines {start_line}-{end_line}")
22550                    }
22551                })
22552                .collect();
22553            // Don't show label for single line in single excerpt
22554            if ranges.len() == 1 && ranges[0].0 == ranges[0].1 {
22555                return None;
22556            }
22557            Some(formatted.join(""))
22558        }
22559
22560        let theme = cx.theme();
22561        let colors = theme.colors();
22562
22563        let (comments, comments_expanded, inline_editors, user_avatar_uri, line_ranges) =
22564            editor_handle
22565                .upgrade()
22566                .map(|editor| {
22567                    let editor = editor.read(cx);
22568                    let snapshot = editor.buffer().read(cx).snapshot(cx);
22569                    let comments = editor.comments_for_hunk(hunk_key, &snapshot).to_vec();
22570                    let (expanded, editors, avatar_uri, line_ranges) = editor
22571                        .diff_review_overlays
22572                        .iter()
22573                        .find(|overlay| {
22574                            Editor::hunk_keys_match(&overlay.hunk_key, hunk_key, &snapshot)
22575                        })
22576                        .map(|o| {
22577                            let start_point = o.anchor_range.start.to_point(&snapshot);
22578                            let end_point = o.anchor_range.end.to_point(&snapshot);
22579                            // Get line ranges per excerpt to detect discontinuities
22580                            let buffer_ranges =
22581                                snapshot.range_to_buffer_ranges(start_point..end_point);
22582                            let ranges: Vec<(u32, u32)> = buffer_ranges
22583                                .iter()
22584                                .map(|(buffer, range, _)| {
22585                                    let start = buffer.offset_to_point(range.start.0).row;
22586                                    let end = buffer.offset_to_point(range.end.0).row;
22587                                    (start, end)
22588                                })
22589                                .collect();
22590                            (
22591                                o.comments_expanded,
22592                                o.inline_edit_editors.clone(),
22593                                o.user_avatar_uri.clone(),
22594                                if ranges.is_empty() {
22595                                    None
22596                                } else {
22597                                    Some(ranges)
22598                                },
22599                            )
22600                        })
22601                        .unwrap_or((true, HashMap::default(), None, None));
22602                    (comments, expanded, editors, avatar_uri, line_ranges)
22603                })
22604                .unwrap_or((Vec::new(), true, HashMap::default(), None, None));
22605
22606        let comment_count = comments.len();
22607        let avatar_size = px(20.);
22608        let action_icon_size = IconSize::XSmall;
22609
22610        v_flex()
22611            .w_full()
22612            .bg(colors.editor_background)
22613            .border_b_1()
22614            .border_color(colors.border)
22615            .px_2()
22616            .pb_2()
22617            .gap_2()
22618            // Line range indicator (only shown for multi-line selections or multiple excerpts)
22619            .when_some(line_ranges, |el, ranges| {
22620                let label = format_line_ranges(&ranges);
22621                if let Some(label) = label {
22622                    el.child(
22623                        h_flex()
22624                            .w_full()
22625                            .px_2()
22626                            .child(Label::new(label).size(LabelSize::Small).color(Color::Muted)),
22627                    )
22628                } else {
22629                    el
22630                }
22631            })
22632            // Top row: editable input with user's avatar
22633            .child(
22634                h_flex()
22635                    .w_full()
22636                    .items_center()
22637                    .gap_2()
22638                    .px_2()
22639                    .py_1p5()
22640                    .rounded_md()
22641                    .bg(colors.surface_background)
22642                    .child(
22643                        div()
22644                            .size(avatar_size)
22645                            .flex_shrink_0()
22646                            .rounded_full()
22647                            .overflow_hidden()
22648                            .child(if let Some(ref avatar_uri) = user_avatar_uri {
22649                                Avatar::new(avatar_uri.clone())
22650                                    .size(avatar_size)
22651                                    .into_any_element()
22652                            } else {
22653                                Icon::new(IconName::Person)
22654                                    .size(IconSize::Small)
22655                                    .color(ui::Color::Muted)
22656                                    .into_any_element()
22657                            }),
22658                    )
22659                    .child(
22660                        div()
22661                            .flex_1()
22662                            .border_1()
22663                            .border_color(colors.border)
22664                            .rounded_md()
22665                            .bg(colors.editor_background)
22666                            .px_2()
22667                            .py_1()
22668                            .child(prompt_editor.clone()),
22669                    )
22670                    .child(
22671                        h_flex()
22672                            .flex_shrink_0()
22673                            .gap_1()
22674                            .child(
22675                                IconButton::new("diff-review-close", IconName::Close)
22676                                    .icon_color(ui::Color::Muted)
22677                                    .icon_size(action_icon_size)
22678                                    .tooltip(Tooltip::text("Close"))
22679                                    .on_click(|_, window, cx| {
22680                                        window
22681                                            .dispatch_action(Box::new(crate::actions::Cancel), cx);
22682                                    }),
22683                            )
22684                            .child(
22685                                IconButton::new("diff-review-add", IconName::Return)
22686                                    .icon_color(ui::Color::Muted)
22687                                    .icon_size(action_icon_size)
22688                                    .tooltip(Tooltip::text("Add comment"))
22689                                    .on_click(|_, window, cx| {
22690                                        window.dispatch_action(
22691                                            Box::new(crate::actions::SubmitDiffReviewComment),
22692                                            cx,
22693                                        );
22694                                    }),
22695                            ),
22696                    ),
22697            )
22698            // Expandable comments section (only shown when there are comments)
22699            .when(comment_count > 0, |el| {
22700                el.child(Self::render_comments_section(
22701                    comments,
22702                    comments_expanded,
22703                    inline_editors,
22704                    user_avatar_uri,
22705                    avatar_size,
22706                    action_icon_size,
22707                    colors,
22708                ))
22709            })
22710            .into_any_element()
22711    }
22712
22713    fn render_comments_section(
22714        comments: Vec<StoredReviewComment>,
22715        expanded: bool,
22716        inline_editors: HashMap<usize, Entity<Editor>>,
22717        user_avatar_uri: Option<SharedUri>,
22718        avatar_size: Pixels,
22719        action_icon_size: IconSize,
22720        colors: &theme::ThemeColors,
22721    ) -> impl IntoElement {
22722        let comment_count = comments.len();
22723
22724        v_flex()
22725            .w_full()
22726            .gap_1()
22727            // Header with expand/collapse toggle
22728            .child(
22729                h_flex()
22730                    .id("review-comments-header")
22731                    .w_full()
22732                    .items_center()
22733                    .gap_1()
22734                    .px_2()
22735                    .py_1()
22736                    .cursor_pointer()
22737                    .rounded_md()
22738                    .hover(|style| style.bg(colors.ghost_element_hover))
22739                    .on_click(|_, window: &mut Window, cx| {
22740                        window.dispatch_action(
22741                            Box::new(crate::actions::ToggleReviewCommentsExpanded),
22742                            cx,
22743                        );
22744                    })
22745                    .child(
22746                        Icon::new(if expanded {
22747                            IconName::ChevronDown
22748                        } else {
22749                            IconName::ChevronRight
22750                        })
22751                        .size(IconSize::Small)
22752                        .color(ui::Color::Muted),
22753                    )
22754                    .child(
22755                        Label::new(format!(
22756                            "{} Comment{}",
22757                            comment_count,
22758                            if comment_count == 1 { "" } else { "s" }
22759                        ))
22760                        .size(LabelSize::Small)
22761                        .color(Color::Muted),
22762                    ),
22763            )
22764            // Comments list (when expanded)
22765            .when(expanded, |el| {
22766                el.children(comments.into_iter().map(|comment| {
22767                    let inline_editor = inline_editors.get(&comment.id).cloned();
22768                    Self::render_comment_row(
22769                        comment,
22770                        inline_editor,
22771                        user_avatar_uri.clone(),
22772                        avatar_size,
22773                        action_icon_size,
22774                        colors,
22775                    )
22776                }))
22777            })
22778    }
22779
22780    fn render_comment_row(
22781        comment: StoredReviewComment,
22782        inline_editor: Option<Entity<Editor>>,
22783        user_avatar_uri: Option<SharedUri>,
22784        avatar_size: Pixels,
22785        action_icon_size: IconSize,
22786        colors: &theme::ThemeColors,
22787    ) -> impl IntoElement {
22788        let comment_id = comment.id;
22789        let is_editing = inline_editor.is_some();
22790
22791        h_flex()
22792            .w_full()
22793            .items_center()
22794            .gap_2()
22795            .px_2()
22796            .py_1p5()
22797            .rounded_md()
22798            .bg(colors.surface_background)
22799            .child(
22800                div()
22801                    .size(avatar_size)
22802                    .flex_shrink_0()
22803                    .rounded_full()
22804                    .overflow_hidden()
22805                    .child(if let Some(ref avatar_uri) = user_avatar_uri {
22806                        Avatar::new(avatar_uri.clone())
22807                            .size(avatar_size)
22808                            .into_any_element()
22809                    } else {
22810                        Icon::new(IconName::Person)
22811                            .size(IconSize::Small)
22812                            .color(ui::Color::Muted)
22813                            .into_any_element()
22814                    }),
22815            )
22816            .child(if let Some(editor) = inline_editor {
22817                // Inline edit mode: show an editable text field
22818                div()
22819                    .flex_1()
22820                    .border_1()
22821                    .border_color(colors.border)
22822                    .rounded_md()
22823                    .bg(colors.editor_background)
22824                    .px_2()
22825                    .py_1()
22826                    .child(editor)
22827                    .into_any_element()
22828            } else {
22829                // Display mode: show the comment text
22830                div()
22831                    .flex_1()
22832                    .text_sm()
22833                    .text_color(colors.text)
22834                    .child(comment.comment)
22835                    .into_any_element()
22836            })
22837            .child(if is_editing {
22838                // Editing mode: show close and confirm buttons
22839                h_flex()
22840                    .gap_1()
22841                    .child(
22842                        IconButton::new(
22843                            format!("diff-review-cancel-edit-{comment_id}"),
22844                            IconName::Close,
22845                        )
22846                        .icon_color(ui::Color::Muted)
22847                        .icon_size(action_icon_size)
22848                        .tooltip(Tooltip::text("Cancel"))
22849                        .on_click(move |_, window, cx| {
22850                            window.dispatch_action(
22851                                Box::new(crate::actions::CancelEditReviewComment {
22852                                    id: comment_id,
22853                                }),
22854                                cx,
22855                            );
22856                        }),
22857                    )
22858                    .child(
22859                        IconButton::new(
22860                            format!("diff-review-confirm-edit-{comment_id}"),
22861                            IconName::Return,
22862                        )
22863                        .icon_color(ui::Color::Muted)
22864                        .icon_size(action_icon_size)
22865                        .tooltip(Tooltip::text("Confirm"))
22866                        .on_click(move |_, window, cx| {
22867                            window.dispatch_action(
22868                                Box::new(crate::actions::ConfirmEditReviewComment {
22869                                    id: comment_id,
22870                                }),
22871                                cx,
22872                            );
22873                        }),
22874                    )
22875                    .into_any_element()
22876            } else {
22877                // Display mode: no action buttons for now (edit/delete not yet implemented)
22878                gpui::Empty.into_any_element()
22879            })
22880    }
22881
22882    pub fn set_masked(&mut self, masked: bool, cx: &mut Context<Self>) {
22883        if self.display_map.read(cx).masked != masked {
22884            self.display_map.update(cx, |map, _| map.masked = masked);
22885        }
22886        cx.notify()
22887    }
22888
22889    pub fn set_show_wrap_guides(&mut self, show_wrap_guides: bool, cx: &mut Context<Self>) {
22890        self.show_wrap_guides = Some(show_wrap_guides);
22891        cx.notify();
22892    }
22893
22894    pub fn set_show_indent_guides(&mut self, show_indent_guides: bool, cx: &mut Context<Self>) {
22895        self.show_indent_guides = Some(show_indent_guides);
22896        cx.notify();
22897    }
22898
22899    pub fn working_directory(&self, cx: &App) -> Option<PathBuf> {
22900        if let Some(buffer) = self.buffer().read(cx).as_singleton() {
22901            if let Some(file) = buffer.read(cx).file().and_then(|f| f.as_local())
22902                && let Some(dir) = file.abs_path(cx).parent()
22903            {
22904                return Some(dir.to_owned());
22905            }
22906        }
22907
22908        None
22909    }
22910
22911    fn target_file<'a>(&self, cx: &'a App) -> Option<&'a dyn language::LocalFile> {
22912        self.active_excerpt(cx)?
22913            .1
22914            .read(cx)
22915            .file()
22916            .and_then(|f| f.as_local())
22917    }
22918
22919    pub fn target_file_abs_path(&self, cx: &mut Context<Self>) -> Option<PathBuf> {
22920        self.active_excerpt(cx).and_then(|(_, buffer, _)| {
22921            let buffer = buffer.read(cx);
22922            if let Some(project_path) = buffer.project_path(cx) {
22923                let project = self.project()?.read(cx);
22924                project.absolute_path(&project_path, cx)
22925            } else {
22926                buffer
22927                    .file()
22928                    .and_then(|file| file.as_local().map(|file| file.abs_path(cx)))
22929            }
22930        })
22931    }
22932
22933    pub fn reveal_in_finder(
22934        &mut self,
22935        _: &RevealInFileManager,
22936        _window: &mut Window,
22937        cx: &mut Context<Self>,
22938    ) {
22939        if let Some(path) = self.target_file_abs_path(cx) {
22940            if let Some(project) = self.project() {
22941                project.update(cx, |project, cx| project.reveal_path(&path, cx));
22942            } else {
22943                cx.reveal_path(&path);
22944            }
22945        }
22946    }
22947
22948    pub fn copy_path(
22949        &mut self,
22950        _: &zed_actions::workspace::CopyPath,
22951        _window: &mut Window,
22952        cx: &mut Context<Self>,
22953    ) {
22954        if let Some(path) = self.target_file_abs_path(cx)
22955            && let Some(path) = path.to_str()
22956        {
22957            cx.write_to_clipboard(ClipboardItem::new_string(path.to_string()));
22958        } else {
22959            cx.propagate();
22960        }
22961    }
22962
22963    pub fn copy_relative_path(
22964        &mut self,
22965        _: &zed_actions::workspace::CopyRelativePath,
22966        _window: &mut Window,
22967        cx: &mut Context<Self>,
22968    ) {
22969        if let Some(path) = self.active_excerpt(cx).and_then(|(_, buffer, _)| {
22970            let project = self.project()?.read(cx);
22971            let path = buffer.read(cx).file()?.path();
22972            let path = path.display(project.path_style(cx));
22973            Some(path)
22974        }) {
22975            cx.write_to_clipboard(ClipboardItem::new_string(path.to_string()));
22976        } else {
22977            cx.propagate();
22978        }
22979    }
22980
22981    /// Returns the project path for the editor's buffer, if any buffer is
22982    /// opened in the editor.
22983    pub fn project_path(&self, cx: &App) -> Option<ProjectPath> {
22984        if let Some(buffer) = self.buffer.read(cx).as_singleton() {
22985            buffer.read(cx).project_path(cx)
22986        } else {
22987            None
22988        }
22989    }
22990
22991    // Returns true if the editor handled a go-to-line request
22992    pub fn go_to_active_debug_line(&mut self, window: &mut Window, cx: &mut Context<Self>) -> bool {
22993        maybe!({
22994            let breakpoint_store = self.breakpoint_store.as_ref()?;
22995
22996            let (active_stack_frame, debug_line_pane_id) = {
22997                let store = breakpoint_store.read(cx);
22998                let active_stack_frame = store.active_position().cloned();
22999                let debug_line_pane_id = store.active_debug_line_pane_id();
23000                (active_stack_frame, debug_line_pane_id)
23001            };
23002
23003            let Some(active_stack_frame) = active_stack_frame else {
23004                self.clear_row_highlights::<ActiveDebugLine>();
23005                return None;
23006            };
23007
23008            if let Some(debug_line_pane_id) = debug_line_pane_id {
23009                if let Some(workspace) = self
23010                    .workspace
23011                    .as_ref()
23012                    .and_then(|(workspace, _)| workspace.upgrade())
23013                {
23014                    let editor_pane_id = workspace
23015                        .read(cx)
23016                        .pane_for_item_id(cx.entity_id())
23017                        .map(|pane| pane.entity_id());
23018
23019                    if editor_pane_id.is_some_and(|id| id != debug_line_pane_id) {
23020                        self.clear_row_highlights::<ActiveDebugLine>();
23021                        return None;
23022                    }
23023                }
23024            }
23025
23026            let position = active_stack_frame.position;
23027            let buffer_id = position.buffer_id?;
23028            let snapshot = self
23029                .project
23030                .as_ref()?
23031                .read(cx)
23032                .buffer_for_id(buffer_id, cx)?
23033                .read(cx)
23034                .snapshot();
23035
23036            let mut handled = false;
23037            for (id, _, ExcerptRange { context, .. }) in
23038                self.buffer.read(cx).excerpts_for_buffer(buffer_id, cx)
23039            {
23040                if context.start.cmp(&position, &snapshot).is_ge()
23041                    || context.end.cmp(&position, &snapshot).is_lt()
23042                {
23043                    continue;
23044                }
23045                let snapshot = self.buffer.read(cx).snapshot(cx);
23046                let multibuffer_anchor = snapshot.anchor_in_excerpt(id, position)?;
23047
23048                handled = true;
23049                self.clear_row_highlights::<ActiveDebugLine>();
23050
23051                self.go_to_line::<ActiveDebugLine>(
23052                    multibuffer_anchor,
23053                    Some(cx.theme().colors().editor_debugger_active_line_background),
23054                    window,
23055                    cx,
23056                );
23057
23058                cx.notify();
23059            }
23060
23061            handled.then_some(())
23062        })
23063        .is_some()
23064    }
23065
23066    pub fn copy_file_name_without_extension(
23067        &mut self,
23068        _: &CopyFileNameWithoutExtension,
23069        _: &mut Window,
23070        cx: &mut Context<Self>,
23071    ) {
23072        if let Some(file_stem) = self.active_excerpt(cx).and_then(|(_, buffer, _)| {
23073            let file = buffer.read(cx).file()?;
23074            file.path().file_stem()
23075        }) {
23076            cx.write_to_clipboard(ClipboardItem::new_string(file_stem.to_string()));
23077        }
23078    }
23079
23080    pub fn copy_file_name(&mut self, _: &CopyFileName, _: &mut Window, cx: &mut Context<Self>) {
23081        if let Some(file_name) = self.active_excerpt(cx).and_then(|(_, buffer, _)| {
23082            let file = buffer.read(cx).file()?;
23083            Some(file.file_name(cx))
23084        }) {
23085            cx.write_to_clipboard(ClipboardItem::new_string(file_name.to_string()));
23086        }
23087    }
23088
23089    pub fn toggle_git_blame(
23090        &mut self,
23091        _: &::git::Blame,
23092        window: &mut Window,
23093        cx: &mut Context<Self>,
23094    ) {
23095        self.show_git_blame_gutter = !self.show_git_blame_gutter;
23096
23097        if self.show_git_blame_gutter && !self.has_blame_entries(cx) {
23098            self.start_git_blame(true, window, cx);
23099        }
23100
23101        cx.notify();
23102    }
23103
23104    pub fn toggle_git_blame_inline(
23105        &mut self,
23106        _: &ToggleGitBlameInline,
23107        window: &mut Window,
23108        cx: &mut Context<Self>,
23109    ) {
23110        self.toggle_git_blame_inline_internal(true, window, cx);
23111        cx.notify();
23112    }
23113
23114    pub fn open_git_blame_commit(
23115        &mut self,
23116        _: &OpenGitBlameCommit,
23117        window: &mut Window,
23118        cx: &mut Context<Self>,
23119    ) {
23120        self.open_git_blame_commit_internal(window, cx);
23121    }
23122
23123    fn open_git_blame_commit_internal(
23124        &mut self,
23125        window: &mut Window,
23126        cx: &mut Context<Self>,
23127    ) -> Option<()> {
23128        let blame = self.blame.as_ref()?;
23129        let snapshot = self.snapshot(window, cx);
23130        let cursor = self
23131            .selections
23132            .newest::<Point>(&snapshot.display_snapshot)
23133            .head();
23134        let (buffer, point, _) = snapshot.buffer_snapshot().point_to_buffer_point(cursor)?;
23135        let (_, blame_entry) = blame
23136            .update(cx, |blame, cx| {
23137                blame
23138                    .blame_for_rows(
23139                        &[RowInfo {
23140                            buffer_id: Some(buffer.remote_id()),
23141                            buffer_row: Some(point.row),
23142                            ..Default::default()
23143                        }],
23144                        cx,
23145                    )
23146                    .next()
23147            })
23148            .flatten()?;
23149        let renderer = cx.global::<GlobalBlameRenderer>().0.clone();
23150        let repo = blame.read(cx).repository(cx, buffer.remote_id())?;
23151        let workspace = self.workspace()?.downgrade();
23152        renderer.open_blame_commit(blame_entry, repo, workspace, window, cx);
23153        None
23154    }
23155
23156    pub fn git_blame_inline_enabled(&self) -> bool {
23157        self.git_blame_inline_enabled
23158    }
23159
23160    pub fn toggle_selection_menu(
23161        &mut self,
23162        _: &ToggleSelectionMenu,
23163        _: &mut Window,
23164        cx: &mut Context<Self>,
23165    ) {
23166        self.show_selection_menu = self
23167            .show_selection_menu
23168            .map(|show_selections_menu| !show_selections_menu)
23169            .or_else(|| Some(!EditorSettings::get_global(cx).toolbar.selections_menu));
23170
23171        cx.notify();
23172    }
23173
23174    pub fn selection_menu_enabled(&self, cx: &App) -> bool {
23175        self.show_selection_menu
23176            .unwrap_or_else(|| EditorSettings::get_global(cx).toolbar.selections_menu)
23177    }
23178
23179    fn start_git_blame(
23180        &mut self,
23181        user_triggered: bool,
23182        window: &mut Window,
23183        cx: &mut Context<Self>,
23184    ) {
23185        if let Some(project) = self.project() {
23186            if let Some(buffer) = self.buffer().read(cx).as_singleton()
23187                && buffer.read(cx).file().is_none()
23188            {
23189                return;
23190            }
23191
23192            let focused = self.focus_handle(cx).contains_focused(window, cx);
23193
23194            let project = project.clone();
23195            let blame = cx
23196                .new(|cx| GitBlame::new(self.buffer.clone(), project, user_triggered, focused, cx));
23197            self.blame_subscription =
23198                Some(cx.observe_in(&blame, window, |_, _, _, cx| cx.notify()));
23199            self.blame = Some(blame);
23200        }
23201    }
23202
23203    fn toggle_git_blame_inline_internal(
23204        &mut self,
23205        user_triggered: bool,
23206        window: &mut Window,
23207        cx: &mut Context<Self>,
23208    ) {
23209        if self.git_blame_inline_enabled {
23210            self.git_blame_inline_enabled = false;
23211            self.show_git_blame_inline = false;
23212            self.show_git_blame_inline_delay_task.take();
23213        } else {
23214            self.git_blame_inline_enabled = true;
23215            self.start_git_blame_inline(user_triggered, window, cx);
23216        }
23217
23218        cx.notify();
23219    }
23220
23221    fn start_git_blame_inline(
23222        &mut self,
23223        user_triggered: bool,
23224        window: &mut Window,
23225        cx: &mut Context<Self>,
23226    ) {
23227        self.start_git_blame(user_triggered, window, cx);
23228
23229        if ProjectSettings::get_global(cx)
23230            .git
23231            .inline_blame_delay()
23232            .is_some()
23233        {
23234            self.start_inline_blame_timer(window, cx);
23235        } else {
23236            self.show_git_blame_inline = true
23237        }
23238    }
23239
23240    pub fn blame(&self) -> Option<&Entity<GitBlame>> {
23241        self.blame.as_ref()
23242    }
23243
23244    pub fn show_git_blame_gutter(&self) -> bool {
23245        self.show_git_blame_gutter
23246    }
23247
23248    pub fn render_git_blame_gutter(&self, cx: &App) -> bool {
23249        !self.mode().is_minimap() && self.show_git_blame_gutter && self.has_blame_entries(cx)
23250    }
23251
23252    pub fn render_git_blame_inline(&self, window: &Window, cx: &App) -> bool {
23253        self.show_git_blame_inline
23254            && (self.focus_handle.is_focused(window) || self.inline_blame_popover.is_some())
23255            && !self.newest_selection_head_on_empty_line(cx)
23256            && self.has_blame_entries(cx)
23257    }
23258
23259    fn has_blame_entries(&self, cx: &App) -> bool {
23260        self.blame()
23261            .is_some_and(|blame| blame.read(cx).has_generated_entries())
23262    }
23263
23264    fn newest_selection_head_on_empty_line(&self, cx: &App) -> bool {
23265        let cursor_anchor = self.selections.newest_anchor().head();
23266
23267        let snapshot = self.buffer.read(cx).snapshot(cx);
23268        let buffer_row = MultiBufferRow(cursor_anchor.to_point(&snapshot).row);
23269
23270        snapshot.line_len(buffer_row) == 0
23271    }
23272
23273    fn get_permalink_to_line(&self, cx: &mut Context<Self>) -> Task<Result<url::Url>> {
23274        let buffer_and_selection = maybe!({
23275            let selection = self.selections.newest::<Point>(&self.display_snapshot(cx));
23276            let selection_range = selection.range();
23277
23278            let multi_buffer = self.buffer().read(cx);
23279            let multi_buffer_snapshot = multi_buffer.snapshot(cx);
23280            let buffer_ranges = multi_buffer_snapshot
23281                .range_to_buffer_ranges(selection_range.start..=selection_range.end);
23282
23283            let (buffer, range, _) = if selection.reversed {
23284                buffer_ranges.first()
23285            } else {
23286                buffer_ranges.last()
23287            }?;
23288
23289            let buffer_range = range.to_point(buffer);
23290
23291            let Some(buffer_diff) = multi_buffer.diff_for(buffer.remote_id()) else {
23292                return Some((
23293                    multi_buffer.buffer(buffer.remote_id()).unwrap(),
23294                    buffer_range.start.row..buffer_range.end.row,
23295                ));
23296            };
23297
23298            let buffer_diff_snapshot = buffer_diff.read(cx).snapshot(cx);
23299            let start =
23300                buffer_diff_snapshot.buffer_point_to_base_text_point(buffer_range.start, buffer);
23301            let end =
23302                buffer_diff_snapshot.buffer_point_to_base_text_point(buffer_range.end, buffer);
23303
23304            Some((
23305                multi_buffer.buffer(buffer.remote_id()).unwrap(),
23306                start.row..end.row,
23307            ))
23308        });
23309
23310        let Some((buffer, selection)) = buffer_and_selection else {
23311            return Task::ready(Err(anyhow!("failed to determine buffer and selection")));
23312        };
23313
23314        let Some(project) = self.project() else {
23315            return Task::ready(Err(anyhow!("editor does not have project")));
23316        };
23317
23318        project.update(cx, |project, cx| {
23319            project.get_permalink_to_line(&buffer, selection, cx)
23320        })
23321    }
23322
23323    pub fn copy_permalink_to_line(
23324        &mut self,
23325        _: &CopyPermalinkToLine,
23326        window: &mut Window,
23327        cx: &mut Context<Self>,
23328    ) {
23329        let permalink_task = self.get_permalink_to_line(cx);
23330        let workspace = self.workspace();
23331
23332        cx.spawn_in(window, async move |_, cx| match permalink_task.await {
23333            Ok(permalink) => {
23334                cx.update(|_, cx| {
23335                    cx.write_to_clipboard(ClipboardItem::new_string(permalink.to_string()));
23336                })
23337                .ok();
23338            }
23339            Err(err) => {
23340                let message = format!("Failed to copy permalink: {err}");
23341
23342                anyhow::Result::<()>::Err(err).log_err();
23343
23344                if let Some(workspace) = workspace {
23345                    workspace
23346                        .update_in(cx, |workspace, _, cx| {
23347                            struct CopyPermalinkToLine;
23348
23349                            workspace.show_toast(
23350                                Toast::new(
23351                                    NotificationId::unique::<CopyPermalinkToLine>(),
23352                                    message,
23353                                ),
23354                                cx,
23355                            )
23356                        })
23357                        .ok();
23358                }
23359            }
23360        })
23361        .detach();
23362    }
23363
23364    pub fn copy_file_location(
23365        &mut self,
23366        _: &CopyFileLocation,
23367        _: &mut Window,
23368        cx: &mut Context<Self>,
23369    ) {
23370        let selection = self.selections.newest::<Point>(&self.display_snapshot(cx));
23371
23372        let start_line = selection.start.row + 1;
23373        let end_line = selection.end.row + 1;
23374
23375        let end_line = if selection.end.column == 0 && end_line > start_line {
23376            end_line - 1
23377        } else {
23378            end_line
23379        };
23380
23381        if let Some(file_location) = self.active_excerpt(cx).and_then(|(_, buffer, _)| {
23382            let project = self.project()?.read(cx);
23383            let file = buffer.read(cx).file()?;
23384            let path = file.path().display(project.path_style(cx));
23385
23386            let location = if start_line == end_line {
23387                format!("{path}:{start_line}")
23388            } else {
23389                format!("{path}:{start_line}-{end_line}")
23390            };
23391            Some(location)
23392        }) {
23393            cx.write_to_clipboard(ClipboardItem::new_string(file_location));
23394        }
23395    }
23396
23397    pub fn open_permalink_to_line(
23398        &mut self,
23399        _: &OpenPermalinkToLine,
23400        window: &mut Window,
23401        cx: &mut Context<Self>,
23402    ) {
23403        let permalink_task = self.get_permalink_to_line(cx);
23404        let workspace = self.workspace();
23405
23406        cx.spawn_in(window, async move |_, cx| match permalink_task.await {
23407            Ok(permalink) => {
23408                cx.update(|_, cx| {
23409                    cx.open_url(permalink.as_ref());
23410                })
23411                .ok();
23412            }
23413            Err(err) => {
23414                let message = format!("Failed to open permalink: {err}");
23415
23416                anyhow::Result::<()>::Err(err).log_err();
23417
23418                if let Some(workspace) = workspace {
23419                    workspace.update(cx, |workspace, cx| {
23420                        struct OpenPermalinkToLine;
23421
23422                        workspace.show_toast(
23423                            Toast::new(NotificationId::unique::<OpenPermalinkToLine>(), message),
23424                            cx,
23425                        )
23426                    });
23427                }
23428            }
23429        })
23430        .detach();
23431    }
23432
23433    pub fn insert_uuid_v4(
23434        &mut self,
23435        _: &InsertUuidV4,
23436        window: &mut Window,
23437        cx: &mut Context<Self>,
23438    ) {
23439        self.insert_uuid(UuidVersion::V4, window, cx);
23440    }
23441
23442    pub fn insert_uuid_v7(
23443        &mut self,
23444        _: &InsertUuidV7,
23445        window: &mut Window,
23446        cx: &mut Context<Self>,
23447    ) {
23448        self.insert_uuid(UuidVersion::V7, window, cx);
23449    }
23450
23451    fn insert_uuid(&mut self, version: UuidVersion, window: &mut Window, cx: &mut Context<Self>) {
23452        self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
23453        self.transact(window, cx, |this, window, cx| {
23454            let edits = this
23455                .selections
23456                .all::<Point>(&this.display_snapshot(cx))
23457                .into_iter()
23458                .map(|selection| {
23459                    let uuid = match version {
23460                        UuidVersion::V4 => uuid::Uuid::new_v4(),
23461                        UuidVersion::V7 => uuid::Uuid::now_v7(),
23462                    };
23463
23464                    (selection.range(), uuid.to_string())
23465                });
23466            this.edit(edits, cx);
23467            this.refresh_edit_prediction(true, false, window, cx);
23468        });
23469    }
23470
23471    pub fn open_selections_in_multibuffer(
23472        &mut self,
23473        _: &OpenSelectionsInMultibuffer,
23474        window: &mut Window,
23475        cx: &mut Context<Self>,
23476    ) {
23477        let multibuffer = self.buffer.read(cx);
23478
23479        let Some(buffer) = multibuffer.as_singleton() else {
23480            return;
23481        };
23482
23483        let Some(workspace) = self.workspace() else {
23484            return;
23485        };
23486
23487        let title = multibuffer.title(cx).to_string();
23488
23489        let locations = self
23490            .selections
23491            .all_anchors(&self.display_snapshot(cx))
23492            .iter()
23493            .map(|selection| {
23494                (
23495                    buffer.clone(),
23496                    (selection.start.text_anchor..selection.end.text_anchor)
23497                        .to_point(buffer.read(cx)),
23498                )
23499            })
23500            .into_group_map();
23501
23502        cx.spawn_in(window, async move |_, cx| {
23503            workspace.update_in(cx, |workspace, window, cx| {
23504                Self::open_locations_in_multibuffer(
23505                    workspace,
23506                    locations,
23507                    format!("Selections for '{title}'"),
23508                    false,
23509                    false,
23510                    MultibufferSelectionMode::All,
23511                    window,
23512                    cx,
23513                );
23514            })
23515        })
23516        .detach();
23517    }
23518
23519    /// Adds a row highlight for the given range. If a row has multiple highlights, the
23520    /// last highlight added will be used.
23521    ///
23522    /// If the range ends at the beginning of a line, then that line will not be highlighted.
23523    pub fn highlight_rows<T: 'static>(
23524        &mut self,
23525        range: Range<Anchor>,
23526        color: Hsla,
23527        options: RowHighlightOptions,
23528        cx: &mut Context<Self>,
23529    ) {
23530        let snapshot = self.buffer().read(cx).snapshot(cx);
23531        let row_highlights = self.highlighted_rows.entry(TypeId::of::<T>()).or_default();
23532        let ix = row_highlights.binary_search_by(|highlight| {
23533            Ordering::Equal
23534                .then_with(|| highlight.range.start.cmp(&range.start, &snapshot))
23535                .then_with(|| highlight.range.end.cmp(&range.end, &snapshot))
23536        });
23537
23538        if let Err(mut ix) = ix {
23539            let index = post_inc(&mut self.highlight_order);
23540
23541            // If this range intersects with the preceding highlight, then merge it with
23542            // the preceding highlight. Otherwise insert a new highlight.
23543            let mut merged = false;
23544            if ix > 0 {
23545                let prev_highlight = &mut row_highlights[ix - 1];
23546                if prev_highlight
23547                    .range
23548                    .end
23549                    .cmp(&range.start, &snapshot)
23550                    .is_ge()
23551                {
23552                    ix -= 1;
23553                    if prev_highlight.range.end.cmp(&range.end, &snapshot).is_lt() {
23554                        prev_highlight.range.end = range.end;
23555                    }
23556                    merged = true;
23557                    prev_highlight.index = index;
23558                    prev_highlight.color = color;
23559                    prev_highlight.options = options;
23560                }
23561            }
23562
23563            if !merged {
23564                row_highlights.insert(
23565                    ix,
23566                    RowHighlight {
23567                        range,
23568                        index,
23569                        color,
23570                        options,
23571                        type_id: TypeId::of::<T>(),
23572                    },
23573                );
23574            }
23575
23576            // If any of the following highlights intersect with this one, merge them.
23577            while let Some(next_highlight) = row_highlights.get(ix + 1) {
23578                let highlight = &row_highlights[ix];
23579                if next_highlight
23580                    .range
23581                    .start
23582                    .cmp(&highlight.range.end, &snapshot)
23583                    .is_le()
23584                {
23585                    if next_highlight
23586                        .range
23587                        .end
23588                        .cmp(&highlight.range.end, &snapshot)
23589                        .is_gt()
23590                    {
23591                        row_highlights[ix].range.end = next_highlight.range.end;
23592                    }
23593                    row_highlights.remove(ix + 1);
23594                } else {
23595                    break;
23596                }
23597            }
23598        }
23599    }
23600
23601    /// Remove any highlighted row ranges of the given type that intersect the
23602    /// given ranges.
23603    pub fn remove_highlighted_rows<T: 'static>(
23604        &mut self,
23605        ranges_to_remove: Vec<Range<Anchor>>,
23606        cx: &mut Context<Self>,
23607    ) {
23608        let snapshot = self.buffer().read(cx).snapshot(cx);
23609        let row_highlights = self.highlighted_rows.entry(TypeId::of::<T>()).or_default();
23610        let mut ranges_to_remove = ranges_to_remove.iter().peekable();
23611        row_highlights.retain(|highlight| {
23612            while let Some(range_to_remove) = ranges_to_remove.peek() {
23613                match range_to_remove.end.cmp(&highlight.range.start, &snapshot) {
23614                    Ordering::Less | Ordering::Equal => {
23615                        ranges_to_remove.next();
23616                    }
23617                    Ordering::Greater => {
23618                        match range_to_remove.start.cmp(&highlight.range.end, &snapshot) {
23619                            Ordering::Less | Ordering::Equal => {
23620                                return false;
23621                            }
23622                            Ordering::Greater => break,
23623                        }
23624                    }
23625                }
23626            }
23627
23628            true
23629        })
23630    }
23631
23632    /// Clear all anchor ranges for a certain highlight context type, so no corresponding rows will be highlighted.
23633    pub fn clear_row_highlights<T: 'static>(&mut self) {
23634        self.highlighted_rows.remove(&TypeId::of::<T>());
23635    }
23636
23637    /// For a highlight given context type, gets all anchor ranges that will be used for row highlighting.
23638    pub fn highlighted_rows<T: 'static>(&self) -> impl '_ + Iterator<Item = (Range<Anchor>, Hsla)> {
23639        self.highlighted_rows
23640            .get(&TypeId::of::<T>())
23641            .map_or(&[] as &[_], |vec| vec.as_slice())
23642            .iter()
23643            .map(|highlight| (highlight.range.clone(), highlight.color))
23644    }
23645
23646    /// Merges all anchor ranges for all context types ever set, picking the last highlight added in case of a row conflict.
23647    /// Returns a map of display rows that are highlighted and their corresponding highlight color.
23648    /// Allows to ignore certain kinds of highlights.
23649    pub fn highlighted_display_rows(
23650        &self,
23651        window: &mut Window,
23652        cx: &mut App,
23653    ) -> BTreeMap<DisplayRow, LineHighlight> {
23654        let snapshot = self.snapshot(window, cx);
23655        let mut used_highlight_orders = HashMap::default();
23656        self.highlighted_rows
23657            .iter()
23658            .flat_map(|(_, highlighted_rows)| highlighted_rows.iter())
23659            .fold(
23660                BTreeMap::<DisplayRow, LineHighlight>::new(),
23661                |mut unique_rows, highlight| {
23662                    let start = highlight.range.start.to_display_point(&snapshot);
23663                    let end = highlight.range.end.to_display_point(&snapshot);
23664                    let start_row = start.row().0;
23665                    let end_row = if !highlight.range.end.text_anchor.is_max() && end.column() == 0
23666                    {
23667                        end.row().0.saturating_sub(1)
23668                    } else {
23669                        end.row().0
23670                    };
23671                    for row in start_row..=end_row {
23672                        let used_index =
23673                            used_highlight_orders.entry(row).or_insert(highlight.index);
23674                        if highlight.index >= *used_index {
23675                            *used_index = highlight.index;
23676                            unique_rows.insert(
23677                                DisplayRow(row),
23678                                LineHighlight {
23679                                    include_gutter: highlight.options.include_gutter,
23680                                    border: None,
23681                                    background: highlight.color.into(),
23682                                    type_id: Some(highlight.type_id),
23683                                },
23684                            );
23685                        }
23686                    }
23687                    unique_rows
23688                },
23689            )
23690    }
23691
23692    pub fn highlighted_display_row_for_autoscroll(
23693        &self,
23694        snapshot: &DisplaySnapshot,
23695    ) -> Option<DisplayRow> {
23696        self.highlighted_rows
23697            .values()
23698            .flat_map(|highlighted_rows| highlighted_rows.iter())
23699            .filter_map(|highlight| {
23700                if highlight.options.autoscroll {
23701                    Some(highlight.range.start.to_display_point(snapshot).row())
23702                } else {
23703                    None
23704                }
23705            })
23706            .min()
23707    }
23708
23709    pub fn set_search_within_ranges(&mut self, ranges: &[Range<Anchor>], cx: &mut Context<Self>) {
23710        self.highlight_background(
23711            HighlightKey::SearchWithinRange,
23712            ranges,
23713            |_, colors| colors.colors().editor_document_highlight_read_background,
23714            cx,
23715        )
23716    }
23717
23718    pub fn set_breadcrumb_header(&mut self, new_header: String) {
23719        self.breadcrumb_header = Some(new_header);
23720    }
23721
23722    pub fn clear_search_within_ranges(&mut self, cx: &mut Context<Self>) {
23723        self.clear_background_highlights(HighlightKey::SearchWithinRange, cx);
23724    }
23725
23726    pub fn highlight_background(
23727        &mut self,
23728        key: HighlightKey,
23729        ranges: &[Range<Anchor>],
23730        color_fetcher: impl Fn(&usize, &Theme) -> Hsla + Send + Sync + 'static,
23731        cx: &mut Context<Self>,
23732    ) {
23733        self.background_highlights
23734            .insert(key, (Arc::new(color_fetcher), Arc::from(ranges)));
23735        self.scrollbar_marker_state.dirty = true;
23736        cx.notify();
23737    }
23738
23739    pub fn clear_background_highlights(
23740        &mut self,
23741        key: HighlightKey,
23742        cx: &mut Context<Self>,
23743    ) -> Option<BackgroundHighlight> {
23744        let text_highlights = self.background_highlights.remove(&key)?;
23745        if !text_highlights.1.is_empty() {
23746            self.scrollbar_marker_state.dirty = true;
23747            cx.notify();
23748        }
23749        Some(text_highlights)
23750    }
23751
23752    pub fn highlight_gutter<T: 'static>(
23753        &mut self,
23754        ranges: impl Into<Vec<Range<Anchor>>>,
23755        color_fetcher: fn(&App) -> Hsla,
23756        cx: &mut Context<Self>,
23757    ) {
23758        self.gutter_highlights
23759            .insert(TypeId::of::<T>(), (color_fetcher, ranges.into()));
23760        cx.notify();
23761    }
23762
23763    pub fn clear_gutter_highlights<T: 'static>(
23764        &mut self,
23765        cx: &mut Context<Self>,
23766    ) -> Option<GutterHighlight> {
23767        cx.notify();
23768        self.gutter_highlights.remove(&TypeId::of::<T>())
23769    }
23770
23771    pub fn insert_gutter_highlight<T: 'static>(
23772        &mut self,
23773        range: Range<Anchor>,
23774        color_fetcher: fn(&App) -> Hsla,
23775        cx: &mut Context<Self>,
23776    ) {
23777        let snapshot = self.buffer().read(cx).snapshot(cx);
23778        let mut highlights = self
23779            .gutter_highlights
23780            .remove(&TypeId::of::<T>())
23781            .map(|(_, highlights)| highlights)
23782            .unwrap_or_default();
23783        let ix = highlights.binary_search_by(|highlight| {
23784            Ordering::Equal
23785                .then_with(|| highlight.start.cmp(&range.start, &snapshot))
23786                .then_with(|| highlight.end.cmp(&range.end, &snapshot))
23787        });
23788        if let Err(ix) = ix {
23789            highlights.insert(ix, range);
23790        }
23791        self.gutter_highlights
23792            .insert(TypeId::of::<T>(), (color_fetcher, highlights));
23793    }
23794
23795    pub fn remove_gutter_highlights<T: 'static>(
23796        &mut self,
23797        ranges_to_remove: Vec<Range<Anchor>>,
23798        cx: &mut Context<Self>,
23799    ) {
23800        let snapshot = self.buffer().read(cx).snapshot(cx);
23801        let Some((color_fetcher, mut gutter_highlights)) =
23802            self.gutter_highlights.remove(&TypeId::of::<T>())
23803        else {
23804            return;
23805        };
23806        let mut ranges_to_remove = ranges_to_remove.iter().peekable();
23807        gutter_highlights.retain(|highlight| {
23808            while let Some(range_to_remove) = ranges_to_remove.peek() {
23809                match range_to_remove.end.cmp(&highlight.start, &snapshot) {
23810                    Ordering::Less | Ordering::Equal => {
23811                        ranges_to_remove.next();
23812                    }
23813                    Ordering::Greater => {
23814                        match range_to_remove.start.cmp(&highlight.end, &snapshot) {
23815                            Ordering::Less | Ordering::Equal => {
23816                                return false;
23817                            }
23818                            Ordering::Greater => break,
23819                        }
23820                    }
23821                }
23822            }
23823
23824            true
23825        });
23826        self.gutter_highlights
23827            .insert(TypeId::of::<T>(), (color_fetcher, gutter_highlights));
23828    }
23829
23830    #[cfg(any(test, feature = "test-support"))]
23831    pub fn all_text_highlights(
23832        &self,
23833        window: &mut Window,
23834        cx: &mut Context<Self>,
23835    ) -> Vec<(HighlightStyle, Vec<Range<DisplayPoint>>)> {
23836        let snapshot = self.snapshot(window, cx);
23837        self.display_map.update(cx, |display_map, _| {
23838            display_map
23839                .all_text_highlights()
23840                .map(|(_, highlight)| {
23841                    let (style, ranges) = highlight.as_ref();
23842                    (
23843                        *style,
23844                        ranges
23845                            .iter()
23846                            .map(|range| range.clone().to_display_points(&snapshot))
23847                            .collect(),
23848                    )
23849                })
23850                .collect()
23851        })
23852    }
23853
23854    #[cfg(any(test, feature = "test-support"))]
23855    pub fn all_text_background_highlights(
23856        &self,
23857        window: &mut Window,
23858        cx: &mut Context<Self>,
23859    ) -> Vec<(Range<DisplayPoint>, Hsla)> {
23860        let snapshot = self.snapshot(window, cx);
23861        let buffer = &snapshot.buffer_snapshot();
23862        let start = buffer.anchor_before(MultiBufferOffset(0));
23863        let end = buffer.anchor_after(buffer.len());
23864        self.sorted_background_highlights_in_range(start..end, &snapshot, cx.theme())
23865    }
23866
23867    #[cfg(any(test, feature = "test-support"))]
23868    pub fn sorted_background_highlights_in_range(
23869        &self,
23870        search_range: Range<Anchor>,
23871        display_snapshot: &DisplaySnapshot,
23872        theme: &Theme,
23873    ) -> Vec<(Range<DisplayPoint>, Hsla)> {
23874        let mut res = self.background_highlights_in_range(search_range, display_snapshot, theme);
23875        res.sort_by(|a, b| {
23876            a.0.start
23877                .cmp(&b.0.start)
23878                .then_with(|| a.0.end.cmp(&b.0.end))
23879                .then_with(|| a.1.cmp(&b.1))
23880        });
23881        res
23882    }
23883
23884    #[cfg(any(test, feature = "test-support"))]
23885    pub fn search_background_highlights(&mut self, cx: &mut Context<Self>) -> Vec<Range<Point>> {
23886        let snapshot = self.buffer().read(cx).snapshot(cx);
23887
23888        let highlights = self
23889            .background_highlights
23890            .get(&HighlightKey::BufferSearchHighlights);
23891
23892        if let Some((_color, ranges)) = highlights {
23893            ranges
23894                .iter()
23895                .map(|range| range.start.to_point(&snapshot)..range.end.to_point(&snapshot))
23896                .collect_vec()
23897        } else {
23898            vec![]
23899        }
23900    }
23901
23902    fn document_highlights_for_position<'a>(
23903        &'a self,
23904        position: Anchor,
23905        buffer: &'a MultiBufferSnapshot,
23906    ) -> impl 'a + Iterator<Item = &'a Range<Anchor>> {
23907        let read_highlights = self
23908            .background_highlights
23909            .get(&HighlightKey::DocumentHighlightRead)
23910            .map(|h| &h.1);
23911        let write_highlights = self
23912            .background_highlights
23913            .get(&HighlightKey::DocumentHighlightWrite)
23914            .map(|h| &h.1);
23915        let left_position = position.bias_left(buffer);
23916        let right_position = position.bias_right(buffer);
23917        read_highlights
23918            .into_iter()
23919            .chain(write_highlights)
23920            .flat_map(move |ranges| {
23921                let start_ix = match ranges.binary_search_by(|probe| {
23922                    let cmp = probe.end.cmp(&left_position, buffer);
23923                    if cmp.is_ge() {
23924                        Ordering::Greater
23925                    } else {
23926                        Ordering::Less
23927                    }
23928                }) {
23929                    Ok(i) | Err(i) => i,
23930                };
23931
23932                ranges[start_ix..]
23933                    .iter()
23934                    .take_while(move |range| range.start.cmp(&right_position, buffer).is_le())
23935            })
23936    }
23937
23938    pub fn has_background_highlights(&self, key: HighlightKey) -> bool {
23939        self.background_highlights
23940            .get(&key)
23941            .is_some_and(|(_, highlights)| !highlights.is_empty())
23942    }
23943
23944    /// Returns all background highlights for a given range.
23945    ///
23946    /// The order of highlights is not deterministic, do sort the ranges if needed for the logic.
23947    pub fn background_highlights_in_range(
23948        &self,
23949        search_range: Range<Anchor>,
23950        display_snapshot: &DisplaySnapshot,
23951        theme: &Theme,
23952    ) -> Vec<(Range<DisplayPoint>, Hsla)> {
23953        let mut results = Vec::new();
23954        for (color_fetcher, ranges) in self.background_highlights.values() {
23955            let start_ix = match ranges.binary_search_by(|probe| {
23956                let cmp = probe
23957                    .end
23958                    .cmp(&search_range.start, &display_snapshot.buffer_snapshot());
23959                if cmp.is_gt() {
23960                    Ordering::Greater
23961                } else {
23962                    Ordering::Less
23963                }
23964            }) {
23965                Ok(i) | Err(i) => i,
23966            };
23967            for (index, range) in ranges[start_ix..].iter().enumerate() {
23968                if range
23969                    .start
23970                    .cmp(&search_range.end, &display_snapshot.buffer_snapshot())
23971                    .is_ge()
23972                {
23973                    break;
23974                }
23975
23976                let color = color_fetcher(&(start_ix + index), theme);
23977                let start = range.start.to_display_point(display_snapshot);
23978                let end = range.end.to_display_point(display_snapshot);
23979                results.push((start..end, color))
23980            }
23981        }
23982        results
23983    }
23984
23985    pub fn gutter_highlights_in_range(
23986        &self,
23987        search_range: Range<Anchor>,
23988        display_snapshot: &DisplaySnapshot,
23989        cx: &App,
23990    ) -> Vec<(Range<DisplayPoint>, Hsla)> {
23991        let mut results = Vec::new();
23992        for (color_fetcher, ranges) in self.gutter_highlights.values() {
23993            let color = color_fetcher(cx);
23994            let start_ix = match ranges.binary_search_by(|probe| {
23995                let cmp = probe
23996                    .end
23997                    .cmp(&search_range.start, &display_snapshot.buffer_snapshot());
23998                if cmp.is_gt() {
23999                    Ordering::Greater
24000                } else {
24001                    Ordering::Less
24002                }
24003            }) {
24004                Ok(i) | Err(i) => i,
24005            };
24006            for range in &ranges[start_ix..] {
24007                if range
24008                    .start
24009                    .cmp(&search_range.end, &display_snapshot.buffer_snapshot())
24010                    .is_ge()
24011                {
24012                    break;
24013                }
24014
24015                let start = range.start.to_display_point(display_snapshot);
24016                let end = range.end.to_display_point(display_snapshot);
24017                results.push((start..end, color))
24018            }
24019        }
24020        results
24021    }
24022
24023    /// Get the text ranges corresponding to the redaction query
24024    pub fn redacted_ranges(
24025        &self,
24026        search_range: Range<Anchor>,
24027        display_snapshot: &DisplaySnapshot,
24028        cx: &App,
24029    ) -> Vec<Range<DisplayPoint>> {
24030        display_snapshot
24031            .buffer_snapshot()
24032            .redacted_ranges(search_range, |file| {
24033                if let Some(file) = file {
24034                    file.is_private()
24035                        && EditorSettings::get(
24036                            Some(SettingsLocation {
24037                                worktree_id: file.worktree_id(cx),
24038                                path: file.path().as_ref(),
24039                            }),
24040                            cx,
24041                        )
24042                        .redact_private_values
24043                } else {
24044                    false
24045                }
24046            })
24047            .map(|range| {
24048                range.start.to_display_point(display_snapshot)
24049                    ..range.end.to_display_point(display_snapshot)
24050            })
24051            .collect()
24052    }
24053
24054    pub fn highlight_text_key(
24055        &mut self,
24056        key: HighlightKey,
24057        ranges: Vec<Range<Anchor>>,
24058        style: HighlightStyle,
24059        merge: bool,
24060        cx: &mut Context<Self>,
24061    ) {
24062        self.display_map.update(cx, |map, cx| {
24063            map.highlight_text(key, ranges, style, merge, cx);
24064        });
24065        cx.notify();
24066    }
24067
24068    pub fn highlight_text(
24069        &mut self,
24070        key: HighlightKey,
24071        ranges: Vec<Range<Anchor>>,
24072        style: HighlightStyle,
24073        cx: &mut Context<Self>,
24074    ) {
24075        self.display_map.update(cx, |map, cx| {
24076            map.highlight_text(key, ranges, style, false, cx)
24077        });
24078        cx.notify();
24079    }
24080
24081    pub fn text_highlights<'a>(
24082        &'a self,
24083        key: HighlightKey,
24084        cx: &'a App,
24085    ) -> Option<(HighlightStyle, &'a [Range<Anchor>])> {
24086        self.display_map.read(cx).text_highlights(key)
24087    }
24088
24089    pub fn clear_highlights(&mut self, key: HighlightKey, cx: &mut Context<Self>) {
24090        let cleared = self
24091            .display_map
24092            .update(cx, |map, _| map.clear_highlights(key));
24093        if cleared {
24094            cx.notify();
24095        }
24096    }
24097
24098    pub fn clear_highlights_with(
24099        &mut self,
24100        f: &mut dyn FnMut(&HighlightKey) -> bool,
24101        cx: &mut Context<Self>,
24102    ) {
24103        let cleared = self
24104            .display_map
24105            .update(cx, |map, _| map.clear_highlights_with(f));
24106        if cleared {
24107            cx.notify();
24108        }
24109    }
24110
24111    pub fn show_local_cursors(&self, window: &mut Window, cx: &mut App) -> bool {
24112        (self.read_only(cx) || self.blink_manager.read(cx).visible())
24113            && self.focus_handle.is_focused(window)
24114    }
24115
24116    pub fn set_show_cursor_when_unfocused(&mut self, is_enabled: bool, cx: &mut Context<Self>) {
24117        self.show_cursor_when_unfocused = is_enabled;
24118        cx.notify();
24119    }
24120
24121    fn on_buffer_changed(&mut self, _: Entity<MultiBuffer>, cx: &mut Context<Self>) {
24122        cx.notify();
24123    }
24124
24125    fn on_debug_session_event(
24126        &mut self,
24127        _session: Entity<Session>,
24128        event: &SessionEvent,
24129        cx: &mut Context<Self>,
24130    ) {
24131        if let SessionEvent::InvalidateInlineValue = event {
24132            self.refresh_inline_values(cx);
24133        }
24134    }
24135
24136    pub fn refresh_inline_values(&mut self, cx: &mut Context<Self>) {
24137        let Some(semantics) = self.semantics_provider.clone() else {
24138            return;
24139        };
24140
24141        if !self.inline_value_cache.enabled {
24142            let inlays = std::mem::take(&mut self.inline_value_cache.inlays);
24143            self.splice_inlays(&inlays, Vec::new(), cx);
24144            return;
24145        }
24146
24147        let current_execution_position = self
24148            .highlighted_rows
24149            .get(&TypeId::of::<ActiveDebugLine>())
24150            .and_then(|lines| lines.last().map(|line| line.range.end));
24151
24152        self.inline_value_cache.refresh_task = cx.spawn(async move |editor, cx| {
24153            let inline_values = editor
24154                .update(cx, |editor, cx| {
24155                    let Some(current_execution_position) = current_execution_position else {
24156                        return Some(Task::ready(Ok(Vec::new())));
24157                    };
24158
24159                    let buffer = editor.buffer.read_with(cx, |buffer, cx| {
24160                        let snapshot = buffer.snapshot(cx);
24161
24162                        let excerpt = snapshot.excerpt_containing(
24163                            current_execution_position..current_execution_position,
24164                        )?;
24165
24166                        editor.buffer.read(cx).buffer(excerpt.buffer_id())
24167                    })?;
24168
24169                    if current_execution_position
24170                        .text_anchor
24171                        .buffer_id
24172                        .is_some_and(|id| id != buffer.read(cx).remote_id())
24173                    {
24174                        return Some(Task::ready(Ok(Vec::new())));
24175                    }
24176
24177                    let range =
24178                        buffer.read(cx).anchor_before(0)..current_execution_position.text_anchor;
24179
24180                    semantics.inline_values(buffer, range, cx)
24181                })
24182                .ok()
24183                .flatten()?
24184                .await
24185                .context("refreshing debugger inlays")
24186                .log_err()?;
24187
24188            let mut buffer_inline_values: HashMap<BufferId, Vec<InlayHint>> = HashMap::default();
24189
24190            for (buffer_id, inline_value) in inline_values
24191                .into_iter()
24192                .filter_map(|hint| Some((hint.position.buffer_id?, hint)))
24193            {
24194                buffer_inline_values
24195                    .entry(buffer_id)
24196                    .or_default()
24197                    .push(inline_value);
24198            }
24199
24200            editor
24201                .update(cx, |editor, cx| {
24202                    let snapshot = editor.buffer.read(cx).snapshot(cx);
24203                    let mut new_inlays = Vec::default();
24204
24205                    for (excerpt_id, buffer_snapshot, _) in snapshot.excerpts() {
24206                        let buffer_id = buffer_snapshot.remote_id();
24207                        buffer_inline_values
24208                            .get(&buffer_id)
24209                            .into_iter()
24210                            .flatten()
24211                            .for_each(|hint| {
24212                                let inlay = Inlay::debugger(
24213                                    post_inc(&mut editor.next_inlay_id),
24214                                    Anchor::in_buffer(excerpt_id, hint.position),
24215                                    hint.text(),
24216                                );
24217                                if !inlay.text().chars().contains(&'\n') {
24218                                    new_inlays.push(inlay);
24219                                }
24220                            });
24221                    }
24222
24223                    let mut inlay_ids = new_inlays.iter().map(|inlay| inlay.id).collect();
24224                    std::mem::swap(&mut editor.inline_value_cache.inlays, &mut inlay_ids);
24225
24226                    editor.splice_inlays(&inlay_ids, new_inlays, cx);
24227                })
24228                .ok()?;
24229            Some(())
24230        });
24231    }
24232
24233    fn on_buffer_event(
24234        &mut self,
24235        multibuffer: &Entity<MultiBuffer>,
24236        event: &multi_buffer::Event,
24237        window: &mut Window,
24238        cx: &mut Context<Self>,
24239    ) {
24240        match event {
24241            multi_buffer::Event::Edited {
24242                edited_buffer,
24243                is_local,
24244            } => {
24245                self.scrollbar_marker_state.dirty = true;
24246                self.active_indent_guides_state.dirty = true;
24247                self.refresh_active_diagnostics(cx);
24248                self.refresh_code_actions(window, cx);
24249                self.refresh_single_line_folds(window, cx);
24250                let snapshot = self.snapshot(window, cx);
24251                self.refresh_matching_bracket_highlights(&snapshot, cx);
24252                self.refresh_outline_symbols_at_cursor(cx);
24253                self.refresh_sticky_headers(&snapshot, cx);
24254                if *is_local && self.has_active_edit_prediction() {
24255                    self.update_visible_edit_prediction(window, cx);
24256                }
24257
24258                // Clean up orphaned review comments after edits
24259                self.cleanup_orphaned_review_comments(cx);
24260
24261                if let Some(buffer) = edited_buffer {
24262                    if buffer.read(cx).file().is_none() {
24263                        cx.emit(EditorEvent::TitleChanged);
24264                    }
24265
24266                    if self.project.is_some() {
24267                        let buffer_id = buffer.read(cx).remote_id();
24268                        self.register_buffer(buffer_id, cx);
24269                        self.update_lsp_data(Some(buffer_id), window, cx);
24270                        self.refresh_inlay_hints(
24271                            InlayHintRefreshReason::BufferEdited(buffer_id),
24272                            cx,
24273                        );
24274                    }
24275                }
24276
24277                cx.emit(EditorEvent::BufferEdited);
24278                cx.emit(SearchEvent::MatchesInvalidated);
24279
24280                let Some(project) = &self.project else { return };
24281                let (telemetry, is_via_ssh) = {
24282                    let project = project.read(cx);
24283                    let telemetry = project.client().telemetry().clone();
24284                    let is_via_ssh = project.is_via_remote_server();
24285                    (telemetry, is_via_ssh)
24286                };
24287                telemetry.log_edit_event("editor", is_via_ssh);
24288            }
24289            multi_buffer::Event::ExcerptsAdded {
24290                buffer,
24291                predecessor,
24292                excerpts,
24293            } => {
24294                let buffer_id = buffer.read(cx).remote_id();
24295                if self.buffer.read(cx).diff_for(buffer_id).is_none()
24296                    && let Some(project) = &self.project
24297                {
24298                    update_uncommitted_diff_for_buffer(
24299                        cx.entity(),
24300                        project,
24301                        [buffer.clone()],
24302                        self.buffer.clone(),
24303                        cx,
24304                    )
24305                    .detach();
24306                }
24307                self.semantic_token_state
24308                    .invalidate_buffer(&buffer.read(cx).remote_id());
24309                self.update_lsp_data(Some(buffer_id), window, cx);
24310                self.refresh_inlay_hints(InlayHintRefreshReason::NewLinesShown, cx);
24311                self.refresh_runnables(None, window, cx);
24312                self.colorize_brackets(false, cx);
24313                self.refresh_selected_text_highlights(&self.display_snapshot(cx), true, window, cx);
24314                cx.emit(EditorEvent::ExcerptsAdded {
24315                    buffer: buffer.clone(),
24316                    predecessor: *predecessor,
24317                    excerpts: excerpts.clone(),
24318                });
24319            }
24320            multi_buffer::Event::ExcerptsRemoved {
24321                ids,
24322                removed_buffer_ids,
24323            } => {
24324                if let Some(inlay_hints) = &mut self.inlay_hints {
24325                    inlay_hints.remove_inlay_chunk_data(removed_buffer_ids);
24326                }
24327                self.refresh_inlay_hints(InlayHintRefreshReason::ExcerptsRemoved(ids.clone()), cx);
24328                for buffer_id in removed_buffer_ids {
24329                    self.registered_buffers.remove(buffer_id);
24330                    self.clear_runnables(Some(*buffer_id));
24331                    self.semantic_token_state.invalidate_buffer(buffer_id);
24332                    self.display_map.update(cx, |display_map, cx| {
24333                        display_map.invalidate_semantic_highlights(*buffer_id);
24334                        display_map.clear_lsp_folding_ranges(*buffer_id, cx);
24335                    });
24336                }
24337
24338                self.display_map.update(cx, |display_map, cx| {
24339                    display_map.unfold_buffers(removed_buffer_ids.iter().copied(), cx);
24340                });
24341
24342                jsx_tag_auto_close::refresh_enabled_in_any_buffer(self, multibuffer, cx);
24343                cx.emit(EditorEvent::ExcerptsRemoved {
24344                    ids: ids.clone(),
24345                    removed_buffer_ids: removed_buffer_ids.clone(),
24346                });
24347            }
24348            multi_buffer::Event::ExcerptsEdited {
24349                excerpt_ids,
24350                buffer_ids,
24351            } => {
24352                self.display_map.update(cx, |map, cx| {
24353                    map.unfold_buffers(buffer_ids.iter().copied(), cx)
24354                });
24355                cx.emit(EditorEvent::ExcerptsEdited {
24356                    ids: excerpt_ids.clone(),
24357                });
24358            }
24359            multi_buffer::Event::ExcerptsExpanded { ids } => {
24360                self.refresh_inlay_hints(InlayHintRefreshReason::NewLinesShown, cx);
24361                self.refresh_document_highlights(cx);
24362                let snapshot = multibuffer.read(cx).snapshot(cx);
24363                for id in ids {
24364                    self.bracket_fetched_tree_sitter_chunks.remove(id);
24365                    if let Some(buffer) = snapshot.buffer_for_excerpt(*id) {
24366                        self.semantic_token_state
24367                            .invalidate_buffer(&buffer.remote_id());
24368                    }
24369                }
24370                self.colorize_brackets(false, cx);
24371                self.update_lsp_data(None, window, cx);
24372                self.refresh_runnables(None, window, cx);
24373                cx.emit(EditorEvent::ExcerptsExpanded { ids: ids.clone() })
24374            }
24375            multi_buffer::Event::Reparsed(buffer_id) => {
24376                self.refresh_runnables(Some(*buffer_id), window, cx);
24377                self.refresh_selected_text_highlights(&self.display_snapshot(cx), true, window, cx);
24378                self.colorize_brackets(true, cx);
24379                jsx_tag_auto_close::refresh_enabled_in_any_buffer(self, multibuffer, cx);
24380
24381                cx.emit(EditorEvent::Reparsed(*buffer_id));
24382            }
24383            multi_buffer::Event::DiffHunksToggled => {
24384                self.refresh_runnables(None, window, cx);
24385            }
24386            multi_buffer::Event::LanguageChanged(buffer_id, is_fresh_language) => {
24387                if !is_fresh_language {
24388                    self.registered_buffers.remove(&buffer_id);
24389                }
24390                jsx_tag_auto_close::refresh_enabled_in_any_buffer(self, multibuffer, cx);
24391                cx.emit(EditorEvent::Reparsed(*buffer_id));
24392                self.update_edit_prediction_settings(cx);
24393                cx.notify();
24394            }
24395            multi_buffer::Event::DirtyChanged => cx.emit(EditorEvent::DirtyChanged),
24396            multi_buffer::Event::Saved => cx.emit(EditorEvent::Saved),
24397            multi_buffer::Event::FileHandleChanged
24398            | multi_buffer::Event::Reloaded
24399            | multi_buffer::Event::BufferDiffChanged => cx.emit(EditorEvent::TitleChanged),
24400            multi_buffer::Event::DiagnosticsUpdated => {
24401                self.update_diagnostics_state(window, cx);
24402            }
24403            _ => {}
24404        };
24405    }
24406
24407    fn update_diagnostics_state(&mut self, window: &mut Window, cx: &mut Context<'_, Editor>) {
24408        if !self.diagnostics_enabled() {
24409            return;
24410        }
24411        self.refresh_active_diagnostics(cx);
24412        self.refresh_inline_diagnostics(true, window, cx);
24413        self.scrollbar_marker_state.dirty = true;
24414        cx.notify();
24415    }
24416
24417    pub fn start_temporary_diff_override(&mut self) {
24418        self.load_diff_task.take();
24419        self.temporary_diff_override = true;
24420    }
24421
24422    pub fn end_temporary_diff_override(&mut self, cx: &mut Context<Self>) {
24423        self.temporary_diff_override = false;
24424        self.set_render_diff_hunk_controls(Arc::new(render_diff_hunk_controls), cx);
24425        self.buffer.update(cx, |buffer, cx| {
24426            buffer.set_all_diff_hunks_collapsed(cx);
24427        });
24428
24429        if let Some(project) = self.project.clone() {
24430            self.load_diff_task = Some(
24431                update_uncommitted_diff_for_buffer(
24432                    cx.entity(),
24433                    &project,
24434                    self.buffer.read(cx).all_buffers(),
24435                    self.buffer.clone(),
24436                    cx,
24437                )
24438                .shared(),
24439            );
24440        }
24441    }
24442
24443    fn on_display_map_changed(
24444        &mut self,
24445        _: Entity<DisplayMap>,
24446        _: &mut Window,
24447        cx: &mut Context<Self>,
24448    ) {
24449        cx.notify();
24450    }
24451
24452    fn fetch_accent_data(&self, cx: &App) -> Option<AccentData> {
24453        if !self.mode.is_full() {
24454            return None;
24455        }
24456
24457        let theme_settings = theme_settings::ThemeSettings::get_global(cx);
24458        let theme = cx.theme();
24459        let accent_colors = theme.accents().clone();
24460
24461        let accent_overrides = theme_settings
24462            .theme_overrides
24463            .get(theme.name.as_ref())
24464            .map(|theme_style| &theme_style.accents)
24465            .into_iter()
24466            .flatten()
24467            .chain(
24468                theme_settings
24469                    .experimental_theme_overrides
24470                    .as_ref()
24471                    .map(|overrides| &overrides.accents)
24472                    .into_iter()
24473                    .flatten(),
24474            )
24475            .flat_map(|accent| accent.0.clone().map(SharedString::from))
24476            .collect();
24477
24478        Some(AccentData {
24479            colors: accent_colors,
24480            overrides: accent_overrides,
24481        })
24482    }
24483
24484    fn fetch_applicable_language_settings(
24485        &self,
24486        cx: &App,
24487    ) -> HashMap<Option<LanguageName>, LanguageSettings> {
24488        if !self.mode.is_full() {
24489            return HashMap::default();
24490        }
24491
24492        self.buffer().read(cx).all_buffers().into_iter().fold(
24493            HashMap::default(),
24494            |mut acc, buffer| {
24495                let buffer = buffer.read(cx);
24496                let language = buffer.language().map(|language| language.name());
24497                if let hash_map::Entry::Vacant(v) = acc.entry(language) {
24498                    v.insert(LanguageSettings::for_buffer(&buffer, cx).into_owned());
24499                }
24500                acc
24501            },
24502        )
24503    }
24504
24505    fn settings_changed(&mut self, window: &mut Window, cx: &mut Context<Self>) {
24506        let new_language_settings = self.fetch_applicable_language_settings(cx);
24507        let language_settings_changed = new_language_settings != self.applicable_language_settings;
24508        self.applicable_language_settings = new_language_settings;
24509
24510        let new_accents = self.fetch_accent_data(cx);
24511        let accents_changed = new_accents != self.accent_data;
24512        self.accent_data = new_accents;
24513
24514        if self.diagnostics_enabled() {
24515            let new_severity = EditorSettings::get_global(cx)
24516                .diagnostics_max_severity
24517                .unwrap_or(DiagnosticSeverity::Hint);
24518            self.set_max_diagnostics_severity(new_severity, cx);
24519        }
24520        self.refresh_runnables(None, window, cx);
24521        self.update_edit_prediction_settings(cx);
24522        self.refresh_edit_prediction(true, false, window, cx);
24523        self.refresh_inline_values(cx);
24524
24525        let old_cursor_shape = self.cursor_shape;
24526        let old_show_breadcrumbs = self.show_breadcrumbs;
24527
24528        {
24529            let editor_settings = EditorSettings::get_global(cx);
24530            self.scroll_manager.vertical_scroll_margin = editor_settings.vertical_scroll_margin;
24531            self.show_breadcrumbs = editor_settings.toolbar.breadcrumbs;
24532            self.cursor_shape = editor_settings.cursor_shape.unwrap_or_default();
24533            self.hide_mouse_mode = editor_settings.hide_mouse.unwrap_or_default();
24534        }
24535
24536        if old_cursor_shape != self.cursor_shape {
24537            cx.emit(EditorEvent::CursorShapeChanged);
24538        }
24539
24540        if old_show_breadcrumbs != self.show_breadcrumbs {
24541            cx.emit(EditorEvent::BreadcrumbsChanged);
24542        }
24543
24544        let (restore_unsaved_buffers, show_inline_diagnostics, inline_blame_enabled) = {
24545            let project_settings = ProjectSettings::get_global(cx);
24546            (
24547                project_settings.session.restore_unsaved_buffers,
24548                project_settings.diagnostics.inline.enabled,
24549                project_settings.git.inline_blame.enabled,
24550            )
24551        };
24552        self.buffer_serialization = self
24553            .should_serialize_buffer()
24554            .then(|| BufferSerialization::new(restore_unsaved_buffers));
24555
24556        if self.mode.is_full() {
24557            if self.show_inline_diagnostics != show_inline_diagnostics {
24558                self.show_inline_diagnostics = show_inline_diagnostics;
24559                self.refresh_inline_diagnostics(false, window, cx);
24560            }
24561
24562            if self.git_blame_inline_enabled != inline_blame_enabled {
24563                self.toggle_git_blame_inline_internal(false, window, cx);
24564            }
24565
24566            let minimap_settings = EditorSettings::get_global(cx).minimap;
24567            if self.minimap_visibility != MinimapVisibility::Disabled {
24568                if self.minimap_visibility.settings_visibility()
24569                    != minimap_settings.minimap_enabled()
24570                {
24571                    self.set_minimap_visibility(
24572                        MinimapVisibility::for_mode(self.mode(), cx),
24573                        window,
24574                        cx,
24575                    );
24576                } else if let Some(minimap_entity) = self.minimap.as_ref() {
24577                    minimap_entity.update(cx, |minimap_editor, cx| {
24578                        minimap_editor.update_minimap_configuration(minimap_settings, cx)
24579                    })
24580                }
24581            }
24582
24583            if language_settings_changed || accents_changed {
24584                self.colorize_brackets(true, cx);
24585            }
24586
24587            if language_settings_changed {
24588                self.clear_disabled_lsp_folding_ranges(window, cx);
24589                self.refresh_document_symbols(None, cx);
24590            }
24591
24592            if let Some(inlay_splice) = self.colors.as_mut().and_then(|colors| {
24593                colors.render_mode_updated(EditorSettings::get_global(cx).lsp_document_colors)
24594            }) {
24595                if !inlay_splice.is_empty() {
24596                    self.splice_inlays(&inlay_splice.to_remove, inlay_splice.to_insert, cx);
24597                }
24598                self.refresh_document_colors(None, window, cx);
24599            }
24600
24601            self.refresh_inlay_hints(
24602                InlayHintRefreshReason::SettingsChange(inlay_hint_settings(
24603                    self.selections.newest_anchor().head(),
24604                    &self.buffer.read(cx).snapshot(cx),
24605                    cx,
24606                )),
24607                cx,
24608            );
24609
24610            let new_semantic_token_rules = ProjectSettings::get_global(cx)
24611                .global_lsp_settings
24612                .semantic_token_rules
24613                .clone();
24614            let semantic_token_rules_changed = self
24615                .semantic_token_state
24616                .update_rules(new_semantic_token_rules);
24617            if language_settings_changed || semantic_token_rules_changed {
24618                self.invalidate_semantic_tokens(None);
24619                self.refresh_semantic_tokens(None, None, cx);
24620            }
24621        }
24622
24623        cx.notify();
24624    }
24625
24626    fn theme_changed(&mut self, _: &mut Window, cx: &mut Context<Self>) {
24627        if !self.mode.is_full() {
24628            return;
24629        }
24630
24631        let new_accents = self.fetch_accent_data(cx);
24632        if new_accents != self.accent_data {
24633            self.accent_data = new_accents;
24634            self.colorize_brackets(true, cx);
24635        }
24636
24637        self.invalidate_semantic_tokens(None);
24638        self.refresh_semantic_tokens(None, None, cx);
24639    }
24640
24641    pub fn set_searchable(&mut self, searchable: bool) {
24642        self.searchable = searchable;
24643    }
24644
24645    pub fn searchable(&self) -> bool {
24646        self.searchable
24647    }
24648
24649    pub fn open_excerpts_in_split(
24650        &mut self,
24651        _: &OpenExcerptsSplit,
24652        window: &mut Window,
24653        cx: &mut Context<Self>,
24654    ) {
24655        self.open_excerpts_common(None, true, window, cx)
24656    }
24657
24658    pub fn open_excerpts(&mut self, _: &OpenExcerpts, window: &mut Window, cx: &mut Context<Self>) {
24659        self.open_excerpts_common(None, false, window, cx)
24660    }
24661
24662    pub(crate) fn open_excerpts_common(
24663        &mut self,
24664        jump_data: Option<JumpData>,
24665        split: bool,
24666        window: &mut Window,
24667        cx: &mut Context<Self>,
24668    ) {
24669        if self.buffer.read(cx).is_singleton() {
24670            cx.propagate();
24671            return;
24672        }
24673
24674        let mut new_selections_by_buffer = HashMap::default();
24675        match &jump_data {
24676            Some(JumpData::MultiBufferPoint {
24677                excerpt_id,
24678                position,
24679                anchor,
24680                line_offset_from_top,
24681            }) => {
24682                let multi_buffer_snapshot = self.buffer.read(cx).snapshot(cx);
24683                if let Some(buffer) = multi_buffer_snapshot
24684                    .buffer_id_for_excerpt(*excerpt_id)
24685                    .and_then(|buffer_id| self.buffer.read(cx).buffer(buffer_id))
24686                {
24687                    let buffer_snapshot = buffer.read(cx).snapshot();
24688                    let jump_to_point = if buffer_snapshot.can_resolve(anchor) {
24689                        language::ToPoint::to_point(anchor, &buffer_snapshot)
24690                    } else {
24691                        buffer_snapshot.clip_point(*position, Bias::Left)
24692                    };
24693                    let jump_to_offset = buffer_snapshot.point_to_offset(jump_to_point);
24694                    new_selections_by_buffer.insert(
24695                        buffer,
24696                        (
24697                            vec![BufferOffset(jump_to_offset)..BufferOffset(jump_to_offset)],
24698                            Some(*line_offset_from_top),
24699                        ),
24700                    );
24701                }
24702            }
24703            Some(JumpData::MultiBufferRow {
24704                row,
24705                line_offset_from_top,
24706            }) => {
24707                let point = MultiBufferPoint::new(row.0, 0);
24708                if let Some((buffer, buffer_point, _)) =
24709                    self.buffer.read(cx).point_to_buffer_point(point, cx)
24710                {
24711                    let buffer_offset = buffer.read(cx).point_to_offset(buffer_point);
24712                    new_selections_by_buffer
24713                        .entry(buffer)
24714                        .or_insert((Vec::new(), Some(*line_offset_from_top)))
24715                        .0
24716                        .push(BufferOffset(buffer_offset)..BufferOffset(buffer_offset))
24717                }
24718            }
24719            None => {
24720                let selections = self
24721                    .selections
24722                    .all::<MultiBufferOffset>(&self.display_snapshot(cx));
24723                let multi_buffer = self.buffer.read(cx);
24724                for selection in selections {
24725                    for (snapshot, range, _, anchor) in multi_buffer
24726                        .snapshot(cx)
24727                        .range_to_buffer_ranges_with_deleted_hunks(selection.range())
24728                    {
24729                        if let Some(anchor) = anchor {
24730                            let Some(buffer_handle) = multi_buffer.buffer_for_anchor(anchor, cx)
24731                            else {
24732                                continue;
24733                            };
24734                            let offset = text::ToOffset::to_offset(
24735                                &anchor.text_anchor,
24736                                &buffer_handle.read(cx).snapshot(),
24737                            );
24738                            let range = BufferOffset(offset)..BufferOffset(offset);
24739                            new_selections_by_buffer
24740                                .entry(buffer_handle)
24741                                .or_insert((Vec::new(), None))
24742                                .0
24743                                .push(range)
24744                        } else {
24745                            let Some(buffer_handle) = multi_buffer.buffer(snapshot.remote_id())
24746                            else {
24747                                continue;
24748                            };
24749                            new_selections_by_buffer
24750                                .entry(buffer_handle)
24751                                .or_insert((Vec::new(), None))
24752                                .0
24753                                .push(range)
24754                        }
24755                    }
24756                }
24757            }
24758        }
24759
24760        if self.delegate_open_excerpts {
24761            let selections_by_buffer: HashMap<_, _> = new_selections_by_buffer
24762                .into_iter()
24763                .map(|(buffer, value)| (buffer.read(cx).remote_id(), value))
24764                .collect();
24765            if !selections_by_buffer.is_empty() {
24766                cx.emit(EditorEvent::OpenExcerptsRequested {
24767                    selections_by_buffer,
24768                    split,
24769                });
24770            }
24771            return;
24772        }
24773
24774        let Some(workspace) = self.workspace() else {
24775            cx.propagate();
24776            return;
24777        };
24778
24779        new_selections_by_buffer
24780            .retain(|buffer, _| buffer.read(cx).file().is_none_or(|file| file.can_open()));
24781
24782        if new_selections_by_buffer.is_empty() {
24783            return;
24784        }
24785
24786        Self::open_buffers_in_workspace(
24787            workspace.downgrade(),
24788            new_selections_by_buffer,
24789            split,
24790            window,
24791            cx,
24792        );
24793    }
24794
24795    pub(crate) fn open_buffers_in_workspace(
24796        workspace: WeakEntity<Workspace>,
24797        new_selections_by_buffer: HashMap<
24798            Entity<language::Buffer>,
24799            (Vec<Range<BufferOffset>>, Option<u32>),
24800        >,
24801        split: bool,
24802        window: &mut Window,
24803        cx: &mut App,
24804    ) {
24805        // We defer the pane interaction because we ourselves are a workspace item
24806        // and activating a new item causes the pane to call a method on us reentrantly,
24807        // which panics if we're on the stack.
24808        window.defer(cx, move |window, cx| {
24809            workspace
24810                .update(cx, |workspace, cx| {
24811                    let pane = if split {
24812                        workspace.adjacent_pane(window, cx)
24813                    } else {
24814                        workspace.active_pane().clone()
24815                    };
24816
24817                    for (buffer, (ranges, scroll_offset)) in new_selections_by_buffer {
24818                        let buffer_read = buffer.read(cx);
24819                        let (has_file, is_project_file) = if let Some(file) = buffer_read.file() {
24820                            (true, project::File::from_dyn(Some(file)).is_some())
24821                        } else {
24822                            (false, false)
24823                        };
24824
24825                        // If project file is none workspace.open_project_item will fail to open the excerpt
24826                        // in a pre existing workspace item if one exists, because Buffer entity_id will be None
24827                        // so we check if there's a tab match in that case first
24828                        let editor = (!has_file || !is_project_file)
24829                            .then(|| {
24830                                // Handle file-less buffers separately: those are not really the project items, so won't have a project path or entity id,
24831                                // so `workspace.open_project_item` will never find them, always opening a new editor.
24832                                // Instead, we try to activate the existing editor in the pane first.
24833                                let (editor, pane_item_index, pane_item_id) =
24834                                    pane.read(cx).items().enumerate().find_map(|(i, item)| {
24835                                        let editor = item.downcast::<Editor>()?;
24836                                        let singleton_buffer =
24837                                            editor.read(cx).buffer().read(cx).as_singleton()?;
24838                                        if singleton_buffer == buffer {
24839                                            Some((editor, i, item.item_id()))
24840                                        } else {
24841                                            None
24842                                        }
24843                                    })?;
24844                                pane.update(cx, |pane, cx| {
24845                                    pane.activate_item(pane_item_index, true, true, window, cx);
24846                                    if !PreviewTabsSettings::get_global(cx)
24847                                        .enable_preview_from_multibuffer
24848                                    {
24849                                        pane.unpreview_item_if_preview(pane_item_id);
24850                                    }
24851                                });
24852                                Some(editor)
24853                            })
24854                            .flatten()
24855                            .unwrap_or_else(|| {
24856                                let keep_old_preview = PreviewTabsSettings::get_global(cx)
24857                                    .enable_keep_preview_on_code_navigation;
24858                                let allow_new_preview = PreviewTabsSettings::get_global(cx)
24859                                    .enable_preview_from_multibuffer;
24860                                workspace.open_project_item::<Self>(
24861                                    pane.clone(),
24862                                    buffer,
24863                                    true,
24864                                    true,
24865                                    keep_old_preview,
24866                                    allow_new_preview,
24867                                    window,
24868                                    cx,
24869                                )
24870                            });
24871
24872                        editor.update(cx, |editor, cx| {
24873                            if has_file && !is_project_file {
24874                                editor.set_read_only(true);
24875                            }
24876                            let autoscroll = match scroll_offset {
24877                                Some(scroll_offset) => {
24878                                    Autoscroll::top_relative(scroll_offset as usize)
24879                                }
24880                                None => Autoscroll::newest(),
24881                            };
24882                            let nav_history = editor.nav_history.take();
24883                            let multibuffer_snapshot = editor.buffer().read(cx).snapshot(cx);
24884                            let Some((excerpt_id, _, buffer_snapshot)) =
24885                                multibuffer_snapshot.as_singleton()
24886                            else {
24887                                return;
24888                            };
24889                            editor.change_selections(
24890                                SelectionEffects::scroll(autoscroll),
24891                                window,
24892                                cx,
24893                                |s| {
24894                                    s.select_ranges(ranges.into_iter().map(|range| {
24895                                        let range = buffer_snapshot.anchor_before(range.start)
24896                                            ..buffer_snapshot.anchor_after(range.end);
24897                                        multibuffer_snapshot
24898                                            .anchor_range_in_excerpt(excerpt_id, range)
24899                                            .unwrap()
24900                                    }));
24901                                },
24902                            );
24903                            editor.nav_history = nav_history;
24904                        });
24905                    }
24906                })
24907                .ok();
24908        });
24909    }
24910
24911    fn marked_text_ranges(&self, cx: &App) -> Option<Vec<Range<MultiBufferOffsetUtf16>>> {
24912        let snapshot = self.buffer.read(cx).read(cx);
24913        let (_, ranges) = self.text_highlights(HighlightKey::InputComposition, cx)?;
24914        Some(
24915            ranges
24916                .iter()
24917                .map(move |range| {
24918                    range.start.to_offset_utf16(&snapshot)..range.end.to_offset_utf16(&snapshot)
24919                })
24920                .collect(),
24921        )
24922    }
24923
24924    fn selection_replacement_ranges(
24925        &self,
24926        range: Range<MultiBufferOffsetUtf16>,
24927        cx: &mut App,
24928    ) -> Vec<Range<MultiBufferOffsetUtf16>> {
24929        let selections = self
24930            .selections
24931            .all::<MultiBufferOffsetUtf16>(&self.display_snapshot(cx));
24932        let newest_selection = selections
24933            .iter()
24934            .max_by_key(|selection| selection.id)
24935            .unwrap();
24936        let start_delta = range.start.0.0 as isize - newest_selection.start.0.0 as isize;
24937        let end_delta = range.end.0.0 as isize - newest_selection.end.0.0 as isize;
24938        let snapshot = self.buffer.read(cx).read(cx);
24939        selections
24940            .into_iter()
24941            .map(|mut selection| {
24942                selection.start.0.0 =
24943                    (selection.start.0.0 as isize).saturating_add(start_delta) as usize;
24944                selection.end.0.0 = (selection.end.0.0 as isize).saturating_add(end_delta) as usize;
24945                snapshot.clip_offset_utf16(selection.start, Bias::Left)
24946                    ..snapshot.clip_offset_utf16(selection.end, Bias::Right)
24947            })
24948            .collect()
24949    }
24950
24951    fn report_editor_event(
24952        &self,
24953        reported_event: ReportEditorEvent,
24954        file_extension: Option<String>,
24955        cx: &App,
24956    ) {
24957        if cfg!(any(test, feature = "test-support")) {
24958            return;
24959        }
24960
24961        let Some(project) = &self.project else { return };
24962
24963        // If None, we are in a file without an extension
24964        let file = self
24965            .buffer
24966            .read(cx)
24967            .as_singleton()
24968            .and_then(|b| b.read(cx).file());
24969        let file_extension = file_extension.or(file
24970            .as_ref()
24971            .and_then(|file| Path::new(file.file_name(cx)).extension())
24972            .and_then(|e| e.to_str())
24973            .map(|a| a.to_string()));
24974
24975        let vim_mode = vim_mode_setting::VimModeSetting::try_get(cx)
24976            .map(|vim_mode| vim_mode.0)
24977            .unwrap_or(false);
24978
24979        let edit_predictions_provider = all_language_settings(file, cx).edit_predictions.provider;
24980        let copilot_enabled = edit_predictions_provider
24981            == language::language_settings::EditPredictionProvider::Copilot;
24982        let copilot_enabled_for_language = self
24983            .buffer
24984            .read(cx)
24985            .language_settings(cx)
24986            .show_edit_predictions;
24987
24988        let project = project.read(cx);
24989        let event_type = reported_event.event_type();
24990
24991        if let ReportEditorEvent::Saved { auto_saved } = reported_event {
24992            telemetry::event!(
24993                event_type,
24994                type = if auto_saved {"autosave"} else {"manual"},
24995                file_extension,
24996                vim_mode,
24997                copilot_enabled,
24998                copilot_enabled_for_language,
24999                edit_predictions_provider,
25000                is_via_ssh = project.is_via_remote_server(),
25001            );
25002        } else {
25003            telemetry::event!(
25004                event_type,
25005                file_extension,
25006                vim_mode,
25007                copilot_enabled,
25008                copilot_enabled_for_language,
25009                edit_predictions_provider,
25010                is_via_ssh = project.is_via_remote_server(),
25011            );
25012        };
25013    }
25014
25015    /// Copy the highlighted chunks to the clipboard as JSON. The format is an array of lines,
25016    /// with each line being an array of {text, highlight} objects.
25017    fn copy_highlight_json(
25018        &mut self,
25019        _: &CopyHighlightJson,
25020        window: &mut Window,
25021        cx: &mut Context<Self>,
25022    ) {
25023        #[derive(Serialize)]
25024        struct Chunk<'a> {
25025            text: String,
25026            highlight: Option<&'a str>,
25027        }
25028
25029        let snapshot = self.buffer.read(cx).snapshot(cx);
25030        let range = self
25031            .selected_text_range(false, window, cx)
25032            .and_then(|selection| {
25033                if selection.range.is_empty() {
25034                    None
25035                } else {
25036                    Some(
25037                        snapshot.offset_utf16_to_offset(MultiBufferOffsetUtf16(OffsetUtf16(
25038                            selection.range.start,
25039                        )))
25040                            ..snapshot.offset_utf16_to_offset(MultiBufferOffsetUtf16(OffsetUtf16(
25041                                selection.range.end,
25042                            ))),
25043                    )
25044                }
25045            })
25046            .unwrap_or_else(|| MultiBufferOffset(0)..snapshot.len());
25047
25048        let chunks = snapshot.chunks(range, true);
25049        let mut lines = Vec::new();
25050        let mut line: VecDeque<Chunk> = VecDeque::new();
25051
25052        let Some(style) = self.style.as_ref() else {
25053            return;
25054        };
25055
25056        for chunk in chunks {
25057            let highlight = chunk
25058                .syntax_highlight_id
25059                .and_then(|id| style.syntax.get_capture_name(id));
25060
25061            let mut chunk_lines = chunk.text.split('\n').peekable();
25062            while let Some(text) = chunk_lines.next() {
25063                let mut merged_with_last_token = false;
25064                if let Some(last_token) = line.back_mut()
25065                    && last_token.highlight == highlight
25066                {
25067                    last_token.text.push_str(text);
25068                    merged_with_last_token = true;
25069                }
25070
25071                if !merged_with_last_token {
25072                    line.push_back(Chunk {
25073                        text: text.into(),
25074                        highlight,
25075                    });
25076                }
25077
25078                if chunk_lines.peek().is_some() {
25079                    if line.len() > 1 && line.front().unwrap().text.is_empty() {
25080                        line.pop_front();
25081                    }
25082                    if line.len() > 1 && line.back().unwrap().text.is_empty() {
25083                        line.pop_back();
25084                    }
25085
25086                    lines.push(mem::take(&mut line));
25087                }
25088            }
25089        }
25090
25091        let Some(lines) = serde_json::to_string_pretty(&lines).log_err() else {
25092            return;
25093        };
25094        cx.write_to_clipboard(ClipboardItem::new_string(lines));
25095    }
25096
25097    pub fn open_context_menu(
25098        &mut self,
25099        _: &OpenContextMenu,
25100        window: &mut Window,
25101        cx: &mut Context<Self>,
25102    ) {
25103        self.request_autoscroll(Autoscroll::newest(), cx);
25104        let position = self
25105            .selections
25106            .newest_display(&self.display_snapshot(cx))
25107            .start;
25108        mouse_context_menu::deploy_context_menu(self, None, position, window, cx);
25109    }
25110
25111    pub fn replay_insert_event(
25112        &mut self,
25113        text: &str,
25114        relative_utf16_range: Option<Range<isize>>,
25115        window: &mut Window,
25116        cx: &mut Context<Self>,
25117    ) {
25118        if !self.input_enabled {
25119            cx.emit(EditorEvent::InputIgnored { text: text.into() });
25120            return;
25121        }
25122        if let Some(relative_utf16_range) = relative_utf16_range {
25123            let selections = self
25124                .selections
25125                .all::<MultiBufferOffsetUtf16>(&self.display_snapshot(cx));
25126            self.change_selections(SelectionEffects::no_scroll(), window, cx, |s| {
25127                let new_ranges = selections.into_iter().map(|range| {
25128                    let start = MultiBufferOffsetUtf16(OffsetUtf16(
25129                        range
25130                            .head()
25131                            .0
25132                            .0
25133                            .saturating_add_signed(relative_utf16_range.start),
25134                    ));
25135                    let end = MultiBufferOffsetUtf16(OffsetUtf16(
25136                        range
25137                            .head()
25138                            .0
25139                            .0
25140                            .saturating_add_signed(relative_utf16_range.end),
25141                    ));
25142                    start..end
25143                });
25144                s.select_ranges(new_ranges);
25145            });
25146        }
25147
25148        self.handle_input(text, window, cx);
25149    }
25150
25151    pub fn is_focused(&self, window: &Window) -> bool {
25152        self.focus_handle.is_focused(window)
25153    }
25154
25155    fn handle_focus(&mut self, window: &mut Window, cx: &mut Context<Self>) {
25156        cx.emit(EditorEvent::Focused);
25157
25158        if let Some(descendant) = self
25159            .last_focused_descendant
25160            .take()
25161            .and_then(|descendant| descendant.upgrade())
25162        {
25163            window.focus(&descendant, cx);
25164        } else {
25165            if let Some(blame) = self.blame.as_ref() {
25166                blame.update(cx, GitBlame::focus)
25167            }
25168
25169            self.blink_manager.update(cx, BlinkManager::enable);
25170            self.show_cursor_names(window, cx);
25171            self.buffer.update(cx, |buffer, cx| {
25172                buffer.finalize_last_transaction(cx);
25173                if self.leader_id.is_none() {
25174                    buffer.set_active_selections(
25175                        &self.selections.disjoint_anchors_arc(),
25176                        self.selections.line_mode(),
25177                        self.cursor_shape,
25178                        cx,
25179                    );
25180                }
25181            });
25182
25183            if let Some(position_map) = self.last_position_map.clone()
25184                && !self.mouse_cursor_hidden
25185            {
25186                EditorElement::mouse_moved(
25187                    self,
25188                    &MouseMoveEvent {
25189                        position: window.mouse_position(),
25190                        pressed_button: None,
25191                        modifiers: window.modifiers(),
25192                    },
25193                    &position_map,
25194                    None,
25195                    window,
25196                    cx,
25197                );
25198            }
25199        }
25200    }
25201
25202    fn handle_focus_in(&mut self, _: &mut Window, cx: &mut Context<Self>) {
25203        cx.emit(EditorEvent::FocusedIn)
25204    }
25205
25206    fn handle_focus_out(
25207        &mut self,
25208        event: FocusOutEvent,
25209        _window: &mut Window,
25210        cx: &mut Context<Self>,
25211    ) {
25212        if event.blurred != self.focus_handle {
25213            self.last_focused_descendant = Some(event.blurred);
25214        }
25215        self.selection_drag_state = SelectionDragState::None;
25216        self.refresh_inlay_hints(InlayHintRefreshReason::ModifiersChanged(false), cx);
25217    }
25218
25219    pub fn handle_blur(&mut self, window: &mut Window, cx: &mut Context<Self>) {
25220        self.blink_manager.update(cx, BlinkManager::disable);
25221        self.buffer
25222            .update(cx, |buffer, cx| buffer.remove_active_selections(cx));
25223
25224        if let Some(blame) = self.blame.as_ref() {
25225            blame.update(cx, GitBlame::blur)
25226        }
25227        if !self.hover_state.focused(window, cx) {
25228            hide_hover(self, cx);
25229        }
25230        if !self
25231            .context_menu
25232            .borrow()
25233            .as_ref()
25234            .is_some_and(|context_menu| context_menu.focused(window, cx))
25235        {
25236            self.hide_context_menu(window, cx);
25237        }
25238        self.take_active_edit_prediction(true, cx);
25239        cx.emit(EditorEvent::Blurred);
25240        cx.notify();
25241    }
25242
25243    pub fn observe_pending_input(&mut self, window: &mut Window, cx: &mut Context<Self>) {
25244        let mut pending: String = window
25245            .pending_input_keystrokes()
25246            .into_iter()
25247            .flatten()
25248            .filter_map(|keystroke| keystroke.key_char.clone())
25249            .collect();
25250
25251        if !self.input_enabled || self.read_only || !self.focus_handle.is_focused(window) {
25252            pending = "".to_string();
25253        }
25254
25255        let existing_pending = self
25256            .text_highlights(HighlightKey::PendingInput, cx)
25257            .map(|(_, ranges)| ranges.to_vec());
25258        if existing_pending.is_none() && pending.is_empty() {
25259            return;
25260        }
25261        let transaction =
25262            self.transact(window, cx, |this, window, cx| {
25263                let selections = this
25264                    .selections
25265                    .all::<MultiBufferOffset>(&this.display_snapshot(cx));
25266                let edits = selections
25267                    .iter()
25268                    .map(|selection| (selection.end..selection.end, pending.clone()));
25269                this.edit(edits, cx);
25270                this.change_selections(SelectionEffects::no_scroll(), window, cx, |s| {
25271                    s.select_ranges(selections.into_iter().enumerate().map(|(ix, sel)| {
25272                        sel.start + ix * pending.len()..sel.end + ix * pending.len()
25273                    }));
25274                });
25275                if let Some(existing_ranges) = existing_pending {
25276                    let edits = existing_ranges.iter().map(|range| (range.clone(), ""));
25277                    this.edit(edits, cx);
25278                }
25279            });
25280
25281        let snapshot = self.snapshot(window, cx);
25282        let ranges = self
25283            .selections
25284            .all::<MultiBufferOffset>(&snapshot.display_snapshot)
25285            .into_iter()
25286            .map(|selection| {
25287                snapshot.buffer_snapshot().anchor_after(selection.end)
25288                    ..snapshot
25289                        .buffer_snapshot()
25290                        .anchor_before(selection.end + pending.len())
25291            })
25292            .collect();
25293
25294        if pending.is_empty() {
25295            self.clear_highlights(HighlightKey::PendingInput, cx);
25296        } else {
25297            self.highlight_text(
25298                HighlightKey::PendingInput,
25299                ranges,
25300                HighlightStyle {
25301                    underline: Some(UnderlineStyle {
25302                        thickness: px(1.),
25303                        color: None,
25304                        wavy: false,
25305                    }),
25306                    ..Default::default()
25307                },
25308                cx,
25309            );
25310        }
25311
25312        self.ime_transaction = self.ime_transaction.or(transaction);
25313        if let Some(transaction) = self.ime_transaction {
25314            self.buffer.update(cx, |buffer, cx| {
25315                buffer.group_until_transaction(transaction, cx);
25316            });
25317        }
25318
25319        if self
25320            .text_highlights(HighlightKey::PendingInput, cx)
25321            .is_none()
25322        {
25323            self.ime_transaction.take();
25324        }
25325    }
25326
25327    pub fn register_action_renderer(
25328        &mut self,
25329        listener: impl Fn(&Editor, &mut Window, &mut Context<Editor>) + 'static,
25330    ) -> Subscription {
25331        let id = self.next_editor_action_id.post_inc();
25332        self.editor_actions
25333            .borrow_mut()
25334            .insert(id, Box::new(listener));
25335
25336        let editor_actions = self.editor_actions.clone();
25337        Subscription::new(move || {
25338            editor_actions.borrow_mut().remove(&id);
25339        })
25340    }
25341
25342    pub fn register_action<A: Action>(
25343        &mut self,
25344        listener: impl Fn(&A, &mut Window, &mut App) + 'static,
25345    ) -> Subscription {
25346        let id = self.next_editor_action_id.post_inc();
25347        let listener = Arc::new(listener);
25348        self.editor_actions.borrow_mut().insert(
25349            id,
25350            Box::new(move |_, window, _| {
25351                let listener = listener.clone();
25352                window.on_action(TypeId::of::<A>(), move |action, phase, window, cx| {
25353                    let action = action.downcast_ref().unwrap();
25354                    if phase == DispatchPhase::Bubble {
25355                        listener(action, window, cx)
25356                    }
25357                })
25358            }),
25359        );
25360
25361        let editor_actions = self.editor_actions.clone();
25362        Subscription::new(move || {
25363            editor_actions.borrow_mut().remove(&id);
25364        })
25365    }
25366
25367    pub fn file_header_size(&self) -> u32 {
25368        FILE_HEADER_HEIGHT
25369    }
25370
25371    pub fn restore(
25372        &mut self,
25373        revert_changes: HashMap<BufferId, Vec<(Range<text::Anchor>, Rope)>>,
25374        window: &mut Window,
25375        cx: &mut Context<Self>,
25376    ) {
25377        self.buffer().update(cx, |multi_buffer, cx| {
25378            for (buffer_id, changes) in revert_changes {
25379                if let Some(buffer) = multi_buffer.buffer(buffer_id) {
25380                    buffer.update(cx, |buffer, cx| {
25381                        buffer.edit(
25382                            changes
25383                                .into_iter()
25384                                .map(|(range, text)| (range, text.to_string())),
25385                            None,
25386                            cx,
25387                        );
25388                    });
25389                }
25390            }
25391        });
25392        self.change_selections(SelectionEffects::no_scroll(), window, cx, |selections| {
25393            selections.refresh()
25394        });
25395    }
25396
25397    pub fn to_pixel_point(
25398        &mut self,
25399        source: Anchor,
25400        editor_snapshot: &EditorSnapshot,
25401        window: &mut Window,
25402        cx: &mut App,
25403    ) -> Option<gpui::Point<Pixels>> {
25404        let source_point = source.to_display_point(editor_snapshot);
25405        self.display_to_pixel_point(source_point, editor_snapshot, window, cx)
25406    }
25407
25408    pub fn display_to_pixel_point(
25409        &mut self,
25410        source: DisplayPoint,
25411        editor_snapshot: &EditorSnapshot,
25412        window: &mut Window,
25413        cx: &mut App,
25414    ) -> Option<gpui::Point<Pixels>> {
25415        let line_height = self.style(cx).text.line_height_in_pixels(window.rem_size());
25416        let text_layout_details = self.text_layout_details(window, cx);
25417        let scroll_top = text_layout_details
25418            .scroll_anchor
25419            .scroll_position(editor_snapshot)
25420            .y;
25421
25422        if source.row().as_f64() < scroll_top.floor() {
25423            return None;
25424        }
25425        let source_x = editor_snapshot.x_for_display_point(source, &text_layout_details);
25426        let source_y = line_height * (source.row().as_f64() - scroll_top) as f32;
25427        Some(gpui::Point::new(source_x, source_y))
25428    }
25429
25430    pub fn has_visible_completions_menu(&self) -> bool {
25431        !self.edit_prediction_preview_is_active()
25432            && self.context_menu.borrow().as_ref().is_some_and(|menu| {
25433                menu.visible() && matches!(menu, CodeContextMenu::Completions(_))
25434            })
25435    }
25436
25437    pub fn register_addon<T: Addon>(&mut self, instance: T) {
25438        if self.mode.is_minimap() {
25439            return;
25440        }
25441        self.addons
25442            .insert(std::any::TypeId::of::<T>(), Box::new(instance));
25443    }
25444
25445    pub fn unregister_addon<T: Addon>(&mut self) {
25446        self.addons.remove(&std::any::TypeId::of::<T>());
25447    }
25448
25449    pub fn addon<T: Addon>(&self) -> Option<&T> {
25450        let type_id = std::any::TypeId::of::<T>();
25451        self.addons
25452            .get(&type_id)
25453            .and_then(|item| item.to_any().downcast_ref::<T>())
25454    }
25455
25456    pub fn addon_mut<T: Addon>(&mut self) -> Option<&mut T> {
25457        let type_id = std::any::TypeId::of::<T>();
25458        self.addons
25459            .get_mut(&type_id)
25460            .and_then(|item| item.to_any_mut()?.downcast_mut::<T>())
25461    }
25462
25463    fn character_dimensions(&self, window: &mut Window, cx: &mut App) -> CharacterDimensions {
25464        let text_layout_details = self.text_layout_details(window, cx);
25465        let style = &text_layout_details.editor_style;
25466        let font_id = window.text_system().resolve_font(&style.text.font());
25467        let font_size = style.text.font_size.to_pixels(window.rem_size());
25468        let line_height = style.text.line_height_in_pixels(window.rem_size());
25469        let em_width = window.text_system().em_width(font_id, font_size).unwrap();
25470        let em_advance = window.text_system().em_advance(font_id, font_size).unwrap();
25471
25472        CharacterDimensions {
25473            em_width,
25474            em_advance,
25475            line_height,
25476        }
25477    }
25478
25479    pub fn wait_for_diff_to_load(&self) -> Option<Shared<Task<()>>> {
25480        self.load_diff_task.clone()
25481    }
25482
25483    fn read_metadata_from_db(
25484        &mut self,
25485        item_id: u64,
25486        workspace_id: WorkspaceId,
25487        window: &mut Window,
25488        cx: &mut Context<Editor>,
25489    ) {
25490        if self.buffer_kind(cx) == ItemBufferKind::Singleton
25491            && !self.mode.is_minimap()
25492            && WorkspaceSettings::get(None, cx).restore_on_startup
25493                != RestoreOnStartupBehavior::EmptyTab
25494        {
25495            let buffer_snapshot = OnceCell::new();
25496
25497            // Get file path for path-based fold lookup
25498            let file_path: Option<Arc<Path>> =
25499                self.buffer().read(cx).as_singleton().and_then(|buffer| {
25500                    project::File::from_dyn(buffer.read(cx).file())
25501                        .map(|file| Arc::from(file.abs_path(cx)))
25502                });
25503
25504            // Try file_folds (path-based) first, fallback to editor_folds (migration)
25505            let db = EditorDb::global(cx);
25506            let (folds, needs_migration) = if let Some(ref path) = file_path {
25507                if let Some(folds) = db.get_file_folds(workspace_id, path).log_err()
25508                    && !folds.is_empty()
25509                {
25510                    (Some(folds), false)
25511                } else if let Some(folds) = db.get_editor_folds(item_id, workspace_id).log_err()
25512                    && !folds.is_empty()
25513                {
25514                    // Found old editor_folds data, will migrate to file_folds
25515                    (Some(folds), true)
25516                } else {
25517                    (None, false)
25518                }
25519            } else {
25520                // No file path, try editor_folds as fallback
25521                let folds = db.get_editor_folds(item_id, workspace_id).log_err();
25522                (folds.filter(|f| !f.is_empty()), false)
25523            };
25524
25525            if let Some(folds) = folds {
25526                let snapshot = buffer_snapshot.get_or_init(|| self.buffer.read(cx).snapshot(cx));
25527                let snapshot_len = snapshot.len().0;
25528
25529                // Helper: search for fingerprint in buffer, return offset if found
25530                let find_fingerprint = |fingerprint: &str, search_start: usize| -> Option<usize> {
25531                    // Ensure we start at a character boundary (defensive)
25532                    let search_start = snapshot
25533                        .clip_offset(MultiBufferOffset(search_start), Bias::Left)
25534                        .0;
25535                    let search_end = snapshot_len.saturating_sub(fingerprint.len());
25536
25537                    let mut byte_offset = search_start;
25538                    for ch in snapshot.chars_at(MultiBufferOffset(search_start)) {
25539                        if byte_offset > search_end {
25540                            break;
25541                        }
25542                        if snapshot.contains_str_at(MultiBufferOffset(byte_offset), fingerprint) {
25543                            return Some(byte_offset);
25544                        }
25545                        byte_offset += ch.len_utf8();
25546                    }
25547                    None
25548                };
25549
25550                // Track search position to handle duplicate fingerprints correctly.
25551                // Folds are stored in document order, so we advance after each match.
25552                let mut search_start = 0usize;
25553
25554                // Collect db_folds for migration (only folds with valid fingerprints)
25555                let mut db_folds_for_migration: Vec<(usize, usize, String, String)> = Vec::new();
25556
25557                let valid_folds: Vec<_> = folds
25558                    .into_iter()
25559                    .filter_map(|(stored_start, stored_end, start_fp, end_fp)| {
25560                        // Skip folds without fingerprints (old data before migration)
25561                        let sfp = start_fp?;
25562                        let efp = end_fp?;
25563                        let efp_len = efp.len();
25564
25565                        // Fast path: check if fingerprints match at stored offsets
25566                        // Note: end_fp is content BEFORE fold end, so check at (stored_end - efp_len)
25567                        let start_matches = stored_start < snapshot_len
25568                            && snapshot.contains_str_at(MultiBufferOffset(stored_start), &sfp);
25569                        let efp_check_pos = stored_end.saturating_sub(efp_len);
25570                        let end_matches = efp_check_pos >= stored_start
25571                            && stored_end <= snapshot_len
25572                            && snapshot.contains_str_at(MultiBufferOffset(efp_check_pos), &efp);
25573
25574                        let (new_start, new_end) = if start_matches && end_matches {
25575                            // Offsets unchanged, use stored values
25576                            (stored_start, stored_end)
25577                        } else if sfp == efp {
25578                            // Short fold: identical fingerprints can only match once per search
25579                            // Use stored fold length to compute new_end
25580                            let new_start = find_fingerprint(&sfp, search_start)?;
25581                            let fold_len = stored_end - stored_start;
25582                            let new_end = new_start + fold_len;
25583                            (new_start, new_end)
25584                        } else {
25585                            // Slow path: search for fingerprints in buffer
25586                            let new_start = find_fingerprint(&sfp, search_start)?;
25587                            // Search for end_fp after start, then add efp_len to get actual fold end
25588                            let efp_pos = find_fingerprint(&efp, new_start + sfp.len())?;
25589                            let new_end = efp_pos + efp_len;
25590                            (new_start, new_end)
25591                        };
25592
25593                        // Advance search position for next fold
25594                        search_start = new_end;
25595
25596                        // Validate fold makes sense (end must be after start)
25597                        if new_end <= new_start {
25598                            return None;
25599                        }
25600
25601                        // Collect for migration if needed
25602                        if needs_migration {
25603                            db_folds_for_migration.push((new_start, new_end, sfp, efp));
25604                        }
25605
25606                        Some(
25607                            snapshot.clip_offset(MultiBufferOffset(new_start), Bias::Left)
25608                                ..snapshot.clip_offset(MultiBufferOffset(new_end), Bias::Right),
25609                        )
25610                    })
25611                    .collect();
25612
25613                if !valid_folds.is_empty() {
25614                    self.fold_ranges(valid_folds, false, window, cx);
25615
25616                    // Migrate from editor_folds to file_folds if we loaded from old table
25617                    if needs_migration {
25618                        if let Some(ref path) = file_path {
25619                            let path = path.clone();
25620                            let db = EditorDb::global(cx);
25621                            cx.spawn(async move |_, _| {
25622                                db.save_file_folds(workspace_id, path, db_folds_for_migration)
25623                                    .await
25624                                    .log_err();
25625                            })
25626                            .detach();
25627                        }
25628                    }
25629                }
25630            }
25631
25632            if let Some(selections) = db.get_editor_selections(item_id, workspace_id).log_err()
25633                && !selections.is_empty()
25634            {
25635                let snapshot = buffer_snapshot.get_or_init(|| self.buffer.read(cx).snapshot(cx));
25636                // skip adding the initial selection to selection history
25637                self.selection_history.mode = SelectionHistoryMode::Skipping;
25638                self.change_selections(SelectionEffects::no_scroll(), window, cx, |s| {
25639                    s.select_ranges(selections.into_iter().map(|(start, end)| {
25640                        snapshot.clip_offset(MultiBufferOffset(start), Bias::Left)
25641                            ..snapshot.clip_offset(MultiBufferOffset(end), Bias::Right)
25642                    }));
25643                });
25644                self.selection_history.mode = SelectionHistoryMode::Normal;
25645            };
25646        }
25647
25648        self.read_scroll_position_from_db(item_id, workspace_id, window, cx);
25649    }
25650
25651    /// Load folds from the file_folds database table by file path.
25652    /// Used when manually opening a file that was previously closed.
25653    fn load_folds_from_db(
25654        &mut self,
25655        workspace_id: WorkspaceId,
25656        file_path: PathBuf,
25657        window: &mut Window,
25658        cx: &mut Context<Editor>,
25659    ) {
25660        if self.mode.is_minimap()
25661            || WorkspaceSettings::get(None, cx).restore_on_startup
25662                == RestoreOnStartupBehavior::EmptyTab
25663        {
25664            return;
25665        }
25666
25667        let Some(folds) = EditorDb::global(cx)
25668            .get_file_folds(workspace_id, &file_path)
25669            .log_err()
25670        else {
25671            return;
25672        };
25673        if folds.is_empty() {
25674            return;
25675        }
25676
25677        let snapshot = self.buffer.read(cx).snapshot(cx);
25678        let snapshot_len = snapshot.len().0;
25679
25680        // Helper: search for fingerprint in buffer, return offset if found
25681        let find_fingerprint = |fingerprint: &str, search_start: usize| -> Option<usize> {
25682            let search_start = snapshot
25683                .clip_offset(MultiBufferOffset(search_start), Bias::Left)
25684                .0;
25685            let search_end = snapshot_len.saturating_sub(fingerprint.len());
25686
25687            let mut byte_offset = search_start;
25688            for ch in snapshot.chars_at(MultiBufferOffset(search_start)) {
25689                if byte_offset > search_end {
25690                    break;
25691                }
25692                if snapshot.contains_str_at(MultiBufferOffset(byte_offset), fingerprint) {
25693                    return Some(byte_offset);
25694                }
25695                byte_offset += ch.len_utf8();
25696            }
25697            None
25698        };
25699
25700        let mut search_start = 0usize;
25701
25702        let valid_folds: Vec<_> = folds
25703            .into_iter()
25704            .filter_map(|(stored_start, stored_end, start_fp, end_fp)| {
25705                let sfp = start_fp?;
25706                let efp = end_fp?;
25707                let efp_len = efp.len();
25708
25709                let start_matches = stored_start < snapshot_len
25710                    && snapshot.contains_str_at(MultiBufferOffset(stored_start), &sfp);
25711                let efp_check_pos = stored_end.saturating_sub(efp_len);
25712                let end_matches = efp_check_pos >= stored_start
25713                    && stored_end <= snapshot_len
25714                    && snapshot.contains_str_at(MultiBufferOffset(efp_check_pos), &efp);
25715
25716                let (new_start, new_end) = if start_matches && end_matches {
25717                    (stored_start, stored_end)
25718                } else if sfp == efp {
25719                    let new_start = find_fingerprint(&sfp, search_start)?;
25720                    let fold_len = stored_end - stored_start;
25721                    let new_end = new_start + fold_len;
25722                    (new_start, new_end)
25723                } else {
25724                    let new_start = find_fingerprint(&sfp, search_start)?;
25725                    let efp_pos = find_fingerprint(&efp, new_start + sfp.len())?;
25726                    let new_end = efp_pos + efp_len;
25727                    (new_start, new_end)
25728                };
25729
25730                search_start = new_end;
25731
25732                if new_end <= new_start {
25733                    return None;
25734                }
25735
25736                Some(
25737                    snapshot.clip_offset(MultiBufferOffset(new_start), Bias::Left)
25738                        ..snapshot.clip_offset(MultiBufferOffset(new_end), Bias::Right),
25739                )
25740            })
25741            .collect();
25742
25743        if !valid_folds.is_empty() {
25744            self.fold_ranges(valid_folds, false, window, cx);
25745        }
25746    }
25747
25748    fn lsp_data_enabled(&self) -> bool {
25749        self.enable_lsp_data && self.mode().is_full()
25750    }
25751
25752    fn update_lsp_data(
25753        &mut self,
25754        for_buffer: Option<BufferId>,
25755        window: &mut Window,
25756        cx: &mut Context<'_, Self>,
25757    ) {
25758        if !self.lsp_data_enabled() {
25759            return;
25760        }
25761
25762        if let Some(buffer_id) = for_buffer {
25763            self.pull_diagnostics(buffer_id, window, cx);
25764        }
25765        self.refresh_semantic_tokens(for_buffer, None, cx);
25766        self.refresh_document_colors(for_buffer, window, cx);
25767        self.refresh_folding_ranges(for_buffer, window, cx);
25768        self.refresh_document_symbols(for_buffer, cx);
25769    }
25770
25771    fn register_visible_buffers(&mut self, cx: &mut Context<Self>) {
25772        if !self.lsp_data_enabled() {
25773            return;
25774        }
25775        for (_, (visible_buffer, _, _)) in self.visible_excerpts(true, cx) {
25776            self.register_buffer(visible_buffer.read(cx).remote_id(), cx);
25777        }
25778    }
25779
25780    fn register_buffer(&mut self, buffer_id: BufferId, cx: &mut Context<Self>) {
25781        if !self.lsp_data_enabled() {
25782            return;
25783        }
25784
25785        if !self.registered_buffers.contains_key(&buffer_id)
25786            && let Some(project) = self.project.as_ref()
25787        {
25788            if let Some(buffer) = self.buffer.read(cx).buffer(buffer_id) {
25789                project.update(cx, |project, cx| {
25790                    self.registered_buffers.insert(
25791                        buffer_id,
25792                        project.register_buffer_with_language_servers(&buffer, cx),
25793                    );
25794                });
25795            } else {
25796                self.registered_buffers.remove(&buffer_id);
25797            }
25798        }
25799    }
25800
25801    fn create_style(&self, cx: &App) -> EditorStyle {
25802        let settings = ThemeSettings::get_global(cx);
25803
25804        let mut text_style = match self.mode {
25805            EditorMode::SingleLine | EditorMode::AutoHeight { .. } => TextStyle {
25806                color: cx.theme().colors().editor_foreground,
25807                font_family: settings.ui_font.family.clone(),
25808                font_features: settings.ui_font.features.clone(),
25809                font_fallbacks: settings.ui_font.fallbacks.clone(),
25810                font_size: rems(0.875).into(),
25811                font_weight: settings.ui_font.weight,
25812                line_height: relative(settings.buffer_line_height.value()),
25813                ..Default::default()
25814            },
25815            EditorMode::Full { .. } | EditorMode::Minimap { .. } => TextStyle {
25816                color: cx.theme().colors().editor_foreground,
25817                font_family: settings.buffer_font.family.clone(),
25818                font_features: settings.buffer_font.features.clone(),
25819                font_fallbacks: settings.buffer_font.fallbacks.clone(),
25820                font_size: settings.buffer_font_size(cx).into(),
25821                font_weight: settings.buffer_font.weight,
25822                line_height: relative(settings.buffer_line_height.value()),
25823                ..Default::default()
25824            },
25825        };
25826        if let Some(text_style_refinement) = &self.text_style_refinement {
25827            text_style.refine(text_style_refinement)
25828        }
25829
25830        let background = match self.mode {
25831            EditorMode::SingleLine => cx.theme().system().transparent,
25832            EditorMode::AutoHeight { .. } => cx.theme().system().transparent,
25833            EditorMode::Full { .. } => cx.theme().colors().editor_background,
25834            EditorMode::Minimap { .. } => cx.theme().colors().editor_background.opacity(0.7),
25835        };
25836
25837        EditorStyle {
25838            background,
25839            border: cx.theme().colors().border,
25840            local_player: cx.theme().players().local(),
25841            text: text_style,
25842            scrollbar_width: EditorElement::SCROLLBAR_WIDTH,
25843            syntax: cx.theme().syntax().clone(),
25844            status: cx.theme().status().clone(),
25845            inlay_hints_style: make_inlay_hints_style(cx),
25846            edit_prediction_styles: make_suggestion_styles(cx),
25847            unnecessary_code_fade: settings.unnecessary_code_fade,
25848            show_underlines: self.diagnostics_enabled(),
25849        }
25850    }
25851
25852    fn breadcrumbs_inner(&self, cx: &App) -> Option<Vec<HighlightedText>> {
25853        let multibuffer = self.buffer().read(cx);
25854        let is_singleton = multibuffer.is_singleton();
25855        let (buffer_id, symbols) = self.outline_symbols_at_cursor.as_ref()?;
25856        let buffer = multibuffer.buffer(*buffer_id)?;
25857
25858        let buffer = buffer.read(cx);
25859        // In a multi-buffer layout, we don't want to include the filename in the breadcrumbs
25860        let mut breadcrumbs = if is_singleton {
25861            let text = self.breadcrumb_header.clone().unwrap_or_else(|| {
25862                buffer
25863                    .snapshot()
25864                    .resolve_file_path(
25865                        self.project
25866                            .as_ref()
25867                            .map(|project| project.read(cx).visible_worktrees(cx).count() > 1)
25868                            .unwrap_or_default(),
25869                        cx,
25870                    )
25871                    .unwrap_or_else(|| {
25872                        if multibuffer.is_singleton() {
25873                            multibuffer.title(cx).to_string()
25874                        } else {
25875                            "untitled".to_string()
25876                        }
25877                    })
25878            });
25879            vec![HighlightedText {
25880                text: text.into(),
25881                highlights: vec![],
25882            }]
25883        } else {
25884            vec![]
25885        };
25886
25887        breadcrumbs.extend(symbols.iter().map(|symbol| HighlightedText {
25888            text: symbol.text.clone().into(),
25889            highlights: symbol.highlight_ranges.clone(),
25890        }));
25891        Some(breadcrumbs)
25892    }
25893
25894    fn disable_lsp_data(&mut self) {
25895        self.enable_lsp_data = false;
25896    }
25897
25898    fn disable_runnables(&mut self) {
25899        self.enable_runnables = false;
25900    }
25901
25902    fn update_data_on_scroll(&mut self, window: &mut Window, cx: &mut Context<'_, Self>) {
25903        self.register_visible_buffers(cx);
25904        self.colorize_brackets(false, cx);
25905        self.refresh_inlay_hints(InlayHintRefreshReason::NewLinesShown, cx);
25906        if !self.buffer().read(cx).is_singleton() {
25907            self.update_lsp_data(None, window, cx);
25908            self.refresh_runnables(None, window, cx);
25909        }
25910    }
25911}
25912
25913fn edit_for_markdown_paste<'a>(
25914    buffer: &MultiBufferSnapshot,
25915    range: Range<MultiBufferOffset>,
25916    to_insert: &'a str,
25917    url: Option<url::Url>,
25918) -> (Range<MultiBufferOffset>, Cow<'a, str>) {
25919    if url.is_none() {
25920        return (range, Cow::Borrowed(to_insert));
25921    };
25922
25923    let old_text = buffer.text_for_range(range.clone()).collect::<String>();
25924
25925    let new_text = if range.is_empty() || url::Url::parse(&old_text).is_ok() {
25926        Cow::Borrowed(to_insert)
25927    } else {
25928        Cow::Owned(format!("[{old_text}]({to_insert})"))
25929    };
25930    (range, new_text)
25931}
25932
25933fn process_completion_for_edit(
25934    completion: &Completion,
25935    intent: CompletionIntent,
25936    buffer: &Entity<Buffer>,
25937    cursor_position: &text::Anchor,
25938    cx: &mut Context<Editor>,
25939) -> CompletionEdit {
25940    let buffer = buffer.read(cx);
25941    let buffer_snapshot = buffer.snapshot();
25942    let (snippet, new_text) = if completion.is_snippet() {
25943        let mut snippet_source = completion.new_text.clone();
25944        // Workaround for typescript language server issues so that methods don't expand within
25945        // strings and functions with type expressions. The previous point is used because the query
25946        // for function identifier doesn't match when the cursor is immediately after. See PR #30312
25947        let previous_point = text::ToPoint::to_point(cursor_position, &buffer_snapshot);
25948        let previous_point = if previous_point.column > 0 {
25949            cursor_position.to_previous_offset(&buffer_snapshot)
25950        } else {
25951            cursor_position.to_offset(&buffer_snapshot)
25952        };
25953        if let Some(scope) = buffer_snapshot.language_scope_at(previous_point)
25954            && scope.prefers_label_for_snippet_in_completion()
25955            && let Some(label) = completion.label()
25956            && matches!(
25957                completion.kind(),
25958                Some(CompletionItemKind::FUNCTION) | Some(CompletionItemKind::METHOD)
25959            )
25960        {
25961            snippet_source = label;
25962        }
25963        match Snippet::parse(&snippet_source).log_err() {
25964            Some(parsed_snippet) => (Some(parsed_snippet.clone()), parsed_snippet.text),
25965            None => (None, completion.new_text.clone()),
25966        }
25967    } else {
25968        (None, completion.new_text.clone())
25969    };
25970
25971    let mut range_to_replace = {
25972        let replace_range = &completion.replace_range;
25973        if let CompletionSource::Lsp {
25974            insert_range: Some(insert_range),
25975            ..
25976        } = &completion.source
25977        {
25978            debug_assert_eq!(
25979                insert_range.start, replace_range.start,
25980                "insert_range and replace_range should start at the same position"
25981            );
25982            debug_assert!(
25983                insert_range
25984                    .start
25985                    .cmp(cursor_position, &buffer_snapshot)
25986                    .is_le(),
25987                "insert_range should start before or at cursor position"
25988            );
25989            debug_assert!(
25990                replace_range
25991                    .start
25992                    .cmp(cursor_position, &buffer_snapshot)
25993                    .is_le(),
25994                "replace_range should start before or at cursor position"
25995            );
25996
25997            let should_replace = match intent {
25998                CompletionIntent::CompleteWithInsert => false,
25999                CompletionIntent::CompleteWithReplace => true,
26000                CompletionIntent::Complete | CompletionIntent::Compose => {
26001                    let insert_mode = LanguageSettings::for_buffer(&buffer, cx)
26002                        .completions
26003                        .lsp_insert_mode;
26004                    match insert_mode {
26005                        LspInsertMode::Insert => false,
26006                        LspInsertMode::Replace => true,
26007                        LspInsertMode::ReplaceSubsequence => {
26008                            let mut text_to_replace = buffer.chars_for_range(
26009                                buffer.anchor_before(replace_range.start)
26010                                    ..buffer.anchor_after(replace_range.end),
26011                            );
26012                            let mut current_needle = text_to_replace.next();
26013                            for haystack_ch in completion.label.text.chars() {
26014                                if let Some(needle_ch) = current_needle
26015                                    && haystack_ch.eq_ignore_ascii_case(&needle_ch)
26016                                {
26017                                    current_needle = text_to_replace.next();
26018                                }
26019                            }
26020                            current_needle.is_none()
26021                        }
26022                        LspInsertMode::ReplaceSuffix => {
26023                            if replace_range
26024                                .end
26025                                .cmp(cursor_position, &buffer_snapshot)
26026                                .is_gt()
26027                            {
26028                                let range_after_cursor = *cursor_position..replace_range.end;
26029                                let text_after_cursor = buffer
26030                                    .text_for_range(
26031                                        buffer.anchor_before(range_after_cursor.start)
26032                                            ..buffer.anchor_after(range_after_cursor.end),
26033                                    )
26034                                    .collect::<String>()
26035                                    .to_ascii_lowercase();
26036                                completion
26037                                    .label
26038                                    .text
26039                                    .to_ascii_lowercase()
26040                                    .ends_with(&text_after_cursor)
26041                            } else {
26042                                true
26043                            }
26044                        }
26045                    }
26046                }
26047            };
26048
26049            if should_replace {
26050                replace_range.clone()
26051            } else {
26052                insert_range.clone()
26053            }
26054        } else {
26055            replace_range.clone()
26056        }
26057    };
26058
26059    if range_to_replace
26060        .end
26061        .cmp(cursor_position, &buffer_snapshot)
26062        .is_lt()
26063    {
26064        range_to_replace.end = *cursor_position;
26065    }
26066
26067    let replace_range = range_to_replace.to_offset(buffer);
26068    CompletionEdit {
26069        new_text,
26070        replace_range: BufferOffset(replace_range.start)..BufferOffset(replace_range.end),
26071        snippet,
26072    }
26073}
26074
26075struct CompletionEdit {
26076    new_text: String,
26077    replace_range: Range<BufferOffset>,
26078    snippet: Option<Snippet>,
26079}
26080
26081fn comment_delimiter_for_newline(
26082    start_point: &Point,
26083    buffer: &MultiBufferSnapshot,
26084    language: &LanguageScope,
26085) -> Option<Arc<str>> {
26086    let delimiters = language.line_comment_prefixes();
26087    let max_len_of_delimiter = delimiters.iter().map(|delimiter| delimiter.len()).max()?;
26088    let (snapshot, range) = buffer.buffer_line_for_row(MultiBufferRow(start_point.row))?;
26089
26090    let num_of_whitespaces = snapshot
26091        .chars_for_range(range.clone())
26092        .take_while(|c| c.is_whitespace())
26093        .count();
26094    let comment_candidate = snapshot
26095        .chars_for_range(range.clone())
26096        .skip(num_of_whitespaces)
26097        .take(max_len_of_delimiter + 2)
26098        .collect::<String>();
26099    let (delimiter, trimmed_len, is_repl) = delimiters
26100        .iter()
26101        .filter_map(|delimiter| {
26102            let prefix = delimiter.trim_end();
26103            if comment_candidate.starts_with(prefix) {
26104                let is_repl = if let Some(stripped_comment) = comment_candidate.strip_prefix(prefix)
26105                {
26106                    stripped_comment.starts_with(" %%")
26107                } else {
26108                    false
26109                };
26110                Some((delimiter, prefix.len(), is_repl))
26111            } else {
26112                None
26113            }
26114        })
26115        .max_by_key(|(_, len, _)| *len)?;
26116
26117    if let Some(BlockCommentConfig {
26118        start: block_start, ..
26119    }) = language.block_comment()
26120    {
26121        let block_start_trimmed = block_start.trim_end();
26122        if block_start_trimmed.starts_with(delimiter.trim_end()) {
26123            let line_content = snapshot
26124                .chars_for_range(range.clone())
26125                .skip(num_of_whitespaces)
26126                .take(block_start_trimmed.len())
26127                .collect::<String>();
26128
26129            if line_content.starts_with(block_start_trimmed) {
26130                return None;
26131            }
26132        }
26133    }
26134
26135    let cursor_is_placed_after_comment_marker =
26136        num_of_whitespaces + trimmed_len <= start_point.column as usize;
26137    if cursor_is_placed_after_comment_marker {
26138        if !is_repl {
26139            return Some(delimiter.clone());
26140        }
26141
26142        let line_content_after_cursor: String = snapshot
26143            .chars_for_range(range)
26144            .skip(start_point.column as usize)
26145            .collect();
26146
26147        if line_content_after_cursor.trim().is_empty() {
26148            return None;
26149        } else {
26150            return Some(delimiter.clone());
26151        }
26152    } else {
26153        None
26154    }
26155}
26156
26157fn documentation_delimiter_for_newline(
26158    start_point: &Point,
26159    buffer: &MultiBufferSnapshot,
26160    language: &LanguageScope,
26161    newline_config: &mut NewlineConfig,
26162) -> Option<Arc<str>> {
26163    let BlockCommentConfig {
26164        start: start_tag,
26165        end: end_tag,
26166        prefix: delimiter,
26167        tab_size: len,
26168    } = language.documentation_comment()?;
26169    let is_within_block_comment = buffer
26170        .language_scope_at(*start_point)
26171        .is_some_and(|scope| scope.override_name() == Some("comment"));
26172    if !is_within_block_comment {
26173        return None;
26174    }
26175
26176    let (snapshot, range) = buffer.buffer_line_for_row(MultiBufferRow(start_point.row))?;
26177
26178    let num_of_whitespaces = snapshot
26179        .chars_for_range(range.clone())
26180        .take_while(|c| c.is_whitespace())
26181        .count();
26182
26183    // 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.
26184    let column = start_point.column;
26185    let cursor_is_after_start_tag = {
26186        let start_tag_len = start_tag.len();
26187        let start_tag_line = snapshot
26188            .chars_for_range(range.clone())
26189            .skip(num_of_whitespaces)
26190            .take(start_tag_len)
26191            .collect::<String>();
26192        if start_tag_line.starts_with(start_tag.as_ref()) {
26193            num_of_whitespaces + start_tag_len <= column as usize
26194        } else {
26195            false
26196        }
26197    };
26198
26199    let cursor_is_after_delimiter = {
26200        let delimiter_trim = delimiter.trim_end();
26201        let delimiter_line = snapshot
26202            .chars_for_range(range.clone())
26203            .skip(num_of_whitespaces)
26204            .take(delimiter_trim.len())
26205            .collect::<String>();
26206        if delimiter_line.starts_with(delimiter_trim) {
26207            num_of_whitespaces + delimiter_trim.len() <= column as usize
26208        } else {
26209            false
26210        }
26211    };
26212
26213    let mut needs_extra_line = false;
26214    let mut extra_line_additional_indent = IndentSize::spaces(0);
26215
26216    let cursor_is_before_end_tag_if_exists = {
26217        let mut char_position = 0u32;
26218        let mut end_tag_offset = None;
26219
26220        'outer: for chunk in snapshot.text_for_range(range) {
26221            if let Some(byte_pos) = chunk.find(&**end_tag) {
26222                let chars_before_match = chunk[..byte_pos].chars().count() as u32;
26223                end_tag_offset = Some(char_position + chars_before_match);
26224                break 'outer;
26225            }
26226            char_position += chunk.chars().count() as u32;
26227        }
26228
26229        if let Some(end_tag_offset) = end_tag_offset {
26230            let cursor_is_before_end_tag = column <= end_tag_offset;
26231            if cursor_is_after_start_tag {
26232                if cursor_is_before_end_tag {
26233                    needs_extra_line = true;
26234                }
26235                let cursor_is_at_start_of_end_tag = column == end_tag_offset;
26236                if cursor_is_at_start_of_end_tag {
26237                    extra_line_additional_indent.len = *len;
26238                }
26239            }
26240            cursor_is_before_end_tag
26241        } else {
26242            true
26243        }
26244    };
26245
26246    if (cursor_is_after_start_tag || cursor_is_after_delimiter)
26247        && cursor_is_before_end_tag_if_exists
26248    {
26249        let additional_indent = if cursor_is_after_start_tag {
26250            IndentSize::spaces(*len)
26251        } else {
26252            IndentSize::spaces(0)
26253        };
26254
26255        *newline_config = NewlineConfig::Newline {
26256            additional_indent,
26257            extra_line_additional_indent: if needs_extra_line {
26258                Some(extra_line_additional_indent)
26259            } else {
26260                None
26261            },
26262            prevent_auto_indent: true,
26263        };
26264        Some(delimiter.clone())
26265    } else {
26266        None
26267    }
26268}
26269
26270const ORDERED_LIST_MAX_MARKER_LEN: usize = 16;
26271
26272fn list_delimiter_for_newline(
26273    start_point: &Point,
26274    buffer: &MultiBufferSnapshot,
26275    language: &LanguageScope,
26276    newline_config: &mut NewlineConfig,
26277) -> Option<Arc<str>> {
26278    let (snapshot, range) = buffer.buffer_line_for_row(MultiBufferRow(start_point.row))?;
26279
26280    let num_of_whitespaces = snapshot
26281        .chars_for_range(range.clone())
26282        .take_while(|c| c.is_whitespace())
26283        .count();
26284
26285    let task_list_entries: Vec<_> = language
26286        .task_list()
26287        .into_iter()
26288        .flat_map(|config| {
26289            config
26290                .prefixes
26291                .iter()
26292                .map(|prefix| (prefix.as_ref(), config.continuation.as_ref()))
26293        })
26294        .collect();
26295    let unordered_list_entries: Vec<_> = language
26296        .unordered_list()
26297        .iter()
26298        .map(|marker| (marker.as_ref(), marker.as_ref()))
26299        .collect();
26300
26301    let all_entries: Vec<_> = task_list_entries
26302        .into_iter()
26303        .chain(unordered_list_entries)
26304        .collect();
26305
26306    if let Some(max_prefix_len) = all_entries.iter().map(|(p, _)| p.len()).max() {
26307        let candidate: String = snapshot
26308            .chars_for_range(range.clone())
26309            .skip(num_of_whitespaces)
26310            .take(max_prefix_len)
26311            .collect();
26312
26313        if let Some((prefix, continuation)) = all_entries
26314            .iter()
26315            .filter(|(prefix, _)| candidate.starts_with(*prefix))
26316            .max_by_key(|(prefix, _)| prefix.len())
26317        {
26318            let end_of_prefix = num_of_whitespaces + prefix.len();
26319            let cursor_is_after_prefix = end_of_prefix <= start_point.column as usize;
26320            let has_content_after_marker = snapshot
26321                .chars_for_range(range)
26322                .skip(end_of_prefix)
26323                .any(|c| !c.is_whitespace());
26324
26325            if has_content_after_marker && cursor_is_after_prefix {
26326                return Some((*continuation).into());
26327            }
26328
26329            if start_point.column as usize == end_of_prefix {
26330                if num_of_whitespaces == 0 {
26331                    *newline_config = NewlineConfig::ClearCurrentLine;
26332                } else {
26333                    *newline_config = NewlineConfig::UnindentCurrentLine {
26334                        continuation: (*continuation).into(),
26335                    };
26336                }
26337            }
26338
26339            return None;
26340        }
26341    }
26342
26343    let candidate: String = snapshot
26344        .chars_for_range(range.clone())
26345        .skip(num_of_whitespaces)
26346        .take(ORDERED_LIST_MAX_MARKER_LEN)
26347        .collect();
26348
26349    for ordered_config in language.ordered_list() {
26350        let regex = match Regex::new(&ordered_config.pattern) {
26351            Ok(r) => r,
26352            Err(_) => continue,
26353        };
26354
26355        if let Some(captures) = regex.captures(&candidate) {
26356            let full_match = captures.get(0)?;
26357            let marker_len = full_match.len();
26358            let end_of_prefix = num_of_whitespaces + marker_len;
26359            let cursor_is_after_prefix = end_of_prefix <= start_point.column as usize;
26360
26361            let has_content_after_marker = snapshot
26362                .chars_for_range(range)
26363                .skip(end_of_prefix)
26364                .any(|c| !c.is_whitespace());
26365
26366            if has_content_after_marker && cursor_is_after_prefix {
26367                let number: u32 = captures.get(1)?.as_str().parse().ok()?;
26368                let continuation = ordered_config
26369                    .format
26370                    .replace("{1}", &(number + 1).to_string());
26371                return Some(continuation.into());
26372            }
26373
26374            if start_point.column as usize == end_of_prefix {
26375                let continuation = ordered_config.format.replace("{1}", "1");
26376                if num_of_whitespaces == 0 {
26377                    *newline_config = NewlineConfig::ClearCurrentLine;
26378                } else {
26379                    *newline_config = NewlineConfig::UnindentCurrentLine {
26380                        continuation: continuation.into(),
26381                    };
26382                }
26383            }
26384
26385            return None;
26386        }
26387    }
26388
26389    None
26390}
26391
26392fn is_list_prefix_row(
26393    row: MultiBufferRow,
26394    buffer: &MultiBufferSnapshot,
26395    language: &LanguageScope,
26396) -> bool {
26397    let Some((snapshot, range)) = buffer.buffer_line_for_row(row) else {
26398        return false;
26399    };
26400
26401    let num_of_whitespaces = snapshot
26402        .chars_for_range(range.clone())
26403        .take_while(|c| c.is_whitespace())
26404        .count();
26405
26406    let task_list_prefixes: Vec<_> = language
26407        .task_list()
26408        .into_iter()
26409        .flat_map(|config| {
26410            config
26411                .prefixes
26412                .iter()
26413                .map(|p| p.as_ref())
26414                .collect::<Vec<_>>()
26415        })
26416        .collect();
26417    let unordered_list_markers: Vec<_> = language
26418        .unordered_list()
26419        .iter()
26420        .map(|marker| marker.as_ref())
26421        .collect();
26422    let all_prefixes: Vec<_> = task_list_prefixes
26423        .into_iter()
26424        .chain(unordered_list_markers)
26425        .collect();
26426    if let Some(max_prefix_len) = all_prefixes.iter().map(|p| p.len()).max() {
26427        let candidate: String = snapshot
26428            .chars_for_range(range.clone())
26429            .skip(num_of_whitespaces)
26430            .take(max_prefix_len)
26431            .collect();
26432        if all_prefixes
26433            .iter()
26434            .any(|prefix| candidate.starts_with(*prefix))
26435        {
26436            return true;
26437        }
26438    }
26439
26440    let ordered_list_candidate: String = snapshot
26441        .chars_for_range(range)
26442        .skip(num_of_whitespaces)
26443        .take(ORDERED_LIST_MAX_MARKER_LEN)
26444        .collect();
26445    for ordered_config in language.ordered_list() {
26446        let regex = match Regex::new(&ordered_config.pattern) {
26447            Ok(r) => r,
26448            Err(_) => continue,
26449        };
26450        if let Some(captures) = regex.captures(&ordered_list_candidate) {
26451            return captures.get(0).is_some();
26452        }
26453    }
26454
26455    false
26456}
26457
26458#[derive(Debug)]
26459enum NewlineConfig {
26460    /// Insert newline with optional additional indent and optional extra blank line
26461    Newline {
26462        additional_indent: IndentSize,
26463        extra_line_additional_indent: Option<IndentSize>,
26464        prevent_auto_indent: bool,
26465    },
26466    /// Clear the current line
26467    ClearCurrentLine,
26468    /// Unindent the current line and add continuation
26469    UnindentCurrentLine { continuation: Arc<str> },
26470}
26471
26472impl NewlineConfig {
26473    fn has_extra_line(&self) -> bool {
26474        matches!(
26475            self,
26476            Self::Newline {
26477                extra_line_additional_indent: Some(_),
26478                ..
26479            }
26480        )
26481    }
26482
26483    fn insert_extra_newline_brackets(
26484        buffer: &MultiBufferSnapshot,
26485        range: Range<MultiBufferOffset>,
26486        language: &language::LanguageScope,
26487    ) -> bool {
26488        let leading_whitespace_len = buffer
26489            .reversed_chars_at(range.start)
26490            .take_while(|c| c.is_whitespace() && *c != '\n')
26491            .map(|c| c.len_utf8())
26492            .sum::<usize>();
26493        let trailing_whitespace_len = buffer
26494            .chars_at(range.end)
26495            .take_while(|c| c.is_whitespace() && *c != '\n')
26496            .map(|c| c.len_utf8())
26497            .sum::<usize>();
26498        let range = range.start - leading_whitespace_len..range.end + trailing_whitespace_len;
26499
26500        language.brackets().any(|(pair, enabled)| {
26501            let pair_start = pair.start.trim_end();
26502            let pair_end = pair.end.trim_start();
26503
26504            enabled
26505                && pair.newline
26506                && buffer.contains_str_at(range.end, pair_end)
26507                && buffer.contains_str_at(
26508                    range.start.saturating_sub_usize(pair_start.len()),
26509                    pair_start,
26510                )
26511        })
26512    }
26513
26514    fn insert_extra_newline_tree_sitter(
26515        buffer: &MultiBufferSnapshot,
26516        range: Range<MultiBufferOffset>,
26517    ) -> bool {
26518        let (buffer, range) = match buffer
26519            .range_to_buffer_ranges(range.start..=range.end)
26520            .as_slice()
26521        {
26522            [(buffer, range, _)] => (*buffer, range.clone()),
26523            _ => return false,
26524        };
26525        let pair = {
26526            let mut result: Option<BracketMatch<usize>> = None;
26527
26528            for pair in buffer
26529                .all_bracket_ranges(range.start.0..range.end.0)
26530                .filter(move |pair| {
26531                    pair.open_range.start <= range.start.0 && pair.close_range.end >= range.end.0
26532                })
26533            {
26534                let len = pair.close_range.end - pair.open_range.start;
26535
26536                if let Some(existing) = &result {
26537                    let existing_len = existing.close_range.end - existing.open_range.start;
26538                    if len > existing_len {
26539                        continue;
26540                    }
26541                }
26542
26543                result = Some(pair);
26544            }
26545
26546            result
26547        };
26548        let Some(pair) = pair else {
26549            return false;
26550        };
26551        pair.newline_only
26552            && buffer
26553                .chars_for_range(pair.open_range.end..range.start.0)
26554                .chain(buffer.chars_for_range(range.end.0..pair.close_range.start))
26555                .all(|c| c.is_whitespace() && c != '\n')
26556    }
26557}
26558
26559fn update_uncommitted_diff_for_buffer(
26560    editor: Entity<Editor>,
26561    project: &Entity<Project>,
26562    buffers: impl IntoIterator<Item = Entity<Buffer>>,
26563    buffer: Entity<MultiBuffer>,
26564    cx: &mut App,
26565) -> Task<()> {
26566    let mut tasks = Vec::new();
26567    project.update(cx, |project, cx| {
26568        for buffer in buffers {
26569            if project::File::from_dyn(buffer.read(cx).file()).is_some() {
26570                tasks.push(project.open_uncommitted_diff(buffer.clone(), cx))
26571            }
26572        }
26573    });
26574    cx.spawn(async move |cx| {
26575        let diffs = future::join_all(tasks).await;
26576        if editor.read_with(cx, |editor, _cx| editor.temporary_diff_override) {
26577            return;
26578        }
26579
26580        buffer.update(cx, |buffer, cx| {
26581            for diff in diffs.into_iter().flatten() {
26582                buffer.add_diff(diff, cx);
26583            }
26584        });
26585    })
26586}
26587
26588fn char_len_with_expanded_tabs(offset: usize, text: &str, tab_size: NonZeroU32) -> usize {
26589    let tab_size = tab_size.get() as usize;
26590    let mut width = offset;
26591
26592    for ch in text.chars() {
26593        width += if ch == '\t' {
26594            tab_size - (width % tab_size)
26595        } else {
26596            1
26597        };
26598    }
26599
26600    width - offset
26601}
26602
26603#[cfg(test)]
26604mod tests {
26605    use super::*;
26606
26607    #[test]
26608    fn test_string_size_with_expanded_tabs() {
26609        let nz = |val| NonZeroU32::new(val).unwrap();
26610        assert_eq!(char_len_with_expanded_tabs(0, "", nz(4)), 0);
26611        assert_eq!(char_len_with_expanded_tabs(0, "hello", nz(4)), 5);
26612        assert_eq!(char_len_with_expanded_tabs(0, "\thello", nz(4)), 9);
26613        assert_eq!(char_len_with_expanded_tabs(0, "abc\tab", nz(4)), 6);
26614        assert_eq!(char_len_with_expanded_tabs(0, "hello\t", nz(4)), 8);
26615        assert_eq!(char_len_with_expanded_tabs(0, "\t\t", nz(8)), 16);
26616        assert_eq!(char_len_with_expanded_tabs(0, "x\t", nz(8)), 8);
26617        assert_eq!(char_len_with_expanded_tabs(7, "x\t", nz(8)), 9);
26618    }
26619}
26620
26621/// Tokenizes a string into runs of text that should stick together, or that is whitespace.
26622struct WordBreakingTokenizer<'a> {
26623    input: &'a str,
26624}
26625
26626impl<'a> WordBreakingTokenizer<'a> {
26627    fn new(input: &'a str) -> Self {
26628        Self { input }
26629    }
26630}
26631
26632fn is_char_ideographic(ch: char) -> bool {
26633    use unicode_script::Script::*;
26634    use unicode_script::UnicodeScript;
26635    matches!(ch.script(), Han | Tangut | Yi)
26636}
26637
26638fn is_grapheme_ideographic(text: &str) -> bool {
26639    text.chars().any(is_char_ideographic)
26640}
26641
26642fn is_grapheme_whitespace(text: &str) -> bool {
26643    text.chars().any(|x| x.is_whitespace())
26644}
26645
26646fn should_stay_with_preceding_ideograph(text: &str) -> bool {
26647    text.chars()
26648        .next()
26649        .is_some_and(|ch| matches!(ch, '。' | '、' | ',' | '?' | '!' | ':' | ';' | '…'))
26650}
26651
26652#[derive(PartialEq, Eq, Debug, Clone, Copy)]
26653enum WordBreakToken<'a> {
26654    Word { token: &'a str, grapheme_len: usize },
26655    InlineWhitespace { token: &'a str, grapheme_len: usize },
26656    Newline,
26657}
26658
26659impl<'a> Iterator for WordBreakingTokenizer<'a> {
26660    /// Yields a span, the count of graphemes in the token, and whether it was
26661    /// whitespace. Note that it also breaks at word boundaries.
26662    type Item = WordBreakToken<'a>;
26663
26664    fn next(&mut self) -> Option<Self::Item> {
26665        use unicode_segmentation::UnicodeSegmentation;
26666        if self.input.is_empty() {
26667            return None;
26668        }
26669
26670        let mut iter = self.input.graphemes(true).peekable();
26671        let mut offset = 0;
26672        let mut grapheme_len = 0;
26673        if let Some(first_grapheme) = iter.next() {
26674            let is_newline = first_grapheme == "\n";
26675            let is_whitespace = is_grapheme_whitespace(first_grapheme);
26676            offset += first_grapheme.len();
26677            grapheme_len += 1;
26678            if is_grapheme_ideographic(first_grapheme) && !is_whitespace {
26679                if let Some(grapheme) = iter.peek().copied()
26680                    && should_stay_with_preceding_ideograph(grapheme)
26681                {
26682                    offset += grapheme.len();
26683                    grapheme_len += 1;
26684                }
26685            } else {
26686                let mut words = self.input[offset..].split_word_bound_indices().peekable();
26687                let mut next_word_bound = words.peek().copied();
26688                if next_word_bound.is_some_and(|(i, _)| i == 0) {
26689                    next_word_bound = words.next();
26690                }
26691                while let Some(grapheme) = iter.peek().copied() {
26692                    if next_word_bound.is_some_and(|(i, _)| i == offset) {
26693                        break;
26694                    };
26695                    if is_grapheme_whitespace(grapheme) != is_whitespace
26696                        || (grapheme == "\n") != is_newline
26697                    {
26698                        break;
26699                    };
26700                    offset += grapheme.len();
26701                    grapheme_len += 1;
26702                    iter.next();
26703                }
26704            }
26705            let token = &self.input[..offset];
26706            self.input = &self.input[offset..];
26707            if token == "\n" {
26708                Some(WordBreakToken::Newline)
26709            } else if is_whitespace {
26710                Some(WordBreakToken::InlineWhitespace {
26711                    token,
26712                    grapheme_len,
26713                })
26714            } else {
26715                Some(WordBreakToken::Word {
26716                    token,
26717                    grapheme_len,
26718                })
26719            }
26720        } else {
26721            None
26722        }
26723    }
26724}
26725
26726#[test]
26727fn test_word_breaking_tokenizer() {
26728    let tests: &[(&str, &[WordBreakToken<'static>])] = &[
26729        ("", &[]),
26730        ("  ", &[whitespace("  ", 2)]),
26731        ("Ʒ", &[word("Ʒ", 1)]),
26732        ("Ǽ", &[word("Ǽ", 1)]),
26733        ("", &[word("", 1)]),
26734        ("⋑⋑", &[word("⋑⋑", 2)]),
26735        (
26736            "原理,进而",
26737            &[word("", 1), word("理,", 2), word("", 1), word("", 1)],
26738        ),
26739        (
26740            "hello world",
26741            &[word("hello", 5), whitespace(" ", 1), word("world", 5)],
26742        ),
26743        (
26744            "hello, world",
26745            &[word("hello,", 6), whitespace(" ", 1), word("world", 5)],
26746        ),
26747        (
26748            "  hello world",
26749            &[
26750                whitespace("  ", 2),
26751                word("hello", 5),
26752                whitespace(" ", 1),
26753                word("world", 5),
26754            ],
26755        ),
26756        (
26757            "这是什么 \n 钢笔",
26758            &[
26759                word("", 1),
26760                word("", 1),
26761                word("", 1),
26762                word("", 1),
26763                whitespace(" ", 1),
26764                newline(),
26765                whitespace(" ", 1),
26766                word("", 1),
26767                word("", 1),
26768            ],
26769        ),
26770        (" mutton", &[whitespace("", 1), word("mutton", 6)]),
26771    ];
26772
26773    fn word(token: &'static str, grapheme_len: usize) -> WordBreakToken<'static> {
26774        WordBreakToken::Word {
26775            token,
26776            grapheme_len,
26777        }
26778    }
26779
26780    fn whitespace(token: &'static str, grapheme_len: usize) -> WordBreakToken<'static> {
26781        WordBreakToken::InlineWhitespace {
26782            token,
26783            grapheme_len,
26784        }
26785    }
26786
26787    fn newline() -> WordBreakToken<'static> {
26788        WordBreakToken::Newline
26789    }
26790
26791    for (input, result) in tests {
26792        assert_eq!(
26793            WordBreakingTokenizer::new(input)
26794                .collect::<Vec<_>>()
26795                .as_slice(),
26796            *result,
26797        );
26798    }
26799}
26800
26801fn wrap_with_prefix(
26802    first_line_prefix: String,
26803    subsequent_lines_prefix: String,
26804    unwrapped_text: String,
26805    wrap_column: usize,
26806    tab_size: NonZeroU32,
26807    preserve_existing_whitespace: bool,
26808) -> String {
26809    let first_line_prefix_len = char_len_with_expanded_tabs(0, &first_line_prefix, tab_size);
26810    let subsequent_lines_prefix_len =
26811        char_len_with_expanded_tabs(0, &subsequent_lines_prefix, tab_size);
26812    let mut wrapped_text = String::new();
26813    let mut current_line = first_line_prefix;
26814    let mut is_first_line = true;
26815
26816    let tokenizer = WordBreakingTokenizer::new(&unwrapped_text);
26817    let mut current_line_len = first_line_prefix_len;
26818    let mut in_whitespace = false;
26819    for token in tokenizer {
26820        let have_preceding_whitespace = in_whitespace;
26821        match token {
26822            WordBreakToken::Word {
26823                token,
26824                grapheme_len,
26825            } => {
26826                in_whitespace = false;
26827                let current_prefix_len = if is_first_line {
26828                    first_line_prefix_len
26829                } else {
26830                    subsequent_lines_prefix_len
26831                };
26832                if current_line_len + grapheme_len > wrap_column
26833                    && current_line_len != current_prefix_len
26834                {
26835                    wrapped_text.push_str(current_line.trim_end());
26836                    wrapped_text.push('\n');
26837                    is_first_line = false;
26838                    current_line = subsequent_lines_prefix.clone();
26839                    current_line_len = subsequent_lines_prefix_len;
26840                }
26841                current_line.push_str(token);
26842                current_line_len += grapheme_len;
26843            }
26844            WordBreakToken::InlineWhitespace {
26845                mut token,
26846                mut grapheme_len,
26847            } => {
26848                in_whitespace = true;
26849                if have_preceding_whitespace && !preserve_existing_whitespace {
26850                    continue;
26851                }
26852                if !preserve_existing_whitespace {
26853                    // Keep a single whitespace grapheme as-is
26854                    if let Some(first) =
26855                        unicode_segmentation::UnicodeSegmentation::graphemes(token, true).next()
26856                    {
26857                        token = first;
26858                    } else {
26859                        token = " ";
26860                    }
26861                    grapheme_len = 1;
26862                }
26863                let current_prefix_len = if is_first_line {
26864                    first_line_prefix_len
26865                } else {
26866                    subsequent_lines_prefix_len
26867                };
26868                if current_line_len + grapheme_len > wrap_column {
26869                    wrapped_text.push_str(current_line.trim_end());
26870                    wrapped_text.push('\n');
26871                    is_first_line = false;
26872                    current_line = subsequent_lines_prefix.clone();
26873                    current_line_len = subsequent_lines_prefix_len;
26874                } else if current_line_len != current_prefix_len || preserve_existing_whitespace {
26875                    current_line.push_str(token);
26876                    current_line_len += grapheme_len;
26877                }
26878            }
26879            WordBreakToken::Newline => {
26880                in_whitespace = true;
26881                let current_prefix_len = if is_first_line {
26882                    first_line_prefix_len
26883                } else {
26884                    subsequent_lines_prefix_len
26885                };
26886                if preserve_existing_whitespace {
26887                    wrapped_text.push_str(current_line.trim_end());
26888                    wrapped_text.push('\n');
26889                    is_first_line = false;
26890                    current_line = subsequent_lines_prefix.clone();
26891                    current_line_len = subsequent_lines_prefix_len;
26892                } else if have_preceding_whitespace {
26893                    continue;
26894                } else if current_line_len + 1 > wrap_column
26895                    && current_line_len != current_prefix_len
26896                {
26897                    wrapped_text.push_str(current_line.trim_end());
26898                    wrapped_text.push('\n');
26899                    is_first_line = false;
26900                    current_line = subsequent_lines_prefix.clone();
26901                    current_line_len = subsequent_lines_prefix_len;
26902                } else if current_line_len != current_prefix_len {
26903                    current_line.push(' ');
26904                    current_line_len += 1;
26905                }
26906            }
26907        }
26908    }
26909
26910    if !current_line.is_empty() {
26911        wrapped_text.push_str(&current_line);
26912    }
26913    wrapped_text
26914}
26915
26916#[test]
26917fn test_wrap_with_prefix() {
26918    assert_eq!(
26919        wrap_with_prefix(
26920            "# ".to_string(),
26921            "# ".to_string(),
26922            "abcdefg".to_string(),
26923            4,
26924            NonZeroU32::new(4).unwrap(),
26925            false,
26926        ),
26927        "# abcdefg"
26928    );
26929    assert_eq!(
26930        wrap_with_prefix(
26931            "".to_string(),
26932            "".to_string(),
26933            "\thello world".to_string(),
26934            8,
26935            NonZeroU32::new(4).unwrap(),
26936            false,
26937        ),
26938        "hello\nworld"
26939    );
26940    assert_eq!(
26941        wrap_with_prefix(
26942            "// ".to_string(),
26943            "// ".to_string(),
26944            "xx \nyy zz aa bb cc".to_string(),
26945            12,
26946            NonZeroU32::new(4).unwrap(),
26947            false,
26948        ),
26949        "// xx yy zz\n// aa bb cc"
26950    );
26951    assert_eq!(
26952        wrap_with_prefix(
26953            String::new(),
26954            String::new(),
26955            "这是什么 \n 钢笔".to_string(),
26956            3,
26957            NonZeroU32::new(4).unwrap(),
26958            false,
26959        ),
26960        "这是什\n么 钢\n"
26961    );
26962    assert_eq!(
26963        wrap_with_prefix(
26964            String::new(),
26965            String::new(),
26966            format!("foo{}bar", '\u{2009}'), // thin space
26967            80,
26968            NonZeroU32::new(4).unwrap(),
26969            false,
26970        ),
26971        format!("foo{}bar", '\u{2009}')
26972    );
26973}
26974
26975pub trait CollaborationHub {
26976    fn collaborators<'a>(&self, cx: &'a App) -> &'a HashMap<PeerId, Collaborator>;
26977    fn user_participant_indices<'a>(&self, cx: &'a App) -> &'a HashMap<u64, ParticipantIndex>;
26978    fn user_names(&self, cx: &App) -> HashMap<u64, SharedString>;
26979}
26980
26981impl CollaborationHub for Entity<Project> {
26982    fn collaborators<'a>(&self, cx: &'a App) -> &'a HashMap<PeerId, Collaborator> {
26983        self.read(cx).collaborators()
26984    }
26985
26986    fn user_participant_indices<'a>(&self, cx: &'a App) -> &'a HashMap<u64, ParticipantIndex> {
26987        self.read(cx).user_store().read(cx).participant_indices()
26988    }
26989
26990    fn user_names(&self, cx: &App) -> HashMap<u64, SharedString> {
26991        let this = self.read(cx);
26992        let user_ids = this.collaborators().values().map(|c| c.user_id);
26993        this.user_store().read(cx).participant_names(user_ids, cx)
26994    }
26995}
26996
26997pub trait SemanticsProvider {
26998    fn hover(
26999        &self,
27000        buffer: &Entity<Buffer>,
27001        position: text::Anchor,
27002        cx: &mut App,
27003    ) -> Option<Task<Option<Vec<project::Hover>>>>;
27004
27005    fn inline_values(
27006        &self,
27007        buffer_handle: Entity<Buffer>,
27008        range: Range<text::Anchor>,
27009        cx: &mut App,
27010    ) -> Option<Task<anyhow::Result<Vec<InlayHint>>>>;
27011
27012    fn applicable_inlay_chunks(
27013        &self,
27014        buffer: &Entity<Buffer>,
27015        ranges: &[Range<text::Anchor>],
27016        cx: &mut App,
27017    ) -> Vec<Range<BufferRow>>;
27018
27019    fn invalidate_inlay_hints(&self, for_buffers: &HashSet<BufferId>, cx: &mut App);
27020
27021    fn inlay_hints(
27022        &self,
27023        invalidate: InvalidationStrategy,
27024        buffer: Entity<Buffer>,
27025        ranges: Vec<Range<text::Anchor>>,
27026        known_chunks: Option<(clock::Global, HashSet<Range<BufferRow>>)>,
27027        cx: &mut App,
27028    ) -> Option<HashMap<Range<BufferRow>, Task<Result<CacheInlayHints>>>>;
27029
27030    fn semantic_tokens(
27031        &self,
27032        buffer: Entity<Buffer>,
27033        refresh: Option<RefreshForServer>,
27034        cx: &mut App,
27035    ) -> Option<Shared<Task<std::result::Result<BufferSemanticTokens, Arc<anyhow::Error>>>>>;
27036
27037    fn supports_inlay_hints(&self, buffer: &Entity<Buffer>, cx: &mut App) -> bool;
27038
27039    fn supports_semantic_tokens(&self, buffer: &Entity<Buffer>, cx: &mut App) -> bool;
27040
27041    fn document_highlights(
27042        &self,
27043        buffer: &Entity<Buffer>,
27044        position: text::Anchor,
27045        cx: &mut App,
27046    ) -> Option<Task<Result<Vec<DocumentHighlight>>>>;
27047
27048    fn definitions(
27049        &self,
27050        buffer: &Entity<Buffer>,
27051        position: text::Anchor,
27052        kind: GotoDefinitionKind,
27053        cx: &mut App,
27054    ) -> Option<Task<Result<Option<Vec<LocationLink>>>>>;
27055
27056    fn range_for_rename(
27057        &self,
27058        buffer: &Entity<Buffer>,
27059        position: text::Anchor,
27060        cx: &mut App,
27061    ) -> Option<Task<Result<Option<Range<text::Anchor>>>>>;
27062
27063    fn perform_rename(
27064        &self,
27065        buffer: &Entity<Buffer>,
27066        position: text::Anchor,
27067        new_name: String,
27068        cx: &mut App,
27069    ) -> Option<Task<Result<ProjectTransaction>>>;
27070}
27071
27072pub trait CompletionProvider {
27073    fn completions(
27074        &self,
27075        excerpt_id: ExcerptId,
27076        buffer: &Entity<Buffer>,
27077        buffer_position: text::Anchor,
27078        trigger: CompletionContext,
27079        window: &mut Window,
27080        cx: &mut Context<Editor>,
27081    ) -> Task<Result<Vec<CompletionResponse>>>;
27082
27083    fn resolve_completions(
27084        &self,
27085        _buffer: Entity<Buffer>,
27086        _completion_indices: Vec<usize>,
27087        _completions: Rc<RefCell<Box<[Completion]>>>,
27088        _cx: &mut Context<Editor>,
27089    ) -> Task<Result<bool>> {
27090        Task::ready(Ok(false))
27091    }
27092
27093    fn apply_additional_edits_for_completion(
27094        &self,
27095        _buffer: Entity<Buffer>,
27096        _completions: Rc<RefCell<Box<[Completion]>>>,
27097        _completion_index: usize,
27098        _push_to_history: bool,
27099        _all_commit_ranges: Vec<Range<language::Anchor>>,
27100        _cx: &mut Context<Editor>,
27101    ) -> Task<Result<Option<language::Transaction>>> {
27102        Task::ready(Ok(None))
27103    }
27104
27105    fn is_completion_trigger(
27106        &self,
27107        buffer: &Entity<Buffer>,
27108        position: language::Anchor,
27109        text: &str,
27110        trigger_in_words: bool,
27111        cx: &mut Context<Editor>,
27112    ) -> bool;
27113
27114    fn selection_changed(&self, _mat: Option<&StringMatch>, _window: &mut Window, _cx: &mut App) {}
27115
27116    fn sort_completions(&self) -> bool {
27117        true
27118    }
27119
27120    fn filter_completions(&self) -> bool {
27121        true
27122    }
27123
27124    fn show_snippets(&self) -> bool {
27125        false
27126    }
27127}
27128
27129pub trait CodeActionProvider {
27130    fn id(&self) -> Arc<str>;
27131
27132    fn code_actions(
27133        &self,
27134        buffer: &Entity<Buffer>,
27135        range: Range<text::Anchor>,
27136        window: &mut Window,
27137        cx: &mut App,
27138    ) -> Task<Result<Vec<CodeAction>>>;
27139
27140    fn apply_code_action(
27141        &self,
27142        buffer_handle: Entity<Buffer>,
27143        action: CodeAction,
27144        excerpt_id: ExcerptId,
27145        push_to_history: bool,
27146        window: &mut Window,
27147        cx: &mut App,
27148    ) -> Task<Result<ProjectTransaction>>;
27149}
27150
27151impl CodeActionProvider for Entity<Project> {
27152    fn id(&self) -> Arc<str> {
27153        "project".into()
27154    }
27155
27156    fn code_actions(
27157        &self,
27158        buffer: &Entity<Buffer>,
27159        range: Range<text::Anchor>,
27160        _window: &mut Window,
27161        cx: &mut App,
27162    ) -> Task<Result<Vec<CodeAction>>> {
27163        self.update(cx, |project, cx| {
27164            let code_lens_actions = project.code_lens_actions(buffer, range.clone(), cx);
27165            let code_actions = project.code_actions(buffer, range, None, cx);
27166            cx.background_spawn(async move {
27167                let (code_lens_actions, code_actions) = join(code_lens_actions, code_actions).await;
27168                Ok(code_lens_actions
27169                    .context("code lens fetch")?
27170                    .into_iter()
27171                    .flatten()
27172                    .chain(
27173                        code_actions
27174                            .context("code action fetch")?
27175                            .into_iter()
27176                            .flatten(),
27177                    )
27178                    .collect())
27179            })
27180        })
27181    }
27182
27183    fn apply_code_action(
27184        &self,
27185        buffer_handle: Entity<Buffer>,
27186        action: CodeAction,
27187        _excerpt_id: ExcerptId,
27188        push_to_history: bool,
27189        _window: &mut Window,
27190        cx: &mut App,
27191    ) -> Task<Result<ProjectTransaction>> {
27192        self.update(cx, |project, cx| {
27193            project.apply_code_action(buffer_handle, action, push_to_history, cx)
27194        })
27195    }
27196}
27197
27198fn snippet_completions(
27199    project: &Project,
27200    buffer: &Entity<Buffer>,
27201    buffer_anchor: text::Anchor,
27202    classifier: CharClassifier,
27203    cx: &mut App,
27204) -> Task<Result<CompletionResponse>> {
27205    let languages = buffer.read(cx).languages_at(buffer_anchor);
27206    let snippet_store = project.snippets().read(cx);
27207
27208    let scopes: Vec<_> = languages
27209        .iter()
27210        .filter_map(|language| {
27211            let language_name = language.lsp_id();
27212            let snippets = snippet_store.snippets_for(Some(language_name), cx);
27213
27214            if snippets.is_empty() {
27215                None
27216            } else {
27217                Some((language.default_scope(), snippets))
27218            }
27219        })
27220        .collect();
27221
27222    if scopes.is_empty() {
27223        return Task::ready(Ok(CompletionResponse {
27224            completions: vec![],
27225            display_options: CompletionDisplayOptions::default(),
27226            is_incomplete: false,
27227        }));
27228    }
27229
27230    let snapshot = buffer.read(cx).text_snapshot();
27231    let executor = cx.background_executor().clone();
27232
27233    cx.background_spawn(async move {
27234        let is_word_char = |c| classifier.is_word(c);
27235
27236        let mut is_incomplete = false;
27237        let mut completions: Vec<Completion> = Vec::new();
27238
27239        const MAX_PREFIX_LEN: usize = 128;
27240        let buffer_offset = text::ToOffset::to_offset(&buffer_anchor, &snapshot);
27241        let window_start = buffer_offset.saturating_sub(MAX_PREFIX_LEN);
27242        let window_start = snapshot.clip_offset(window_start, Bias::Left);
27243
27244        let max_buffer_window: String = snapshot
27245            .text_for_range(window_start..buffer_offset)
27246            .collect();
27247
27248        if max_buffer_window.is_empty() {
27249            return Ok(CompletionResponse {
27250                completions: vec![],
27251                display_options: CompletionDisplayOptions::default(),
27252                is_incomplete: true,
27253            });
27254        }
27255
27256        for (_scope, snippets) in scopes.into_iter() {
27257            // Sort snippets by word count to match longer snippet prefixes first.
27258            let mut sorted_snippet_candidates = snippets
27259                .iter()
27260                .enumerate()
27261                .flat_map(|(snippet_ix, snippet)| {
27262                    snippet
27263                        .prefix
27264                        .iter()
27265                        .enumerate()
27266                        .map(move |(prefix_ix, prefix)| {
27267                            let word_count =
27268                                snippet_candidate_suffixes(prefix, &is_word_char).count();
27269                            ((snippet_ix, prefix_ix), prefix, word_count)
27270                        })
27271                })
27272                .collect_vec();
27273            sorted_snippet_candidates
27274                .sort_unstable_by_key(|(_, _, word_count)| Reverse(*word_count));
27275
27276            // Each prefix may be matched multiple times; the completion menu must filter out duplicates.
27277
27278            let buffer_windows = snippet_candidate_suffixes(&max_buffer_window, &is_word_char)
27279                .take(
27280                    sorted_snippet_candidates
27281                        .first()
27282                        .map(|(_, _, word_count)| *word_count)
27283                        .unwrap_or_default(),
27284                )
27285                .collect_vec();
27286
27287            const MAX_RESULTS: usize = 100;
27288            // Each match also remembers how many characters from the buffer it consumed
27289            let mut matches: Vec<(StringMatch, usize)> = vec![];
27290
27291            let mut snippet_list_cutoff_index = 0;
27292            for (buffer_index, buffer_window) in buffer_windows.iter().enumerate().rev() {
27293                let word_count = buffer_index + 1;
27294                // Increase `snippet_list_cutoff_index` until we have all of the
27295                // snippets with sufficiently many words.
27296                while sorted_snippet_candidates
27297                    .get(snippet_list_cutoff_index)
27298                    .is_some_and(|(_ix, _prefix, snippet_word_count)| {
27299                        *snippet_word_count >= word_count
27300                    })
27301                {
27302                    snippet_list_cutoff_index += 1;
27303                }
27304
27305                // Take only the candidates with at least `word_count` many words
27306                let snippet_candidates_at_word_len =
27307                    &sorted_snippet_candidates[..snippet_list_cutoff_index];
27308
27309                let candidates = snippet_candidates_at_word_len
27310                    .iter()
27311                    .map(|(_snippet_ix, prefix, _snippet_word_count)| prefix)
27312                    .enumerate() // index in `sorted_snippet_candidates`
27313                    // First char must match
27314                    .filter(|(_ix, prefix)| {
27315                        itertools::equal(
27316                            prefix
27317                                .chars()
27318                                .next()
27319                                .into_iter()
27320                                .flat_map(|c| c.to_lowercase()),
27321                            buffer_window
27322                                .chars()
27323                                .next()
27324                                .into_iter()
27325                                .flat_map(|c| c.to_lowercase()),
27326                        )
27327                    })
27328                    .map(|(ix, prefix)| StringMatchCandidate::new(ix, prefix))
27329                    .collect::<Vec<StringMatchCandidate>>();
27330
27331                matches.extend(
27332                    fuzzy::match_strings(
27333                        &candidates,
27334                        &buffer_window,
27335                        buffer_window.chars().any(|c| c.is_uppercase()),
27336                        true,
27337                        MAX_RESULTS - matches.len(), // always prioritize longer snippets
27338                        &Default::default(),
27339                        executor.clone(),
27340                    )
27341                    .await
27342                    .into_iter()
27343                    .map(|string_match| (string_match, buffer_window.len())),
27344                );
27345
27346                if matches.len() >= MAX_RESULTS {
27347                    break;
27348                }
27349            }
27350
27351            let to_lsp = |point: &text::Anchor| {
27352                let end = text::ToPointUtf16::to_point_utf16(point, &snapshot);
27353                point_to_lsp(end)
27354            };
27355            let lsp_end = to_lsp(&buffer_anchor);
27356
27357            if matches.len() >= MAX_RESULTS {
27358                is_incomplete = true;
27359            }
27360
27361            completions.extend(matches.iter().map(|(string_match, buffer_window_len)| {
27362                let ((snippet_index, prefix_index), matching_prefix, _snippet_word_count) =
27363                    sorted_snippet_candidates[string_match.candidate_id];
27364                let snippet = &snippets[snippet_index];
27365                let start = buffer_offset - buffer_window_len;
27366                let start = snapshot.anchor_before(start);
27367                let range = start..buffer_anchor;
27368                let lsp_start = to_lsp(&start);
27369                let lsp_range = lsp::Range {
27370                    start: lsp_start,
27371                    end: lsp_end,
27372                };
27373                Completion {
27374                    replace_range: range,
27375                    new_text: snippet.body.clone(),
27376                    source: CompletionSource::Lsp {
27377                        insert_range: None,
27378                        server_id: LanguageServerId(usize::MAX),
27379                        resolved: true,
27380                        lsp_completion: Box::new(lsp::CompletionItem {
27381                            label: snippet.prefix.first().unwrap().clone(),
27382                            kind: Some(CompletionItemKind::SNIPPET),
27383                            label_details: snippet.description.as_ref().map(|description| {
27384                                lsp::CompletionItemLabelDetails {
27385                                    detail: Some(description.clone()),
27386                                    description: None,
27387                                }
27388                            }),
27389                            insert_text_format: Some(InsertTextFormat::SNIPPET),
27390                            text_edit: Some(lsp::CompletionTextEdit::InsertAndReplace(
27391                                lsp::InsertReplaceEdit {
27392                                    new_text: snippet.body.clone(),
27393                                    insert: lsp_range,
27394                                    replace: lsp_range,
27395                                },
27396                            )),
27397                            filter_text: Some(snippet.body.clone()),
27398                            sort_text: Some(char::MAX.to_string()),
27399                            ..lsp::CompletionItem::default()
27400                        }),
27401                        lsp_defaults: None,
27402                    },
27403                    label: CodeLabel {
27404                        text: matching_prefix.clone(),
27405                        runs: Vec::new(),
27406                        filter_range: 0..matching_prefix.len(),
27407                    },
27408                    icon_path: None,
27409                    documentation: Some(CompletionDocumentation::SingleLineAndMultiLinePlainText {
27410                        single_line: snippet.name.clone().into(),
27411                        plain_text: snippet
27412                            .description
27413                            .clone()
27414                            .map(|description| description.into()),
27415                    }),
27416                    insert_text_mode: None,
27417                    confirm: None,
27418                    match_start: Some(start),
27419                    snippet_deduplication_key: Some((snippet_index, prefix_index)),
27420                }
27421            }));
27422        }
27423
27424        Ok(CompletionResponse {
27425            completions,
27426            display_options: CompletionDisplayOptions::default(),
27427            is_incomplete,
27428        })
27429    })
27430}
27431
27432impl CompletionProvider for Entity<Project> {
27433    fn completions(
27434        &self,
27435        _excerpt_id: ExcerptId,
27436        buffer: &Entity<Buffer>,
27437        buffer_position: text::Anchor,
27438        options: CompletionContext,
27439        _window: &mut Window,
27440        cx: &mut Context<Editor>,
27441    ) -> Task<Result<Vec<CompletionResponse>>> {
27442        self.update(cx, |project, cx| {
27443            let task = project.completions(buffer, buffer_position, options, cx);
27444            cx.background_spawn(task)
27445        })
27446    }
27447
27448    fn resolve_completions(
27449        &self,
27450        buffer: Entity<Buffer>,
27451        completion_indices: Vec<usize>,
27452        completions: Rc<RefCell<Box<[Completion]>>>,
27453        cx: &mut Context<Editor>,
27454    ) -> Task<Result<bool>> {
27455        self.update(cx, |project, cx| {
27456            project.lsp_store().update(cx, |lsp_store, cx| {
27457                lsp_store.resolve_completions(buffer, completion_indices, completions, cx)
27458            })
27459        })
27460    }
27461
27462    fn apply_additional_edits_for_completion(
27463        &self,
27464        buffer: Entity<Buffer>,
27465        completions: Rc<RefCell<Box<[Completion]>>>,
27466        completion_index: usize,
27467        push_to_history: bool,
27468        all_commit_ranges: Vec<Range<language::Anchor>>,
27469        cx: &mut Context<Editor>,
27470    ) -> Task<Result<Option<language::Transaction>>> {
27471        self.update(cx, |project, cx| {
27472            project.lsp_store().update(cx, |lsp_store, cx| {
27473                lsp_store.apply_additional_edits_for_completion(
27474                    buffer,
27475                    completions,
27476                    completion_index,
27477                    push_to_history,
27478                    all_commit_ranges,
27479                    cx,
27480                )
27481            })
27482        })
27483    }
27484
27485    fn is_completion_trigger(
27486        &self,
27487        buffer: &Entity<Buffer>,
27488        position: language::Anchor,
27489        text: &str,
27490        trigger_in_words: bool,
27491        cx: &mut Context<Editor>,
27492    ) -> bool {
27493        let mut chars = text.chars();
27494        let char = if let Some(char) = chars.next() {
27495            char
27496        } else {
27497            return false;
27498        };
27499        if chars.next().is_some() {
27500            return false;
27501        }
27502
27503        let buffer = buffer.read(cx);
27504        let snapshot = buffer.snapshot();
27505        let classifier = snapshot
27506            .char_classifier_at(position)
27507            .scope_context(Some(CharScopeContext::Completion));
27508        if trigger_in_words && classifier.is_word(char) {
27509            return true;
27510        }
27511
27512        buffer.completion_triggers().contains(text)
27513    }
27514
27515    fn show_snippets(&self) -> bool {
27516        true
27517    }
27518}
27519
27520impl SemanticsProvider for WeakEntity<Project> {
27521    fn hover(
27522        &self,
27523        buffer: &Entity<Buffer>,
27524        position: text::Anchor,
27525        cx: &mut App,
27526    ) -> Option<Task<Option<Vec<project::Hover>>>> {
27527        self.update(cx, |project, cx| project.hover(buffer, position, cx))
27528            .ok()
27529    }
27530
27531    fn document_highlights(
27532        &self,
27533        buffer: &Entity<Buffer>,
27534        position: text::Anchor,
27535        cx: &mut App,
27536    ) -> Option<Task<Result<Vec<DocumentHighlight>>>> {
27537        self.update(cx, |project, cx| {
27538            project.document_highlights(buffer, position, cx)
27539        })
27540        .ok()
27541    }
27542
27543    fn definitions(
27544        &self,
27545        buffer: &Entity<Buffer>,
27546        position: text::Anchor,
27547        kind: GotoDefinitionKind,
27548        cx: &mut App,
27549    ) -> Option<Task<Result<Option<Vec<LocationLink>>>>> {
27550        self.update(cx, |project, cx| match kind {
27551            GotoDefinitionKind::Symbol => project.definitions(buffer, position, cx),
27552            GotoDefinitionKind::Declaration => project.declarations(buffer, position, cx),
27553            GotoDefinitionKind::Type => project.type_definitions(buffer, position, cx),
27554            GotoDefinitionKind::Implementation => project.implementations(buffer, position, cx),
27555        })
27556        .ok()
27557    }
27558
27559    fn supports_inlay_hints(&self, buffer: &Entity<Buffer>, cx: &mut App) -> bool {
27560        self.update(cx, |project, cx| {
27561            if project
27562                .active_debug_session(cx)
27563                .is_some_and(|(session, _)| session.read(cx).any_stopped_thread())
27564            {
27565                return true;
27566            }
27567
27568            buffer.update(cx, |buffer, cx| {
27569                project.any_language_server_supports_inlay_hints(buffer, cx)
27570            })
27571        })
27572        .unwrap_or(false)
27573    }
27574
27575    fn supports_semantic_tokens(&self, buffer: &Entity<Buffer>, cx: &mut App) -> bool {
27576        self.update(cx, |project, cx| {
27577            buffer.update(cx, |buffer, cx| {
27578                project.any_language_server_supports_semantic_tokens(buffer, cx)
27579            })
27580        })
27581        .unwrap_or(false)
27582    }
27583
27584    fn inline_values(
27585        &self,
27586        buffer_handle: Entity<Buffer>,
27587        range: Range<text::Anchor>,
27588        cx: &mut App,
27589    ) -> Option<Task<anyhow::Result<Vec<InlayHint>>>> {
27590        self.update(cx, |project, cx| {
27591            let (session, active_stack_frame) = project.active_debug_session(cx)?;
27592
27593            Some(project.inline_values(session, active_stack_frame, buffer_handle, range, cx))
27594        })
27595        .ok()
27596        .flatten()
27597    }
27598
27599    fn applicable_inlay_chunks(
27600        &self,
27601        buffer: &Entity<Buffer>,
27602        ranges: &[Range<text::Anchor>],
27603        cx: &mut App,
27604    ) -> Vec<Range<BufferRow>> {
27605        self.update(cx, |project, cx| {
27606            project.lsp_store().update(cx, |lsp_store, cx| {
27607                lsp_store.applicable_inlay_chunks(buffer, ranges, cx)
27608            })
27609        })
27610        .unwrap_or_default()
27611    }
27612
27613    fn invalidate_inlay_hints(&self, for_buffers: &HashSet<BufferId>, cx: &mut App) {
27614        self.update(cx, |project, cx| {
27615            project.lsp_store().update(cx, |lsp_store, _| {
27616                lsp_store.invalidate_inlay_hints(for_buffers)
27617            })
27618        })
27619        .ok();
27620    }
27621
27622    fn inlay_hints(
27623        &self,
27624        invalidate: InvalidationStrategy,
27625        buffer: Entity<Buffer>,
27626        ranges: Vec<Range<text::Anchor>>,
27627        known_chunks: Option<(clock::Global, HashSet<Range<BufferRow>>)>,
27628        cx: &mut App,
27629    ) -> Option<HashMap<Range<BufferRow>, Task<Result<CacheInlayHints>>>> {
27630        self.update(cx, |project, cx| {
27631            project.lsp_store().update(cx, |lsp_store, cx| {
27632                lsp_store.inlay_hints(invalidate, buffer, ranges, known_chunks, cx)
27633            })
27634        })
27635        .ok()
27636    }
27637
27638    fn semantic_tokens(
27639        &self,
27640        buffer: Entity<Buffer>,
27641        refresh: Option<RefreshForServer>,
27642        cx: &mut App,
27643    ) -> Option<Shared<Task<std::result::Result<BufferSemanticTokens, Arc<anyhow::Error>>>>> {
27644        self.update(cx, |this, cx| {
27645            this.lsp_store().update(cx, |lsp_store, cx| {
27646                lsp_store.semantic_tokens(buffer, refresh, cx)
27647            })
27648        })
27649        .ok()
27650    }
27651
27652    fn range_for_rename(
27653        &self,
27654        buffer: &Entity<Buffer>,
27655        position: text::Anchor,
27656        cx: &mut App,
27657    ) -> Option<Task<Result<Option<Range<text::Anchor>>>>> {
27658        self.update(cx, |project, cx| {
27659            let buffer = buffer.clone();
27660            let task = project.prepare_rename(buffer.clone(), position, cx);
27661            cx.spawn(async move |_, cx| {
27662                Ok(match task.await? {
27663                    PrepareRenameResponse::Success(range) => Some(range),
27664                    PrepareRenameResponse::InvalidPosition => None,
27665                    PrepareRenameResponse::OnlyUnpreparedRenameSupported => {
27666                        // Fallback on using TreeSitter info to determine identifier range
27667                        buffer.read_with(cx, |buffer, _| {
27668                            let snapshot = buffer.snapshot();
27669                            let (range, kind) = snapshot.surrounding_word(position, None);
27670                            if kind != Some(CharKind::Word) {
27671                                return None;
27672                            }
27673                            Some(
27674                                snapshot.anchor_before(range.start)
27675                                    ..snapshot.anchor_after(range.end),
27676                            )
27677                        })
27678                    }
27679                })
27680            })
27681        })
27682        .ok()
27683    }
27684
27685    fn perform_rename(
27686        &self,
27687        buffer: &Entity<Buffer>,
27688        position: text::Anchor,
27689        new_name: String,
27690        cx: &mut App,
27691    ) -> Option<Task<Result<ProjectTransaction>>> {
27692        self.update(cx, |project, cx| {
27693            project.perform_rename(buffer.clone(), position, new_name, cx)
27694        })
27695        .ok()
27696    }
27697}
27698
27699fn consume_contiguous_rows(
27700    contiguous_row_selections: &mut Vec<Selection<Point>>,
27701    selection: &Selection<Point>,
27702    display_map: &DisplaySnapshot,
27703    selections: &mut Peekable<std::slice::Iter<Selection<Point>>>,
27704) -> (MultiBufferRow, MultiBufferRow) {
27705    contiguous_row_selections.push(selection.clone());
27706    let start_row = starting_row(selection, display_map);
27707    let mut end_row = ending_row(selection, display_map);
27708
27709    while let Some(next_selection) = selections.peek() {
27710        if next_selection.start.row <= end_row.0 {
27711            end_row = ending_row(next_selection, display_map);
27712            contiguous_row_selections.push(selections.next().unwrap().clone());
27713        } else {
27714            break;
27715        }
27716    }
27717    (start_row, end_row)
27718}
27719
27720fn starting_row(selection: &Selection<Point>, display_map: &DisplaySnapshot) -> MultiBufferRow {
27721    if selection.start.column > 0 {
27722        MultiBufferRow(display_map.prev_line_boundary(selection.start).0.row)
27723    } else {
27724        MultiBufferRow(selection.start.row)
27725    }
27726}
27727
27728fn ending_row(next_selection: &Selection<Point>, display_map: &DisplaySnapshot) -> MultiBufferRow {
27729    if next_selection.end.column > 0 || next_selection.is_empty() {
27730        MultiBufferRow(display_map.next_line_boundary(next_selection.end).0.row + 1)
27731    } else {
27732        MultiBufferRow(next_selection.end.row)
27733    }
27734}
27735
27736impl EditorSnapshot {
27737    pub fn remote_selections_in_range<'a>(
27738        &'a self,
27739        range: &'a Range<Anchor>,
27740        collaboration_hub: &dyn CollaborationHub,
27741        cx: &'a App,
27742    ) -> impl 'a + Iterator<Item = RemoteSelection> {
27743        let participant_names = collaboration_hub.user_names(cx);
27744        let participant_indices = collaboration_hub.user_participant_indices(cx);
27745        let collaborators_by_peer_id = collaboration_hub.collaborators(cx);
27746        let collaborators_by_replica_id = collaborators_by_peer_id
27747            .values()
27748            .map(|collaborator| (collaborator.replica_id, collaborator))
27749            .collect::<HashMap<_, _>>();
27750        self.buffer_snapshot()
27751            .selections_in_range(range, false)
27752            .filter_map(move |(replica_id, line_mode, cursor_shape, selection)| {
27753                if replica_id == ReplicaId::AGENT {
27754                    Some(RemoteSelection {
27755                        replica_id,
27756                        selection,
27757                        cursor_shape,
27758                        line_mode,
27759                        collaborator_id: CollaboratorId::Agent,
27760                        user_name: Some("Agent".into()),
27761                        color: cx.theme().players().agent(),
27762                    })
27763                } else {
27764                    let collaborator = collaborators_by_replica_id.get(&replica_id)?;
27765                    let participant_index = participant_indices.get(&collaborator.user_id).copied();
27766                    let user_name = participant_names.get(&collaborator.user_id).cloned();
27767                    Some(RemoteSelection {
27768                        replica_id,
27769                        selection,
27770                        cursor_shape,
27771                        line_mode,
27772                        collaborator_id: CollaboratorId::PeerId(collaborator.peer_id),
27773                        user_name,
27774                        color: if let Some(index) = participant_index {
27775                            cx.theme().players().color_for_participant(index.0)
27776                        } else {
27777                            cx.theme().players().absent()
27778                        },
27779                    })
27780                }
27781            })
27782    }
27783
27784    pub fn hunks_for_ranges(
27785        &self,
27786        ranges: impl IntoIterator<Item = Range<Point>>,
27787    ) -> Vec<MultiBufferDiffHunk> {
27788        let mut hunks = Vec::new();
27789        let mut processed_buffer_rows: HashMap<BufferId, HashSet<Range<text::Anchor>>> =
27790            HashMap::default();
27791        for query_range in ranges {
27792            let query_rows =
27793                MultiBufferRow(query_range.start.row)..MultiBufferRow(query_range.end.row + 1);
27794            for hunk in self.buffer_snapshot().diff_hunks_in_range(
27795                Point::new(query_rows.start.0, 0)..Point::new(query_rows.end.0, 0),
27796            ) {
27797                // Include deleted hunks that are adjacent to the query range, because
27798                // otherwise they would be missed.
27799                let mut intersects_range = hunk.row_range.overlaps(&query_rows);
27800                if hunk.status().is_deleted() {
27801                    intersects_range |= hunk.row_range.start == query_rows.end;
27802                    intersects_range |= hunk.row_range.end == query_rows.start;
27803                }
27804                if intersects_range {
27805                    if !processed_buffer_rows
27806                        .entry(hunk.buffer_id)
27807                        .or_default()
27808                        .insert(hunk.buffer_range.start..hunk.buffer_range.end)
27809                    {
27810                        continue;
27811                    }
27812                    hunks.push(hunk);
27813                }
27814            }
27815        }
27816
27817        hunks
27818    }
27819
27820    fn display_diff_hunks_for_rows<'a>(
27821        &'a self,
27822        display_rows: Range<DisplayRow>,
27823        folded_buffers: &'a HashSet<BufferId>,
27824    ) -> impl 'a + Iterator<Item = DisplayDiffHunk> {
27825        let buffer_start = DisplayPoint::new(display_rows.start, 0).to_point(self);
27826        let buffer_end = DisplayPoint::new(display_rows.end, 0).to_point(self);
27827
27828        self.buffer_snapshot()
27829            .diff_hunks_in_range(buffer_start..buffer_end)
27830            .filter_map(|hunk| {
27831                if folded_buffers.contains(&hunk.buffer_id)
27832                    || (hunk.row_range.is_empty() && self.buffer.all_diff_hunks_expanded())
27833                {
27834                    return None;
27835                }
27836
27837                let hunk_start_point = Point::new(hunk.row_range.start.0, 0);
27838                let hunk_end_point = if hunk.row_range.end > hunk.row_range.start {
27839                    let last_row = MultiBufferRow(hunk.row_range.end.0 - 1);
27840                    let line_len = self.buffer_snapshot().line_len(last_row);
27841                    Point::new(last_row.0, line_len)
27842                } else {
27843                    Point::new(hunk.row_range.end.0, 0)
27844                };
27845
27846                let hunk_display_start = self.point_to_display_point(hunk_start_point, Bias::Left);
27847                let hunk_display_end = self.point_to_display_point(hunk_end_point, Bias::Right);
27848
27849                let display_hunk = if hunk_display_start.column() != 0 {
27850                    DisplayDiffHunk::Folded {
27851                        display_row: hunk_display_start.row(),
27852                    }
27853                } else {
27854                    let mut end_row = hunk_display_end.row();
27855                    if hunk.row_range.end > hunk.row_range.start || hunk_display_end.column() > 0 {
27856                        end_row.0 += 1;
27857                    }
27858                    let is_created_file = hunk.is_created_file();
27859
27860                    DisplayDiffHunk::Unfolded {
27861                        status: hunk.status(),
27862                        diff_base_byte_range: hunk.diff_base_byte_range.start.0
27863                            ..hunk.diff_base_byte_range.end.0,
27864                        word_diffs: hunk.word_diffs,
27865                        display_row_range: hunk_display_start.row()..end_row,
27866                        multi_buffer_range: Anchor::range_in_buffer(
27867                            hunk.excerpt_id,
27868                            hunk.buffer_range,
27869                        ),
27870                        is_created_file,
27871                    }
27872                };
27873
27874                Some(display_hunk)
27875            })
27876    }
27877
27878    pub fn language_at<T: ToOffset>(&self, position: T) -> Option<&Arc<Language>> {
27879        self.display_snapshot
27880            .buffer_snapshot()
27881            .language_at(position)
27882    }
27883
27884    pub fn is_focused(&self) -> bool {
27885        self.is_focused
27886    }
27887
27888    pub fn placeholder_text(&self) -> Option<String> {
27889        self.placeholder_display_snapshot
27890            .as_ref()
27891            .map(|display_map| display_map.text())
27892    }
27893
27894    pub fn scroll_position(&self) -> gpui::Point<ScrollOffset> {
27895        self.scroll_anchor.scroll_position(&self.display_snapshot)
27896    }
27897
27898    pub fn gutter_dimensions(
27899        &self,
27900        font_id: FontId,
27901        font_size: Pixels,
27902        style: &EditorStyle,
27903        window: &mut Window,
27904        cx: &App,
27905    ) -> GutterDimensions {
27906        if self.show_gutter
27907            && let Some(ch_width) = cx.text_system().ch_width(font_id, font_size).log_err()
27908            && let Some(ch_advance) = cx.text_system().ch_advance(font_id, font_size).log_err()
27909        {
27910            let show_git_gutter = self.show_git_diff_gutter.unwrap_or_else(|| {
27911                matches!(
27912                    ProjectSettings::get_global(cx).git.git_gutter,
27913                    GitGutterSetting::TrackedFiles
27914                )
27915            });
27916            let gutter_settings = EditorSettings::get_global(cx).gutter;
27917            let show_line_numbers = self
27918                .show_line_numbers
27919                .unwrap_or(gutter_settings.line_numbers);
27920            let line_gutter_width = if show_line_numbers {
27921                // Avoid flicker-like gutter resizes when the line number gains another digit by
27922                // only resizing the gutter on files with > 10**min_line_number_digits lines.
27923                let min_width_for_number_on_gutter =
27924                    ch_advance * gutter_settings.min_line_number_digits as f32;
27925                self.max_line_number_width(style, window)
27926                    .max(min_width_for_number_on_gutter)
27927            } else {
27928                0.0.into()
27929            };
27930
27931            let show_runnables = self.show_runnables.unwrap_or(gutter_settings.runnables);
27932            let show_breakpoints = self.show_breakpoints.unwrap_or(gutter_settings.breakpoints);
27933
27934            let git_blame_entries_width =
27935                self.git_blame_gutter_max_author_length
27936                    .map(|max_author_length| {
27937                        let renderer = cx.global::<GlobalBlameRenderer>().0.clone();
27938                        const MAX_RELATIVE_TIMESTAMP: &str = "60 minutes ago";
27939
27940                        /// The number of characters to dedicate to gaps and margins.
27941                        const SPACING_WIDTH: usize = 4;
27942
27943                        let max_char_count = max_author_length.min(renderer.max_author_length())
27944                            + ::git::SHORT_SHA_LENGTH
27945                            + MAX_RELATIVE_TIMESTAMP.len()
27946                            + SPACING_WIDTH;
27947
27948                        ch_advance * max_char_count
27949                    });
27950
27951            let is_singleton = self.buffer_snapshot().is_singleton();
27952
27953            let mut left_padding = git_blame_entries_width.unwrap_or(Pixels::ZERO);
27954            left_padding += if !is_singleton {
27955                ch_width * 4.0
27956            } else if show_runnables || show_breakpoints {
27957                ch_width * 3.0
27958            } else if show_git_gutter && show_line_numbers {
27959                ch_width * 2.0
27960            } else if show_git_gutter || show_line_numbers {
27961                ch_width
27962            } else {
27963                px(0.)
27964            };
27965
27966            let shows_folds = is_singleton && gutter_settings.folds;
27967
27968            let right_padding = if shows_folds && show_line_numbers {
27969                ch_width * 4.0
27970            } else if shows_folds || (!is_singleton && show_line_numbers) {
27971                ch_width * 3.0
27972            } else if show_line_numbers {
27973                ch_width
27974            } else {
27975                px(0.)
27976            };
27977
27978            GutterDimensions {
27979                left_padding,
27980                right_padding,
27981                width: line_gutter_width + left_padding + right_padding,
27982                margin: GutterDimensions::default_gutter_margin(font_id, font_size, cx),
27983                git_blame_entries_width,
27984            }
27985        } else if self.offset_content {
27986            GutterDimensions::default_with_margin(font_id, font_size, cx)
27987        } else {
27988            GutterDimensions::default()
27989        }
27990    }
27991
27992    pub fn render_crease_toggle(
27993        &self,
27994        buffer_row: MultiBufferRow,
27995        row_contains_cursor: bool,
27996        editor: Entity<Editor>,
27997        window: &mut Window,
27998        cx: &mut App,
27999    ) -> Option<AnyElement> {
28000        let folded = self.is_line_folded(buffer_row);
28001        let mut is_foldable = false;
28002
28003        if let Some(crease) = self
28004            .crease_snapshot
28005            .query_row(buffer_row, self.buffer_snapshot())
28006        {
28007            is_foldable = true;
28008            match crease {
28009                Crease::Inline { render_toggle, .. } | Crease::Block { render_toggle, .. } => {
28010                    if let Some(render_toggle) = render_toggle {
28011                        let toggle_callback =
28012                            Arc::new(move |folded, window: &mut Window, cx: &mut App| {
28013                                if folded {
28014                                    editor.update(cx, |editor, cx| {
28015                                        editor.fold_at(buffer_row, window, cx)
28016                                    });
28017                                } else {
28018                                    editor.update(cx, |editor, cx| {
28019                                        editor.unfold_at(buffer_row, window, cx)
28020                                    });
28021                                }
28022                            });
28023                        return Some((render_toggle)(
28024                            buffer_row,
28025                            folded,
28026                            toggle_callback,
28027                            window,
28028                            cx,
28029                        ));
28030                    }
28031                }
28032            }
28033        }
28034
28035        is_foldable |= !self.use_lsp_folding_ranges && self.starts_indent(buffer_row);
28036
28037        if folded || (is_foldable && (row_contains_cursor || self.gutter_hovered)) {
28038            Some(
28039                Disclosure::new(("gutter_crease", buffer_row.0), !folded)
28040                    .toggle_state(folded)
28041                    .on_click(window.listener_for(&editor, move |this, _e, window, cx| {
28042                        if folded {
28043                            this.unfold_at(buffer_row, window, cx);
28044                        } else {
28045                            this.fold_at(buffer_row, window, cx);
28046                        }
28047                    }))
28048                    .into_any_element(),
28049            )
28050        } else {
28051            None
28052        }
28053    }
28054
28055    pub fn render_crease_trailer(
28056        &self,
28057        buffer_row: MultiBufferRow,
28058        window: &mut Window,
28059        cx: &mut App,
28060    ) -> Option<AnyElement> {
28061        let folded = self.is_line_folded(buffer_row);
28062        if let Crease::Inline { render_trailer, .. } = self
28063            .crease_snapshot
28064            .query_row(buffer_row, self.buffer_snapshot())?
28065        {
28066            let render_trailer = render_trailer.as_ref()?;
28067            Some(render_trailer(buffer_row, folded, window, cx))
28068        } else {
28069            None
28070        }
28071    }
28072
28073    pub fn max_line_number_width(&self, style: &EditorStyle, window: &mut Window) -> Pixels {
28074        let digit_count = self.widest_line_number().ilog10() + 1;
28075        column_pixels(style, digit_count as usize, window)
28076    }
28077
28078    /// Returns the line delta from `base` to `line` in the multibuffer, ignoring wrapped lines.
28079    ///
28080    /// This is positive if `base` is before `line`.
28081    fn relative_line_delta(
28082        &self,
28083        current_selection_head: DisplayRow,
28084        first_visible_row: DisplayRow,
28085        consider_wrapped_lines: bool,
28086    ) -> i64 {
28087        let current_selection_head = current_selection_head.as_display_point().to_point(self);
28088        let first_visible_row = first_visible_row.as_display_point().to_point(self);
28089
28090        if consider_wrapped_lines {
28091            let wrap_snapshot = self.wrap_snapshot();
28092            let base_wrap_row = wrap_snapshot
28093                .make_wrap_point(current_selection_head, Bias::Left)
28094                .row();
28095            let wrap_row = wrap_snapshot
28096                .make_wrap_point(first_visible_row, Bias::Left)
28097                .row();
28098
28099            wrap_row.0 as i64 - base_wrap_row.0 as i64
28100        } else {
28101            let fold_snapshot = self.fold_snapshot();
28102            let base_fold_row = fold_snapshot
28103                .to_fold_point(self.to_inlay_point(current_selection_head), Bias::Left)
28104                .row();
28105            let fold_row = fold_snapshot
28106                .to_fold_point(self.to_inlay_point(first_visible_row), Bias::Left)
28107                .row();
28108
28109            fold_row as i64 - base_fold_row as i64
28110        }
28111    }
28112
28113    /// Returns the unsigned relative line number to display for each row in `rows`.
28114    ///
28115    /// Wrapped rows are excluded from the hashmap if `count_relative_lines` is `false`.
28116    pub fn calculate_relative_line_numbers(
28117        &self,
28118        rows: &Range<DisplayRow>,
28119        current_selection_head: DisplayRow,
28120        count_wrapped_lines: bool,
28121    ) -> HashMap<DisplayRow, u32> {
28122        let initial_offset =
28123            self.relative_line_delta(current_selection_head, rows.start, count_wrapped_lines);
28124
28125        self.row_infos(rows.start)
28126            .take(rows.len())
28127            .enumerate()
28128            .map(|(i, row_info)| (DisplayRow(rows.start.0 + i as u32), row_info))
28129            .filter(|(_row, row_info)| {
28130                row_info.buffer_row.is_some()
28131                    || (count_wrapped_lines && row_info.wrapped_buffer_row.is_some())
28132            })
28133            .enumerate()
28134            .filter_map(|(i, (row, row_info))| {
28135                // We want to ensure here that the current line has absolute
28136                // numbering, even if we are in a soft-wrapped line. With the
28137                // exception that if we are in a deleted line, we should number this
28138                // relative with 0, as otherwise it would have no line number at all
28139                let relative_line_number = (initial_offset + i as i64).unsigned_abs() as u32;
28140
28141                (relative_line_number != 0
28142                    || row_info
28143                        .diff_status
28144                        .is_some_and(|status| status.is_deleted()))
28145                .then_some((row, relative_line_number))
28146            })
28147            .collect()
28148    }
28149}
28150
28151pub fn column_pixels(style: &EditorStyle, column: usize, window: &Window) -> Pixels {
28152    let font_size = style.text.font_size.to_pixels(window.rem_size());
28153    let layout = window.text_system().shape_line(
28154        SharedString::from(" ".repeat(column)),
28155        font_size,
28156        &[TextRun {
28157            len: column,
28158            font: style.text.font(),
28159            color: Hsla::default(),
28160            ..Default::default()
28161        }],
28162        None,
28163    );
28164
28165    layout.width
28166}
28167
28168impl Deref for EditorSnapshot {
28169    type Target = DisplaySnapshot;
28170
28171    fn deref(&self) -> &Self::Target {
28172        &self.display_snapshot
28173    }
28174}
28175
28176#[derive(Clone, Debug, PartialEq, Eq)]
28177pub enum EditorEvent {
28178    /// Emitted when the stored review comments change (added, removed, or updated).
28179    ReviewCommentsChanged {
28180        /// The new total count of review comments.
28181        total_count: usize,
28182    },
28183    InputIgnored {
28184        text: Arc<str>,
28185    },
28186    InputHandled {
28187        utf16_range_to_replace: Option<Range<isize>>,
28188        text: Arc<str>,
28189    },
28190    ExcerptsAdded {
28191        buffer: Entity<Buffer>,
28192        predecessor: ExcerptId,
28193        excerpts: Vec<(ExcerptId, ExcerptRange<language::Anchor>)>,
28194    },
28195    ExcerptsRemoved {
28196        ids: Vec<ExcerptId>,
28197        removed_buffer_ids: Vec<BufferId>,
28198    },
28199    BufferFoldToggled {
28200        ids: Vec<ExcerptId>,
28201        folded: bool,
28202    },
28203    ExcerptsEdited {
28204        ids: Vec<ExcerptId>,
28205    },
28206    ExcerptsExpanded {
28207        ids: Vec<ExcerptId>,
28208    },
28209    ExpandExcerptsRequested {
28210        excerpt_ids: Vec<ExcerptId>,
28211        lines: u32,
28212        direction: ExpandExcerptDirection,
28213    },
28214    StageOrUnstageRequested {
28215        stage: bool,
28216        hunks: Vec<MultiBufferDiffHunk>,
28217    },
28218    OpenExcerptsRequested {
28219        selections_by_buffer: HashMap<BufferId, (Vec<Range<BufferOffset>>, Option<u32>)>,
28220        split: bool,
28221    },
28222    RestoreRequested {
28223        hunks: Vec<MultiBufferDiffHunk>,
28224    },
28225    BufferEdited,
28226    Edited {
28227        transaction_id: clock::Lamport,
28228    },
28229    Reparsed(BufferId),
28230    Focused,
28231    FocusedIn,
28232    Blurred,
28233    DirtyChanged,
28234    Saved,
28235    TitleChanged,
28236    SelectionsChanged {
28237        local: bool,
28238    },
28239    ScrollPositionChanged {
28240        local: bool,
28241        autoscroll: bool,
28242    },
28243    TransactionUndone {
28244        transaction_id: clock::Lamport,
28245    },
28246    TransactionBegun {
28247        transaction_id: clock::Lamport,
28248    },
28249    CursorShapeChanged,
28250    BreadcrumbsChanged,
28251    OutlineSymbolsChanged,
28252    PushedToNavHistory {
28253        anchor: Anchor,
28254        is_deactivate: bool,
28255    },
28256}
28257
28258impl EventEmitter<EditorEvent> for Editor {}
28259
28260impl Focusable for Editor {
28261    fn focus_handle(&self, _cx: &App) -> FocusHandle {
28262        self.focus_handle.clone()
28263    }
28264}
28265
28266impl Render for Editor {
28267    fn render(&mut self, _: &mut Window, cx: &mut Context<Self>) -> impl IntoElement {
28268        EditorElement::new(&cx.entity(), self.create_style(cx))
28269    }
28270}
28271
28272impl EntityInputHandler for Editor {
28273    fn text_for_range(
28274        &mut self,
28275        range_utf16: Range<usize>,
28276        adjusted_range: &mut Option<Range<usize>>,
28277        _: &mut Window,
28278        cx: &mut Context<Self>,
28279    ) -> Option<String> {
28280        let snapshot = self.buffer.read(cx).read(cx);
28281        let start = snapshot.clip_offset_utf16(
28282            MultiBufferOffsetUtf16(OffsetUtf16(range_utf16.start)),
28283            Bias::Left,
28284        );
28285        let end = snapshot.clip_offset_utf16(
28286            MultiBufferOffsetUtf16(OffsetUtf16(range_utf16.end)),
28287            Bias::Right,
28288        );
28289        if (start.0.0..end.0.0) != range_utf16 {
28290            adjusted_range.replace(start.0.0..end.0.0);
28291        }
28292        Some(snapshot.text_for_range(start..end).collect())
28293    }
28294
28295    fn selected_text_range(
28296        &mut self,
28297        ignore_disabled_input: bool,
28298        _: &mut Window,
28299        cx: &mut Context<Self>,
28300    ) -> Option<UTF16Selection> {
28301        // Prevent the IME menu from appearing when holding down an alphabetic key
28302        // while input is disabled.
28303        if !ignore_disabled_input && !self.input_enabled {
28304            return None;
28305        }
28306
28307        let selection = self
28308            .selections
28309            .newest::<MultiBufferOffsetUtf16>(&self.display_snapshot(cx));
28310        let range = selection.range();
28311
28312        Some(UTF16Selection {
28313            range: range.start.0.0..range.end.0.0,
28314            reversed: selection.reversed,
28315        })
28316    }
28317
28318    fn marked_text_range(&self, _: &mut Window, cx: &mut Context<Self>) -> Option<Range<usize>> {
28319        let snapshot = self.buffer.read(cx).read(cx);
28320        let range = self
28321            .text_highlights(HighlightKey::InputComposition, cx)?
28322            .1
28323            .first()?;
28324        Some(range.start.to_offset_utf16(&snapshot).0.0..range.end.to_offset_utf16(&snapshot).0.0)
28325    }
28326
28327    fn unmark_text(&mut self, _: &mut Window, cx: &mut Context<Self>) {
28328        self.clear_highlights(HighlightKey::InputComposition, cx);
28329        self.ime_transaction.take();
28330    }
28331
28332    fn replace_text_in_range(
28333        &mut self,
28334        range_utf16: Option<Range<usize>>,
28335        text: &str,
28336        window: &mut Window,
28337        cx: &mut Context<Self>,
28338    ) {
28339        if !self.input_enabled {
28340            cx.emit(EditorEvent::InputIgnored { text: text.into() });
28341            return;
28342        }
28343
28344        self.transact(window, cx, |this, window, cx| {
28345            let new_selected_ranges = if let Some(range_utf16) = range_utf16 {
28346                if let Some(marked_ranges) = this.marked_text_ranges(cx) {
28347                    // During IME composition, macOS reports the replacement range
28348                    // relative to the first marked region (the only one visible via
28349                    // marked_text_range). The correct targets for replacement are the
28350                    // marked ranges themselves — one per cursor — so use them directly.
28351                    Some(marked_ranges)
28352                } else if range_utf16.start == range_utf16.end {
28353                    // An empty replacement range means "insert at cursor" with no text
28354                    // to replace. macOS reports the cursor position from its own
28355                    // (single-cursor) view of the buffer, which diverges from our actual
28356                    // cursor positions after multi-cursor edits have shifted offsets.
28357                    // Treating this as range_utf16=None lets each cursor insert in place.
28358                    None
28359                } else {
28360                    // Outside of IME composition (e.g. Accessibility Keyboard word
28361                    // completion), the range is an absolute document offset for the
28362                    // newest cursor. Fan it out to all cursors via
28363                    // selection_replacement_ranges, which applies the delta relative
28364                    // to the newest selection to every cursor.
28365                    let range_utf16 = MultiBufferOffsetUtf16(OffsetUtf16(range_utf16.start))
28366                        ..MultiBufferOffsetUtf16(OffsetUtf16(range_utf16.end));
28367                    Some(this.selection_replacement_ranges(range_utf16, cx))
28368                }
28369            } else {
28370                this.marked_text_ranges(cx)
28371            };
28372
28373            let range_to_replace = new_selected_ranges.as_ref().and_then(|ranges_to_replace| {
28374                let newest_selection_id = this.selections.newest_anchor().id;
28375                this.selections
28376                    .all::<MultiBufferOffsetUtf16>(&this.display_snapshot(cx))
28377                    .iter()
28378                    .zip(ranges_to_replace.iter())
28379                    .find_map(|(selection, range)| {
28380                        if selection.id == newest_selection_id {
28381                            Some(
28382                                (range.start.0.0 as isize - selection.head().0.0 as isize)
28383                                    ..(range.end.0.0 as isize - selection.head().0.0 as isize),
28384                            )
28385                        } else {
28386                            None
28387                        }
28388                    })
28389            });
28390
28391            cx.emit(EditorEvent::InputHandled {
28392                utf16_range_to_replace: range_to_replace,
28393                text: text.into(),
28394            });
28395
28396            if let Some(new_selected_ranges) = new_selected_ranges {
28397                // Only backspace if at least one range covers actual text. When all
28398                // ranges are empty (e.g. a trailing-space insertion from Accessibility
28399                // Keyboard sends replacementRange=cursor..cursor), backspace would
28400                // incorrectly delete the character just before the cursor.
28401                let should_backspace = new_selected_ranges.iter().any(|r| r.start != r.end);
28402                this.change_selections(SelectionEffects::no_scroll(), window, cx, |selections| {
28403                    selections.select_ranges(new_selected_ranges)
28404                });
28405                if should_backspace {
28406                    this.backspace(&Default::default(), window, cx);
28407                }
28408            }
28409
28410            this.handle_input(text, window, cx);
28411        });
28412
28413        if let Some(transaction) = self.ime_transaction {
28414            self.buffer.update(cx, |buffer, cx| {
28415                buffer.group_until_transaction(transaction, cx);
28416            });
28417        }
28418
28419        self.unmark_text(window, cx);
28420    }
28421
28422    fn replace_and_mark_text_in_range(
28423        &mut self,
28424        range_utf16: Option<Range<usize>>,
28425        text: &str,
28426        new_selected_range_utf16: Option<Range<usize>>,
28427        window: &mut Window,
28428        cx: &mut Context<Self>,
28429    ) {
28430        if !self.input_enabled {
28431            return;
28432        }
28433
28434        let transaction = self.transact(window, cx, |this, window, cx| {
28435            let ranges_to_replace = if let Some(mut marked_ranges) = this.marked_text_ranges(cx) {
28436                let snapshot = this.buffer.read(cx).read(cx);
28437                if let Some(relative_range_utf16) = range_utf16.as_ref() {
28438                    for marked_range in &mut marked_ranges {
28439                        marked_range.end = marked_range.start + relative_range_utf16.end;
28440                        marked_range.start += relative_range_utf16.start;
28441                        marked_range.start =
28442                            snapshot.clip_offset_utf16(marked_range.start, Bias::Left);
28443                        marked_range.end =
28444                            snapshot.clip_offset_utf16(marked_range.end, Bias::Right);
28445                    }
28446                }
28447                Some(marked_ranges)
28448            } else if let Some(range_utf16) = range_utf16 {
28449                let range_utf16 = MultiBufferOffsetUtf16(OffsetUtf16(range_utf16.start))
28450                    ..MultiBufferOffsetUtf16(OffsetUtf16(range_utf16.end));
28451                Some(this.selection_replacement_ranges(range_utf16, cx))
28452            } else {
28453                None
28454            };
28455
28456            let range_to_replace = ranges_to_replace.as_ref().and_then(|ranges_to_replace| {
28457                let newest_selection_id = this.selections.newest_anchor().id;
28458                this.selections
28459                    .all::<MultiBufferOffsetUtf16>(&this.display_snapshot(cx))
28460                    .iter()
28461                    .zip(ranges_to_replace.iter())
28462                    .find_map(|(selection, range)| {
28463                        if selection.id == newest_selection_id {
28464                            Some(
28465                                (range.start.0.0 as isize - selection.head().0.0 as isize)
28466                                    ..(range.end.0.0 as isize - selection.head().0.0 as isize),
28467                            )
28468                        } else {
28469                            None
28470                        }
28471                    })
28472            });
28473
28474            cx.emit(EditorEvent::InputHandled {
28475                utf16_range_to_replace: range_to_replace,
28476                text: text.into(),
28477            });
28478
28479            if let Some(ranges) = ranges_to_replace {
28480                this.change_selections(SelectionEffects::no_scroll(), window, cx, |s| {
28481                    s.select_ranges(ranges)
28482                });
28483            }
28484
28485            let marked_ranges = {
28486                let snapshot = this.buffer.read(cx).read(cx);
28487                this.selections
28488                    .disjoint_anchors_arc()
28489                    .iter()
28490                    .map(|selection| {
28491                        selection.start.bias_left(&snapshot)..selection.end.bias_right(&snapshot)
28492                    })
28493                    .collect::<Vec<_>>()
28494            };
28495
28496            if text.is_empty() {
28497                this.unmark_text(window, cx);
28498            } else {
28499                this.highlight_text(
28500                    HighlightKey::InputComposition,
28501                    marked_ranges.clone(),
28502                    HighlightStyle {
28503                        underline: Some(UnderlineStyle {
28504                            thickness: px(1.),
28505                            color: None,
28506                            wavy: false,
28507                        }),
28508                        ..Default::default()
28509                    },
28510                    cx,
28511                );
28512            }
28513
28514            // Disable auto-closing when composing text (i.e. typing a `"` on a Brazilian keyboard)
28515            let use_autoclose = this.use_autoclose;
28516            let use_auto_surround = this.use_auto_surround;
28517            this.set_use_autoclose(false);
28518            this.set_use_auto_surround(false);
28519            this.handle_input(text, window, cx);
28520            this.set_use_autoclose(use_autoclose);
28521            this.set_use_auto_surround(use_auto_surround);
28522
28523            if let Some(new_selected_range) = new_selected_range_utf16 {
28524                let snapshot = this.buffer.read(cx).read(cx);
28525                let new_selected_ranges = marked_ranges
28526                    .into_iter()
28527                    .map(|marked_range| {
28528                        let insertion_start = marked_range.start.to_offset_utf16(&snapshot).0;
28529                        let new_start = MultiBufferOffsetUtf16(OffsetUtf16(
28530                            insertion_start.0 + new_selected_range.start,
28531                        ));
28532                        let new_end = MultiBufferOffsetUtf16(OffsetUtf16(
28533                            insertion_start.0 + new_selected_range.end,
28534                        ));
28535                        snapshot.clip_offset_utf16(new_start, Bias::Left)
28536                            ..snapshot.clip_offset_utf16(new_end, Bias::Right)
28537                    })
28538                    .collect::<Vec<_>>();
28539
28540                drop(snapshot);
28541                this.change_selections(SelectionEffects::no_scroll(), window, cx, |selections| {
28542                    selections.select_ranges(new_selected_ranges)
28543                });
28544            }
28545        });
28546
28547        self.ime_transaction = self.ime_transaction.or(transaction);
28548        if let Some(transaction) = self.ime_transaction {
28549            self.buffer.update(cx, |buffer, cx| {
28550                buffer.group_until_transaction(transaction, cx);
28551            });
28552        }
28553
28554        if self
28555            .text_highlights(HighlightKey::InputComposition, cx)
28556            .is_none()
28557        {
28558            self.ime_transaction.take();
28559        }
28560    }
28561
28562    fn bounds_for_range(
28563        &mut self,
28564        range_utf16: Range<usize>,
28565        element_bounds: gpui::Bounds<Pixels>,
28566        window: &mut Window,
28567        cx: &mut Context<Self>,
28568    ) -> Option<gpui::Bounds<Pixels>> {
28569        let text_layout_details = self.text_layout_details(window, cx);
28570        let CharacterDimensions {
28571            em_width,
28572            em_advance,
28573            line_height,
28574        } = self.character_dimensions(window, cx);
28575
28576        let snapshot = self.snapshot(window, cx);
28577        let scroll_position = snapshot.scroll_position();
28578        let scroll_left = scroll_position.x * ScrollOffset::from(em_advance);
28579
28580        let start =
28581            MultiBufferOffsetUtf16(OffsetUtf16(range_utf16.start)).to_display_point(&snapshot);
28582        let x = Pixels::from(
28583            ScrollOffset::from(
28584                snapshot.x_for_display_point(start, &text_layout_details)
28585                    + self.gutter_dimensions.full_width(),
28586            ) - scroll_left,
28587        );
28588        let y = line_height * (start.row().as_f64() - scroll_position.y) as f32;
28589
28590        Some(Bounds {
28591            origin: element_bounds.origin + point(x, y),
28592            size: size(em_width, line_height),
28593        })
28594    }
28595
28596    fn character_index_for_point(
28597        &mut self,
28598        point: gpui::Point<Pixels>,
28599        _window: &mut Window,
28600        _cx: &mut Context<Self>,
28601    ) -> Option<usize> {
28602        let position_map = self.last_position_map.as_ref()?;
28603        if !position_map.text_hitbox.contains(&point) {
28604            return None;
28605        }
28606        let display_point = position_map.point_for_position(point).previous_valid;
28607        let anchor = position_map
28608            .snapshot
28609            .display_point_to_anchor(display_point, Bias::Left);
28610        let utf16_offset = anchor.to_offset_utf16(&position_map.snapshot.buffer_snapshot());
28611        Some(utf16_offset.0.0)
28612    }
28613
28614    fn accepts_text_input(&self, _window: &mut Window, _cx: &mut Context<Self>) -> bool {
28615        self.expects_character_input
28616    }
28617}
28618
28619trait SelectionExt {
28620    fn display_range(&self, map: &DisplaySnapshot) -> Range<DisplayPoint>;
28621    fn spanned_rows(
28622        &self,
28623        include_end_if_at_line_start: bool,
28624        map: &DisplaySnapshot,
28625    ) -> Range<MultiBufferRow>;
28626}
28627
28628impl<T: ToPoint + ToOffset> SelectionExt for Selection<T> {
28629    fn display_range(&self, map: &DisplaySnapshot) -> Range<DisplayPoint> {
28630        let start = self
28631            .start
28632            .to_point(map.buffer_snapshot())
28633            .to_display_point(map);
28634        let end = self
28635            .end
28636            .to_point(map.buffer_snapshot())
28637            .to_display_point(map);
28638        if self.reversed {
28639            end..start
28640        } else {
28641            start..end
28642        }
28643    }
28644
28645    fn spanned_rows(
28646        &self,
28647        include_end_if_at_line_start: bool,
28648        map: &DisplaySnapshot,
28649    ) -> Range<MultiBufferRow> {
28650        let start = self.start.to_point(map.buffer_snapshot());
28651        let mut end = self.end.to_point(map.buffer_snapshot());
28652        if !include_end_if_at_line_start && start.row != end.row && end.column == 0 {
28653            end.row -= 1;
28654        }
28655
28656        let buffer_start = map.prev_line_boundary(start).0;
28657        let buffer_end = map.next_line_boundary(end).0;
28658        MultiBufferRow(buffer_start.row)..MultiBufferRow(buffer_end.row + 1)
28659    }
28660}
28661
28662impl<T: InvalidationRegion> InvalidationStack<T> {
28663    fn invalidate<S>(&mut self, selections: &[Selection<S>], buffer: &MultiBufferSnapshot)
28664    where
28665        S: Clone + ToOffset,
28666    {
28667        while let Some(region) = self.last() {
28668            let all_selections_inside_invalidation_ranges =
28669                if selections.len() == region.ranges().len() {
28670                    selections
28671                        .iter()
28672                        .zip(region.ranges().iter().map(|r| r.to_offset(buffer)))
28673                        .all(|(selection, invalidation_range)| {
28674                            let head = selection.head().to_offset(buffer);
28675                            invalidation_range.start <= head && invalidation_range.end >= head
28676                        })
28677                } else {
28678                    false
28679                };
28680
28681            if all_selections_inside_invalidation_ranges {
28682                break;
28683            } else {
28684                self.pop();
28685            }
28686        }
28687    }
28688}
28689
28690#[derive(Clone)]
28691struct ErasedEditorImpl(Entity<Editor>);
28692
28693impl ui_input::ErasedEditor for ErasedEditorImpl {
28694    fn text(&self, cx: &App) -> String {
28695        self.0.read(cx).text(cx)
28696    }
28697
28698    fn set_text(&self, text: &str, window: &mut Window, cx: &mut App) {
28699        self.0.update(cx, |this, cx| {
28700            this.set_text(text, window, cx);
28701        })
28702    }
28703
28704    fn clear(&self, window: &mut Window, cx: &mut App) {
28705        self.0.update(cx, |this, cx| this.clear(window, cx));
28706    }
28707
28708    fn set_placeholder_text(&self, text: &str, window: &mut Window, cx: &mut App) {
28709        self.0.update(cx, |this, cx| {
28710            this.set_placeholder_text(text, window, cx);
28711        });
28712    }
28713
28714    fn focus_handle(&self, cx: &App) -> FocusHandle {
28715        self.0.read(cx).focus_handle(cx)
28716    }
28717
28718    fn render(&self, _: &mut Window, cx: &App) -> AnyElement {
28719        let settings = ThemeSettings::get_global(cx);
28720        let theme_color = cx.theme().colors();
28721
28722        let text_style = TextStyle {
28723            font_family: settings.ui_font.family.clone(),
28724            font_features: settings.ui_font.features.clone(),
28725            font_size: rems(0.875).into(),
28726            font_weight: settings.ui_font.weight,
28727            font_style: FontStyle::Normal,
28728            line_height: relative(1.2),
28729            color: theme_color.text,
28730            ..Default::default()
28731        };
28732        let editor_style = EditorStyle {
28733            background: theme_color.ghost_element_background,
28734            local_player: cx.theme().players().local(),
28735            syntax: cx.theme().syntax().clone(),
28736            text: text_style,
28737            ..Default::default()
28738        };
28739        EditorElement::new(&self.0, editor_style).into_any()
28740    }
28741
28742    fn as_any(&self) -> &dyn Any {
28743        &self.0
28744    }
28745
28746    fn move_selection_to_end(&self, window: &mut Window, cx: &mut App) {
28747        self.0.update(cx, |editor, cx| {
28748            let editor_offset = editor.buffer().read(cx).len(cx);
28749            editor.change_selections(
28750                SelectionEffects::scroll(Autoscroll::Next),
28751                window,
28752                cx,
28753                |s| s.select_ranges(Some(editor_offset..editor_offset)),
28754            );
28755        });
28756    }
28757
28758    fn subscribe(
28759        &self,
28760        mut callback: Box<dyn FnMut(ui_input::ErasedEditorEvent, &mut Window, &mut App) + 'static>,
28761        window: &mut Window,
28762        cx: &mut App,
28763    ) -> Subscription {
28764        window.subscribe(&self.0, cx, move |_, event: &EditorEvent, window, cx| {
28765            let event = match event {
28766                EditorEvent::BufferEdited => ui_input::ErasedEditorEvent::BufferEdited,
28767                EditorEvent::Blurred => ui_input::ErasedEditorEvent::Blurred,
28768                _ => return,
28769            };
28770            (callback)(event, window, cx);
28771        })
28772    }
28773
28774    fn set_masked(&self, masked: bool, _window: &mut Window, cx: &mut App) {
28775        self.0.update(cx, |editor, cx| {
28776            editor.set_masked(masked, cx);
28777        });
28778    }
28779}
28780impl<T> Default for InvalidationStack<T> {
28781    fn default() -> Self {
28782        Self(Default::default())
28783    }
28784}
28785
28786impl<T> Deref for InvalidationStack<T> {
28787    type Target = Vec<T>;
28788
28789    fn deref(&self) -> &Self::Target {
28790        &self.0
28791    }
28792}
28793
28794impl<T> DerefMut for InvalidationStack<T> {
28795    fn deref_mut(&mut self) -> &mut Self::Target {
28796        &mut self.0
28797    }
28798}
28799
28800impl InvalidationRegion for SnippetState {
28801    fn ranges(&self) -> &[Range<Anchor>] {
28802        &self.ranges[self.active_index]
28803    }
28804}
28805
28806fn edit_prediction_edit_text(
28807    current_snapshot: &BufferSnapshot,
28808    edits: &[(Range<Anchor>, impl AsRef<str>)],
28809    edit_preview: &EditPreview,
28810    include_deletions: bool,
28811    cx: &App,
28812) -> HighlightedText {
28813    let edits = edits
28814        .iter()
28815        .map(|(anchor, text)| (anchor.start.text_anchor..anchor.end.text_anchor, text))
28816        .collect::<Vec<_>>();
28817
28818    edit_preview.highlight_edits(current_snapshot, &edits, include_deletions, cx)
28819}
28820
28821fn edit_prediction_fallback_text(edits: &[(Range<Anchor>, Arc<str>)], cx: &App) -> HighlightedText {
28822    // Fallback for providers that don't provide edit_preview (like Copilot)
28823    // Just show the raw edit text with basic styling
28824    let mut text = String::new();
28825    let mut highlights = Vec::new();
28826
28827    let insertion_highlight_style = HighlightStyle {
28828        color: Some(cx.theme().colors().text),
28829        ..Default::default()
28830    };
28831
28832    for (_, edit_text) in edits {
28833        let start_offset = text.len();
28834        text.push_str(edit_text);
28835        let end_offset = text.len();
28836
28837        if start_offset < end_offset {
28838            highlights.push((start_offset..end_offset, insertion_highlight_style));
28839        }
28840    }
28841
28842    HighlightedText {
28843        text: text.into(),
28844        highlights,
28845    }
28846}
28847
28848pub fn diagnostic_style(severity: lsp::DiagnosticSeverity, colors: &StatusColors) -> Hsla {
28849    match severity {
28850        lsp::DiagnosticSeverity::ERROR => colors.error,
28851        lsp::DiagnosticSeverity::WARNING => colors.warning,
28852        lsp::DiagnosticSeverity::INFORMATION => colors.info,
28853        lsp::DiagnosticSeverity::HINT => colors.info,
28854        _ => colors.ignored,
28855    }
28856}
28857
28858pub fn styled_runs_for_code_label<'a>(
28859    label: &'a CodeLabel,
28860    syntax_theme: &'a theme::SyntaxTheme,
28861    local_player: &'a theme::PlayerColor,
28862) -> impl 'a + Iterator<Item = (Range<usize>, HighlightStyle)> {
28863    let fade_out = HighlightStyle {
28864        fade_out: Some(0.35),
28865        ..Default::default()
28866    };
28867
28868    if label.runs.is_empty() {
28869        let desc_start = label.filter_range.end;
28870        let fade_run =
28871            (desc_start < label.text.len()).then(|| (desc_start..label.text.len(), fade_out));
28872        return Either::Left(fade_run.into_iter());
28873    }
28874
28875    let mut prev_end = label.filter_range.end;
28876    Either::Right(
28877        label
28878            .runs
28879            .iter()
28880            .enumerate()
28881            .flat_map(move |(ix, (range, highlight_id))| {
28882                let style = if *highlight_id == language::HighlightId::TABSTOP_INSERT_ID {
28883                    HighlightStyle {
28884                        color: Some(local_player.cursor),
28885                        ..Default::default()
28886                    }
28887                } else if *highlight_id == language::HighlightId::TABSTOP_REPLACE_ID {
28888                    HighlightStyle {
28889                        background_color: Some(local_player.selection),
28890                        ..Default::default()
28891                    }
28892                } else if let Some(style) = syntax_theme.get(*highlight_id).cloned() {
28893                    style
28894                } else {
28895                    return Default::default();
28896                };
28897
28898                let mut runs = SmallVec::<[(Range<usize>, HighlightStyle); 3]>::new();
28899                let muted_style = style.highlight(fade_out);
28900                if range.start >= label.filter_range.end {
28901                    if range.start > prev_end {
28902                        runs.push((prev_end..range.start, fade_out));
28903                    }
28904                    runs.push((range.clone(), muted_style));
28905                } else if range.end <= label.filter_range.end {
28906                    runs.push((range.clone(), style));
28907                } else {
28908                    runs.push((range.start..label.filter_range.end, style));
28909                    runs.push((label.filter_range.end..range.end, muted_style));
28910                }
28911                prev_end = cmp::max(prev_end, range.end);
28912
28913                if ix + 1 == label.runs.len() && label.text.len() > prev_end {
28914                    runs.push((prev_end..label.text.len(), fade_out));
28915                }
28916
28917                runs
28918            }),
28919    )
28920}
28921
28922pub(crate) fn split_words(text: &str) -> impl std::iter::Iterator<Item = &str> + '_ {
28923    let mut prev_index = 0;
28924    let mut prev_codepoint: Option<char> = None;
28925    text.char_indices()
28926        .chain([(text.len(), '\0')])
28927        .filter_map(move |(index, codepoint)| {
28928            let prev_codepoint = prev_codepoint.replace(codepoint)?;
28929            let is_boundary = index == text.len()
28930                || !prev_codepoint.is_uppercase() && codepoint.is_uppercase()
28931                || !prev_codepoint.is_alphanumeric() && codepoint.is_alphanumeric();
28932            if is_boundary {
28933                let chunk = &text[prev_index..index];
28934                prev_index = index;
28935                Some(chunk)
28936            } else {
28937                None
28938            }
28939        })
28940}
28941
28942/// Given a string of text immediately before the cursor, iterates over possible
28943/// strings a snippet could match to. More precisely: returns an iterator over
28944/// suffixes of `text` created by splitting at word boundaries (before & after
28945/// every non-word character).
28946///
28947/// Shorter suffixes are returned first.
28948pub(crate) fn snippet_candidate_suffixes<'a>(
28949    text: &'a str,
28950    is_word_char: &'a dyn Fn(char) -> bool,
28951) -> impl std::iter::Iterator<Item = &'a str> + 'a {
28952    let mut prev_index = text.len();
28953    let mut prev_codepoint = None;
28954    text.char_indices()
28955        .rev()
28956        .chain([(0, '\0')])
28957        .filter_map(move |(index, codepoint)| {
28958            let prev_index = std::mem::replace(&mut prev_index, index);
28959            let prev_codepoint = prev_codepoint.replace(codepoint)?;
28960            if is_word_char(prev_codepoint) && is_word_char(codepoint) {
28961                None
28962            } else {
28963                let chunk = &text[prev_index..]; // go to end of string
28964                Some(chunk)
28965            }
28966        })
28967}
28968
28969pub trait RangeToAnchorExt: Sized {
28970    fn to_anchors(self, snapshot: &MultiBufferSnapshot) -> Range<Anchor>;
28971
28972    fn to_display_points(self, snapshot: &EditorSnapshot) -> Range<DisplayPoint> {
28973        let anchor_range = self.to_anchors(&snapshot.buffer_snapshot());
28974        anchor_range.start.to_display_point(snapshot)..anchor_range.end.to_display_point(snapshot)
28975    }
28976}
28977
28978impl<T: ToOffset> RangeToAnchorExt for Range<T> {
28979    fn to_anchors(self, snapshot: &MultiBufferSnapshot) -> Range<Anchor> {
28980        let start_offset = self.start.to_offset(snapshot);
28981        let end_offset = self.end.to_offset(snapshot);
28982        if start_offset == end_offset {
28983            snapshot.anchor_before(start_offset)..snapshot.anchor_before(end_offset)
28984        } else {
28985            snapshot.anchor_after(self.start)..snapshot.anchor_before(self.end)
28986        }
28987    }
28988}
28989
28990pub trait RowExt {
28991    fn as_f64(&self) -> f64;
28992
28993    fn next_row(&self) -> Self;
28994
28995    fn previous_row(&self) -> Self;
28996
28997    fn minus(&self, other: Self) -> u32;
28998}
28999
29000impl RowExt for DisplayRow {
29001    fn as_f64(&self) -> f64 {
29002        self.0 as _
29003    }
29004
29005    fn next_row(&self) -> Self {
29006        Self(self.0 + 1)
29007    }
29008
29009    fn previous_row(&self) -> Self {
29010        Self(self.0.saturating_sub(1))
29011    }
29012
29013    fn minus(&self, other: Self) -> u32 {
29014        self.0 - other.0
29015    }
29016}
29017
29018impl RowExt for MultiBufferRow {
29019    fn as_f64(&self) -> f64 {
29020        self.0 as _
29021    }
29022
29023    fn next_row(&self) -> Self {
29024        Self(self.0 + 1)
29025    }
29026
29027    fn previous_row(&self) -> Self {
29028        Self(self.0.saturating_sub(1))
29029    }
29030
29031    fn minus(&self, other: Self) -> u32 {
29032        self.0 - other.0
29033    }
29034}
29035
29036trait RowRangeExt {
29037    type Row;
29038
29039    fn len(&self) -> usize;
29040
29041    fn iter_rows(&self) -> impl DoubleEndedIterator<Item = Self::Row>;
29042}
29043
29044impl RowRangeExt for Range<MultiBufferRow> {
29045    type Row = MultiBufferRow;
29046
29047    fn len(&self) -> usize {
29048        (self.end.0 - self.start.0) as usize
29049    }
29050
29051    fn iter_rows(&self) -> impl DoubleEndedIterator<Item = MultiBufferRow> {
29052        (self.start.0..self.end.0).map(MultiBufferRow)
29053    }
29054}
29055
29056impl RowRangeExt for Range<DisplayRow> {
29057    type Row = DisplayRow;
29058
29059    fn len(&self) -> usize {
29060        (self.end.0 - self.start.0) as usize
29061    }
29062
29063    fn iter_rows(&self) -> impl DoubleEndedIterator<Item = DisplayRow> {
29064        (self.start.0..self.end.0).map(DisplayRow)
29065    }
29066}
29067
29068/// If select range has more than one line, we
29069/// just point the cursor to range.start.
29070fn collapse_multiline_range(range: Range<Point>) -> Range<Point> {
29071    if range.start.row == range.end.row {
29072        range
29073    } else {
29074        range.start..range.start
29075    }
29076}
29077pub struct KillRing(ClipboardItem);
29078impl Global for KillRing {}
29079
29080const UPDATE_DEBOUNCE: Duration = Duration::from_millis(50);
29081
29082enum BreakpointPromptEditAction {
29083    Log,
29084    Condition,
29085    HitCondition,
29086}
29087
29088struct BreakpointPromptEditor {
29089    pub(crate) prompt: Entity<Editor>,
29090    editor: WeakEntity<Editor>,
29091    breakpoint_anchor: Anchor,
29092    breakpoint: Breakpoint,
29093    edit_action: BreakpointPromptEditAction,
29094    block_ids: HashSet<CustomBlockId>,
29095    editor_margins: Arc<Mutex<EditorMargins>>,
29096    _subscriptions: Vec<Subscription>,
29097}
29098
29099impl BreakpointPromptEditor {
29100    const MAX_LINES: u8 = 4;
29101
29102    fn new(
29103        editor: WeakEntity<Editor>,
29104        breakpoint_anchor: Anchor,
29105        breakpoint: Breakpoint,
29106        edit_action: BreakpointPromptEditAction,
29107        window: &mut Window,
29108        cx: &mut Context<Self>,
29109    ) -> Self {
29110        let base_text = match edit_action {
29111            BreakpointPromptEditAction::Log => breakpoint.message.as_ref(),
29112            BreakpointPromptEditAction::Condition => breakpoint.condition.as_ref(),
29113            BreakpointPromptEditAction::HitCondition => breakpoint.hit_condition.as_ref(),
29114        }
29115        .map(|msg| msg.to_string())
29116        .unwrap_or_default();
29117
29118        let buffer = cx.new(|cx| Buffer::local(base_text, cx));
29119        let buffer = cx.new(|cx| MultiBuffer::singleton(buffer, cx));
29120
29121        let prompt = cx.new(|cx| {
29122            let mut prompt = Editor::new(
29123                EditorMode::AutoHeight {
29124                    min_lines: 1,
29125                    max_lines: Some(Self::MAX_LINES as usize),
29126                },
29127                buffer,
29128                None,
29129                window,
29130                cx,
29131            );
29132            prompt.set_soft_wrap_mode(language::language_settings::SoftWrap::EditorWidth, cx);
29133            prompt.set_show_cursor_when_unfocused(false, cx);
29134            prompt.set_placeholder_text(
29135                match edit_action {
29136                    BreakpointPromptEditAction::Log => "Message to log when a breakpoint is hit. Expressions within {} are interpolated.",
29137                    BreakpointPromptEditAction::Condition => "Condition when a breakpoint is hit. Expressions within {} are interpolated.",
29138                    BreakpointPromptEditAction::HitCondition => "How many breakpoint hits to ignore",
29139                },
29140                window,
29141                cx,
29142            );
29143
29144            prompt
29145        });
29146
29147        Self {
29148            prompt,
29149            editor,
29150            breakpoint_anchor,
29151            breakpoint,
29152            edit_action,
29153            editor_margins: Arc::new(Mutex::new(EditorMargins::default())),
29154            block_ids: Default::default(),
29155            _subscriptions: vec![],
29156        }
29157    }
29158
29159    pub(crate) fn add_block_ids(&mut self, block_ids: Vec<CustomBlockId>) {
29160        self.block_ids.extend(block_ids)
29161    }
29162
29163    fn confirm(&mut self, _: &menu::Confirm, window: &mut Window, cx: &mut Context<Self>) {
29164        if let Some(editor) = self.editor.upgrade() {
29165            let message = self
29166                .prompt
29167                .read(cx)
29168                .buffer
29169                .read(cx)
29170                .as_singleton()
29171                .expect("A multi buffer in breakpoint prompt isn't possible")
29172                .read(cx)
29173                .as_rope()
29174                .to_string();
29175
29176            editor.update(cx, |editor, cx| {
29177                editor.edit_breakpoint_at_anchor(
29178                    self.breakpoint_anchor,
29179                    self.breakpoint.clone(),
29180                    match self.edit_action {
29181                        BreakpointPromptEditAction::Log => {
29182                            BreakpointEditAction::EditLogMessage(message.into())
29183                        }
29184                        BreakpointPromptEditAction::Condition => {
29185                            BreakpointEditAction::EditCondition(message.into())
29186                        }
29187                        BreakpointPromptEditAction::HitCondition => {
29188                            BreakpointEditAction::EditHitCondition(message.into())
29189                        }
29190                    },
29191                    cx,
29192                );
29193
29194                editor.remove_blocks(self.block_ids.clone(), None, cx);
29195                cx.focus_self(window);
29196            });
29197        }
29198    }
29199
29200    fn cancel(&mut self, _: &menu::Cancel, window: &mut Window, cx: &mut Context<Self>) {
29201        self.editor
29202            .update(cx, |editor, cx| {
29203                editor.remove_blocks(self.block_ids.clone(), None, cx);
29204                window.focus(&editor.focus_handle, cx);
29205            })
29206            .log_err();
29207    }
29208
29209    fn render_prompt_editor(&self, cx: &mut Context<Self>) -> impl IntoElement {
29210        let settings = ThemeSettings::get_global(cx);
29211        let text_style = TextStyle {
29212            color: if self.prompt.read(cx).read_only(cx) {
29213                cx.theme().colors().text_disabled
29214            } else {
29215                cx.theme().colors().text
29216            },
29217            font_family: settings.buffer_font.family.clone(),
29218            font_fallbacks: settings.buffer_font.fallbacks.clone(),
29219            font_size: settings.buffer_font_size(cx).into(),
29220            font_weight: settings.buffer_font.weight,
29221            line_height: relative(settings.buffer_line_height.value()),
29222            ..Default::default()
29223        };
29224        EditorElement::new(
29225            &self.prompt,
29226            EditorStyle {
29227                background: cx.theme().colors().editor_background,
29228                local_player: cx.theme().players().local(),
29229                text: text_style,
29230                ..Default::default()
29231            },
29232        )
29233    }
29234
29235    fn render_close_button(&self, cx: &mut Context<Self>) -> impl IntoElement {
29236        let focus_handle = self.prompt.focus_handle(cx);
29237        IconButton::new("cancel", IconName::Close)
29238            .icon_color(Color::Muted)
29239            .shape(IconButtonShape::Square)
29240            .tooltip(move |_window, cx| {
29241                Tooltip::for_action_in("Cancel", &menu::Cancel, &focus_handle, cx)
29242            })
29243            .on_click(cx.listener(|this, _, window, cx| {
29244                this.cancel(&menu::Cancel, window, cx);
29245            }))
29246    }
29247
29248    fn render_confirm_button(&self, cx: &mut Context<Self>) -> impl IntoElement {
29249        let focus_handle = self.prompt.focus_handle(cx);
29250        IconButton::new("confirm", IconName::Return)
29251            .icon_color(Color::Muted)
29252            .shape(IconButtonShape::Square)
29253            .tooltip(move |_window, cx| {
29254                Tooltip::for_action_in("Confirm", &menu::Confirm, &focus_handle, cx)
29255            })
29256            .on_click(cx.listener(|this, _, window, cx| {
29257                this.confirm(&menu::Confirm, window, cx);
29258            }))
29259    }
29260}
29261
29262impl Render for BreakpointPromptEditor {
29263    fn render(&mut self, window: &mut Window, cx: &mut Context<Self>) -> impl IntoElement {
29264        let ui_font_size = ThemeSettings::get_global(cx).ui_font_size(cx);
29265        let editor_margins = *self.editor_margins.lock();
29266        let gutter_dimensions = editor_margins.gutter;
29267        let left_gutter_width = gutter_dimensions.full_width() + (gutter_dimensions.margin / 2.0);
29268        let right_padding = editor_margins.right + px(9.);
29269        h_flex()
29270            .key_context("Editor")
29271            .bg(cx.theme().colors().editor_background)
29272            .border_y_1()
29273            .border_color(cx.theme().status().info_border)
29274            .size_full()
29275            .py(window.line_height() / 2.5)
29276            .pr(right_padding)
29277            .on_action(cx.listener(Self::confirm))
29278            .on_action(cx.listener(Self::cancel))
29279            .child(
29280                WithRemSize::new(ui_font_size)
29281                    .h_full()
29282                    .w(left_gutter_width)
29283                    .flex()
29284                    .flex_row()
29285                    .flex_shrink_0()
29286                    .items_center()
29287                    .justify_center()
29288                    .gap_1()
29289                    .child(self.render_close_button(cx)),
29290            )
29291            .child(
29292                h_flex()
29293                    .w_full()
29294                    .justify_between()
29295                    .child(div().flex_1().child(self.render_prompt_editor(cx)))
29296                    .child(
29297                        WithRemSize::new(ui_font_size)
29298                            .flex()
29299                            .flex_row()
29300                            .items_center()
29301                            .child(self.render_confirm_button(cx)),
29302                    ),
29303            )
29304    }
29305}
29306
29307impl Focusable for BreakpointPromptEditor {
29308    fn focus_handle(&self, cx: &App) -> FocusHandle {
29309        self.prompt.focus_handle(cx)
29310    }
29311}
29312
29313fn all_edits_insertions_or_deletions(
29314    edits: &Vec<(Range<Anchor>, Arc<str>)>,
29315    snapshot: &MultiBufferSnapshot,
29316) -> bool {
29317    let mut all_insertions = true;
29318    let mut all_deletions = true;
29319
29320    for (range, new_text) in edits.iter() {
29321        let range_is_empty = range.to_offset(snapshot).is_empty();
29322        let text_is_empty = new_text.is_empty();
29323
29324        if range_is_empty != text_is_empty {
29325            if range_is_empty {
29326                all_deletions = false;
29327            } else {
29328                all_insertions = false;
29329            }
29330        } else {
29331            return false;
29332        }
29333
29334        if !all_insertions && !all_deletions {
29335            return false;
29336        }
29337    }
29338    all_insertions || all_deletions
29339}
29340
29341struct MissingEditPredictionKeybindingTooltip;
29342
29343impl Render for MissingEditPredictionKeybindingTooltip {
29344    fn render(&mut self, _: &mut Window, cx: &mut Context<Self>) -> impl IntoElement {
29345        ui::tooltip_container(cx, |container, cx| {
29346            container
29347                .flex_shrink_0()
29348                .max_w_80()
29349                .min_h(rems_from_px(124.))
29350                .justify_between()
29351                .child(
29352                    v_flex()
29353                        .flex_1()
29354                        .text_ui_sm(cx)
29355                        .child(Label::new("Conflict with Accept Keybinding"))
29356                        .child("Your keymap currently overrides the default accept keybinding. To continue, assign one keybinding for the `editor::AcceptEditPrediction` action.")
29357                )
29358                .child(
29359                    h_flex()
29360                        .pb_1()
29361                        .gap_1()
29362                        .items_end()
29363                        .w_full()
29364                        .child(Button::new("open-keymap", "Assign Keybinding").size(ButtonSize::Compact).on_click(|_ev, window, cx| {
29365                            window.dispatch_action(zed_actions::OpenKeymapFile.boxed_clone(), cx)
29366                        }))
29367                        .child(Button::new("see-docs", "See Docs").size(ButtonSize::Compact).on_click(|_ev, _window, cx| {
29368                            cx.open_url("https://zed.dev/docs/completions#edit-predictions-missing-keybinding");
29369                        })),
29370                )
29371        })
29372    }
29373}
29374
29375#[derive(Debug, Clone, Copy, PartialEq)]
29376pub struct LineHighlight {
29377    pub background: Background,
29378    pub border: Option<gpui::Hsla>,
29379    pub include_gutter: bool,
29380    pub type_id: Option<TypeId>,
29381}
29382
29383struct LineManipulationResult {
29384    pub new_text: String,
29385    pub line_count_before: usize,
29386    pub line_count_after: usize,
29387}
29388
29389fn render_diff_hunk_controls(
29390    row: u32,
29391    status: &DiffHunkStatus,
29392    hunk_range: Range<Anchor>,
29393    is_created_file: bool,
29394    line_height: Pixels,
29395    editor: &Entity<Editor>,
29396    _window: &mut Window,
29397    cx: &mut App,
29398) -> AnyElement {
29399    h_flex()
29400        .h(line_height)
29401        .mr_1()
29402        .gap_1()
29403        .px_0p5()
29404        .pb_1()
29405        .border_x_1()
29406        .border_b_1()
29407        .border_color(cx.theme().colors().border_variant)
29408        .rounded_b_lg()
29409        .bg(cx.theme().colors().editor_background)
29410        .gap_1()
29411        .block_mouse_except_scroll()
29412        .shadow_md()
29413        .child(if status.has_secondary_hunk() {
29414            Button::new(("stage", row as u64), "Stage")
29415                .alpha(if status.is_pending() { 0.66 } else { 1.0 })
29416                .tooltip({
29417                    let focus_handle = editor.focus_handle(cx);
29418                    move |_window, cx| {
29419                        Tooltip::for_action_in(
29420                            "Stage Hunk",
29421                            &::git::ToggleStaged,
29422                            &focus_handle,
29423                            cx,
29424                        )
29425                    }
29426                })
29427                .on_click({
29428                    let editor = editor.clone();
29429                    move |_event, _window, cx| {
29430                        editor.update(cx, |editor, cx| {
29431                            editor.stage_or_unstage_diff_hunks(
29432                                true,
29433                                vec![hunk_range.start..hunk_range.start],
29434                                cx,
29435                            );
29436                        });
29437                    }
29438                })
29439        } else {
29440            Button::new(("unstage", row as u64), "Unstage")
29441                .alpha(if status.is_pending() { 0.66 } else { 1.0 })
29442                .tooltip({
29443                    let focus_handle = editor.focus_handle(cx);
29444                    move |_window, cx| {
29445                        Tooltip::for_action_in(
29446                            "Unstage Hunk",
29447                            &::git::ToggleStaged,
29448                            &focus_handle,
29449                            cx,
29450                        )
29451                    }
29452                })
29453                .on_click({
29454                    let editor = editor.clone();
29455                    move |_event, _window, cx| {
29456                        editor.update(cx, |editor, cx| {
29457                            editor.stage_or_unstage_diff_hunks(
29458                                false,
29459                                vec![hunk_range.start..hunk_range.start],
29460                                cx,
29461                            );
29462                        });
29463                    }
29464                })
29465        })
29466        .child(
29467            Button::new(("restore", row as u64), "Restore")
29468                .tooltip({
29469                    let focus_handle = editor.focus_handle(cx);
29470                    move |_window, cx| {
29471                        Tooltip::for_action_in("Restore Hunk", &::git::Restore, &focus_handle, cx)
29472                    }
29473                })
29474                .on_click({
29475                    let editor = editor.clone();
29476                    move |_event, window, cx| {
29477                        editor.update(cx, |editor, cx| {
29478                            let snapshot = editor.snapshot(window, cx);
29479                            let point = hunk_range.start.to_point(&snapshot.buffer_snapshot());
29480                            editor.restore_hunks_in_ranges(vec![point..point], window, cx);
29481                        });
29482                    }
29483                })
29484                .disabled(is_created_file),
29485        )
29486        .when(
29487            !editor.read(cx).buffer().read(cx).all_diff_hunks_expanded(),
29488            |el| {
29489                el.child(
29490                    IconButton::new(("next-hunk", row as u64), IconName::ArrowDown)
29491                        .shape(IconButtonShape::Square)
29492                        .icon_size(IconSize::Small)
29493                        // .disabled(!has_multiple_hunks)
29494                        .tooltip({
29495                            let focus_handle = editor.focus_handle(cx);
29496                            move |_window, cx| {
29497                                Tooltip::for_action_in("Next Hunk", &GoToHunk, &focus_handle, cx)
29498                            }
29499                        })
29500                        .on_click({
29501                            let editor = editor.clone();
29502                            move |_event, window, cx| {
29503                                editor.update(cx, |editor, cx| {
29504                                    let snapshot = editor.snapshot(window, cx);
29505                                    let position =
29506                                        hunk_range.end.to_point(&snapshot.buffer_snapshot());
29507                                    editor.go_to_hunk_before_or_after_position(
29508                                        &snapshot,
29509                                        position,
29510                                        Direction::Next,
29511                                        true,
29512                                        window,
29513                                        cx,
29514                                    );
29515                                    editor.expand_selected_diff_hunks(cx);
29516                                });
29517                            }
29518                        }),
29519                )
29520                .child(
29521                    IconButton::new(("prev-hunk", row as u64), IconName::ArrowUp)
29522                        .shape(IconButtonShape::Square)
29523                        .icon_size(IconSize::Small)
29524                        // .disabled(!has_multiple_hunks)
29525                        .tooltip({
29526                            let focus_handle = editor.focus_handle(cx);
29527                            move |_window, cx| {
29528                                Tooltip::for_action_in(
29529                                    "Previous Hunk",
29530                                    &GoToPreviousHunk,
29531                                    &focus_handle,
29532                                    cx,
29533                                )
29534                            }
29535                        })
29536                        .on_click({
29537                            let editor = editor.clone();
29538                            move |_event, window, cx| {
29539                                editor.update(cx, |editor, cx| {
29540                                    let snapshot = editor.snapshot(window, cx);
29541                                    let point =
29542                                        hunk_range.start.to_point(&snapshot.buffer_snapshot());
29543                                    editor.go_to_hunk_before_or_after_position(
29544                                        &snapshot,
29545                                        point,
29546                                        Direction::Prev,
29547                                        true,
29548                                        window,
29549                                        cx,
29550                                    );
29551                                    editor.expand_selected_diff_hunks(cx);
29552                                });
29553                            }
29554                        }),
29555                )
29556            },
29557        )
29558        .into_any_element()
29559}
29560
29561pub fn multibuffer_context_lines(cx: &App) -> u32 {
29562    EditorSettings::try_get(cx)
29563        .map(|settings| settings.excerpt_context_lines)
29564        .unwrap_or(2)
29565        .min(32)
29566}