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, EditorSettingsScrollbarProxy, HideMouseMode, ScrollBeyondLastLine,
   65    ScrollbarAxes, SearchSettings, ShowMinimap, ui_scrollbar_settings_from_raw,
   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        let tx_id = 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            let additional_edits_tx = 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            if let Some(tx_id) = tx_id
 6792                && let Some(additional_edits_tx) = additional_edits_tx
 6793            {
 6794                editor
 6795                    .update(cx, |editor, cx| {
 6796                        editor.buffer.update(cx, |buffer, cx| {
 6797                            buffer.merge_transactions(additional_edits_tx.id, tx_id, cx)
 6798                        });
 6799                    })
 6800                    .context("merge transactions")?;
 6801            }
 6802
 6803            Ok(())
 6804        }))
 6805    }
 6806
 6807    pub fn toggle_code_actions(
 6808        &mut self,
 6809        action: &ToggleCodeActions,
 6810        window: &mut Window,
 6811        cx: &mut Context<Self>,
 6812    ) {
 6813        let quick_launch = action.quick_launch;
 6814        let mut context_menu = self.context_menu.borrow_mut();
 6815        if let Some(CodeContextMenu::CodeActions(code_actions)) = context_menu.as_ref() {
 6816            if code_actions.deployed_from == action.deployed_from {
 6817                // Toggle if we're selecting the same one
 6818                *context_menu = None;
 6819                cx.notify();
 6820                return;
 6821            } else {
 6822                // Otherwise, clear it and start a new one
 6823                *context_menu = None;
 6824                cx.notify();
 6825            }
 6826        }
 6827        drop(context_menu);
 6828        let snapshot = self.snapshot(window, cx);
 6829        let deployed_from = action.deployed_from.clone();
 6830        let action = action.clone();
 6831        self.completion_tasks.clear();
 6832        self.discard_edit_prediction(EditPredictionDiscardReason::Ignored, cx);
 6833
 6834        let multibuffer_point = match &action.deployed_from {
 6835            Some(CodeActionSource::Indicator(row)) | Some(CodeActionSource::RunMenu(row)) => {
 6836                DisplayPoint::new(*row, 0).to_point(&snapshot)
 6837            }
 6838            _ => self
 6839                .selections
 6840                .newest::<Point>(&snapshot.display_snapshot)
 6841                .head(),
 6842        };
 6843        let Some((buffer, buffer_row)) = snapshot
 6844            .buffer_snapshot()
 6845            .buffer_line_for_row(MultiBufferRow(multibuffer_point.row))
 6846            .and_then(|(buffer_snapshot, range)| {
 6847                self.buffer()
 6848                    .read(cx)
 6849                    .buffer(buffer_snapshot.remote_id())
 6850                    .map(|buffer| (buffer, range.start.row))
 6851            })
 6852        else {
 6853            return;
 6854        };
 6855        let buffer_id = buffer.read(cx).remote_id();
 6856        let tasks = self
 6857            .runnables
 6858            .runnables((buffer_id, buffer_row))
 6859            .map(|t| Arc::new(t.to_owned()));
 6860
 6861        if !self.focus_handle.is_focused(window) {
 6862            return;
 6863        }
 6864        let project = self.project.clone();
 6865
 6866        let code_actions_task = match deployed_from {
 6867            Some(CodeActionSource::RunMenu(_)) => Task::ready(None),
 6868            _ => self.code_actions(buffer_row, window, cx),
 6869        };
 6870
 6871        let runnable_task = match deployed_from {
 6872            Some(CodeActionSource::Indicator(_)) => Task::ready(Ok(Default::default())),
 6873            _ => {
 6874                let mut task_context_task = Task::ready(None);
 6875                if let Some(tasks) = &tasks
 6876                    && let Some(project) = project
 6877                {
 6878                    task_context_task =
 6879                        Self::build_tasks_context(&project, &buffer, buffer_row, tasks, cx);
 6880                }
 6881
 6882                cx.spawn_in(window, {
 6883                    let buffer = buffer.clone();
 6884                    async move |editor, cx| {
 6885                        let task_context = task_context_task.await;
 6886
 6887                        let resolved_tasks =
 6888                            tasks
 6889                                .zip(task_context.clone())
 6890                                .map(|(tasks, task_context)| ResolvedTasks {
 6891                                    templates: tasks.resolve(&task_context).collect(),
 6892                                    position: snapshot.buffer_snapshot().anchor_before(Point::new(
 6893                                        multibuffer_point.row,
 6894                                        tasks.column,
 6895                                    )),
 6896                                });
 6897                        let debug_scenarios = editor
 6898                            .update(cx, |editor, cx| {
 6899                                editor.debug_scenarios(&resolved_tasks, &buffer, cx)
 6900                            })?
 6901                            .await;
 6902                        anyhow::Ok((resolved_tasks, debug_scenarios, task_context))
 6903                    }
 6904                })
 6905            }
 6906        };
 6907
 6908        cx.spawn_in(window, async move |editor, cx| {
 6909            let (resolved_tasks, debug_scenarios, task_context) = runnable_task.await?;
 6910            let code_actions = code_actions_task.await;
 6911            let spawn_straight_away = quick_launch
 6912                && resolved_tasks
 6913                    .as_ref()
 6914                    .is_some_and(|tasks| tasks.templates.len() == 1)
 6915                && code_actions
 6916                    .as_ref()
 6917                    .is_none_or(|actions| actions.is_empty())
 6918                && debug_scenarios.is_empty();
 6919
 6920            editor.update_in(cx, |editor, window, cx| {
 6921                crate::hover_popover::hide_hover(editor, cx);
 6922                let actions = CodeActionContents::new(
 6923                    resolved_tasks,
 6924                    code_actions,
 6925                    debug_scenarios,
 6926                    task_context.unwrap_or_default(),
 6927                );
 6928
 6929                // Don't show the menu if there are no actions available
 6930                if actions.is_empty() {
 6931                    cx.notify();
 6932                    return Task::ready(Ok(()));
 6933                }
 6934
 6935                *editor.context_menu.borrow_mut() =
 6936                    Some(CodeContextMenu::CodeActions(CodeActionsMenu {
 6937                        buffer,
 6938                        actions,
 6939                        selected_item: Default::default(),
 6940                        scroll_handle: UniformListScrollHandle::default(),
 6941                        deployed_from,
 6942                    }));
 6943                cx.notify();
 6944                if spawn_straight_away
 6945                    && let Some(task) = editor.confirm_code_action(
 6946                        &ConfirmCodeAction { item_ix: Some(0) },
 6947                        window,
 6948                        cx,
 6949                    )
 6950                {
 6951                    return task;
 6952                }
 6953
 6954                Task::ready(Ok(()))
 6955            })
 6956        })
 6957        .detach_and_log_err(cx);
 6958    }
 6959
 6960    fn debug_scenarios(
 6961        &mut self,
 6962        resolved_tasks: &Option<ResolvedTasks>,
 6963        buffer: &Entity<Buffer>,
 6964        cx: &mut App,
 6965    ) -> Task<Vec<task::DebugScenario>> {
 6966        maybe!({
 6967            let project = self.project()?;
 6968            let dap_store = project.read(cx).dap_store();
 6969            let mut scenarios = vec![];
 6970            let resolved_tasks = resolved_tasks.as_ref()?;
 6971            let buffer = buffer.read(cx);
 6972            let language = buffer.language()?;
 6973            let debug_adapter = LanguageSettings::for_buffer(&buffer, cx)
 6974                .debuggers
 6975                .first()
 6976                .map(SharedString::from)
 6977                .or_else(|| language.config().debuggers.first().map(SharedString::from))?;
 6978
 6979            dap_store.update(cx, |dap_store, cx| {
 6980                for (_, task) in &resolved_tasks.templates {
 6981                    let maybe_scenario = dap_store.debug_scenario_for_build_task(
 6982                        task.original_task().clone(),
 6983                        debug_adapter.clone().into(),
 6984                        task.display_label().to_owned().into(),
 6985                        cx,
 6986                    );
 6987                    scenarios.push(maybe_scenario);
 6988                }
 6989            });
 6990            Some(cx.background_spawn(async move {
 6991                futures::future::join_all(scenarios)
 6992                    .await
 6993                    .into_iter()
 6994                    .flatten()
 6995                    .collect::<Vec<_>>()
 6996            }))
 6997        })
 6998        .unwrap_or_else(|| Task::ready(vec![]))
 6999    }
 7000
 7001    fn code_actions(
 7002        &mut self,
 7003        buffer_row: u32,
 7004        window: &mut Window,
 7005        cx: &mut Context<Self>,
 7006    ) -> Task<Option<Rc<[AvailableCodeAction]>>> {
 7007        let mut task = self.code_actions_task.take();
 7008        cx.spawn_in(window, async move |editor, cx| {
 7009            while let Some(prev_task) = task {
 7010                prev_task.await.log_err();
 7011                task = editor
 7012                    .update(cx, |this, _| this.code_actions_task.take())
 7013                    .ok()?;
 7014            }
 7015
 7016            editor
 7017                .update(cx, |editor, cx| {
 7018                    editor
 7019                        .available_code_actions
 7020                        .clone()
 7021                        .and_then(|(location, code_actions)| {
 7022                            let snapshot = location.buffer.read(cx).snapshot();
 7023                            let point_range = location.range.to_point(&snapshot);
 7024                            let point_range = point_range.start.row..=point_range.end.row;
 7025                            if point_range.contains(&buffer_row) {
 7026                                Some(code_actions)
 7027                            } else {
 7028                                None
 7029                            }
 7030                        })
 7031                })
 7032                .ok()
 7033                .flatten()
 7034        })
 7035    }
 7036
 7037    pub fn confirm_code_action(
 7038        &mut self,
 7039        action: &ConfirmCodeAction,
 7040        window: &mut Window,
 7041        cx: &mut Context<Self>,
 7042    ) -> Option<Task<Result<()>>> {
 7043        self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
 7044
 7045        let actions_menu =
 7046            if let CodeContextMenu::CodeActions(menu) = self.hide_context_menu(window, cx)? {
 7047                menu
 7048            } else {
 7049                return None;
 7050            };
 7051
 7052        let action_ix = action.item_ix.unwrap_or(actions_menu.selected_item);
 7053        let action = actions_menu.actions.get(action_ix)?;
 7054        let title = action.label();
 7055        let buffer = actions_menu.buffer;
 7056        let workspace = self.workspace()?;
 7057
 7058        match action {
 7059            CodeActionsItem::Task(task_source_kind, resolved_task) => {
 7060                workspace.update(cx, |workspace, cx| {
 7061                    workspace.schedule_resolved_task(
 7062                        task_source_kind,
 7063                        resolved_task,
 7064                        false,
 7065                        window,
 7066                        cx,
 7067                    );
 7068
 7069                    Some(Task::ready(Ok(())))
 7070                })
 7071            }
 7072            CodeActionsItem::CodeAction {
 7073                excerpt_id,
 7074                action,
 7075                provider,
 7076            } => {
 7077                let apply_code_action =
 7078                    provider.apply_code_action(buffer, action, excerpt_id, true, window, cx);
 7079                let workspace = workspace.downgrade();
 7080                Some(cx.spawn_in(window, async move |editor, cx| {
 7081                    let project_transaction = apply_code_action.await?;
 7082                    Self::open_project_transaction(
 7083                        &editor,
 7084                        workspace,
 7085                        project_transaction,
 7086                        title,
 7087                        cx,
 7088                    )
 7089                    .await
 7090                }))
 7091            }
 7092            CodeActionsItem::DebugScenario(scenario) => {
 7093                let context = actions_menu.actions.context.into();
 7094
 7095                workspace.update(cx, |workspace, cx| {
 7096                    dap::send_telemetry(&scenario, TelemetrySpawnLocation::Gutter, cx);
 7097                    workspace.start_debug_session(
 7098                        scenario,
 7099                        context,
 7100                        Some(buffer),
 7101                        None,
 7102                        window,
 7103                        cx,
 7104                    );
 7105                });
 7106                Some(Task::ready(Ok(())))
 7107            }
 7108        }
 7109    }
 7110
 7111    fn open_transaction_for_hidden_buffers(
 7112        workspace: Entity<Workspace>,
 7113        transaction: ProjectTransaction,
 7114        title: String,
 7115        window: &mut Window,
 7116        cx: &mut Context<Self>,
 7117    ) {
 7118        if transaction.0.is_empty() {
 7119            return;
 7120        }
 7121
 7122        let edited_buffers_already_open = {
 7123            let other_editors: Vec<Entity<Editor>> = workspace
 7124                .read(cx)
 7125                .panes()
 7126                .iter()
 7127                .flat_map(|pane| pane.read(cx).items_of_type::<Editor>())
 7128                .filter(|editor| editor.entity_id() != cx.entity_id())
 7129                .collect();
 7130
 7131            transaction.0.keys().all(|buffer| {
 7132                other_editors.iter().any(|editor| {
 7133                    let multi_buffer = editor.read(cx).buffer();
 7134                    multi_buffer.read(cx).is_singleton()
 7135                        && multi_buffer
 7136                            .read(cx)
 7137                            .as_singleton()
 7138                            .map_or(false, |singleton| {
 7139                                singleton.entity_id() == buffer.entity_id()
 7140                            })
 7141                })
 7142            })
 7143        };
 7144        if !edited_buffers_already_open {
 7145            let workspace = workspace.downgrade();
 7146            cx.defer_in(window, move |_, window, cx| {
 7147                cx.spawn_in(window, async move |editor, cx| {
 7148                    Self::open_project_transaction(&editor, workspace, transaction, title, cx)
 7149                        .await
 7150                        .ok()
 7151                })
 7152                .detach();
 7153            });
 7154        }
 7155    }
 7156
 7157    pub async fn open_project_transaction(
 7158        editor: &WeakEntity<Editor>,
 7159        workspace: WeakEntity<Workspace>,
 7160        transaction: ProjectTransaction,
 7161        title: String,
 7162        cx: &mut AsyncWindowContext,
 7163    ) -> Result<()> {
 7164        let mut entries = transaction.0.into_iter().collect::<Vec<_>>();
 7165        cx.update(|_, cx| {
 7166            entries.sort_unstable_by_key(|(buffer, _)| {
 7167                buffer.read(cx).file().map(|f| f.path().clone())
 7168            });
 7169        })?;
 7170        if entries.is_empty() {
 7171            return Ok(());
 7172        }
 7173
 7174        // If the project transaction's edits are all contained within this editor, then
 7175        // avoid opening a new editor to display them.
 7176
 7177        if let [(buffer, transaction)] = &*entries {
 7178            let excerpt = editor.update(cx, |editor, cx| {
 7179                editor
 7180                    .buffer()
 7181                    .read(cx)
 7182                    .excerpt_containing(editor.selections.newest_anchor().head(), cx)
 7183            })?;
 7184            if let Some((_, excerpted_buffer, excerpt_range)) = excerpt
 7185                && excerpted_buffer == *buffer
 7186            {
 7187                let all_edits_within_excerpt = buffer.read_with(cx, |buffer, _| {
 7188                    let excerpt_range = excerpt_range.to_offset(buffer);
 7189                    buffer
 7190                        .edited_ranges_for_transaction::<usize>(transaction)
 7191                        .all(|range| {
 7192                            excerpt_range.start <= range.start && excerpt_range.end >= range.end
 7193                        })
 7194                });
 7195
 7196                if all_edits_within_excerpt {
 7197                    return Ok(());
 7198                }
 7199            }
 7200        }
 7201
 7202        let mut ranges_to_highlight = Vec::new();
 7203        let excerpt_buffer = cx.new(|cx| {
 7204            let mut multibuffer = MultiBuffer::new(Capability::ReadWrite).with_title(title);
 7205            for (buffer_handle, transaction) in &entries {
 7206                let edited_ranges = buffer_handle
 7207                    .read(cx)
 7208                    .edited_ranges_for_transaction::<Point>(transaction)
 7209                    .collect::<Vec<_>>();
 7210                let (ranges, _) = multibuffer.set_excerpts_for_path(
 7211                    PathKey::for_buffer(buffer_handle, cx),
 7212                    buffer_handle.clone(),
 7213                    edited_ranges,
 7214                    multibuffer_context_lines(cx),
 7215                    cx,
 7216                );
 7217
 7218                ranges_to_highlight.extend(ranges);
 7219            }
 7220            multibuffer.push_transaction(entries.iter().map(|(b, t)| (b, t)), cx);
 7221            multibuffer
 7222        });
 7223
 7224        workspace.update_in(cx, |workspace, window, cx| {
 7225            let project = workspace.project().clone();
 7226            let editor =
 7227                cx.new(|cx| Editor::for_multibuffer(excerpt_buffer, Some(project), window, cx));
 7228            workspace.add_item_to_active_pane(Box::new(editor.clone()), None, true, window, cx);
 7229            editor.update(cx, |editor, cx| {
 7230                editor.highlight_background(
 7231                    HighlightKey::Editor,
 7232                    &ranges_to_highlight,
 7233                    |_, theme| theme.colors().editor_highlighted_line_background,
 7234                    cx,
 7235                );
 7236            });
 7237        })?;
 7238
 7239        Ok(())
 7240    }
 7241
 7242    pub fn clear_code_action_providers(&mut self) {
 7243        self.code_action_providers.clear();
 7244        self.available_code_actions.take();
 7245    }
 7246
 7247    pub fn add_code_action_provider(
 7248        &mut self,
 7249        provider: Rc<dyn CodeActionProvider>,
 7250        window: &mut Window,
 7251        cx: &mut Context<Self>,
 7252    ) {
 7253        if self
 7254            .code_action_providers
 7255            .iter()
 7256            .any(|existing_provider| existing_provider.id() == provider.id())
 7257        {
 7258            return;
 7259        }
 7260
 7261        self.code_action_providers.push(provider);
 7262        self.refresh_code_actions(window, cx);
 7263    }
 7264
 7265    pub fn remove_code_action_provider(
 7266        &mut self,
 7267        id: Arc<str>,
 7268        window: &mut Window,
 7269        cx: &mut Context<Self>,
 7270    ) {
 7271        self.code_action_providers
 7272            .retain(|provider| provider.id() != id);
 7273        self.refresh_code_actions(window, cx);
 7274    }
 7275
 7276    pub fn code_actions_enabled_for_toolbar(&self, cx: &App) -> bool {
 7277        !self.code_action_providers.is_empty()
 7278            && EditorSettings::get_global(cx).toolbar.code_actions
 7279    }
 7280
 7281    pub fn has_available_code_actions(&self) -> bool {
 7282        self.available_code_actions
 7283            .as_ref()
 7284            .is_some_and(|(_, actions)| !actions.is_empty())
 7285    }
 7286
 7287    fn render_inline_code_actions(
 7288        &self,
 7289        icon_size: ui::IconSize,
 7290        display_row: DisplayRow,
 7291        is_active: bool,
 7292        cx: &mut Context<Self>,
 7293    ) -> AnyElement {
 7294        let show_tooltip = !self.context_menu_visible();
 7295        IconButton::new("inline_code_actions", ui::IconName::BoltFilled)
 7296            .icon_size(icon_size)
 7297            .shape(ui::IconButtonShape::Square)
 7298            .icon_color(ui::Color::Hidden)
 7299            .toggle_state(is_active)
 7300            .when(show_tooltip, |this| {
 7301                this.tooltip({
 7302                    let focus_handle = self.focus_handle.clone();
 7303                    move |_window, cx| {
 7304                        Tooltip::for_action_in(
 7305                            "Toggle Code Actions",
 7306                            &ToggleCodeActions {
 7307                                deployed_from: None,
 7308                                quick_launch: false,
 7309                            },
 7310                            &focus_handle,
 7311                            cx,
 7312                        )
 7313                    }
 7314                })
 7315            })
 7316            .on_click(cx.listener(move |editor, _: &ClickEvent, window, cx| {
 7317                window.focus(&editor.focus_handle(cx), cx);
 7318                editor.toggle_code_actions(
 7319                    &crate::actions::ToggleCodeActions {
 7320                        deployed_from: Some(crate::actions::CodeActionSource::Indicator(
 7321                            display_row,
 7322                        )),
 7323                        quick_launch: false,
 7324                    },
 7325                    window,
 7326                    cx,
 7327                );
 7328            }))
 7329            .into_any_element()
 7330    }
 7331
 7332    pub fn context_menu(&self) -> &RefCell<Option<CodeContextMenu>> {
 7333        &self.context_menu
 7334    }
 7335
 7336    fn refresh_code_actions(&mut self, window: &mut Window, cx: &mut Context<Self>) {
 7337        self.code_actions_task = Some(cx.spawn_in(window, async move |this, cx| {
 7338            cx.background_executor()
 7339                .timer(CODE_ACTIONS_DEBOUNCE_TIMEOUT)
 7340                .await;
 7341
 7342            let (start_buffer, start, _, end, newest_selection) = this
 7343                .update(cx, |this, cx| {
 7344                    let newest_selection = this.selections.newest_anchor().clone();
 7345                    if newest_selection.head().diff_base_anchor.is_some() {
 7346                        return None;
 7347                    }
 7348                    let display_snapshot = this.display_snapshot(cx);
 7349                    let newest_selection_adjusted =
 7350                        this.selections.newest_adjusted(&display_snapshot);
 7351                    let buffer = this.buffer.read(cx);
 7352
 7353                    let (start_buffer, start) =
 7354                        buffer.text_anchor_for_position(newest_selection_adjusted.start, cx)?;
 7355                    let (end_buffer, end) =
 7356                        buffer.text_anchor_for_position(newest_selection_adjusted.end, cx)?;
 7357
 7358                    Some((start_buffer, start, end_buffer, end, newest_selection))
 7359                })?
 7360                .filter(|(start_buffer, _, end_buffer, _, _)| start_buffer == end_buffer)
 7361                .context(
 7362                    "Expected selection to lie in a single buffer when refreshing code actions",
 7363                )?;
 7364            let (providers, tasks) = this.update_in(cx, |this, window, cx| {
 7365                let providers = this.code_action_providers.clone();
 7366                let tasks = this
 7367                    .code_action_providers
 7368                    .iter()
 7369                    .map(|provider| provider.code_actions(&start_buffer, start..end, window, cx))
 7370                    .collect::<Vec<_>>();
 7371                (providers, tasks)
 7372            })?;
 7373
 7374            let mut actions = Vec::new();
 7375            for (provider, provider_actions) in
 7376                providers.into_iter().zip(future::join_all(tasks).await)
 7377            {
 7378                if let Some(provider_actions) = provider_actions.log_err() {
 7379                    actions.extend(provider_actions.into_iter().map(|action| {
 7380                        AvailableCodeAction {
 7381                            excerpt_id: newest_selection.start.excerpt_id,
 7382                            action,
 7383                            provider: provider.clone(),
 7384                        }
 7385                    }));
 7386                }
 7387            }
 7388
 7389            this.update(cx, |this, cx| {
 7390                this.available_code_actions = if actions.is_empty() {
 7391                    None
 7392                } else {
 7393                    Some((
 7394                        Location {
 7395                            buffer: start_buffer,
 7396                            range: start..end,
 7397                        },
 7398                        actions.into(),
 7399                    ))
 7400                };
 7401                cx.notify();
 7402            })
 7403        }));
 7404    }
 7405
 7406    fn start_inline_blame_timer(&mut self, window: &mut Window, cx: &mut Context<Self>) {
 7407        if let Some(delay) = ProjectSettings::get_global(cx).git.inline_blame_delay() {
 7408            self.show_git_blame_inline = false;
 7409
 7410            self.show_git_blame_inline_delay_task =
 7411                Some(cx.spawn_in(window, async move |this, cx| {
 7412                    cx.background_executor().timer(delay).await;
 7413
 7414                    this.update(cx, |this, cx| {
 7415                        this.show_git_blame_inline = true;
 7416                        cx.notify();
 7417                    })
 7418                    .log_err();
 7419                }));
 7420        }
 7421    }
 7422
 7423    pub fn blame_hover(&mut self, _: &BlameHover, window: &mut Window, cx: &mut Context<Self>) {
 7424        let snapshot = self.snapshot(window, cx);
 7425        let cursor = self
 7426            .selections
 7427            .newest::<Point>(&snapshot.display_snapshot)
 7428            .head();
 7429        let Some((buffer, point, _)) = snapshot.buffer_snapshot().point_to_buffer_point(cursor)
 7430        else {
 7431            return;
 7432        };
 7433
 7434        if self.blame.is_none() {
 7435            self.start_git_blame(true, window, cx);
 7436        }
 7437        let Some(blame) = self.blame.as_ref() else {
 7438            return;
 7439        };
 7440
 7441        let row_info = RowInfo {
 7442            buffer_id: Some(buffer.remote_id()),
 7443            buffer_row: Some(point.row),
 7444            ..Default::default()
 7445        };
 7446        let Some((buffer, blame_entry)) = blame
 7447            .update(cx, |blame, cx| blame.blame_for_rows(&[row_info], cx).next())
 7448            .flatten()
 7449        else {
 7450            return;
 7451        };
 7452
 7453        let anchor = self.selections.newest_anchor().head();
 7454        let position = self.to_pixel_point(anchor, &snapshot, window, cx);
 7455        if let (Some(position), Some(last_bounds)) = (position, self.last_bounds) {
 7456            self.show_blame_popover(
 7457                buffer,
 7458                &blame_entry,
 7459                position + last_bounds.origin,
 7460                true,
 7461                cx,
 7462            );
 7463        };
 7464    }
 7465
 7466    fn show_blame_popover(
 7467        &mut self,
 7468        buffer: BufferId,
 7469        blame_entry: &BlameEntry,
 7470        position: gpui::Point<Pixels>,
 7471        ignore_timeout: bool,
 7472        cx: &mut Context<Self>,
 7473    ) {
 7474        if let Some(state) = &mut self.inline_blame_popover {
 7475            state.hide_task.take();
 7476        } else {
 7477            let blame_popover_delay = EditorSettings::get_global(cx).hover_popover_delay.0;
 7478            let blame_entry = blame_entry.clone();
 7479            let show_task = cx.spawn(async move |editor, cx| {
 7480                if !ignore_timeout {
 7481                    cx.background_executor()
 7482                        .timer(std::time::Duration::from_millis(blame_popover_delay))
 7483                        .await;
 7484                }
 7485                editor
 7486                    .update(cx, |editor, cx| {
 7487                        editor.inline_blame_popover_show_task.take();
 7488                        let Some(blame) = editor.blame.as_ref() else {
 7489                            return;
 7490                        };
 7491                        let blame = blame.read(cx);
 7492                        let details = blame.details_for_entry(buffer, &blame_entry);
 7493                        let markdown = cx.new(|cx| {
 7494                            Markdown::new(
 7495                                details
 7496                                    .as_ref()
 7497                                    .map(|message| message.message.clone())
 7498                                    .unwrap_or_default(),
 7499                                None,
 7500                                None,
 7501                                cx,
 7502                            )
 7503                        });
 7504                        editor.inline_blame_popover = Some(InlineBlamePopover {
 7505                            position,
 7506                            hide_task: None,
 7507                            popover_bounds: None,
 7508                            popover_state: InlineBlamePopoverState {
 7509                                scroll_handle: ScrollHandle::new(),
 7510                                commit_message: details,
 7511                                markdown,
 7512                            },
 7513                            keyboard_grace: ignore_timeout,
 7514                        });
 7515                        cx.notify();
 7516                    })
 7517                    .ok();
 7518            });
 7519            self.inline_blame_popover_show_task = Some(show_task);
 7520        }
 7521    }
 7522
 7523    pub fn has_mouse_context_menu(&self) -> bool {
 7524        self.mouse_context_menu.is_some()
 7525    }
 7526
 7527    pub fn hide_blame_popover(&mut self, ignore_timeout: bool, cx: &mut Context<Self>) -> bool {
 7528        self.inline_blame_popover_show_task.take();
 7529        if let Some(state) = &mut self.inline_blame_popover {
 7530            let hide_task = cx.spawn(async move |editor, cx| {
 7531                if !ignore_timeout {
 7532                    cx.background_executor()
 7533                        .timer(std::time::Duration::from_millis(100))
 7534                        .await;
 7535                }
 7536                editor
 7537                    .update(cx, |editor, cx| {
 7538                        editor.inline_blame_popover.take();
 7539                        cx.notify();
 7540                    })
 7541                    .ok();
 7542            });
 7543            state.hide_task = Some(hide_task);
 7544            true
 7545        } else {
 7546            false
 7547        }
 7548    }
 7549
 7550    fn refresh_document_highlights(&mut self, cx: &mut Context<Self>) -> Option<()> {
 7551        if self.pending_rename.is_some() {
 7552            return None;
 7553        }
 7554
 7555        let provider = self.semantics_provider.clone()?;
 7556        let buffer = self.buffer.read(cx);
 7557        let newest_selection = self.selections.newest_anchor().clone();
 7558        let cursor_position = newest_selection.head();
 7559        let (cursor_buffer, cursor_buffer_position) =
 7560            buffer.text_anchor_for_position(cursor_position, cx)?;
 7561        let (tail_buffer, tail_buffer_position) =
 7562            buffer.text_anchor_for_position(newest_selection.tail(), cx)?;
 7563        if cursor_buffer != tail_buffer {
 7564            return None;
 7565        }
 7566
 7567        let snapshot = cursor_buffer.read(cx).snapshot();
 7568        let word_ranges = cx.background_spawn(async move {
 7569            // this might look odd to put on the background thread, but
 7570            // `surrounding_word` can be quite expensive as it calls into
 7571            // tree-sitter language scopes
 7572            let (start_word_range, _) = snapshot.surrounding_word(cursor_buffer_position, None);
 7573            let (end_word_range, _) = snapshot.surrounding_word(tail_buffer_position, None);
 7574            (start_word_range, end_word_range)
 7575        });
 7576
 7577        let debounce = EditorSettings::get_global(cx).lsp_highlight_debounce.0;
 7578        self.document_highlights_task = Some(cx.spawn(async move |this, cx| {
 7579            let (start_word_range, end_word_range) = word_ranges.await;
 7580            if start_word_range != end_word_range {
 7581                this.update(cx, |this, cx| {
 7582                    this.document_highlights_task.take();
 7583                    this.clear_background_highlights(HighlightKey::DocumentHighlightRead, cx);
 7584                    this.clear_background_highlights(HighlightKey::DocumentHighlightWrite, cx);
 7585                })
 7586                .ok();
 7587                return;
 7588            }
 7589            cx.background_executor()
 7590                .timer(Duration::from_millis(debounce))
 7591                .await;
 7592
 7593            let highlights = if let Some(highlights) = cx.update(|cx| {
 7594                provider.document_highlights(&cursor_buffer, cursor_buffer_position, cx)
 7595            }) {
 7596                highlights.await.log_err()
 7597            } else {
 7598                None
 7599            };
 7600
 7601            if let Some(highlights) = highlights {
 7602                this.update(cx, |this, cx| {
 7603                    if this.pending_rename.is_some() {
 7604                        return;
 7605                    }
 7606
 7607                    let buffer = this.buffer.read(cx);
 7608                    if buffer
 7609                        .text_anchor_for_position(cursor_position, cx)
 7610                        .is_none_or(|(buffer, _)| buffer != cursor_buffer)
 7611                    {
 7612                        return;
 7613                    }
 7614
 7615                    let cursor_buffer_snapshot = cursor_buffer.read(cx);
 7616                    let mut write_ranges = Vec::new();
 7617                    let mut read_ranges = Vec::new();
 7618                    for highlight in highlights {
 7619                        let buffer_id = cursor_buffer.read(cx).remote_id();
 7620                        for (excerpt_id, _, excerpt_range) in
 7621                            buffer.excerpts_for_buffer(buffer_id, cx)
 7622                        {
 7623                            let start = highlight
 7624                                .range
 7625                                .start
 7626                                .max(&excerpt_range.context.start, cursor_buffer_snapshot);
 7627                            let end = highlight
 7628                                .range
 7629                                .end
 7630                                .min(&excerpt_range.context.end, cursor_buffer_snapshot);
 7631                            if start.cmp(&end, cursor_buffer_snapshot).is_ge() {
 7632                                continue;
 7633                            }
 7634
 7635                            let range = Anchor::range_in_buffer(excerpt_id, *start..*end);
 7636                            if highlight.kind == lsp::DocumentHighlightKind::WRITE {
 7637                                write_ranges.push(range);
 7638                            } else {
 7639                                read_ranges.push(range);
 7640                            }
 7641                        }
 7642                    }
 7643
 7644                    this.highlight_background(
 7645                        HighlightKey::DocumentHighlightRead,
 7646                        &read_ranges,
 7647                        |_, theme| theme.colors().editor_document_highlight_read_background,
 7648                        cx,
 7649                    );
 7650                    this.highlight_background(
 7651                        HighlightKey::DocumentHighlightWrite,
 7652                        &write_ranges,
 7653                        |_, theme| theme.colors().editor_document_highlight_write_background,
 7654                        cx,
 7655                    );
 7656                    cx.notify();
 7657                })
 7658                .log_err();
 7659            }
 7660        }));
 7661        None
 7662    }
 7663
 7664    fn prepare_highlight_query_from_selection(
 7665        &mut self,
 7666        snapshot: &DisplaySnapshot,
 7667        cx: &mut Context<Editor>,
 7668    ) -> Option<(String, Range<Anchor>)> {
 7669        if matches!(self.mode, EditorMode::SingleLine) {
 7670            return None;
 7671        }
 7672        if !EditorSettings::get_global(cx).selection_highlight {
 7673            return None;
 7674        }
 7675        if self.selections.count() != 1 || self.selections.line_mode() {
 7676            return None;
 7677        }
 7678        let selection = self.selections.newest::<Point>(&snapshot);
 7679        // If the selection spans multiple rows OR it is empty
 7680        if selection.start.row != selection.end.row
 7681            || selection.start.column == selection.end.column
 7682        {
 7683            return None;
 7684        }
 7685        let selection_anchor_range = selection.range().to_anchors(snapshot.buffer_snapshot());
 7686        let query = snapshot
 7687            .buffer_snapshot()
 7688            .text_for_range(selection_anchor_range.clone())
 7689            .collect::<String>();
 7690        if query.trim().is_empty() {
 7691            return None;
 7692        }
 7693        Some((query, selection_anchor_range))
 7694    }
 7695
 7696    #[ztracing::instrument(skip_all)]
 7697    fn update_selection_occurrence_highlights(
 7698        &mut self,
 7699        multi_buffer_snapshot: MultiBufferSnapshot,
 7700        query_text: String,
 7701        query_range: Range<Anchor>,
 7702        multi_buffer_range_to_query: Range<Point>,
 7703        use_debounce: bool,
 7704        window: &mut Window,
 7705        cx: &mut Context<Editor>,
 7706    ) -> Task<()> {
 7707        cx.spawn_in(window, async move |editor, cx| {
 7708            if use_debounce {
 7709                cx.background_executor()
 7710                    .timer(SELECTION_HIGHLIGHT_DEBOUNCE_TIMEOUT)
 7711                    .await;
 7712            }
 7713            let match_task = cx.background_spawn(async move {
 7714                let buffer_ranges = multi_buffer_snapshot
 7715                    .range_to_buffer_ranges(
 7716                        multi_buffer_range_to_query.start..=multi_buffer_range_to_query.end,
 7717                    )
 7718                    .into_iter()
 7719                    .filter(|(_, excerpt_visible_range, _)| !excerpt_visible_range.is_empty());
 7720                let mut match_ranges = Vec::new();
 7721                let Ok(regex) = project::search::SearchQuery::text(
 7722                    query_text,
 7723                    false,
 7724                    false,
 7725                    false,
 7726                    Default::default(),
 7727                    Default::default(),
 7728                    false,
 7729                    None,
 7730                ) else {
 7731                    return Vec::default();
 7732                };
 7733                let query_range = query_range.to_anchors(&multi_buffer_snapshot);
 7734                for (buffer_snapshot, search_range, excerpt_id) in buffer_ranges {
 7735                    match_ranges.extend(
 7736                        regex
 7737                            .search(
 7738                                buffer_snapshot,
 7739                                Some(search_range.start.0..search_range.end.0),
 7740                            )
 7741                            .await
 7742                            .into_iter()
 7743                            .filter_map(|match_range| {
 7744                                let match_start = buffer_snapshot
 7745                                    .anchor_after(search_range.start + match_range.start);
 7746                                let match_end = buffer_snapshot
 7747                                    .anchor_before(search_range.start + match_range.end);
 7748                                let match_anchor_range =
 7749                                    Anchor::range_in_buffer(excerpt_id, match_start..match_end);
 7750                                (match_anchor_range != query_range).then_some(match_anchor_range)
 7751                            }),
 7752                    );
 7753                }
 7754                match_ranges
 7755            });
 7756            let match_ranges = match_task.await;
 7757            editor
 7758                .update_in(cx, |editor, _, cx| {
 7759                    if use_debounce {
 7760                        editor.clear_background_highlights(HighlightKey::SelectedTextHighlight, cx);
 7761                        editor.debounced_selection_highlight_complete = true;
 7762                    } else if editor.debounced_selection_highlight_complete {
 7763                        return;
 7764                    }
 7765                    if !match_ranges.is_empty() {
 7766                        editor.highlight_background(
 7767                            HighlightKey::SelectedTextHighlight,
 7768                            &match_ranges,
 7769                            |_, theme| theme.colors().editor_document_highlight_bracket_background,
 7770                            cx,
 7771                        )
 7772                    }
 7773                })
 7774                .log_err();
 7775        })
 7776    }
 7777
 7778    fn refresh_single_line_folds(&mut self, window: &mut Window, cx: &mut Context<Editor>) {
 7779        struct NewlineFold;
 7780        let type_id = std::any::TypeId::of::<NewlineFold>();
 7781        if !self.mode.is_single_line() {
 7782            return;
 7783        }
 7784        let snapshot = self.snapshot(window, cx);
 7785        if snapshot.buffer_snapshot().max_point().row == 0 {
 7786            return;
 7787        }
 7788        let task = cx.background_spawn(async move {
 7789            let new_newlines = snapshot
 7790                .buffer_chars_at(MultiBufferOffset(0))
 7791                .filter_map(|(c, i)| {
 7792                    if c == '\n' {
 7793                        Some(
 7794                            snapshot.buffer_snapshot().anchor_after(i)
 7795                                ..snapshot.buffer_snapshot().anchor_before(i + 1usize),
 7796                        )
 7797                    } else {
 7798                        None
 7799                    }
 7800                })
 7801                .collect::<Vec<_>>();
 7802            let existing_newlines = snapshot
 7803                .folds_in_range(MultiBufferOffset(0)..snapshot.buffer_snapshot().len())
 7804                .filter_map(|fold| {
 7805                    if fold.placeholder.type_tag == Some(type_id) {
 7806                        Some(fold.range.start..fold.range.end)
 7807                    } else {
 7808                        None
 7809                    }
 7810                })
 7811                .collect::<Vec<_>>();
 7812
 7813            (new_newlines, existing_newlines)
 7814        });
 7815        self.folding_newlines = cx.spawn(async move |this, cx| {
 7816            let (new_newlines, existing_newlines) = task.await;
 7817            if new_newlines == existing_newlines {
 7818                return;
 7819            }
 7820            let placeholder = FoldPlaceholder {
 7821                render: Arc::new(move |_, _, cx| {
 7822                    div()
 7823                        .bg(cx.theme().status().hint_background)
 7824                        .border_b_1()
 7825                        .size_full()
 7826                        .font(ThemeSettings::get_global(cx).buffer_font.clone())
 7827                        .border_color(cx.theme().status().hint)
 7828                        .child("\\n")
 7829                        .into_any()
 7830                }),
 7831                constrain_width: false,
 7832                merge_adjacent: false,
 7833                type_tag: Some(type_id),
 7834                collapsed_text: None,
 7835            };
 7836            let creases = new_newlines
 7837                .into_iter()
 7838                .map(|range| Crease::simple(range, placeholder.clone()))
 7839                .collect();
 7840            this.update(cx, |this, cx| {
 7841                this.display_map.update(cx, |display_map, cx| {
 7842                    display_map.remove_folds_with_type(existing_newlines, type_id, cx);
 7843                    display_map.fold(creases, cx);
 7844                });
 7845            })
 7846            .ok();
 7847        });
 7848    }
 7849
 7850    #[ztracing::instrument(skip_all)]
 7851    fn refresh_outline_symbols_at_cursor(&mut self, cx: &mut Context<Editor>) {
 7852        if !self.lsp_data_enabled() {
 7853            return;
 7854        }
 7855        let cursor = self.selections.newest_anchor().head();
 7856        let multi_buffer_snapshot = self.buffer().read(cx).snapshot(cx);
 7857
 7858        if self.uses_lsp_document_symbols(cursor, &multi_buffer_snapshot, cx) {
 7859            self.outline_symbols_at_cursor =
 7860                self.lsp_symbols_at_cursor(cursor, &multi_buffer_snapshot, cx);
 7861            cx.emit(EditorEvent::OutlineSymbolsChanged);
 7862            cx.notify();
 7863        } else {
 7864            let syntax = cx.theme().syntax().clone();
 7865            let background_task = cx.background_spawn(async move {
 7866                multi_buffer_snapshot.symbols_containing(cursor, Some(&syntax))
 7867            });
 7868            self.refresh_outline_symbols_at_cursor_at_cursor_task =
 7869                cx.spawn(async move |this, cx| {
 7870                    let symbols = background_task.await;
 7871                    this.update(cx, |this, cx| {
 7872                        this.outline_symbols_at_cursor = symbols;
 7873                        cx.emit(EditorEvent::OutlineSymbolsChanged);
 7874                        cx.notify();
 7875                    })
 7876                    .ok();
 7877                });
 7878        }
 7879    }
 7880
 7881    #[ztracing::instrument(skip_all)]
 7882    fn refresh_selected_text_highlights(
 7883        &mut self,
 7884        snapshot: &DisplaySnapshot,
 7885        on_buffer_edit: bool,
 7886        window: &mut Window,
 7887        cx: &mut Context<Editor>,
 7888    ) {
 7889        let Some((query_text, query_range)) =
 7890            self.prepare_highlight_query_from_selection(snapshot, cx)
 7891        else {
 7892            self.clear_background_highlights(HighlightKey::SelectedTextHighlight, cx);
 7893            self.quick_selection_highlight_task.take();
 7894            self.debounced_selection_highlight_task.take();
 7895            self.debounced_selection_highlight_complete = false;
 7896            return;
 7897        };
 7898        let display_snapshot = self.display_map.update(cx, |map, cx| map.snapshot(cx));
 7899        let multi_buffer_snapshot = self.buffer().read(cx).snapshot(cx);
 7900        let query_changed = self
 7901            .quick_selection_highlight_task
 7902            .as_ref()
 7903            .is_none_or(|(prev_anchor_range, _)| prev_anchor_range != &query_range);
 7904        if query_changed {
 7905            self.debounced_selection_highlight_complete = false;
 7906        }
 7907        if on_buffer_edit || query_changed {
 7908            self.quick_selection_highlight_task = Some((
 7909                query_range.clone(),
 7910                self.update_selection_occurrence_highlights(
 7911                    snapshot.buffer.clone(),
 7912                    query_text.clone(),
 7913                    query_range.clone(),
 7914                    self.multi_buffer_visible_range(&display_snapshot, cx),
 7915                    false,
 7916                    window,
 7917                    cx,
 7918                ),
 7919            ));
 7920        }
 7921        if on_buffer_edit
 7922            || self
 7923                .debounced_selection_highlight_task
 7924                .as_ref()
 7925                .is_none_or(|(prev_anchor_range, _)| prev_anchor_range != &query_range)
 7926        {
 7927            let multi_buffer_start = multi_buffer_snapshot
 7928                .anchor_before(MultiBufferOffset(0))
 7929                .to_point(&multi_buffer_snapshot);
 7930            let multi_buffer_end = multi_buffer_snapshot
 7931                .anchor_after(multi_buffer_snapshot.len())
 7932                .to_point(&multi_buffer_snapshot);
 7933            let multi_buffer_full_range = multi_buffer_start..multi_buffer_end;
 7934            self.debounced_selection_highlight_task = Some((
 7935                query_range.clone(),
 7936                self.update_selection_occurrence_highlights(
 7937                    snapshot.buffer.clone(),
 7938                    query_text,
 7939                    query_range,
 7940                    multi_buffer_full_range,
 7941                    true,
 7942                    window,
 7943                    cx,
 7944                ),
 7945            ));
 7946        }
 7947    }
 7948
 7949    pub fn multi_buffer_visible_range(
 7950        &self,
 7951        display_snapshot: &DisplaySnapshot,
 7952        cx: &App,
 7953    ) -> Range<Point> {
 7954        let visible_start = self
 7955            .scroll_manager
 7956            .native_anchor(display_snapshot, cx)
 7957            .anchor
 7958            .to_point(display_snapshot.buffer_snapshot())
 7959            .to_display_point(display_snapshot);
 7960
 7961        let mut target_end = visible_start;
 7962        *target_end.row_mut() += self.visible_line_count().unwrap_or(0.).ceil() as u32;
 7963
 7964        visible_start.to_point(display_snapshot)
 7965            ..display_snapshot
 7966                .clip_point(target_end, Bias::Right)
 7967                .to_point(display_snapshot)
 7968    }
 7969
 7970    pub fn refresh_edit_prediction(
 7971        &mut self,
 7972        debounce: bool,
 7973        user_requested: bool,
 7974        window: &mut Window,
 7975        cx: &mut Context<Self>,
 7976    ) -> Option<()> {
 7977        if self.leader_id.is_some() {
 7978            self.discard_edit_prediction(EditPredictionDiscardReason::Ignored, cx);
 7979            return None;
 7980        }
 7981
 7982        let cursor = self.selections.newest_anchor().head();
 7983        let (buffer, cursor_buffer_position) =
 7984            self.buffer.read(cx).text_anchor_for_position(cursor, cx)?;
 7985
 7986        if DisableAiSettings::is_ai_disabled_for_buffer(Some(&buffer), cx) {
 7987            return None;
 7988        }
 7989
 7990        if !self.edit_predictions_enabled_in_buffer(&buffer, cursor_buffer_position, cx) {
 7991            self.discard_edit_prediction(EditPredictionDiscardReason::Ignored, cx);
 7992            return None;
 7993        }
 7994
 7995        self.update_visible_edit_prediction(window, cx);
 7996
 7997        if !user_requested
 7998            && (!self.should_show_edit_predictions()
 7999                || !self.is_focused(window)
 8000                || buffer.read(cx).is_empty())
 8001        {
 8002            self.discard_edit_prediction(EditPredictionDiscardReason::Ignored, cx);
 8003            return None;
 8004        }
 8005
 8006        self.edit_prediction_provider()?
 8007            .refresh(buffer, cursor_buffer_position, debounce, cx);
 8008        Some(())
 8009    }
 8010
 8011    fn show_edit_predictions_in_menu(&self) -> bool {
 8012        match self.edit_prediction_settings {
 8013            EditPredictionSettings::Disabled => false,
 8014            EditPredictionSettings::Enabled { show_in_menu, .. } => show_in_menu,
 8015        }
 8016    }
 8017
 8018    pub fn edit_predictions_enabled(&self) -> bool {
 8019        match self.edit_prediction_settings {
 8020            EditPredictionSettings::Disabled => false,
 8021            EditPredictionSettings::Enabled { .. } => true,
 8022        }
 8023    }
 8024
 8025    fn edit_prediction_requires_modifier(&self) -> bool {
 8026        match self.edit_prediction_settings {
 8027            EditPredictionSettings::Disabled => false,
 8028            EditPredictionSettings::Enabled {
 8029                preview_requires_modifier,
 8030                ..
 8031            } => preview_requires_modifier,
 8032        }
 8033    }
 8034
 8035    pub fn update_edit_prediction_settings(&mut self, cx: &mut Context<Self>) {
 8036        if self.edit_prediction_provider.is_none() {
 8037            self.edit_prediction_settings = EditPredictionSettings::Disabled;
 8038            self.discard_edit_prediction(EditPredictionDiscardReason::Ignored, cx);
 8039            return;
 8040        }
 8041
 8042        let selection = self.selections.newest_anchor();
 8043        let cursor = selection.head();
 8044
 8045        if let Some((buffer, cursor_buffer_position)) =
 8046            self.buffer.read(cx).text_anchor_for_position(cursor, cx)
 8047        {
 8048            if DisableAiSettings::is_ai_disabled_for_buffer(Some(&buffer), cx) {
 8049                self.edit_prediction_settings = EditPredictionSettings::Disabled;
 8050                self.discard_edit_prediction(EditPredictionDiscardReason::Ignored, cx);
 8051                return;
 8052            }
 8053            self.edit_prediction_settings =
 8054                self.edit_prediction_settings_at_position(&buffer, cursor_buffer_position, cx);
 8055        }
 8056    }
 8057
 8058    fn edit_prediction_settings_at_position(
 8059        &self,
 8060        buffer: &Entity<Buffer>,
 8061        buffer_position: language::Anchor,
 8062        cx: &App,
 8063    ) -> EditPredictionSettings {
 8064        if !self.mode.is_full()
 8065            || !self.show_edit_predictions_override.unwrap_or(true)
 8066            || self.edit_predictions_disabled_in_scope(buffer, buffer_position, cx)
 8067        {
 8068            return EditPredictionSettings::Disabled;
 8069        }
 8070
 8071        if !LanguageSettings::for_buffer(&buffer.read(cx), cx).show_edit_predictions {
 8072            return EditPredictionSettings::Disabled;
 8073        };
 8074
 8075        let by_provider = matches!(
 8076            self.menu_edit_predictions_policy,
 8077            MenuEditPredictionsPolicy::ByProvider
 8078        );
 8079
 8080        let show_in_menu = by_provider
 8081            && self
 8082                .edit_prediction_provider
 8083                .as_ref()
 8084                .is_some_and(|provider| provider.provider.show_predictions_in_menu());
 8085
 8086        let file = buffer.read(cx).file();
 8087        let preview_requires_modifier =
 8088            all_language_settings(file, cx).edit_predictions_mode() == EditPredictionsMode::Subtle;
 8089
 8090        EditPredictionSettings::Enabled {
 8091            show_in_menu,
 8092            preview_requires_modifier,
 8093        }
 8094    }
 8095
 8096    fn should_show_edit_predictions(&self) -> bool {
 8097        self.snippet_stack.is_empty() && self.edit_predictions_enabled()
 8098    }
 8099
 8100    pub fn edit_prediction_preview_is_active(&self) -> bool {
 8101        matches!(
 8102            self.edit_prediction_preview,
 8103            EditPredictionPreview::Active { .. }
 8104        )
 8105    }
 8106
 8107    pub fn edit_predictions_enabled_at_cursor(&self, cx: &App) -> bool {
 8108        let cursor = self.selections.newest_anchor().head();
 8109        if let Some((buffer, cursor_position)) =
 8110            self.buffer.read(cx).text_anchor_for_position(cursor, cx)
 8111        {
 8112            self.edit_predictions_enabled_in_buffer(&buffer, cursor_position, cx)
 8113        } else {
 8114            false
 8115        }
 8116    }
 8117
 8118    pub fn supports_minimap(&self, cx: &App) -> bool {
 8119        !self.minimap_visibility.disabled() && self.buffer_kind(cx) == ItemBufferKind::Singleton
 8120    }
 8121
 8122    fn edit_predictions_enabled_in_buffer(
 8123        &self,
 8124        buffer: &Entity<Buffer>,
 8125        buffer_position: language::Anchor,
 8126        cx: &App,
 8127    ) -> bool {
 8128        maybe!({
 8129            if self.read_only(cx) || self.leader_id.is_some() {
 8130                return Some(false);
 8131            }
 8132            let provider = self.edit_prediction_provider()?;
 8133            if !provider.is_enabled(buffer, buffer_position, cx) {
 8134                return Some(false);
 8135            }
 8136            let buffer = buffer.read(cx);
 8137            let Some(file) = buffer.file() else {
 8138                return Some(true);
 8139            };
 8140            let settings = all_language_settings(Some(file), cx);
 8141            Some(settings.edit_predictions_enabled_for_file(file, cx))
 8142        })
 8143        .unwrap_or(false)
 8144    }
 8145
 8146    pub fn show_edit_prediction(
 8147        &mut self,
 8148        _: &ShowEditPrediction,
 8149        window: &mut Window,
 8150        cx: &mut Context<Self>,
 8151    ) {
 8152        if !self.has_active_edit_prediction() {
 8153            self.refresh_edit_prediction(false, true, window, cx);
 8154            return;
 8155        }
 8156
 8157        self.update_visible_edit_prediction(window, cx);
 8158    }
 8159
 8160    pub fn display_cursor_names(
 8161        &mut self,
 8162        _: &DisplayCursorNames,
 8163        window: &mut Window,
 8164        cx: &mut Context<Self>,
 8165    ) {
 8166        self.show_cursor_names(window, cx);
 8167    }
 8168
 8169    fn show_cursor_names(&mut self, window: &mut Window, cx: &mut Context<Self>) {
 8170        self.show_cursor_names = true;
 8171        cx.notify();
 8172        cx.spawn_in(window, async move |this, cx| {
 8173            cx.background_executor().timer(CURSORS_VISIBLE_FOR).await;
 8174            this.update(cx, |this, cx| {
 8175                this.show_cursor_names = false;
 8176                cx.notify()
 8177            })
 8178            .ok()
 8179        })
 8180        .detach();
 8181    }
 8182
 8183    pub fn accept_partial_edit_prediction(
 8184        &mut self,
 8185        granularity: EditPredictionGranularity,
 8186        window: &mut Window,
 8187        cx: &mut Context<Self>,
 8188    ) {
 8189        if self.show_edit_predictions_in_menu() {
 8190            self.hide_context_menu(window, cx);
 8191        }
 8192
 8193        let Some(active_edit_prediction) = self.active_edit_prediction.as_ref() else {
 8194            return;
 8195        };
 8196
 8197        if !matches!(granularity, EditPredictionGranularity::Full) && self.selections.count() != 1 {
 8198            return;
 8199        }
 8200
 8201        match &active_edit_prediction.completion {
 8202            EditPrediction::MoveWithin { target, .. } => {
 8203                let target = *target;
 8204
 8205                if matches!(granularity, EditPredictionGranularity::Full) {
 8206                    if let Some(position_map) = &self.last_position_map {
 8207                        let target_row = target.to_display_point(&position_map.snapshot).row();
 8208                        let is_visible = position_map.visible_row_range.contains(&target_row);
 8209
 8210                        if is_visible || !self.edit_prediction_requires_modifier() {
 8211                            self.unfold_ranges(&[target..target], true, false, cx);
 8212                            self.change_selections(
 8213                                SelectionEffects::scroll(Autoscroll::newest()),
 8214                                window,
 8215                                cx,
 8216                                |selections| {
 8217                                    selections.select_anchor_ranges([target..target]);
 8218                                },
 8219                            );
 8220                            self.clear_row_highlights::<EditPredictionPreview>();
 8221                            self.edit_prediction_preview
 8222                                .set_previous_scroll_position(None);
 8223                        } else {
 8224                            // Highlight and request scroll
 8225                            self.edit_prediction_preview
 8226                                .set_previous_scroll_position(Some(
 8227                                    position_map.snapshot.scroll_anchor,
 8228                                ));
 8229                            self.highlight_rows::<EditPredictionPreview>(
 8230                                target..target,
 8231                                cx.theme().colors().editor_highlighted_line_background,
 8232                                RowHighlightOptions {
 8233                                    autoscroll: true,
 8234                                    ..Default::default()
 8235                                },
 8236                                cx,
 8237                            );
 8238                            self.request_autoscroll(Autoscroll::fit(), cx);
 8239                        }
 8240                    }
 8241                } else {
 8242                    self.change_selections(
 8243                        SelectionEffects::scroll(Autoscroll::newest()),
 8244                        window,
 8245                        cx,
 8246                        |selections| {
 8247                            selections.select_anchor_ranges([target..target]);
 8248                        },
 8249                    );
 8250                }
 8251            }
 8252            EditPrediction::MoveOutside { snapshot, target } => {
 8253                if let Some(workspace) = self.workspace() {
 8254                    Self::open_editor_at_anchor(snapshot, *target, &workspace, window, cx)
 8255                        .detach_and_log_err(cx);
 8256                }
 8257            }
 8258            EditPrediction::Edit {
 8259                edits,
 8260                cursor_position,
 8261                ..
 8262            } => {
 8263                self.report_edit_prediction_event(
 8264                    active_edit_prediction.completion_id.clone(),
 8265                    true,
 8266                    cx,
 8267                );
 8268
 8269                match granularity {
 8270                    EditPredictionGranularity::Full => {
 8271                        let transaction_id_prev = self.buffer.read(cx).last_transaction_id(cx);
 8272
 8273                        // Compute fallback cursor position BEFORE applying the edit,
 8274                        // so the anchor tracks through the edit correctly
 8275                        let fallback_cursor_target = {
 8276                            let snapshot = self.buffer.read(cx).snapshot(cx);
 8277                            edits.last().unwrap().0.end.bias_right(&snapshot)
 8278                        };
 8279
 8280                        self.buffer.update(cx, |buffer, cx| {
 8281                            buffer.edit(edits.iter().cloned(), None, cx)
 8282                        });
 8283
 8284                        if let Some(provider) = self.edit_prediction_provider() {
 8285                            provider.accept(cx);
 8286                        }
 8287
 8288                        // Resolve cursor position after the edit is applied
 8289                        let cursor_target = if let Some((anchor, offset)) = cursor_position {
 8290                            // The anchor tracks through the edit, then we add the offset
 8291                            let snapshot = self.buffer.read(cx).snapshot(cx);
 8292                            let base_offset = anchor.to_offset(&snapshot).0;
 8293                            let target_offset =
 8294                                MultiBufferOffset((base_offset + offset).min(snapshot.len().0));
 8295                            snapshot.anchor_after(target_offset)
 8296                        } else {
 8297                            fallback_cursor_target
 8298                        };
 8299
 8300                        self.change_selections(SelectionEffects::no_scroll(), window, cx, |s| {
 8301                            s.select_anchor_ranges([cursor_target..cursor_target]);
 8302                        });
 8303
 8304                        let selections = self.selections.disjoint_anchors_arc();
 8305                        if let Some(transaction_id_now) =
 8306                            self.buffer.read(cx).last_transaction_id(cx)
 8307                        {
 8308                            if transaction_id_prev != Some(transaction_id_now) {
 8309                                self.selection_history
 8310                                    .insert_transaction(transaction_id_now, selections);
 8311                            }
 8312                        }
 8313
 8314                        self.update_visible_edit_prediction(window, cx);
 8315                        if self.active_edit_prediction.is_none() {
 8316                            self.refresh_edit_prediction(true, true, window, cx);
 8317                        }
 8318                        cx.notify();
 8319                    }
 8320                    _ => {
 8321                        let snapshot = self.buffer.read(cx).snapshot(cx);
 8322                        let cursor_offset = self
 8323                            .selections
 8324                            .newest::<MultiBufferOffset>(&self.display_snapshot(cx))
 8325                            .head();
 8326
 8327                        let insertion = edits.iter().find_map(|(range, text)| {
 8328                            let range = range.to_offset(&snapshot);
 8329                            if range.is_empty() && range.start == cursor_offset {
 8330                                Some(text)
 8331                            } else {
 8332                                None
 8333                            }
 8334                        });
 8335
 8336                        if let Some(text) = insertion {
 8337                            let text_to_insert = match granularity {
 8338                                EditPredictionGranularity::Word => {
 8339                                    let mut partial = text
 8340                                        .chars()
 8341                                        .by_ref()
 8342                                        .take_while(|c| c.is_alphabetic())
 8343                                        .collect::<String>();
 8344                                    if partial.is_empty() {
 8345                                        partial = text
 8346                                            .chars()
 8347                                            .by_ref()
 8348                                            .take_while(|c| c.is_whitespace() || !c.is_alphabetic())
 8349                                            .collect::<String>();
 8350                                    }
 8351                                    partial
 8352                                }
 8353                                EditPredictionGranularity::Line => {
 8354                                    if let Some(line) = text.split_inclusive('\n').next() {
 8355                                        line.to_string()
 8356                                    } else {
 8357                                        text.to_string()
 8358                                    }
 8359                                }
 8360                                EditPredictionGranularity::Full => unreachable!(),
 8361                            };
 8362
 8363                            cx.emit(EditorEvent::InputHandled {
 8364                                utf16_range_to_replace: None,
 8365                                text: text_to_insert.clone().into(),
 8366                            });
 8367
 8368                            self.replace_selections(&text_to_insert, None, window, cx, false);
 8369                            self.refresh_edit_prediction(true, true, window, cx);
 8370                            cx.notify();
 8371                        } else {
 8372                            self.accept_partial_edit_prediction(
 8373                                EditPredictionGranularity::Full,
 8374                                window,
 8375                                cx,
 8376                            );
 8377                        }
 8378                    }
 8379                }
 8380            }
 8381        }
 8382    }
 8383
 8384    pub fn accept_next_word_edit_prediction(
 8385        &mut self,
 8386        _: &AcceptNextWordEditPrediction,
 8387        window: &mut Window,
 8388        cx: &mut Context<Self>,
 8389    ) {
 8390        self.accept_partial_edit_prediction(EditPredictionGranularity::Word, window, cx);
 8391    }
 8392
 8393    pub fn accept_next_line_edit_prediction(
 8394        &mut self,
 8395        _: &AcceptNextLineEditPrediction,
 8396        window: &mut Window,
 8397        cx: &mut Context<Self>,
 8398    ) {
 8399        self.accept_partial_edit_prediction(EditPredictionGranularity::Line, window, cx);
 8400    }
 8401
 8402    pub fn accept_edit_prediction(
 8403        &mut self,
 8404        _: &AcceptEditPrediction,
 8405        window: &mut Window,
 8406        cx: &mut Context<Self>,
 8407    ) {
 8408        self.accept_partial_edit_prediction(EditPredictionGranularity::Full, window, cx);
 8409    }
 8410
 8411    fn discard_edit_prediction(
 8412        &mut self,
 8413        reason: EditPredictionDiscardReason,
 8414        cx: &mut Context<Self>,
 8415    ) -> bool {
 8416        if reason == EditPredictionDiscardReason::Rejected {
 8417            let completion_id = self
 8418                .active_edit_prediction
 8419                .as_ref()
 8420                .and_then(|active_completion| active_completion.completion_id.clone());
 8421
 8422            self.report_edit_prediction_event(completion_id, false, cx);
 8423        }
 8424
 8425        if let Some(provider) = self.edit_prediction_provider() {
 8426            provider.discard(reason, cx);
 8427        }
 8428
 8429        self.take_active_edit_prediction(reason == EditPredictionDiscardReason::Ignored, cx)
 8430    }
 8431
 8432    fn report_edit_prediction_event(&self, id: Option<SharedString>, accepted: bool, cx: &App) {
 8433        let Some(provider) = self.edit_prediction_provider() else {
 8434            return;
 8435        };
 8436
 8437        let Some((_, buffer, _)) = self
 8438            .buffer
 8439            .read(cx)
 8440            .excerpt_containing(self.selections.newest_anchor().head(), cx)
 8441        else {
 8442            return;
 8443        };
 8444
 8445        let extension = buffer
 8446            .read(cx)
 8447            .file()
 8448            .and_then(|file| Some(file.path().extension()?.to_string()));
 8449
 8450        let event_type = match accepted {
 8451            true => "Edit Prediction Accepted",
 8452            false => "Edit Prediction Discarded",
 8453        };
 8454        telemetry::event!(
 8455            event_type,
 8456            provider = provider.name(),
 8457            prediction_id = id,
 8458            suggestion_accepted = accepted,
 8459            file_extension = extension,
 8460        );
 8461    }
 8462
 8463    fn open_editor_at_anchor(
 8464        snapshot: &language::BufferSnapshot,
 8465        target: language::Anchor,
 8466        workspace: &Entity<Workspace>,
 8467        window: &mut Window,
 8468        cx: &mut App,
 8469    ) -> Task<Result<()>> {
 8470        workspace.update(cx, |workspace, cx| {
 8471            let path = snapshot.file().map(|file| file.full_path(cx));
 8472            let Some(path) =
 8473                path.and_then(|path| workspace.project().read(cx).find_project_path(path, cx))
 8474            else {
 8475                return Task::ready(Err(anyhow::anyhow!("Project path not found")));
 8476            };
 8477            let target = text::ToPoint::to_point(&target, snapshot);
 8478            let item = workspace.open_path(path, None, true, window, cx);
 8479            window.spawn(cx, async move |cx| {
 8480                let Some(editor) = item.await?.downcast::<Editor>() else {
 8481                    return Ok(());
 8482                };
 8483                editor
 8484                    .update_in(cx, |editor, window, cx| {
 8485                        editor.go_to_singleton_buffer_point(target, window, cx);
 8486                    })
 8487                    .ok();
 8488                anyhow::Ok(())
 8489            })
 8490        })
 8491    }
 8492
 8493    pub fn has_active_edit_prediction(&self) -> bool {
 8494        self.active_edit_prediction.is_some()
 8495    }
 8496
 8497    fn take_active_edit_prediction(
 8498        &mut self,
 8499        preserve_stale_in_menu: bool,
 8500        cx: &mut Context<Self>,
 8501    ) -> bool {
 8502        let Some(active_edit_prediction) = self.active_edit_prediction.take() else {
 8503            if !preserve_stale_in_menu {
 8504                self.stale_edit_prediction_in_menu = None;
 8505            }
 8506            return false;
 8507        };
 8508
 8509        self.splice_inlays(&active_edit_prediction.inlay_ids, Default::default(), cx);
 8510        self.clear_highlights(HighlightKey::EditPredictionHighlight, cx);
 8511        self.stale_edit_prediction_in_menu =
 8512            preserve_stale_in_menu.then_some(active_edit_prediction);
 8513        true
 8514    }
 8515
 8516    /// Returns true when we're displaying the edit prediction popover below the cursor
 8517    /// like we are not previewing and the LSP autocomplete menu is visible
 8518    /// or we are in `when_holding_modifier` mode.
 8519    pub fn edit_prediction_visible_in_cursor_popover(&self, has_completion: bool) -> bool {
 8520        if self.edit_prediction_preview_is_active()
 8521            || !self.show_edit_predictions_in_menu()
 8522            || !self.edit_predictions_enabled()
 8523        {
 8524            return false;
 8525        }
 8526
 8527        if self.has_visible_completions_menu() {
 8528            return true;
 8529        }
 8530
 8531        has_completion && self.edit_prediction_requires_modifier()
 8532    }
 8533
 8534    fn handle_modifiers_changed(
 8535        &mut self,
 8536        modifiers: Modifiers,
 8537        position_map: &PositionMap,
 8538        window: &mut Window,
 8539        cx: &mut Context<Self>,
 8540    ) {
 8541        self.update_edit_prediction_settings(cx);
 8542
 8543        // Ensure that the edit prediction preview is updated, even when not
 8544        // enabled, if there's an active edit prediction preview.
 8545        if self.show_edit_predictions_in_menu()
 8546            || self.edit_prediction_requires_modifier()
 8547            || matches!(
 8548                self.edit_prediction_preview,
 8549                EditPredictionPreview::Active { .. }
 8550            )
 8551        {
 8552            self.update_edit_prediction_preview(&modifiers, window, cx);
 8553        }
 8554
 8555        self.update_selection_mode(&modifiers, position_map, window, cx);
 8556
 8557        let mouse_position = window.mouse_position();
 8558        if !position_map.text_hitbox.is_hovered(window) {
 8559            return;
 8560        }
 8561
 8562        self.update_hovered_link(
 8563            position_map.point_for_position(mouse_position),
 8564            Some(mouse_position),
 8565            &position_map.snapshot,
 8566            modifiers,
 8567            window,
 8568            cx,
 8569        )
 8570    }
 8571
 8572    fn is_cmd_or_ctrl_pressed(modifiers: &Modifiers, cx: &mut Context<Self>) -> bool {
 8573        match EditorSettings::get_global(cx).multi_cursor_modifier {
 8574            MultiCursorModifier::Alt => modifiers.secondary(),
 8575            MultiCursorModifier::CmdOrCtrl => modifiers.alt,
 8576        }
 8577    }
 8578
 8579    fn is_alt_pressed(modifiers: &Modifiers, cx: &mut Context<Self>) -> bool {
 8580        match EditorSettings::get_global(cx).multi_cursor_modifier {
 8581            MultiCursorModifier::Alt => modifiers.alt,
 8582            MultiCursorModifier::CmdOrCtrl => modifiers.secondary(),
 8583        }
 8584    }
 8585
 8586    fn columnar_selection_mode(
 8587        modifiers: &Modifiers,
 8588        cx: &mut Context<Self>,
 8589    ) -> Option<ColumnarMode> {
 8590        if modifiers.shift && modifiers.number_of_modifiers() == 2 {
 8591            if Self::is_cmd_or_ctrl_pressed(modifiers, cx) {
 8592                Some(ColumnarMode::FromMouse)
 8593            } else if Self::is_alt_pressed(modifiers, cx) {
 8594                Some(ColumnarMode::FromSelection)
 8595            } else {
 8596                None
 8597            }
 8598        } else {
 8599            None
 8600        }
 8601    }
 8602
 8603    fn update_selection_mode(
 8604        &mut self,
 8605        modifiers: &Modifiers,
 8606        position_map: &PositionMap,
 8607        window: &mut Window,
 8608        cx: &mut Context<Self>,
 8609    ) {
 8610        let Some(mode) = Self::columnar_selection_mode(modifiers, cx) else {
 8611            return;
 8612        };
 8613        if self.selections.pending_anchor().is_none() {
 8614            return;
 8615        }
 8616
 8617        let mouse_position = window.mouse_position();
 8618        let point_for_position = position_map.point_for_position(mouse_position);
 8619        let position = point_for_position.previous_valid;
 8620
 8621        self.select(
 8622            SelectPhase::BeginColumnar {
 8623                position,
 8624                reset: false,
 8625                mode,
 8626                goal_column: point_for_position.exact_unclipped.column(),
 8627            },
 8628            window,
 8629            cx,
 8630        );
 8631    }
 8632
 8633    fn update_edit_prediction_preview(
 8634        &mut self,
 8635        modifiers: &Modifiers,
 8636        window: &mut Window,
 8637        cx: &mut Context<Self>,
 8638    ) {
 8639        let modifiers_held = self.edit_prediction_preview_modifiers_held(modifiers, window, cx);
 8640
 8641        if modifiers_held {
 8642            if matches!(
 8643                self.edit_prediction_preview,
 8644                EditPredictionPreview::Inactive { .. }
 8645            ) {
 8646                self.edit_prediction_preview = EditPredictionPreview::Active {
 8647                    previous_scroll_position: None,
 8648                    since: Instant::now(),
 8649                };
 8650
 8651                self.update_visible_edit_prediction(window, cx);
 8652                cx.notify();
 8653            }
 8654        } else if let EditPredictionPreview::Active {
 8655            previous_scroll_position,
 8656            since,
 8657        } = self.edit_prediction_preview
 8658        {
 8659            if let (Some(previous_scroll_position), Some(position_map)) =
 8660                (previous_scroll_position, self.last_position_map.as_ref())
 8661            {
 8662                self.set_scroll_position(
 8663                    previous_scroll_position
 8664                        .scroll_position(&position_map.snapshot.display_snapshot),
 8665                    window,
 8666                    cx,
 8667                );
 8668            }
 8669
 8670            self.edit_prediction_preview = EditPredictionPreview::Inactive {
 8671                released_too_fast: since.elapsed() < Duration::from_millis(200),
 8672            };
 8673            self.clear_row_highlights::<EditPredictionPreview>();
 8674            self.update_visible_edit_prediction(window, cx);
 8675            cx.notify();
 8676        }
 8677    }
 8678
 8679    fn update_visible_edit_prediction(
 8680        &mut self,
 8681        _window: &mut Window,
 8682        cx: &mut Context<Self>,
 8683    ) -> Option<()> {
 8684        if self.ime_transaction.is_some() {
 8685            self.discard_edit_prediction(EditPredictionDiscardReason::Ignored, cx);
 8686            return None;
 8687        }
 8688
 8689        let selection = self.selections.newest_anchor();
 8690        let cursor = selection.head();
 8691        let multibuffer = self.buffer.read(cx).snapshot(cx);
 8692
 8693        // Check project-level disable_ai setting for the current buffer
 8694        if let Some((buffer, _)) = self.buffer.read(cx).text_anchor_for_position(cursor, cx) {
 8695            if DisableAiSettings::is_ai_disabled_for_buffer(Some(&buffer), cx) {
 8696                return None;
 8697            }
 8698        }
 8699        let offset_selection = selection.map(|endpoint| endpoint.to_offset(&multibuffer));
 8700        let excerpt_id = cursor.excerpt_id;
 8701
 8702        let show_in_menu = self.show_edit_predictions_in_menu();
 8703        let completions_menu_has_precedence = !show_in_menu
 8704            && (self.context_menu.borrow().is_some()
 8705                || (!self.completion_tasks.is_empty() && !self.has_active_edit_prediction()));
 8706
 8707        if completions_menu_has_precedence
 8708            || !offset_selection.is_empty()
 8709            || self
 8710                .active_edit_prediction
 8711                .as_ref()
 8712                .is_some_and(|completion| {
 8713                    let Some(invalidation_range) = completion.invalidation_range.as_ref() else {
 8714                        return false;
 8715                    };
 8716                    let invalidation_range = invalidation_range.to_offset(&multibuffer);
 8717                    let invalidation_range = invalidation_range.start..=invalidation_range.end;
 8718                    !invalidation_range.contains(&offset_selection.head())
 8719                })
 8720        {
 8721            self.discard_edit_prediction(EditPredictionDiscardReason::Ignored, cx);
 8722            return None;
 8723        }
 8724
 8725        self.take_active_edit_prediction(true, cx);
 8726        let Some(provider) = self.edit_prediction_provider() else {
 8727            self.edit_prediction_settings = EditPredictionSettings::Disabled;
 8728            return None;
 8729        };
 8730
 8731        let (buffer, cursor_buffer_position) =
 8732            self.buffer.read(cx).text_anchor_for_position(cursor, cx)?;
 8733
 8734        self.edit_prediction_settings =
 8735            self.edit_prediction_settings_at_position(&buffer, cursor_buffer_position, cx);
 8736
 8737        self.in_leading_whitespace = multibuffer.is_line_whitespace_upto(cursor);
 8738
 8739        if self.in_leading_whitespace {
 8740            let cursor_point = cursor.to_point(&multibuffer);
 8741            let mut suggested_indent = None;
 8742            multibuffer.suggested_indents_callback(
 8743                cursor_point.row..cursor_point.row + 1,
 8744                &mut |_, indent| {
 8745                    suggested_indent = Some(indent);
 8746                    ControlFlow::Break(())
 8747                },
 8748                cx,
 8749            );
 8750
 8751            if let Some(indent) = suggested_indent
 8752                && indent.len == cursor_point.column
 8753            {
 8754                self.in_leading_whitespace = false;
 8755            }
 8756        }
 8757
 8758        let edit_prediction = provider.suggest(&buffer, cursor_buffer_position, cx)?;
 8759
 8760        let (completion_id, edits, predicted_cursor_position, edit_preview) = match edit_prediction
 8761        {
 8762            edit_prediction_types::EditPrediction::Local {
 8763                id,
 8764                edits,
 8765                cursor_position,
 8766                edit_preview,
 8767            } => (id, edits, cursor_position, edit_preview),
 8768            edit_prediction_types::EditPrediction::Jump {
 8769                id,
 8770                snapshot,
 8771                target,
 8772            } => {
 8773                if let Some(provider) = &self.edit_prediction_provider {
 8774                    provider.provider.did_show(SuggestionDisplayType::Jump, cx);
 8775                }
 8776                self.stale_edit_prediction_in_menu = None;
 8777                self.active_edit_prediction = Some(EditPredictionState {
 8778                    inlay_ids: vec![],
 8779                    completion: EditPrediction::MoveOutside { snapshot, target },
 8780                    completion_id: id,
 8781                    invalidation_range: None,
 8782                });
 8783                cx.notify();
 8784                return Some(());
 8785            }
 8786        };
 8787
 8788        let edits = edits
 8789            .into_iter()
 8790            .flat_map(|(range, new_text)| {
 8791                Some((
 8792                    multibuffer.anchor_range_in_excerpt(excerpt_id, range)?,
 8793                    new_text,
 8794                ))
 8795            })
 8796            .collect::<Vec<_>>();
 8797        if edits.is_empty() {
 8798            return None;
 8799        }
 8800
 8801        let cursor_position = predicted_cursor_position.and_then(|predicted| {
 8802            let anchor = multibuffer.anchor_in_excerpt(excerpt_id, predicted.anchor)?;
 8803            Some((anchor, predicted.offset))
 8804        });
 8805
 8806        let first_edit_start = edits.first().unwrap().0.start;
 8807        let first_edit_start_point = first_edit_start.to_point(&multibuffer);
 8808        let edit_start_row = first_edit_start_point.row.saturating_sub(2);
 8809
 8810        let last_edit_end = edits.last().unwrap().0.end;
 8811        let last_edit_end_point = last_edit_end.to_point(&multibuffer);
 8812        let edit_end_row = cmp::min(multibuffer.max_point().row, last_edit_end_point.row + 2);
 8813
 8814        let cursor_row = cursor.to_point(&multibuffer).row;
 8815
 8816        let snapshot = multibuffer.buffer_for_excerpt(excerpt_id).cloned()?;
 8817
 8818        let mut inlay_ids = Vec::new();
 8819        let invalidation_row_range;
 8820        let move_invalidation_row_range = if cursor_row < edit_start_row {
 8821            Some(cursor_row..edit_end_row)
 8822        } else if cursor_row > edit_end_row {
 8823            Some(edit_start_row..cursor_row)
 8824        } else {
 8825            None
 8826        };
 8827        let supports_jump = self
 8828            .edit_prediction_provider
 8829            .as_ref()
 8830            .map(|provider| provider.provider.supports_jump_to_edit())
 8831            .unwrap_or(true);
 8832
 8833        let is_move = supports_jump
 8834            && (move_invalidation_row_range.is_some() || self.edit_predictions_hidden_for_vim_mode);
 8835        let completion = if is_move {
 8836            if let Some(provider) = &self.edit_prediction_provider {
 8837                provider.provider.did_show(SuggestionDisplayType::Jump, cx);
 8838            }
 8839            invalidation_row_range =
 8840                move_invalidation_row_range.unwrap_or(edit_start_row..edit_end_row);
 8841            let target = first_edit_start;
 8842            EditPrediction::MoveWithin { target, snapshot }
 8843        } else {
 8844            let show_completions_in_menu = self.has_visible_completions_menu();
 8845            let show_completions_in_buffer = !self.edit_prediction_visible_in_cursor_popover(true)
 8846                && !self.edit_predictions_hidden_for_vim_mode;
 8847
 8848            let display_mode = if all_edits_insertions_or_deletions(&edits, &multibuffer) {
 8849                if provider.show_tab_accept_marker() {
 8850                    EditDisplayMode::TabAccept
 8851                } else {
 8852                    EditDisplayMode::Inline
 8853                }
 8854            } else {
 8855                EditDisplayMode::DiffPopover
 8856            };
 8857
 8858            let report_shown = match display_mode {
 8859                EditDisplayMode::DiffPopover | EditDisplayMode::Inline => {
 8860                    show_completions_in_buffer || show_completions_in_menu
 8861                }
 8862                EditDisplayMode::TabAccept => {
 8863                    show_completions_in_menu || self.edit_prediction_preview_is_active()
 8864                }
 8865            };
 8866
 8867            if report_shown && let Some(provider) = &self.edit_prediction_provider {
 8868                let suggestion_display_type = match display_mode {
 8869                    EditDisplayMode::DiffPopover => SuggestionDisplayType::DiffPopover,
 8870                    EditDisplayMode::Inline | EditDisplayMode::TabAccept => {
 8871                        SuggestionDisplayType::GhostText
 8872                    }
 8873                };
 8874                provider.provider.did_show(suggestion_display_type, cx);
 8875            }
 8876
 8877            if show_completions_in_buffer {
 8878                if edits
 8879                    .iter()
 8880                    .all(|(range, _)| range.to_offset(&multibuffer).is_empty())
 8881                {
 8882                    let mut inlays = Vec::new();
 8883                    for (range, new_text) in &edits {
 8884                        let inlay = Inlay::edit_prediction(
 8885                            post_inc(&mut self.next_inlay_id),
 8886                            range.start,
 8887                            new_text.as_ref(),
 8888                        );
 8889                        inlay_ids.push(inlay.id);
 8890                        inlays.push(inlay);
 8891                    }
 8892
 8893                    self.splice_inlays(&[], inlays, cx);
 8894                } else {
 8895                    let background_color = cx.theme().status().deleted_background;
 8896                    self.highlight_text(
 8897                        HighlightKey::EditPredictionHighlight,
 8898                        edits.iter().map(|(range, _)| range.clone()).collect(),
 8899                        HighlightStyle {
 8900                            background_color: Some(background_color),
 8901                            ..Default::default()
 8902                        },
 8903                        cx,
 8904                    );
 8905                }
 8906            }
 8907
 8908            invalidation_row_range = edit_start_row..edit_end_row;
 8909
 8910            EditPrediction::Edit {
 8911                edits,
 8912                cursor_position,
 8913                edit_preview,
 8914                display_mode,
 8915                snapshot,
 8916            }
 8917        };
 8918
 8919        let invalidation_range = multibuffer
 8920            .anchor_before(Point::new(invalidation_row_range.start, 0))
 8921            ..multibuffer.anchor_after(Point::new(
 8922                invalidation_row_range.end,
 8923                multibuffer.line_len(MultiBufferRow(invalidation_row_range.end)),
 8924            ));
 8925
 8926        self.stale_edit_prediction_in_menu = None;
 8927        self.active_edit_prediction = Some(EditPredictionState {
 8928            inlay_ids,
 8929            completion,
 8930            completion_id,
 8931            invalidation_range: Some(invalidation_range),
 8932        });
 8933
 8934        cx.notify();
 8935
 8936        Some(())
 8937    }
 8938
 8939    pub fn edit_prediction_provider(&self) -> Option<Arc<dyn EditPredictionDelegateHandle>> {
 8940        Some(self.edit_prediction_provider.as_ref()?.provider.clone())
 8941    }
 8942
 8943    /// Get all display points of breakpoints that will be rendered within editor
 8944    ///
 8945    /// This function is used to handle overlaps between breakpoints and Code action/runner symbol.
 8946    /// It's also used to set the color of line numbers with breakpoints to the breakpoint color.
 8947    /// TODO debugger: Use this function to color toggle symbols that house nested breakpoints
 8948    fn active_breakpoints(
 8949        &self,
 8950        range: Range<DisplayRow>,
 8951        window: &mut Window,
 8952        cx: &mut Context<Self>,
 8953    ) -> HashMap<DisplayRow, (Anchor, Breakpoint, Option<BreakpointSessionState>)> {
 8954        let mut breakpoint_display_points = HashMap::default();
 8955
 8956        let Some(breakpoint_store) = self.breakpoint_store.clone() else {
 8957            return breakpoint_display_points;
 8958        };
 8959
 8960        let snapshot = self.snapshot(window, cx);
 8961
 8962        let multi_buffer_snapshot = snapshot.buffer_snapshot();
 8963        let Some(project) = self.project() else {
 8964            return breakpoint_display_points;
 8965        };
 8966
 8967        let range = snapshot.display_point_to_point(DisplayPoint::new(range.start, 0), Bias::Left)
 8968            ..snapshot.display_point_to_point(DisplayPoint::new(range.end, 0), Bias::Right);
 8969
 8970        for (buffer_snapshot, range, excerpt_id) in
 8971            multi_buffer_snapshot.range_to_buffer_ranges(range.start..=range.end)
 8972        {
 8973            let Some(buffer) = project
 8974                .read(cx)
 8975                .buffer_for_id(buffer_snapshot.remote_id(), cx)
 8976            else {
 8977                continue;
 8978            };
 8979            let breakpoints = breakpoint_store.read(cx).breakpoints(
 8980                &buffer,
 8981                Some(
 8982                    buffer_snapshot.anchor_before(range.start)
 8983                        ..buffer_snapshot.anchor_after(range.end),
 8984                ),
 8985                buffer_snapshot,
 8986                cx,
 8987            );
 8988            for (breakpoint, state) in breakpoints {
 8989                let multi_buffer_anchor = Anchor::in_buffer(excerpt_id, breakpoint.position);
 8990                let position = multi_buffer_anchor
 8991                    .to_point(&multi_buffer_snapshot)
 8992                    .to_display_point(&snapshot);
 8993
 8994                breakpoint_display_points.insert(
 8995                    position.row(),
 8996                    (multi_buffer_anchor, breakpoint.bp.clone(), state),
 8997                );
 8998            }
 8999        }
 9000
 9001        breakpoint_display_points
 9002    }
 9003
 9004    fn breakpoint_context_menu(
 9005        &self,
 9006        anchor: Anchor,
 9007        window: &mut Window,
 9008        cx: &mut Context<Self>,
 9009    ) -> Entity<ui::ContextMenu> {
 9010        let weak_editor = cx.weak_entity();
 9011        let focus_handle = self.focus_handle(cx);
 9012
 9013        let row = self
 9014            .buffer
 9015            .read(cx)
 9016            .snapshot(cx)
 9017            .summary_for_anchor::<Point>(&anchor)
 9018            .row;
 9019
 9020        let breakpoint = self
 9021            .breakpoint_at_row(row, window, cx)
 9022            .map(|(anchor, bp)| (anchor, Arc::from(bp)));
 9023
 9024        let log_breakpoint_msg = if breakpoint.as_ref().is_some_and(|bp| bp.1.message.is_some()) {
 9025            "Edit Log Breakpoint"
 9026        } else {
 9027            "Set Log Breakpoint"
 9028        };
 9029
 9030        let condition_breakpoint_msg = if breakpoint
 9031            .as_ref()
 9032            .is_some_and(|bp| bp.1.condition.is_some())
 9033        {
 9034            "Edit Condition Breakpoint"
 9035        } else {
 9036            "Set Condition Breakpoint"
 9037        };
 9038
 9039        let hit_condition_breakpoint_msg = if breakpoint
 9040            .as_ref()
 9041            .is_some_and(|bp| bp.1.hit_condition.is_some())
 9042        {
 9043            "Edit Hit Condition Breakpoint"
 9044        } else {
 9045            "Set Hit Condition Breakpoint"
 9046        };
 9047
 9048        let set_breakpoint_msg = if breakpoint.as_ref().is_some() {
 9049            "Unset Breakpoint"
 9050        } else {
 9051            "Set Breakpoint"
 9052        };
 9053
 9054        let run_to_cursor = window.is_action_available(&RunToCursor, cx);
 9055
 9056        let toggle_state_msg = breakpoint.as_ref().map_or(None, |bp| match bp.1.state {
 9057            BreakpointState::Enabled => Some("Disable"),
 9058            BreakpointState::Disabled => Some("Enable"),
 9059        });
 9060
 9061        let (anchor, breakpoint) =
 9062            breakpoint.unwrap_or_else(|| (anchor, Arc::new(Breakpoint::new_standard())));
 9063
 9064        ui::ContextMenu::build(window, cx, |menu, _, _cx| {
 9065            menu.on_blur_subscription(Subscription::new(|| {}))
 9066                .context(focus_handle)
 9067                .when(run_to_cursor, |this| {
 9068                    let weak_editor = weak_editor.clone();
 9069                    this.entry("Run to Cursor", None, move |window, cx| {
 9070                        weak_editor
 9071                            .update(cx, |editor, cx| {
 9072                                editor.change_selections(
 9073                                    SelectionEffects::no_scroll(),
 9074                                    window,
 9075                                    cx,
 9076                                    |s| s.select_ranges([Point::new(row, 0)..Point::new(row, 0)]),
 9077                                );
 9078                            })
 9079                            .ok();
 9080
 9081                        window.dispatch_action(Box::new(RunToCursor), cx);
 9082                    })
 9083                    .separator()
 9084                })
 9085                .when_some(toggle_state_msg, |this, msg| {
 9086                    this.entry(msg, None, {
 9087                        let weak_editor = weak_editor.clone();
 9088                        let breakpoint = breakpoint.clone();
 9089                        move |_window, cx| {
 9090                            weak_editor
 9091                                .update(cx, |this, cx| {
 9092                                    this.edit_breakpoint_at_anchor(
 9093                                        anchor,
 9094                                        breakpoint.as_ref().clone(),
 9095                                        BreakpointEditAction::InvertState,
 9096                                        cx,
 9097                                    );
 9098                                })
 9099                                .log_err();
 9100                        }
 9101                    })
 9102                })
 9103                .entry(set_breakpoint_msg, None, {
 9104                    let weak_editor = weak_editor.clone();
 9105                    let breakpoint = breakpoint.clone();
 9106                    move |_window, cx| {
 9107                        weak_editor
 9108                            .update(cx, |this, cx| {
 9109                                this.edit_breakpoint_at_anchor(
 9110                                    anchor,
 9111                                    breakpoint.as_ref().clone(),
 9112                                    BreakpointEditAction::Toggle,
 9113                                    cx,
 9114                                );
 9115                            })
 9116                            .log_err();
 9117                    }
 9118                })
 9119                .entry(log_breakpoint_msg, None, {
 9120                    let breakpoint = breakpoint.clone();
 9121                    let weak_editor = weak_editor.clone();
 9122                    move |window, cx| {
 9123                        weak_editor
 9124                            .update(cx, |this, cx| {
 9125                                this.add_edit_breakpoint_block(
 9126                                    anchor,
 9127                                    breakpoint.as_ref(),
 9128                                    BreakpointPromptEditAction::Log,
 9129                                    window,
 9130                                    cx,
 9131                                );
 9132                            })
 9133                            .log_err();
 9134                    }
 9135                })
 9136                .entry(condition_breakpoint_msg, None, {
 9137                    let breakpoint = breakpoint.clone();
 9138                    let weak_editor = weak_editor.clone();
 9139                    move |window, cx| {
 9140                        weak_editor
 9141                            .update(cx, |this, cx| {
 9142                                this.add_edit_breakpoint_block(
 9143                                    anchor,
 9144                                    breakpoint.as_ref(),
 9145                                    BreakpointPromptEditAction::Condition,
 9146                                    window,
 9147                                    cx,
 9148                                );
 9149                            })
 9150                            .log_err();
 9151                    }
 9152                })
 9153                .entry(hit_condition_breakpoint_msg, None, move |window, cx| {
 9154                    weak_editor
 9155                        .update(cx, |this, cx| {
 9156                            this.add_edit_breakpoint_block(
 9157                                anchor,
 9158                                breakpoint.as_ref(),
 9159                                BreakpointPromptEditAction::HitCondition,
 9160                                window,
 9161                                cx,
 9162                            );
 9163                        })
 9164                        .log_err();
 9165                })
 9166        })
 9167    }
 9168
 9169    fn render_breakpoint(
 9170        &self,
 9171        position: Anchor,
 9172        row: DisplayRow,
 9173        breakpoint: &Breakpoint,
 9174        state: Option<BreakpointSessionState>,
 9175        cx: &mut Context<Self>,
 9176    ) -> IconButton {
 9177        let is_rejected = state.is_some_and(|s| !s.verified);
 9178        // Is it a breakpoint that shows up when hovering over gutter?
 9179        let (is_phantom, collides_with_existing) = self.gutter_breakpoint_indicator.0.map_or(
 9180            (false, false),
 9181            |PhantomBreakpointIndicator {
 9182                 is_active,
 9183                 display_row,
 9184                 collides_with_existing_breakpoint,
 9185             }| {
 9186                (
 9187                    is_active && display_row == row,
 9188                    collides_with_existing_breakpoint,
 9189                )
 9190            },
 9191        );
 9192
 9193        let (color, icon) = {
 9194            let icon = match (&breakpoint.message.is_some(), breakpoint.is_disabled()) {
 9195                (false, false) => ui::IconName::DebugBreakpoint,
 9196                (true, false) => ui::IconName::DebugLogBreakpoint,
 9197                (false, true) => ui::IconName::DebugDisabledBreakpoint,
 9198                (true, true) => ui::IconName::DebugDisabledLogBreakpoint,
 9199            };
 9200
 9201            let theme_colors = cx.theme().colors();
 9202
 9203            let color = if is_phantom {
 9204                if collides_with_existing {
 9205                    Color::Custom(
 9206                        theme_colors
 9207                            .debugger_accent
 9208                            .blend(theme_colors.text.opacity(0.6)),
 9209                    )
 9210                } else {
 9211                    Color::Hint
 9212                }
 9213            } else if is_rejected {
 9214                Color::Disabled
 9215            } else {
 9216                Color::Debugger
 9217            };
 9218
 9219            (color, icon)
 9220        };
 9221
 9222        let breakpoint = Arc::from(breakpoint.clone());
 9223
 9224        let alt_as_text = gpui::Keystroke {
 9225            modifiers: Modifiers::secondary_key(),
 9226            ..Default::default()
 9227        };
 9228        let primary_action_text = if breakpoint.is_disabled() {
 9229            "Enable breakpoint"
 9230        } else if is_phantom && !collides_with_existing {
 9231            "Set breakpoint"
 9232        } else {
 9233            "Unset breakpoint"
 9234        };
 9235        let focus_handle = self.focus_handle.clone();
 9236
 9237        let meta = if is_rejected {
 9238            SharedString::from("No executable code is associated with this line.")
 9239        } else if collides_with_existing && !breakpoint.is_disabled() {
 9240            SharedString::from(format!(
 9241                "{alt_as_text}-click to disable,\nright-click for more options."
 9242            ))
 9243        } else {
 9244            SharedString::from("Right-click for more options.")
 9245        };
 9246        IconButton::new(("breakpoint_indicator", row.0 as usize), icon)
 9247            .icon_size(IconSize::XSmall)
 9248            .size(ui::ButtonSize::None)
 9249            .when(is_rejected, |this| {
 9250                this.indicator(Indicator::icon(Icon::new(IconName::Warning)).color(Color::Warning))
 9251            })
 9252            .icon_color(color)
 9253            .style(ButtonStyle::Transparent)
 9254            .on_click(cx.listener({
 9255                move |editor, event: &ClickEvent, window, cx| {
 9256                    let edit_action = if event.modifiers().platform || breakpoint.is_disabled() {
 9257                        BreakpointEditAction::InvertState
 9258                    } else {
 9259                        BreakpointEditAction::Toggle
 9260                    };
 9261
 9262                    window.focus(&editor.focus_handle(cx), cx);
 9263                    editor.update_breakpoint_collision_on_toggle(row, &edit_action);
 9264                    editor.edit_breakpoint_at_anchor(
 9265                        position,
 9266                        breakpoint.as_ref().clone(),
 9267                        edit_action,
 9268                        cx,
 9269                    );
 9270                }
 9271            }))
 9272            .on_right_click(cx.listener(move |editor, event: &ClickEvent, window, cx| {
 9273                editor.set_breakpoint_context_menu(
 9274                    row,
 9275                    Some(position),
 9276                    event.position(),
 9277                    window,
 9278                    cx,
 9279                );
 9280            }))
 9281            .tooltip(move |_window, cx| {
 9282                Tooltip::with_meta_in(
 9283                    primary_action_text,
 9284                    Some(&ToggleBreakpoint),
 9285                    meta.clone(),
 9286                    &focus_handle,
 9287                    cx,
 9288                )
 9289            })
 9290    }
 9291
 9292    fn build_tasks_context(
 9293        project: &Entity<Project>,
 9294        buffer: &Entity<Buffer>,
 9295        buffer_row: u32,
 9296        tasks: &Arc<RunnableTasks>,
 9297        cx: &mut Context<Self>,
 9298    ) -> Task<Option<task::TaskContext>> {
 9299        let position = Point::new(buffer_row, tasks.column);
 9300        let range_start = buffer.read(cx).anchor_at(position, Bias::Right);
 9301        let location = Location {
 9302            buffer: buffer.clone(),
 9303            range: range_start..range_start,
 9304        };
 9305        // Fill in the environmental variables from the tree-sitter captures
 9306        let mut captured_task_variables = TaskVariables::default();
 9307        for (capture_name, value) in tasks.extra_variables.clone() {
 9308            captured_task_variables.insert(
 9309                task::VariableName::Custom(capture_name.into()),
 9310                value.clone(),
 9311            );
 9312        }
 9313        project.update(cx, |project, cx| {
 9314            project.task_store().update(cx, |task_store, cx| {
 9315                task_store.task_context_for_location(captured_task_variables, location, cx)
 9316            })
 9317        })
 9318    }
 9319
 9320    pub fn context_menu_visible(&self) -> bool {
 9321        !self.edit_prediction_preview_is_active()
 9322            && self
 9323                .context_menu
 9324                .borrow()
 9325                .as_ref()
 9326                .is_some_and(|menu| menu.visible())
 9327    }
 9328
 9329    pub fn context_menu_origin(&self) -> Option<ContextMenuOrigin> {
 9330        self.context_menu
 9331            .borrow()
 9332            .as_ref()
 9333            .map(|menu| menu.origin())
 9334    }
 9335
 9336    pub fn set_context_menu_options(&mut self, options: ContextMenuOptions) {
 9337        self.context_menu_options = Some(options);
 9338    }
 9339
 9340    const EDIT_PREDICTION_POPOVER_PADDING_X: Pixels = px(24.);
 9341    const EDIT_PREDICTION_POPOVER_PADDING_Y: Pixels = px(2.);
 9342
 9343    fn render_edit_prediction_popover(
 9344        &mut self,
 9345        text_bounds: &Bounds<Pixels>,
 9346        content_origin: gpui::Point<Pixels>,
 9347        right_margin: Pixels,
 9348        editor_snapshot: &EditorSnapshot,
 9349        visible_row_range: Range<DisplayRow>,
 9350        scroll_top: ScrollOffset,
 9351        scroll_bottom: ScrollOffset,
 9352        line_layouts: &[LineWithInvisibles],
 9353        line_height: Pixels,
 9354        scroll_position: gpui::Point<ScrollOffset>,
 9355        scroll_pixel_position: gpui::Point<ScrollPixelOffset>,
 9356        newest_selection_head: Option<DisplayPoint>,
 9357        editor_width: Pixels,
 9358        style: &EditorStyle,
 9359        window: &mut Window,
 9360        cx: &mut App,
 9361    ) -> Option<(AnyElement, gpui::Point<Pixels>)> {
 9362        if self.mode().is_minimap() {
 9363            return None;
 9364        }
 9365        let active_edit_prediction = self.active_edit_prediction.as_ref()?;
 9366
 9367        if self.edit_prediction_visible_in_cursor_popover(true) {
 9368            return None;
 9369        }
 9370
 9371        match &active_edit_prediction.completion {
 9372            EditPrediction::MoveWithin { target, .. } => {
 9373                let target_display_point = target.to_display_point(editor_snapshot);
 9374
 9375                if self.edit_prediction_requires_modifier() {
 9376                    if !self.edit_prediction_preview_is_active() {
 9377                        return None;
 9378                    }
 9379
 9380                    self.render_edit_prediction_modifier_jump_popover(
 9381                        text_bounds,
 9382                        content_origin,
 9383                        visible_row_range,
 9384                        line_layouts,
 9385                        line_height,
 9386                        scroll_pixel_position,
 9387                        newest_selection_head,
 9388                        target_display_point,
 9389                        window,
 9390                        cx,
 9391                    )
 9392                } else {
 9393                    self.render_edit_prediction_eager_jump_popover(
 9394                        text_bounds,
 9395                        content_origin,
 9396                        editor_snapshot,
 9397                        visible_row_range,
 9398                        scroll_top,
 9399                        scroll_bottom,
 9400                        line_height,
 9401                        scroll_pixel_position,
 9402                        target_display_point,
 9403                        editor_width,
 9404                        window,
 9405                        cx,
 9406                    )
 9407                }
 9408            }
 9409            EditPrediction::Edit {
 9410                display_mode: EditDisplayMode::Inline,
 9411                ..
 9412            } => None,
 9413            EditPrediction::Edit {
 9414                display_mode: EditDisplayMode::TabAccept,
 9415                edits,
 9416                ..
 9417            } => {
 9418                let range = &edits.first()?.0;
 9419                let target_display_point = range.end.to_display_point(editor_snapshot);
 9420
 9421                self.render_edit_prediction_end_of_line_popover(
 9422                    "Accept",
 9423                    editor_snapshot,
 9424                    visible_row_range,
 9425                    target_display_point,
 9426                    line_height,
 9427                    scroll_pixel_position,
 9428                    content_origin,
 9429                    editor_width,
 9430                    window,
 9431                    cx,
 9432                )
 9433            }
 9434            EditPrediction::Edit {
 9435                edits,
 9436                edit_preview,
 9437                display_mode: EditDisplayMode::DiffPopover,
 9438                snapshot,
 9439                ..
 9440            } => self.render_edit_prediction_diff_popover(
 9441                text_bounds,
 9442                content_origin,
 9443                right_margin,
 9444                editor_snapshot,
 9445                visible_row_range,
 9446                line_layouts,
 9447                line_height,
 9448                scroll_position,
 9449                scroll_pixel_position,
 9450                newest_selection_head,
 9451                editor_width,
 9452                style,
 9453                edits,
 9454                edit_preview,
 9455                snapshot,
 9456                window,
 9457                cx,
 9458            ),
 9459            EditPrediction::MoveOutside { snapshot, .. } => {
 9460                let mut element = self
 9461                    .render_edit_prediction_jump_outside_popover(snapshot, window, cx)
 9462                    .into_any();
 9463
 9464                let size = element.layout_as_root(AvailableSpace::min_size(), window, cx);
 9465                let origin_x = text_bounds.size.width - size.width - px(30.);
 9466                let origin = text_bounds.origin + gpui::Point::new(origin_x, px(16.));
 9467                element.prepaint_at(origin, window, cx);
 9468
 9469                Some((element, origin))
 9470            }
 9471        }
 9472    }
 9473
 9474    fn render_edit_prediction_modifier_jump_popover(
 9475        &mut self,
 9476        text_bounds: &Bounds<Pixels>,
 9477        content_origin: gpui::Point<Pixels>,
 9478        visible_row_range: Range<DisplayRow>,
 9479        line_layouts: &[LineWithInvisibles],
 9480        line_height: Pixels,
 9481        scroll_pixel_position: gpui::Point<ScrollPixelOffset>,
 9482        newest_selection_head: Option<DisplayPoint>,
 9483        target_display_point: DisplayPoint,
 9484        window: &mut Window,
 9485        cx: &mut App,
 9486    ) -> Option<(AnyElement, gpui::Point<Pixels>)> {
 9487        let scrolled_content_origin =
 9488            content_origin - gpui::Point::new(scroll_pixel_position.x.into(), Pixels::ZERO);
 9489
 9490        const SCROLL_PADDING_Y: Pixels = px(12.);
 9491
 9492        if target_display_point.row() < visible_row_range.start {
 9493            return self.render_edit_prediction_scroll_popover(
 9494                &|_| SCROLL_PADDING_Y,
 9495                IconName::ArrowUp,
 9496                visible_row_range,
 9497                line_layouts,
 9498                newest_selection_head,
 9499                scrolled_content_origin,
 9500                window,
 9501                cx,
 9502            );
 9503        } else if target_display_point.row() >= visible_row_range.end {
 9504            return self.render_edit_prediction_scroll_popover(
 9505                &|size| text_bounds.size.height - size.height - SCROLL_PADDING_Y,
 9506                IconName::ArrowDown,
 9507                visible_row_range,
 9508                line_layouts,
 9509                newest_selection_head,
 9510                scrolled_content_origin,
 9511                window,
 9512                cx,
 9513            );
 9514        }
 9515
 9516        const POLE_WIDTH: Pixels = px(2.);
 9517
 9518        let line_layout =
 9519            line_layouts.get(target_display_point.row().minus(visible_row_range.start) as usize)?;
 9520        let target_column = target_display_point.column() as usize;
 9521
 9522        let target_x = line_layout.x_for_index(target_column);
 9523        let target_y = (target_display_point.row().as_f64() * f64::from(line_height))
 9524            - scroll_pixel_position.y;
 9525
 9526        let flag_on_right = target_x < text_bounds.size.width / 2.;
 9527
 9528        let mut border_color = Self::edit_prediction_callout_popover_border_color(cx);
 9529        border_color.l += 0.001;
 9530
 9531        let mut element = v_flex()
 9532            .items_end()
 9533            .when(flag_on_right, |el| el.items_start())
 9534            .child(if flag_on_right {
 9535                self.render_edit_prediction_line_popover("Jump", None, window, cx)
 9536                    .rounded_bl(px(0.))
 9537                    .rounded_tl(px(0.))
 9538                    .border_l_2()
 9539                    .border_color(border_color)
 9540            } else {
 9541                self.render_edit_prediction_line_popover("Jump", None, window, cx)
 9542                    .rounded_br(px(0.))
 9543                    .rounded_tr(px(0.))
 9544                    .border_r_2()
 9545                    .border_color(border_color)
 9546            })
 9547            .child(div().w(POLE_WIDTH).bg(border_color).h(line_height))
 9548            .into_any();
 9549
 9550        let size = element.layout_as_root(AvailableSpace::min_size(), window, cx);
 9551
 9552        let mut origin = scrolled_content_origin + point(target_x, target_y.into())
 9553            - point(
 9554                if flag_on_right {
 9555                    POLE_WIDTH
 9556                } else {
 9557                    size.width - POLE_WIDTH
 9558                },
 9559                size.height - line_height,
 9560            );
 9561
 9562        origin.x = origin.x.max(content_origin.x);
 9563
 9564        element.prepaint_at(origin, window, cx);
 9565
 9566        Some((element, origin))
 9567    }
 9568
 9569    fn render_edit_prediction_scroll_popover(
 9570        &mut self,
 9571        to_y: &dyn Fn(Size<Pixels>) -> Pixels,
 9572        scroll_icon: IconName,
 9573        visible_row_range: Range<DisplayRow>,
 9574        line_layouts: &[LineWithInvisibles],
 9575        newest_selection_head: Option<DisplayPoint>,
 9576        scrolled_content_origin: gpui::Point<Pixels>,
 9577        window: &mut Window,
 9578        cx: &mut App,
 9579    ) -> Option<(AnyElement, gpui::Point<Pixels>)> {
 9580        let mut element = self
 9581            .render_edit_prediction_line_popover("Scroll", Some(scroll_icon), window, cx)
 9582            .into_any();
 9583
 9584        let size = element.layout_as_root(AvailableSpace::min_size(), window, cx);
 9585
 9586        let cursor = newest_selection_head?;
 9587        let cursor_row_layout =
 9588            line_layouts.get(cursor.row().minus(visible_row_range.start) as usize)?;
 9589        let cursor_column = cursor.column() as usize;
 9590
 9591        let cursor_character_x = cursor_row_layout.x_for_index(cursor_column);
 9592
 9593        let origin = scrolled_content_origin + point(cursor_character_x, to_y(size));
 9594
 9595        element.prepaint_at(origin, window, cx);
 9596        Some((element, origin))
 9597    }
 9598
 9599    fn render_edit_prediction_eager_jump_popover(
 9600        &mut self,
 9601        text_bounds: &Bounds<Pixels>,
 9602        content_origin: gpui::Point<Pixels>,
 9603        editor_snapshot: &EditorSnapshot,
 9604        visible_row_range: Range<DisplayRow>,
 9605        scroll_top: ScrollOffset,
 9606        scroll_bottom: ScrollOffset,
 9607        line_height: Pixels,
 9608        scroll_pixel_position: gpui::Point<ScrollPixelOffset>,
 9609        target_display_point: DisplayPoint,
 9610        editor_width: Pixels,
 9611        window: &mut Window,
 9612        cx: &mut App,
 9613    ) -> Option<(AnyElement, gpui::Point<Pixels>)> {
 9614        if target_display_point.row().as_f64() < scroll_top {
 9615            let mut element = self
 9616                .render_edit_prediction_line_popover(
 9617                    "Jump to Edit",
 9618                    Some(IconName::ArrowUp),
 9619                    window,
 9620                    cx,
 9621                )
 9622                .into_any();
 9623
 9624            let size = element.layout_as_root(AvailableSpace::min_size(), window, cx);
 9625            let offset = point(
 9626                (text_bounds.size.width - size.width) / 2.,
 9627                Self::EDIT_PREDICTION_POPOVER_PADDING_Y,
 9628            );
 9629
 9630            let origin = text_bounds.origin + offset;
 9631            element.prepaint_at(origin, window, cx);
 9632            Some((element, origin))
 9633        } else if (target_display_point.row().as_f64() + 1.) > scroll_bottom {
 9634            let mut element = self
 9635                .render_edit_prediction_line_popover(
 9636                    "Jump to Edit",
 9637                    Some(IconName::ArrowDown),
 9638                    window,
 9639                    cx,
 9640                )
 9641                .into_any();
 9642
 9643            let size = element.layout_as_root(AvailableSpace::min_size(), window, cx);
 9644            let offset = point(
 9645                (text_bounds.size.width - size.width) / 2.,
 9646                text_bounds.size.height - size.height - Self::EDIT_PREDICTION_POPOVER_PADDING_Y,
 9647            );
 9648
 9649            let origin = text_bounds.origin + offset;
 9650            element.prepaint_at(origin, window, cx);
 9651            Some((element, origin))
 9652        } else {
 9653            self.render_edit_prediction_end_of_line_popover(
 9654                "Jump to Edit",
 9655                editor_snapshot,
 9656                visible_row_range,
 9657                target_display_point,
 9658                line_height,
 9659                scroll_pixel_position,
 9660                content_origin,
 9661                editor_width,
 9662                window,
 9663                cx,
 9664            )
 9665        }
 9666    }
 9667
 9668    fn render_edit_prediction_end_of_line_popover(
 9669        self: &mut Editor,
 9670        label: &'static str,
 9671        editor_snapshot: &EditorSnapshot,
 9672        visible_row_range: Range<DisplayRow>,
 9673        target_display_point: DisplayPoint,
 9674        line_height: Pixels,
 9675        scroll_pixel_position: gpui::Point<ScrollPixelOffset>,
 9676        content_origin: gpui::Point<Pixels>,
 9677        editor_width: Pixels,
 9678        window: &mut Window,
 9679        cx: &mut App,
 9680    ) -> Option<(AnyElement, gpui::Point<Pixels>)> {
 9681        let target_line_end = DisplayPoint::new(
 9682            target_display_point.row(),
 9683            editor_snapshot.line_len(target_display_point.row()),
 9684        );
 9685
 9686        let mut element = self
 9687            .render_edit_prediction_line_popover(label, None, window, cx)
 9688            .into_any();
 9689
 9690        let size = element.layout_as_root(AvailableSpace::min_size(), window, cx);
 9691
 9692        let line_origin =
 9693            self.display_to_pixel_point(target_line_end, editor_snapshot, window, cx)?;
 9694
 9695        let start_point = content_origin - point(scroll_pixel_position.x.into(), Pixels::ZERO);
 9696        let mut origin = start_point
 9697            + line_origin
 9698            + point(Self::EDIT_PREDICTION_POPOVER_PADDING_X, Pixels::ZERO);
 9699        origin.x = origin.x.max(content_origin.x);
 9700
 9701        let max_x = content_origin.x + editor_width - size.width;
 9702
 9703        if origin.x > max_x {
 9704            let offset = line_height + Self::EDIT_PREDICTION_POPOVER_PADDING_Y;
 9705
 9706            let icon = if visible_row_range.contains(&(target_display_point.row() + 2)) {
 9707                origin.y += offset;
 9708                IconName::ArrowUp
 9709            } else {
 9710                origin.y -= offset;
 9711                IconName::ArrowDown
 9712            };
 9713
 9714            element = self
 9715                .render_edit_prediction_line_popover(label, Some(icon), window, cx)
 9716                .into_any();
 9717
 9718            let size = element.layout_as_root(AvailableSpace::min_size(), window, cx);
 9719
 9720            origin.x = content_origin.x + editor_width - size.width - px(2.);
 9721        }
 9722
 9723        element.prepaint_at(origin, window, cx);
 9724        Some((element, origin))
 9725    }
 9726
 9727    fn render_edit_prediction_diff_popover(
 9728        self: &Editor,
 9729        text_bounds: &Bounds<Pixels>,
 9730        content_origin: gpui::Point<Pixels>,
 9731        right_margin: Pixels,
 9732        editor_snapshot: &EditorSnapshot,
 9733        visible_row_range: Range<DisplayRow>,
 9734        line_layouts: &[LineWithInvisibles],
 9735        line_height: Pixels,
 9736        scroll_position: gpui::Point<ScrollOffset>,
 9737        scroll_pixel_position: gpui::Point<ScrollPixelOffset>,
 9738        newest_selection_head: Option<DisplayPoint>,
 9739        editor_width: Pixels,
 9740        style: &EditorStyle,
 9741        edits: &Vec<(Range<Anchor>, Arc<str>)>,
 9742        edit_preview: &Option<language::EditPreview>,
 9743        snapshot: &language::BufferSnapshot,
 9744        window: &mut Window,
 9745        cx: &mut App,
 9746    ) -> Option<(AnyElement, gpui::Point<Pixels>)> {
 9747        let edit_start = edits
 9748            .first()
 9749            .unwrap()
 9750            .0
 9751            .start
 9752            .to_display_point(editor_snapshot);
 9753        let edit_end = edits
 9754            .last()
 9755            .unwrap()
 9756            .0
 9757            .end
 9758            .to_display_point(editor_snapshot);
 9759
 9760        let is_visible = visible_row_range.contains(&edit_start.row())
 9761            || visible_row_range.contains(&edit_end.row());
 9762        if !is_visible {
 9763            return None;
 9764        }
 9765
 9766        let highlighted_edits = if let Some(edit_preview) = edit_preview.as_ref() {
 9767            crate::edit_prediction_edit_text(snapshot, edits, edit_preview, false, cx)
 9768        } else {
 9769            // Fallback for providers without edit_preview
 9770            crate::edit_prediction_fallback_text(edits, cx)
 9771        };
 9772
 9773        let styled_text = highlighted_edits.to_styled_text(&style.text);
 9774        let line_count = highlighted_edits.text.lines().count();
 9775
 9776        const BORDER_WIDTH: Pixels = px(1.);
 9777
 9778        let keybind = self.render_edit_prediction_keybind(window, cx);
 9779        let has_keybind = keybind.is_some();
 9780
 9781        let mut element = h_flex()
 9782            .items_start()
 9783            .child(
 9784                h_flex()
 9785                    .bg(cx.theme().colors().editor_background)
 9786                    .border(BORDER_WIDTH)
 9787                    .shadow_xs()
 9788                    .border_color(cx.theme().colors().border)
 9789                    .rounded_l_lg()
 9790                    .when(line_count > 1, |el| el.rounded_br_lg())
 9791                    .pr_1()
 9792                    .child(styled_text),
 9793            )
 9794            .child(
 9795                h_flex()
 9796                    .h(line_height + BORDER_WIDTH * 2.)
 9797                    .px_1p5()
 9798                    .gap_1()
 9799                    // Workaround: For some reason, there's a gap if we don't do this
 9800                    .ml(-BORDER_WIDTH)
 9801                    .shadow(vec![gpui::BoxShadow {
 9802                        color: gpui::black().opacity(0.05),
 9803                        offset: point(px(1.), px(1.)),
 9804                        blur_radius: px(2.),
 9805                        spread_radius: px(0.),
 9806                    }])
 9807                    .bg(Editor::edit_prediction_line_popover_bg_color(cx))
 9808                    .border(BORDER_WIDTH)
 9809                    .border_color(cx.theme().colors().border)
 9810                    .rounded_r_lg()
 9811                    .id("edit_prediction_diff_popover_keybind")
 9812                    .when(!has_keybind, |el| {
 9813                        let status_colors = cx.theme().status();
 9814
 9815                        el.bg(status_colors.error_background)
 9816                            .border_color(status_colors.error.opacity(0.6))
 9817                            .child(Icon::new(IconName::Info).color(Color::Error))
 9818                            .cursor_default()
 9819                            .hoverable_tooltip(move |_window, cx| {
 9820                                cx.new(|_| MissingEditPredictionKeybindingTooltip).into()
 9821                            })
 9822                    })
 9823                    .children(keybind),
 9824            )
 9825            .into_any();
 9826
 9827        let longest_row =
 9828            editor_snapshot.longest_row_in_range(edit_start.row()..edit_end.row() + 1);
 9829        let longest_line_width = if visible_row_range.contains(&longest_row) {
 9830            line_layouts[(longest_row.0 - visible_row_range.start.0) as usize].width
 9831        } else {
 9832            layout_line(
 9833                longest_row,
 9834                editor_snapshot,
 9835                style,
 9836                editor_width,
 9837                |_| false,
 9838                window,
 9839                cx,
 9840            )
 9841            .width
 9842        };
 9843
 9844        let viewport_bounds =
 9845            Bounds::new(Default::default(), window.viewport_size()).extend(Edges {
 9846                right: -right_margin,
 9847                ..Default::default()
 9848            });
 9849
 9850        let x_after_longest = Pixels::from(
 9851            ScrollPixelOffset::from(
 9852                text_bounds.origin.x + longest_line_width + Self::EDIT_PREDICTION_POPOVER_PADDING_X,
 9853            ) - scroll_pixel_position.x,
 9854        );
 9855
 9856        let element_bounds = element.layout_as_root(AvailableSpace::min_size(), window, cx);
 9857
 9858        // Fully visible if it can be displayed within the window (allow overlapping other
 9859        // panes). However, this is only allowed if the popover starts within text_bounds.
 9860        let can_position_to_the_right = x_after_longest < text_bounds.right()
 9861            && x_after_longest + element_bounds.width < viewport_bounds.right();
 9862
 9863        let mut origin = if can_position_to_the_right {
 9864            point(
 9865                x_after_longest,
 9866                text_bounds.origin.y
 9867                    + Pixels::from(
 9868                        edit_start.row().as_f64() * ScrollPixelOffset::from(line_height)
 9869                            - scroll_pixel_position.y,
 9870                    ),
 9871            )
 9872        } else {
 9873            let cursor_row = newest_selection_head.map(|head| head.row());
 9874            let above_edit = edit_start
 9875                .row()
 9876                .0
 9877                .checked_sub(line_count as u32)
 9878                .map(DisplayRow);
 9879            let below_edit = Some(edit_end.row() + 1);
 9880            let above_cursor =
 9881                cursor_row.and_then(|row| row.0.checked_sub(line_count as u32).map(DisplayRow));
 9882            let below_cursor = cursor_row.map(|cursor_row| cursor_row + 1);
 9883
 9884            // Place the edit popover adjacent to the edit if there is a location
 9885            // available that is onscreen and does not obscure the cursor. Otherwise,
 9886            // place it adjacent to the cursor.
 9887            let row_target = [above_edit, below_edit, above_cursor, below_cursor]
 9888                .into_iter()
 9889                .flatten()
 9890                .find(|&start_row| {
 9891                    let end_row = start_row + line_count as u32;
 9892                    visible_row_range.contains(&start_row)
 9893                        && visible_row_range.contains(&end_row)
 9894                        && cursor_row
 9895                            .is_none_or(|cursor_row| !((start_row..end_row).contains(&cursor_row)))
 9896                })?;
 9897
 9898            content_origin
 9899                + point(
 9900                    Pixels::from(-scroll_pixel_position.x),
 9901                    Pixels::from(
 9902                        (row_target.as_f64() - scroll_position.y) * f64::from(line_height),
 9903                    ),
 9904                )
 9905        };
 9906
 9907        origin.x -= BORDER_WIDTH;
 9908
 9909        window.with_content_mask(
 9910            Some(gpui::ContentMask {
 9911                bounds: *text_bounds,
 9912            }),
 9913            |window| {
 9914                window.defer_draw(element, origin, 1, Some(window.content_mask()));
 9915            },
 9916        );
 9917
 9918        // Do not return an element, since it will already be drawn due to defer_draw.
 9919        None
 9920    }
 9921
 9922    fn edit_prediction_cursor_popover_height(&self) -> Pixels {
 9923        px(30.)
 9924    }
 9925
 9926    fn current_user_player_color(&self, cx: &mut App) -> PlayerColor {
 9927        if self.read_only(cx) {
 9928            cx.theme().players().read_only()
 9929        } else {
 9930            self.style.as_ref().unwrap().local_player
 9931        }
 9932    }
 9933
 9934    fn render_edit_prediction_inline_keystroke(
 9935        &self,
 9936        keystroke: &gpui::KeybindingKeystroke,
 9937        modifiers_color: Color,
 9938        cx: &App,
 9939    ) -> AnyElement {
 9940        let is_platform_style_mac = PlatformStyle::platform() == PlatformStyle::Mac;
 9941
 9942        h_flex()
 9943            .px_0p5()
 9944            .when(is_platform_style_mac, |parent| parent.gap_0p5())
 9945            .font(
 9946                theme_settings::ThemeSettings::get_global(cx)
 9947                    .buffer_font
 9948                    .clone(),
 9949            )
 9950            .text_size(TextSize::XSmall.rems(cx))
 9951            .child(h_flex().children(ui::render_modifiers(
 9952                keystroke.modifiers(),
 9953                PlatformStyle::platform(),
 9954                Some(modifiers_color),
 9955                Some(IconSize::XSmall.rems().into()),
 9956                true,
 9957            )))
 9958            .when(is_platform_style_mac, |parent| {
 9959                parent.child(keystroke.key().to_string())
 9960            })
 9961            .when(!is_platform_style_mac, |parent| {
 9962                parent.child(
 9963                    Key::new(ui::utils::capitalize(keystroke.key()), Some(Color::Default))
 9964                        .size(Some(IconSize::XSmall.rems().into())),
 9965                )
 9966            })
 9967            .into_any()
 9968    }
 9969
 9970    fn render_edit_prediction_popover_keystroke(
 9971        &self,
 9972        keystroke: &gpui::KeybindingKeystroke,
 9973        color: Color,
 9974        cx: &App,
 9975    ) -> AnyElement {
 9976        let is_platform_style_mac = PlatformStyle::platform() == PlatformStyle::Mac;
 9977
 9978        if keystroke.modifiers().modified() {
 9979            h_flex()
 9980                .font(
 9981                    theme_settings::ThemeSettings::get_global(cx)
 9982                        .buffer_font
 9983                        .clone(),
 9984                )
 9985                .when(is_platform_style_mac, |parent| parent.gap_1())
 9986                .child(h_flex().children(ui::render_modifiers(
 9987                    keystroke.modifiers(),
 9988                    PlatformStyle::platform(),
 9989                    Some(color),
 9990                    None,
 9991                    false,
 9992                )))
 9993                .into_any()
 9994        } else {
 9995            Key::new(ui::utils::capitalize(keystroke.key()), Some(color))
 9996                .size(Some(IconSize::XSmall.rems().into()))
 9997                .into_any_element()
 9998        }
 9999    }
10000
10001    fn render_edit_prediction_keybind(
10002        &self,
10003        window: &mut Window,
10004        cx: &mut App,
10005    ) -> Option<AnyElement> {
10006        let keybind_display =
10007            self.edit_prediction_keybind_display(EditPredictionKeybindSurface::Inline, window, cx);
10008        let keystroke = keybind_display.displayed_keystroke.as_ref()?;
10009
10010        let modifiers_color = if *keystroke.modifiers() == window.modifiers() {
10011            Color::Accent
10012        } else {
10013            Color::Muted
10014        };
10015
10016        Some(self.render_edit_prediction_inline_keystroke(keystroke, modifiers_color, cx))
10017    }
10018
10019    fn render_edit_prediction_line_popover(
10020        &self,
10021        label: impl Into<SharedString>,
10022        icon: Option<IconName>,
10023        window: &mut Window,
10024        cx: &mut App,
10025    ) -> Stateful<Div> {
10026        let padding_right = if icon.is_some() { px(4.) } else { px(8.) };
10027
10028        let keybind = self.render_edit_prediction_keybind(window, cx);
10029        let has_keybind = keybind.is_some();
10030        let icons = Self::get_prediction_provider_icons(&self.edit_prediction_provider, cx);
10031
10032        h_flex()
10033            .id("ep-line-popover")
10034            .py_0p5()
10035            .pl_1()
10036            .pr(padding_right)
10037            .gap_1()
10038            .rounded_md()
10039            .border_1()
10040            .bg(Self::edit_prediction_line_popover_bg_color(cx))
10041            .border_color(Self::edit_prediction_callout_popover_border_color(cx))
10042            .shadow_xs()
10043            .when(!has_keybind, |el| {
10044                let status_colors = cx.theme().status();
10045
10046                el.bg(status_colors.error_background)
10047                    .border_color(status_colors.error.opacity(0.6))
10048                    .pl_2()
10049                    .child(Icon::new(icons.error).color(Color::Error))
10050                    .cursor_default()
10051                    .hoverable_tooltip(move |_window, cx| {
10052                        cx.new(|_| MissingEditPredictionKeybindingTooltip).into()
10053                    })
10054            })
10055            .children(keybind)
10056            .child(
10057                Label::new(label)
10058                    .size(LabelSize::Small)
10059                    .when(!has_keybind, |el| {
10060                        el.color(cx.theme().status().error.into()).strikethrough()
10061                    }),
10062            )
10063            .when(!has_keybind, |el| {
10064                el.child(
10065                    h_flex().ml_1().child(
10066                        Icon::new(IconName::Info)
10067                            .size(IconSize::Small)
10068                            .color(cx.theme().status().error.into()),
10069                    ),
10070                )
10071            })
10072            .when_some(icon, |element, icon| {
10073                element.child(
10074                    div()
10075                        .mt(px(1.5))
10076                        .child(Icon::new(icon).size(IconSize::Small)),
10077                )
10078            })
10079    }
10080
10081    fn render_edit_prediction_jump_outside_popover(
10082        &self,
10083        snapshot: &BufferSnapshot,
10084        window: &mut Window,
10085        cx: &mut App,
10086    ) -> Stateful<Div> {
10087        let keybind = self.render_edit_prediction_keybind(window, cx);
10088        let has_keybind = keybind.is_some();
10089        let icons = Self::get_prediction_provider_icons(&self.edit_prediction_provider, cx);
10090
10091        let file_name = snapshot
10092            .file()
10093            .map(|file| SharedString::new(file.file_name(cx)))
10094            .unwrap_or(SharedString::new_static("untitled"));
10095
10096        h_flex()
10097            .id("ep-jump-outside-popover")
10098            .py_1()
10099            .px_2()
10100            .gap_1()
10101            .rounded_md()
10102            .border_1()
10103            .bg(Self::edit_prediction_line_popover_bg_color(cx))
10104            .border_color(Self::edit_prediction_callout_popover_border_color(cx))
10105            .shadow_xs()
10106            .when(!has_keybind, |el| {
10107                let status_colors = cx.theme().status();
10108
10109                el.bg(status_colors.error_background)
10110                    .border_color(status_colors.error.opacity(0.6))
10111                    .pl_2()
10112                    .child(Icon::new(icons.error).color(Color::Error))
10113                    .cursor_default()
10114                    .hoverable_tooltip(move |_window, cx| {
10115                        cx.new(|_| MissingEditPredictionKeybindingTooltip).into()
10116                    })
10117            })
10118            .children(keybind)
10119            .child(
10120                Label::new(file_name)
10121                    .size(LabelSize::Small)
10122                    .buffer_font(cx)
10123                    .when(!has_keybind, |el| {
10124                        el.color(cx.theme().status().error.into()).strikethrough()
10125                    }),
10126            )
10127            .when(!has_keybind, |el| {
10128                el.child(
10129                    h_flex().ml_1().child(
10130                        Icon::new(IconName::Info)
10131                            .size(IconSize::Small)
10132                            .color(cx.theme().status().error.into()),
10133                    ),
10134                )
10135            })
10136            .child(
10137                div()
10138                    .mt(px(1.5))
10139                    .child(Icon::new(IconName::ArrowUpRight).size(IconSize::Small)),
10140            )
10141    }
10142
10143    fn edit_prediction_line_popover_bg_color(cx: &App) -> Hsla {
10144        let accent_color = cx.theme().colors().text_accent;
10145        let editor_bg_color = cx.theme().colors().editor_background;
10146        editor_bg_color.blend(accent_color.opacity(0.1))
10147    }
10148
10149    fn edit_prediction_callout_popover_border_color(cx: &App) -> Hsla {
10150        let accent_color = cx.theme().colors().text_accent;
10151        let editor_bg_color = cx.theme().colors().editor_background;
10152        editor_bg_color.blend(accent_color.opacity(0.6))
10153    }
10154    fn get_prediction_provider_icons(
10155        provider: &Option<RegisteredEditPredictionDelegate>,
10156        cx: &App,
10157    ) -> edit_prediction_types::EditPredictionIconSet {
10158        match provider {
10159            Some(provider) => provider.provider.icons(cx),
10160            None => edit_prediction_types::EditPredictionIconSet::new(IconName::ZedPredict),
10161        }
10162    }
10163
10164    fn render_edit_prediction_cursor_popover(
10165        &self,
10166        min_width: Pixels,
10167        max_width: Pixels,
10168        cursor_point: Point,
10169        style: &EditorStyle,
10170        window: &mut Window,
10171        cx: &mut Context<Editor>,
10172    ) -> Option<AnyElement> {
10173        let provider = self.edit_prediction_provider.as_ref()?;
10174        let icons = Self::get_prediction_provider_icons(&self.edit_prediction_provider, cx);
10175
10176        let is_refreshing = provider.provider.is_refreshing(cx);
10177
10178        fn pending_completion_container(icon: IconName) -> Div {
10179            h_flex().h_full().flex_1().gap_2().child(Icon::new(icon))
10180        }
10181
10182        let completion = match &self.active_edit_prediction {
10183            Some(prediction) => {
10184                if !self.has_visible_completions_menu() {
10185                    const RADIUS: Pixels = px(6.);
10186                    const BORDER_WIDTH: Pixels = px(1.);
10187                    let keybind_display = self.edit_prediction_keybind_display(
10188                        EditPredictionKeybindSurface::CursorPopoverCompact,
10189                        window,
10190                        cx,
10191                    );
10192
10193                    return Some(
10194                        h_flex()
10195                            .elevation_2(cx)
10196                            .border(BORDER_WIDTH)
10197                            .border_color(cx.theme().colors().border)
10198                            .when(keybind_display.missing_accept_keystroke, |el| {
10199                                el.border_color(cx.theme().status().error)
10200                            })
10201                            .rounded(RADIUS)
10202                            .rounded_tl(px(0.))
10203                            .overflow_hidden()
10204                            .child(div().px_1p5().child(match &prediction.completion {
10205                                EditPrediction::MoveWithin { target, snapshot } => {
10206                                    use text::ToPoint as _;
10207                                    if target.text_anchor.to_point(snapshot).row > cursor_point.row
10208                                    {
10209                                        Icon::new(icons.down)
10210                                    } else {
10211                                        Icon::new(icons.up)
10212                                    }
10213                                }
10214                                EditPrediction::MoveOutside { .. } => {
10215                                    // TODO [zeta2] custom icon for external jump?
10216                                    Icon::new(icons.base)
10217                                }
10218                                EditPrediction::Edit { .. } => Icon::new(icons.base),
10219                            }))
10220                            .child(
10221                                h_flex()
10222                                    .gap_1()
10223                                    .py_1()
10224                                    .px_2()
10225                                    .rounded_r(RADIUS - BORDER_WIDTH)
10226                                    .border_l_1()
10227                                    .border_color(cx.theme().colors().border)
10228                                    .bg(Self::edit_prediction_line_popover_bg_color(cx))
10229                                    .when(keybind_display.show_hold_label, |el| {
10230                                        el.child(
10231                                            Label::new("Hold")
10232                                                .size(LabelSize::Small)
10233                                                .when(
10234                                                    keybind_display.missing_accept_keystroke,
10235                                                    |el| el.strikethrough(),
10236                                                )
10237                                                .line_height_style(LineHeightStyle::UiLabel),
10238                                        )
10239                                    })
10240                                    .id("edit_prediction_cursor_popover_keybind")
10241                                    .when(keybind_display.missing_accept_keystroke, |el| {
10242                                        let status_colors = cx.theme().status();
10243
10244                                        el.bg(status_colors.error_background)
10245                                            .border_color(status_colors.error.opacity(0.6))
10246                                            .child(Icon::new(IconName::Info).color(Color::Error))
10247                                            .cursor_default()
10248                                            .hoverable_tooltip(move |_window, cx| {
10249                                                cx.new(|_| MissingEditPredictionKeybindingTooltip)
10250                                                    .into()
10251                                            })
10252                                    })
10253                                    .when_some(
10254                                        keybind_display.displayed_keystroke.as_ref(),
10255                                        |el, compact_keystroke| {
10256                                            el.child(self.render_edit_prediction_popover_keystroke(
10257                                                compact_keystroke,
10258                                                Color::Default,
10259                                                cx,
10260                                            ))
10261                                        },
10262                                    ),
10263                            )
10264                            .into_any(),
10265                    );
10266                }
10267
10268                self.render_edit_prediction_cursor_popover_preview(
10269                    prediction,
10270                    cursor_point,
10271                    style,
10272                    cx,
10273                )?
10274            }
10275
10276            None if is_refreshing => match &self.stale_edit_prediction_in_menu {
10277                Some(stale_completion) => self.render_edit_prediction_cursor_popover_preview(
10278                    stale_completion,
10279                    cursor_point,
10280                    style,
10281                    cx,
10282                )?,
10283
10284                None => pending_completion_container(icons.base)
10285                    .child(Label::new("...").size(LabelSize::Small)),
10286            },
10287
10288            None => pending_completion_container(icons.base)
10289                .child(Label::new("...").size(LabelSize::Small)),
10290        };
10291
10292        let completion = if is_refreshing || self.active_edit_prediction.is_none() {
10293            completion
10294                .with_animation(
10295                    "loading-completion",
10296                    Animation::new(Duration::from_secs(2))
10297                        .repeat()
10298                        .with_easing(pulsating_between(0.4, 0.8)),
10299                    |label, delta| label.opacity(delta),
10300                )
10301                .into_any_element()
10302        } else {
10303            completion.into_any_element()
10304        };
10305
10306        let has_completion = self.active_edit_prediction.is_some();
10307        let keybind_display = self.edit_prediction_keybind_display(
10308            EditPredictionKeybindSurface::CursorPopoverExpanded,
10309            window,
10310            cx,
10311        );
10312
10313        Some(
10314            h_flex()
10315                .min_w(min_width)
10316                .max_w(max_width)
10317                .flex_1()
10318                .elevation_2(cx)
10319                .border_color(cx.theme().colors().border)
10320                .child(
10321                    div()
10322                        .flex_1()
10323                        .py_1()
10324                        .px_2()
10325                        .overflow_hidden()
10326                        .child(completion),
10327                )
10328                .when_some(
10329                    keybind_display.displayed_keystroke.as_ref(),
10330                    |el, keystroke| {
10331                        let key_color = if !has_completion {
10332                            Color::Muted
10333                        } else {
10334                            Color::Default
10335                        };
10336
10337                        if keybind_display.action == EditPredictionKeybindAction::Preview {
10338                            el.child(
10339                                h_flex()
10340                                    .h_full()
10341                                    .border_l_1()
10342                                    .rounded_r_lg()
10343                                    .border_color(cx.theme().colors().border)
10344                                    .bg(Self::edit_prediction_line_popover_bg_color(cx))
10345                                    .gap_1()
10346                                    .py_1()
10347                                    .px_2()
10348                                    .child(self.render_edit_prediction_popover_keystroke(
10349                                        keystroke, key_color, cx,
10350                                    ))
10351                                    .child(Label::new("Preview").into_any_element())
10352                                    .opacity(if has_completion { 1.0 } else { 0.4 }),
10353                            )
10354                        } else {
10355                            el.child(
10356                                h_flex()
10357                                    .h_full()
10358                                    .border_l_1()
10359                                    .rounded_r_lg()
10360                                    .border_color(cx.theme().colors().border)
10361                                    .bg(Self::edit_prediction_line_popover_bg_color(cx))
10362                                    .gap_1()
10363                                    .py_1()
10364                                    .px_2()
10365                                    .child(self.render_edit_prediction_popover_keystroke(
10366                                        keystroke, key_color, cx,
10367                                    ))
10368                                    .opacity(if has_completion { 1.0 } else { 0.4 }),
10369                            )
10370                        }
10371                    },
10372                )
10373                .into_any(),
10374        )
10375    }
10376
10377    fn render_edit_prediction_cursor_popover_preview(
10378        &self,
10379        completion: &EditPredictionState,
10380        cursor_point: Point,
10381        style: &EditorStyle,
10382        cx: &mut Context<Editor>,
10383    ) -> Option<Div> {
10384        use text::ToPoint as _;
10385
10386        fn render_relative_row_jump(
10387            prefix: impl Into<String>,
10388            current_row: u32,
10389            target_row: u32,
10390        ) -> Div {
10391            let (row_diff, arrow) = if target_row < current_row {
10392                (current_row - target_row, IconName::ArrowUp)
10393            } else {
10394                (target_row - current_row, IconName::ArrowDown)
10395            };
10396
10397            h_flex()
10398                .child(
10399                    Label::new(format!("{}{}", prefix.into(), row_diff))
10400                        .color(Color::Muted)
10401                        .size(LabelSize::Small),
10402                )
10403                .child(Icon::new(arrow).color(Color::Muted).size(IconSize::Small))
10404        }
10405
10406        let supports_jump = self
10407            .edit_prediction_provider
10408            .as_ref()
10409            .map(|provider| provider.provider.supports_jump_to_edit())
10410            .unwrap_or(true);
10411
10412        let icons = Self::get_prediction_provider_icons(&self.edit_prediction_provider, cx);
10413
10414        match &completion.completion {
10415            EditPrediction::MoveWithin {
10416                target, snapshot, ..
10417            } => {
10418                if !supports_jump {
10419                    return None;
10420                }
10421
10422                Some(
10423                    h_flex()
10424                        .px_2()
10425                        .gap_2()
10426                        .flex_1()
10427                        .child(
10428                            if target.text_anchor.to_point(snapshot).row > cursor_point.row {
10429                                Icon::new(icons.down)
10430                            } else {
10431                                Icon::new(icons.up)
10432                            },
10433                        )
10434                        .child(Label::new("Jump to Edit")),
10435                )
10436            }
10437            EditPrediction::MoveOutside { snapshot, .. } => {
10438                let file_name = snapshot
10439                    .file()
10440                    .map(|file| file.file_name(cx))
10441                    .unwrap_or("untitled");
10442                Some(
10443                    h_flex()
10444                        .px_2()
10445                        .gap_2()
10446                        .flex_1()
10447                        .child(Icon::new(icons.base))
10448                        .child(Label::new(format!("Jump to {file_name}"))),
10449                )
10450            }
10451            EditPrediction::Edit {
10452                edits,
10453                edit_preview,
10454                snapshot,
10455                ..
10456            } => {
10457                let first_edit_row = edits.first()?.0.start.text_anchor.to_point(snapshot).row;
10458
10459                let (highlighted_edits, has_more_lines) =
10460                    if let Some(edit_preview) = edit_preview.as_ref() {
10461                        crate::edit_prediction_edit_text(snapshot, edits, edit_preview, true, cx)
10462                            .first_line_preview()
10463                    } else {
10464                        crate::edit_prediction_fallback_text(edits, cx).first_line_preview()
10465                    };
10466
10467                let styled_text = gpui::StyledText::new(highlighted_edits.text)
10468                    .with_default_highlights(&style.text, highlighted_edits.highlights);
10469
10470                let preview = h_flex()
10471                    .gap_1()
10472                    .min_w_16()
10473                    .child(styled_text)
10474                    .when(has_more_lines, |parent| parent.child(""));
10475
10476                let left = if supports_jump && first_edit_row != cursor_point.row {
10477                    render_relative_row_jump("", cursor_point.row, first_edit_row)
10478                        .into_any_element()
10479                } else {
10480                    Icon::new(icons.base).into_any_element()
10481                };
10482
10483                Some(
10484                    h_flex()
10485                        .h_full()
10486                        .flex_1()
10487                        .gap_2()
10488                        .pr_1()
10489                        .overflow_x_hidden()
10490                        .font(
10491                            theme_settings::ThemeSettings::get_global(cx)
10492                                .buffer_font
10493                                .clone(),
10494                        )
10495                        .child(left)
10496                        .child(preview),
10497                )
10498            }
10499        }
10500    }
10501
10502    pub fn render_context_menu(
10503        &mut self,
10504        max_height_in_lines: u32,
10505        window: &mut Window,
10506        cx: &mut Context<Editor>,
10507    ) -> Option<AnyElement> {
10508        let menu = self.context_menu.borrow();
10509        let menu = menu.as_ref()?;
10510        if !menu.visible() {
10511            return None;
10512        };
10513        self.style
10514            .as_ref()
10515            .map(|style| menu.render(style, max_height_in_lines, window, cx))
10516    }
10517
10518    fn render_context_menu_aside(
10519        &mut self,
10520        max_size: Size<Pixels>,
10521        window: &mut Window,
10522        cx: &mut Context<Editor>,
10523    ) -> Option<AnyElement> {
10524        self.context_menu.borrow_mut().as_mut().and_then(|menu| {
10525            if menu.visible() {
10526                menu.render_aside(max_size, window, cx)
10527            } else {
10528                None
10529            }
10530        })
10531    }
10532
10533    fn hide_context_menu(
10534        &mut self,
10535        window: &mut Window,
10536        cx: &mut Context<Self>,
10537    ) -> Option<CodeContextMenu> {
10538        cx.notify();
10539        self.completion_tasks.clear();
10540        let context_menu = self.context_menu.borrow_mut().take();
10541        self.stale_edit_prediction_in_menu.take();
10542        self.update_visible_edit_prediction(window, cx);
10543        if let Some(CodeContextMenu::Completions(_)) = &context_menu
10544            && let Some(completion_provider) = &self.completion_provider
10545        {
10546            completion_provider.selection_changed(None, window, cx);
10547        }
10548        context_menu
10549    }
10550
10551    fn show_snippet_choices(
10552        &mut self,
10553        choices: &Vec<String>,
10554        selection: Range<Anchor>,
10555        cx: &mut Context<Self>,
10556    ) {
10557        let Some((_, buffer, _)) = self
10558            .buffer()
10559            .read(cx)
10560            .excerpt_containing(selection.start, cx)
10561        else {
10562            return;
10563        };
10564        let Some((_, end_buffer, _)) = self.buffer().read(cx).excerpt_containing(selection.end, cx)
10565        else {
10566            return;
10567        };
10568        if buffer != end_buffer {
10569            log::error!("expected anchor range to have matching buffer IDs");
10570            return;
10571        }
10572
10573        let id = post_inc(&mut self.next_completion_id);
10574        let snippet_sort_order = EditorSettings::get_global(cx).snippet_sort_order;
10575        let mut context_menu = self.context_menu.borrow_mut();
10576        let old_menu = context_menu.take();
10577        *context_menu = Some(CodeContextMenu::Completions(
10578            CompletionsMenu::new_snippet_choices(
10579                id,
10580                true,
10581                choices,
10582                selection,
10583                buffer,
10584                old_menu.map(|menu| menu.primary_scroll_handle()),
10585                snippet_sort_order,
10586            ),
10587        ));
10588    }
10589
10590    pub fn insert_snippet(
10591        &mut self,
10592        insertion_ranges: &[Range<MultiBufferOffset>],
10593        snippet: Snippet,
10594        window: &mut Window,
10595        cx: &mut Context<Self>,
10596    ) -> Result<()> {
10597        struct Tabstop<T> {
10598            is_end_tabstop: bool,
10599            ranges: Vec<Range<T>>,
10600            choices: Option<Vec<String>>,
10601        }
10602
10603        let tabstops = self.buffer.update(cx, |buffer, cx| {
10604            let snippet_text: Arc<str> = snippet.text.clone().into();
10605            let edits = insertion_ranges
10606                .iter()
10607                .cloned()
10608                .map(|range| (range, snippet_text.clone()));
10609            let autoindent_mode = AutoindentMode::Block {
10610                original_indent_columns: Vec::new(),
10611            };
10612            buffer.edit(edits, Some(autoindent_mode), cx);
10613
10614            let snapshot = &*buffer.read(cx);
10615            let snippet = &snippet;
10616            snippet
10617                .tabstops
10618                .iter()
10619                .map(|tabstop| {
10620                    let is_end_tabstop = tabstop.ranges.first().is_some_and(|tabstop| {
10621                        tabstop.is_empty() && tabstop.start == snippet.text.len() as isize
10622                    });
10623                    let mut tabstop_ranges = tabstop
10624                        .ranges
10625                        .iter()
10626                        .flat_map(|tabstop_range| {
10627                            let mut delta = 0_isize;
10628                            insertion_ranges.iter().map(move |insertion_range| {
10629                                let insertion_start = insertion_range.start + delta;
10630                                delta += snippet.text.len() as isize
10631                                    - (insertion_range.end - insertion_range.start) as isize;
10632
10633                                let start =
10634                                    (insertion_start + tabstop_range.start).min(snapshot.len());
10635                                let end = (insertion_start + tabstop_range.end).min(snapshot.len());
10636                                snapshot.anchor_before(start)..snapshot.anchor_after(end)
10637                            })
10638                        })
10639                        .collect::<Vec<_>>();
10640                    tabstop_ranges.sort_unstable_by(|a, b| a.start.cmp(&b.start, snapshot));
10641
10642                    Tabstop {
10643                        is_end_tabstop,
10644                        ranges: tabstop_ranges,
10645                        choices: tabstop.choices.clone(),
10646                    }
10647                })
10648                .collect::<Vec<_>>()
10649        });
10650        if let Some(tabstop) = tabstops.first() {
10651            self.change_selections(Default::default(), window, cx, |s| {
10652                // Reverse order so that the first range is the newest created selection.
10653                // Completions will use it and autoscroll will prioritize it.
10654                s.select_ranges(tabstop.ranges.iter().rev().cloned());
10655            });
10656
10657            if let Some(choices) = &tabstop.choices
10658                && let Some(selection) = tabstop.ranges.first()
10659            {
10660                self.show_snippet_choices(choices, selection.clone(), cx)
10661            }
10662
10663            // If we're already at the last tabstop and it's at the end of the snippet,
10664            // we're done, we don't need to keep the state around.
10665            if !tabstop.is_end_tabstop {
10666                let choices = tabstops
10667                    .iter()
10668                    .map(|tabstop| tabstop.choices.clone())
10669                    .collect();
10670
10671                let ranges = tabstops
10672                    .into_iter()
10673                    .map(|tabstop| tabstop.ranges)
10674                    .collect::<Vec<_>>();
10675
10676                self.snippet_stack.push(SnippetState {
10677                    active_index: 0,
10678                    ranges,
10679                    choices,
10680                });
10681            }
10682
10683            // Check whether the just-entered snippet ends with an auto-closable bracket.
10684            if self.autoclose_regions.is_empty() {
10685                let snapshot = self.buffer.read(cx).snapshot(cx);
10686                for selection in &mut self.selections.all::<Point>(&self.display_snapshot(cx)) {
10687                    let selection_head = selection.head();
10688                    let Some(scope) = snapshot.language_scope_at(selection_head) else {
10689                        continue;
10690                    };
10691
10692                    let mut bracket_pair = None;
10693                    let max_lookup_length = scope
10694                        .brackets()
10695                        .map(|(pair, _)| {
10696                            pair.start
10697                                .as_str()
10698                                .chars()
10699                                .count()
10700                                .max(pair.end.as_str().chars().count())
10701                        })
10702                        .max();
10703                    if let Some(max_lookup_length) = max_lookup_length {
10704                        let next_text = snapshot
10705                            .chars_at(selection_head)
10706                            .take(max_lookup_length)
10707                            .collect::<String>();
10708                        let prev_text = snapshot
10709                            .reversed_chars_at(selection_head)
10710                            .take(max_lookup_length)
10711                            .collect::<String>();
10712
10713                        for (pair, enabled) in scope.brackets() {
10714                            if enabled
10715                                && pair.close
10716                                && prev_text.starts_with(pair.start.as_str())
10717                                && next_text.starts_with(pair.end.as_str())
10718                            {
10719                                bracket_pair = Some(pair.clone());
10720                                break;
10721                            }
10722                        }
10723                    }
10724
10725                    if let Some(pair) = bracket_pair {
10726                        let snapshot_settings = snapshot.language_settings_at(selection_head, cx);
10727                        let autoclose_enabled =
10728                            self.use_autoclose && snapshot_settings.use_autoclose;
10729                        if autoclose_enabled {
10730                            let start = snapshot.anchor_after(selection_head);
10731                            let end = snapshot.anchor_after(selection_head);
10732                            self.autoclose_regions.push(AutocloseRegion {
10733                                selection_id: selection.id,
10734                                range: start..end,
10735                                pair,
10736                            });
10737                        }
10738                    }
10739                }
10740            }
10741        }
10742        Ok(())
10743    }
10744
10745    pub fn move_to_next_snippet_tabstop(
10746        &mut self,
10747        window: &mut Window,
10748        cx: &mut Context<Self>,
10749    ) -> bool {
10750        self.move_to_snippet_tabstop(Bias::Right, window, cx)
10751    }
10752
10753    pub fn move_to_prev_snippet_tabstop(
10754        &mut self,
10755        window: &mut Window,
10756        cx: &mut Context<Self>,
10757    ) -> bool {
10758        self.move_to_snippet_tabstop(Bias::Left, window, cx)
10759    }
10760
10761    pub fn move_to_snippet_tabstop(
10762        &mut self,
10763        bias: Bias,
10764        window: &mut Window,
10765        cx: &mut Context<Self>,
10766    ) -> bool {
10767        if let Some(mut snippet) = self.snippet_stack.pop() {
10768            match bias {
10769                Bias::Left => {
10770                    if snippet.active_index > 0 {
10771                        snippet.active_index -= 1;
10772                    } else {
10773                        self.snippet_stack.push(snippet);
10774                        return false;
10775                    }
10776                }
10777                Bias::Right => {
10778                    if snippet.active_index + 1 < snippet.ranges.len() {
10779                        snippet.active_index += 1;
10780                    } else {
10781                        self.snippet_stack.push(snippet);
10782                        return false;
10783                    }
10784                }
10785            }
10786            if let Some(current_ranges) = snippet.ranges.get(snippet.active_index) {
10787                self.change_selections(Default::default(), window, cx, |s| {
10788                    // Reverse order so that the first range is the newest created selection.
10789                    // Completions will use it and autoscroll will prioritize it.
10790                    s.select_ranges(current_ranges.iter().rev().cloned())
10791                });
10792
10793                if let Some(choices) = &snippet.choices[snippet.active_index]
10794                    && let Some(selection) = current_ranges.first()
10795                {
10796                    self.show_snippet_choices(choices, selection.clone(), cx);
10797                }
10798
10799                // If snippet state is not at the last tabstop, push it back on the stack
10800                if snippet.active_index + 1 < snippet.ranges.len() {
10801                    self.snippet_stack.push(snippet);
10802                }
10803                return true;
10804            }
10805        }
10806
10807        false
10808    }
10809
10810    pub fn clear(&mut self, window: &mut Window, cx: &mut Context<Self>) {
10811        self.transact(window, cx, |this, window, cx| {
10812            this.select_all(&SelectAll, window, cx);
10813            this.insert("", window, cx);
10814        });
10815    }
10816
10817    pub fn backspace(&mut self, _: &Backspace, window: &mut Window, cx: &mut Context<Self>) {
10818        if self.read_only(cx) {
10819            return;
10820        }
10821        self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
10822        self.transact(window, cx, |this, window, cx| {
10823            this.select_autoclose_pair(window, cx);
10824
10825            let linked_edits = this.linked_edits_for_selections(Arc::from(""), cx);
10826
10827            let display_map = this.display_map.update(cx, |map, cx| map.snapshot(cx));
10828            let mut selections = this.selections.all::<MultiBufferPoint>(&display_map);
10829            for selection in &mut selections {
10830                if selection.is_empty() {
10831                    let old_head = selection.head();
10832                    let mut new_head =
10833                        movement::left(&display_map, old_head.to_display_point(&display_map))
10834                            .to_point(&display_map);
10835                    if let Some((buffer, line_buffer_range)) = display_map
10836                        .buffer_snapshot()
10837                        .buffer_line_for_row(MultiBufferRow(old_head.row))
10838                    {
10839                        let indent_size = buffer.indent_size_for_line(line_buffer_range.start.row);
10840                        let indent_len = match indent_size.kind {
10841                            IndentKind::Space => {
10842                                buffer.settings_at(line_buffer_range.start, cx).tab_size
10843                            }
10844                            IndentKind::Tab => NonZeroU32::new(1).unwrap(),
10845                        };
10846                        if old_head.column <= indent_size.len && old_head.column > 0 {
10847                            let indent_len = indent_len.get();
10848                            new_head = cmp::min(
10849                                new_head,
10850                                MultiBufferPoint::new(
10851                                    old_head.row,
10852                                    ((old_head.column - 1) / indent_len) * indent_len,
10853                                ),
10854                            );
10855                        }
10856                    }
10857
10858                    selection.set_head(new_head, SelectionGoal::None);
10859                }
10860            }
10861
10862            this.change_selections(Default::default(), window, cx, |s| s.select(selections));
10863            this.insert("", window, cx);
10864            linked_edits.apply_with_left_expansion(cx);
10865            this.refresh_edit_prediction(true, false, window, cx);
10866            refresh_linked_ranges(this, window, cx);
10867        });
10868    }
10869
10870    pub fn delete(&mut self, _: &Delete, window: &mut Window, cx: &mut Context<Self>) {
10871        if self.read_only(cx) {
10872            return;
10873        }
10874        self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
10875        self.transact(window, cx, |this, window, cx| {
10876            this.change_selections(Default::default(), window, cx, |s| {
10877                s.move_with(&mut |map, selection| {
10878                    if selection.is_empty() {
10879                        let cursor = movement::right(map, selection.head());
10880                        selection.end = cursor;
10881                        selection.reversed = true;
10882                        selection.goal = SelectionGoal::None;
10883                    }
10884                })
10885            });
10886            let linked_edits = this.linked_edits_for_selections(Arc::from(""), cx);
10887            this.insert("", window, cx);
10888            linked_edits.apply(cx);
10889            this.refresh_edit_prediction(true, false, window, cx);
10890            refresh_linked_ranges(this, window, cx);
10891        });
10892    }
10893
10894    pub fn backtab(&mut self, _: &Backtab, window: &mut Window, cx: &mut Context<Self>) {
10895        if self.mode.is_single_line() {
10896            cx.propagate();
10897            return;
10898        }
10899
10900        self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
10901        if self.move_to_prev_snippet_tabstop(window, cx) {
10902            return;
10903        }
10904        self.outdent(&Outdent, window, cx);
10905    }
10906
10907    pub fn next_snippet_tabstop(
10908        &mut self,
10909        _: &NextSnippetTabstop,
10910        window: &mut Window,
10911        cx: &mut Context<Self>,
10912    ) {
10913        if self.mode.is_single_line() || self.snippet_stack.is_empty() {
10914            cx.propagate();
10915            return;
10916        }
10917
10918        if self.move_to_next_snippet_tabstop(window, cx) {
10919            self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
10920            return;
10921        }
10922        cx.propagate();
10923    }
10924
10925    pub fn previous_snippet_tabstop(
10926        &mut self,
10927        _: &PreviousSnippetTabstop,
10928        window: &mut Window,
10929        cx: &mut Context<Self>,
10930    ) {
10931        if self.mode.is_single_line() || self.snippet_stack.is_empty() {
10932            cx.propagate();
10933            return;
10934        }
10935
10936        if self.move_to_prev_snippet_tabstop(window, cx) {
10937            self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
10938            return;
10939        }
10940        cx.propagate();
10941    }
10942
10943    pub fn tab(&mut self, _: &Tab, window: &mut Window, cx: &mut Context<Self>) {
10944        if self.mode.is_single_line() {
10945            cx.propagate();
10946            return;
10947        }
10948
10949        if self.move_to_next_snippet_tabstop(window, cx) {
10950            self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
10951            return;
10952        }
10953        if self.read_only(cx) {
10954            return;
10955        }
10956        self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
10957        let mut selections = self.selections.all_adjusted(&self.display_snapshot(cx));
10958        let buffer = self.buffer.read(cx);
10959        let snapshot = buffer.snapshot(cx);
10960        let rows_iter = selections.iter().map(|s| s.head().row);
10961        let suggested_indents = snapshot.suggested_indents(rows_iter, cx);
10962
10963        let has_some_cursor_in_whitespace = selections
10964            .iter()
10965            .filter(|selection| selection.is_empty())
10966            .any(|selection| {
10967                let cursor = selection.head();
10968                let current_indent = snapshot.indent_size_for_line(MultiBufferRow(cursor.row));
10969                cursor.column < current_indent.len
10970            });
10971
10972        let mut edits = Vec::new();
10973        let mut prev_edited_row = 0;
10974        let mut row_delta = 0;
10975        for selection in &mut selections {
10976            if selection.start.row != prev_edited_row {
10977                row_delta = 0;
10978            }
10979            prev_edited_row = selection.end.row;
10980
10981            // If cursor is after a list prefix, make selection non-empty to trigger line indent
10982            if selection.is_empty() {
10983                let cursor = selection.head();
10984                let settings = buffer.language_settings_at(cursor, cx);
10985                if settings.indent_list_on_tab {
10986                    if let Some(language) = snapshot.language_scope_at(Point::new(cursor.row, 0)) {
10987                        if is_list_prefix_row(MultiBufferRow(cursor.row), &snapshot, &language) {
10988                            row_delta = Self::indent_selection(
10989                                buffer, &snapshot, selection, &mut edits, row_delta, cx,
10990                            );
10991                            continue;
10992                        }
10993                    }
10994                }
10995            }
10996
10997            // If the selection is non-empty, then increase the indentation of the selected lines.
10998            if !selection.is_empty() {
10999                row_delta =
11000                    Self::indent_selection(buffer, &snapshot, selection, &mut edits, row_delta, cx);
11001                continue;
11002            }
11003
11004            let cursor = selection.head();
11005            let current_indent = snapshot.indent_size_for_line(MultiBufferRow(cursor.row));
11006            if let Some(suggested_indent) =
11007                suggested_indents.get(&MultiBufferRow(cursor.row)).copied()
11008            {
11009                // Don't do anything if already at suggested indent
11010                // and there is any other cursor which is not
11011                if has_some_cursor_in_whitespace
11012                    && cursor.column == current_indent.len
11013                    && current_indent.len == suggested_indent.len
11014                {
11015                    continue;
11016                }
11017
11018                // Adjust line and move cursor to suggested indent
11019                // if cursor is not at suggested indent
11020                if cursor.column < suggested_indent.len
11021                    && cursor.column <= current_indent.len
11022                    && current_indent.len <= suggested_indent.len
11023                {
11024                    selection.start = Point::new(cursor.row, suggested_indent.len);
11025                    selection.end = selection.start;
11026                    if row_delta == 0 {
11027                        edits.extend(Buffer::edit_for_indent_size_adjustment(
11028                            cursor.row,
11029                            current_indent,
11030                            suggested_indent,
11031                        ));
11032                        row_delta = suggested_indent.len - current_indent.len;
11033                    }
11034                    continue;
11035                }
11036
11037                // If current indent is more than suggested indent
11038                // only move cursor to current indent and skip indent
11039                if cursor.column < current_indent.len && current_indent.len > suggested_indent.len {
11040                    selection.start = Point::new(cursor.row, current_indent.len);
11041                    selection.end = selection.start;
11042                    continue;
11043                }
11044            }
11045
11046            // Otherwise, insert a hard or soft tab.
11047            let settings = buffer.language_settings_at(cursor, cx);
11048            let tab_size = if settings.hard_tabs {
11049                IndentSize::tab()
11050            } else {
11051                let tab_size = settings.tab_size.get();
11052                let indent_remainder = snapshot
11053                    .text_for_range(Point::new(cursor.row, 0)..cursor)
11054                    .flat_map(str::chars)
11055                    .fold(row_delta % tab_size, |counter: u32, c| {
11056                        if c == '\t' {
11057                            0
11058                        } else {
11059                            (counter + 1) % tab_size
11060                        }
11061                    });
11062
11063                let chars_to_next_tab_stop = tab_size - indent_remainder;
11064                IndentSize::spaces(chars_to_next_tab_stop)
11065            };
11066            selection.start = Point::new(cursor.row, cursor.column + row_delta + tab_size.len);
11067            selection.end = selection.start;
11068            edits.push((cursor..cursor, tab_size.chars().collect::<String>()));
11069            row_delta += tab_size.len;
11070        }
11071
11072        self.transact(window, cx, |this, window, cx| {
11073            this.buffer.update(cx, |b, cx| b.edit(edits, None, cx));
11074            this.change_selections(Default::default(), window, cx, |s| s.select(selections));
11075            this.refresh_edit_prediction(true, false, window, cx);
11076        });
11077    }
11078
11079    pub fn indent(&mut self, _: &Indent, window: &mut Window, cx: &mut Context<Self>) {
11080        if self.read_only(cx) {
11081            return;
11082        }
11083        if self.mode.is_single_line() {
11084            cx.propagate();
11085            return;
11086        }
11087
11088        self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
11089        let mut selections = self.selections.all::<Point>(&self.display_snapshot(cx));
11090        let mut prev_edited_row = 0;
11091        let mut row_delta = 0;
11092        let mut edits = Vec::new();
11093        let buffer = self.buffer.read(cx);
11094        let snapshot = buffer.snapshot(cx);
11095        for selection in &mut selections {
11096            if selection.start.row != prev_edited_row {
11097                row_delta = 0;
11098            }
11099            prev_edited_row = selection.end.row;
11100
11101            row_delta =
11102                Self::indent_selection(buffer, &snapshot, selection, &mut edits, row_delta, cx);
11103        }
11104
11105        self.transact(window, cx, |this, window, cx| {
11106            this.buffer.update(cx, |b, cx| b.edit(edits, None, cx));
11107            this.change_selections(Default::default(), window, cx, |s| s.select(selections));
11108        });
11109    }
11110
11111    fn indent_selection(
11112        buffer: &MultiBuffer,
11113        snapshot: &MultiBufferSnapshot,
11114        selection: &mut Selection<Point>,
11115        edits: &mut Vec<(Range<Point>, String)>,
11116        delta_for_start_row: u32,
11117        cx: &App,
11118    ) -> u32 {
11119        let settings = buffer.language_settings_at(selection.start, cx);
11120        let tab_size = settings.tab_size.get();
11121        let indent_kind = if settings.hard_tabs {
11122            IndentKind::Tab
11123        } else {
11124            IndentKind::Space
11125        };
11126        let mut start_row = selection.start.row;
11127        let mut end_row = selection.end.row + 1;
11128
11129        // If a selection ends at the beginning of a line, don't indent
11130        // that last line.
11131        if selection.end.column == 0 && selection.end.row > selection.start.row {
11132            end_row -= 1;
11133        }
11134
11135        // Avoid re-indenting a row that has already been indented by a
11136        // previous selection, but still update this selection's column
11137        // to reflect that indentation.
11138        if delta_for_start_row > 0 {
11139            start_row += 1;
11140            selection.start.column += delta_for_start_row;
11141            if selection.end.row == selection.start.row {
11142                selection.end.column += delta_for_start_row;
11143            }
11144        }
11145
11146        let mut delta_for_end_row = 0;
11147        let has_multiple_rows = start_row + 1 != end_row;
11148        for row in start_row..end_row {
11149            let current_indent = snapshot.indent_size_for_line(MultiBufferRow(row));
11150            let indent_delta = match (current_indent.kind, indent_kind) {
11151                (IndentKind::Space, IndentKind::Space) => {
11152                    let columns_to_next_tab_stop = tab_size - (current_indent.len % tab_size);
11153                    IndentSize::spaces(columns_to_next_tab_stop)
11154                }
11155                (IndentKind::Tab, IndentKind::Space) => IndentSize::spaces(tab_size),
11156                (_, IndentKind::Tab) => IndentSize::tab(),
11157            };
11158
11159            let start = if has_multiple_rows || current_indent.len < selection.start.column {
11160                0
11161            } else {
11162                selection.start.column
11163            };
11164            let row_start = Point::new(row, start);
11165            edits.push((
11166                row_start..row_start,
11167                indent_delta.chars().collect::<String>(),
11168            ));
11169
11170            // Update this selection's endpoints to reflect the indentation.
11171            if row == selection.start.row {
11172                selection.start.column += indent_delta.len;
11173            }
11174            if row == selection.end.row {
11175                selection.end.column += indent_delta.len;
11176                delta_for_end_row = indent_delta.len;
11177            }
11178        }
11179
11180        if selection.start.row == selection.end.row {
11181            delta_for_start_row + delta_for_end_row
11182        } else {
11183            delta_for_end_row
11184        }
11185    }
11186
11187    pub fn outdent(&mut self, _: &Outdent, window: &mut Window, cx: &mut Context<Self>) {
11188        if self.read_only(cx) {
11189            return;
11190        }
11191        if self.mode.is_single_line() {
11192            cx.propagate();
11193            return;
11194        }
11195
11196        self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
11197        let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
11198        let selections = self.selections.all::<Point>(&display_map);
11199        let mut deletion_ranges = Vec::new();
11200        let mut last_outdent = None;
11201        {
11202            let buffer = self.buffer.read(cx);
11203            let snapshot = buffer.snapshot(cx);
11204            for selection in &selections {
11205                let settings = buffer.language_settings_at(selection.start, cx);
11206                let tab_size = settings.tab_size.get();
11207                let mut rows = selection.spanned_rows(false, &display_map);
11208
11209                // Avoid re-outdenting a row that has already been outdented by a
11210                // previous selection.
11211                if let Some(last_row) = last_outdent
11212                    && last_row == rows.start
11213                {
11214                    rows.start = rows.start.next_row();
11215                }
11216                let has_multiple_rows = rows.len() > 1;
11217                for row in rows.iter_rows() {
11218                    let indent_size = snapshot.indent_size_for_line(row);
11219                    if indent_size.len > 0 {
11220                        let deletion_len = match indent_size.kind {
11221                            IndentKind::Space => {
11222                                let columns_to_prev_tab_stop = indent_size.len % tab_size;
11223                                if columns_to_prev_tab_stop == 0 {
11224                                    tab_size
11225                                } else {
11226                                    columns_to_prev_tab_stop
11227                                }
11228                            }
11229                            IndentKind::Tab => 1,
11230                        };
11231                        let start = if has_multiple_rows
11232                            || deletion_len > selection.start.column
11233                            || indent_size.len < selection.start.column
11234                        {
11235                            0
11236                        } else {
11237                            selection.start.column - deletion_len
11238                        };
11239                        deletion_ranges.push(
11240                            Point::new(row.0, start)..Point::new(row.0, start + deletion_len),
11241                        );
11242                        last_outdent = Some(row);
11243                    }
11244                }
11245            }
11246        }
11247
11248        self.transact(window, cx, |this, window, cx| {
11249            this.buffer.update(cx, |buffer, cx| {
11250                let empty_str: Arc<str> = Arc::default();
11251                buffer.edit(
11252                    deletion_ranges
11253                        .into_iter()
11254                        .map(|range| (range, empty_str.clone())),
11255                    None,
11256                    cx,
11257                );
11258            });
11259            let selections = this
11260                .selections
11261                .all::<MultiBufferOffset>(&this.display_snapshot(cx));
11262            this.change_selections(Default::default(), window, cx, |s| s.select(selections));
11263        });
11264    }
11265
11266    pub fn autoindent(&mut self, _: &AutoIndent, window: &mut Window, cx: &mut Context<Self>) {
11267        if self.read_only(cx) {
11268            return;
11269        }
11270        if self.mode.is_single_line() {
11271            cx.propagate();
11272            return;
11273        }
11274
11275        self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
11276        let selections = self
11277            .selections
11278            .all::<MultiBufferOffset>(&self.display_snapshot(cx))
11279            .into_iter()
11280            .map(|s| s.range());
11281
11282        self.transact(window, cx, |this, window, cx| {
11283            this.buffer.update(cx, |buffer, cx| {
11284                buffer.autoindent_ranges(selections, cx);
11285            });
11286            let selections = this
11287                .selections
11288                .all::<MultiBufferOffset>(&this.display_snapshot(cx));
11289            this.change_selections(Default::default(), window, cx, |s| s.select(selections));
11290        });
11291    }
11292
11293    pub fn delete_line(&mut self, _: &DeleteLine, window: &mut Window, cx: &mut Context<Self>) {
11294        self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
11295        let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
11296        let selections = self.selections.all::<Point>(&display_map);
11297
11298        let mut new_cursors = Vec::new();
11299        let mut edit_ranges = Vec::new();
11300        let mut selections = selections.iter().peekable();
11301        while let Some(selection) = selections.next() {
11302            let mut rows = selection.spanned_rows(false, &display_map);
11303
11304            // Accumulate contiguous regions of rows that we want to delete.
11305            while let Some(next_selection) = selections.peek() {
11306                let next_rows = next_selection.spanned_rows(false, &display_map);
11307                if next_rows.start <= rows.end {
11308                    rows.end = next_rows.end;
11309                    selections.next().unwrap();
11310                } else {
11311                    break;
11312                }
11313            }
11314
11315            let buffer = display_map.buffer_snapshot();
11316            let mut edit_start = ToOffset::to_offset(&Point::new(rows.start.0, 0), buffer);
11317            let (edit_end, target_row) = if buffer.max_point().row >= rows.end.0 {
11318                // If there's a line after the range, delete the \n from the end of the row range
11319                (
11320                    ToOffset::to_offset(&Point::new(rows.end.0, 0), buffer),
11321                    rows.end,
11322                )
11323            } else {
11324                // If there isn't a line after the range, delete the \n from the line before the
11325                // start of the row range
11326                edit_start = edit_start.saturating_sub_usize(1);
11327                (buffer.len(), rows.start.previous_row())
11328            };
11329
11330            let text_layout_details = self.text_layout_details(window, cx);
11331            let x = display_map.x_for_display_point(
11332                selection.head().to_display_point(&display_map),
11333                &text_layout_details,
11334            );
11335            let row = Point::new(target_row.0, 0)
11336                .to_display_point(&display_map)
11337                .row();
11338            let column = display_map.display_column_for_x(row, x, &text_layout_details);
11339
11340            new_cursors.push((
11341                selection.id,
11342                buffer.anchor_after(DisplayPoint::new(row, column).to_point(&display_map)),
11343                SelectionGoal::None,
11344            ));
11345            edit_ranges.push(edit_start..edit_end);
11346        }
11347
11348        self.transact(window, cx, |this, window, cx| {
11349            let buffer = this.buffer.update(cx, |buffer, cx| {
11350                let empty_str: Arc<str> = Arc::default();
11351                buffer.edit(
11352                    edit_ranges
11353                        .into_iter()
11354                        .map(|range| (range, empty_str.clone())),
11355                    None,
11356                    cx,
11357                );
11358                buffer.snapshot(cx)
11359            });
11360            let new_selections = new_cursors
11361                .into_iter()
11362                .map(|(id, cursor, goal)| {
11363                    let cursor = cursor.to_point(&buffer);
11364                    Selection {
11365                        id,
11366                        start: cursor,
11367                        end: cursor,
11368                        reversed: false,
11369                        goal,
11370                    }
11371                })
11372                .collect();
11373
11374            this.change_selections(Default::default(), window, cx, |s| {
11375                s.select(new_selections);
11376            });
11377        });
11378    }
11379
11380    pub fn join_lines_impl(
11381        &mut self,
11382        insert_whitespace: bool,
11383        window: &mut Window,
11384        cx: &mut Context<Self>,
11385    ) {
11386        if self.read_only(cx) {
11387            return;
11388        }
11389        let mut row_ranges = Vec::<Range<MultiBufferRow>>::new();
11390        for selection in self.selections.all::<Point>(&self.display_snapshot(cx)) {
11391            let start = MultiBufferRow(selection.start.row);
11392            // Treat single line selections as if they include the next line. Otherwise this action
11393            // would do nothing for single line selections individual cursors.
11394            let end = if selection.start.row == selection.end.row {
11395                MultiBufferRow(selection.start.row + 1)
11396            } else if selection.end.column == 0 {
11397                // If the selection ends at the start of a line, it's logically at the end of the
11398                // previous line (plus its newline).
11399                // Don't include the end line unless there's only one line selected.
11400                if selection.start.row + 1 == selection.end.row {
11401                    MultiBufferRow(selection.end.row)
11402                } else {
11403                    MultiBufferRow(selection.end.row - 1)
11404                }
11405            } else {
11406                MultiBufferRow(selection.end.row)
11407            };
11408
11409            if let Some(last_row_range) = row_ranges.last_mut()
11410                && start <= last_row_range.end
11411            {
11412                last_row_range.end = end;
11413                continue;
11414            }
11415            row_ranges.push(start..end);
11416        }
11417
11418        let snapshot = self.buffer.read(cx).snapshot(cx);
11419        let mut cursor_positions = Vec::new();
11420        for row_range in &row_ranges {
11421            let anchor = snapshot.anchor_before(Point::new(
11422                row_range.end.previous_row().0,
11423                snapshot.line_len(row_range.end.previous_row()),
11424            ));
11425            cursor_positions.push(anchor..anchor);
11426        }
11427
11428        self.transact(window, cx, |this, window, cx| {
11429            for row_range in row_ranges.into_iter().rev() {
11430                for row in row_range.iter_rows().rev() {
11431                    let end_of_line = Point::new(row.0, snapshot.line_len(row));
11432                    let next_line_row = row.next_row();
11433                    let indent = snapshot.indent_size_for_line(next_line_row);
11434                    let mut join_start_column = indent.len;
11435
11436                    if let Some(language_scope) =
11437                        snapshot.language_scope_at(Point::new(next_line_row.0, indent.len))
11438                    {
11439                        let line_end =
11440                            Point::new(next_line_row.0, snapshot.line_len(next_line_row));
11441                        let line_text_after_indent = snapshot
11442                            .text_for_range(Point::new(next_line_row.0, indent.len)..line_end)
11443                            .collect::<String>();
11444
11445                        if !line_text_after_indent.is_empty() {
11446                            let block_prefix = language_scope
11447                                .block_comment()
11448                                .map(|c| c.prefix.as_ref())
11449                                .filter(|p| !p.is_empty());
11450                            let doc_prefix = language_scope
11451                                .documentation_comment()
11452                                .map(|c| c.prefix.as_ref())
11453                                .filter(|p| !p.is_empty());
11454                            let all_prefixes = language_scope
11455                                .line_comment_prefixes()
11456                                .iter()
11457                                .map(|p| p.as_ref())
11458                                .chain(block_prefix)
11459                                .chain(doc_prefix)
11460                                .chain(language_scope.unordered_list().iter().map(|p| p.as_ref()));
11461
11462                            let mut longest_prefix_len = None;
11463                            for prefix in all_prefixes {
11464                                let trimmed = prefix.trim_end();
11465                                if line_text_after_indent.starts_with(trimmed) {
11466                                    let candidate_len =
11467                                        if line_text_after_indent.starts_with(prefix) {
11468                                            prefix.len()
11469                                        } else {
11470                                            trimmed.len()
11471                                        };
11472                                    if longest_prefix_len.map_or(true, |len| candidate_len > len) {
11473                                        longest_prefix_len = Some(candidate_len);
11474                                    }
11475                                }
11476                            }
11477
11478                            if let Some(prefix_len) = longest_prefix_len {
11479                                join_start_column =
11480                                    join_start_column.saturating_add(prefix_len as u32);
11481                            }
11482                        }
11483                    }
11484
11485                    let start_of_next_line = Point::new(next_line_row.0, join_start_column);
11486
11487                    let replace = if snapshot.line_len(next_line_row) > join_start_column
11488                        && insert_whitespace
11489                    {
11490                        " "
11491                    } else {
11492                        ""
11493                    };
11494
11495                    this.buffer.update(cx, |buffer, cx| {
11496                        buffer.edit([(end_of_line..start_of_next_line, replace)], None, cx)
11497                    });
11498                }
11499            }
11500
11501            this.change_selections(Default::default(), window, cx, |s| {
11502                s.select_anchor_ranges(cursor_positions)
11503            });
11504        });
11505    }
11506
11507    pub fn join_lines(&mut self, _: &JoinLines, window: &mut Window, cx: &mut Context<Self>) {
11508        self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
11509        self.join_lines_impl(true, window, cx);
11510    }
11511
11512    pub fn sort_lines_case_sensitive(
11513        &mut self,
11514        _: &SortLinesCaseSensitive,
11515        window: &mut Window,
11516        cx: &mut Context<Self>,
11517    ) {
11518        self.manipulate_immutable_lines(window, cx, |lines| lines.sort())
11519    }
11520
11521    pub fn sort_lines_by_length(
11522        &mut self,
11523        _: &SortLinesByLength,
11524        window: &mut Window,
11525        cx: &mut Context<Self>,
11526    ) {
11527        self.manipulate_immutable_lines(window, cx, |lines| {
11528            lines.sort_by_key(|&line| line.chars().count())
11529        })
11530    }
11531
11532    pub fn sort_lines_case_insensitive(
11533        &mut self,
11534        _: &SortLinesCaseInsensitive,
11535        window: &mut Window,
11536        cx: &mut Context<Self>,
11537    ) {
11538        self.manipulate_immutable_lines(window, cx, |lines| {
11539            lines.sort_by_key(|line| line.to_lowercase())
11540        })
11541    }
11542
11543    pub fn unique_lines_case_insensitive(
11544        &mut self,
11545        _: &UniqueLinesCaseInsensitive,
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.to_lowercase()));
11552        })
11553    }
11554
11555    pub fn unique_lines_case_sensitive(
11556        &mut self,
11557        _: &UniqueLinesCaseSensitive,
11558        window: &mut Window,
11559        cx: &mut Context<Self>,
11560    ) {
11561        self.manipulate_immutable_lines(window, cx, |lines| {
11562            let mut seen = HashSet::default();
11563            lines.retain(|line| seen.insert(*line));
11564        })
11565    }
11566
11567    fn enable_wrap_selections_in_tag(&self, cx: &App) -> bool {
11568        let snapshot = self.buffer.read(cx).snapshot(cx);
11569        for selection in self.selections.disjoint_anchors_arc().iter() {
11570            if snapshot
11571                .language_at(selection.start)
11572                .and_then(|lang| lang.config().wrap_characters.as_ref())
11573                .is_some()
11574            {
11575                return true;
11576            }
11577        }
11578        false
11579    }
11580
11581    fn wrap_selections_in_tag(
11582        &mut self,
11583        _: &WrapSelectionsInTag,
11584        window: &mut Window,
11585        cx: &mut Context<Self>,
11586    ) {
11587        self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
11588
11589        let snapshot = self.buffer.read(cx).snapshot(cx);
11590
11591        let mut edits = Vec::new();
11592        let mut boundaries = Vec::new();
11593
11594        for selection in self
11595            .selections
11596            .all_adjusted(&self.display_snapshot(cx))
11597            .iter()
11598        {
11599            let Some(wrap_config) = snapshot
11600                .language_at(selection.start)
11601                .and_then(|lang| lang.config().wrap_characters.clone())
11602            else {
11603                continue;
11604            };
11605
11606            let open_tag = format!("{}{}", wrap_config.start_prefix, wrap_config.start_suffix);
11607            let close_tag = format!("{}{}", wrap_config.end_prefix, wrap_config.end_suffix);
11608
11609            let start_before = snapshot.anchor_before(selection.start);
11610            let end_after = snapshot.anchor_after(selection.end);
11611
11612            edits.push((start_before..start_before, open_tag));
11613            edits.push((end_after..end_after, close_tag));
11614
11615            boundaries.push((
11616                start_before,
11617                end_after,
11618                wrap_config.start_prefix.len(),
11619                wrap_config.end_suffix.len(),
11620            ));
11621        }
11622
11623        if edits.is_empty() {
11624            return;
11625        }
11626
11627        self.transact(window, cx, |this, window, cx| {
11628            let buffer = this.buffer.update(cx, |buffer, cx| {
11629                buffer.edit(edits, None, cx);
11630                buffer.snapshot(cx)
11631            });
11632
11633            let mut new_selections = Vec::with_capacity(boundaries.len() * 2);
11634            for (start_before, end_after, start_prefix_len, end_suffix_len) in
11635                boundaries.into_iter()
11636            {
11637                let open_offset = start_before.to_offset(&buffer) + start_prefix_len;
11638                let close_offset = end_after
11639                    .to_offset(&buffer)
11640                    .saturating_sub_usize(end_suffix_len);
11641                new_selections.push(open_offset..open_offset);
11642                new_selections.push(close_offset..close_offset);
11643            }
11644
11645            this.change_selections(Default::default(), window, cx, |s| {
11646                s.select_ranges(new_selections);
11647            });
11648
11649            this.request_autoscroll(Autoscroll::fit(), cx);
11650        });
11651    }
11652
11653    pub fn toggle_read_only(
11654        &mut self,
11655        _: &workspace::ToggleReadOnlyFile,
11656        _: &mut Window,
11657        cx: &mut Context<Self>,
11658    ) {
11659        if let Some(buffer) = self.buffer.read(cx).as_singleton() {
11660            buffer.update(cx, |buffer, cx| {
11661                buffer.set_capability(
11662                    match buffer.capability() {
11663                        Capability::ReadWrite => Capability::Read,
11664                        Capability::Read => Capability::ReadWrite,
11665                        Capability::ReadOnly => Capability::ReadOnly,
11666                    },
11667                    cx,
11668                );
11669            })
11670        }
11671    }
11672
11673    pub fn reload_file(&mut self, _: &ReloadFile, window: &mut Window, cx: &mut Context<Self>) {
11674        let Some(project) = self.project.clone() else {
11675            return;
11676        };
11677        let task = self.reload(project, window, cx);
11678        self.detach_and_notify_err(task, window, cx);
11679    }
11680
11681    pub fn restore_file(
11682        &mut self,
11683        _: &::git::RestoreFile,
11684        window: &mut Window,
11685        cx: &mut Context<Self>,
11686    ) {
11687        self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
11688        let mut buffer_ids = HashSet::default();
11689        let snapshot = self.buffer().read(cx).snapshot(cx);
11690        for selection in self
11691            .selections
11692            .all::<MultiBufferOffset>(&self.display_snapshot(cx))
11693        {
11694            buffer_ids.extend(snapshot.buffer_ids_for_range(selection.range()))
11695        }
11696
11697        let buffer = self.buffer().read(cx);
11698        let ranges = buffer_ids
11699            .into_iter()
11700            .flat_map(|buffer_id| buffer.excerpt_ranges_for_buffer(buffer_id, cx))
11701            .collect::<Vec<_>>();
11702
11703        self.restore_hunks_in_ranges(ranges, window, cx);
11704    }
11705
11706    pub fn git_restore(&mut self, _: &Restore, window: &mut Window, cx: &mut Context<Self>) {
11707        self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
11708        let selections = self
11709            .selections
11710            .all(&self.display_snapshot(cx))
11711            .into_iter()
11712            .map(|s| s.range())
11713            .collect();
11714        self.restore_hunks_in_ranges(selections, window, cx);
11715    }
11716
11717    /// Restores the diff hunks in the editor's selections and moves the cursor
11718    /// to the next diff hunk. Wraps around to the beginning of the buffer if
11719    /// not all diff hunks are expanded.
11720    pub fn restore_and_next(
11721        &mut self,
11722        _: &::git::RestoreAndNext,
11723        window: &mut Window,
11724        cx: &mut Context<Self>,
11725    ) {
11726        let selections = self
11727            .selections
11728            .all(&self.display_snapshot(cx))
11729            .into_iter()
11730            .map(|selection| selection.range())
11731            .collect();
11732
11733        self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
11734        self.restore_hunks_in_ranges(selections, window, cx);
11735
11736        let all_diff_hunks_expanded = self.buffer().read(cx).all_diff_hunks_expanded();
11737        let wrap_around = !all_diff_hunks_expanded;
11738        let snapshot = self.snapshot(window, cx);
11739        let position = self
11740            .selections
11741            .newest::<Point>(&snapshot.display_snapshot)
11742            .head();
11743
11744        self.go_to_hunk_before_or_after_position(
11745            &snapshot,
11746            position,
11747            Direction::Next,
11748            wrap_around,
11749            window,
11750            cx,
11751        );
11752    }
11753
11754    pub fn restore_hunks_in_ranges(
11755        &mut self,
11756        ranges: Vec<Range<Point>>,
11757        window: &mut Window,
11758        cx: &mut Context<Editor>,
11759    ) {
11760        if self.delegate_stage_and_restore {
11761            let hunks = self.snapshot(window, cx).hunks_for_ranges(ranges);
11762            if !hunks.is_empty() {
11763                cx.emit(EditorEvent::RestoreRequested { hunks });
11764            }
11765            return;
11766        }
11767        let hunks = self.snapshot(window, cx).hunks_for_ranges(ranges);
11768        self.transact(window, cx, |editor, window, cx| {
11769            editor.restore_diff_hunks(hunks, cx);
11770            editor.change_selections(SelectionEffects::no_scroll(), window, cx, |selections| {
11771                selections.refresh()
11772            });
11773        });
11774    }
11775
11776    pub(crate) fn restore_diff_hunks(&self, hunks: Vec<MultiBufferDiffHunk>, cx: &mut App) {
11777        let mut revert_changes = HashMap::default();
11778        let chunk_by = hunks.into_iter().chunk_by(|hunk| hunk.buffer_id);
11779        for (buffer_id, hunks) in &chunk_by {
11780            let hunks = hunks.collect::<Vec<_>>();
11781            for hunk in &hunks {
11782                self.prepare_restore_change(&mut revert_changes, hunk, cx);
11783            }
11784            self.do_stage_or_unstage(false, buffer_id, hunks.into_iter(), cx);
11785        }
11786        if !revert_changes.is_empty() {
11787            self.buffer().update(cx, |multi_buffer, cx| {
11788                for (buffer_id, changes) in revert_changes {
11789                    if let Some(buffer) = multi_buffer.buffer(buffer_id) {
11790                        buffer.update(cx, |buffer, cx| {
11791                            buffer.edit(
11792                                changes
11793                                    .into_iter()
11794                                    .map(|(range, text)| (range, text.to_string())),
11795                                None,
11796                                cx,
11797                            );
11798                        });
11799                    }
11800                }
11801            });
11802        }
11803    }
11804
11805    pub fn status_for_buffer_id(&self, buffer_id: BufferId, cx: &App) -> Option<FileStatus> {
11806        if let Some(status) = self
11807            .addons
11808            .iter()
11809            .find_map(|(_, addon)| addon.override_status_for_buffer_id(buffer_id, cx))
11810        {
11811            return Some(status);
11812        }
11813        self.project
11814            .as_ref()?
11815            .read(cx)
11816            .status_for_buffer_id(buffer_id, cx)
11817    }
11818
11819    pub fn open_active_item_in_terminal(
11820        &mut self,
11821        _: &OpenInTerminal,
11822        window: &mut Window,
11823        cx: &mut Context<Self>,
11824    ) {
11825        if let Some(working_directory) = self.active_excerpt(cx).and_then(|(_, buffer, _)| {
11826            let project_path = buffer.read(cx).project_path(cx)?;
11827            let project = self.project()?.read(cx);
11828            let entry = project.entry_for_path(&project_path, cx)?;
11829            let parent = match &entry.canonical_path {
11830                Some(canonical_path) => canonical_path.to_path_buf(),
11831                None => project.absolute_path(&project_path, cx)?,
11832            }
11833            .parent()?
11834            .to_path_buf();
11835            Some(parent)
11836        }) {
11837            window.dispatch_action(
11838                OpenTerminal {
11839                    working_directory,
11840                    local: false,
11841                }
11842                .boxed_clone(),
11843                cx,
11844            );
11845        }
11846    }
11847
11848    fn set_breakpoint_context_menu(
11849        &mut self,
11850        display_row: DisplayRow,
11851        position: Option<Anchor>,
11852        clicked_point: gpui::Point<Pixels>,
11853        window: &mut Window,
11854        cx: &mut Context<Self>,
11855    ) {
11856        let source = self
11857            .buffer
11858            .read(cx)
11859            .snapshot(cx)
11860            .anchor_before(Point::new(display_row.0, 0u32));
11861
11862        let context_menu = self.breakpoint_context_menu(position.unwrap_or(source), window, cx);
11863
11864        self.mouse_context_menu = MouseContextMenu::pinned_to_editor(
11865            self,
11866            source,
11867            clicked_point,
11868            context_menu,
11869            window,
11870            cx,
11871        );
11872    }
11873
11874    fn add_edit_breakpoint_block(
11875        &mut self,
11876        anchor: Anchor,
11877        breakpoint: &Breakpoint,
11878        edit_action: BreakpointPromptEditAction,
11879        window: &mut Window,
11880        cx: &mut Context<Self>,
11881    ) {
11882        let weak_editor = cx.weak_entity();
11883        let bp_prompt = cx.new(|cx| {
11884            BreakpointPromptEditor::new(
11885                weak_editor,
11886                anchor,
11887                breakpoint.clone(),
11888                edit_action,
11889                window,
11890                cx,
11891            )
11892        });
11893
11894        let height = bp_prompt.update(cx, |this, cx| {
11895            this.prompt
11896                .update(cx, |prompt, cx| prompt.max_point(cx).row().0 + 1 + 2)
11897        });
11898        let cloned_prompt = bp_prompt.clone();
11899        let blocks = vec![BlockProperties {
11900            style: BlockStyle::Sticky,
11901            placement: BlockPlacement::Above(anchor),
11902            height: Some(height),
11903            render: Arc::new(move |cx| {
11904                *cloned_prompt.read(cx).editor_margins.lock() = *cx.margins;
11905                cloned_prompt.clone().into_any_element()
11906            }),
11907            priority: 0,
11908        }];
11909
11910        let focus_handle = bp_prompt.focus_handle(cx);
11911        window.focus(&focus_handle, cx);
11912
11913        let block_ids = self.insert_blocks(blocks, None, cx);
11914        bp_prompt.update(cx, |prompt, _| {
11915            prompt.add_block_ids(block_ids);
11916        });
11917    }
11918
11919    pub(crate) fn breakpoint_at_row(
11920        &self,
11921        row: u32,
11922        window: &mut Window,
11923        cx: &mut Context<Self>,
11924    ) -> Option<(Anchor, Breakpoint)> {
11925        let snapshot = self.snapshot(window, cx);
11926        let breakpoint_position = snapshot.buffer_snapshot().anchor_before(Point::new(row, 0));
11927
11928        self.breakpoint_at_anchor(breakpoint_position, &snapshot, cx)
11929    }
11930
11931    pub(crate) fn breakpoint_at_anchor(
11932        &self,
11933        breakpoint_position: Anchor,
11934        snapshot: &EditorSnapshot,
11935        cx: &mut Context<Self>,
11936    ) -> Option<(Anchor, Breakpoint)> {
11937        let buffer = self
11938            .buffer
11939            .read(cx)
11940            .buffer_for_anchor(breakpoint_position, cx)?;
11941
11942        let enclosing_excerpt = breakpoint_position.excerpt_id;
11943        let buffer_snapshot = buffer.read(cx).snapshot();
11944
11945        let row = buffer_snapshot
11946            .summary_for_anchor::<text::PointUtf16>(&breakpoint_position.text_anchor)
11947            .row;
11948
11949        let line_len = snapshot.buffer_snapshot().line_len(MultiBufferRow(row));
11950        let anchor_end = snapshot
11951            .buffer_snapshot()
11952            .anchor_after(Point::new(row, line_len));
11953
11954        self.breakpoint_store
11955            .as_ref()?
11956            .read_with(cx, |breakpoint_store, cx| {
11957                breakpoint_store
11958                    .breakpoints(
11959                        &buffer,
11960                        Some(breakpoint_position.text_anchor..anchor_end.text_anchor),
11961                        &buffer_snapshot,
11962                        cx,
11963                    )
11964                    .next()
11965                    .and_then(|(bp, _)| {
11966                        let breakpoint_row = buffer_snapshot
11967                            .summary_for_anchor::<text::PointUtf16>(&bp.position)
11968                            .row;
11969
11970                        if breakpoint_row == row {
11971                            snapshot
11972                                .buffer_snapshot()
11973                                .anchor_in_excerpt(enclosing_excerpt, bp.position)
11974                                .map(|position| (position, bp.bp.clone()))
11975                        } else {
11976                            None
11977                        }
11978                    })
11979            })
11980    }
11981
11982    pub fn edit_log_breakpoint(
11983        &mut self,
11984        _: &EditLogBreakpoint,
11985        window: &mut Window,
11986        cx: &mut Context<Self>,
11987    ) {
11988        if self.breakpoint_store.is_none() {
11989            return;
11990        }
11991
11992        for (anchor, breakpoint) in self.breakpoints_at_cursors(window, cx) {
11993            let breakpoint = breakpoint.unwrap_or_else(|| Breakpoint {
11994                message: None,
11995                state: BreakpointState::Enabled,
11996                condition: None,
11997                hit_condition: None,
11998            });
11999
12000            self.add_edit_breakpoint_block(
12001                anchor,
12002                &breakpoint,
12003                BreakpointPromptEditAction::Log,
12004                window,
12005                cx,
12006            );
12007        }
12008    }
12009
12010    fn breakpoints_at_cursors(
12011        &self,
12012        window: &mut Window,
12013        cx: &mut Context<Self>,
12014    ) -> Vec<(Anchor, Option<Breakpoint>)> {
12015        let snapshot = self.snapshot(window, cx);
12016        let cursors = self
12017            .selections
12018            .disjoint_anchors_arc()
12019            .iter()
12020            .map(|selection| {
12021                let cursor_position: Point = selection.head().to_point(&snapshot.buffer_snapshot());
12022
12023                let breakpoint_position = self
12024                    .breakpoint_at_row(cursor_position.row, window, cx)
12025                    .map(|bp| bp.0)
12026                    .unwrap_or_else(|| {
12027                        snapshot
12028                            .display_snapshot
12029                            .buffer_snapshot()
12030                            .anchor_after(Point::new(cursor_position.row, 0))
12031                    });
12032
12033                let breakpoint = self
12034                    .breakpoint_at_anchor(breakpoint_position, &snapshot, cx)
12035                    .map(|(anchor, breakpoint)| (anchor, Some(breakpoint)));
12036
12037                breakpoint.unwrap_or_else(|| (breakpoint_position, None))
12038            })
12039            // 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.
12040            .collect::<HashMap<Anchor, _>>();
12041
12042        cursors.into_iter().collect()
12043    }
12044
12045    pub fn enable_breakpoint(
12046        &mut self,
12047        _: &crate::actions::EnableBreakpoint,
12048        window: &mut Window,
12049        cx: &mut Context<Self>,
12050    ) {
12051        if self.breakpoint_store.is_none() {
12052            return;
12053        }
12054
12055        for (anchor, breakpoint) in self.breakpoints_at_cursors(window, cx) {
12056            let Some(breakpoint) = breakpoint.filter(|breakpoint| breakpoint.is_disabled()) else {
12057                continue;
12058            };
12059            self.edit_breakpoint_at_anchor(
12060                anchor,
12061                breakpoint,
12062                BreakpointEditAction::InvertState,
12063                cx,
12064            );
12065        }
12066    }
12067
12068    pub fn align_selections(
12069        &mut self,
12070        _: &crate::actions::AlignSelections,
12071        window: &mut Window,
12072        cx: &mut Context<Self>,
12073    ) {
12074        self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
12075
12076        let display_snapshot = self.display_snapshot(cx);
12077
12078        struct CursorData {
12079            anchor: Anchor,
12080            point: Point,
12081        }
12082        let cursor_data: Vec<CursorData> = self
12083            .selections
12084            .disjoint_anchors()
12085            .iter()
12086            .map(|selection| {
12087                let anchor = if selection.reversed {
12088                    selection.head()
12089                } else {
12090                    selection.tail()
12091                };
12092                CursorData {
12093                    anchor: anchor,
12094                    point: anchor.to_point(&display_snapshot.buffer_snapshot()),
12095                }
12096            })
12097            .collect();
12098
12099        let rows_anchors_count: Vec<usize> = cursor_data
12100            .iter()
12101            .map(|cursor| cursor.point.row)
12102            .chunk_by(|&row| row)
12103            .into_iter()
12104            .map(|(_, group)| group.count())
12105            .collect();
12106        let max_columns = rows_anchors_count.iter().max().copied().unwrap_or(0);
12107        let mut rows_column_offset = vec![0; rows_anchors_count.len()];
12108        let mut edits = Vec::new();
12109
12110        for column_idx in 0..max_columns {
12111            let mut cursor_index = 0;
12112
12113            // Calculate target_column => position that the selections will go
12114            let mut target_column = 0;
12115            for (row_idx, cursor_count) in rows_anchors_count.iter().enumerate() {
12116                // Skip rows that don't have this column
12117                if column_idx >= *cursor_count {
12118                    cursor_index += cursor_count;
12119                    continue;
12120                }
12121
12122                let point = &cursor_data[cursor_index + column_idx].point;
12123                let adjusted_column = point.column + rows_column_offset[row_idx];
12124                if adjusted_column > target_column {
12125                    target_column = adjusted_column;
12126                }
12127                cursor_index += cursor_count;
12128            }
12129
12130            // Collect edits for this column
12131            cursor_index = 0;
12132            for (row_idx, cursor_count) in rows_anchors_count.iter().enumerate() {
12133                // Skip rows that don't have this column
12134                if column_idx >= *cursor_count {
12135                    cursor_index += *cursor_count;
12136                    continue;
12137                }
12138
12139                let point = &cursor_data[cursor_index + column_idx].point;
12140                let spaces_needed = target_column - point.column - rows_column_offset[row_idx];
12141                if spaces_needed > 0 {
12142                    let anchor = cursor_data[cursor_index + column_idx]
12143                        .anchor
12144                        .bias_left(&display_snapshot);
12145                    edits.push((anchor..anchor, " ".repeat(spaces_needed as usize)));
12146                }
12147                rows_column_offset[row_idx] += spaces_needed;
12148
12149                cursor_index += *cursor_count;
12150            }
12151        }
12152
12153        if !edits.is_empty() {
12154            self.transact(window, cx, |editor, _window, cx| {
12155                editor.edit(edits, cx);
12156            });
12157        }
12158    }
12159
12160    pub fn disable_breakpoint(
12161        &mut self,
12162        _: &crate::actions::DisableBreakpoint,
12163        window: &mut Window,
12164        cx: &mut Context<Self>,
12165    ) {
12166        if self.breakpoint_store.is_none() {
12167            return;
12168        }
12169
12170        for (anchor, breakpoint) in self.breakpoints_at_cursors(window, cx) {
12171            let Some(breakpoint) = breakpoint.filter(|breakpoint| breakpoint.is_enabled()) else {
12172                continue;
12173            };
12174            self.edit_breakpoint_at_anchor(
12175                anchor,
12176                breakpoint,
12177                BreakpointEditAction::InvertState,
12178                cx,
12179            );
12180        }
12181    }
12182
12183    pub fn toggle_breakpoint(
12184        &mut self,
12185        _: &crate::actions::ToggleBreakpoint,
12186        window: &mut Window,
12187        cx: &mut Context<Self>,
12188    ) {
12189        if self.breakpoint_store.is_none() {
12190            return;
12191        }
12192
12193        let snapshot = self.snapshot(window, cx);
12194        for (anchor, breakpoint) in self.breakpoints_at_cursors(window, cx) {
12195            if self.gutter_breakpoint_indicator.0.is_some() {
12196                let display_row = anchor
12197                    .to_point(snapshot.buffer_snapshot())
12198                    .to_display_point(&snapshot.display_snapshot)
12199                    .row();
12200                self.update_breakpoint_collision_on_toggle(
12201                    display_row,
12202                    &BreakpointEditAction::Toggle,
12203                );
12204            }
12205
12206            if let Some(breakpoint) = breakpoint {
12207                self.edit_breakpoint_at_anchor(
12208                    anchor,
12209                    breakpoint,
12210                    BreakpointEditAction::Toggle,
12211                    cx,
12212                );
12213            } else {
12214                self.edit_breakpoint_at_anchor(
12215                    anchor,
12216                    Breakpoint::new_standard(),
12217                    BreakpointEditAction::Toggle,
12218                    cx,
12219                );
12220            }
12221        }
12222    }
12223
12224    fn update_breakpoint_collision_on_toggle(
12225        &mut self,
12226        display_row: DisplayRow,
12227        edit_action: &BreakpointEditAction,
12228    ) {
12229        if let Some(ref mut breakpoint_indicator) = self.gutter_breakpoint_indicator.0 {
12230            if breakpoint_indicator.display_row == display_row
12231                && matches!(edit_action, BreakpointEditAction::Toggle)
12232            {
12233                breakpoint_indicator.collides_with_existing_breakpoint =
12234                    !breakpoint_indicator.collides_with_existing_breakpoint;
12235            }
12236        }
12237    }
12238
12239    pub fn edit_breakpoint_at_anchor(
12240        &mut self,
12241        breakpoint_position: Anchor,
12242        breakpoint: Breakpoint,
12243        edit_action: BreakpointEditAction,
12244        cx: &mut Context<Self>,
12245    ) {
12246        let Some(breakpoint_store) = &self.breakpoint_store else {
12247            return;
12248        };
12249
12250        let Some(buffer) = self
12251            .buffer
12252            .read(cx)
12253            .buffer_for_anchor(breakpoint_position, cx)
12254        else {
12255            return;
12256        };
12257
12258        breakpoint_store.update(cx, |breakpoint_store, cx| {
12259            breakpoint_store.toggle_breakpoint(
12260                buffer,
12261                BreakpointWithPosition {
12262                    position: breakpoint_position.text_anchor,
12263                    bp: breakpoint,
12264                },
12265                edit_action,
12266                cx,
12267            );
12268        });
12269
12270        cx.notify();
12271    }
12272
12273    #[cfg(any(test, feature = "test-support"))]
12274    pub fn breakpoint_store(&self) -> Option<Entity<BreakpointStore>> {
12275        self.breakpoint_store.clone()
12276    }
12277
12278    pub fn prepare_restore_change(
12279        &self,
12280        revert_changes: &mut HashMap<BufferId, Vec<(Range<text::Anchor>, Rope)>>,
12281        hunk: &MultiBufferDiffHunk,
12282        cx: &mut App,
12283    ) -> Option<()> {
12284        if hunk.is_created_file() {
12285            return None;
12286        }
12287        let multi_buffer = self.buffer.read(cx);
12288        let multi_buffer_snapshot = multi_buffer.snapshot(cx);
12289        let diff_snapshot = multi_buffer_snapshot.diff_for_buffer_id(hunk.buffer_id)?;
12290        let original_text = diff_snapshot
12291            .base_text()
12292            .as_rope()
12293            .slice(hunk.diff_base_byte_range.start.0..hunk.diff_base_byte_range.end.0);
12294        let buffer = multi_buffer.buffer(hunk.buffer_id)?;
12295        let buffer = buffer.read(cx);
12296        let buffer_snapshot = buffer.snapshot();
12297        let buffer_revert_changes = revert_changes.entry(buffer.remote_id()).or_default();
12298        if let Err(i) = buffer_revert_changes.binary_search_by(|probe| {
12299            probe
12300                .0
12301                .start
12302                .cmp(&hunk.buffer_range.start, &buffer_snapshot)
12303                .then(probe.0.end.cmp(&hunk.buffer_range.end, &buffer_snapshot))
12304        }) {
12305            buffer_revert_changes.insert(i, (hunk.buffer_range.clone(), original_text));
12306            Some(())
12307        } else {
12308            None
12309        }
12310    }
12311
12312    pub fn reverse_lines(&mut self, _: &ReverseLines, window: &mut Window, cx: &mut Context<Self>) {
12313        self.manipulate_immutable_lines(window, cx, |lines| lines.reverse())
12314    }
12315
12316    pub fn shuffle_lines(&mut self, _: &ShuffleLines, window: &mut Window, cx: &mut Context<Self>) {
12317        self.manipulate_immutable_lines(window, cx, |lines| lines.shuffle(&mut rand::rng()))
12318    }
12319
12320    pub fn rotate_selections_forward(
12321        &mut self,
12322        _: &RotateSelectionsForward,
12323        window: &mut Window,
12324        cx: &mut Context<Self>,
12325    ) {
12326        self.rotate_selections(window, cx, false)
12327    }
12328
12329    pub fn rotate_selections_backward(
12330        &mut self,
12331        _: &RotateSelectionsBackward,
12332        window: &mut Window,
12333        cx: &mut Context<Self>,
12334    ) {
12335        self.rotate_selections(window, cx, true)
12336    }
12337
12338    fn rotate_selections(&mut self, window: &mut Window, cx: &mut Context<Self>, reverse: bool) {
12339        self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
12340        let display_snapshot = self.display_snapshot(cx);
12341        let selections = self.selections.all::<MultiBufferOffset>(&display_snapshot);
12342
12343        if selections.len() < 2 {
12344            return;
12345        }
12346
12347        let (edits, new_selections) = {
12348            let buffer = self.buffer.read(cx).read(cx);
12349            let has_selections = selections.iter().any(|s| !s.is_empty());
12350            if has_selections {
12351                let mut selected_texts: Vec<String> = selections
12352                    .iter()
12353                    .map(|selection| {
12354                        buffer
12355                            .text_for_range(selection.start..selection.end)
12356                            .collect()
12357                    })
12358                    .collect();
12359
12360                if reverse {
12361                    selected_texts.rotate_left(1);
12362                } else {
12363                    selected_texts.rotate_right(1);
12364                }
12365
12366                let mut offset_delta: i64 = 0;
12367                let mut new_selections = Vec::new();
12368                let edits: Vec<_> = selections
12369                    .iter()
12370                    .zip(selected_texts.iter())
12371                    .map(|(selection, new_text)| {
12372                        let old_len = (selection.end.0 - selection.start.0) as i64;
12373                        let new_len = new_text.len() as i64;
12374                        let adjusted_start =
12375                            MultiBufferOffset((selection.start.0 as i64 + offset_delta) as usize);
12376                        let adjusted_end =
12377                            MultiBufferOffset((adjusted_start.0 as i64 + new_len) as usize);
12378
12379                        new_selections.push(Selection {
12380                            id: selection.id,
12381                            start: adjusted_start,
12382                            end: adjusted_end,
12383                            reversed: selection.reversed,
12384                            goal: selection.goal,
12385                        });
12386
12387                        offset_delta += new_len - old_len;
12388                        (selection.start..selection.end, new_text.clone())
12389                    })
12390                    .collect();
12391                (edits, new_selections)
12392            } else {
12393                let mut all_rows: Vec<u32> = selections
12394                    .iter()
12395                    .map(|selection| buffer.offset_to_point(selection.start).row)
12396                    .collect();
12397                all_rows.sort_unstable();
12398                all_rows.dedup();
12399
12400                if all_rows.len() < 2 {
12401                    return;
12402                }
12403
12404                let line_ranges: Vec<Range<MultiBufferOffset>> = all_rows
12405                    .iter()
12406                    .map(|&row| {
12407                        let start = Point::new(row, 0);
12408                        let end = Point::new(row, buffer.line_len(MultiBufferRow(row)));
12409                        buffer.point_to_offset(start)..buffer.point_to_offset(end)
12410                    })
12411                    .collect();
12412
12413                let mut line_texts: Vec<String> = line_ranges
12414                    .iter()
12415                    .map(|range| buffer.text_for_range(range.clone()).collect())
12416                    .collect();
12417
12418                if reverse {
12419                    line_texts.rotate_left(1);
12420                } else {
12421                    line_texts.rotate_right(1);
12422                }
12423
12424                let edits = line_ranges
12425                    .iter()
12426                    .zip(line_texts.iter())
12427                    .map(|(range, new_text)| (range.clone(), new_text.clone()))
12428                    .collect();
12429
12430                let num_rows = all_rows.len();
12431                let row_to_index: std::collections::HashMap<u32, usize> = all_rows
12432                    .iter()
12433                    .enumerate()
12434                    .map(|(i, &row)| (row, i))
12435                    .collect();
12436
12437                // Compute new line start offsets after rotation (handles CRLF)
12438                let newline_len = line_ranges[1].start.0 - line_ranges[0].end.0;
12439                let first_line_start = line_ranges[0].start.0;
12440                let mut new_line_starts: Vec<usize> = vec![first_line_start];
12441                for text in line_texts.iter().take(num_rows - 1) {
12442                    let prev_start = *new_line_starts.last().unwrap();
12443                    new_line_starts.push(prev_start + text.len() + newline_len);
12444                }
12445
12446                let new_selections = selections
12447                    .iter()
12448                    .map(|selection| {
12449                        let point = buffer.offset_to_point(selection.start);
12450                        let old_index = row_to_index[&point.row];
12451                        let new_index = if reverse {
12452                            (old_index + num_rows - 1) % num_rows
12453                        } else {
12454                            (old_index + 1) % num_rows
12455                        };
12456                        let new_offset =
12457                            MultiBufferOffset(new_line_starts[new_index] + point.column as usize);
12458                        Selection {
12459                            id: selection.id,
12460                            start: new_offset,
12461                            end: new_offset,
12462                            reversed: selection.reversed,
12463                            goal: selection.goal,
12464                        }
12465                    })
12466                    .collect();
12467
12468                (edits, new_selections)
12469            }
12470        };
12471
12472        self.transact(window, cx, |this, window, cx| {
12473            this.buffer.update(cx, |buffer, cx| {
12474                buffer.edit(edits, None, cx);
12475            });
12476            this.change_selections(Default::default(), window, cx, |s| {
12477                s.select(new_selections);
12478            });
12479        });
12480    }
12481
12482    fn manipulate_lines<M>(
12483        &mut self,
12484        window: &mut Window,
12485        cx: &mut Context<Self>,
12486        mut manipulate: M,
12487    ) where
12488        M: FnMut(&str) -> LineManipulationResult,
12489    {
12490        self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
12491
12492        let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
12493        let buffer = self.buffer.read(cx).snapshot(cx);
12494
12495        let mut edits = Vec::new();
12496
12497        let selections = self.selections.all::<Point>(&display_map);
12498        let mut selections = selections.iter().peekable();
12499        let mut contiguous_row_selections = Vec::new();
12500        let mut new_selections = Vec::new();
12501        let mut added_lines = 0;
12502        let mut removed_lines = 0;
12503
12504        while let Some(selection) = selections.next() {
12505            let (start_row, end_row) = consume_contiguous_rows(
12506                &mut contiguous_row_selections,
12507                selection,
12508                &display_map,
12509                &mut selections,
12510            );
12511
12512            let start_point = Point::new(start_row.0, 0);
12513            let end_point = Point::new(
12514                end_row.previous_row().0,
12515                buffer.line_len(end_row.previous_row()),
12516            );
12517            let text = buffer
12518                .text_for_range(start_point..end_point)
12519                .collect::<String>();
12520
12521            let LineManipulationResult {
12522                new_text,
12523                line_count_before,
12524                line_count_after,
12525            } = manipulate(&text);
12526
12527            edits.push((start_point..end_point, new_text));
12528
12529            // Selections must change based on added and removed line count
12530            let start_row =
12531                MultiBufferRow(start_point.row + added_lines as u32 - removed_lines as u32);
12532            let end_row = MultiBufferRow(start_row.0 + line_count_after.saturating_sub(1) as u32);
12533            new_selections.push(Selection {
12534                id: selection.id,
12535                start: start_row,
12536                end: end_row,
12537                goal: SelectionGoal::None,
12538                reversed: selection.reversed,
12539            });
12540
12541            if line_count_after > line_count_before {
12542                added_lines += line_count_after - line_count_before;
12543            } else if line_count_before > line_count_after {
12544                removed_lines += line_count_before - line_count_after;
12545            }
12546        }
12547
12548        self.transact(window, cx, |this, window, cx| {
12549            let buffer = this.buffer.update(cx, |buffer, cx| {
12550                buffer.edit(edits, None, cx);
12551                buffer.snapshot(cx)
12552            });
12553
12554            // Recalculate offsets on newly edited buffer
12555            let new_selections = new_selections
12556                .iter()
12557                .map(|s| {
12558                    let start_point = Point::new(s.start.0, 0);
12559                    let end_point = Point::new(s.end.0, buffer.line_len(s.end));
12560                    Selection {
12561                        id: s.id,
12562                        start: buffer.point_to_offset(start_point),
12563                        end: buffer.point_to_offset(end_point),
12564                        goal: s.goal,
12565                        reversed: s.reversed,
12566                    }
12567                })
12568                .collect();
12569
12570            this.change_selections(Default::default(), window, cx, |s| {
12571                s.select(new_selections);
12572            });
12573
12574            this.request_autoscroll(Autoscroll::fit(), cx);
12575        });
12576    }
12577
12578    fn manipulate_immutable_lines<Fn>(
12579        &mut self,
12580        window: &mut Window,
12581        cx: &mut Context<Self>,
12582        mut callback: Fn,
12583    ) where
12584        Fn: FnMut(&mut Vec<&str>),
12585    {
12586        self.manipulate_lines(window, cx, |text| {
12587            let mut lines: Vec<&str> = text.split('\n').collect();
12588            let line_count_before = lines.len();
12589
12590            callback(&mut lines);
12591
12592            LineManipulationResult {
12593                new_text: lines.join("\n"),
12594                line_count_before,
12595                line_count_after: lines.len(),
12596            }
12597        });
12598    }
12599
12600    fn manipulate_mutable_lines<Fn>(
12601        &mut self,
12602        window: &mut Window,
12603        cx: &mut Context<Self>,
12604        mut callback: Fn,
12605    ) where
12606        Fn: FnMut(&mut Vec<Cow<'_, str>>),
12607    {
12608        self.manipulate_lines(window, cx, |text| {
12609            let mut lines: Vec<Cow<str>> = text.split('\n').map(Cow::from).collect();
12610            let line_count_before = lines.len();
12611
12612            callback(&mut lines);
12613
12614            LineManipulationResult {
12615                new_text: lines.join("\n"),
12616                line_count_before,
12617                line_count_after: lines.len(),
12618            }
12619        });
12620    }
12621
12622    pub fn convert_indentation_to_spaces(
12623        &mut self,
12624        _: &ConvertIndentationToSpaces,
12625        window: &mut Window,
12626        cx: &mut Context<Self>,
12627    ) {
12628        let settings = self.buffer.read(cx).language_settings(cx);
12629        let tab_size = settings.tab_size.get() as usize;
12630
12631        self.manipulate_mutable_lines(window, cx, |lines| {
12632            // Allocates a reasonably sized scratch buffer once for the whole loop
12633            let mut reindented_line = String::with_capacity(MAX_LINE_LEN);
12634            // Avoids recomputing spaces that could be inserted many times
12635            let space_cache: Vec<Vec<char>> = (1..=tab_size)
12636                .map(|n| IndentSize::spaces(n as u32).chars().collect())
12637                .collect();
12638
12639            for line in lines.iter_mut().filter(|line| !line.is_empty()) {
12640                let mut chars = line.as_ref().chars();
12641                let mut col = 0;
12642                let mut changed = false;
12643
12644                for ch in chars.by_ref() {
12645                    match ch {
12646                        ' ' => {
12647                            reindented_line.push(' ');
12648                            col += 1;
12649                        }
12650                        '\t' => {
12651                            // \t are converted to spaces depending on the current column
12652                            let spaces_len = tab_size - (col % tab_size);
12653                            reindented_line.extend(&space_cache[spaces_len - 1]);
12654                            col += spaces_len;
12655                            changed = true;
12656                        }
12657                        _ => {
12658                            // If we dont append before break, the character is consumed
12659                            reindented_line.push(ch);
12660                            break;
12661                        }
12662                    }
12663                }
12664
12665                if !changed {
12666                    reindented_line.clear();
12667                    continue;
12668                }
12669                // Append the rest of the line and replace old reference with new one
12670                reindented_line.extend(chars);
12671                *line = Cow::Owned(reindented_line.clone());
12672                reindented_line.clear();
12673            }
12674        });
12675    }
12676
12677    pub fn convert_indentation_to_tabs(
12678        &mut self,
12679        _: &ConvertIndentationToTabs,
12680        window: &mut Window,
12681        cx: &mut Context<Self>,
12682    ) {
12683        let settings = self.buffer.read(cx).language_settings(cx);
12684        let tab_size = settings.tab_size.get() as usize;
12685
12686        self.manipulate_mutable_lines(window, cx, |lines| {
12687            // Allocates a reasonably sized buffer once for the whole loop
12688            let mut reindented_line = String::with_capacity(MAX_LINE_LEN);
12689            // Avoids recomputing spaces that could be inserted many times
12690            let space_cache: Vec<Vec<char>> = (1..=tab_size)
12691                .map(|n| IndentSize::spaces(n as u32).chars().collect())
12692                .collect();
12693
12694            for line in lines.iter_mut().filter(|line| !line.is_empty()) {
12695                let mut chars = line.chars();
12696                let mut spaces_count = 0;
12697                let mut first_non_indent_char = None;
12698                let mut changed = false;
12699
12700                for ch in chars.by_ref() {
12701                    match ch {
12702                        ' ' => {
12703                            // Keep track of spaces. Append \t when we reach tab_size
12704                            spaces_count += 1;
12705                            changed = true;
12706                            if spaces_count == tab_size {
12707                                reindented_line.push('\t');
12708                                spaces_count = 0;
12709                            }
12710                        }
12711                        '\t' => {
12712                            reindented_line.push('\t');
12713                            spaces_count = 0;
12714                        }
12715                        _ => {
12716                            // Dont append it yet, we might have remaining spaces
12717                            first_non_indent_char = Some(ch);
12718                            break;
12719                        }
12720                    }
12721                }
12722
12723                if !changed {
12724                    reindented_line.clear();
12725                    continue;
12726                }
12727                // Remaining spaces that didn't make a full tab stop
12728                if spaces_count > 0 {
12729                    reindented_line.extend(&space_cache[spaces_count - 1]);
12730                }
12731                // If we consume an extra character that was not indentation, add it back
12732                if let Some(extra_char) = first_non_indent_char {
12733                    reindented_line.push(extra_char);
12734                }
12735                // Append the rest of the line and replace old reference with new one
12736                reindented_line.extend(chars);
12737                *line = Cow::Owned(reindented_line.clone());
12738                reindented_line.clear();
12739            }
12740        });
12741    }
12742
12743    pub fn convert_to_upper_case(
12744        &mut self,
12745        _: &ConvertToUpperCase,
12746        window: &mut Window,
12747        cx: &mut Context<Self>,
12748    ) {
12749        self.manipulate_text(window, cx, |text| text.to_uppercase())
12750    }
12751
12752    pub fn convert_to_lower_case(
12753        &mut self,
12754        _: &ConvertToLowerCase,
12755        window: &mut Window,
12756        cx: &mut Context<Self>,
12757    ) {
12758        self.manipulate_text(window, cx, |text| text.to_lowercase())
12759    }
12760
12761    pub fn convert_to_title_case(
12762        &mut self,
12763        _: &ConvertToTitleCase,
12764        window: &mut Window,
12765        cx: &mut Context<Self>,
12766    ) {
12767        self.manipulate_text(window, cx, |text| {
12768            Self::convert_text_case(text, Case::Title)
12769        })
12770    }
12771
12772    pub fn convert_to_snake_case(
12773        &mut self,
12774        _: &ConvertToSnakeCase,
12775        window: &mut Window,
12776        cx: &mut Context<Self>,
12777    ) {
12778        self.manipulate_text(window, cx, |text| {
12779            Self::convert_text_case(text, Case::Snake)
12780        })
12781    }
12782
12783    pub fn convert_to_kebab_case(
12784        &mut self,
12785        _: &ConvertToKebabCase,
12786        window: &mut Window,
12787        cx: &mut Context<Self>,
12788    ) {
12789        self.manipulate_text(window, cx, |text| {
12790            Self::convert_text_case(text, Case::Kebab)
12791        })
12792    }
12793
12794    pub fn convert_to_upper_camel_case(
12795        &mut self,
12796        _: &ConvertToUpperCamelCase,
12797        window: &mut Window,
12798        cx: &mut Context<Self>,
12799    ) {
12800        self.manipulate_text(window, cx, |text| {
12801            Self::convert_text_case(text, Case::UpperCamel)
12802        })
12803    }
12804
12805    pub fn convert_to_lower_camel_case(
12806        &mut self,
12807        _: &ConvertToLowerCamelCase,
12808        window: &mut Window,
12809        cx: &mut Context<Self>,
12810    ) {
12811        self.manipulate_text(window, cx, |text| {
12812            Self::convert_text_case(text, Case::Camel)
12813        })
12814    }
12815
12816    pub fn convert_to_opposite_case(
12817        &mut self,
12818        _: &ConvertToOppositeCase,
12819        window: &mut Window,
12820        cx: &mut Context<Self>,
12821    ) {
12822        self.manipulate_text(window, cx, |text| {
12823            text.chars()
12824                .fold(String::with_capacity(text.len()), |mut t, c| {
12825                    if c.is_uppercase() {
12826                        t.extend(c.to_lowercase());
12827                    } else {
12828                        t.extend(c.to_uppercase());
12829                    }
12830                    t
12831                })
12832        })
12833    }
12834
12835    pub fn convert_to_sentence_case(
12836        &mut self,
12837        _: &ConvertToSentenceCase,
12838        window: &mut Window,
12839        cx: &mut Context<Self>,
12840    ) {
12841        self.manipulate_text(window, cx, |text| {
12842            Self::convert_text_case(text, Case::Sentence)
12843        })
12844    }
12845
12846    pub fn toggle_case(&mut self, _: &ToggleCase, window: &mut Window, cx: &mut Context<Self>) {
12847        self.manipulate_text(window, cx, |text| {
12848            let has_upper_case_characters = text.chars().any(|c| c.is_uppercase());
12849            if has_upper_case_characters {
12850                text.to_lowercase()
12851            } else {
12852                text.to_uppercase()
12853            }
12854        })
12855    }
12856
12857    pub fn convert_to_rot13(
12858        &mut self,
12859        _: &ConvertToRot13,
12860        window: &mut Window,
12861        cx: &mut Context<Self>,
12862    ) {
12863        self.manipulate_text(window, cx, |text| {
12864            text.chars()
12865                .map(|c| match c {
12866                    'A'..='M' | 'a'..='m' => ((c as u8) + 13) as char,
12867                    'N'..='Z' | 'n'..='z' => ((c as u8) - 13) as char,
12868                    _ => c,
12869                })
12870                .collect()
12871        })
12872    }
12873
12874    fn convert_text_case(text: &str, case: Case) -> String {
12875        text.lines()
12876            .map(|line| {
12877                let trimmed_start = line.trim_start();
12878                let leading = &line[..line.len() - trimmed_start.len()];
12879                let trimmed = trimmed_start.trim_end();
12880                let trailing = &trimmed_start[trimmed.len()..];
12881                format!("{}{}{}", leading, trimmed.to_case(case), trailing)
12882            })
12883            .join("\n")
12884    }
12885
12886    pub fn convert_to_rot47(
12887        &mut self,
12888        _: &ConvertToRot47,
12889        window: &mut Window,
12890        cx: &mut Context<Self>,
12891    ) {
12892        self.manipulate_text(window, cx, |text| {
12893            text.chars()
12894                .map(|c| {
12895                    let code_point = c as u32;
12896                    if code_point >= 33 && code_point <= 126 {
12897                        return char::from_u32(33 + ((code_point + 14) % 94)).unwrap();
12898                    }
12899                    c
12900                })
12901                .collect()
12902        })
12903    }
12904
12905    fn manipulate_text<Fn>(&mut self, window: &mut Window, cx: &mut Context<Self>, mut callback: Fn)
12906    where
12907        Fn: FnMut(&str) -> String,
12908    {
12909        let buffer = self.buffer.read(cx).snapshot(cx);
12910
12911        let mut new_selections = Vec::new();
12912        let mut edits = Vec::new();
12913        let mut selection_adjustment = 0isize;
12914
12915        for selection in self.selections.all_adjusted(&self.display_snapshot(cx)) {
12916            let selection_is_empty = selection.is_empty();
12917
12918            let (start, end) = if selection_is_empty {
12919                let (word_range, _) = buffer.surrounding_word(selection.start, None);
12920                (word_range.start, word_range.end)
12921            } else {
12922                (
12923                    buffer.point_to_offset(selection.start),
12924                    buffer.point_to_offset(selection.end),
12925                )
12926            };
12927
12928            let text = buffer.text_for_range(start..end).collect::<String>();
12929            let old_length = text.len() as isize;
12930            let text = callback(&text);
12931
12932            new_selections.push(Selection {
12933                start: MultiBufferOffset((start.0 as isize - selection_adjustment) as usize),
12934                end: MultiBufferOffset(
12935                    ((start.0 + text.len()) as isize - selection_adjustment) as usize,
12936                ),
12937                goal: SelectionGoal::None,
12938                id: selection.id,
12939                reversed: selection.reversed,
12940            });
12941
12942            selection_adjustment += old_length - text.len() as isize;
12943
12944            edits.push((start..end, text));
12945        }
12946
12947        self.transact(window, cx, |this, window, cx| {
12948            this.buffer.update(cx, |buffer, cx| {
12949                buffer.edit(edits, None, cx);
12950            });
12951
12952            this.change_selections(Default::default(), window, cx, |s| {
12953                s.select(new_selections);
12954            });
12955
12956            this.request_autoscroll(Autoscroll::fit(), cx);
12957        });
12958    }
12959
12960    pub fn move_selection_on_drop(
12961        &mut self,
12962        selection: &Selection<Anchor>,
12963        target: DisplayPoint,
12964        is_cut: bool,
12965        window: &mut Window,
12966        cx: &mut Context<Self>,
12967    ) {
12968        let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
12969        let buffer = display_map.buffer_snapshot();
12970        let mut edits = Vec::new();
12971        let insert_point = display_map
12972            .clip_point(target, Bias::Left)
12973            .to_point(&display_map);
12974        let text = buffer
12975            .text_for_range(selection.start..selection.end)
12976            .collect::<String>();
12977        if is_cut {
12978            edits.push(((selection.start..selection.end), String::new()));
12979        }
12980        let insert_anchor = buffer.anchor_before(insert_point);
12981        edits.push(((insert_anchor..insert_anchor), text));
12982        let last_edit_start = insert_anchor.bias_left(buffer);
12983        let last_edit_end = insert_anchor.bias_right(buffer);
12984        self.transact(window, cx, |this, window, cx| {
12985            this.buffer.update(cx, |buffer, cx| {
12986                buffer.edit(edits, None, cx);
12987            });
12988            this.change_selections(Default::default(), window, cx, |s| {
12989                s.select_anchor_ranges([last_edit_start..last_edit_end]);
12990            });
12991        });
12992    }
12993
12994    pub fn clear_selection_drag_state(&mut self) {
12995        self.selection_drag_state = SelectionDragState::None;
12996    }
12997
12998    pub fn duplicate(
12999        &mut self,
13000        upwards: bool,
13001        whole_lines: bool,
13002        window: &mut Window,
13003        cx: &mut Context<Self>,
13004    ) {
13005        self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
13006
13007        let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
13008        let buffer = display_map.buffer_snapshot();
13009        let selections = self.selections.all::<Point>(&display_map);
13010
13011        let mut edits = Vec::new();
13012        let mut selections_iter = selections.iter().peekable();
13013        while let Some(selection) = selections_iter.next() {
13014            let mut rows = selection.spanned_rows(false, &display_map);
13015            // duplicate line-wise
13016            if whole_lines || selection.start == selection.end {
13017                // Avoid duplicating the same lines twice.
13018                while let Some(next_selection) = selections_iter.peek() {
13019                    let next_rows = next_selection.spanned_rows(false, &display_map);
13020                    if next_rows.start < rows.end {
13021                        rows.end = next_rows.end;
13022                        selections_iter.next().unwrap();
13023                    } else {
13024                        break;
13025                    }
13026                }
13027
13028                // Copy the text from the selected row region and splice it either at the start
13029                // or end of the region.
13030                let start = Point::new(rows.start.0, 0);
13031                let end = Point::new(
13032                    rows.end.previous_row().0,
13033                    buffer.line_len(rows.end.previous_row()),
13034                );
13035
13036                let mut text = buffer.text_for_range(start..end).collect::<String>();
13037
13038                let insert_location = if upwards {
13039                    // When duplicating upward, we need to insert before the current line.
13040                    // If we're on the last line and it doesn't end with a newline,
13041                    // we need to add a newline before the duplicated content.
13042                    let needs_leading_newline = rows.end.0 >= buffer.max_point().row
13043                        && buffer.max_point().column > 0
13044                        && !text.ends_with('\n');
13045
13046                    if needs_leading_newline {
13047                        text.insert(0, '\n');
13048                        end
13049                    } else {
13050                        text.push('\n');
13051                        Point::new(rows.start.0, 0)
13052                    }
13053                } else {
13054                    text.push('\n');
13055                    start
13056                };
13057                edits.push((insert_location..insert_location, text));
13058            } else {
13059                // duplicate character-wise
13060                let start = selection.start;
13061                let end = selection.end;
13062                let text = buffer.text_for_range(start..end).collect::<String>();
13063                edits.push((selection.end..selection.end, text));
13064            }
13065        }
13066
13067        self.transact(window, cx, |this, window, cx| {
13068            this.buffer.update(cx, |buffer, cx| {
13069                buffer.edit(edits, None, cx);
13070            });
13071
13072            // When duplicating upward with whole lines, move the cursor to the duplicated line
13073            if upwards && whole_lines {
13074                let display_map = this.display_map.update(cx, |map, cx| map.snapshot(cx));
13075
13076                this.change_selections(SelectionEffects::no_scroll(), window, cx, |s| {
13077                    let mut new_ranges = Vec::new();
13078                    let selections = s.all::<Point>(&display_map);
13079                    let mut selections_iter = selections.iter().peekable();
13080
13081                    while let Some(first_selection) = selections_iter.next() {
13082                        // Group contiguous selections together to find the total row span
13083                        let mut group_selections = vec![first_selection];
13084                        let mut rows = first_selection.spanned_rows(false, &display_map);
13085
13086                        while let Some(next_selection) = selections_iter.peek() {
13087                            let next_rows = next_selection.spanned_rows(false, &display_map);
13088                            if next_rows.start < rows.end {
13089                                rows.end = next_rows.end;
13090                                group_selections.push(selections_iter.next().unwrap());
13091                            } else {
13092                                break;
13093                            }
13094                        }
13095
13096                        let row_count = rows.end.0 - rows.start.0;
13097
13098                        // Move all selections in this group up by the total number of duplicated rows
13099                        for selection in group_selections {
13100                            let new_start = Point::new(
13101                                selection.start.row.saturating_sub(row_count),
13102                                selection.start.column,
13103                            );
13104
13105                            let new_end = Point::new(
13106                                selection.end.row.saturating_sub(row_count),
13107                                selection.end.column,
13108                            );
13109
13110                            new_ranges.push(new_start..new_end);
13111                        }
13112                    }
13113
13114                    s.select_ranges(new_ranges);
13115                });
13116            }
13117
13118            this.request_autoscroll(Autoscroll::fit(), cx);
13119        });
13120    }
13121
13122    pub fn duplicate_line_up(
13123        &mut self,
13124        _: &DuplicateLineUp,
13125        window: &mut Window,
13126        cx: &mut Context<Self>,
13127    ) {
13128        self.duplicate(true, true, window, cx);
13129    }
13130
13131    pub fn duplicate_line_down(
13132        &mut self,
13133        _: &DuplicateLineDown,
13134        window: &mut Window,
13135        cx: &mut Context<Self>,
13136    ) {
13137        self.duplicate(false, true, window, cx);
13138    }
13139
13140    pub fn duplicate_selection(
13141        &mut self,
13142        _: &DuplicateSelection,
13143        window: &mut Window,
13144        cx: &mut Context<Self>,
13145    ) {
13146        self.duplicate(false, false, window, cx);
13147    }
13148
13149    pub fn move_line_up(&mut self, _: &MoveLineUp, window: &mut Window, cx: &mut Context<Self>) {
13150        self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
13151        if self.mode.is_single_line() {
13152            cx.propagate();
13153            return;
13154        }
13155
13156        let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
13157        let buffer = self.buffer.read(cx).snapshot(cx);
13158
13159        let mut edits = Vec::new();
13160        let mut unfold_ranges = Vec::new();
13161        let mut refold_creases = Vec::new();
13162
13163        let selections = self.selections.all::<Point>(&display_map);
13164        let mut selections = selections.iter().peekable();
13165        let mut contiguous_row_selections = Vec::new();
13166        let mut new_selections = Vec::new();
13167
13168        while let Some(selection) = selections.next() {
13169            // Find all the selections that span a contiguous row range
13170            let (start_row, end_row) = consume_contiguous_rows(
13171                &mut contiguous_row_selections,
13172                selection,
13173                &display_map,
13174                &mut selections,
13175            );
13176
13177            // Move the text spanned by the row range to be before the line preceding the row range
13178            if start_row.0 > 0 {
13179                let range_to_move = Point::new(
13180                    start_row.previous_row().0,
13181                    buffer.line_len(start_row.previous_row()),
13182                )
13183                    ..Point::new(
13184                        end_row.previous_row().0,
13185                        buffer.line_len(end_row.previous_row()),
13186                    );
13187                let insertion_point = display_map
13188                    .prev_line_boundary(Point::new(start_row.previous_row().0, 0))
13189                    .0;
13190
13191                // Don't move lines across excerpts
13192                if buffer
13193                    .excerpt_containing(insertion_point..range_to_move.end)
13194                    .is_some()
13195                {
13196                    let text = buffer
13197                        .text_for_range(range_to_move.clone())
13198                        .flat_map(|s| s.chars())
13199                        .skip(1)
13200                        .chain(['\n'])
13201                        .collect::<String>();
13202
13203                    edits.push((
13204                        buffer.anchor_after(range_to_move.start)
13205                            ..buffer.anchor_before(range_to_move.end),
13206                        String::new(),
13207                    ));
13208                    let insertion_anchor = buffer.anchor_after(insertion_point);
13209                    edits.push((insertion_anchor..insertion_anchor, text));
13210
13211                    let row_delta = range_to_move.start.row - insertion_point.row + 1;
13212
13213                    // Move selections up
13214                    new_selections.extend(contiguous_row_selections.drain(..).map(
13215                        |mut selection| {
13216                            selection.start.row -= row_delta;
13217                            selection.end.row -= row_delta;
13218                            selection
13219                        },
13220                    ));
13221
13222                    // Move folds up
13223                    unfold_ranges.push(range_to_move.clone());
13224                    for fold in display_map.folds_in_range(
13225                        buffer.anchor_before(range_to_move.start)
13226                            ..buffer.anchor_after(range_to_move.end),
13227                    ) {
13228                        let mut start = fold.range.start.to_point(&buffer);
13229                        let mut end = fold.range.end.to_point(&buffer);
13230                        start.row -= row_delta;
13231                        end.row -= row_delta;
13232                        refold_creases.push(Crease::simple(start..end, fold.placeholder.clone()));
13233                    }
13234                }
13235            }
13236
13237            // If we didn't move line(s), preserve the existing selections
13238            new_selections.append(&mut contiguous_row_selections);
13239        }
13240
13241        self.transact(window, cx, |this, window, cx| {
13242            this.unfold_ranges(&unfold_ranges, true, true, cx);
13243            this.buffer.update(cx, |buffer, cx| {
13244                for (range, text) in edits {
13245                    buffer.edit([(range, text)], None, cx);
13246                }
13247            });
13248            this.fold_creases(refold_creases, true, window, cx);
13249            this.change_selections(Default::default(), window, cx, |s| {
13250                s.select(new_selections);
13251            })
13252        });
13253    }
13254
13255    pub fn move_line_down(
13256        &mut self,
13257        _: &MoveLineDown,
13258        window: &mut Window,
13259        cx: &mut Context<Self>,
13260    ) {
13261        self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
13262        if self.mode.is_single_line() {
13263            cx.propagate();
13264            return;
13265        }
13266
13267        let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
13268        let buffer = self.buffer.read(cx).snapshot(cx);
13269
13270        let mut edits = Vec::new();
13271        let mut unfold_ranges = Vec::new();
13272        let mut refold_creases = Vec::new();
13273
13274        let selections = self.selections.all::<Point>(&display_map);
13275        let mut selections = selections.iter().peekable();
13276        let mut contiguous_row_selections = Vec::new();
13277        let mut new_selections = Vec::new();
13278
13279        while let Some(selection) = selections.next() {
13280            // Find all the selections that span a contiguous row range
13281            let (start_row, end_row) = consume_contiguous_rows(
13282                &mut contiguous_row_selections,
13283                selection,
13284                &display_map,
13285                &mut selections,
13286            );
13287
13288            // Move the text spanned by the row range to be after the last line of the row range
13289            if end_row.0 <= buffer.max_point().row {
13290                let range_to_move =
13291                    MultiBufferPoint::new(start_row.0, 0)..MultiBufferPoint::new(end_row.0, 0);
13292                let insertion_point = display_map
13293                    .next_line_boundary(MultiBufferPoint::new(end_row.0, 0))
13294                    .0;
13295
13296                // Don't move lines across excerpt boundaries
13297                if buffer
13298                    .excerpt_containing(range_to_move.start..insertion_point)
13299                    .is_some()
13300                {
13301                    let mut text = String::from("\n");
13302                    text.extend(buffer.text_for_range(range_to_move.clone()));
13303                    text.pop(); // Drop trailing newline
13304                    edits.push((
13305                        buffer.anchor_after(range_to_move.start)
13306                            ..buffer.anchor_before(range_to_move.end),
13307                        String::new(),
13308                    ));
13309                    let insertion_anchor = buffer.anchor_after(insertion_point);
13310                    edits.push((insertion_anchor..insertion_anchor, text));
13311
13312                    let row_delta = insertion_point.row - range_to_move.end.row + 1;
13313
13314                    // Move selections down
13315                    new_selections.extend(contiguous_row_selections.drain(..).map(
13316                        |mut selection| {
13317                            selection.start.row += row_delta;
13318                            selection.end.row += row_delta;
13319                            selection
13320                        },
13321                    ));
13322
13323                    // Move folds down
13324                    unfold_ranges.push(range_to_move.clone());
13325                    for fold in display_map.folds_in_range(
13326                        buffer.anchor_before(range_to_move.start)
13327                            ..buffer.anchor_after(range_to_move.end),
13328                    ) {
13329                        let mut start = fold.range.start.to_point(&buffer);
13330                        let mut end = fold.range.end.to_point(&buffer);
13331                        start.row += row_delta;
13332                        end.row += row_delta;
13333                        refold_creases.push(Crease::simple(start..end, fold.placeholder.clone()));
13334                    }
13335                }
13336            }
13337
13338            // If we didn't move line(s), preserve the existing selections
13339            new_selections.append(&mut contiguous_row_selections);
13340        }
13341
13342        self.transact(window, cx, |this, window, cx| {
13343            this.unfold_ranges(&unfold_ranges, true, true, cx);
13344            this.buffer.update(cx, |buffer, cx| {
13345                for (range, text) in edits {
13346                    buffer.edit([(range, text)], None, cx);
13347                }
13348            });
13349            this.fold_creases(refold_creases, true, window, cx);
13350            this.change_selections(Default::default(), window, cx, |s| s.select(new_selections));
13351        });
13352    }
13353
13354    pub fn transpose(&mut self, _: &Transpose, window: &mut Window, cx: &mut Context<Self>) {
13355        self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
13356        let text_layout_details = &self.text_layout_details(window, cx);
13357        self.transact(window, cx, |this, window, cx| {
13358            let edits = this.change_selections(Default::default(), window, cx, |s| {
13359                let mut edits: Vec<(Range<MultiBufferOffset>, String)> = Default::default();
13360                s.move_with(&mut |display_map, selection| {
13361                    if !selection.is_empty() {
13362                        return;
13363                    }
13364
13365                    let mut head = selection.head();
13366                    let mut transpose_offset = head.to_offset(display_map, Bias::Right);
13367                    if head.column() == display_map.line_len(head.row()) {
13368                        transpose_offset = display_map
13369                            .buffer_snapshot()
13370                            .clip_offset(transpose_offset.saturating_sub_usize(1), Bias::Left);
13371                    }
13372
13373                    if transpose_offset == MultiBufferOffset(0) {
13374                        return;
13375                    }
13376
13377                    *head.column_mut() += 1;
13378                    head = display_map.clip_point(head, Bias::Right);
13379                    let goal = SelectionGoal::HorizontalPosition(
13380                        display_map
13381                            .x_for_display_point(head, text_layout_details)
13382                            .into(),
13383                    );
13384                    selection.collapse_to(head, goal);
13385
13386                    let transpose_start = display_map
13387                        .buffer_snapshot()
13388                        .clip_offset(transpose_offset.saturating_sub_usize(1), Bias::Left);
13389                    if edits.last().is_none_or(|e| e.0.end <= transpose_start) {
13390                        let transpose_end = display_map
13391                            .buffer_snapshot()
13392                            .clip_offset(transpose_offset + 1usize, Bias::Right);
13393                        if let Some(ch) = display_map
13394                            .buffer_snapshot()
13395                            .chars_at(transpose_start)
13396                            .next()
13397                        {
13398                            edits.push((transpose_start..transpose_offset, String::new()));
13399                            edits.push((transpose_end..transpose_end, ch.to_string()));
13400                        }
13401                    }
13402                });
13403                edits
13404            });
13405            this.buffer
13406                .update(cx, |buffer, cx| buffer.edit(edits, None, cx));
13407            let selections = this
13408                .selections
13409                .all::<MultiBufferOffset>(&this.display_snapshot(cx));
13410            this.change_selections(Default::default(), window, cx, |s| {
13411                s.select(selections);
13412            });
13413        });
13414    }
13415
13416    pub fn rewrap(&mut self, _: &Rewrap, _: &mut Window, cx: &mut Context<Self>) {
13417        self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
13418        if self.mode.is_single_line() {
13419            cx.propagate();
13420            return;
13421        }
13422
13423        self.rewrap_impl(RewrapOptions::default(), cx)
13424    }
13425
13426    pub fn rewrap_impl(&mut self, options: RewrapOptions, cx: &mut Context<Self>) {
13427        let buffer = self.buffer.read(cx).snapshot(cx);
13428        let selections = self.selections.all::<Point>(&self.display_snapshot(cx));
13429
13430        #[derive(Clone, Debug, PartialEq)]
13431        enum CommentFormat {
13432            /// single line comment, with prefix for line
13433            Line(String),
13434            /// single line within a block comment, with prefix for line
13435            BlockLine(String),
13436            /// a single line of a block comment that includes the initial delimiter
13437            BlockCommentWithStart(BlockCommentConfig),
13438            /// a single line of a block comment that includes the ending delimiter
13439            BlockCommentWithEnd(BlockCommentConfig),
13440        }
13441
13442        // Split selections to respect paragraph, indent, and comment prefix boundaries.
13443        let wrap_ranges = selections.into_iter().flat_map(|selection| {
13444            let language_settings = buffer.language_settings_at(selection.head(), cx);
13445            let language_scope = buffer.language_scope_at(selection.head());
13446
13447            let indent_and_prefix_for_row =
13448                |row: u32| -> (IndentSize, Option<CommentFormat>, Option<String>) {
13449                    let indent = buffer.indent_size_for_line(MultiBufferRow(row));
13450                    let (comment_prefix, rewrap_prefix) = if let Some(language_scope) =
13451                        &language_scope
13452                    {
13453                        let indent_end = Point::new(row, indent.len);
13454                        let line_end = Point::new(row, buffer.line_len(MultiBufferRow(row)));
13455                        let line_text_after_indent = buffer
13456                            .text_for_range(indent_end..line_end)
13457                            .collect::<String>();
13458
13459                        let is_within_comment_override = buffer
13460                            .language_scope_at(indent_end)
13461                            .is_some_and(|scope| scope.override_name() == Some("comment"));
13462                        let comment_delimiters = if is_within_comment_override {
13463                            // we are within a comment syntax node, but we don't
13464                            // yet know what kind of comment: block, doc or line
13465                            match (
13466                                language_scope.documentation_comment(),
13467                                language_scope.block_comment(),
13468                            ) {
13469                                (Some(config), _) | (_, Some(config))
13470                                    if buffer.contains_str_at(indent_end, &config.start) =>
13471                                {
13472                                    Some(CommentFormat::BlockCommentWithStart(config.clone()))
13473                                }
13474                                (Some(config), _) | (_, Some(config))
13475                                    if line_text_after_indent.ends_with(config.end.as_ref()) =>
13476                                {
13477                                    Some(CommentFormat::BlockCommentWithEnd(config.clone()))
13478                                }
13479                                (Some(config), _) | (_, Some(config))
13480                                    if buffer.contains_str_at(indent_end, &config.prefix) =>
13481                                {
13482                                    Some(CommentFormat::BlockLine(config.prefix.to_string()))
13483                                }
13484                                (_, _) => language_scope
13485                                    .line_comment_prefixes()
13486                                    .iter()
13487                                    .find(|prefix| buffer.contains_str_at(indent_end, prefix))
13488                                    .map(|prefix| CommentFormat::Line(prefix.to_string())),
13489                            }
13490                        } else {
13491                            // we not in an overridden comment node, but we may
13492                            // be within a non-overridden line comment node
13493                            language_scope
13494                                .line_comment_prefixes()
13495                                .iter()
13496                                .find(|prefix| buffer.contains_str_at(indent_end, prefix))
13497                                .map(|prefix| CommentFormat::Line(prefix.to_string()))
13498                        };
13499
13500                        let rewrap_prefix = language_scope
13501                            .rewrap_prefixes()
13502                            .iter()
13503                            .find_map(|prefix_regex| {
13504                                prefix_regex.find(&line_text_after_indent).map(|mat| {
13505                                    if mat.start() == 0 {
13506                                        Some(mat.as_str().to_string())
13507                                    } else {
13508                                        None
13509                                    }
13510                                })
13511                            })
13512                            .flatten();
13513                        (comment_delimiters, rewrap_prefix)
13514                    } else {
13515                        (None, None)
13516                    };
13517                    (indent, comment_prefix, rewrap_prefix)
13518                };
13519
13520            let mut start_row = selection.start.row;
13521            let mut end_row = selection.end.row;
13522
13523            if selection.is_empty() {
13524                let cursor_row = selection.start.row;
13525
13526                let (mut indent_size, comment_prefix, _) = indent_and_prefix_for_row(cursor_row);
13527                let line_prefix = match &comment_prefix {
13528                    Some(CommentFormat::Line(prefix) | CommentFormat::BlockLine(prefix)) => {
13529                        Some(prefix.as_str())
13530                    }
13531                    Some(CommentFormat::BlockCommentWithEnd(BlockCommentConfig {
13532                        prefix, ..
13533                    })) => Some(prefix.as_ref()),
13534                    Some(CommentFormat::BlockCommentWithStart(BlockCommentConfig {
13535                        start: _,
13536                        end: _,
13537                        prefix,
13538                        tab_size,
13539                    })) => {
13540                        indent_size.len += tab_size;
13541                        Some(prefix.as_ref())
13542                    }
13543                    None => None,
13544                };
13545                let indent_prefix = indent_size.chars().collect::<String>();
13546                let line_prefix = format!("{indent_prefix}{}", line_prefix.unwrap_or(""));
13547
13548                'expand_upwards: while start_row > 0 {
13549                    let prev_row = start_row - 1;
13550                    if buffer.contains_str_at(Point::new(prev_row, 0), &line_prefix)
13551                        && buffer.line_len(MultiBufferRow(prev_row)) as usize > line_prefix.len()
13552                        && !buffer.is_line_blank(MultiBufferRow(prev_row))
13553                    {
13554                        start_row = prev_row;
13555                    } else {
13556                        break 'expand_upwards;
13557                    }
13558                }
13559
13560                'expand_downwards: while end_row < buffer.max_point().row {
13561                    let next_row = end_row + 1;
13562                    if buffer.contains_str_at(Point::new(next_row, 0), &line_prefix)
13563                        && buffer.line_len(MultiBufferRow(next_row)) as usize > line_prefix.len()
13564                        && !buffer.is_line_blank(MultiBufferRow(next_row))
13565                    {
13566                        end_row = next_row;
13567                    } else {
13568                        break 'expand_downwards;
13569                    }
13570                }
13571            }
13572
13573            let mut non_blank_rows_iter = (start_row..=end_row)
13574                .filter(|row| !buffer.is_line_blank(MultiBufferRow(*row)))
13575                .peekable();
13576
13577            let first_row = if let Some(&row) = non_blank_rows_iter.peek() {
13578                row
13579            } else {
13580                return Vec::new();
13581            };
13582
13583            let mut ranges = Vec::new();
13584
13585            let mut current_range_start = first_row;
13586            let mut prev_row = first_row;
13587            let (
13588                mut current_range_indent,
13589                mut current_range_comment_delimiters,
13590                mut current_range_rewrap_prefix,
13591            ) = indent_and_prefix_for_row(first_row);
13592
13593            for row in non_blank_rows_iter.skip(1) {
13594                let has_paragraph_break = row > prev_row + 1;
13595
13596                let (row_indent, row_comment_delimiters, row_rewrap_prefix) =
13597                    indent_and_prefix_for_row(row);
13598
13599                let has_indent_change = row_indent != current_range_indent;
13600                let has_comment_change = row_comment_delimiters != current_range_comment_delimiters;
13601
13602                let has_boundary_change = has_comment_change
13603                    || row_rewrap_prefix.is_some()
13604                    || (has_indent_change && current_range_comment_delimiters.is_some());
13605
13606                if has_paragraph_break || has_boundary_change {
13607                    ranges.push((
13608                        language_settings.clone(),
13609                        Point::new(current_range_start, 0)
13610                            ..Point::new(prev_row, buffer.line_len(MultiBufferRow(prev_row))),
13611                        current_range_indent,
13612                        current_range_comment_delimiters.clone(),
13613                        current_range_rewrap_prefix.clone(),
13614                    ));
13615                    current_range_start = row;
13616                    current_range_indent = row_indent;
13617                    current_range_comment_delimiters = row_comment_delimiters;
13618                    current_range_rewrap_prefix = row_rewrap_prefix;
13619                }
13620                prev_row = row;
13621            }
13622
13623            ranges.push((
13624                language_settings.clone(),
13625                Point::new(current_range_start, 0)
13626                    ..Point::new(prev_row, buffer.line_len(MultiBufferRow(prev_row))),
13627                current_range_indent,
13628                current_range_comment_delimiters,
13629                current_range_rewrap_prefix,
13630            ));
13631
13632            ranges
13633        });
13634
13635        let mut edits = Vec::new();
13636        let mut rewrapped_row_ranges = Vec::<RangeInclusive<u32>>::new();
13637
13638        for (language_settings, wrap_range, mut indent_size, comment_prefix, rewrap_prefix) in
13639            wrap_ranges
13640        {
13641            let start_row = wrap_range.start.row;
13642            let end_row = wrap_range.end.row;
13643
13644            // Skip selections that overlap with a range that has already been rewrapped.
13645            let selection_range = start_row..end_row;
13646            if rewrapped_row_ranges
13647                .iter()
13648                .any(|range| range.overlaps(&selection_range))
13649            {
13650                continue;
13651            }
13652
13653            let tab_size = language_settings.tab_size;
13654
13655            let (line_prefix, inside_comment) = match &comment_prefix {
13656                Some(CommentFormat::Line(prefix) | CommentFormat::BlockLine(prefix)) => {
13657                    (Some(prefix.as_str()), true)
13658                }
13659                Some(CommentFormat::BlockCommentWithEnd(BlockCommentConfig { prefix, .. })) => {
13660                    (Some(prefix.as_ref()), true)
13661                }
13662                Some(CommentFormat::BlockCommentWithStart(BlockCommentConfig {
13663                    start: _,
13664                    end: _,
13665                    prefix,
13666                    tab_size,
13667                })) => {
13668                    indent_size.len += tab_size;
13669                    (Some(prefix.as_ref()), true)
13670                }
13671                None => (None, false),
13672            };
13673            let indent_prefix = indent_size.chars().collect::<String>();
13674            let line_prefix = format!("{indent_prefix}{}", line_prefix.unwrap_or(""));
13675
13676            let allow_rewrap_based_on_language = match language_settings.allow_rewrap {
13677                RewrapBehavior::InComments => inside_comment,
13678                RewrapBehavior::InSelections => !wrap_range.is_empty(),
13679                RewrapBehavior::Anywhere => true,
13680            };
13681
13682            let should_rewrap = options.override_language_settings
13683                || allow_rewrap_based_on_language
13684                || self.hard_wrap.is_some();
13685            if !should_rewrap {
13686                continue;
13687            }
13688
13689            let start = Point::new(start_row, 0);
13690            let start_offset = ToOffset::to_offset(&start, &buffer);
13691            let end = Point::new(end_row, buffer.line_len(MultiBufferRow(end_row)));
13692            let selection_text = buffer.text_for_range(start..end).collect::<String>();
13693            let mut first_line_delimiter = None;
13694            let mut last_line_delimiter = None;
13695            let Some(lines_without_prefixes) = selection_text
13696                .lines()
13697                .enumerate()
13698                .map(|(ix, line)| {
13699                    let line_trimmed = line.trim_start();
13700                    if rewrap_prefix.is_some() && ix > 0 {
13701                        Ok(line_trimmed)
13702                    } else if let Some(
13703                        CommentFormat::BlockCommentWithStart(BlockCommentConfig {
13704                            start,
13705                            prefix,
13706                            end,
13707                            tab_size,
13708                        })
13709                        | CommentFormat::BlockCommentWithEnd(BlockCommentConfig {
13710                            start,
13711                            prefix,
13712                            end,
13713                            tab_size,
13714                        }),
13715                    ) = &comment_prefix
13716                    {
13717                        let line_trimmed = line_trimmed
13718                            .strip_prefix(start.as_ref())
13719                            .map(|s| {
13720                                let mut indent_size = indent_size;
13721                                indent_size.len -= tab_size;
13722                                let indent_prefix: String = indent_size.chars().collect();
13723                                first_line_delimiter = Some((indent_prefix, start));
13724                                s.trim_start()
13725                            })
13726                            .unwrap_or(line_trimmed);
13727                        let line_trimmed = line_trimmed
13728                            .strip_suffix(end.as_ref())
13729                            .map(|s| {
13730                                last_line_delimiter = Some(end);
13731                                s.trim_end()
13732                            })
13733                            .unwrap_or(line_trimmed);
13734                        let line_trimmed = line_trimmed
13735                            .strip_prefix(prefix.as_ref())
13736                            .unwrap_or(line_trimmed);
13737                        Ok(line_trimmed)
13738                    } else if let Some(CommentFormat::BlockLine(prefix)) = &comment_prefix {
13739                        line_trimmed.strip_prefix(prefix).with_context(|| {
13740                            format!("line did not start with prefix {prefix:?}: {line:?}")
13741                        })
13742                    } else {
13743                        line_trimmed
13744                            .strip_prefix(&line_prefix.trim_start())
13745                            .with_context(|| {
13746                                format!("line did not start with prefix {line_prefix:?}: {line:?}")
13747                            })
13748                    }
13749                })
13750                .collect::<Result<Vec<_>, _>>()
13751                .log_err()
13752            else {
13753                continue;
13754            };
13755
13756            let wrap_column = options.line_length.or(self.hard_wrap).unwrap_or_else(|| {
13757                buffer
13758                    .language_settings_at(Point::new(start_row, 0), cx)
13759                    .preferred_line_length as usize
13760            });
13761
13762            let subsequent_lines_prefix = if let Some(rewrap_prefix_str) = &rewrap_prefix {
13763                format!("{}{}", indent_prefix, " ".repeat(rewrap_prefix_str.len()))
13764            } else {
13765                line_prefix.clone()
13766            };
13767
13768            let wrapped_text = {
13769                let mut wrapped_text = wrap_with_prefix(
13770                    line_prefix,
13771                    subsequent_lines_prefix,
13772                    lines_without_prefixes.join("\n"),
13773                    wrap_column,
13774                    tab_size,
13775                    options.preserve_existing_whitespace,
13776                );
13777
13778                if let Some((indent, delimiter)) = first_line_delimiter {
13779                    wrapped_text = format!("{indent}{delimiter}\n{wrapped_text}");
13780                }
13781                if let Some(last_line) = last_line_delimiter {
13782                    wrapped_text = format!("{wrapped_text}\n{indent_prefix}{last_line}");
13783                }
13784
13785                wrapped_text
13786            };
13787
13788            // TODO: should always use char-based diff while still supporting cursor behavior that
13789            // matches vim.
13790            let mut diff_options = DiffOptions::default();
13791            if options.override_language_settings {
13792                diff_options.max_word_diff_len = 0;
13793                diff_options.max_word_diff_line_count = 0;
13794            } else {
13795                diff_options.max_word_diff_len = usize::MAX;
13796                diff_options.max_word_diff_line_count = usize::MAX;
13797            }
13798
13799            for (old_range, new_text) in
13800                text_diff_with_options(&selection_text, &wrapped_text, diff_options)
13801            {
13802                let edit_start = buffer.anchor_after(start_offset + old_range.start);
13803                let edit_end = buffer.anchor_after(start_offset + old_range.end);
13804                edits.push((edit_start..edit_end, new_text));
13805            }
13806
13807            rewrapped_row_ranges.push(start_row..=end_row);
13808        }
13809
13810        self.buffer
13811            .update(cx, |buffer, cx| buffer.edit(edits, None, cx));
13812    }
13813
13814    pub fn cut_common(
13815        &mut self,
13816        cut_no_selection_line: bool,
13817        window: &mut Window,
13818        cx: &mut Context<Self>,
13819    ) -> ClipboardItem {
13820        let mut text = String::new();
13821        let buffer = self.buffer.read(cx).snapshot(cx);
13822        let mut selections = self.selections.all::<Point>(&self.display_snapshot(cx));
13823        let mut clipboard_selections = Vec::with_capacity(selections.len());
13824        {
13825            let max_point = buffer.max_point();
13826            let mut is_first = true;
13827            let mut prev_selection_was_entire_line = false;
13828            for selection in &mut selections {
13829                let is_entire_line =
13830                    (selection.is_empty() && cut_no_selection_line) || self.selections.line_mode();
13831                if is_entire_line {
13832                    selection.start = Point::new(selection.start.row, 0);
13833                    if !selection.is_empty() && selection.end.column == 0 {
13834                        selection.end = cmp::min(max_point, selection.end);
13835                    } else {
13836                        selection.end = cmp::min(max_point, Point::new(selection.end.row + 1, 0));
13837                    }
13838                    selection.goal = SelectionGoal::None;
13839                }
13840                if is_first {
13841                    is_first = false;
13842                } else if !prev_selection_was_entire_line {
13843                    text += "\n";
13844                }
13845                prev_selection_was_entire_line = is_entire_line;
13846                let mut len = 0;
13847                for chunk in buffer.text_for_range(selection.start..selection.end) {
13848                    text.push_str(chunk);
13849                    len += chunk.len();
13850                }
13851
13852                clipboard_selections.push(ClipboardSelection::for_buffer(
13853                    len,
13854                    is_entire_line,
13855                    selection.range(),
13856                    &buffer,
13857                    self.project.as_ref(),
13858                    cx,
13859                ));
13860            }
13861        }
13862
13863        self.transact(window, cx, |this, window, cx| {
13864            this.change_selections(Default::default(), window, cx, |s| {
13865                s.select(selections);
13866            });
13867            this.insert("", window, cx);
13868        });
13869        ClipboardItem::new_string_with_json_metadata(text, clipboard_selections)
13870    }
13871
13872    pub fn cut(&mut self, _: &Cut, window: &mut Window, cx: &mut Context<Self>) {
13873        self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
13874        let item = self.cut_common(true, window, cx);
13875        cx.write_to_clipboard(item);
13876    }
13877
13878    pub fn kill_ring_cut(&mut self, _: &KillRingCut, window: &mut Window, cx: &mut Context<Self>) {
13879        self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
13880        self.change_selections(SelectionEffects::no_scroll(), window, cx, |s| {
13881            s.move_with(&mut |snapshot, sel| {
13882                if sel.is_empty() {
13883                    sel.end = DisplayPoint::new(sel.end.row(), snapshot.line_len(sel.end.row()));
13884                }
13885                if sel.is_empty() {
13886                    sel.end = DisplayPoint::new(sel.end.row() + 1_u32, 0);
13887                }
13888            });
13889        });
13890        let item = self.cut_common(false, window, cx);
13891        cx.set_global(KillRing(item))
13892    }
13893
13894    pub fn kill_ring_yank(
13895        &mut self,
13896        _: &KillRingYank,
13897        window: &mut Window,
13898        cx: &mut Context<Self>,
13899    ) {
13900        self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
13901        let (text, metadata) = if let Some(KillRing(item)) = cx.try_global() {
13902            if let Some(ClipboardEntry::String(kill_ring)) = item.entries().first() {
13903                (kill_ring.text().to_string(), kill_ring.metadata_json())
13904            } else {
13905                return;
13906            }
13907        } else {
13908            return;
13909        };
13910        self.do_paste(&text, metadata, false, window, cx);
13911    }
13912
13913    pub fn copy_and_trim(&mut self, _: &CopyAndTrim, _: &mut Window, cx: &mut Context<Self>) {
13914        self.do_copy(true, cx);
13915    }
13916
13917    pub fn copy(&mut self, _: &Copy, _: &mut Window, cx: &mut Context<Self>) {
13918        self.do_copy(false, cx);
13919    }
13920
13921    fn do_copy(&self, strip_leading_indents: bool, cx: &mut Context<Self>) {
13922        let selections = self.selections.all::<Point>(&self.display_snapshot(cx));
13923        let buffer = self.buffer.read(cx).read(cx);
13924        let mut text = String::new();
13925        let mut clipboard_selections = Vec::with_capacity(selections.len());
13926
13927        let max_point = buffer.max_point();
13928        let mut is_first = true;
13929        for selection in &selections {
13930            let mut start = selection.start;
13931            let mut end = selection.end;
13932            let is_entire_line = selection.is_empty() || self.selections.line_mode();
13933            let mut add_trailing_newline = false;
13934            if is_entire_line {
13935                start = Point::new(start.row, 0);
13936                let next_line_start = Point::new(end.row + 1, 0);
13937                if next_line_start <= max_point {
13938                    end = next_line_start;
13939                } else {
13940                    // We're on the last line without a trailing newline.
13941                    // Copy to the end of the line and add a newline afterwards.
13942                    end = Point::new(end.row, buffer.line_len(MultiBufferRow(end.row)));
13943                    add_trailing_newline = true;
13944                }
13945            }
13946
13947            let mut trimmed_selections = Vec::new();
13948            if strip_leading_indents && end.row.saturating_sub(start.row) > 0 {
13949                let row = MultiBufferRow(start.row);
13950                let first_indent = buffer.indent_size_for_line(row);
13951                if first_indent.len == 0 || start.column > first_indent.len {
13952                    trimmed_selections.push(start..end);
13953                } else {
13954                    trimmed_selections.push(
13955                        Point::new(row.0, first_indent.len)
13956                            ..Point::new(row.0, buffer.line_len(row)),
13957                    );
13958                    for row in start.row + 1..=end.row {
13959                        let mut line_len = buffer.line_len(MultiBufferRow(row));
13960                        if row == end.row {
13961                            line_len = end.column;
13962                        }
13963                        if line_len == 0 {
13964                            trimmed_selections.push(Point::new(row, 0)..Point::new(row, line_len));
13965                            continue;
13966                        }
13967                        let row_indent_size = buffer.indent_size_for_line(MultiBufferRow(row));
13968                        if row_indent_size.len >= first_indent.len {
13969                            trimmed_selections
13970                                .push(Point::new(row, first_indent.len)..Point::new(row, line_len));
13971                        } else {
13972                            trimmed_selections.clear();
13973                            trimmed_selections.push(start..end);
13974                            break;
13975                        }
13976                    }
13977                }
13978            } else {
13979                trimmed_selections.push(start..end);
13980            }
13981
13982            let is_multiline_trim = trimmed_selections.len() > 1;
13983            let mut selection_len: usize = 0;
13984            let prev_selection_was_entire_line = is_entire_line && !is_multiline_trim;
13985
13986            for trimmed_range in trimmed_selections {
13987                if is_first {
13988                    is_first = false;
13989                } else if is_multiline_trim || !prev_selection_was_entire_line {
13990                    text.push('\n');
13991                    if is_multiline_trim {
13992                        selection_len += 1;
13993                    }
13994                }
13995                for chunk in buffer.text_for_range(trimmed_range.start..trimmed_range.end) {
13996                    text.push_str(chunk);
13997                    selection_len += chunk.len();
13998                }
13999                if add_trailing_newline {
14000                    text.push('\n');
14001                    selection_len += 1;
14002                }
14003            }
14004
14005            clipboard_selections.push(ClipboardSelection::for_buffer(
14006                selection_len,
14007                is_entire_line,
14008                start..end,
14009                &buffer,
14010                self.project.as_ref(),
14011                cx,
14012            ));
14013        }
14014
14015        cx.write_to_clipboard(ClipboardItem::new_string_with_json_metadata(
14016            text,
14017            clipboard_selections,
14018        ));
14019    }
14020
14021    pub fn do_paste(
14022        &mut self,
14023        text: &String,
14024        clipboard_selections: Option<Vec<ClipboardSelection>>,
14025        handle_entire_lines: bool,
14026        window: &mut Window,
14027        cx: &mut Context<Self>,
14028    ) {
14029        if self.read_only(cx) {
14030            return;
14031        }
14032
14033        self.finalize_last_transaction(cx);
14034
14035        let clipboard_text = Cow::Borrowed(text.as_str());
14036
14037        self.transact(window, cx, |this, window, cx| {
14038            let had_active_edit_prediction = this.has_active_edit_prediction();
14039            let display_map = this.display_snapshot(cx);
14040            let old_selections = this.selections.all::<MultiBufferOffset>(&display_map);
14041            let cursor_offset = this
14042                .selections
14043                .last::<MultiBufferOffset>(&display_map)
14044                .head();
14045
14046            if let Some(mut clipboard_selections) = clipboard_selections {
14047                let all_selections_were_entire_line =
14048                    clipboard_selections.iter().all(|s| s.is_entire_line);
14049                let first_selection_indent_column =
14050                    clipboard_selections.first().map(|s| s.first_line_indent);
14051                if clipboard_selections.len() != old_selections.len() {
14052                    clipboard_selections.drain(..);
14053                }
14054                let mut auto_indent_on_paste = true;
14055
14056                this.buffer.update(cx, |buffer, cx| {
14057                    let snapshot = buffer.read(cx);
14058                    auto_indent_on_paste = snapshot
14059                        .language_settings_at(cursor_offset, cx)
14060                        .auto_indent_on_paste;
14061
14062                    let mut start_offset = 0;
14063                    let mut edits = Vec::new();
14064                    let mut original_indent_columns = Vec::new();
14065                    for (ix, selection) in old_selections.iter().enumerate() {
14066                        let to_insert;
14067                        let entire_line;
14068                        let original_indent_column;
14069                        if let Some(clipboard_selection) = clipboard_selections.get(ix) {
14070                            let end_offset = start_offset + clipboard_selection.len;
14071                            to_insert = &clipboard_text[start_offset..end_offset];
14072                            entire_line = clipboard_selection.is_entire_line;
14073                            start_offset = if entire_line {
14074                                end_offset
14075                            } else {
14076                                end_offset + 1
14077                            };
14078                            original_indent_column = Some(clipboard_selection.first_line_indent);
14079                        } else {
14080                            to_insert = &*clipboard_text;
14081                            entire_line = all_selections_were_entire_line;
14082                            original_indent_column = first_selection_indent_column
14083                        }
14084
14085                        let (range, to_insert) =
14086                            if selection.is_empty() && handle_entire_lines && entire_line {
14087                                // If the corresponding selection was empty when this slice of the
14088                                // clipboard text was written, then the entire line containing the
14089                                // selection was copied. If this selection is also currently empty,
14090                                // then paste the line before the current line of the buffer.
14091                                let column = selection.start.to_point(&snapshot).column as usize;
14092                                let line_start = selection.start - column;
14093                                (line_start..line_start, Cow::Borrowed(to_insert))
14094                            } else {
14095                                let language = snapshot.language_at(selection.head());
14096                                let range = selection.range();
14097                                if let Some(language) = language
14098                                    && language.name() == "Markdown"
14099                                {
14100                                    edit_for_markdown_paste(
14101                                        &snapshot,
14102                                        range,
14103                                        to_insert,
14104                                        url::Url::parse(to_insert).ok(),
14105                                    )
14106                                } else {
14107                                    (range, Cow::Borrowed(to_insert))
14108                                }
14109                            };
14110
14111                        edits.push((range, to_insert));
14112                        original_indent_columns.push(original_indent_column);
14113                    }
14114                    drop(snapshot);
14115
14116                    buffer.edit(
14117                        edits,
14118                        if auto_indent_on_paste {
14119                            Some(AutoindentMode::Block {
14120                                original_indent_columns,
14121                            })
14122                        } else {
14123                            None
14124                        },
14125                        cx,
14126                    );
14127                });
14128
14129                let selections = this
14130                    .selections
14131                    .all::<MultiBufferOffset>(&this.display_snapshot(cx));
14132                this.change_selections(Default::default(), window, cx, |s| s.select(selections));
14133            } else {
14134                let url = url::Url::parse(&clipboard_text).ok();
14135
14136                let auto_indent_mode = if !clipboard_text.is_empty() {
14137                    Some(AutoindentMode::Block {
14138                        original_indent_columns: Vec::new(),
14139                    })
14140                } else {
14141                    None
14142                };
14143
14144                let selection_anchors = this.buffer.update(cx, |buffer, cx| {
14145                    let snapshot = buffer.snapshot(cx);
14146
14147                    let anchors = old_selections
14148                        .iter()
14149                        .map(|s| {
14150                            let anchor = snapshot.anchor_after(s.head());
14151                            s.map(|_| anchor)
14152                        })
14153                        .collect::<Vec<_>>();
14154
14155                    let mut edits = Vec::new();
14156
14157                    // When pasting text without metadata (e.g. copied from an
14158                    // external editor using multiple cursors) and the number of
14159                    // lines matches the number of selections, distribute one
14160                    // line per cursor instead of pasting the whole text at each.
14161                    let lines: Vec<&str> = clipboard_text.split('\n').collect();
14162                    let distribute_lines =
14163                        old_selections.len() > 1 && lines.len() == old_selections.len();
14164
14165                    for (ix, selection) in old_selections.iter().enumerate() {
14166                        let language = snapshot.language_at(selection.head());
14167                        let range = selection.range();
14168
14169                        let text_for_cursor: &str = if distribute_lines {
14170                            lines[ix]
14171                        } else {
14172                            &clipboard_text
14173                        };
14174
14175                        let (edit_range, edit_text) = if let Some(language) = language
14176                            && language.name() == "Markdown"
14177                        {
14178                            edit_for_markdown_paste(&snapshot, range, text_for_cursor, url.clone())
14179                        } else {
14180                            (range, Cow::Borrowed(text_for_cursor))
14181                        };
14182
14183                        edits.push((edit_range, edit_text));
14184                    }
14185
14186                    drop(snapshot);
14187                    buffer.edit(edits, auto_indent_mode, cx);
14188
14189                    anchors
14190                });
14191
14192                this.change_selections(Default::default(), window, cx, |s| {
14193                    s.select_anchors(selection_anchors);
14194                });
14195            }
14196
14197            //   🤔                 |    ..     | show_in_menu |
14198            // | ..                  |   true        true
14199            // | had_edit_prediction |   false       true
14200
14201            let trigger_in_words =
14202                this.show_edit_predictions_in_menu() || !had_active_edit_prediction;
14203
14204            this.trigger_completion_on_input(text, trigger_in_words, window, cx);
14205        });
14206    }
14207
14208    pub fn diff_clipboard_with_selection(
14209        &mut self,
14210        _: &DiffClipboardWithSelection,
14211        window: &mut Window,
14212        cx: &mut Context<Self>,
14213    ) {
14214        let selections = self
14215            .selections
14216            .all::<MultiBufferOffset>(&self.display_snapshot(cx));
14217
14218        if selections.is_empty() {
14219            log::warn!("There should always be at least one selection in Zed. This is a bug.");
14220            return;
14221        };
14222
14223        let clipboard_text = cx.read_from_clipboard().and_then(|item| {
14224            item.entries().iter().find_map(|entry| match entry {
14225                ClipboardEntry::String(text) => Some(text.text().to_string()),
14226                _ => None,
14227            })
14228        });
14229
14230        let Some(clipboard_text) = clipboard_text else {
14231            log::warn!("Clipboard doesn't contain text.");
14232            return;
14233        };
14234
14235        window.dispatch_action(
14236            Box::new(DiffClipboardWithSelectionData {
14237                clipboard_text,
14238                editor: cx.entity(),
14239            }),
14240            cx,
14241        );
14242    }
14243
14244    pub fn paste(&mut self, _: &Paste, window: &mut Window, cx: &mut Context<Self>) {
14245        self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
14246        if let Some(item) = cx.read_from_clipboard() {
14247            let clipboard_string = item.entries().iter().find_map(|entry| match entry {
14248                ClipboardEntry::String(s) => Some(s),
14249                _ => None,
14250            });
14251            match clipboard_string {
14252                Some(clipboard_string) => self.do_paste(
14253                    clipboard_string.text(),
14254                    clipboard_string.metadata_json::<Vec<ClipboardSelection>>(),
14255                    true,
14256                    window,
14257                    cx,
14258                ),
14259                _ => self.do_paste(&item.text().unwrap_or_default(), None, true, window, cx),
14260            }
14261        }
14262    }
14263
14264    pub fn undo(&mut self, _: &Undo, window: &mut Window, cx: &mut Context<Self>) {
14265        if self.read_only(cx) {
14266            return;
14267        }
14268
14269        self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
14270
14271        if let Some(transaction_id) = self.buffer.update(cx, |buffer, cx| buffer.undo(cx)) {
14272            if let Some((selections, _)) =
14273                self.selection_history.transaction(transaction_id).cloned()
14274            {
14275                self.change_selections(SelectionEffects::no_scroll(), window, cx, |s| {
14276                    s.select_anchors(selections.to_vec());
14277                });
14278            } else {
14279                log::error!(
14280                    "No entry in selection_history found for undo. \
14281                     This may correspond to a bug where undo does not update the selection. \
14282                     If this is occurring, please add details to \
14283                     https://github.com/zed-industries/zed/issues/22692"
14284                );
14285            }
14286            self.request_autoscroll(Autoscroll::fit(), cx);
14287            self.unmark_text(window, cx);
14288            self.refresh_edit_prediction(true, false, window, cx);
14289            cx.emit(EditorEvent::Edited { transaction_id });
14290            cx.emit(EditorEvent::TransactionUndone { transaction_id });
14291        }
14292    }
14293
14294    pub fn redo(&mut self, _: &Redo, window: &mut Window, cx: &mut Context<Self>) {
14295        if self.read_only(cx) {
14296            return;
14297        }
14298
14299        self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
14300
14301        if let Some(transaction_id) = self.buffer.update(cx, |buffer, cx| buffer.redo(cx)) {
14302            if let Some((_, Some(selections))) =
14303                self.selection_history.transaction(transaction_id).cloned()
14304            {
14305                self.change_selections(SelectionEffects::no_scroll(), window, cx, |s| {
14306                    s.select_anchors(selections.to_vec());
14307                });
14308            } else {
14309                log::error!(
14310                    "No entry in selection_history found for redo. \
14311                     This may correspond to a bug where undo does not update the selection. \
14312                     If this is occurring, please add details to \
14313                     https://github.com/zed-industries/zed/issues/22692"
14314                );
14315            }
14316            self.request_autoscroll(Autoscroll::fit(), cx);
14317            self.unmark_text(window, cx);
14318            self.refresh_edit_prediction(true, false, window, cx);
14319            cx.emit(EditorEvent::Edited { transaction_id });
14320        }
14321    }
14322
14323    pub fn finalize_last_transaction(&mut self, cx: &mut Context<Self>) {
14324        self.buffer
14325            .update(cx, |buffer, cx| buffer.finalize_last_transaction(cx));
14326    }
14327
14328    pub fn group_until_transaction(&mut self, tx_id: TransactionId, cx: &mut Context<Self>) {
14329        self.buffer
14330            .update(cx, |buffer, cx| buffer.group_until_transaction(tx_id, cx));
14331    }
14332
14333    pub fn move_left(&mut self, _: &MoveLeft, window: &mut Window, cx: &mut Context<Self>) {
14334        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
14335        self.change_selections(Default::default(), window, cx, |s| {
14336            s.move_with(&mut |map, selection| {
14337                let cursor = if selection.is_empty() {
14338                    movement::left(map, selection.start)
14339                } else {
14340                    selection.start
14341                };
14342                selection.collapse_to(cursor, SelectionGoal::None);
14343            });
14344        })
14345    }
14346
14347    pub fn select_left(&mut self, _: &SelectLeft, window: &mut Window, cx: &mut Context<Self>) {
14348        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
14349        self.change_selections(Default::default(), window, cx, |s| {
14350            s.move_heads_with(&mut |map, head, _| (movement::left(map, head), SelectionGoal::None));
14351        })
14352    }
14353
14354    pub fn move_right(&mut self, _: &MoveRight, window: &mut Window, cx: &mut Context<Self>) {
14355        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
14356        self.change_selections(Default::default(), window, cx, |s| {
14357            s.move_with(&mut |map, selection| {
14358                let cursor = if selection.is_empty() {
14359                    movement::right(map, selection.end)
14360                } else {
14361                    selection.end
14362                };
14363                selection.collapse_to(cursor, SelectionGoal::None)
14364            });
14365        })
14366    }
14367
14368    pub fn select_right(&mut self, _: &SelectRight, window: &mut Window, cx: &mut Context<Self>) {
14369        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
14370        self.change_selections(Default::default(), window, cx, |s| {
14371            s.move_heads_with(&mut |map, head, _| {
14372                (movement::right(map, head), SelectionGoal::None)
14373            });
14374        });
14375    }
14376
14377    pub fn move_up(&mut self, _: &MoveUp, window: &mut Window, cx: &mut Context<Self>) {
14378        if self.take_rename(true, window, cx).is_some() {
14379            return;
14380        }
14381
14382        if self.mode.is_single_line() {
14383            cx.propagate();
14384            return;
14385        }
14386
14387        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
14388
14389        let text_layout_details = &self.text_layout_details(window, cx);
14390        let selection_count = self.selections.count();
14391        let first_selection = self.selections.first_anchor();
14392
14393        self.change_selections(Default::default(), window, cx, |s| {
14394            s.move_with(&mut |map, selection| {
14395                if !selection.is_empty() {
14396                    selection.goal = SelectionGoal::None;
14397                }
14398                let (cursor, goal) = movement::up(
14399                    map,
14400                    selection.start,
14401                    selection.goal,
14402                    false,
14403                    text_layout_details,
14404                );
14405                selection.collapse_to(cursor, goal);
14406            });
14407        });
14408
14409        if selection_count == 1 && first_selection.range() == self.selections.first_anchor().range()
14410        {
14411            cx.propagate();
14412        }
14413    }
14414
14415    pub fn move_up_by_lines(
14416        &mut self,
14417        action: &MoveUpByLines,
14418        window: &mut Window,
14419        cx: &mut Context<Self>,
14420    ) {
14421        if self.take_rename(true, window, cx).is_some() {
14422            return;
14423        }
14424
14425        if self.mode.is_single_line() {
14426            cx.propagate();
14427            return;
14428        }
14429
14430        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
14431
14432        let text_layout_details = &self.text_layout_details(window, cx);
14433
14434        self.change_selections(Default::default(), window, cx, |s| {
14435            s.move_with(&mut |map, selection| {
14436                if !selection.is_empty() {
14437                    selection.goal = SelectionGoal::None;
14438                }
14439                let (cursor, goal) = movement::up_by_rows(
14440                    map,
14441                    selection.start,
14442                    action.lines,
14443                    selection.goal,
14444                    false,
14445                    text_layout_details,
14446                );
14447                selection.collapse_to(cursor, goal);
14448            });
14449        })
14450    }
14451
14452    pub fn move_down_by_lines(
14453        &mut self,
14454        action: &MoveDownByLines,
14455        window: &mut Window,
14456        cx: &mut Context<Self>,
14457    ) {
14458        if self.take_rename(true, window, cx).is_some() {
14459            return;
14460        }
14461
14462        if self.mode.is_single_line() {
14463            cx.propagate();
14464            return;
14465        }
14466
14467        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
14468
14469        let text_layout_details = &self.text_layout_details(window, cx);
14470
14471        self.change_selections(Default::default(), window, cx, |s| {
14472            s.move_with(&mut |map, selection| {
14473                if !selection.is_empty() {
14474                    selection.goal = SelectionGoal::None;
14475                }
14476                let (cursor, goal) = movement::down_by_rows(
14477                    map,
14478                    selection.start,
14479                    action.lines,
14480                    selection.goal,
14481                    false,
14482                    text_layout_details,
14483                );
14484                selection.collapse_to(cursor, goal);
14485            });
14486        })
14487    }
14488
14489    pub fn select_down_by_lines(
14490        &mut self,
14491        action: &SelectDownByLines,
14492        window: &mut Window,
14493        cx: &mut Context<Self>,
14494    ) {
14495        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
14496        let text_layout_details = &self.text_layout_details(window, cx);
14497        self.change_selections(Default::default(), window, cx, |s| {
14498            s.move_heads_with(&mut |map, head, goal| {
14499                movement::down_by_rows(map, head, action.lines, goal, false, text_layout_details)
14500            })
14501        })
14502    }
14503
14504    pub fn select_up_by_lines(
14505        &mut self,
14506        action: &SelectUpByLines,
14507        window: &mut Window,
14508        cx: &mut Context<Self>,
14509    ) {
14510        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
14511        let text_layout_details = &self.text_layout_details(window, cx);
14512        self.change_selections(Default::default(), window, cx, |s| {
14513            s.move_heads_with(&mut |map, head, goal| {
14514                movement::up_by_rows(map, head, action.lines, goal, false, text_layout_details)
14515            })
14516        })
14517    }
14518
14519    pub fn select_page_up(
14520        &mut self,
14521        _: &SelectPageUp,
14522        window: &mut Window,
14523        cx: &mut Context<Self>,
14524    ) {
14525        let Some(row_count) = self.visible_row_count() else {
14526            return;
14527        };
14528
14529        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
14530
14531        let text_layout_details = &self.text_layout_details(window, cx);
14532
14533        self.change_selections(Default::default(), window, cx, |s| {
14534            s.move_heads_with(&mut |map, head, goal| {
14535                movement::up_by_rows(map, head, row_count, goal, false, text_layout_details)
14536            })
14537        })
14538    }
14539
14540    pub fn move_page_up(
14541        &mut self,
14542        action: &MovePageUp,
14543        window: &mut Window,
14544        cx: &mut Context<Self>,
14545    ) {
14546        if self.take_rename(true, window, cx).is_some() {
14547            return;
14548        }
14549
14550        if self
14551            .context_menu
14552            .borrow_mut()
14553            .as_mut()
14554            .map(|menu| menu.select_first(self.completion_provider.as_deref(), window, cx))
14555            .unwrap_or(false)
14556        {
14557            return;
14558        }
14559
14560        if matches!(self.mode, EditorMode::SingleLine) {
14561            cx.propagate();
14562            return;
14563        }
14564
14565        let Some(row_count) = self.visible_row_count() else {
14566            return;
14567        };
14568
14569        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
14570
14571        let effects = if action.center_cursor {
14572            SelectionEffects::scroll(Autoscroll::center())
14573        } else {
14574            SelectionEffects::default()
14575        };
14576
14577        let text_layout_details = &self.text_layout_details(window, cx);
14578
14579        self.change_selections(effects, window, cx, |s| {
14580            s.move_with(&mut |map, selection| {
14581                if !selection.is_empty() {
14582                    selection.goal = SelectionGoal::None;
14583                }
14584                let (cursor, goal) = movement::up_by_rows(
14585                    map,
14586                    selection.end,
14587                    row_count,
14588                    selection.goal,
14589                    false,
14590                    text_layout_details,
14591                );
14592                selection.collapse_to(cursor, goal);
14593            });
14594        });
14595    }
14596
14597    pub fn select_up(&mut self, _: &SelectUp, window: &mut Window, cx: &mut Context<Self>) {
14598        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
14599        let text_layout_details = &self.text_layout_details(window, cx);
14600        self.change_selections(Default::default(), window, cx, |s| {
14601            s.move_heads_with(&mut |map, head, goal| {
14602                movement::up(map, head, goal, false, text_layout_details)
14603            })
14604        })
14605    }
14606
14607    pub fn move_down(&mut self, _: &MoveDown, window: &mut Window, cx: &mut Context<Self>) {
14608        self.take_rename(true, window, cx);
14609
14610        if self.mode.is_single_line() {
14611            cx.propagate();
14612            return;
14613        }
14614
14615        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
14616
14617        let text_layout_details = &self.text_layout_details(window, cx);
14618        let selection_count = self.selections.count();
14619        let first_selection = self.selections.first_anchor();
14620
14621        self.change_selections(Default::default(), window, cx, |s| {
14622            s.move_with(&mut |map, selection| {
14623                if !selection.is_empty() {
14624                    selection.goal = SelectionGoal::None;
14625                }
14626                let (cursor, goal) = movement::down(
14627                    map,
14628                    selection.end,
14629                    selection.goal,
14630                    false,
14631                    text_layout_details,
14632                );
14633                selection.collapse_to(cursor, goal);
14634            });
14635        });
14636
14637        if selection_count == 1 && first_selection.range() == self.selections.first_anchor().range()
14638        {
14639            cx.propagate();
14640        }
14641    }
14642
14643    pub fn select_page_down(
14644        &mut self,
14645        _: &SelectPageDown,
14646        window: &mut Window,
14647        cx: &mut Context<Self>,
14648    ) {
14649        let Some(row_count) = self.visible_row_count() else {
14650            return;
14651        };
14652
14653        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
14654
14655        let text_layout_details = &self.text_layout_details(window, cx);
14656
14657        self.change_selections(Default::default(), window, cx, |s| {
14658            s.move_heads_with(&mut |map, head, goal| {
14659                movement::down_by_rows(map, head, row_count, goal, false, text_layout_details)
14660            })
14661        })
14662    }
14663
14664    pub fn move_page_down(
14665        &mut self,
14666        action: &MovePageDown,
14667        window: &mut Window,
14668        cx: &mut Context<Self>,
14669    ) {
14670        if self.take_rename(true, window, cx).is_some() {
14671            return;
14672        }
14673
14674        if self
14675            .context_menu
14676            .borrow_mut()
14677            .as_mut()
14678            .map(|menu| menu.select_last(self.completion_provider.as_deref(), window, cx))
14679            .unwrap_or(false)
14680        {
14681            return;
14682        }
14683
14684        if matches!(self.mode, EditorMode::SingleLine) {
14685            cx.propagate();
14686            return;
14687        }
14688
14689        let Some(row_count) = self.visible_row_count() else {
14690            return;
14691        };
14692
14693        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
14694
14695        let effects = if action.center_cursor {
14696            SelectionEffects::scroll(Autoscroll::center())
14697        } else {
14698            SelectionEffects::default()
14699        };
14700
14701        let text_layout_details = &self.text_layout_details(window, cx);
14702        self.change_selections(effects, window, cx, |s| {
14703            s.move_with(&mut |map, selection| {
14704                if !selection.is_empty() {
14705                    selection.goal = SelectionGoal::None;
14706                }
14707                let (cursor, goal) = movement::down_by_rows(
14708                    map,
14709                    selection.end,
14710                    row_count,
14711                    selection.goal,
14712                    false,
14713                    text_layout_details,
14714                );
14715                selection.collapse_to(cursor, goal);
14716            });
14717        });
14718    }
14719
14720    pub fn select_down(&mut self, _: &SelectDown, window: &mut Window, cx: &mut Context<Self>) {
14721        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
14722        let text_layout_details = &self.text_layout_details(window, cx);
14723        self.change_selections(Default::default(), window, cx, |s| {
14724            s.move_heads_with(&mut |map, head, goal| {
14725                movement::down(map, head, goal, false, text_layout_details)
14726            })
14727        });
14728    }
14729
14730    pub fn context_menu_first(
14731        &mut self,
14732        _: &ContextMenuFirst,
14733        window: &mut Window,
14734        cx: &mut Context<Self>,
14735    ) {
14736        if let Some(context_menu) = self.context_menu.borrow_mut().as_mut() {
14737            context_menu.select_first(self.completion_provider.as_deref(), window, cx);
14738        }
14739    }
14740
14741    pub fn context_menu_prev(
14742        &mut self,
14743        _: &ContextMenuPrevious,
14744        window: &mut Window,
14745        cx: &mut Context<Self>,
14746    ) {
14747        if let Some(context_menu) = self.context_menu.borrow_mut().as_mut() {
14748            context_menu.select_prev(self.completion_provider.as_deref(), window, cx);
14749        }
14750    }
14751
14752    pub fn context_menu_next(
14753        &mut self,
14754        _: &ContextMenuNext,
14755        window: &mut Window,
14756        cx: &mut Context<Self>,
14757    ) {
14758        if let Some(context_menu) = self.context_menu.borrow_mut().as_mut() {
14759            context_menu.select_next(self.completion_provider.as_deref(), window, cx);
14760        }
14761    }
14762
14763    pub fn context_menu_last(
14764        &mut self,
14765        _: &ContextMenuLast,
14766        window: &mut Window,
14767        cx: &mut Context<Self>,
14768    ) {
14769        if let Some(context_menu) = self.context_menu.borrow_mut().as_mut() {
14770            context_menu.select_last(self.completion_provider.as_deref(), window, cx);
14771        }
14772    }
14773
14774    pub fn signature_help_prev(
14775        &mut self,
14776        _: &SignatureHelpPrevious,
14777        _: &mut Window,
14778        cx: &mut Context<Self>,
14779    ) {
14780        if let Some(popover) = self.signature_help_state.popover_mut() {
14781            if popover.current_signature == 0 {
14782                popover.current_signature = popover.signatures.len() - 1;
14783            } else {
14784                popover.current_signature -= 1;
14785            }
14786            cx.notify();
14787        }
14788    }
14789
14790    pub fn signature_help_next(
14791        &mut self,
14792        _: &SignatureHelpNext,
14793        _: &mut Window,
14794        cx: &mut Context<Self>,
14795    ) {
14796        if let Some(popover) = self.signature_help_state.popover_mut() {
14797            if popover.current_signature + 1 == popover.signatures.len() {
14798                popover.current_signature = 0;
14799            } else {
14800                popover.current_signature += 1;
14801            }
14802            cx.notify();
14803        }
14804    }
14805
14806    pub fn move_to_previous_word_start(
14807        &mut self,
14808        _: &MoveToPreviousWordStart,
14809        window: &mut Window,
14810        cx: &mut Context<Self>,
14811    ) {
14812        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
14813        self.change_selections(Default::default(), window, cx, |s| {
14814            s.move_cursors_with(&mut |map, head, _| {
14815                (
14816                    movement::previous_word_start(map, head),
14817                    SelectionGoal::None,
14818                )
14819            });
14820        })
14821    }
14822
14823    pub fn move_to_previous_subword_start(
14824        &mut self,
14825        _: &MoveToPreviousSubwordStart,
14826        window: &mut Window,
14827        cx: &mut Context<Self>,
14828    ) {
14829        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
14830        self.change_selections(Default::default(), window, cx, |s| {
14831            s.move_cursors_with(&mut |map, head, _| {
14832                (
14833                    movement::previous_subword_start(map, head),
14834                    SelectionGoal::None,
14835                )
14836            });
14837        })
14838    }
14839
14840    pub fn select_to_previous_word_start(
14841        &mut self,
14842        _: &SelectToPreviousWordStart,
14843        window: &mut Window,
14844        cx: &mut Context<Self>,
14845    ) {
14846        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
14847        self.change_selections(Default::default(), window, cx, |s| {
14848            s.move_heads_with(&mut |map, head, _| {
14849                (
14850                    movement::previous_word_start(map, head),
14851                    SelectionGoal::None,
14852                )
14853            });
14854        })
14855    }
14856
14857    pub fn select_to_previous_subword_start(
14858        &mut self,
14859        _: &SelectToPreviousSubwordStart,
14860        window: &mut Window,
14861        cx: &mut Context<Self>,
14862    ) {
14863        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
14864        self.change_selections(Default::default(), window, cx, |s| {
14865            s.move_heads_with(&mut |map, head, _| {
14866                (
14867                    movement::previous_subword_start(map, head),
14868                    SelectionGoal::None,
14869                )
14870            });
14871        })
14872    }
14873
14874    pub fn delete_to_previous_word_start(
14875        &mut self,
14876        action: &DeleteToPreviousWordStart,
14877        window: &mut Window,
14878        cx: &mut Context<Self>,
14879    ) {
14880        self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
14881        self.transact(window, cx, |this, window, cx| {
14882            this.select_autoclose_pair(window, cx);
14883            this.change_selections(Default::default(), window, cx, |s| {
14884                s.move_with(&mut |map, selection| {
14885                    if selection.is_empty() {
14886                        let mut cursor = if action.ignore_newlines {
14887                            movement::previous_word_start(map, selection.head())
14888                        } else {
14889                            movement::previous_word_start_or_newline(map, selection.head())
14890                        };
14891                        cursor = movement::adjust_greedy_deletion(
14892                            map,
14893                            selection.head(),
14894                            cursor,
14895                            action.ignore_brackets,
14896                        );
14897                        selection.set_head(cursor, SelectionGoal::None);
14898                    }
14899                });
14900            });
14901            this.insert("", window, cx);
14902        });
14903    }
14904
14905    pub fn delete_to_previous_subword_start(
14906        &mut self,
14907        action: &DeleteToPreviousSubwordStart,
14908        window: &mut Window,
14909        cx: &mut Context<Self>,
14910    ) {
14911        self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
14912        self.transact(window, cx, |this, window, cx| {
14913            this.select_autoclose_pair(window, cx);
14914            this.change_selections(Default::default(), window, cx, |s| {
14915                s.move_with(&mut |map, selection| {
14916                    if selection.is_empty() {
14917                        let mut cursor = if action.ignore_newlines {
14918                            movement::previous_subword_start(map, selection.head())
14919                        } else {
14920                            movement::previous_subword_start_or_newline(map, selection.head())
14921                        };
14922                        cursor = movement::adjust_greedy_deletion(
14923                            map,
14924                            selection.head(),
14925                            cursor,
14926                            action.ignore_brackets,
14927                        );
14928                        selection.set_head(cursor, SelectionGoal::None);
14929                    }
14930                });
14931            });
14932            this.insert("", window, cx);
14933        });
14934    }
14935
14936    pub fn move_to_next_word_end(
14937        &mut self,
14938        _: &MoveToNextWordEnd,
14939        window: &mut Window,
14940        cx: &mut Context<Self>,
14941    ) {
14942        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
14943        self.change_selections(Default::default(), window, cx, |s| {
14944            s.move_cursors_with(&mut |map, head, _| {
14945                (movement::next_word_end(map, head), SelectionGoal::None)
14946            });
14947        })
14948    }
14949
14950    pub fn move_to_next_subword_end(
14951        &mut self,
14952        _: &MoveToNextSubwordEnd,
14953        window: &mut Window,
14954        cx: &mut Context<Self>,
14955    ) {
14956        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
14957        self.change_selections(Default::default(), window, cx, |s| {
14958            s.move_cursors_with(&mut |map, head, _| {
14959                (movement::next_subword_end(map, head), SelectionGoal::None)
14960            });
14961        })
14962    }
14963
14964    pub fn select_to_next_word_end(
14965        &mut self,
14966        _: &SelectToNextWordEnd,
14967        window: &mut Window,
14968        cx: &mut Context<Self>,
14969    ) {
14970        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
14971        self.change_selections(Default::default(), window, cx, |s| {
14972            s.move_heads_with(&mut |map, head, _| {
14973                (movement::next_word_end(map, head), SelectionGoal::None)
14974            });
14975        })
14976    }
14977
14978    pub fn select_to_next_subword_end(
14979        &mut self,
14980        _: &SelectToNextSubwordEnd,
14981        window: &mut Window,
14982        cx: &mut Context<Self>,
14983    ) {
14984        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
14985        self.change_selections(Default::default(), window, cx, |s| {
14986            s.move_heads_with(&mut |map, head, _| {
14987                (movement::next_subword_end(map, head), SelectionGoal::None)
14988            });
14989        })
14990    }
14991
14992    pub fn delete_to_next_word_end(
14993        &mut self,
14994        action: &DeleteToNextWordEnd,
14995        window: &mut Window,
14996        cx: &mut Context<Self>,
14997    ) {
14998        self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
14999        self.transact(window, cx, |this, window, cx| {
15000            this.change_selections(Default::default(), window, cx, |s| {
15001                s.move_with(&mut |map, selection| {
15002                    if selection.is_empty() {
15003                        let mut cursor = if action.ignore_newlines {
15004                            movement::next_word_end(map, selection.head())
15005                        } else {
15006                            movement::next_word_end_or_newline(map, selection.head())
15007                        };
15008                        cursor = movement::adjust_greedy_deletion(
15009                            map,
15010                            selection.head(),
15011                            cursor,
15012                            action.ignore_brackets,
15013                        );
15014                        selection.set_head(cursor, SelectionGoal::None);
15015                    }
15016                });
15017            });
15018            this.insert("", window, cx);
15019        });
15020    }
15021
15022    pub fn delete_to_next_subword_end(
15023        &mut self,
15024        action: &DeleteToNextSubwordEnd,
15025        window: &mut Window,
15026        cx: &mut Context<Self>,
15027    ) {
15028        self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
15029        self.transact(window, cx, |this, window, cx| {
15030            this.change_selections(Default::default(), window, cx, |s| {
15031                s.move_with(&mut |map, selection| {
15032                    if selection.is_empty() {
15033                        let mut cursor = if action.ignore_newlines {
15034                            movement::next_subword_end(map, selection.head())
15035                        } else {
15036                            movement::next_subword_end_or_newline(map, selection.head())
15037                        };
15038                        cursor = movement::adjust_greedy_deletion(
15039                            map,
15040                            selection.head(),
15041                            cursor,
15042                            action.ignore_brackets,
15043                        );
15044                        selection.set_head(cursor, SelectionGoal::None);
15045                    }
15046                });
15047            });
15048            this.insert("", window, cx);
15049        });
15050    }
15051
15052    pub fn move_to_beginning_of_line(
15053        &mut self,
15054        action: &MoveToBeginningOfLine,
15055        window: &mut Window,
15056        cx: &mut Context<Self>,
15057    ) {
15058        let stop_at_indent = action.stop_at_indent && !self.mode.is_single_line();
15059        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
15060        self.change_selections(Default::default(), window, cx, |s| {
15061            s.move_cursors_with(&mut |map, head, _| {
15062                (
15063                    movement::indented_line_beginning(
15064                        map,
15065                        head,
15066                        action.stop_at_soft_wraps,
15067                        stop_at_indent,
15068                    ),
15069                    SelectionGoal::None,
15070                )
15071            });
15072        })
15073    }
15074
15075    pub fn select_to_beginning_of_line(
15076        &mut self,
15077        action: &SelectToBeginningOfLine,
15078        window: &mut Window,
15079        cx: &mut Context<Self>,
15080    ) {
15081        let stop_at_indent = action.stop_at_indent && !self.mode.is_single_line();
15082        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
15083        self.change_selections(Default::default(), window, cx, |s| {
15084            s.move_heads_with(&mut |map, head, _| {
15085                (
15086                    movement::indented_line_beginning(
15087                        map,
15088                        head,
15089                        action.stop_at_soft_wraps,
15090                        stop_at_indent,
15091                    ),
15092                    SelectionGoal::None,
15093                )
15094            });
15095        });
15096    }
15097
15098    pub fn delete_to_beginning_of_line(
15099        &mut self,
15100        action: &DeleteToBeginningOfLine,
15101        window: &mut Window,
15102        cx: &mut Context<Self>,
15103    ) {
15104        self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
15105        self.transact(window, cx, |this, window, cx| {
15106            this.change_selections(Default::default(), window, cx, |s| {
15107                s.move_with(&mut |_, selection| {
15108                    selection.reversed = true;
15109                });
15110            });
15111
15112            this.select_to_beginning_of_line(
15113                &SelectToBeginningOfLine {
15114                    stop_at_soft_wraps: false,
15115                    stop_at_indent: action.stop_at_indent,
15116                },
15117                window,
15118                cx,
15119            );
15120            this.backspace(&Backspace, window, cx);
15121        });
15122    }
15123
15124    pub fn move_to_end_of_line(
15125        &mut self,
15126        action: &MoveToEndOfLine,
15127        window: &mut Window,
15128        cx: &mut Context<Self>,
15129    ) {
15130        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
15131        self.change_selections(Default::default(), window, cx, |s| {
15132            s.move_cursors_with(&mut |map, head, _| {
15133                (
15134                    movement::line_end(map, head, action.stop_at_soft_wraps),
15135                    SelectionGoal::None,
15136                )
15137            });
15138        })
15139    }
15140
15141    pub fn select_to_end_of_line(
15142        &mut self,
15143        action: &SelectToEndOfLine,
15144        window: &mut Window,
15145        cx: &mut Context<Self>,
15146    ) {
15147        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
15148        self.change_selections(Default::default(), window, cx, |s| {
15149            s.move_heads_with(&mut |map, head, _| {
15150                (
15151                    movement::line_end(map, head, action.stop_at_soft_wraps),
15152                    SelectionGoal::None,
15153                )
15154            });
15155        })
15156    }
15157
15158    pub fn delete_to_end_of_line(
15159        &mut self,
15160        _: &DeleteToEndOfLine,
15161        window: &mut Window,
15162        cx: &mut Context<Self>,
15163    ) {
15164        self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
15165        self.transact(window, cx, |this, window, cx| {
15166            this.select_to_end_of_line(
15167                &SelectToEndOfLine {
15168                    stop_at_soft_wraps: false,
15169                },
15170                window,
15171                cx,
15172            );
15173            this.delete(&Delete, window, cx);
15174        });
15175    }
15176
15177    pub fn cut_to_end_of_line(
15178        &mut self,
15179        action: &CutToEndOfLine,
15180        window: &mut Window,
15181        cx: &mut Context<Self>,
15182    ) {
15183        self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
15184        self.transact(window, cx, |this, window, cx| {
15185            this.select_to_end_of_line(
15186                &SelectToEndOfLine {
15187                    stop_at_soft_wraps: false,
15188                },
15189                window,
15190                cx,
15191            );
15192            if !action.stop_at_newlines {
15193                this.change_selections(Default::default(), window, cx, |s| {
15194                    s.move_with(&mut |_, sel| {
15195                        if sel.is_empty() {
15196                            sel.end = DisplayPoint::new(sel.end.row() + 1_u32, 0);
15197                        }
15198                    });
15199                });
15200            }
15201            this.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
15202            let item = this.cut_common(false, window, cx);
15203            cx.write_to_clipboard(item);
15204        });
15205    }
15206
15207    pub fn move_to_start_of_paragraph(
15208        &mut self,
15209        _: &MoveToStartOfParagraph,
15210        window: &mut Window,
15211        cx: &mut Context<Self>,
15212    ) {
15213        if matches!(self.mode, EditorMode::SingleLine) {
15214            cx.propagate();
15215            return;
15216        }
15217        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
15218        self.change_selections(Default::default(), window, cx, |s| {
15219            s.move_with(&mut |map, selection| {
15220                selection.collapse_to(
15221                    movement::start_of_paragraph(map, selection.head(), 1),
15222                    SelectionGoal::None,
15223                )
15224            });
15225        })
15226    }
15227
15228    pub fn move_to_end_of_paragraph(
15229        &mut self,
15230        _: &MoveToEndOfParagraph,
15231        window: &mut Window,
15232        cx: &mut Context<Self>,
15233    ) {
15234        if matches!(self.mode, EditorMode::SingleLine) {
15235            cx.propagate();
15236            return;
15237        }
15238        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
15239        self.change_selections(Default::default(), window, cx, |s| {
15240            s.move_with(&mut |map, selection| {
15241                selection.collapse_to(
15242                    movement::end_of_paragraph(map, selection.head(), 1),
15243                    SelectionGoal::None,
15244                )
15245            });
15246        })
15247    }
15248
15249    pub fn select_to_start_of_paragraph(
15250        &mut self,
15251        _: &SelectToStartOfParagraph,
15252        window: &mut Window,
15253        cx: &mut Context<Self>,
15254    ) {
15255        if matches!(self.mode, EditorMode::SingleLine) {
15256            cx.propagate();
15257            return;
15258        }
15259        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
15260        self.change_selections(Default::default(), window, cx, |s| {
15261            s.move_heads_with(&mut |map, head, _| {
15262                (
15263                    movement::start_of_paragraph(map, head, 1),
15264                    SelectionGoal::None,
15265                )
15266            });
15267        })
15268    }
15269
15270    pub fn select_to_end_of_paragraph(
15271        &mut self,
15272        _: &SelectToEndOfParagraph,
15273        window: &mut Window,
15274        cx: &mut Context<Self>,
15275    ) {
15276        if matches!(self.mode, EditorMode::SingleLine) {
15277            cx.propagate();
15278            return;
15279        }
15280        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
15281        self.change_selections(Default::default(), window, cx, |s| {
15282            s.move_heads_with(&mut |map, head, _| {
15283                (
15284                    movement::end_of_paragraph(map, head, 1),
15285                    SelectionGoal::None,
15286                )
15287            });
15288        })
15289    }
15290
15291    pub fn move_to_start_of_excerpt(
15292        &mut self,
15293        _: &MoveToStartOfExcerpt,
15294        window: &mut Window,
15295        cx: &mut Context<Self>,
15296    ) {
15297        if matches!(self.mode, EditorMode::SingleLine) {
15298            cx.propagate();
15299            return;
15300        }
15301        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
15302        self.change_selections(Default::default(), window, cx, |s| {
15303            s.move_with(&mut |map, selection| {
15304                selection.collapse_to(
15305                    movement::start_of_excerpt(
15306                        map,
15307                        selection.head(),
15308                        workspace::searchable::Direction::Prev,
15309                    ),
15310                    SelectionGoal::None,
15311                )
15312            });
15313        })
15314    }
15315
15316    pub fn move_to_start_of_next_excerpt(
15317        &mut self,
15318        _: &MoveToStartOfNextExcerpt,
15319        window: &mut Window,
15320        cx: &mut Context<Self>,
15321    ) {
15322        if matches!(self.mode, EditorMode::SingleLine) {
15323            cx.propagate();
15324            return;
15325        }
15326
15327        self.change_selections(Default::default(), window, cx, |s| {
15328            s.move_with(&mut |map, selection| {
15329                selection.collapse_to(
15330                    movement::start_of_excerpt(
15331                        map,
15332                        selection.head(),
15333                        workspace::searchable::Direction::Next,
15334                    ),
15335                    SelectionGoal::None,
15336                )
15337            });
15338        })
15339    }
15340
15341    pub fn move_to_end_of_excerpt(
15342        &mut self,
15343        _: &MoveToEndOfExcerpt,
15344        window: &mut Window,
15345        cx: &mut Context<Self>,
15346    ) {
15347        if matches!(self.mode, EditorMode::SingleLine) {
15348            cx.propagate();
15349            return;
15350        }
15351        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
15352        self.change_selections(Default::default(), window, cx, |s| {
15353            s.move_with(&mut |map, selection| {
15354                selection.collapse_to(
15355                    movement::end_of_excerpt(
15356                        map,
15357                        selection.head(),
15358                        workspace::searchable::Direction::Next,
15359                    ),
15360                    SelectionGoal::None,
15361                )
15362            });
15363        })
15364    }
15365
15366    pub fn move_to_end_of_previous_excerpt(
15367        &mut self,
15368        _: &MoveToEndOfPreviousExcerpt,
15369        window: &mut Window,
15370        cx: &mut Context<Self>,
15371    ) {
15372        if matches!(self.mode, EditorMode::SingleLine) {
15373            cx.propagate();
15374            return;
15375        }
15376        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
15377        self.change_selections(Default::default(), window, cx, |s| {
15378            s.move_with(&mut |map, selection| {
15379                selection.collapse_to(
15380                    movement::end_of_excerpt(
15381                        map,
15382                        selection.head(),
15383                        workspace::searchable::Direction::Prev,
15384                    ),
15385                    SelectionGoal::None,
15386                )
15387            });
15388        })
15389    }
15390
15391    pub fn select_to_start_of_excerpt(
15392        &mut self,
15393        _: &SelectToStartOfExcerpt,
15394        window: &mut Window,
15395        cx: &mut Context<Self>,
15396    ) {
15397        if matches!(self.mode, EditorMode::SingleLine) {
15398            cx.propagate();
15399            return;
15400        }
15401        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
15402        self.change_selections(Default::default(), window, cx, |s| {
15403            s.move_heads_with(&mut |map, head, _| {
15404                (
15405                    movement::start_of_excerpt(map, head, workspace::searchable::Direction::Prev),
15406                    SelectionGoal::None,
15407                )
15408            });
15409        })
15410    }
15411
15412    pub fn select_to_start_of_next_excerpt(
15413        &mut self,
15414        _: &SelectToStartOfNextExcerpt,
15415        window: &mut Window,
15416        cx: &mut Context<Self>,
15417    ) {
15418        if matches!(self.mode, EditorMode::SingleLine) {
15419            cx.propagate();
15420            return;
15421        }
15422        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
15423        self.change_selections(Default::default(), window, cx, |s| {
15424            s.move_heads_with(&mut |map, head, _| {
15425                (
15426                    movement::start_of_excerpt(map, head, workspace::searchable::Direction::Next),
15427                    SelectionGoal::None,
15428                )
15429            });
15430        })
15431    }
15432
15433    pub fn select_to_end_of_excerpt(
15434        &mut self,
15435        _: &SelectToEndOfExcerpt,
15436        window: &mut Window,
15437        cx: &mut Context<Self>,
15438    ) {
15439        if matches!(self.mode, EditorMode::SingleLine) {
15440            cx.propagate();
15441            return;
15442        }
15443        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
15444        self.change_selections(Default::default(), window, cx, |s| {
15445            s.move_heads_with(&mut |map, head, _| {
15446                (
15447                    movement::end_of_excerpt(map, head, workspace::searchable::Direction::Next),
15448                    SelectionGoal::None,
15449                )
15450            });
15451        })
15452    }
15453
15454    pub fn select_to_end_of_previous_excerpt(
15455        &mut self,
15456        _: &SelectToEndOfPreviousExcerpt,
15457        window: &mut Window,
15458        cx: &mut Context<Self>,
15459    ) {
15460        if matches!(self.mode, EditorMode::SingleLine) {
15461            cx.propagate();
15462            return;
15463        }
15464        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
15465        self.change_selections(Default::default(), window, cx, |s| {
15466            s.move_heads_with(&mut |map, head, _| {
15467                (
15468                    movement::end_of_excerpt(map, head, workspace::searchable::Direction::Prev),
15469                    SelectionGoal::None,
15470                )
15471            });
15472        })
15473    }
15474
15475    pub fn move_to_beginning(
15476        &mut self,
15477        _: &MoveToBeginning,
15478        window: &mut Window,
15479        cx: &mut Context<Self>,
15480    ) {
15481        if matches!(self.mode, EditorMode::SingleLine) {
15482            cx.propagate();
15483            return;
15484        }
15485        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
15486        self.change_selections(Default::default(), window, cx, |s| {
15487            s.select_ranges(vec![Anchor::min()..Anchor::min()]);
15488        });
15489    }
15490
15491    pub fn select_to_beginning(
15492        &mut self,
15493        _: &SelectToBeginning,
15494        window: &mut Window,
15495        cx: &mut Context<Self>,
15496    ) {
15497        let mut selection = self.selections.last::<Point>(&self.display_snapshot(cx));
15498        selection.set_head(Point::zero(), SelectionGoal::None);
15499        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
15500        self.change_selections(Default::default(), window, cx, |s| {
15501            s.select(vec![selection]);
15502        });
15503    }
15504
15505    pub fn move_to_end(&mut self, _: &MoveToEnd, window: &mut Window, cx: &mut Context<Self>) {
15506        if matches!(self.mode, EditorMode::SingleLine) {
15507            cx.propagate();
15508            return;
15509        }
15510        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
15511        let cursor = self.buffer.read(cx).read(cx).len();
15512        self.change_selections(Default::default(), window, cx, |s| {
15513            s.select_ranges(vec![cursor..cursor])
15514        });
15515    }
15516
15517    pub fn set_nav_history(&mut self, nav_history: Option<ItemNavHistory>) {
15518        self.nav_history = nav_history;
15519    }
15520
15521    pub fn nav_history(&self) -> Option<&ItemNavHistory> {
15522        self.nav_history.as_ref()
15523    }
15524
15525    pub fn create_nav_history_entry(&mut self, cx: &mut Context<Self>) {
15526        self.push_to_nav_history(
15527            self.selections.newest_anchor().head(),
15528            None,
15529            false,
15530            true,
15531            cx,
15532        );
15533    }
15534
15535    fn navigation_data(&self, cursor_anchor: Anchor, cx: &mut Context<Self>) -> NavigationData {
15536        let display_snapshot = self.display_map.update(cx, |map, cx| map.snapshot(cx));
15537        let buffer = self.buffer.read(cx).read(cx);
15538        let cursor_position = cursor_anchor.to_point(&buffer);
15539        let scroll_anchor = self.scroll_manager.native_anchor(&display_snapshot, cx);
15540        let scroll_top_row = scroll_anchor.top_row(&buffer);
15541        drop(buffer);
15542
15543        NavigationData {
15544            cursor_anchor,
15545            cursor_position,
15546            scroll_anchor,
15547            scroll_top_row,
15548        }
15549    }
15550
15551    fn navigation_entry(
15552        &self,
15553        cursor_anchor: Anchor,
15554        cx: &mut Context<Self>,
15555    ) -> Option<NavigationEntry> {
15556        let Some(history) = self.nav_history.clone() else {
15557            return None;
15558        };
15559        let data = self.navigation_data(cursor_anchor, cx);
15560        Some(history.navigation_entry(Some(Arc::new(data) as Arc<dyn Any + Send + Sync>)))
15561    }
15562
15563    fn push_to_nav_history(
15564        &mut self,
15565        cursor_anchor: Anchor,
15566        new_position: Option<Point>,
15567        is_deactivate: bool,
15568        always: bool,
15569        cx: &mut Context<Self>,
15570    ) {
15571        let data = self.navigation_data(cursor_anchor, cx);
15572        if let Some(nav_history) = self.nav_history.as_mut() {
15573            if let Some(new_position) = new_position {
15574                let row_delta = (new_position.row as i64 - data.cursor_position.row as i64).abs();
15575                if row_delta == 0 || (row_delta < MIN_NAVIGATION_HISTORY_ROW_DELTA && !always) {
15576                    return;
15577                }
15578            }
15579
15580            let cursor_row = data.cursor_position.row;
15581            nav_history.push(Some(data), Some(cursor_row), cx);
15582            cx.emit(EditorEvent::PushedToNavHistory {
15583                anchor: cursor_anchor,
15584                is_deactivate,
15585            })
15586        }
15587    }
15588
15589    pub fn select_to_end(&mut self, _: &SelectToEnd, window: &mut Window, cx: &mut Context<Self>) {
15590        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
15591        let buffer = self.buffer.read(cx).snapshot(cx);
15592        let mut selection = self
15593            .selections
15594            .first::<MultiBufferOffset>(&self.display_snapshot(cx));
15595        selection.set_head(buffer.len(), SelectionGoal::None);
15596        self.change_selections(Default::default(), window, cx, |s| {
15597            s.select(vec![selection]);
15598        });
15599    }
15600
15601    pub fn select_all(&mut self, _: &SelectAll, window: &mut Window, cx: &mut Context<Self>) {
15602        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
15603        self.change_selections(SelectionEffects::no_scroll(), window, cx, |s| {
15604            s.select_ranges([Anchor::min()..Anchor::max()]);
15605        });
15606    }
15607
15608    pub fn select_line(&mut self, _: &SelectLine, window: &mut Window, cx: &mut Context<Self>) {
15609        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
15610        let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
15611        let mut selections = self.selections.all::<Point>(&display_map);
15612        let max_point = display_map.buffer_snapshot().max_point();
15613        for selection in &mut selections {
15614            let rows = selection.spanned_rows(true, &display_map);
15615            selection.start = Point::new(rows.start.0, 0);
15616            selection.end = cmp::min(max_point, Point::new(rows.end.0, 0));
15617            selection.reversed = false;
15618        }
15619        self.change_selections(Default::default(), window, cx, |s| {
15620            s.select(selections);
15621        });
15622    }
15623
15624    pub fn split_selection_into_lines(
15625        &mut self,
15626        action: &SplitSelectionIntoLines,
15627        window: &mut Window,
15628        cx: &mut Context<Self>,
15629    ) {
15630        let selections = self
15631            .selections
15632            .all::<Point>(&self.display_snapshot(cx))
15633            .into_iter()
15634            .map(|selection| selection.start..selection.end)
15635            .collect::<Vec<_>>();
15636        self.unfold_ranges(&selections, true, false, cx);
15637
15638        let mut new_selection_ranges = Vec::new();
15639        {
15640            let buffer = self.buffer.read(cx).read(cx);
15641            for selection in selections {
15642                for row in selection.start.row..selection.end.row {
15643                    let line_start = Point::new(row, 0);
15644                    let line_end = Point::new(row, buffer.line_len(MultiBufferRow(row)));
15645
15646                    if action.keep_selections {
15647                        // Keep the selection range for each line
15648                        let selection_start = if row == selection.start.row {
15649                            selection.start
15650                        } else {
15651                            line_start
15652                        };
15653                        new_selection_ranges.push(selection_start..line_end);
15654                    } else {
15655                        // Collapse to cursor at end of line
15656                        new_selection_ranges.push(line_end..line_end);
15657                    }
15658                }
15659
15660                let is_multiline_selection = selection.start.row != selection.end.row;
15661                // Don't insert last one if it's a multi-line selection ending at the start of a line,
15662                // so this action feels more ergonomic when paired with other selection operations
15663                let should_skip_last = is_multiline_selection && selection.end.column == 0;
15664                if !should_skip_last {
15665                    if action.keep_selections {
15666                        if is_multiline_selection {
15667                            let line_start = Point::new(selection.end.row, 0);
15668                            new_selection_ranges.push(line_start..selection.end);
15669                        } else {
15670                            new_selection_ranges.push(selection.start..selection.end);
15671                        }
15672                    } else {
15673                        new_selection_ranges.push(selection.end..selection.end);
15674                    }
15675                }
15676            }
15677        }
15678        self.change_selections(SelectionEffects::no_scroll(), window, cx, |s| {
15679            s.select_ranges(new_selection_ranges);
15680        });
15681    }
15682
15683    pub fn add_selection_above(
15684        &mut self,
15685        action: &AddSelectionAbove,
15686        window: &mut Window,
15687        cx: &mut Context<Self>,
15688    ) {
15689        self.add_selection(true, action.skip_soft_wrap, window, cx);
15690    }
15691
15692    pub fn add_selection_below(
15693        &mut self,
15694        action: &AddSelectionBelow,
15695        window: &mut Window,
15696        cx: &mut Context<Self>,
15697    ) {
15698        self.add_selection(false, action.skip_soft_wrap, window, cx);
15699    }
15700
15701    fn add_selection(
15702        &mut self,
15703        above: bool,
15704        skip_soft_wrap: bool,
15705        window: &mut Window,
15706        cx: &mut Context<Self>,
15707    ) {
15708        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
15709
15710        let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
15711        let all_selections = self.selections.all::<Point>(&display_map);
15712        let text_layout_details = self.text_layout_details(window, cx);
15713
15714        let (mut columnar_selections, new_selections_to_columnarize) = {
15715            if let Some(state) = self.add_selections_state.as_ref() {
15716                let columnar_selection_ids: HashSet<_> = state
15717                    .groups
15718                    .iter()
15719                    .flat_map(|group| group.stack.iter())
15720                    .copied()
15721                    .collect();
15722
15723                all_selections
15724                    .into_iter()
15725                    .partition(|s| columnar_selection_ids.contains(&s.id))
15726            } else {
15727                (Vec::new(), all_selections)
15728            }
15729        };
15730
15731        let mut state = self
15732            .add_selections_state
15733            .take()
15734            .unwrap_or_else(|| AddSelectionsState { groups: Vec::new() });
15735
15736        for selection in new_selections_to_columnarize {
15737            let range = selection.display_range(&display_map).sorted();
15738            let start_x = display_map.x_for_display_point(range.start, &text_layout_details);
15739            let end_x = display_map.x_for_display_point(range.end, &text_layout_details);
15740            let positions = start_x.min(end_x)..start_x.max(end_x);
15741            let mut stack = Vec::new();
15742            for row in range.start.row().0..=range.end.row().0 {
15743                if let Some(selection) = self.selections.build_columnar_selection(
15744                    &display_map,
15745                    DisplayRow(row),
15746                    &positions,
15747                    selection.reversed,
15748                    &text_layout_details,
15749                ) {
15750                    stack.push(selection.id);
15751                    columnar_selections.push(selection);
15752                }
15753            }
15754            if !stack.is_empty() {
15755                if above {
15756                    stack.reverse();
15757                }
15758                state.groups.push(AddSelectionsGroup { above, stack });
15759            }
15760        }
15761
15762        let mut final_selections = Vec::new();
15763        let end_row = if above {
15764            DisplayRow(0)
15765        } else {
15766            display_map.max_point().row()
15767        };
15768
15769        // When `skip_soft_wrap` is true, we use UTF-16 columns instead of pixel
15770        // positions to place new selections, so we need to keep track of the
15771        // column range of the oldest selection in each group, because
15772        // intermediate selections may have been clamped to shorter lines.
15773        let mut goal_columns_by_selection_id = if skip_soft_wrap {
15774            let mut map = HashMap::default();
15775            for group in state.groups.iter() {
15776                if let Some(oldest_id) = group.stack.first() {
15777                    if let Some(oldest_selection) =
15778                        columnar_selections.iter().find(|s| s.id == *oldest_id)
15779                    {
15780                        let snapshot = display_map.buffer_snapshot();
15781                        let start_col =
15782                            snapshot.point_to_point_utf16(oldest_selection.start).column;
15783                        let end_col = snapshot.point_to_point_utf16(oldest_selection.end).column;
15784                        let goal_columns = start_col.min(end_col)..start_col.max(end_col);
15785                        for id in &group.stack {
15786                            map.insert(*id, goal_columns.clone());
15787                        }
15788                    }
15789                }
15790            }
15791            map
15792        } else {
15793            HashMap::default()
15794        };
15795
15796        let mut last_added_item_per_group = HashMap::default();
15797        for group in state.groups.iter_mut() {
15798            if let Some(last_id) = group.stack.last() {
15799                last_added_item_per_group.insert(*last_id, group);
15800            }
15801        }
15802
15803        for selection in columnar_selections {
15804            if let Some(group) = last_added_item_per_group.get_mut(&selection.id) {
15805                if above == group.above {
15806                    let range = selection.display_range(&display_map).sorted();
15807                    debug_assert_eq!(range.start.row(), range.end.row());
15808                    let row = range.start.row();
15809                    let positions =
15810                        if let SelectionGoal::HorizontalRange { start, end } = selection.goal {
15811                            Pixels::from(start)..Pixels::from(end)
15812                        } else {
15813                            let start_x =
15814                                display_map.x_for_display_point(range.start, &text_layout_details);
15815                            let end_x =
15816                                display_map.x_for_display_point(range.end, &text_layout_details);
15817                            start_x.min(end_x)..start_x.max(end_x)
15818                        };
15819
15820                    let maybe_new_selection = if skip_soft_wrap {
15821                        let goal_columns = goal_columns_by_selection_id
15822                            .remove(&selection.id)
15823                            .unwrap_or_else(|| {
15824                                let snapshot = display_map.buffer_snapshot();
15825                                let start_col =
15826                                    snapshot.point_to_point_utf16(selection.start).column;
15827                                let end_col = snapshot.point_to_point_utf16(selection.end).column;
15828                                start_col.min(end_col)..start_col.max(end_col)
15829                            });
15830                        self.selections.find_next_columnar_selection_by_buffer_row(
15831                            &display_map,
15832                            row,
15833                            end_row,
15834                            above,
15835                            &goal_columns,
15836                            selection.reversed,
15837                            &text_layout_details,
15838                        )
15839                    } else {
15840                        self.selections.find_next_columnar_selection_by_display_row(
15841                            &display_map,
15842                            row,
15843                            end_row,
15844                            above,
15845                            &positions,
15846                            selection.reversed,
15847                            &text_layout_details,
15848                        )
15849                    };
15850
15851                    if let Some(new_selection) = maybe_new_selection {
15852                        group.stack.push(new_selection.id);
15853                        if above {
15854                            final_selections.push(new_selection);
15855                            final_selections.push(selection);
15856                        } else {
15857                            final_selections.push(selection);
15858                            final_selections.push(new_selection);
15859                        }
15860                    } else {
15861                        final_selections.push(selection);
15862                    }
15863                } else {
15864                    group.stack.pop();
15865                }
15866            } else {
15867                final_selections.push(selection);
15868            }
15869        }
15870
15871        self.change_selections(Default::default(), window, cx, |s| {
15872            s.select(final_selections);
15873        });
15874
15875        let final_selection_ids: HashSet<_> = self
15876            .selections
15877            .all::<Point>(&display_map)
15878            .iter()
15879            .map(|s| s.id)
15880            .collect();
15881        state.groups.retain_mut(|group| {
15882            // selections might get merged above so we remove invalid items from stacks
15883            group.stack.retain(|id| final_selection_ids.contains(id));
15884
15885            // single selection in stack can be treated as initial state
15886            group.stack.len() > 1
15887        });
15888
15889        if !state.groups.is_empty() {
15890            self.add_selections_state = Some(state);
15891        }
15892    }
15893
15894    pub fn insert_snippet_at_selections(
15895        &mut self,
15896        action: &InsertSnippet,
15897        window: &mut Window,
15898        cx: &mut Context<Self>,
15899    ) {
15900        self.try_insert_snippet_at_selections(action, window, cx)
15901            .log_err();
15902    }
15903
15904    fn try_insert_snippet_at_selections(
15905        &mut self,
15906        action: &InsertSnippet,
15907        window: &mut Window,
15908        cx: &mut Context<Self>,
15909    ) -> Result<()> {
15910        let insertion_ranges = self
15911            .selections
15912            .all::<MultiBufferOffset>(&self.display_snapshot(cx))
15913            .into_iter()
15914            .map(|selection| selection.range())
15915            .collect_vec();
15916
15917        let snippet = if let Some(snippet_body) = &action.snippet {
15918            if action.language.is_none() && action.name.is_none() {
15919                Snippet::parse(snippet_body)?
15920            } else {
15921                bail!("`snippet` is mutually exclusive with `language` and `name`")
15922            }
15923        } else if let Some(name) = &action.name {
15924            let project = self.project().context("no project")?;
15925            let snippet_store = project.read(cx).snippets().read(cx);
15926            let snippet = snippet_store
15927                .snippets_for(action.language.clone(), cx)
15928                .into_iter()
15929                .find(|snippet| snippet.name == *name)
15930                .context("snippet not found")?;
15931            Snippet::parse(&snippet.body)?
15932        } else {
15933            // todo(andrew): open modal to select snippet
15934            bail!("`name` or `snippet` is required")
15935        };
15936
15937        self.insert_snippet(&insertion_ranges, snippet, window, cx)
15938    }
15939
15940    fn select_match_ranges(
15941        &mut self,
15942        range: Range<MultiBufferOffset>,
15943        reversed: bool,
15944        replace_newest: bool,
15945        auto_scroll: Option<Autoscroll>,
15946        window: &mut Window,
15947        cx: &mut Context<Editor>,
15948    ) {
15949        self.unfold_ranges(
15950            std::slice::from_ref(&range),
15951            false,
15952            auto_scroll.is_some(),
15953            cx,
15954        );
15955        let effects = if let Some(scroll) = auto_scroll {
15956            SelectionEffects::scroll(scroll)
15957        } else {
15958            SelectionEffects::no_scroll()
15959        };
15960        self.change_selections(effects, window, cx, |s| {
15961            if replace_newest {
15962                s.delete(s.newest_anchor().id);
15963            }
15964            if reversed {
15965                s.insert_range(range.end..range.start);
15966            } else {
15967                s.insert_range(range);
15968            }
15969        });
15970    }
15971
15972    pub fn select_next_match_internal(
15973        &mut self,
15974        display_map: &DisplaySnapshot,
15975        replace_newest: bool,
15976        autoscroll: Option<Autoscroll>,
15977        window: &mut Window,
15978        cx: &mut Context<Self>,
15979    ) -> Result<()> {
15980        let buffer = display_map.buffer_snapshot();
15981        let mut selections = self.selections.all::<MultiBufferOffset>(&display_map);
15982        if let Some(mut select_next_state) = self.select_next_state.take() {
15983            let query = &select_next_state.query;
15984            if !select_next_state.done {
15985                let first_selection = selections.iter().min_by_key(|s| s.id).unwrap();
15986                let last_selection = selections.iter().max_by_key(|s| s.id).unwrap();
15987                let mut next_selected_range = None;
15988
15989                let bytes_after_last_selection =
15990                    buffer.bytes_in_range(last_selection.end..buffer.len());
15991                let bytes_before_first_selection =
15992                    buffer.bytes_in_range(MultiBufferOffset(0)..first_selection.start);
15993                let query_matches = query
15994                    .stream_find_iter(bytes_after_last_selection)
15995                    .map(|result| (last_selection.end, result))
15996                    .chain(
15997                        query
15998                            .stream_find_iter(bytes_before_first_selection)
15999                            .map(|result| (MultiBufferOffset(0), result)),
16000                    );
16001
16002                for (start_offset, query_match) in query_matches {
16003                    let query_match = query_match.unwrap(); // can only fail due to I/O
16004                    let offset_range =
16005                        start_offset + query_match.start()..start_offset + query_match.end();
16006
16007                    if !select_next_state.wordwise
16008                        || (!buffer.is_inside_word(offset_range.start, None)
16009                            && !buffer.is_inside_word(offset_range.end, None))
16010                    {
16011                        let idx = selections
16012                            .partition_point(|selection| selection.end <= offset_range.start);
16013                        let overlaps = selections
16014                            .get(idx)
16015                            .map_or(false, |selection| selection.start < offset_range.end);
16016
16017                        if !overlaps {
16018                            next_selected_range = Some(offset_range);
16019                            break;
16020                        }
16021                    }
16022                }
16023
16024                if let Some(next_selected_range) = next_selected_range {
16025                    self.select_match_ranges(
16026                        next_selected_range,
16027                        last_selection.reversed,
16028                        replace_newest,
16029                        autoscroll,
16030                        window,
16031                        cx,
16032                    );
16033                } else {
16034                    select_next_state.done = true;
16035                }
16036            }
16037
16038            self.select_next_state = Some(select_next_state);
16039        } else {
16040            let mut only_carets = true;
16041            let mut same_text_selected = true;
16042            let mut selected_text = None;
16043
16044            let mut selections_iter = selections.iter().peekable();
16045            while let Some(selection) = selections_iter.next() {
16046                if selection.start != selection.end {
16047                    only_carets = false;
16048                }
16049
16050                if same_text_selected {
16051                    if selected_text.is_none() {
16052                        selected_text =
16053                            Some(buffer.text_for_range(selection.range()).collect::<String>());
16054                    }
16055
16056                    if let Some(next_selection) = selections_iter.peek() {
16057                        if next_selection.len() == selection.len() {
16058                            let next_selected_text = buffer
16059                                .text_for_range(next_selection.range())
16060                                .collect::<String>();
16061                            if Some(next_selected_text) != selected_text {
16062                                same_text_selected = false;
16063                                selected_text = None;
16064                            }
16065                        } else {
16066                            same_text_selected = false;
16067                            selected_text = None;
16068                        }
16069                    }
16070                }
16071            }
16072
16073            if only_carets {
16074                for selection in &mut selections {
16075                    let (word_range, _) = buffer.surrounding_word(selection.start, None);
16076                    selection.start = word_range.start;
16077                    selection.end = word_range.end;
16078                    selection.goal = SelectionGoal::None;
16079                    selection.reversed = false;
16080                    self.select_match_ranges(
16081                        selection.start..selection.end,
16082                        selection.reversed,
16083                        replace_newest,
16084                        autoscroll,
16085                        window,
16086                        cx,
16087                    );
16088                }
16089
16090                if selections.len() == 1 {
16091                    let selection = selections
16092                        .last()
16093                        .expect("ensured that there's only one selection");
16094                    let query = buffer
16095                        .text_for_range(selection.start..selection.end)
16096                        .collect::<String>();
16097                    let is_empty = query.is_empty();
16098                    let select_state = SelectNextState {
16099                        query: self.build_query(&[query], cx)?,
16100                        wordwise: true,
16101                        done: is_empty,
16102                    };
16103                    self.select_next_state = Some(select_state);
16104                } else {
16105                    self.select_next_state = None;
16106                }
16107            } else if let Some(selected_text) = selected_text {
16108                self.select_next_state = Some(SelectNextState {
16109                    query: self.build_query(&[selected_text], cx)?,
16110                    wordwise: false,
16111                    done: false,
16112                });
16113                self.select_next_match_internal(
16114                    display_map,
16115                    replace_newest,
16116                    autoscroll,
16117                    window,
16118                    cx,
16119                )?;
16120            }
16121        }
16122        Ok(())
16123    }
16124
16125    pub fn select_all_matches(
16126        &mut self,
16127        _action: &SelectAllMatches,
16128        window: &mut Window,
16129        cx: &mut Context<Self>,
16130    ) -> Result<()> {
16131        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
16132
16133        let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
16134
16135        self.select_next_match_internal(&display_map, false, None, window, cx)?;
16136        let Some(select_next_state) = self.select_next_state.as_mut().filter(|state| !state.done)
16137        else {
16138            return Ok(());
16139        };
16140
16141        let mut new_selections = Vec::new();
16142        let initial_selection = self.selections.oldest::<MultiBufferOffset>(&display_map);
16143        let reversed = initial_selection.reversed;
16144        let buffer = display_map.buffer_snapshot();
16145        let query_matches = select_next_state
16146            .query
16147            .stream_find_iter(buffer.bytes_in_range(MultiBufferOffset(0)..buffer.len()));
16148
16149        for query_match in query_matches.into_iter() {
16150            let query_match = query_match.context("query match for select all action")?; // can only fail due to I/O
16151            let offset_range = if reversed {
16152                MultiBufferOffset(query_match.end())..MultiBufferOffset(query_match.start())
16153            } else {
16154                MultiBufferOffset(query_match.start())..MultiBufferOffset(query_match.end())
16155            };
16156
16157            let is_partial_word_match = select_next_state.wordwise
16158                && (buffer.is_inside_word(offset_range.start, None)
16159                    || buffer.is_inside_word(offset_range.end, None));
16160
16161            let is_initial_selection = MultiBufferOffset(query_match.start())
16162                == initial_selection.start
16163                && MultiBufferOffset(query_match.end()) == initial_selection.end;
16164
16165            if !is_partial_word_match && !is_initial_selection {
16166                new_selections.push(offset_range);
16167            }
16168        }
16169
16170        // Ensure that the initial range is the last selection, as
16171        // `MutableSelectionsCollection::select_ranges` makes the last selection
16172        // the newest selection, which the editor then relies on as the primary
16173        // cursor for scroll targeting. Without this, the last match would then
16174        // be automatically focused when the user started editing the selected
16175        // matches.
16176        let initial_directed_range = if reversed {
16177            initial_selection.end..initial_selection.start
16178        } else {
16179            initial_selection.start..initial_selection.end
16180        };
16181        new_selections.push(initial_directed_range);
16182
16183        select_next_state.done = true;
16184        self.unfold_ranges(&new_selections, false, false, cx);
16185        self.change_selections(SelectionEffects::no_scroll(), window, cx, |selections| {
16186            selections.select_ranges(new_selections)
16187        });
16188
16189        Ok(())
16190    }
16191
16192    pub fn select_next(
16193        &mut self,
16194        action: &SelectNext,
16195        window: &mut Window,
16196        cx: &mut Context<Self>,
16197    ) -> Result<()> {
16198        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
16199        let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
16200        self.select_next_match_internal(
16201            &display_map,
16202            action.replace_newest,
16203            Some(Autoscroll::newest()),
16204            window,
16205            cx,
16206        )
16207    }
16208
16209    pub fn select_previous(
16210        &mut self,
16211        action: &SelectPrevious,
16212        window: &mut Window,
16213        cx: &mut Context<Self>,
16214    ) -> Result<()> {
16215        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
16216        let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
16217        let buffer = display_map.buffer_snapshot();
16218        let mut selections = self.selections.all::<MultiBufferOffset>(&display_map);
16219        if let Some(mut select_prev_state) = self.select_prev_state.take() {
16220            let query = &select_prev_state.query;
16221            if !select_prev_state.done {
16222                let first_selection = selections.iter().min_by_key(|s| s.id).unwrap();
16223                let last_selection = selections.iter().max_by_key(|s| s.id).unwrap();
16224                let mut next_selected_range = None;
16225                // When we're iterating matches backwards, the oldest match will actually be the furthest one in the buffer.
16226                let bytes_before_last_selection =
16227                    buffer.reversed_bytes_in_range(MultiBufferOffset(0)..last_selection.start);
16228                let bytes_after_first_selection =
16229                    buffer.reversed_bytes_in_range(first_selection.end..buffer.len());
16230                let query_matches = query
16231                    .stream_find_iter(bytes_before_last_selection)
16232                    .map(|result| (last_selection.start, result))
16233                    .chain(
16234                        query
16235                            .stream_find_iter(bytes_after_first_selection)
16236                            .map(|result| (buffer.len(), result)),
16237                    );
16238                for (end_offset, query_match) in query_matches {
16239                    let query_match = query_match.unwrap(); // can only fail due to I/O
16240                    let offset_range =
16241                        end_offset - query_match.end()..end_offset - query_match.start();
16242
16243                    if !select_prev_state.wordwise
16244                        || (!buffer.is_inside_word(offset_range.start, None)
16245                            && !buffer.is_inside_word(offset_range.end, None))
16246                    {
16247                        next_selected_range = Some(offset_range);
16248                        break;
16249                    }
16250                }
16251
16252                if let Some(next_selected_range) = next_selected_range {
16253                    self.select_match_ranges(
16254                        next_selected_range,
16255                        last_selection.reversed,
16256                        action.replace_newest,
16257                        Some(Autoscroll::newest()),
16258                        window,
16259                        cx,
16260                    );
16261                } else {
16262                    select_prev_state.done = true;
16263                }
16264            }
16265
16266            self.select_prev_state = Some(select_prev_state);
16267        } else {
16268            let mut only_carets = true;
16269            let mut same_text_selected = true;
16270            let mut selected_text = None;
16271
16272            let mut selections_iter = selections.iter().peekable();
16273            while let Some(selection) = selections_iter.next() {
16274                if selection.start != selection.end {
16275                    only_carets = false;
16276                }
16277
16278                if same_text_selected {
16279                    if selected_text.is_none() {
16280                        selected_text =
16281                            Some(buffer.text_for_range(selection.range()).collect::<String>());
16282                    }
16283
16284                    if let Some(next_selection) = selections_iter.peek() {
16285                        if next_selection.len() == selection.len() {
16286                            let next_selected_text = buffer
16287                                .text_for_range(next_selection.range())
16288                                .collect::<String>();
16289                            if Some(next_selected_text) != selected_text {
16290                                same_text_selected = false;
16291                                selected_text = None;
16292                            }
16293                        } else {
16294                            same_text_selected = false;
16295                            selected_text = None;
16296                        }
16297                    }
16298                }
16299            }
16300
16301            if only_carets {
16302                for selection in &mut selections {
16303                    let (word_range, _) = buffer.surrounding_word(selection.start, None);
16304                    selection.start = word_range.start;
16305                    selection.end = word_range.end;
16306                    selection.goal = SelectionGoal::None;
16307                    selection.reversed = false;
16308                    self.select_match_ranges(
16309                        selection.start..selection.end,
16310                        selection.reversed,
16311                        action.replace_newest,
16312                        Some(Autoscroll::newest()),
16313                        window,
16314                        cx,
16315                    );
16316                }
16317                if selections.len() == 1 {
16318                    let selection = selections
16319                        .last()
16320                        .expect("ensured that there's only one selection");
16321                    let query = buffer
16322                        .text_for_range(selection.start..selection.end)
16323                        .collect::<String>();
16324                    let is_empty = query.is_empty();
16325                    let select_state = SelectNextState {
16326                        query: self.build_query(&[query.chars().rev().collect::<String>()], cx)?,
16327                        wordwise: true,
16328                        done: is_empty,
16329                    };
16330                    self.select_prev_state = Some(select_state);
16331                } else {
16332                    self.select_prev_state = None;
16333                }
16334            } else if let Some(selected_text) = selected_text {
16335                self.select_prev_state = Some(SelectNextState {
16336                    query: self
16337                        .build_query(&[selected_text.chars().rev().collect::<String>()], cx)?,
16338                    wordwise: false,
16339                    done: false,
16340                });
16341                self.select_previous(action, window, cx)?;
16342            }
16343        }
16344        Ok(())
16345    }
16346
16347    /// Builds an `AhoCorasick` automaton from the provided patterns, while
16348    /// setting the case sensitivity based on the global
16349    /// `SelectNextCaseSensitive` setting, if set, otherwise based on the
16350    /// editor's settings.
16351    fn build_query<I, P>(&self, patterns: I, cx: &Context<Self>) -> Result<AhoCorasick, BuildError>
16352    where
16353        I: IntoIterator<Item = P>,
16354        P: AsRef<[u8]>,
16355    {
16356        let case_sensitive = self
16357            .select_next_is_case_sensitive
16358            .unwrap_or_else(|| EditorSettings::get_global(cx).search.case_sensitive);
16359
16360        let mut builder = AhoCorasickBuilder::new();
16361        builder.ascii_case_insensitive(!case_sensitive);
16362        builder.build(patterns)
16363    }
16364
16365    pub fn find_next_match(
16366        &mut self,
16367        _: &FindNextMatch,
16368        window: &mut Window,
16369        cx: &mut Context<Self>,
16370    ) -> Result<()> {
16371        let selections = self.selections.disjoint_anchors_arc();
16372        match selections.first() {
16373            Some(first) if selections.len() >= 2 => {
16374                self.change_selections(Default::default(), window, cx, |s| {
16375                    s.select_ranges([first.range()]);
16376                });
16377            }
16378            _ => self.select_next(
16379                &SelectNext {
16380                    replace_newest: true,
16381                },
16382                window,
16383                cx,
16384            )?,
16385        }
16386        Ok(())
16387    }
16388
16389    pub fn find_previous_match(
16390        &mut self,
16391        _: &FindPreviousMatch,
16392        window: &mut Window,
16393        cx: &mut Context<Self>,
16394    ) -> Result<()> {
16395        let selections = self.selections.disjoint_anchors_arc();
16396        match selections.last() {
16397            Some(last) if selections.len() >= 2 => {
16398                self.change_selections(Default::default(), window, cx, |s| {
16399                    s.select_ranges([last.range()]);
16400                });
16401            }
16402            _ => self.select_previous(
16403                &SelectPrevious {
16404                    replace_newest: true,
16405                },
16406                window,
16407                cx,
16408            )?,
16409        }
16410        Ok(())
16411    }
16412
16413    pub fn toggle_comments(
16414        &mut self,
16415        action: &ToggleComments,
16416        window: &mut Window,
16417        cx: &mut Context<Self>,
16418    ) {
16419        if self.read_only(cx) {
16420            return;
16421        }
16422        self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
16423        let text_layout_details = &self.text_layout_details(window, cx);
16424        self.transact(window, cx, |this, window, cx| {
16425            let mut selections = this
16426                .selections
16427                .all::<MultiBufferPoint>(&this.display_snapshot(cx));
16428            let mut edits = Vec::new();
16429            let mut selection_edit_ranges = Vec::new();
16430            let mut last_toggled_row = None;
16431            let snapshot = this.buffer.read(cx).read(cx);
16432            let empty_str: Arc<str> = Arc::default();
16433            let mut suffixes_inserted = Vec::new();
16434            let ignore_indent = action.ignore_indent;
16435
16436            fn comment_prefix_range(
16437                snapshot: &MultiBufferSnapshot,
16438                row: MultiBufferRow,
16439                comment_prefix: &str,
16440                comment_prefix_whitespace: &str,
16441                ignore_indent: bool,
16442            ) -> Range<Point> {
16443                let indent_size = if ignore_indent {
16444                    0
16445                } else {
16446                    snapshot.indent_size_for_line(row).len
16447                };
16448
16449                let start = Point::new(row.0, indent_size);
16450
16451                let mut line_bytes = snapshot
16452                    .bytes_in_range(start..snapshot.max_point())
16453                    .flatten()
16454                    .copied();
16455
16456                // If this line currently begins with the line comment prefix, then record
16457                // the range containing the prefix.
16458                if line_bytes
16459                    .by_ref()
16460                    .take(comment_prefix.len())
16461                    .eq(comment_prefix.bytes())
16462                {
16463                    // Include any whitespace that matches the comment prefix.
16464                    let matching_whitespace_len = line_bytes
16465                        .zip(comment_prefix_whitespace.bytes())
16466                        .take_while(|(a, b)| a == b)
16467                        .count() as u32;
16468                    let end = Point::new(
16469                        start.row,
16470                        start.column + comment_prefix.len() as u32 + matching_whitespace_len,
16471                    );
16472                    start..end
16473                } else {
16474                    start..start
16475                }
16476            }
16477
16478            fn comment_suffix_range(
16479                snapshot: &MultiBufferSnapshot,
16480                row: MultiBufferRow,
16481                comment_suffix: &str,
16482                comment_suffix_has_leading_space: bool,
16483            ) -> Range<Point> {
16484                let end = Point::new(row.0, snapshot.line_len(row));
16485                let suffix_start_column = end.column.saturating_sub(comment_suffix.len() as u32);
16486
16487                let mut line_end_bytes = snapshot
16488                    .bytes_in_range(Point::new(end.row, suffix_start_column.saturating_sub(1))..end)
16489                    .flatten()
16490                    .copied();
16491
16492                let leading_space_len = if suffix_start_column > 0
16493                    && line_end_bytes.next() == Some(b' ')
16494                    && comment_suffix_has_leading_space
16495                {
16496                    1
16497                } else {
16498                    0
16499                };
16500
16501                // If this line currently begins with the line comment prefix, then record
16502                // the range containing the prefix.
16503                if line_end_bytes.by_ref().eq(comment_suffix.bytes()) {
16504                    let start = Point::new(end.row, suffix_start_column - leading_space_len);
16505                    start..end
16506                } else {
16507                    end..end
16508                }
16509            }
16510
16511            // TODO: Handle selections that cross excerpts
16512            for selection in &mut selections {
16513                let start_column = snapshot
16514                    .indent_size_for_line(MultiBufferRow(selection.start.row))
16515                    .len;
16516                let language = if let Some(language) =
16517                    snapshot.language_scope_at(Point::new(selection.start.row, start_column))
16518                {
16519                    language
16520                } else {
16521                    continue;
16522                };
16523
16524                selection_edit_ranges.clear();
16525
16526                // If multiple selections contain a given row, avoid processing that
16527                // row more than once.
16528                let mut start_row = MultiBufferRow(selection.start.row);
16529                if last_toggled_row == Some(start_row) {
16530                    start_row = start_row.next_row();
16531                }
16532                let end_row =
16533                    if selection.end.row > selection.start.row && selection.end.column == 0 {
16534                        MultiBufferRow(selection.end.row - 1)
16535                    } else {
16536                        MultiBufferRow(selection.end.row)
16537                    };
16538                last_toggled_row = Some(end_row);
16539
16540                if start_row > end_row {
16541                    continue;
16542                }
16543
16544                // If the language has line comments, toggle those.
16545                let mut full_comment_prefixes = language.line_comment_prefixes().to_vec();
16546
16547                // If ignore_indent is set, trim spaces from the right side of all full_comment_prefixes
16548                if ignore_indent {
16549                    full_comment_prefixes = full_comment_prefixes
16550                        .into_iter()
16551                        .map(|s| Arc::from(s.trim_end()))
16552                        .collect();
16553                }
16554
16555                if !full_comment_prefixes.is_empty() {
16556                    let first_prefix = full_comment_prefixes
16557                        .first()
16558                        .expect("prefixes is non-empty");
16559                    let prefix_trimmed_lengths = full_comment_prefixes
16560                        .iter()
16561                        .map(|p| p.trim_end_matches(' ').len())
16562                        .collect::<SmallVec<[usize; 4]>>();
16563
16564                    let mut all_selection_lines_are_comments = true;
16565
16566                    for row in start_row.0..=end_row.0 {
16567                        let row = MultiBufferRow(row);
16568                        if start_row < end_row && snapshot.is_line_blank(row) {
16569                            continue;
16570                        }
16571
16572                        let prefix_range = full_comment_prefixes
16573                            .iter()
16574                            .zip(prefix_trimmed_lengths.iter().copied())
16575                            .map(|(prefix, trimmed_prefix_len)| {
16576                                comment_prefix_range(
16577                                    snapshot.deref(),
16578                                    row,
16579                                    &prefix[..trimmed_prefix_len],
16580                                    &prefix[trimmed_prefix_len..],
16581                                    ignore_indent,
16582                                )
16583                            })
16584                            .max_by_key(|range| range.end.column - range.start.column)
16585                            .expect("prefixes is non-empty");
16586
16587                        if prefix_range.is_empty() {
16588                            all_selection_lines_are_comments = false;
16589                        }
16590
16591                        selection_edit_ranges.push(prefix_range);
16592                    }
16593
16594                    if all_selection_lines_are_comments {
16595                        edits.extend(
16596                            selection_edit_ranges
16597                                .iter()
16598                                .cloned()
16599                                .map(|range| (range, empty_str.clone())),
16600                        );
16601                    } else {
16602                        let min_column = selection_edit_ranges
16603                            .iter()
16604                            .map(|range| range.start.column)
16605                            .min()
16606                            .unwrap_or(0);
16607                        edits.extend(selection_edit_ranges.iter().map(|range| {
16608                            let position = Point::new(range.start.row, min_column);
16609                            (position..position, first_prefix.clone())
16610                        }));
16611                    }
16612                } else if let Some(BlockCommentConfig {
16613                    start: full_comment_prefix,
16614                    end: comment_suffix,
16615                    ..
16616                }) = language.block_comment()
16617                {
16618                    let comment_prefix = full_comment_prefix.trim_end_matches(' ');
16619                    let comment_prefix_whitespace = &full_comment_prefix[comment_prefix.len()..];
16620                    let prefix_range = comment_prefix_range(
16621                        snapshot.deref(),
16622                        start_row,
16623                        comment_prefix,
16624                        comment_prefix_whitespace,
16625                        ignore_indent,
16626                    );
16627                    let suffix_range = comment_suffix_range(
16628                        snapshot.deref(),
16629                        end_row,
16630                        comment_suffix.trim_start_matches(' '),
16631                        comment_suffix.starts_with(' '),
16632                    );
16633
16634                    if prefix_range.is_empty() || suffix_range.is_empty() {
16635                        edits.push((
16636                            prefix_range.start..prefix_range.start,
16637                            full_comment_prefix.clone(),
16638                        ));
16639                        edits.push((suffix_range.end..suffix_range.end, comment_suffix.clone()));
16640                        suffixes_inserted.push((end_row, comment_suffix.len()));
16641                    } else {
16642                        edits.push((prefix_range, empty_str.clone()));
16643                        edits.push((suffix_range, empty_str.clone()));
16644                    }
16645                } else {
16646                    continue;
16647                }
16648            }
16649
16650            drop(snapshot);
16651            this.buffer.update(cx, |buffer, cx| {
16652                buffer.edit(edits, None, cx);
16653            });
16654
16655            // Adjust selections so that they end before any comment suffixes that
16656            // were inserted.
16657            let mut suffixes_inserted = suffixes_inserted.into_iter().peekable();
16658            let mut selections = this.selections.all::<Point>(&this.display_snapshot(cx));
16659            let snapshot = this.buffer.read(cx).read(cx);
16660            for selection in &mut selections {
16661                while let Some((row, suffix_len)) = suffixes_inserted.peek().copied() {
16662                    match row.cmp(&MultiBufferRow(selection.end.row)) {
16663                        Ordering::Less => {
16664                            suffixes_inserted.next();
16665                            continue;
16666                        }
16667                        Ordering::Greater => break,
16668                        Ordering::Equal => {
16669                            if selection.end.column == snapshot.line_len(row) {
16670                                if selection.is_empty() {
16671                                    selection.start.column -= suffix_len as u32;
16672                                }
16673                                selection.end.column -= suffix_len as u32;
16674                            }
16675                            break;
16676                        }
16677                    }
16678                }
16679            }
16680
16681            drop(snapshot);
16682            this.change_selections(Default::default(), window, cx, |s| s.select(selections));
16683
16684            let selections = this.selections.all::<Point>(&this.display_snapshot(cx));
16685            let selections_on_single_row = selections.windows(2).all(|selections| {
16686                selections[0].start.row == selections[1].start.row
16687                    && selections[0].end.row == selections[1].end.row
16688                    && selections[0].start.row == selections[0].end.row
16689            });
16690            let selections_selecting = selections
16691                .iter()
16692                .any(|selection| selection.start != selection.end);
16693            let advance_downwards = action.advance_downwards
16694                && selections_on_single_row
16695                && !selections_selecting
16696                && !matches!(this.mode, EditorMode::SingleLine);
16697
16698            if advance_downwards {
16699                let snapshot = this.buffer.read(cx).snapshot(cx);
16700
16701                this.change_selections(Default::default(), window, cx, |s| {
16702                    s.move_cursors_with(&mut |display_snapshot, display_point, _| {
16703                        let mut point = display_point.to_point(display_snapshot);
16704                        point.row += 1;
16705                        point = snapshot.clip_point(point, Bias::Left);
16706                        let display_point = point.to_display_point(display_snapshot);
16707                        let goal = SelectionGoal::HorizontalPosition(
16708                            display_snapshot
16709                                .x_for_display_point(display_point, text_layout_details)
16710                                .into(),
16711                        );
16712                        (display_point, goal)
16713                    })
16714                });
16715            }
16716        });
16717    }
16718
16719    pub fn select_enclosing_symbol(
16720        &mut self,
16721        _: &SelectEnclosingSymbol,
16722        window: &mut Window,
16723        cx: &mut Context<Self>,
16724    ) {
16725        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
16726
16727        let buffer = self.buffer.read(cx).snapshot(cx);
16728        let old_selections = self
16729            .selections
16730            .all::<MultiBufferOffset>(&self.display_snapshot(cx))
16731            .into_boxed_slice();
16732
16733        fn update_selection(
16734            selection: &Selection<MultiBufferOffset>,
16735            buffer_snap: &MultiBufferSnapshot,
16736        ) -> Option<Selection<MultiBufferOffset>> {
16737            let cursor = selection.head();
16738            let (_buffer_id, symbols) = buffer_snap.symbols_containing(cursor, None)?;
16739            for symbol in symbols.iter().rev() {
16740                let start = symbol.range.start.to_offset(buffer_snap);
16741                let end = symbol.range.end.to_offset(buffer_snap);
16742                let new_range = start..end;
16743                if start < selection.start || end > selection.end {
16744                    return Some(Selection {
16745                        id: selection.id,
16746                        start: new_range.start,
16747                        end: new_range.end,
16748                        goal: SelectionGoal::None,
16749                        reversed: selection.reversed,
16750                    });
16751                }
16752            }
16753            None
16754        }
16755
16756        let mut selected_larger_symbol = false;
16757        let new_selections = old_selections
16758            .iter()
16759            .map(|selection| match update_selection(selection, &buffer) {
16760                Some(new_selection) => {
16761                    if new_selection.range() != selection.range() {
16762                        selected_larger_symbol = true;
16763                    }
16764                    new_selection
16765                }
16766                None => selection.clone(),
16767            })
16768            .collect::<Vec<_>>();
16769
16770        if selected_larger_symbol {
16771            self.change_selections(Default::default(), window, cx, |s| {
16772                s.select(new_selections);
16773            });
16774        }
16775    }
16776
16777    pub fn select_larger_syntax_node(
16778        &mut self,
16779        _: &SelectLargerSyntaxNode,
16780        window: &mut Window,
16781        cx: &mut Context<Self>,
16782    ) {
16783        let Some(visible_row_count) = self.visible_row_count() else {
16784            return;
16785        };
16786        let old_selections: Box<[_]> = self
16787            .selections
16788            .all::<MultiBufferOffset>(&self.display_snapshot(cx))
16789            .into();
16790        if old_selections.is_empty() {
16791            return;
16792        }
16793
16794        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
16795
16796        let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
16797        let buffer = self.buffer.read(cx).snapshot(cx);
16798
16799        let mut selected_larger_node = false;
16800        let mut new_selections = old_selections
16801            .iter()
16802            .map(|selection| {
16803                let old_range = selection.start..selection.end;
16804
16805                if let Some((node, _)) = buffer.syntax_ancestor(old_range.clone()) {
16806                    // manually select word at selection
16807                    if ["string_content", "inline"].contains(&node.kind()) {
16808                        let (word_range, _) = buffer.surrounding_word(old_range.start, None);
16809                        // ignore if word is already selected
16810                        if !word_range.is_empty() && old_range != word_range {
16811                            let (last_word_range, _) = buffer.surrounding_word(old_range.end, None);
16812                            // only select word if start and end point belongs to same word
16813                            if word_range == last_word_range {
16814                                selected_larger_node = true;
16815                                return Selection {
16816                                    id: selection.id,
16817                                    start: word_range.start,
16818                                    end: word_range.end,
16819                                    goal: SelectionGoal::None,
16820                                    reversed: selection.reversed,
16821                                };
16822                            }
16823                        }
16824                    }
16825                }
16826
16827                let mut new_range = old_range.clone();
16828                while let Some((node, range)) = buffer.syntax_ancestor(new_range.clone()) {
16829                    new_range = range;
16830                    if !node.is_named() {
16831                        continue;
16832                    }
16833                    if !display_map.intersects_fold(new_range.start)
16834                        && !display_map.intersects_fold(new_range.end)
16835                    {
16836                        break;
16837                    }
16838                }
16839
16840                selected_larger_node |= new_range != old_range;
16841                Selection {
16842                    id: selection.id,
16843                    start: new_range.start,
16844                    end: new_range.end,
16845                    goal: SelectionGoal::None,
16846                    reversed: selection.reversed,
16847                }
16848            })
16849            .collect::<Vec<_>>();
16850
16851        if !selected_larger_node {
16852            return; // don't put this call in the history
16853        }
16854
16855        // scroll based on transformation done to the last selection created by the user
16856        let (last_old, last_new) = old_selections
16857            .last()
16858            .zip(new_selections.last().cloned())
16859            .expect("old_selections isn't empty");
16860
16861        let is_selection_reversed = if new_selections.len() == 1 {
16862            let should_be_reversed = last_old.start != last_new.start;
16863            new_selections.last_mut().expect("checked above").reversed = should_be_reversed;
16864            should_be_reversed
16865        } else {
16866            last_new.reversed
16867        };
16868
16869        if selected_larger_node {
16870            self.select_syntax_node_history.disable_clearing = true;
16871            self.change_selections(SelectionEffects::no_scroll(), window, cx, |s| {
16872                s.select(new_selections.clone());
16873            });
16874            self.select_syntax_node_history.disable_clearing = false;
16875        }
16876
16877        let start_row = last_new.start.to_display_point(&display_map).row().0;
16878        let end_row = last_new.end.to_display_point(&display_map).row().0;
16879        let selection_height = end_row - start_row + 1;
16880        let scroll_margin_rows = self.vertical_scroll_margin() as u32;
16881
16882        let fits_on_the_screen = visible_row_count >= selection_height + scroll_margin_rows * 2;
16883        let scroll_behavior = if fits_on_the_screen {
16884            self.request_autoscroll(Autoscroll::fit(), cx);
16885            SelectSyntaxNodeScrollBehavior::FitSelection
16886        } else if is_selection_reversed {
16887            self.scroll_cursor_top(&ScrollCursorTop, window, cx);
16888            SelectSyntaxNodeScrollBehavior::CursorTop
16889        } else {
16890            self.scroll_cursor_bottom(&ScrollCursorBottom, window, cx);
16891            SelectSyntaxNodeScrollBehavior::CursorBottom
16892        };
16893
16894        let old_selections: Box<[Selection<Anchor>]> = old_selections
16895            .iter()
16896            .map(|s| s.map(|offset| buffer.anchor_before(offset)))
16897            .collect();
16898        self.select_syntax_node_history.push((
16899            old_selections,
16900            scroll_behavior,
16901            is_selection_reversed,
16902        ));
16903    }
16904
16905    pub fn select_smaller_syntax_node(
16906        &mut self,
16907        _: &SelectSmallerSyntaxNode,
16908        window: &mut Window,
16909        cx: &mut Context<Self>,
16910    ) {
16911        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
16912
16913        if let Some((mut selections, scroll_behavior, is_selection_reversed)) =
16914            self.select_syntax_node_history.pop()
16915        {
16916            if let Some(selection) = selections.last_mut() {
16917                selection.reversed = is_selection_reversed;
16918            }
16919
16920            let snapshot = self.buffer.read(cx).snapshot(cx);
16921            let selections: Vec<Selection<MultiBufferOffset>> = selections
16922                .iter()
16923                .map(|s| s.map(|anchor| anchor.to_offset(&snapshot)))
16924                .collect();
16925
16926            self.select_syntax_node_history.disable_clearing = true;
16927            self.change_selections(SelectionEffects::no_scroll(), window, cx, |s| {
16928                s.select(selections);
16929            });
16930            self.select_syntax_node_history.disable_clearing = false;
16931
16932            match scroll_behavior {
16933                SelectSyntaxNodeScrollBehavior::CursorTop => {
16934                    self.scroll_cursor_top(&ScrollCursorTop, window, cx);
16935                }
16936                SelectSyntaxNodeScrollBehavior::FitSelection => {
16937                    self.request_autoscroll(Autoscroll::fit(), cx);
16938                }
16939                SelectSyntaxNodeScrollBehavior::CursorBottom => {
16940                    self.scroll_cursor_bottom(&ScrollCursorBottom, window, cx);
16941                }
16942            }
16943        }
16944    }
16945
16946    pub fn unwrap_syntax_node(
16947        &mut self,
16948        _: &UnwrapSyntaxNode,
16949        window: &mut Window,
16950        cx: &mut Context<Self>,
16951    ) {
16952        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
16953
16954        let buffer = self.buffer.read(cx).snapshot(cx);
16955        let selections = self
16956            .selections
16957            .all::<MultiBufferOffset>(&self.display_snapshot(cx))
16958            .into_iter()
16959            // subtracting the offset requires sorting
16960            .sorted_by_key(|i| i.start);
16961
16962        let full_edits = selections
16963            .into_iter()
16964            .filter_map(|selection| {
16965                let child = if selection.is_empty()
16966                    && let Some((_, ancestor_range)) =
16967                        buffer.syntax_ancestor(selection.start..selection.end)
16968                {
16969                    ancestor_range
16970                } else {
16971                    selection.range()
16972                };
16973
16974                let mut parent = child.clone();
16975                while let Some((_, ancestor_range)) = buffer.syntax_ancestor(parent.clone()) {
16976                    parent = ancestor_range;
16977                    if parent.start < child.start || parent.end > child.end {
16978                        break;
16979                    }
16980                }
16981
16982                if parent == child {
16983                    return None;
16984                }
16985                let text = buffer.text_for_range(child).collect::<String>();
16986                Some((selection.id, parent, text))
16987            })
16988            .collect::<Vec<_>>();
16989        if full_edits.is_empty() {
16990            return;
16991        }
16992
16993        self.transact(window, cx, |this, window, cx| {
16994            this.buffer.update(cx, |buffer, cx| {
16995                buffer.edit(
16996                    full_edits
16997                        .iter()
16998                        .map(|(_, p, t)| (p.clone(), t.clone()))
16999                        .collect::<Vec<_>>(),
17000                    None,
17001                    cx,
17002                );
17003            });
17004            this.change_selections(Default::default(), window, cx, |s| {
17005                let mut offset = 0;
17006                let mut selections = vec![];
17007                for (id, parent, text) in full_edits {
17008                    let start = parent.start - offset;
17009                    offset += (parent.end - parent.start) - text.len();
17010                    selections.push(Selection {
17011                        id,
17012                        start,
17013                        end: start + text.len(),
17014                        reversed: false,
17015                        goal: Default::default(),
17016                    });
17017                }
17018                s.select(selections);
17019            });
17020        });
17021    }
17022
17023    pub fn select_next_syntax_node(
17024        &mut self,
17025        _: &SelectNextSyntaxNode,
17026        window: &mut Window,
17027        cx: &mut Context<Self>,
17028    ) {
17029        let old_selections: Box<[_]> = self
17030            .selections
17031            .all::<MultiBufferOffset>(&self.display_snapshot(cx))
17032            .into();
17033        if old_selections.is_empty() {
17034            return;
17035        }
17036
17037        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
17038
17039        let buffer = self.buffer.read(cx).snapshot(cx);
17040        let mut selected_sibling = false;
17041
17042        let new_selections = old_selections
17043            .iter()
17044            .map(|selection| {
17045                let old_range = selection.start..selection.end;
17046
17047                let old_range =
17048                    old_range.start.to_offset(&buffer)..old_range.end.to_offset(&buffer);
17049                let excerpt = buffer.excerpt_containing(old_range.clone());
17050
17051                if let Some(mut excerpt) = excerpt
17052                    && let Some(node) = excerpt
17053                        .buffer()
17054                        .syntax_next_sibling(excerpt.map_range_to_buffer(old_range))
17055                {
17056                    let new_range = excerpt.map_range_from_buffer(
17057                        BufferOffset(node.byte_range().start)..BufferOffset(node.byte_range().end),
17058                    );
17059                    selected_sibling = true;
17060                    Selection {
17061                        id: selection.id,
17062                        start: new_range.start,
17063                        end: new_range.end,
17064                        goal: SelectionGoal::None,
17065                        reversed: selection.reversed,
17066                    }
17067                } else {
17068                    selection.clone()
17069                }
17070            })
17071            .collect::<Vec<_>>();
17072
17073        if selected_sibling {
17074            self.change_selections(
17075                SelectionEffects::scroll(Autoscroll::fit()),
17076                window,
17077                cx,
17078                |s| {
17079                    s.select(new_selections);
17080                },
17081            );
17082        }
17083    }
17084
17085    pub fn select_prev_syntax_node(
17086        &mut self,
17087        _: &SelectPreviousSyntaxNode,
17088        window: &mut Window,
17089        cx: &mut Context<Self>,
17090    ) {
17091        let old_selections: Box<[_]> = self
17092            .selections
17093            .all::<MultiBufferOffset>(&self.display_snapshot(cx))
17094            .into();
17095        if old_selections.is_empty() {
17096            return;
17097        }
17098
17099        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
17100
17101        let buffer = self.buffer.read(cx).snapshot(cx);
17102        let mut selected_sibling = false;
17103
17104        let new_selections = old_selections
17105            .iter()
17106            .map(|selection| {
17107                let old_range = selection.start..selection.end;
17108                let old_range =
17109                    old_range.start.to_offset(&buffer)..old_range.end.to_offset(&buffer);
17110                let excerpt = buffer.excerpt_containing(old_range.clone());
17111
17112                if let Some(mut excerpt) = excerpt
17113                    && let Some(node) = excerpt
17114                        .buffer()
17115                        .syntax_prev_sibling(excerpt.map_range_to_buffer(old_range))
17116                {
17117                    let new_range = excerpt.map_range_from_buffer(
17118                        BufferOffset(node.byte_range().start)..BufferOffset(node.byte_range().end),
17119                    );
17120                    selected_sibling = true;
17121                    Selection {
17122                        id: selection.id,
17123                        start: new_range.start,
17124                        end: new_range.end,
17125                        goal: SelectionGoal::None,
17126                        reversed: selection.reversed,
17127                    }
17128                } else {
17129                    selection.clone()
17130                }
17131            })
17132            .collect::<Vec<_>>();
17133
17134        if selected_sibling {
17135            self.change_selections(
17136                SelectionEffects::scroll(Autoscroll::fit()),
17137                window,
17138                cx,
17139                |s| {
17140                    s.select(new_selections);
17141                },
17142            );
17143        }
17144    }
17145
17146    pub fn move_to_start_of_larger_syntax_node(
17147        &mut self,
17148        _: &MoveToStartOfLargerSyntaxNode,
17149        window: &mut Window,
17150        cx: &mut Context<Self>,
17151    ) {
17152        self.move_cursors_to_syntax_nodes(window, cx, false);
17153    }
17154
17155    pub fn move_to_end_of_larger_syntax_node(
17156        &mut self,
17157        _: &MoveToEndOfLargerSyntaxNode,
17158        window: &mut Window,
17159        cx: &mut Context<Self>,
17160    ) {
17161        self.move_cursors_to_syntax_nodes(window, cx, true);
17162    }
17163
17164    fn find_syntax_node_boundary(
17165        &self,
17166        selection_pos: MultiBufferOffset,
17167        move_to_end: bool,
17168        display_map: &DisplaySnapshot,
17169        buffer: &MultiBufferSnapshot,
17170    ) -> MultiBufferOffset {
17171        let old_range = selection_pos..selection_pos;
17172        let mut new_pos = selection_pos;
17173        let mut search_range = old_range;
17174        while let Some((node, range)) = buffer.syntax_ancestor(search_range.clone()) {
17175            search_range = range.clone();
17176            if !node.is_named()
17177                || display_map.intersects_fold(range.start)
17178                || display_map.intersects_fold(range.end)
17179                // If cursor is already at the end of the syntax node, continue searching
17180                || (move_to_end && range.end == selection_pos)
17181                // If cursor is already at the start of the syntax node, continue searching
17182                || (!move_to_end && range.start == selection_pos)
17183            {
17184                continue;
17185            }
17186
17187            // If we found a string_content node, find the largest parent that is still string_content
17188            // Enables us to skip to the end of strings without taking multiple steps inside the string
17189            let (_, final_range) = if node.kind() == "string_content" {
17190                let mut current_node = node;
17191                let mut current_range = range;
17192                while let Some((parent, parent_range)) =
17193                    buffer.syntax_ancestor(current_range.clone())
17194                {
17195                    if parent.kind() == "string_content" {
17196                        current_node = parent;
17197                        current_range = parent_range;
17198                    } else {
17199                        break;
17200                    }
17201                }
17202
17203                (current_node, current_range)
17204            } else {
17205                (node, range)
17206            };
17207
17208            new_pos = if move_to_end {
17209                final_range.end
17210            } else {
17211                final_range.start
17212            };
17213
17214            break;
17215        }
17216
17217        new_pos
17218    }
17219
17220    fn move_cursors_to_syntax_nodes(
17221        &mut self,
17222        window: &mut Window,
17223        cx: &mut Context<Self>,
17224        move_to_end: bool,
17225    ) -> bool {
17226        let old_selections: Box<[_]> = self
17227            .selections
17228            .all::<MultiBufferOffset>(&self.display_snapshot(cx))
17229            .into();
17230        if old_selections.is_empty() {
17231            return false;
17232        }
17233
17234        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
17235
17236        let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
17237        let buffer = self.buffer.read(cx).snapshot(cx);
17238
17239        let mut any_cursor_moved = false;
17240        let new_selections = old_selections
17241            .iter()
17242            .map(|selection| {
17243                if !selection.is_empty() {
17244                    return selection.clone();
17245                }
17246
17247                let selection_pos = selection.head();
17248                let new_pos = self.find_syntax_node_boundary(
17249                    selection_pos,
17250                    move_to_end,
17251                    &display_map,
17252                    &buffer,
17253                );
17254
17255                any_cursor_moved |= new_pos != selection_pos;
17256
17257                Selection {
17258                    id: selection.id,
17259                    start: new_pos,
17260                    end: new_pos,
17261                    goal: SelectionGoal::None,
17262                    reversed: false,
17263                }
17264            })
17265            .collect::<Vec<_>>();
17266
17267        self.change_selections(Default::default(), window, cx, |s| {
17268            s.select(new_selections);
17269        });
17270        self.request_autoscroll(Autoscroll::newest(), cx);
17271
17272        any_cursor_moved
17273    }
17274
17275    pub fn select_to_start_of_larger_syntax_node(
17276        &mut self,
17277        _: &SelectToStartOfLargerSyntaxNode,
17278        window: &mut Window,
17279        cx: &mut Context<Self>,
17280    ) {
17281        self.select_to_syntax_nodes(window, cx, false);
17282    }
17283
17284    pub fn select_to_end_of_larger_syntax_node(
17285        &mut self,
17286        _: &SelectToEndOfLargerSyntaxNode,
17287        window: &mut Window,
17288        cx: &mut Context<Self>,
17289    ) {
17290        self.select_to_syntax_nodes(window, cx, true);
17291    }
17292
17293    fn select_to_syntax_nodes(
17294        &mut self,
17295        window: &mut Window,
17296        cx: &mut Context<Self>,
17297        move_to_end: bool,
17298    ) {
17299        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
17300
17301        let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
17302        let buffer = self.buffer.read(cx).snapshot(cx);
17303        let old_selections = self.selections.all::<MultiBufferOffset>(&display_map);
17304
17305        let new_selections = old_selections
17306            .iter()
17307            .map(|selection| {
17308                let new_pos = self.find_syntax_node_boundary(
17309                    selection.head(),
17310                    move_to_end,
17311                    &display_map,
17312                    &buffer,
17313                );
17314
17315                let mut new_selection = selection.clone();
17316                new_selection.set_head(new_pos, SelectionGoal::None);
17317                new_selection
17318            })
17319            .collect::<Vec<_>>();
17320
17321        self.change_selections(Default::default(), window, cx, |s| {
17322            s.select(new_selections);
17323        });
17324    }
17325
17326    pub fn move_to_enclosing_bracket(
17327        &mut self,
17328        _: &MoveToEnclosingBracket,
17329        window: &mut Window,
17330        cx: &mut Context<Self>,
17331    ) {
17332        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
17333        self.change_selections(Default::default(), window, cx, |s| {
17334            s.move_offsets_with(&mut |snapshot, selection| {
17335                let Some(enclosing_bracket_ranges) =
17336                    snapshot.enclosing_bracket_ranges(selection.start..selection.end)
17337                else {
17338                    return;
17339                };
17340
17341                let mut best_length = usize::MAX;
17342                let mut best_inside = false;
17343                let mut best_in_bracket_range = false;
17344                let mut best_destination = None;
17345                for (open, close) in enclosing_bracket_ranges {
17346                    let close = close.to_inclusive();
17347                    let length = *close.end() - open.start;
17348                    let inside = selection.start >= open.end && selection.end <= *close.start();
17349                    let in_bracket_range = open.to_inclusive().contains(&selection.head())
17350                        || close.contains(&selection.head());
17351
17352                    // If best is next to a bracket and current isn't, skip
17353                    if !in_bracket_range && best_in_bracket_range {
17354                        continue;
17355                    }
17356
17357                    // Prefer smaller lengths unless best is inside and current isn't
17358                    if length > best_length && (best_inside || !inside) {
17359                        continue;
17360                    }
17361
17362                    best_length = length;
17363                    best_inside = inside;
17364                    best_in_bracket_range = in_bracket_range;
17365                    best_destination = Some(
17366                        if close.contains(&selection.start) && close.contains(&selection.end) {
17367                            if inside { open.end } else { open.start }
17368                        } else if inside {
17369                            *close.start()
17370                        } else {
17371                            *close.end()
17372                        },
17373                    );
17374                }
17375
17376                if let Some(destination) = best_destination {
17377                    selection.collapse_to(destination, SelectionGoal::None);
17378                }
17379            })
17380        });
17381    }
17382
17383    pub fn undo_selection(
17384        &mut self,
17385        _: &UndoSelection,
17386        window: &mut Window,
17387        cx: &mut Context<Self>,
17388    ) {
17389        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
17390        if let Some(entry) = self.selection_history.undo_stack.pop_back() {
17391            self.selection_history.mode = SelectionHistoryMode::Undoing;
17392            self.with_selection_effects_deferred(window, cx, |this, window, cx| {
17393                this.end_selection(window, cx);
17394                this.change_selections(
17395                    SelectionEffects::scroll(Autoscroll::newest()),
17396                    window,
17397                    cx,
17398                    |s| s.select_anchors(entry.selections.to_vec()),
17399                );
17400            });
17401            self.selection_history.mode = SelectionHistoryMode::Normal;
17402
17403            self.select_next_state = entry.select_next_state;
17404            self.select_prev_state = entry.select_prev_state;
17405            self.add_selections_state = entry.add_selections_state;
17406        }
17407    }
17408
17409    pub fn redo_selection(
17410        &mut self,
17411        _: &RedoSelection,
17412        window: &mut Window,
17413        cx: &mut Context<Self>,
17414    ) {
17415        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
17416        if let Some(entry) = self.selection_history.redo_stack.pop_back() {
17417            self.selection_history.mode = SelectionHistoryMode::Redoing;
17418            self.with_selection_effects_deferred(window, cx, |this, window, cx| {
17419                this.end_selection(window, cx);
17420                this.change_selections(
17421                    SelectionEffects::scroll(Autoscroll::newest()),
17422                    window,
17423                    cx,
17424                    |s| s.select_anchors(entry.selections.to_vec()),
17425                );
17426            });
17427            self.selection_history.mode = SelectionHistoryMode::Normal;
17428
17429            self.select_next_state = entry.select_next_state;
17430            self.select_prev_state = entry.select_prev_state;
17431            self.add_selections_state = entry.add_selections_state;
17432        }
17433    }
17434
17435    pub fn expand_excerpts(
17436        &mut self,
17437        action: &ExpandExcerpts,
17438        _: &mut Window,
17439        cx: &mut Context<Self>,
17440    ) {
17441        self.expand_excerpts_for_direction(action.lines, ExpandExcerptDirection::UpAndDown, cx)
17442    }
17443
17444    pub fn expand_excerpts_down(
17445        &mut self,
17446        action: &ExpandExcerptsDown,
17447        _: &mut Window,
17448        cx: &mut Context<Self>,
17449    ) {
17450        self.expand_excerpts_for_direction(action.lines, ExpandExcerptDirection::Down, cx)
17451    }
17452
17453    pub fn expand_excerpts_up(
17454        &mut self,
17455        action: &ExpandExcerptsUp,
17456        _: &mut Window,
17457        cx: &mut Context<Self>,
17458    ) {
17459        self.expand_excerpts_for_direction(action.lines, ExpandExcerptDirection::Up, cx)
17460    }
17461
17462    pub fn expand_excerpts_for_direction(
17463        &mut self,
17464        lines: u32,
17465        direction: ExpandExcerptDirection,
17466        cx: &mut Context<Self>,
17467    ) {
17468        let selections = self.selections.disjoint_anchors_arc();
17469
17470        let lines = if lines == 0 {
17471            EditorSettings::get_global(cx).expand_excerpt_lines
17472        } else {
17473            lines
17474        };
17475
17476        let snapshot = self.buffer.read(cx).snapshot(cx);
17477        let excerpt_ids = selections
17478            .iter()
17479            .flat_map(|selection| snapshot.excerpt_ids_for_range(selection.range()))
17480            .unique()
17481            .sorted()
17482            .collect::<Vec<_>>();
17483
17484        if self.delegate_expand_excerpts {
17485            cx.emit(EditorEvent::ExpandExcerptsRequested {
17486                excerpt_ids,
17487                lines,
17488                direction,
17489            });
17490            return;
17491        }
17492
17493        self.buffer.update(cx, |buffer, cx| {
17494            buffer.expand_excerpts(excerpt_ids, lines, direction, cx)
17495        })
17496    }
17497
17498    pub fn expand_excerpt(
17499        &mut self,
17500        excerpt: ExcerptId,
17501        direction: ExpandExcerptDirection,
17502        window: &mut Window,
17503        cx: &mut Context<Self>,
17504    ) {
17505        let lines_to_expand = EditorSettings::get_global(cx).expand_excerpt_lines;
17506
17507        if self.delegate_expand_excerpts {
17508            cx.emit(EditorEvent::ExpandExcerptsRequested {
17509                excerpt_ids: vec![excerpt],
17510                lines: lines_to_expand,
17511                direction,
17512            });
17513            return;
17514        }
17515
17516        let current_scroll_position = self.scroll_position(cx);
17517        let mut scroll = None;
17518
17519        if direction == ExpandExcerptDirection::Down {
17520            let multi_buffer = self.buffer.read(cx);
17521            let snapshot = multi_buffer.snapshot(cx);
17522            if let Some(buffer_id) = snapshot.buffer_id_for_excerpt(excerpt)
17523                && let Some(buffer) = multi_buffer.buffer(buffer_id)
17524                && let Some(excerpt_range) = snapshot.context_range_for_excerpt(excerpt)
17525            {
17526                let buffer_snapshot = buffer.read(cx).snapshot();
17527                let excerpt_end_row = Point::from_anchor(&excerpt_range.end, &buffer_snapshot).row;
17528                let last_row = buffer_snapshot.max_point().row;
17529                let lines_below = last_row.saturating_sub(excerpt_end_row);
17530                if lines_below >= lines_to_expand {
17531                    scroll = Some(
17532                        current_scroll_position
17533                            + gpui::Point::new(0.0, lines_to_expand as ScrollOffset),
17534                    );
17535                }
17536            }
17537        }
17538        if direction == ExpandExcerptDirection::Up
17539            && self
17540                .buffer
17541                .read(cx)
17542                .snapshot(cx)
17543                .excerpt_before(excerpt)
17544                .is_none()
17545        {
17546            scroll = Some(current_scroll_position);
17547        }
17548
17549        self.buffer.update(cx, |buffer, cx| {
17550            buffer.expand_excerpts([excerpt], lines_to_expand, direction, cx)
17551        });
17552
17553        if let Some(new_scroll_position) = scroll {
17554            self.set_scroll_position(new_scroll_position, window, cx);
17555        }
17556    }
17557
17558    pub fn go_to_singleton_buffer_point(
17559        &mut self,
17560        point: Point,
17561        window: &mut Window,
17562        cx: &mut Context<Self>,
17563    ) {
17564        self.go_to_singleton_buffer_range(point..point, window, cx);
17565    }
17566
17567    pub fn go_to_singleton_buffer_range(
17568        &mut self,
17569        range: Range<Point>,
17570        window: &mut Window,
17571        cx: &mut Context<Self>,
17572    ) {
17573        let multibuffer = self.buffer().read(cx);
17574        let Some(buffer) = multibuffer.as_singleton() else {
17575            return;
17576        };
17577        let Some(start) = multibuffer.buffer_point_to_anchor(&buffer, range.start, cx) else {
17578            return;
17579        };
17580        let Some(end) = multibuffer.buffer_point_to_anchor(&buffer, range.end, cx) else {
17581            return;
17582        };
17583        self.change_selections(
17584            SelectionEffects::default().nav_history(true),
17585            window,
17586            cx,
17587            |s| s.select_anchor_ranges([start..end]),
17588        );
17589    }
17590
17591    pub fn go_to_diagnostic(
17592        &mut self,
17593        action: &GoToDiagnostic,
17594        window: &mut Window,
17595        cx: &mut Context<Self>,
17596    ) {
17597        if !self.diagnostics_enabled() {
17598            return;
17599        }
17600        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
17601        self.go_to_diagnostic_impl(Direction::Next, action.severity, window, cx)
17602    }
17603
17604    pub fn go_to_prev_diagnostic(
17605        &mut self,
17606        action: &GoToPreviousDiagnostic,
17607        window: &mut Window,
17608        cx: &mut Context<Self>,
17609    ) {
17610        if !self.diagnostics_enabled() {
17611            return;
17612        }
17613        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
17614        self.go_to_diagnostic_impl(Direction::Prev, action.severity, window, cx)
17615    }
17616
17617    pub fn go_to_diagnostic_impl(
17618        &mut self,
17619        direction: Direction,
17620        severity: GoToDiagnosticSeverityFilter,
17621        window: &mut Window,
17622        cx: &mut Context<Self>,
17623    ) {
17624        let buffer = self.buffer.read(cx).snapshot(cx);
17625        let selection = self
17626            .selections
17627            .newest::<MultiBufferOffset>(&self.display_snapshot(cx));
17628
17629        let mut active_group_id = None;
17630        if let ActiveDiagnostic::Group(active_group) = &self.active_diagnostics
17631            && active_group.active_range.start.to_offset(&buffer) == selection.start
17632        {
17633            active_group_id = Some(active_group.group_id);
17634        }
17635
17636        fn filtered<'a>(
17637            severity: GoToDiagnosticSeverityFilter,
17638            diagnostics: impl Iterator<Item = DiagnosticEntryRef<'a, MultiBufferOffset>>,
17639        ) -> impl Iterator<Item = DiagnosticEntryRef<'a, MultiBufferOffset>> {
17640            diagnostics
17641                .filter(move |entry| severity.matches(entry.diagnostic.severity))
17642                .filter(|entry| entry.range.start != entry.range.end)
17643                .filter(|entry| !entry.diagnostic.is_unnecessary)
17644        }
17645
17646        let before = filtered(
17647            severity,
17648            buffer
17649                .diagnostics_in_range(MultiBufferOffset(0)..selection.start)
17650                .filter(|entry| entry.range.start <= selection.start),
17651        );
17652        let after = filtered(
17653            severity,
17654            buffer
17655                .diagnostics_in_range(selection.start..buffer.len())
17656                .filter(|entry| entry.range.start >= selection.start),
17657        );
17658
17659        let mut found: Option<DiagnosticEntryRef<MultiBufferOffset>> = None;
17660        if direction == Direction::Prev {
17661            'outer: for prev_diagnostics in [before.collect::<Vec<_>>(), after.collect::<Vec<_>>()]
17662            {
17663                for diagnostic in prev_diagnostics.into_iter().rev() {
17664                    if diagnostic.range.start != selection.start
17665                        || active_group_id
17666                            .is_some_and(|active| diagnostic.diagnostic.group_id < active)
17667                    {
17668                        found = Some(diagnostic);
17669                        break 'outer;
17670                    }
17671                }
17672            }
17673        } else {
17674            for diagnostic in after.chain(before) {
17675                if diagnostic.range.start != selection.start
17676                    || active_group_id.is_some_and(|active| diagnostic.diagnostic.group_id > active)
17677                {
17678                    found = Some(diagnostic);
17679                    break;
17680                }
17681            }
17682        }
17683        let Some(next_diagnostic) = found else {
17684            return;
17685        };
17686
17687        let next_diagnostic_start = buffer.anchor_after(next_diagnostic.range.start);
17688        let Some(buffer_id) = buffer.buffer_id_for_anchor(next_diagnostic_start) else {
17689            return;
17690        };
17691        let snapshot = self.snapshot(window, cx);
17692        if snapshot.intersects_fold(next_diagnostic.range.start) {
17693            self.unfold_ranges(
17694                std::slice::from_ref(&next_diagnostic.range),
17695                true,
17696                false,
17697                cx,
17698            );
17699        }
17700        self.change_selections(Default::default(), window, cx, |s| {
17701            s.select_ranges(vec![
17702                next_diagnostic.range.start..next_diagnostic.range.start,
17703            ])
17704        });
17705        self.activate_diagnostics(buffer_id, next_diagnostic, window, cx);
17706        self.refresh_edit_prediction(false, true, window, cx);
17707    }
17708
17709    pub fn go_to_next_hunk(&mut self, _: &GoToHunk, window: &mut Window, cx: &mut Context<Self>) {
17710        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
17711        let snapshot = self.snapshot(window, cx);
17712        let selection = self.selections.newest::<Point>(&self.display_snapshot(cx));
17713        self.go_to_hunk_before_or_after_position(
17714            &snapshot,
17715            selection.head(),
17716            Direction::Next,
17717            true,
17718            window,
17719            cx,
17720        );
17721    }
17722
17723    pub fn go_to_hunk_before_or_after_position(
17724        &mut self,
17725        snapshot: &EditorSnapshot,
17726        position: Point,
17727        direction: Direction,
17728        wrap_around: bool,
17729        window: &mut Window,
17730        cx: &mut Context<Editor>,
17731    ) {
17732        let row = if direction == Direction::Next {
17733            self.hunk_after_position(snapshot, position, wrap_around)
17734                .map(|hunk| hunk.row_range.start)
17735        } else {
17736            self.hunk_before_position(snapshot, position, wrap_around)
17737        };
17738
17739        if let Some(row) = row {
17740            let destination = Point::new(row.0, 0);
17741            let autoscroll = Autoscroll::center();
17742
17743            self.unfold_ranges(&[destination..destination], false, false, cx);
17744            self.change_selections(SelectionEffects::scroll(autoscroll), window, cx, |s| {
17745                s.select_ranges([destination..destination]);
17746            });
17747        }
17748    }
17749
17750    fn hunk_after_position(
17751        &mut self,
17752        snapshot: &EditorSnapshot,
17753        position: Point,
17754        wrap_around: bool,
17755    ) -> Option<MultiBufferDiffHunk> {
17756        let result = snapshot
17757            .buffer_snapshot()
17758            .diff_hunks_in_range(position..snapshot.buffer_snapshot().max_point())
17759            .find(|hunk| hunk.row_range.start.0 > position.row);
17760
17761        if wrap_around {
17762            result.or_else(|| {
17763                snapshot
17764                    .buffer_snapshot()
17765                    .diff_hunks_in_range(Point::zero()..position)
17766                    .find(|hunk| hunk.row_range.end.0 < position.row)
17767            })
17768        } else {
17769            result
17770        }
17771    }
17772
17773    fn go_to_prev_hunk(
17774        &mut self,
17775        _: &GoToPreviousHunk,
17776        window: &mut Window,
17777        cx: &mut Context<Self>,
17778    ) {
17779        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
17780        let snapshot = self.snapshot(window, cx);
17781        let selection = self.selections.newest::<Point>(&snapshot.display_snapshot);
17782        self.go_to_hunk_before_or_after_position(
17783            &snapshot,
17784            selection.head(),
17785            Direction::Prev,
17786            true,
17787            window,
17788            cx,
17789        );
17790    }
17791
17792    fn hunk_before_position(
17793        &mut self,
17794        snapshot: &EditorSnapshot,
17795        position: Point,
17796        wrap_around: bool,
17797    ) -> Option<MultiBufferRow> {
17798        let result = snapshot.buffer_snapshot().diff_hunk_before(position);
17799
17800        if wrap_around {
17801            result.or_else(|| snapshot.buffer_snapshot().diff_hunk_before(Point::MAX))
17802        } else {
17803            result
17804        }
17805    }
17806
17807    fn go_to_next_change(
17808        &mut self,
17809        _: &GoToNextChange,
17810        window: &mut Window,
17811        cx: &mut Context<Self>,
17812    ) {
17813        if let Some(selections) = self
17814            .change_list
17815            .next_change(1, Direction::Next)
17816            .map(|s| s.to_vec())
17817        {
17818            self.change_selections(Default::default(), window, cx, |s| {
17819                let map = s.display_snapshot();
17820                s.select_display_ranges(selections.iter().map(|a| {
17821                    let point = a.to_display_point(&map);
17822                    point..point
17823                }))
17824            })
17825        }
17826    }
17827
17828    fn go_to_previous_change(
17829        &mut self,
17830        _: &GoToPreviousChange,
17831        window: &mut Window,
17832        cx: &mut Context<Self>,
17833    ) {
17834        if let Some(selections) = self
17835            .change_list
17836            .next_change(1, Direction::Prev)
17837            .map(|s| s.to_vec())
17838        {
17839            self.change_selections(Default::default(), window, cx, |s| {
17840                let map = s.display_snapshot();
17841                s.select_display_ranges(selections.iter().map(|a| {
17842                    let point = a.to_display_point(&map);
17843                    point..point
17844                }))
17845            })
17846        }
17847    }
17848
17849    pub fn go_to_next_document_highlight(
17850        &mut self,
17851        _: &GoToNextDocumentHighlight,
17852        window: &mut Window,
17853        cx: &mut Context<Self>,
17854    ) {
17855        self.go_to_document_highlight_before_or_after_position(Direction::Next, window, cx);
17856    }
17857
17858    pub fn go_to_prev_document_highlight(
17859        &mut self,
17860        _: &GoToPreviousDocumentHighlight,
17861        window: &mut Window,
17862        cx: &mut Context<Self>,
17863    ) {
17864        self.go_to_document_highlight_before_or_after_position(Direction::Prev, window, cx);
17865    }
17866
17867    pub fn go_to_document_highlight_before_or_after_position(
17868        &mut self,
17869        direction: Direction,
17870        window: &mut Window,
17871        cx: &mut Context<Editor>,
17872    ) {
17873        self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
17874        let snapshot = self.snapshot(window, cx);
17875        let buffer = &snapshot.buffer_snapshot();
17876        let position = self
17877            .selections
17878            .newest::<Point>(&snapshot.display_snapshot)
17879            .head();
17880        let anchor_position = buffer.anchor_after(position);
17881
17882        // Get all document highlights (both read and write)
17883        let mut all_highlights = Vec::new();
17884
17885        if let Some((_, read_highlights)) = self
17886            .background_highlights
17887            .get(&HighlightKey::DocumentHighlightRead)
17888        {
17889            all_highlights.extend(read_highlights.iter());
17890        }
17891
17892        if let Some((_, write_highlights)) = self
17893            .background_highlights
17894            .get(&HighlightKey::DocumentHighlightWrite)
17895        {
17896            all_highlights.extend(write_highlights.iter());
17897        }
17898
17899        if all_highlights.is_empty() {
17900            return;
17901        }
17902
17903        // Sort highlights by position
17904        all_highlights.sort_by(|a, b| a.start.cmp(&b.start, buffer));
17905
17906        let target_highlight = match direction {
17907            Direction::Next => {
17908                // Find the first highlight after the current position
17909                all_highlights
17910                    .iter()
17911                    .find(|highlight| highlight.start.cmp(&anchor_position, buffer).is_gt())
17912            }
17913            Direction::Prev => {
17914                // Find the last highlight before the current position
17915                all_highlights
17916                    .iter()
17917                    .rev()
17918                    .find(|highlight| highlight.end.cmp(&anchor_position, buffer).is_lt())
17919            }
17920        };
17921
17922        if let Some(highlight) = target_highlight {
17923            let destination = highlight.start.to_point(buffer);
17924            let autoscroll = Autoscroll::center();
17925
17926            self.unfold_ranges(&[destination..destination], false, false, cx);
17927            self.change_selections(SelectionEffects::scroll(autoscroll), window, cx, |s| {
17928                s.select_ranges([destination..destination]);
17929            });
17930        }
17931    }
17932
17933    fn go_to_line<T: 'static>(
17934        &mut self,
17935        position: Anchor,
17936        highlight_color: Option<Hsla>,
17937        window: &mut Window,
17938        cx: &mut Context<Self>,
17939    ) {
17940        let snapshot = self.snapshot(window, cx).display_snapshot;
17941        let position = position.to_point(&snapshot.buffer_snapshot());
17942        let start = snapshot
17943            .buffer_snapshot()
17944            .clip_point(Point::new(position.row, 0), Bias::Left);
17945        let end = start + Point::new(1, 0);
17946        let start = snapshot.buffer_snapshot().anchor_before(start);
17947        let end = snapshot.buffer_snapshot().anchor_before(end);
17948
17949        self.highlight_rows::<T>(
17950            start..end,
17951            highlight_color
17952                .unwrap_or_else(|| cx.theme().colors().editor_highlighted_line_background),
17953            Default::default(),
17954            cx,
17955        );
17956
17957        if self.buffer.read(cx).is_singleton() {
17958            self.request_autoscroll(Autoscroll::center().for_anchor(start), cx);
17959        }
17960    }
17961
17962    pub fn go_to_definition(
17963        &mut self,
17964        _: &GoToDefinition,
17965        window: &mut Window,
17966        cx: &mut Context<Self>,
17967    ) -> Task<Result<Navigated>> {
17968        let definition =
17969            self.go_to_definition_of_kind(GotoDefinitionKind::Symbol, false, window, cx);
17970        let fallback_strategy = EditorSettings::get_global(cx).go_to_definition_fallback;
17971        cx.spawn_in(window, async move |editor, cx| {
17972            if definition.await? == Navigated::Yes {
17973                return Ok(Navigated::Yes);
17974            }
17975            match fallback_strategy {
17976                GoToDefinitionFallback::None => Ok(Navigated::No),
17977                GoToDefinitionFallback::FindAllReferences => {
17978                    match editor.update_in(cx, |editor, window, cx| {
17979                        editor.find_all_references(&FindAllReferences::default(), window, cx)
17980                    })? {
17981                        Some(references) => references.await,
17982                        None => Ok(Navigated::No),
17983                    }
17984                }
17985            }
17986        })
17987    }
17988
17989    pub fn go_to_declaration(
17990        &mut self,
17991        _: &GoToDeclaration,
17992        window: &mut Window,
17993        cx: &mut Context<Self>,
17994    ) -> Task<Result<Navigated>> {
17995        self.go_to_definition_of_kind(GotoDefinitionKind::Declaration, false, window, cx)
17996    }
17997
17998    pub fn go_to_declaration_split(
17999        &mut self,
18000        _: &GoToDeclaration,
18001        window: &mut Window,
18002        cx: &mut Context<Self>,
18003    ) -> Task<Result<Navigated>> {
18004        self.go_to_definition_of_kind(GotoDefinitionKind::Declaration, true, window, cx)
18005    }
18006
18007    pub fn go_to_implementation(
18008        &mut self,
18009        _: &GoToImplementation,
18010        window: &mut Window,
18011        cx: &mut Context<Self>,
18012    ) -> Task<Result<Navigated>> {
18013        self.go_to_definition_of_kind(GotoDefinitionKind::Implementation, false, window, cx)
18014    }
18015
18016    pub fn go_to_implementation_split(
18017        &mut self,
18018        _: &GoToImplementationSplit,
18019        window: &mut Window,
18020        cx: &mut Context<Self>,
18021    ) -> Task<Result<Navigated>> {
18022        self.go_to_definition_of_kind(GotoDefinitionKind::Implementation, true, window, cx)
18023    }
18024
18025    pub fn go_to_type_definition(
18026        &mut self,
18027        _: &GoToTypeDefinition,
18028        window: &mut Window,
18029        cx: &mut Context<Self>,
18030    ) -> Task<Result<Navigated>> {
18031        self.go_to_definition_of_kind(GotoDefinitionKind::Type, false, window, cx)
18032    }
18033
18034    pub fn go_to_definition_split(
18035        &mut self,
18036        _: &GoToDefinitionSplit,
18037        window: &mut Window,
18038        cx: &mut Context<Self>,
18039    ) -> Task<Result<Navigated>> {
18040        self.go_to_definition_of_kind(GotoDefinitionKind::Symbol, true, window, cx)
18041    }
18042
18043    pub fn go_to_type_definition_split(
18044        &mut self,
18045        _: &GoToTypeDefinitionSplit,
18046        window: &mut Window,
18047        cx: &mut Context<Self>,
18048    ) -> Task<Result<Navigated>> {
18049        self.go_to_definition_of_kind(GotoDefinitionKind::Type, true, window, cx)
18050    }
18051
18052    fn go_to_definition_of_kind(
18053        &mut self,
18054        kind: GotoDefinitionKind,
18055        split: bool,
18056        window: &mut Window,
18057        cx: &mut Context<Self>,
18058    ) -> Task<Result<Navigated>> {
18059        let Some(provider) = self.semantics_provider.clone() else {
18060            return Task::ready(Ok(Navigated::No));
18061        };
18062        let head = self
18063            .selections
18064            .newest::<MultiBufferOffset>(&self.display_snapshot(cx))
18065            .head();
18066        let buffer = self.buffer.read(cx);
18067        let Some((buffer, head)) = buffer.text_anchor_for_position(head, cx) else {
18068            return Task::ready(Ok(Navigated::No));
18069        };
18070        let Some(definitions) = provider.definitions(&buffer, head, kind, cx) else {
18071            return Task::ready(Ok(Navigated::No));
18072        };
18073
18074        let nav_entry = self.navigation_entry(self.selections.newest_anchor().head(), cx);
18075
18076        cx.spawn_in(window, async move |editor, cx| {
18077            let Some(definitions) = definitions.await? else {
18078                return Ok(Navigated::No);
18079            };
18080            let navigated = editor
18081                .update_in(cx, |editor, window, cx| {
18082                    editor.navigate_to_hover_links(
18083                        Some(kind),
18084                        definitions
18085                            .into_iter()
18086                            .filter(|location| {
18087                                hover_links::exclude_link_to_position(&buffer, &head, location, cx)
18088                            })
18089                            .map(HoverLink::Text)
18090                            .collect::<Vec<_>>(),
18091                        nav_entry,
18092                        split,
18093                        window,
18094                        cx,
18095                    )
18096                })?
18097                .await?;
18098            anyhow::Ok(navigated)
18099        })
18100    }
18101
18102    pub fn open_url(&mut self, _: &OpenUrl, window: &mut Window, cx: &mut Context<Self>) {
18103        let selection = self.selections.newest_anchor();
18104        let head = selection.head();
18105        let tail = selection.tail();
18106
18107        let Some((buffer, start_position)) =
18108            self.buffer.read(cx).text_anchor_for_position(head, cx)
18109        else {
18110            return;
18111        };
18112
18113        let end_position = if head != tail {
18114            let Some((_, pos)) = self.buffer.read(cx).text_anchor_for_position(tail, cx) else {
18115                return;
18116            };
18117            Some(pos)
18118        } else {
18119            None
18120        };
18121
18122        let url_finder = cx.spawn_in(window, async move |_editor, cx| {
18123            let url = if let Some(end_pos) = end_position {
18124                find_url_from_range(&buffer, start_position..end_pos, cx.clone())
18125            } else {
18126                find_url(&buffer, start_position, cx.clone()).map(|(_, url)| url)
18127            };
18128
18129            if let Some(url) = url {
18130                cx.update(|window, cx| {
18131                    if parse_zed_link(&url, cx).is_some() {
18132                        window.dispatch_action(Box::new(zed_actions::OpenZedUrl { url }), cx);
18133                    } else {
18134                        cx.open_url(&url);
18135                    }
18136                })?;
18137            }
18138
18139            anyhow::Ok(())
18140        });
18141
18142        url_finder.detach();
18143    }
18144
18145    pub fn open_selected_filename(
18146        &mut self,
18147        _: &OpenSelectedFilename,
18148        window: &mut Window,
18149        cx: &mut Context<Self>,
18150    ) {
18151        let Some(workspace) = self.workspace() else {
18152            return;
18153        };
18154
18155        let position = self.selections.newest_anchor().head();
18156
18157        let Some((buffer, buffer_position)) =
18158            self.buffer.read(cx).text_anchor_for_position(position, cx)
18159        else {
18160            return;
18161        };
18162
18163        let project = self.project.clone();
18164
18165        cx.spawn_in(window, async move |_, cx| {
18166            let result = find_file(&buffer, project, buffer_position, cx).await;
18167
18168            if let Some((_, path)) = result {
18169                workspace
18170                    .update_in(cx, |workspace, window, cx| {
18171                        workspace.open_resolved_path(path, window, cx)
18172                    })?
18173                    .await?;
18174            }
18175            anyhow::Ok(())
18176        })
18177        .detach();
18178    }
18179
18180    pub(crate) fn navigate_to_hover_links(
18181        &mut self,
18182        kind: Option<GotoDefinitionKind>,
18183        definitions: Vec<HoverLink>,
18184        origin: Option<NavigationEntry>,
18185        split: bool,
18186        window: &mut Window,
18187        cx: &mut Context<Editor>,
18188    ) -> Task<Result<Navigated>> {
18189        // Separate out url and file links, we can only handle one of them at most or an arbitrary number of locations
18190        let mut first_url_or_file = None;
18191        let definitions: Vec<_> = definitions
18192            .into_iter()
18193            .filter_map(|def| match def {
18194                HoverLink::Text(link) => Some(Task::ready(anyhow::Ok(Some(link.target)))),
18195                HoverLink::InlayHint(lsp_location, server_id) => {
18196                    let computation =
18197                        self.compute_target_location(lsp_location, server_id, window, cx);
18198                    Some(cx.background_spawn(computation))
18199                }
18200                HoverLink::Url(url) => {
18201                    first_url_or_file = Some(Either::Left(url));
18202                    None
18203                }
18204                HoverLink::File(path) => {
18205                    first_url_or_file = Some(Either::Right(path));
18206                    None
18207                }
18208            })
18209            .collect();
18210
18211        let workspace = self.workspace();
18212
18213        let excerpt_context_lines = multi_buffer::excerpt_context_lines(cx);
18214        cx.spawn_in(window, async move |editor, cx| {
18215            let locations: Vec<Location> = future::join_all(definitions)
18216                .await
18217                .into_iter()
18218                .filter_map(|location| location.transpose())
18219                .collect::<Result<_>>()
18220                .context("location tasks")?;
18221            let mut locations = cx.update(|_, cx| {
18222                locations
18223                    .into_iter()
18224                    .map(|location| {
18225                        let buffer = location.buffer.read(cx);
18226                        (location.buffer, location.range.to_point(buffer))
18227                    })
18228                    .into_group_map()
18229            })?;
18230            let mut num_locations = 0;
18231            for ranges in locations.values_mut() {
18232                ranges.sort_by_key(|range| (range.start, Reverse(range.end)));
18233                ranges.dedup();
18234                // Merge overlapping or contained ranges. After sorting by
18235                // (start, Reverse(end)), we can merge in a single pass:
18236                // if the next range starts before the current one ends,
18237                // extend the current range's end if needed.
18238                let mut i = 0;
18239                while i + 1 < ranges.len() {
18240                    if ranges[i + 1].start <= ranges[i].end {
18241                        let merged_end = ranges[i].end.max(ranges[i + 1].end);
18242                        ranges[i].end = merged_end;
18243                        ranges.remove(i + 1);
18244                    } else {
18245                        i += 1;
18246                    }
18247                }
18248                let fits_in_one_excerpt = ranges
18249                    .iter()
18250                    .tuple_windows()
18251                    .all(|(a, b)| b.start.row - a.end.row <= 2 * excerpt_context_lines);
18252                num_locations += if fits_in_one_excerpt { 1 } else { ranges.len() };
18253            }
18254
18255            if num_locations > 1 {
18256                let tab_kind = match kind {
18257                    Some(GotoDefinitionKind::Implementation) => "Implementations",
18258                    Some(GotoDefinitionKind::Symbol) | None => "Definitions",
18259                    Some(GotoDefinitionKind::Declaration) => "Declarations",
18260                    Some(GotoDefinitionKind::Type) => "Types",
18261                };
18262                let title = editor
18263                    .update_in(cx, |_, _, cx| {
18264                        let target = locations
18265                            .iter()
18266                            .flat_map(|(k, v)| iter::repeat(k.clone()).zip(v))
18267                            .map(|(buffer, location)| {
18268                                buffer
18269                                    .read(cx)
18270                                    .text_for_range(location.clone())
18271                                    .collect::<String>()
18272                            })
18273                            .filter(|text| !text.contains('\n'))
18274                            .unique()
18275                            .take(3)
18276                            .join(", ");
18277                        if target.is_empty() {
18278                            tab_kind.to_owned()
18279                        } else {
18280                            format!("{tab_kind} for {target}")
18281                        }
18282                    })
18283                    .context("buffer title")?;
18284
18285                let Some(workspace) = workspace else {
18286                    return Ok(Navigated::No);
18287                };
18288
18289                let opened = workspace
18290                    .update_in(cx, |workspace, window, cx| {
18291                        let allow_preview = PreviewTabsSettings::get_global(cx)
18292                            .enable_preview_multibuffer_from_code_navigation;
18293                        if let Some((target_editor, target_pane)) =
18294                            Self::open_locations_in_multibuffer(
18295                                workspace,
18296                                locations,
18297                                title,
18298                                split,
18299                                allow_preview,
18300                                MultibufferSelectionMode::First,
18301                                window,
18302                                cx,
18303                            )
18304                        {
18305                            // We create our own nav history instead of using
18306                            // `target_editor.nav_history` because `nav_history`
18307                            // seems to be populated asynchronously when an item
18308                            // is added to a pane
18309                            let mut nav_history = target_pane
18310                                .update(cx, |pane, _| pane.nav_history_for_item(&target_editor));
18311                            target_editor.update(cx, |editor, cx| {
18312                                let nav_data = editor
18313                                    .navigation_data(editor.selections.newest_anchor().head(), cx);
18314                                let target =
18315                                    Some(nav_history.navigation_entry(Some(
18316                                        Arc::new(nav_data) as Arc<dyn Any + Send + Sync>
18317                                    )));
18318                                nav_history.push_tag(origin, target);
18319                            })
18320                        }
18321                    })
18322                    .is_ok();
18323
18324                anyhow::Ok(Navigated::from_bool(opened))
18325            } else if num_locations == 0 {
18326                // If there is one url or file, open it directly
18327                match first_url_or_file {
18328                    Some(Either::Left(url)) => {
18329                        cx.update(|window, cx| {
18330                            if parse_zed_link(&url, cx).is_some() {
18331                                window
18332                                    .dispatch_action(Box::new(zed_actions::OpenZedUrl { url }), cx);
18333                            } else {
18334                                cx.open_url(&url);
18335                            }
18336                        })?;
18337                        Ok(Navigated::Yes)
18338                    }
18339                    Some(Either::Right(path)) => {
18340                        // TODO(andrew): respect preview tab settings
18341                        //               `enable_keep_preview_on_code_navigation` and
18342                        //               `enable_preview_file_from_code_navigation`
18343                        let Some(workspace) = workspace else {
18344                            return Ok(Navigated::No);
18345                        };
18346                        workspace
18347                            .update_in(cx, |workspace, window, cx| {
18348                                workspace.open_resolved_path(path, window, cx)
18349                            })?
18350                            .await?;
18351                        Ok(Navigated::Yes)
18352                    }
18353                    None => Ok(Navigated::No),
18354                }
18355            } else {
18356                let (target_buffer, target_ranges) = locations.into_iter().next().unwrap();
18357
18358                editor.update_in(cx, |editor, window, cx| {
18359                    let target_ranges = target_ranges
18360                        .into_iter()
18361                        .map(|r| editor.range_for_match(&r))
18362                        .map(collapse_multiline_range)
18363                        .collect::<Vec<_>>();
18364                    if !split
18365                        && Some(&target_buffer) == editor.buffer.read(cx).as_singleton().as_ref()
18366                    {
18367                        let multibuffer = editor.buffer.read(cx);
18368                        let target_ranges = target_ranges
18369                            .into_iter()
18370                            .filter_map(|r| {
18371                                let start = multibuffer.buffer_point_to_anchor(
18372                                    &target_buffer,
18373                                    r.start,
18374                                    cx,
18375                                )?;
18376                                let end = multibuffer.buffer_point_to_anchor(
18377                                    &target_buffer,
18378                                    r.end,
18379                                    cx,
18380                                )?;
18381                                Some(start..end)
18382                            })
18383                            .collect::<Vec<_>>();
18384                        if target_ranges.is_empty() {
18385                            return Navigated::No;
18386                        }
18387
18388                        editor.change_selections(
18389                            SelectionEffects::default().nav_history(true),
18390                            window,
18391                            cx,
18392                            |s| s.select_anchor_ranges(target_ranges),
18393                        );
18394
18395                        let target =
18396                            editor.navigation_entry(editor.selections.newest_anchor().head(), cx);
18397                        if let Some(mut nav_history) = editor.nav_history.clone() {
18398                            nav_history.push_tag(origin, target);
18399                        }
18400                    } else {
18401                        let Some(workspace) = workspace else {
18402                            return Navigated::No;
18403                        };
18404                        let pane = workspace.read(cx).active_pane().clone();
18405                        window.defer(cx, move |window, cx| {
18406                            let (target_editor, target_pane): (Entity<Self>, Entity<Pane>) =
18407                                workspace.update(cx, |workspace, cx| {
18408                                    let pane = if split {
18409                                        workspace.adjacent_pane(window, cx)
18410                                    } else {
18411                                        workspace.active_pane().clone()
18412                                    };
18413
18414                                    let preview_tabs_settings = PreviewTabsSettings::get_global(cx);
18415                                    let keep_old_preview = preview_tabs_settings
18416                                        .enable_keep_preview_on_code_navigation;
18417                                    let allow_new_preview = preview_tabs_settings
18418                                        .enable_preview_file_from_code_navigation;
18419
18420                                    let editor = workspace.open_project_item(
18421                                        pane.clone(),
18422                                        target_buffer.clone(),
18423                                        true,
18424                                        true,
18425                                        keep_old_preview,
18426                                        allow_new_preview,
18427                                        window,
18428                                        cx,
18429                                    );
18430                                    (editor, pane)
18431                                });
18432                            // We create our own nav history instead of using
18433                            // `target_editor.nav_history` because `nav_history`
18434                            // seems to be populated asynchronously when an item
18435                            // is added to a pane
18436                            let mut nav_history = target_pane
18437                                .update(cx, |pane, _| pane.nav_history_for_item(&target_editor));
18438                            target_editor.update(cx, |target_editor, cx| {
18439                                // When selecting a definition in a different buffer, disable the nav history
18440                                // to avoid creating a history entry at the previous cursor location.
18441                                pane.update(cx, |pane, _| pane.disable_history());
18442
18443                                let multibuffer = target_editor.buffer.read(cx);
18444                                let Some(target_buffer) = multibuffer.as_singleton() else {
18445                                    return Navigated::No;
18446                                };
18447                                let target_ranges = target_ranges
18448                                    .into_iter()
18449                                    .filter_map(|r| {
18450                                        let start = multibuffer.buffer_point_to_anchor(
18451                                            &target_buffer,
18452                                            r.start,
18453                                            cx,
18454                                        )?;
18455                                        let end = multibuffer.buffer_point_to_anchor(
18456                                            &target_buffer,
18457                                            r.end,
18458                                            cx,
18459                                        )?;
18460                                        Some(start..end)
18461                                    })
18462                                    .collect::<Vec<_>>();
18463                                if target_ranges.is_empty() {
18464                                    return Navigated::No;
18465                                }
18466
18467                                target_editor.change_selections(
18468                                    SelectionEffects::default().nav_history(true),
18469                                    window,
18470                                    cx,
18471                                    |s| s.select_anchor_ranges(target_ranges),
18472                                );
18473
18474                                let nav_data = target_editor.navigation_data(
18475                                    target_editor.selections.newest_anchor().head(),
18476                                    cx,
18477                                );
18478                                let target =
18479                                    Some(nav_history.navigation_entry(Some(
18480                                        Arc::new(nav_data) as Arc<dyn Any + Send + Sync>
18481                                    )));
18482                                nav_history.push_tag(origin, target);
18483                                pane.update(cx, |pane, _| pane.enable_history());
18484                                Navigated::Yes
18485                            });
18486                        });
18487                    }
18488                    Navigated::Yes
18489                })
18490            }
18491        })
18492    }
18493
18494    fn compute_target_location(
18495        &self,
18496        lsp_location: lsp::Location,
18497        server_id: LanguageServerId,
18498        window: &mut Window,
18499        cx: &mut Context<Self>,
18500    ) -> Task<anyhow::Result<Option<Location>>> {
18501        let Some(project) = self.project.clone() else {
18502            return Task::ready(Ok(None));
18503        };
18504
18505        cx.spawn_in(window, async move |editor, cx| {
18506            let location_task = editor.update(cx, |_, cx| {
18507                project.update(cx, |project, cx| {
18508                    project.open_local_buffer_via_lsp(lsp_location.uri.clone(), server_id, cx)
18509                })
18510            })?;
18511            let location = Some({
18512                let target_buffer_handle = location_task.await.context("open local buffer")?;
18513                let range = target_buffer_handle.read_with(cx, |target_buffer, _| {
18514                    let target_start = target_buffer
18515                        .clip_point_utf16(point_from_lsp(lsp_location.range.start), Bias::Left);
18516                    let target_end = target_buffer
18517                        .clip_point_utf16(point_from_lsp(lsp_location.range.end), Bias::Left);
18518                    target_buffer.anchor_after(target_start)
18519                        ..target_buffer.anchor_before(target_end)
18520                });
18521                Location {
18522                    buffer: target_buffer_handle,
18523                    range,
18524                }
18525            });
18526            Ok(location)
18527        })
18528    }
18529
18530    fn go_to_next_reference(
18531        &mut self,
18532        _: &GoToNextReference,
18533        window: &mut Window,
18534        cx: &mut Context<Self>,
18535    ) {
18536        let task = self.go_to_reference_before_or_after_position(Direction::Next, 1, window, cx);
18537        if let Some(task) = task {
18538            task.detach();
18539        };
18540    }
18541
18542    fn go_to_prev_reference(
18543        &mut self,
18544        _: &GoToPreviousReference,
18545        window: &mut Window,
18546        cx: &mut Context<Self>,
18547    ) {
18548        let task = self.go_to_reference_before_or_after_position(Direction::Prev, 1, window, cx);
18549        if let Some(task) = task {
18550            task.detach();
18551        };
18552    }
18553
18554    fn go_to_symbol_by_offset(
18555        &mut self,
18556        window: &mut Window,
18557        cx: &mut Context<Self>,
18558        offset: i8,
18559    ) -> Task<Result<()>> {
18560        let editor_snapshot = self.snapshot(window, cx);
18561
18562        // We don't care about multi-buffer symbols
18563        let Some((excerpt_id, _, _)) = editor_snapshot.as_singleton() else {
18564            return Task::ready(Ok(()));
18565        };
18566
18567        let cursor_offset = self
18568            .selections
18569            .newest::<MultiBufferOffset>(&editor_snapshot.display_snapshot)
18570            .head();
18571
18572        cx.spawn_in(window, async move |editor, wcx| -> Result<()> {
18573            let Ok(Some(remote_id)) = editor.update(wcx, |ed, cx| {
18574                let buffer = ed.buffer.read(cx).as_singleton()?;
18575                Some(buffer.read(cx).remote_id())
18576            }) else {
18577                return Ok(());
18578            };
18579
18580            let task = editor.update(wcx, |ed, cx| ed.buffer_outline_items(remote_id, cx))?;
18581            let outline_items: Vec<OutlineItem<text::Anchor>> = task.await;
18582
18583            let multi_snapshot = editor_snapshot.buffer();
18584            let buffer_range = |range: &Range<_>| {
18585                Anchor::range_in_buffer(excerpt_id, range.clone()).to_offset(multi_snapshot)
18586            };
18587
18588            wcx.update_window(wcx.window_handle(), |_, window, acx| {
18589                let current_idx = outline_items
18590                    .iter()
18591                    .enumerate()
18592                    .filter_map(|(idx, item)| {
18593                        // Find the closest outline item by distance between outline text and cursor location
18594                        let source_range = buffer_range(&item.source_range_for_text);
18595                        let distance_to_closest_endpoint = cmp::min(
18596                            (source_range.start.0 as isize - cursor_offset.0 as isize).abs(),
18597                            (source_range.end.0 as isize - cursor_offset.0 as isize).abs(),
18598                        );
18599
18600                        let item_towards_offset =
18601                            (source_range.start.0 as isize - cursor_offset.0 as isize).signum()
18602                                == (offset as isize).signum();
18603
18604                        let source_range_contains_cursor = source_range.contains(&cursor_offset);
18605
18606                        // To pick the next outline to jump to, we should jump in the direction of the offset, and
18607                        // we should not already be within the outline's source range. We then pick the closest outline
18608                        // item.
18609                        (item_towards_offset && !source_range_contains_cursor)
18610                            .then_some((distance_to_closest_endpoint, idx))
18611                    })
18612                    .min()
18613                    .map(|(_, idx)| idx);
18614
18615                let Some(idx) = current_idx else {
18616                    return;
18617                };
18618
18619                let range = buffer_range(&outline_items[idx].source_range_for_text);
18620                let selection = [range.start..range.start];
18621
18622                let _ = editor
18623                    .update(acx, |editor, ecx| {
18624                        editor.change_selections(
18625                            SelectionEffects::scroll(Autoscroll::newest()),
18626                            window,
18627                            ecx,
18628                            |s| s.select_ranges(selection),
18629                        );
18630                    })
18631                    .ok();
18632            })?;
18633
18634            Ok(())
18635        })
18636    }
18637
18638    fn go_to_next_symbol(
18639        &mut self,
18640        _: &GoToNextSymbol,
18641        window: &mut Window,
18642        cx: &mut Context<Self>,
18643    ) {
18644        self.go_to_symbol_by_offset(window, cx, 1).detach();
18645    }
18646
18647    fn go_to_previous_symbol(
18648        &mut self,
18649        _: &GoToPreviousSymbol,
18650        window: &mut Window,
18651        cx: &mut Context<Self>,
18652    ) {
18653        self.go_to_symbol_by_offset(window, cx, -1).detach();
18654    }
18655
18656    pub fn go_to_reference_before_or_after_position(
18657        &mut self,
18658        direction: Direction,
18659        count: usize,
18660        window: &mut Window,
18661        cx: &mut Context<Self>,
18662    ) -> Option<Task<Result<()>>> {
18663        let selection = self.selections.newest_anchor();
18664        let head = selection.head();
18665
18666        let multi_buffer = self.buffer.read(cx);
18667
18668        let (buffer, text_head) = multi_buffer.text_anchor_for_position(head, cx)?;
18669        let workspace = self.workspace()?;
18670        let project = workspace.read(cx).project().clone();
18671        let references =
18672            project.update(cx, |project, cx| project.references(&buffer, text_head, cx));
18673        Some(cx.spawn_in(window, async move |editor, cx| -> Result<()> {
18674            let Some(locations) = references.await? else {
18675                return Ok(());
18676            };
18677
18678            if locations.is_empty() {
18679                // totally normal - the cursor may be on something which is not
18680                // a symbol (e.g. a keyword)
18681                log::info!("no references found under cursor");
18682                return Ok(());
18683            }
18684
18685            let multi_buffer = editor.read_with(cx, |editor, _| editor.buffer().clone())?;
18686
18687            let (locations, current_location_index) =
18688                multi_buffer.update(cx, |multi_buffer, cx| {
18689                    let mut locations = locations
18690                        .into_iter()
18691                        .filter_map(|loc| {
18692                            let start = multi_buffer.buffer_anchor_to_anchor(
18693                                &loc.buffer,
18694                                loc.range.start,
18695                                cx,
18696                            )?;
18697                            let end = multi_buffer.buffer_anchor_to_anchor(
18698                                &loc.buffer,
18699                                loc.range.end,
18700                                cx,
18701                            )?;
18702                            Some(start..end)
18703                        })
18704                        .collect::<Vec<_>>();
18705
18706                    let multi_buffer_snapshot = multi_buffer.snapshot(cx);
18707                    // There is an O(n) implementation, but given this list will be
18708                    // small (usually <100 items), the extra O(log(n)) factor isn't
18709                    // worth the (surprisingly large amount of) extra complexity.
18710                    locations
18711                        .sort_unstable_by(|l, r| l.start.cmp(&r.start, &multi_buffer_snapshot));
18712
18713                    let head_offset = head.to_offset(&multi_buffer_snapshot);
18714
18715                    let current_location_index = locations.iter().position(|loc| {
18716                        loc.start.to_offset(&multi_buffer_snapshot) <= head_offset
18717                            && loc.end.to_offset(&multi_buffer_snapshot) >= head_offset
18718                    });
18719
18720                    (locations, current_location_index)
18721                });
18722
18723            let Some(current_location_index) = current_location_index else {
18724                // This indicates something has gone wrong, because we already
18725                // handle the "no references" case above
18726                log::error!(
18727                    "failed to find current reference under cursor. Total references: {}",
18728                    locations.len()
18729                );
18730                return Ok(());
18731            };
18732
18733            let destination_location_index = match direction {
18734                Direction::Next => (current_location_index + count) % locations.len(),
18735                Direction::Prev => {
18736                    (current_location_index + locations.len() - count % locations.len())
18737                        % locations.len()
18738                }
18739            };
18740
18741            // TODO(cameron): is this needed?
18742            // the thinking is to avoid "jumping to the current location" (avoid
18743            // polluting "jumplist" in vim terms)
18744            if current_location_index == destination_location_index {
18745                return Ok(());
18746            }
18747
18748            let Range { start, end } = locations[destination_location_index];
18749
18750            editor.update_in(cx, |editor, window, cx| {
18751                let effects = SelectionEffects::default();
18752
18753                editor.unfold_ranges(&[start..end], false, false, cx);
18754                editor.change_selections(effects, window, cx, |s| {
18755                    s.select_ranges([start..start]);
18756                });
18757            })?;
18758
18759            Ok(())
18760        }))
18761    }
18762
18763    pub fn find_all_references(
18764        &mut self,
18765        action: &FindAllReferences,
18766        window: &mut Window,
18767        cx: &mut Context<Self>,
18768    ) -> Option<Task<Result<Navigated>>> {
18769        let always_open_multibuffer = action.always_open_multibuffer;
18770        let selection = self.selections.newest_anchor();
18771        let multi_buffer = self.buffer.read(cx);
18772        let multi_buffer_snapshot = multi_buffer.snapshot(cx);
18773        let selection_offset = selection.map(|anchor| anchor.to_offset(&multi_buffer_snapshot));
18774        let selection_point = selection.map(|anchor| anchor.to_point(&multi_buffer_snapshot));
18775        let head = selection_offset.head();
18776
18777        let head_anchor = multi_buffer_snapshot.anchor_at(
18778            head,
18779            if head < selection_offset.tail() {
18780                Bias::Right
18781            } else {
18782                Bias::Left
18783            },
18784        );
18785
18786        match self
18787            .find_all_references_task_sources
18788            .binary_search_by(|anchor| anchor.cmp(&head_anchor, &multi_buffer_snapshot))
18789        {
18790            Ok(_) => {
18791                log::info!(
18792                    "Ignoring repeated FindAllReferences invocation with the position of already running task"
18793                );
18794                return None;
18795            }
18796            Err(i) => {
18797                self.find_all_references_task_sources.insert(i, head_anchor);
18798            }
18799        }
18800
18801        let (buffer, head) = multi_buffer.text_anchor_for_position(head, cx)?;
18802        let workspace = self.workspace()?;
18803        let project = workspace.read(cx).project().clone();
18804        let references = project.update(cx, |project, cx| project.references(&buffer, head, cx));
18805        Some(cx.spawn_in(window, async move |editor, cx| {
18806            let _cleanup = cx.on_drop(&editor, move |editor, _| {
18807                if let Ok(i) = editor
18808                    .find_all_references_task_sources
18809                    .binary_search_by(|anchor| anchor.cmp(&head_anchor, &multi_buffer_snapshot))
18810                {
18811                    editor.find_all_references_task_sources.remove(i);
18812                }
18813            });
18814
18815            let Some(locations) = references.await? else {
18816                return anyhow::Ok(Navigated::No);
18817            };
18818            let mut locations = cx.update(|_, cx| {
18819                locations
18820                    .into_iter()
18821                    .map(|location| {
18822                        let buffer = location.buffer.read(cx);
18823                        (location.buffer, location.range.to_point(buffer))
18824                    })
18825                    // if special-casing the single-match case, remove ranges
18826                    // that intersect current selection
18827                    .filter(|(location_buffer, location)| {
18828                        if always_open_multibuffer || &buffer != location_buffer {
18829                            return true;
18830                        }
18831
18832                        !location.contains_inclusive(&selection_point.range())
18833                    })
18834                    .into_group_map()
18835            })?;
18836            if locations.is_empty() {
18837                return anyhow::Ok(Navigated::No);
18838            }
18839            for ranges in locations.values_mut() {
18840                ranges.sort_by_key(|range| (range.start, Reverse(range.end)));
18841                ranges.dedup();
18842            }
18843            let mut num_locations = 0;
18844            for ranges in locations.values_mut() {
18845                ranges.sort_by_key(|range| (range.start, Reverse(range.end)));
18846                ranges.dedup();
18847                num_locations += ranges.len();
18848            }
18849
18850            if num_locations == 1 && !always_open_multibuffer {
18851                let (target_buffer, target_ranges) = locations.into_iter().next().unwrap();
18852                let target_range = target_ranges.first().unwrap().clone();
18853
18854                return editor.update_in(cx, |editor, window, cx| {
18855                    let range = target_range.to_point(target_buffer.read(cx));
18856                    let range = editor.range_for_match(&range);
18857                    let range = range.start..range.start;
18858
18859                    if Some(&target_buffer) == editor.buffer.read(cx).as_singleton().as_ref() {
18860                        editor.go_to_singleton_buffer_range(range, window, cx);
18861                    } else {
18862                        let pane = workspace.read(cx).active_pane().clone();
18863                        window.defer(cx, move |window, cx| {
18864                            let target_editor: Entity<Self> =
18865                                workspace.update(cx, |workspace, cx| {
18866                                    let pane = workspace.active_pane().clone();
18867
18868                                    let preview_tabs_settings = PreviewTabsSettings::get_global(cx);
18869                                    let keep_old_preview = preview_tabs_settings
18870                                        .enable_keep_preview_on_code_navigation;
18871                                    let allow_new_preview = preview_tabs_settings
18872                                        .enable_preview_file_from_code_navigation;
18873
18874                                    workspace.open_project_item(
18875                                        pane,
18876                                        target_buffer.clone(),
18877                                        true,
18878                                        true,
18879                                        keep_old_preview,
18880                                        allow_new_preview,
18881                                        window,
18882                                        cx,
18883                                    )
18884                                });
18885                            target_editor.update(cx, |target_editor, cx| {
18886                                // When selecting a definition in a different buffer, disable the nav history
18887                                // to avoid creating a history entry at the previous cursor location.
18888                                pane.update(cx, |pane, _| pane.disable_history());
18889                                target_editor.go_to_singleton_buffer_range(range, window, cx);
18890                                pane.update(cx, |pane, _| pane.enable_history());
18891                            });
18892                        });
18893                    }
18894                    Navigated::No
18895                });
18896            }
18897
18898            workspace.update_in(cx, |workspace, window, cx| {
18899                let target = locations
18900                    .iter()
18901                    .flat_map(|(k, v)| iter::repeat(k.clone()).zip(v))
18902                    .map(|(buffer, location)| {
18903                        buffer
18904                            .read(cx)
18905                            .text_for_range(location.clone())
18906                            .collect::<String>()
18907                    })
18908                    .filter(|text| !text.contains('\n'))
18909                    .unique()
18910                    .take(3)
18911                    .join(", ");
18912                let title = if target.is_empty() {
18913                    "References".to_owned()
18914                } else {
18915                    format!("References to {target}")
18916                };
18917                let allow_preview = PreviewTabsSettings::get_global(cx)
18918                    .enable_preview_multibuffer_from_code_navigation;
18919                Self::open_locations_in_multibuffer(
18920                    workspace,
18921                    locations,
18922                    title,
18923                    false,
18924                    allow_preview,
18925                    MultibufferSelectionMode::First,
18926                    window,
18927                    cx,
18928                );
18929                Navigated::Yes
18930            })
18931        }))
18932    }
18933
18934    /// Opens a multibuffer with the given project locations in it.
18935    pub fn open_locations_in_multibuffer(
18936        workspace: &mut Workspace,
18937        locations: std::collections::HashMap<Entity<Buffer>, Vec<Range<Point>>>,
18938        title: String,
18939        split: bool,
18940        allow_preview: bool,
18941        multibuffer_selection_mode: MultibufferSelectionMode,
18942        window: &mut Window,
18943        cx: &mut Context<Workspace>,
18944    ) -> Option<(Entity<Editor>, Entity<Pane>)> {
18945        if locations.is_empty() {
18946            log::error!("bug: open_locations_in_multibuffer called with empty list of locations");
18947            return None;
18948        }
18949
18950        let capability = workspace.project().read(cx).capability();
18951        let mut ranges = <Vec<Range<Anchor>>>::new();
18952
18953        // a key to find existing multibuffer editors with the same set of locations
18954        // to prevent us from opening more and more multibuffer tabs for searches and the like
18955        let mut key = (title.clone(), vec![]);
18956        let excerpt_buffer = cx.new(|cx| {
18957            let key = &mut key.1;
18958            let mut multibuffer = MultiBuffer::new(capability);
18959            for (buffer, mut ranges_for_buffer) in locations {
18960                ranges_for_buffer.sort_by_key(|range| (range.start, Reverse(range.end)));
18961                key.push((buffer.read(cx).remote_id(), ranges_for_buffer.clone()));
18962                let (new_ranges, _) = multibuffer.set_excerpts_for_path(
18963                    PathKey::for_buffer(&buffer, cx),
18964                    buffer.clone(),
18965                    ranges_for_buffer,
18966                    multibuffer_context_lines(cx),
18967                    cx,
18968                );
18969                ranges.extend(new_ranges)
18970            }
18971
18972            multibuffer.with_title(title)
18973        });
18974        let existing = workspace.active_pane().update(cx, |pane, cx| {
18975            pane.items()
18976                .filter_map(|item| item.downcast::<Editor>())
18977                .find(|editor| {
18978                    editor
18979                        .read(cx)
18980                        .lookup_key
18981                        .as_ref()
18982                        .and_then(|it| {
18983                            it.downcast_ref::<(String, Vec<(BufferId, Vec<Range<Point>>)>)>()
18984                        })
18985                        .is_some_and(|it| *it == key)
18986                })
18987        });
18988        let was_existing = existing.is_some();
18989        let editor = existing.unwrap_or_else(|| {
18990            cx.new(|cx| {
18991                let mut editor = Editor::for_multibuffer(
18992                    excerpt_buffer,
18993                    Some(workspace.project().clone()),
18994                    window,
18995                    cx,
18996                );
18997                editor.lookup_key = Some(Box::new(key));
18998                editor
18999            })
19000        });
19001        editor.update(cx, |editor, cx| match multibuffer_selection_mode {
19002            MultibufferSelectionMode::First => {
19003                if let Some(first_range) = ranges.first() {
19004                    editor.change_selections(
19005                        SelectionEffects::no_scroll(),
19006                        window,
19007                        cx,
19008                        |selections| {
19009                            selections.clear_disjoint();
19010                            selections.select_anchor_ranges(std::iter::once(first_range.clone()));
19011                        },
19012                    );
19013                }
19014                editor.highlight_background(
19015                    HighlightKey::Editor,
19016                    &ranges,
19017                    |_, theme| theme.colors().editor_highlighted_line_background,
19018                    cx,
19019                );
19020            }
19021            MultibufferSelectionMode::All => {
19022                editor.change_selections(SelectionEffects::no_scroll(), window, cx, |selections| {
19023                    selections.clear_disjoint();
19024                    selections.select_anchor_ranges(ranges);
19025                });
19026            }
19027        });
19028
19029        let item = Box::new(editor.clone());
19030
19031        let pane = if split {
19032            workspace.adjacent_pane(window, cx)
19033        } else {
19034            workspace.active_pane().clone()
19035        };
19036        let activate_pane = split;
19037
19038        let mut destination_index = None;
19039        pane.update(cx, |pane, cx| {
19040            if allow_preview && !was_existing {
19041                destination_index = pane.replace_preview_item_id(item.item_id(), window, cx);
19042            }
19043            if was_existing && !allow_preview {
19044                pane.unpreview_item_if_preview(item.item_id());
19045            }
19046            pane.add_item(item, activate_pane, true, destination_index, window, cx);
19047        });
19048
19049        Some((editor, pane))
19050    }
19051
19052    pub fn rename(
19053        &mut self,
19054        _: &Rename,
19055        window: &mut Window,
19056        cx: &mut Context<Self>,
19057    ) -> Option<Task<Result<()>>> {
19058        use language::ToOffset as _;
19059
19060        let provider = self.semantics_provider.clone()?;
19061        let selection = self.selections.newest_anchor().clone();
19062        let (cursor_buffer, cursor_buffer_position) = self
19063            .buffer
19064            .read(cx)
19065            .text_anchor_for_position(selection.head(), cx)?;
19066        let (tail_buffer, cursor_buffer_position_end) = self
19067            .buffer
19068            .read(cx)
19069            .text_anchor_for_position(selection.tail(), cx)?;
19070        if tail_buffer != cursor_buffer {
19071            return None;
19072        }
19073
19074        let snapshot = cursor_buffer.read(cx).snapshot();
19075        let cursor_buffer_offset = cursor_buffer_position.to_offset(&snapshot);
19076        let cursor_buffer_offset_end = cursor_buffer_position_end.to_offset(&snapshot);
19077        let prepare_rename = provider
19078            .range_for_rename(&cursor_buffer, cursor_buffer_position, cx)
19079            .unwrap_or_else(|| Task::ready(Ok(None)));
19080        drop(snapshot);
19081
19082        Some(cx.spawn_in(window, async move |this, cx| {
19083            let rename_range = if let Some(range) = prepare_rename.await? {
19084                Some(range)
19085            } else {
19086                this.update(cx, |this, cx| {
19087                    let buffer = this.buffer.read(cx).snapshot(cx);
19088                    let mut buffer_highlights = this
19089                        .document_highlights_for_position(selection.head(), &buffer)
19090                        .filter(|highlight| {
19091                            highlight.start.excerpt_id == selection.head().excerpt_id
19092                                && highlight.end.excerpt_id == selection.head().excerpt_id
19093                        });
19094                    buffer_highlights
19095                        .next()
19096                        .map(|highlight| highlight.start.text_anchor..highlight.end.text_anchor)
19097                })?
19098            };
19099            if let Some(rename_range) = rename_range {
19100                this.update_in(cx, |this, window, cx| {
19101                    let snapshot = cursor_buffer.read(cx).snapshot();
19102                    let rename_buffer_range = rename_range.to_offset(&snapshot);
19103                    let cursor_offset_in_rename_range =
19104                        cursor_buffer_offset.saturating_sub(rename_buffer_range.start);
19105                    let cursor_offset_in_rename_range_end =
19106                        cursor_buffer_offset_end.saturating_sub(rename_buffer_range.start);
19107
19108                    this.take_rename(false, window, cx);
19109                    let buffer = this.buffer.read(cx).read(cx);
19110                    let cursor_offset = selection.head().to_offset(&buffer);
19111                    let rename_start =
19112                        cursor_offset.saturating_sub_usize(cursor_offset_in_rename_range);
19113                    let rename_end = rename_start + rename_buffer_range.len();
19114                    let range = buffer.anchor_before(rename_start)..buffer.anchor_after(rename_end);
19115                    let mut old_highlight_id = None;
19116                    let old_name: Arc<str> = buffer
19117                        .chunks(rename_start..rename_end, true)
19118                        .map(|chunk| {
19119                            if old_highlight_id.is_none() {
19120                                old_highlight_id = chunk.syntax_highlight_id;
19121                            }
19122                            chunk.text
19123                        })
19124                        .collect::<String>()
19125                        .into();
19126
19127                    drop(buffer);
19128
19129                    // Position the selection in the rename editor so that it matches the current selection.
19130                    this.show_local_selections = false;
19131                    let rename_editor = cx.new(|cx| {
19132                        let mut editor = Editor::single_line(window, cx);
19133                        editor.buffer.update(cx, |buffer, cx| {
19134                            buffer.edit(
19135                                [(MultiBufferOffset(0)..MultiBufferOffset(0), old_name.clone())],
19136                                None,
19137                                cx,
19138                            )
19139                        });
19140                        let cursor_offset_in_rename_range =
19141                            MultiBufferOffset(cursor_offset_in_rename_range);
19142                        let cursor_offset_in_rename_range_end =
19143                            MultiBufferOffset(cursor_offset_in_rename_range_end);
19144                        let rename_selection_range = match cursor_offset_in_rename_range
19145                            .cmp(&cursor_offset_in_rename_range_end)
19146                        {
19147                            Ordering::Equal => {
19148                                editor.select_all(&SelectAll, window, cx);
19149                                return editor;
19150                            }
19151                            Ordering::Less => {
19152                                cursor_offset_in_rename_range..cursor_offset_in_rename_range_end
19153                            }
19154                            Ordering::Greater => {
19155                                cursor_offset_in_rename_range_end..cursor_offset_in_rename_range
19156                            }
19157                        };
19158                        if rename_selection_range.end.0 > old_name.len() {
19159                            editor.select_all(&SelectAll, window, cx);
19160                        } else {
19161                            editor.change_selections(Default::default(), window, cx, |s| {
19162                                s.select_ranges([rename_selection_range]);
19163                            });
19164                        }
19165                        editor
19166                    });
19167                    cx.subscribe(&rename_editor, |_, _, e: &EditorEvent, cx| {
19168                        if e == &EditorEvent::Focused {
19169                            cx.emit(EditorEvent::FocusedIn)
19170                        }
19171                    })
19172                    .detach();
19173
19174                    let write_highlights =
19175                        this.clear_background_highlights(HighlightKey::DocumentHighlightWrite, cx);
19176                    let read_highlights =
19177                        this.clear_background_highlights(HighlightKey::DocumentHighlightRead, cx);
19178                    let ranges = write_highlights
19179                        .iter()
19180                        .flat_map(|(_, ranges)| ranges.iter())
19181                        .chain(read_highlights.iter().flat_map(|(_, ranges)| ranges.iter()))
19182                        .cloned()
19183                        .collect();
19184
19185                    this.highlight_text(
19186                        HighlightKey::Rename,
19187                        ranges,
19188                        HighlightStyle {
19189                            fade_out: Some(0.6),
19190                            ..Default::default()
19191                        },
19192                        cx,
19193                    );
19194                    let rename_focus_handle = rename_editor.focus_handle(cx);
19195                    window.focus(&rename_focus_handle, cx);
19196                    let block_id = this.insert_blocks(
19197                        [BlockProperties {
19198                            style: BlockStyle::Flex,
19199                            placement: BlockPlacement::Below(range.start),
19200                            height: Some(1),
19201                            render: Arc::new({
19202                                let rename_editor = rename_editor.clone();
19203                                move |cx: &mut BlockContext| {
19204                                    let mut text_style = cx.editor_style.text.clone();
19205                                    if let Some(highlight_style) = old_highlight_id
19206                                        .and_then(|h| cx.editor_style.syntax.get(h).cloned())
19207                                    {
19208                                        text_style = text_style.highlight(highlight_style);
19209                                    }
19210                                    div()
19211                                        .block_mouse_except_scroll()
19212                                        .pl(cx.anchor_x)
19213                                        .child(EditorElement::new(
19214                                            &rename_editor,
19215                                            EditorStyle {
19216                                                background: cx.theme().system().transparent,
19217                                                local_player: cx.editor_style.local_player,
19218                                                text: text_style,
19219                                                scrollbar_width: cx.editor_style.scrollbar_width,
19220                                                syntax: cx.editor_style.syntax.clone(),
19221                                                status: cx.editor_style.status.clone(),
19222                                                inlay_hints_style: HighlightStyle {
19223                                                    font_weight: Some(FontWeight::BOLD),
19224                                                    ..make_inlay_hints_style(cx.app)
19225                                                },
19226                                                edit_prediction_styles: make_suggestion_styles(
19227                                                    cx.app,
19228                                                ),
19229                                                ..EditorStyle::default()
19230                                            },
19231                                        ))
19232                                        .into_any_element()
19233                                }
19234                            }),
19235                            priority: 0,
19236                        }],
19237                        Some(Autoscroll::fit()),
19238                        cx,
19239                    )[0];
19240                    this.pending_rename = Some(RenameState {
19241                        range,
19242                        old_name,
19243                        editor: rename_editor,
19244                        block_id,
19245                    });
19246                })?;
19247            }
19248
19249            Ok(())
19250        }))
19251    }
19252
19253    pub fn confirm_rename(
19254        &mut self,
19255        _: &ConfirmRename,
19256        window: &mut Window,
19257        cx: &mut Context<Self>,
19258    ) -> Option<Task<Result<()>>> {
19259        let rename = self.take_rename(false, window, cx)?;
19260        let workspace = self.workspace()?.downgrade();
19261        let (buffer, start) = self
19262            .buffer
19263            .read(cx)
19264            .text_anchor_for_position(rename.range.start, cx)?;
19265        let (end_buffer, _) = self
19266            .buffer
19267            .read(cx)
19268            .text_anchor_for_position(rename.range.end, cx)?;
19269        if buffer != end_buffer {
19270            return None;
19271        }
19272
19273        let old_name = rename.old_name;
19274        let new_name = rename.editor.read(cx).text(cx);
19275
19276        let rename = self.semantics_provider.as_ref()?.perform_rename(
19277            &buffer,
19278            start,
19279            new_name.clone(),
19280            cx,
19281        )?;
19282
19283        Some(cx.spawn_in(window, async move |editor, cx| {
19284            let project_transaction = rename.await?;
19285            Self::open_project_transaction(
19286                &editor,
19287                workspace,
19288                project_transaction,
19289                format!("Rename: {}{}", old_name, new_name),
19290                cx,
19291            )
19292            .await?;
19293
19294            editor.update(cx, |editor, cx| {
19295                editor.refresh_document_highlights(cx);
19296            })?;
19297            Ok(())
19298        }))
19299    }
19300
19301    fn take_rename(
19302        &mut self,
19303        moving_cursor: bool,
19304        window: &mut Window,
19305        cx: &mut Context<Self>,
19306    ) -> Option<RenameState> {
19307        let rename = self.pending_rename.take()?;
19308        if rename.editor.focus_handle(cx).is_focused(window) {
19309            window.focus(&self.focus_handle, cx);
19310        }
19311
19312        self.remove_blocks(
19313            [rename.block_id].into_iter().collect(),
19314            Some(Autoscroll::fit()),
19315            cx,
19316        );
19317        self.clear_highlights(HighlightKey::Rename, cx);
19318        self.show_local_selections = true;
19319
19320        if moving_cursor {
19321            let cursor_in_rename_editor = rename.editor.update(cx, |editor, cx| {
19322                editor
19323                    .selections
19324                    .newest::<MultiBufferOffset>(&editor.display_snapshot(cx))
19325                    .head()
19326            });
19327
19328            // Update the selection to match the position of the selection inside
19329            // the rename editor.
19330            let snapshot = self.buffer.read(cx).read(cx);
19331            let rename_range = rename.range.to_offset(&snapshot);
19332            let cursor_in_editor = snapshot
19333                .clip_offset(rename_range.start + cursor_in_rename_editor, Bias::Left)
19334                .min(rename_range.end);
19335            drop(snapshot);
19336
19337            self.change_selections(SelectionEffects::no_scroll(), window, cx, |s| {
19338                s.select_ranges(vec![cursor_in_editor..cursor_in_editor])
19339            });
19340        } else {
19341            self.refresh_document_highlights(cx);
19342        }
19343
19344        Some(rename)
19345    }
19346
19347    pub fn pending_rename(&self) -> Option<&RenameState> {
19348        self.pending_rename.as_ref()
19349    }
19350
19351    fn format(
19352        &mut self,
19353        _: &Format,
19354        window: &mut Window,
19355        cx: &mut Context<Self>,
19356    ) -> Option<Task<Result<()>>> {
19357        self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
19358
19359        let project = match &self.project {
19360            Some(project) => project.clone(),
19361            None => return None,
19362        };
19363
19364        Some(self.perform_format(
19365            project,
19366            FormatTrigger::Manual,
19367            FormatTarget::Buffers(self.buffer.read(cx).all_buffers()),
19368            window,
19369            cx,
19370        ))
19371    }
19372
19373    fn format_selections(
19374        &mut self,
19375        _: &FormatSelections,
19376        window: &mut Window,
19377        cx: &mut Context<Self>,
19378    ) -> Option<Task<Result<()>>> {
19379        self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
19380
19381        let project = match &self.project {
19382            Some(project) => project.clone(),
19383            None => return None,
19384        };
19385
19386        let ranges = self
19387            .selections
19388            .all_adjusted(&self.display_snapshot(cx))
19389            .into_iter()
19390            .map(|selection| selection.range())
19391            .collect_vec();
19392
19393        Some(self.perform_format(
19394            project,
19395            FormatTrigger::Manual,
19396            FormatTarget::Ranges(ranges),
19397            window,
19398            cx,
19399        ))
19400    }
19401
19402    fn perform_format(
19403        &mut self,
19404        project: Entity<Project>,
19405        trigger: FormatTrigger,
19406        target: FormatTarget,
19407        window: &mut Window,
19408        cx: &mut Context<Self>,
19409    ) -> Task<Result<()>> {
19410        let buffer = self.buffer.clone();
19411        let (buffers, target) = match target {
19412            FormatTarget::Buffers(buffers) => (buffers, LspFormatTarget::Buffers),
19413            FormatTarget::Ranges(selection_ranges) => {
19414                let multi_buffer = buffer.read(cx);
19415                let snapshot = multi_buffer.read(cx);
19416                let mut buffers = HashSet::default();
19417                let mut buffer_id_to_ranges: BTreeMap<BufferId, Vec<Range<text::Anchor>>> =
19418                    BTreeMap::new();
19419                for selection_range in selection_ranges {
19420                    for (buffer, buffer_range, _) in
19421                        snapshot.range_to_buffer_ranges(selection_range.start..=selection_range.end)
19422                    {
19423                        let buffer_id = buffer.remote_id();
19424                        let start = buffer.anchor_before(buffer_range.start);
19425                        let end = buffer.anchor_after(buffer_range.end);
19426                        buffers.insert(multi_buffer.buffer(buffer_id).unwrap());
19427                        buffer_id_to_ranges
19428                            .entry(buffer_id)
19429                            .and_modify(|buffer_ranges| buffer_ranges.push(start..end))
19430                            .or_insert_with(|| vec![start..end]);
19431                    }
19432                }
19433                (buffers, LspFormatTarget::Ranges(buffer_id_to_ranges))
19434            }
19435        };
19436
19437        let transaction_id_prev = buffer.read(cx).last_transaction_id(cx);
19438        let selections_prev = transaction_id_prev
19439            .and_then(|transaction_id_prev| {
19440                // default to selections as they were after the last edit, if we have them,
19441                // instead of how they are now.
19442                // This will make it so that editing, moving somewhere else, formatting, then undoing the format
19443                // will take you back to where you made the last edit, instead of staying where you scrolled
19444                self.selection_history
19445                    .transaction(transaction_id_prev)
19446                    .map(|t| t.0.clone())
19447            })
19448            .unwrap_or_else(|| self.selections.disjoint_anchors_arc());
19449
19450        let mut timeout = cx.background_executor().timer(FORMAT_TIMEOUT).fuse();
19451        let format = project.update(cx, |project, cx| {
19452            project.format(buffers, target, true, trigger, cx)
19453        });
19454
19455        cx.spawn_in(window, async move |editor, cx| {
19456            let transaction = futures::select_biased! {
19457                transaction = format.log_err().fuse() => transaction,
19458                () = timeout => {
19459                    log::warn!("timed out waiting for formatting");
19460                    None
19461                }
19462            };
19463
19464            buffer.update(cx, |buffer, cx| {
19465                if let Some(transaction) = transaction
19466                    && !buffer.is_singleton()
19467                {
19468                    buffer.push_transaction(&transaction.0, cx);
19469                }
19470                cx.notify();
19471            });
19472
19473            if let Some(transaction_id_now) =
19474                buffer.read_with(cx, |b, cx| b.last_transaction_id(cx))
19475            {
19476                let has_new_transaction = transaction_id_prev != Some(transaction_id_now);
19477                if has_new_transaction {
19478                    editor
19479                        .update(cx, |editor, _| {
19480                            editor
19481                                .selection_history
19482                                .insert_transaction(transaction_id_now, selections_prev);
19483                        })
19484                        .ok();
19485                }
19486            }
19487
19488            Ok(())
19489        })
19490    }
19491
19492    fn organize_imports(
19493        &mut self,
19494        _: &OrganizeImports,
19495        window: &mut Window,
19496        cx: &mut Context<Self>,
19497    ) -> Option<Task<Result<()>>> {
19498        self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
19499        let project = match &self.project {
19500            Some(project) => project.clone(),
19501            None => return None,
19502        };
19503        Some(self.perform_code_action_kind(
19504            project,
19505            CodeActionKind::SOURCE_ORGANIZE_IMPORTS,
19506            window,
19507            cx,
19508        ))
19509    }
19510
19511    fn perform_code_action_kind(
19512        &mut self,
19513        project: Entity<Project>,
19514        kind: CodeActionKind,
19515        window: &mut Window,
19516        cx: &mut Context<Self>,
19517    ) -> Task<Result<()>> {
19518        let buffer = self.buffer.clone();
19519        let buffers = buffer.read(cx).all_buffers();
19520        let mut timeout = cx.background_executor().timer(CODE_ACTION_TIMEOUT).fuse();
19521        let apply_action = project.update(cx, |project, cx| {
19522            project.apply_code_action_kind(buffers, kind, true, cx)
19523        });
19524        cx.spawn_in(window, async move |_, cx| {
19525            let transaction = futures::select_biased! {
19526                () = timeout => {
19527                    log::warn!("timed out waiting for executing code action");
19528                    None
19529                }
19530                transaction = apply_action.log_err().fuse() => transaction,
19531            };
19532            buffer.update(cx, |buffer, cx| {
19533                // check if we need this
19534                if let Some(transaction) = transaction
19535                    && !buffer.is_singleton()
19536                {
19537                    buffer.push_transaction(&transaction.0, cx);
19538                }
19539                cx.notify();
19540            });
19541            Ok(())
19542        })
19543    }
19544
19545    pub fn restart_language_server(
19546        &mut self,
19547        _: &RestartLanguageServer,
19548        _: &mut Window,
19549        cx: &mut Context<Self>,
19550    ) {
19551        if let Some(project) = self.project.clone() {
19552            self.buffer.update(cx, |multi_buffer, cx| {
19553                project.update(cx, |project, cx| {
19554                    project.restart_language_servers_for_buffers(
19555                        multi_buffer.all_buffers().into_iter().collect(),
19556                        HashSet::default(),
19557                        cx,
19558                    );
19559                });
19560            })
19561        }
19562    }
19563
19564    pub fn stop_language_server(
19565        &mut self,
19566        _: &StopLanguageServer,
19567        _: &mut Window,
19568        cx: &mut Context<Self>,
19569    ) {
19570        if let Some(project) = self.project.clone() {
19571            self.buffer.update(cx, |multi_buffer, cx| {
19572                project.update(cx, |project, cx| {
19573                    project.stop_language_servers_for_buffers(
19574                        multi_buffer.all_buffers().into_iter().collect(),
19575                        HashSet::default(),
19576                        cx,
19577                    );
19578                });
19579            });
19580        }
19581    }
19582
19583    fn cancel_language_server_work(
19584        workspace: &mut Workspace,
19585        _: &actions::CancelLanguageServerWork,
19586        _: &mut Window,
19587        cx: &mut Context<Workspace>,
19588    ) {
19589        let project = workspace.project();
19590        let buffers = workspace
19591            .active_item(cx)
19592            .and_then(|item| item.act_as::<Editor>(cx))
19593            .map_or(HashSet::default(), |editor| {
19594                editor.read(cx).buffer.read(cx).all_buffers()
19595            });
19596        project.update(cx, |project, cx| {
19597            project.cancel_language_server_work_for_buffers(buffers, cx);
19598        });
19599    }
19600
19601    fn show_character_palette(
19602        &mut self,
19603        _: &ShowCharacterPalette,
19604        window: &mut Window,
19605        _: &mut Context<Self>,
19606    ) {
19607        window.show_character_palette();
19608    }
19609
19610    fn refresh_active_diagnostics(&mut self, cx: &mut Context<Editor>) {
19611        if !self.diagnostics_enabled() {
19612            return;
19613        }
19614
19615        if let ActiveDiagnostic::Group(active_diagnostics) = &mut self.active_diagnostics {
19616            let buffer = self.buffer.read(cx).snapshot(cx);
19617            let primary_range_start = active_diagnostics.active_range.start.to_offset(&buffer);
19618            let primary_range_end = active_diagnostics.active_range.end.to_offset(&buffer);
19619            let is_valid = buffer
19620                .diagnostics_in_range::<MultiBufferOffset>(primary_range_start..primary_range_end)
19621                .any(|entry| {
19622                    entry.diagnostic.is_primary
19623                        && !entry.range.is_empty()
19624                        && entry.range.start == primary_range_start
19625                        && entry.diagnostic.message == active_diagnostics.active_message
19626                });
19627
19628            if !is_valid {
19629                self.dismiss_diagnostics(cx);
19630            }
19631        }
19632    }
19633
19634    pub fn active_diagnostic_group(&self) -> Option<&ActiveDiagnosticGroup> {
19635        match &self.active_diagnostics {
19636            ActiveDiagnostic::Group(group) => Some(group),
19637            _ => None,
19638        }
19639    }
19640
19641    pub fn set_all_diagnostics_active(&mut self, cx: &mut Context<Self>) {
19642        if !self.diagnostics_enabled() {
19643            return;
19644        }
19645        self.dismiss_diagnostics(cx);
19646        self.active_diagnostics = ActiveDiagnostic::All;
19647    }
19648
19649    fn activate_diagnostics(
19650        &mut self,
19651        buffer_id: BufferId,
19652        diagnostic: DiagnosticEntryRef<'_, MultiBufferOffset>,
19653        window: &mut Window,
19654        cx: &mut Context<Self>,
19655    ) {
19656        if !self.diagnostics_enabled() || matches!(self.active_diagnostics, ActiveDiagnostic::All) {
19657            return;
19658        }
19659        self.dismiss_diagnostics(cx);
19660        let snapshot = self.snapshot(window, cx);
19661        let buffer = self.buffer.read(cx).snapshot(cx);
19662        let Some(renderer) = GlobalDiagnosticRenderer::global(cx) else {
19663            return;
19664        };
19665
19666        let diagnostic_group = buffer
19667            .diagnostic_group(buffer_id, diagnostic.diagnostic.group_id)
19668            .collect::<Vec<_>>();
19669
19670        let language_registry = self
19671            .project()
19672            .map(|project| project.read(cx).languages().clone());
19673
19674        let blocks = renderer.render_group(
19675            diagnostic_group,
19676            buffer_id,
19677            snapshot,
19678            cx.weak_entity(),
19679            language_registry,
19680            cx,
19681        );
19682
19683        let blocks = self.display_map.update(cx, |display_map, cx| {
19684            display_map.insert_blocks(blocks, cx).into_iter().collect()
19685        });
19686        self.active_diagnostics = ActiveDiagnostic::Group(ActiveDiagnosticGroup {
19687            active_range: buffer.anchor_before(diagnostic.range.start)
19688                ..buffer.anchor_after(diagnostic.range.end),
19689            active_message: diagnostic.diagnostic.message.clone(),
19690            group_id: diagnostic.diagnostic.group_id,
19691            blocks,
19692        });
19693        cx.notify();
19694    }
19695
19696    fn dismiss_diagnostics(&mut self, cx: &mut Context<Self>) {
19697        if matches!(self.active_diagnostics, ActiveDiagnostic::All) {
19698            return;
19699        };
19700
19701        let prev = mem::replace(&mut self.active_diagnostics, ActiveDiagnostic::None);
19702        if let ActiveDiagnostic::Group(group) = prev {
19703            self.display_map.update(cx, |display_map, cx| {
19704                display_map.remove_blocks(group.blocks, cx);
19705            });
19706            cx.notify();
19707        }
19708    }
19709
19710    /// Disable inline diagnostics rendering for this editor.
19711    pub fn disable_inline_diagnostics(&mut self) {
19712        self.inline_diagnostics_enabled = false;
19713        self.inline_diagnostics_update = Task::ready(());
19714        self.inline_diagnostics.clear();
19715    }
19716
19717    pub fn disable_diagnostics(&mut self, cx: &mut Context<Self>) {
19718        self.diagnostics_enabled = false;
19719        self.dismiss_diagnostics(cx);
19720        self.inline_diagnostics_update = Task::ready(());
19721        self.inline_diagnostics.clear();
19722    }
19723
19724    pub fn disable_word_completions(&mut self) {
19725        self.word_completions_enabled = false;
19726    }
19727
19728    pub fn diagnostics_enabled(&self) -> bool {
19729        self.diagnostics_enabled && self.lsp_data_enabled()
19730    }
19731
19732    pub fn inline_diagnostics_enabled(&self) -> bool {
19733        self.inline_diagnostics_enabled && self.diagnostics_enabled()
19734    }
19735
19736    pub fn show_inline_diagnostics(&self) -> bool {
19737        self.show_inline_diagnostics
19738    }
19739
19740    pub fn toggle_inline_diagnostics(
19741        &mut self,
19742        _: &ToggleInlineDiagnostics,
19743        window: &mut Window,
19744        cx: &mut Context<Editor>,
19745    ) {
19746        self.show_inline_diagnostics = !self.show_inline_diagnostics;
19747        self.refresh_inline_diagnostics(false, window, cx);
19748    }
19749
19750    pub fn set_max_diagnostics_severity(&mut self, severity: DiagnosticSeverity, cx: &mut App) {
19751        self.diagnostics_max_severity = severity;
19752        self.display_map.update(cx, |display_map, _| {
19753            display_map.diagnostics_max_severity = self.diagnostics_max_severity;
19754        });
19755    }
19756
19757    pub fn toggle_diagnostics(
19758        &mut self,
19759        _: &ToggleDiagnostics,
19760        window: &mut Window,
19761        cx: &mut Context<Editor>,
19762    ) {
19763        if !self.diagnostics_enabled() {
19764            return;
19765        }
19766
19767        let new_severity = if self.diagnostics_max_severity == DiagnosticSeverity::Off {
19768            EditorSettings::get_global(cx)
19769                .diagnostics_max_severity
19770                .filter(|severity| severity != &DiagnosticSeverity::Off)
19771                .unwrap_or(DiagnosticSeverity::Hint)
19772        } else {
19773            DiagnosticSeverity::Off
19774        };
19775        self.set_max_diagnostics_severity(new_severity, cx);
19776        if self.diagnostics_max_severity == DiagnosticSeverity::Off {
19777            self.active_diagnostics = ActiveDiagnostic::None;
19778            self.inline_diagnostics_update = Task::ready(());
19779            self.inline_diagnostics.clear();
19780        } else {
19781            self.refresh_inline_diagnostics(false, window, cx);
19782        }
19783
19784        cx.notify();
19785    }
19786
19787    pub fn toggle_minimap(
19788        &mut self,
19789        _: &ToggleMinimap,
19790        window: &mut Window,
19791        cx: &mut Context<Editor>,
19792    ) {
19793        if self.supports_minimap(cx) {
19794            self.set_minimap_visibility(self.minimap_visibility.toggle_visibility(), window, cx);
19795        }
19796    }
19797
19798    fn refresh_inline_diagnostics(
19799        &mut self,
19800        debounce: bool,
19801        window: &mut Window,
19802        cx: &mut Context<Self>,
19803    ) {
19804        let max_severity = ProjectSettings::get_global(cx)
19805            .diagnostics
19806            .inline
19807            .max_severity
19808            .unwrap_or(self.diagnostics_max_severity);
19809
19810        if !self.inline_diagnostics_enabled()
19811            || !self.diagnostics_enabled()
19812            || !self.show_inline_diagnostics
19813            || max_severity == DiagnosticSeverity::Off
19814        {
19815            self.inline_diagnostics_update = Task::ready(());
19816            self.inline_diagnostics.clear();
19817            return;
19818        }
19819
19820        let debounce_ms = ProjectSettings::get_global(cx)
19821            .diagnostics
19822            .inline
19823            .update_debounce_ms;
19824        let debounce = if debounce && debounce_ms > 0 {
19825            Some(Duration::from_millis(debounce_ms))
19826        } else {
19827            None
19828        };
19829        self.inline_diagnostics_update = cx.spawn_in(window, async move |editor, cx| {
19830            if let Some(debounce) = debounce {
19831                cx.background_executor().timer(debounce).await;
19832            }
19833            let Some(snapshot) = editor.upgrade().map(|editor| {
19834                editor.update(cx, |editor, cx| editor.buffer().read(cx).snapshot(cx))
19835            }) else {
19836                return;
19837            };
19838
19839            let new_inline_diagnostics = cx
19840                .background_spawn(async move {
19841                    let mut inline_diagnostics = Vec::<(Anchor, InlineDiagnostic)>::new();
19842                    for diagnostic_entry in
19843                        snapshot.diagnostics_in_range(MultiBufferOffset(0)..snapshot.len())
19844                    {
19845                        let message = diagnostic_entry
19846                            .diagnostic
19847                            .message
19848                            .split_once('\n')
19849                            .map(|(line, _)| line)
19850                            .map(SharedString::new)
19851                            .unwrap_or_else(|| {
19852                                SharedString::new(&*diagnostic_entry.diagnostic.message)
19853                            });
19854                        let start_anchor = snapshot.anchor_before(diagnostic_entry.range.start);
19855                        let (Ok(i) | Err(i)) = inline_diagnostics
19856                            .binary_search_by(|(probe, _)| probe.cmp(&start_anchor, &snapshot));
19857                        inline_diagnostics.insert(
19858                            i,
19859                            (
19860                                start_anchor,
19861                                InlineDiagnostic {
19862                                    message,
19863                                    group_id: diagnostic_entry.diagnostic.group_id,
19864                                    start: diagnostic_entry.range.start.to_point(&snapshot),
19865                                    is_primary: diagnostic_entry.diagnostic.is_primary,
19866                                    severity: diagnostic_entry.diagnostic.severity,
19867                                },
19868                            ),
19869                        );
19870                    }
19871                    inline_diagnostics
19872                })
19873                .await;
19874
19875            editor
19876                .update(cx, |editor, cx| {
19877                    editor.inline_diagnostics = new_inline_diagnostics;
19878                    cx.notify();
19879                })
19880                .ok();
19881        });
19882    }
19883
19884    fn pull_diagnostics(
19885        &mut self,
19886        buffer_id: BufferId,
19887        _window: &Window,
19888        cx: &mut Context<Self>,
19889    ) -> Option<()> {
19890        // `ActiveDiagnostic::All` is a special mode where editor's diagnostics are managed by the external view,
19891        // skip any LSP updates for it.
19892
19893        if self.active_diagnostics == ActiveDiagnostic::All || !self.diagnostics_enabled() {
19894            return None;
19895        }
19896        let pull_diagnostics_settings = ProjectSettings::get_global(cx)
19897            .diagnostics
19898            .lsp_pull_diagnostics;
19899        if !pull_diagnostics_settings.enabled {
19900            return None;
19901        }
19902        let debounce = Duration::from_millis(pull_diagnostics_settings.debounce_ms);
19903        let project = self.project()?.downgrade();
19904        let buffer = self.buffer().read(cx).buffer(buffer_id)?;
19905
19906        self.pull_diagnostics_task = cx.spawn(async move |_, cx| {
19907            cx.background_executor().timer(debounce).await;
19908            if let Ok(task) = project.update(cx, |project, cx| {
19909                project.lsp_store().update(cx, |lsp_store, cx| {
19910                    lsp_store.pull_diagnostics_for_buffer(buffer, cx)
19911                })
19912            }) {
19913                task.await.log_err();
19914            }
19915            project
19916                .update(cx, |project, cx| {
19917                    project.lsp_store().update(cx, |lsp_store, cx| {
19918                        lsp_store.pull_document_diagnostics_for_buffer_edit(buffer_id, cx);
19919                    })
19920                })
19921                .log_err();
19922        });
19923
19924        Some(())
19925    }
19926
19927    pub fn set_selections_from_remote(
19928        &mut self,
19929        selections: Vec<Selection<Anchor>>,
19930        pending_selection: Option<Selection<Anchor>>,
19931        window: &mut Window,
19932        cx: &mut Context<Self>,
19933    ) {
19934        let old_cursor_position = self.selections.newest_anchor().head();
19935        self.selections
19936            .change_with(&self.display_snapshot(cx), |s| {
19937                s.select_anchors(selections);
19938                if let Some(pending_selection) = pending_selection {
19939                    s.set_pending(pending_selection, SelectMode::Character);
19940                } else {
19941                    s.clear_pending();
19942                }
19943            });
19944        self.selections_did_change(
19945            false,
19946            &old_cursor_position,
19947            SelectionEffects::default(),
19948            window,
19949            cx,
19950        );
19951    }
19952
19953    pub fn transact(
19954        &mut self,
19955        window: &mut Window,
19956        cx: &mut Context<Self>,
19957        update: impl FnOnce(&mut Self, &mut Window, &mut Context<Self>),
19958    ) -> Option<TransactionId> {
19959        self.with_selection_effects_deferred(window, cx, |this, window, cx| {
19960            this.start_transaction_at(Instant::now(), window, cx);
19961            update(this, window, cx);
19962            this.end_transaction_at(Instant::now(), cx)
19963        })
19964    }
19965
19966    pub fn start_transaction_at(
19967        &mut self,
19968        now: Instant,
19969        window: &mut Window,
19970        cx: &mut Context<Self>,
19971    ) -> Option<TransactionId> {
19972        self.end_selection(window, cx);
19973        if let Some(tx_id) = self
19974            .buffer
19975            .update(cx, |buffer, cx| buffer.start_transaction_at(now, cx))
19976        {
19977            self.selection_history
19978                .insert_transaction(tx_id, self.selections.disjoint_anchors_arc());
19979            cx.emit(EditorEvent::TransactionBegun {
19980                transaction_id: tx_id,
19981            });
19982            Some(tx_id)
19983        } else {
19984            None
19985        }
19986    }
19987
19988    pub fn end_transaction_at(
19989        &mut self,
19990        now: Instant,
19991        cx: &mut Context<Self>,
19992    ) -> Option<TransactionId> {
19993        if let Some(transaction_id) = self
19994            .buffer
19995            .update(cx, |buffer, cx| buffer.end_transaction_at(now, cx))
19996        {
19997            if let Some((_, end_selections)) =
19998                self.selection_history.transaction_mut(transaction_id)
19999            {
20000                *end_selections = Some(self.selections.disjoint_anchors_arc());
20001            } else {
20002                log::error!("unexpectedly ended a transaction that wasn't started by this editor");
20003            }
20004
20005            cx.emit(EditorEvent::Edited { transaction_id });
20006            Some(transaction_id)
20007        } else {
20008            None
20009        }
20010    }
20011
20012    pub fn modify_transaction_selection_history(
20013        &mut self,
20014        transaction_id: TransactionId,
20015        modify: impl FnOnce(&mut (Arc<[Selection<Anchor>]>, Option<Arc<[Selection<Anchor>]>>)),
20016    ) -> bool {
20017        self.selection_history
20018            .transaction_mut(transaction_id)
20019            .map(modify)
20020            .is_some()
20021    }
20022
20023    pub fn set_mark(&mut self, _: &actions::SetMark, window: &mut Window, cx: &mut Context<Self>) {
20024        if self.selection_mark_mode {
20025            self.change_selections(SelectionEffects::no_scroll(), window, cx, |s| {
20026                s.move_with(&mut |_, sel| {
20027                    sel.collapse_to(sel.head(), SelectionGoal::None);
20028                });
20029            })
20030        }
20031        self.selection_mark_mode = true;
20032        cx.notify();
20033    }
20034
20035    pub fn swap_selection_ends(
20036        &mut self,
20037        _: &actions::SwapSelectionEnds,
20038        window: &mut Window,
20039        cx: &mut Context<Self>,
20040    ) {
20041        self.change_selections(SelectionEffects::no_scroll(), window, cx, |s| {
20042            s.move_with(&mut |_, sel| {
20043                if sel.start != sel.end {
20044                    sel.reversed = !sel.reversed
20045                }
20046            });
20047        });
20048        self.request_autoscroll(Autoscroll::newest(), cx);
20049        cx.notify();
20050    }
20051
20052    pub fn toggle_focus(
20053        workspace: &mut Workspace,
20054        _: &actions::ToggleFocus,
20055        window: &mut Window,
20056        cx: &mut Context<Workspace>,
20057    ) {
20058        let Some(item) = workspace.recent_active_item_by_type::<Self>(cx) else {
20059            return;
20060        };
20061        workspace.activate_item(&item, true, true, window, cx);
20062    }
20063
20064    pub fn toggle_fold(
20065        &mut self,
20066        _: &actions::ToggleFold,
20067        window: &mut Window,
20068        cx: &mut Context<Self>,
20069    ) {
20070        if self.buffer_kind(cx) == ItemBufferKind::Singleton {
20071            let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
20072            let selection = self.selections.newest::<Point>(&display_map);
20073
20074            let range = if selection.is_empty() {
20075                let point = selection.head().to_display_point(&display_map);
20076                let start = DisplayPoint::new(point.row(), 0).to_point(&display_map);
20077                let end = DisplayPoint::new(point.row(), display_map.line_len(point.row()))
20078                    .to_point(&display_map);
20079                start..end
20080            } else {
20081                selection.range()
20082            };
20083            if display_map.folds_in_range(range).next().is_some() {
20084                self.unfold_lines(&Default::default(), window, cx)
20085            } else {
20086                self.fold(&Default::default(), window, cx)
20087            }
20088        } else {
20089            let multi_buffer_snapshot = self.buffer.read(cx).snapshot(cx);
20090            let buffer_ids: HashSet<_> = self
20091                .selections
20092                .disjoint_anchor_ranges()
20093                .flat_map(|range| multi_buffer_snapshot.buffer_ids_for_range(range))
20094                .collect();
20095
20096            let should_unfold = buffer_ids
20097                .iter()
20098                .any(|buffer_id| self.is_buffer_folded(*buffer_id, cx));
20099
20100            for buffer_id in buffer_ids {
20101                if should_unfold {
20102                    self.unfold_buffer(buffer_id, cx);
20103                } else {
20104                    self.fold_buffer(buffer_id, cx);
20105                }
20106            }
20107        }
20108    }
20109
20110    pub fn toggle_fold_recursive(
20111        &mut self,
20112        _: &actions::ToggleFoldRecursive,
20113        window: &mut Window,
20114        cx: &mut Context<Self>,
20115    ) {
20116        let selection = self.selections.newest::<Point>(&self.display_snapshot(cx));
20117
20118        let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
20119        let range = if selection.is_empty() {
20120            let point = selection.head().to_display_point(&display_map);
20121            let start = DisplayPoint::new(point.row(), 0).to_point(&display_map);
20122            let end = DisplayPoint::new(point.row(), display_map.line_len(point.row()))
20123                .to_point(&display_map);
20124            start..end
20125        } else {
20126            selection.range()
20127        };
20128        if display_map.folds_in_range(range).next().is_some() {
20129            self.unfold_recursive(&Default::default(), window, cx)
20130        } else {
20131            self.fold_recursive(&Default::default(), window, cx)
20132        }
20133    }
20134
20135    pub fn fold(&mut self, _: &actions::Fold, window: &mut Window, cx: &mut Context<Self>) {
20136        if self.buffer_kind(cx) == ItemBufferKind::Singleton {
20137            let mut to_fold = Vec::new();
20138            let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
20139            let selections = self.selections.all_adjusted(&display_map);
20140
20141            for selection in selections {
20142                let range = selection.range().sorted();
20143                let buffer_start_row = range.start.row;
20144
20145                if range.start.row != range.end.row {
20146                    let mut found = false;
20147                    let mut row = range.start.row;
20148                    while row <= range.end.row {
20149                        if let Some(crease) = display_map.crease_for_buffer_row(MultiBufferRow(row))
20150                        {
20151                            found = true;
20152                            row = crease.range().end.row + 1;
20153                            to_fold.push(crease);
20154                        } else {
20155                            row += 1
20156                        }
20157                    }
20158                    if found {
20159                        continue;
20160                    }
20161                }
20162
20163                for row in (0..=range.start.row).rev() {
20164                    if let Some(crease) = display_map.crease_for_buffer_row(MultiBufferRow(row))
20165                        && crease.range().end.row >= buffer_start_row
20166                    {
20167                        to_fold.push(crease);
20168                        if row <= range.start.row {
20169                            break;
20170                        }
20171                    }
20172                }
20173            }
20174
20175            self.fold_creases(to_fold, true, window, cx);
20176        } else {
20177            let multi_buffer_snapshot = self.buffer.read(cx).snapshot(cx);
20178            let buffer_ids = self
20179                .selections
20180                .disjoint_anchor_ranges()
20181                .flat_map(|range| multi_buffer_snapshot.buffer_ids_for_range(range))
20182                .collect::<HashSet<_>>();
20183            for buffer_id in buffer_ids {
20184                self.fold_buffer(buffer_id, cx);
20185            }
20186        }
20187    }
20188
20189    pub fn toggle_fold_all(
20190        &mut self,
20191        _: &actions::ToggleFoldAll,
20192        window: &mut Window,
20193        cx: &mut Context<Self>,
20194    ) {
20195        let has_folds = if self.buffer.read(cx).is_singleton() {
20196            let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
20197            let has_folds = display_map
20198                .folds_in_range(MultiBufferOffset(0)..display_map.buffer_snapshot().len())
20199                .next()
20200                .is_some();
20201            has_folds
20202        } else {
20203            let buffer_ids = self.buffer.read(cx).excerpt_buffer_ids();
20204            let has_folds = buffer_ids
20205                .iter()
20206                .any(|buffer_id| self.is_buffer_folded(*buffer_id, cx));
20207            has_folds
20208        };
20209
20210        if has_folds {
20211            self.unfold_all(&actions::UnfoldAll, window, cx);
20212        } else {
20213            self.fold_all(&actions::FoldAll, window, cx);
20214        }
20215    }
20216
20217    fn fold_at_level(
20218        &mut self,
20219        fold_at: &FoldAtLevel,
20220        window: &mut Window,
20221        cx: &mut Context<Self>,
20222    ) {
20223        if !self.buffer.read(cx).is_singleton() {
20224            return;
20225        }
20226
20227        let fold_at_level = fold_at.0;
20228        let snapshot = self.buffer.read(cx).snapshot(cx);
20229        let mut to_fold = Vec::new();
20230        let mut stack = vec![(0, snapshot.max_row().0, 1)];
20231
20232        let row_ranges_to_keep: Vec<Range<u32>> = self
20233            .selections
20234            .all::<Point>(&self.display_snapshot(cx))
20235            .into_iter()
20236            .map(|sel| sel.start.row..sel.end.row)
20237            .collect();
20238
20239        while let Some((mut start_row, end_row, current_level)) = stack.pop() {
20240            while start_row < end_row {
20241                match self
20242                    .snapshot(window, cx)
20243                    .crease_for_buffer_row(MultiBufferRow(start_row))
20244                {
20245                    Some(crease) => {
20246                        let nested_start_row = crease.range().start.row + 1;
20247                        let nested_end_row = crease.range().end.row;
20248
20249                        if current_level < fold_at_level {
20250                            stack.push((nested_start_row, nested_end_row, current_level + 1));
20251                        } else if current_level == fold_at_level {
20252                            // Fold iff there is no selection completely contained within the fold region
20253                            if !row_ranges_to_keep.iter().any(|selection| {
20254                                selection.end >= nested_start_row
20255                                    && selection.start <= nested_end_row
20256                            }) {
20257                                to_fold.push(crease);
20258                            }
20259                        }
20260
20261                        start_row = nested_end_row + 1;
20262                    }
20263                    None => start_row += 1,
20264                }
20265            }
20266        }
20267
20268        self.fold_creases(to_fold, true, window, cx);
20269    }
20270
20271    pub fn fold_at_level_1(
20272        &mut self,
20273        _: &actions::FoldAtLevel1,
20274        window: &mut Window,
20275        cx: &mut Context<Self>,
20276    ) {
20277        self.fold_at_level(&actions::FoldAtLevel(1), window, cx);
20278    }
20279
20280    pub fn fold_at_level_2(
20281        &mut self,
20282        _: &actions::FoldAtLevel2,
20283        window: &mut Window,
20284        cx: &mut Context<Self>,
20285    ) {
20286        self.fold_at_level(&actions::FoldAtLevel(2), window, cx);
20287    }
20288
20289    pub fn fold_at_level_3(
20290        &mut self,
20291        _: &actions::FoldAtLevel3,
20292        window: &mut Window,
20293        cx: &mut Context<Self>,
20294    ) {
20295        self.fold_at_level(&actions::FoldAtLevel(3), window, cx);
20296    }
20297
20298    pub fn fold_at_level_4(
20299        &mut self,
20300        _: &actions::FoldAtLevel4,
20301        window: &mut Window,
20302        cx: &mut Context<Self>,
20303    ) {
20304        self.fold_at_level(&actions::FoldAtLevel(4), window, cx);
20305    }
20306
20307    pub fn fold_at_level_5(
20308        &mut self,
20309        _: &actions::FoldAtLevel5,
20310        window: &mut Window,
20311        cx: &mut Context<Self>,
20312    ) {
20313        self.fold_at_level(&actions::FoldAtLevel(5), window, cx);
20314    }
20315
20316    pub fn fold_at_level_6(
20317        &mut self,
20318        _: &actions::FoldAtLevel6,
20319        window: &mut Window,
20320        cx: &mut Context<Self>,
20321    ) {
20322        self.fold_at_level(&actions::FoldAtLevel(6), window, cx);
20323    }
20324
20325    pub fn fold_at_level_7(
20326        &mut self,
20327        _: &actions::FoldAtLevel7,
20328        window: &mut Window,
20329        cx: &mut Context<Self>,
20330    ) {
20331        self.fold_at_level(&actions::FoldAtLevel(7), window, cx);
20332    }
20333
20334    pub fn fold_at_level_8(
20335        &mut self,
20336        _: &actions::FoldAtLevel8,
20337        window: &mut Window,
20338        cx: &mut Context<Self>,
20339    ) {
20340        self.fold_at_level(&actions::FoldAtLevel(8), window, cx);
20341    }
20342
20343    pub fn fold_at_level_9(
20344        &mut self,
20345        _: &actions::FoldAtLevel9,
20346        window: &mut Window,
20347        cx: &mut Context<Self>,
20348    ) {
20349        self.fold_at_level(&actions::FoldAtLevel(9), window, cx);
20350    }
20351
20352    pub fn fold_all(&mut self, _: &actions::FoldAll, window: &mut Window, cx: &mut Context<Self>) {
20353        if self.buffer.read(cx).is_singleton() {
20354            let mut fold_ranges = Vec::new();
20355            let snapshot = self.buffer.read(cx).snapshot(cx);
20356
20357            for row in 0..snapshot.max_row().0 {
20358                if let Some(foldable_range) = self
20359                    .snapshot(window, cx)
20360                    .crease_for_buffer_row(MultiBufferRow(row))
20361                {
20362                    fold_ranges.push(foldable_range);
20363                }
20364            }
20365
20366            self.fold_creases(fold_ranges, true, window, cx);
20367        } else {
20368            self.toggle_fold_multiple_buffers = cx.spawn_in(window, async move |editor, cx| {
20369                editor
20370                    .update_in(cx, |editor, _, cx| {
20371                        for buffer_id in editor.buffer.read(cx).excerpt_buffer_ids() {
20372                            editor.fold_buffer(buffer_id, cx);
20373                        }
20374                    })
20375                    .ok();
20376            });
20377        }
20378    }
20379
20380    pub fn fold_function_bodies(
20381        &mut self,
20382        _: &actions::FoldFunctionBodies,
20383        window: &mut Window,
20384        cx: &mut Context<Self>,
20385    ) {
20386        let snapshot = self.buffer.read(cx).snapshot(cx);
20387
20388        let ranges = snapshot
20389            .text_object_ranges(
20390                MultiBufferOffset(0)..snapshot.len(),
20391                TreeSitterOptions::default(),
20392            )
20393            .filter_map(|(range, obj)| (obj == TextObject::InsideFunction).then_some(range))
20394            .collect::<Vec<_>>();
20395
20396        let creases = ranges
20397            .into_iter()
20398            .map(|range| Crease::simple(range, self.display_map.read(cx).fold_placeholder.clone()))
20399            .collect();
20400
20401        self.fold_creases(creases, true, window, cx);
20402    }
20403
20404    pub fn fold_recursive(
20405        &mut self,
20406        _: &actions::FoldRecursive,
20407        window: &mut Window,
20408        cx: &mut Context<Self>,
20409    ) {
20410        let mut to_fold = Vec::new();
20411        let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
20412        let selections = self.selections.all_adjusted(&display_map);
20413
20414        for selection in selections {
20415            let range = selection.range().sorted();
20416            let buffer_start_row = range.start.row;
20417
20418            if range.start.row != range.end.row {
20419                let mut found = false;
20420                for row in range.start.row..=range.end.row {
20421                    if let Some(crease) = display_map.crease_for_buffer_row(MultiBufferRow(row)) {
20422                        found = true;
20423                        to_fold.push(crease);
20424                    }
20425                }
20426                if found {
20427                    continue;
20428                }
20429            }
20430
20431            for row in (0..=range.start.row).rev() {
20432                if let Some(crease) = display_map.crease_for_buffer_row(MultiBufferRow(row)) {
20433                    if crease.range().end.row >= buffer_start_row {
20434                        to_fold.push(crease);
20435                    } else {
20436                        break;
20437                    }
20438                }
20439            }
20440        }
20441
20442        self.fold_creases(to_fold, true, window, cx);
20443    }
20444
20445    pub fn fold_at(
20446        &mut self,
20447        buffer_row: MultiBufferRow,
20448        window: &mut Window,
20449        cx: &mut Context<Self>,
20450    ) {
20451        let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
20452
20453        if let Some(crease) = display_map.crease_for_buffer_row(buffer_row) {
20454            let autoscroll = self
20455                .selections
20456                .all::<Point>(&display_map)
20457                .iter()
20458                .any(|selection| crease.range().overlaps(&selection.range()));
20459
20460            self.fold_creases(vec![crease], autoscroll, window, cx);
20461        }
20462    }
20463
20464    pub fn unfold_lines(&mut self, _: &UnfoldLines, _window: &mut Window, cx: &mut Context<Self>) {
20465        if self.buffer_kind(cx) == ItemBufferKind::Singleton {
20466            let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
20467            let buffer = display_map.buffer_snapshot();
20468            let selections = self.selections.all::<Point>(&display_map);
20469            let ranges = selections
20470                .iter()
20471                .map(|s| {
20472                    let range = s.display_range(&display_map).sorted();
20473                    let mut start = range.start.to_point(&display_map);
20474                    let mut end = range.end.to_point(&display_map);
20475                    start.column = 0;
20476                    end.column = buffer.line_len(MultiBufferRow(end.row));
20477                    start..end
20478                })
20479                .collect::<Vec<_>>();
20480
20481            self.unfold_ranges(&ranges, true, true, cx);
20482        } else {
20483            let multi_buffer_snapshot = self.buffer.read(cx).snapshot(cx);
20484            let buffer_ids = self
20485                .selections
20486                .disjoint_anchor_ranges()
20487                .flat_map(|range| multi_buffer_snapshot.buffer_ids_for_range(range))
20488                .collect::<HashSet<_>>();
20489            for buffer_id in buffer_ids {
20490                self.unfold_buffer(buffer_id, cx);
20491            }
20492        }
20493    }
20494
20495    pub fn unfold_recursive(
20496        &mut self,
20497        _: &UnfoldRecursive,
20498        _window: &mut Window,
20499        cx: &mut Context<Self>,
20500    ) {
20501        let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
20502        let selections = self.selections.all::<Point>(&display_map);
20503        let ranges = selections
20504            .iter()
20505            .map(|s| {
20506                let mut range = s.display_range(&display_map).sorted();
20507                *range.start.column_mut() = 0;
20508                *range.end.column_mut() = display_map.line_len(range.end.row());
20509                let start = range.start.to_point(&display_map);
20510                let end = range.end.to_point(&display_map);
20511                start..end
20512            })
20513            .collect::<Vec<_>>();
20514
20515        self.unfold_ranges(&ranges, true, true, cx);
20516    }
20517
20518    pub fn unfold_at(
20519        &mut self,
20520        buffer_row: MultiBufferRow,
20521        _window: &mut Window,
20522        cx: &mut Context<Self>,
20523    ) {
20524        let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
20525
20526        let intersection_range = Point::new(buffer_row.0, 0)
20527            ..Point::new(
20528                buffer_row.0,
20529                display_map.buffer_snapshot().line_len(buffer_row),
20530            );
20531
20532        let autoscroll = self
20533            .selections
20534            .all::<Point>(&display_map)
20535            .iter()
20536            .any(|selection| RangeExt::overlaps(&selection.range(), &intersection_range));
20537
20538        self.unfold_ranges(&[intersection_range], true, autoscroll, cx);
20539    }
20540
20541    pub fn unfold_all(
20542        &mut self,
20543        _: &actions::UnfoldAll,
20544        _window: &mut Window,
20545        cx: &mut Context<Self>,
20546    ) {
20547        if self.buffer.read(cx).is_singleton() {
20548            let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
20549            self.unfold_ranges(
20550                &[MultiBufferOffset(0)..display_map.buffer_snapshot().len()],
20551                true,
20552                true,
20553                cx,
20554            );
20555        } else {
20556            self.toggle_fold_multiple_buffers = cx.spawn(async move |editor, cx| {
20557                editor
20558                    .update(cx, |editor, cx| {
20559                        for buffer_id in editor.buffer.read(cx).excerpt_buffer_ids() {
20560                            editor.unfold_buffer(buffer_id, cx);
20561                        }
20562                    })
20563                    .ok();
20564            });
20565        }
20566    }
20567
20568    pub fn fold_selected_ranges(
20569        &mut self,
20570        _: &FoldSelectedRanges,
20571        window: &mut Window,
20572        cx: &mut Context<Self>,
20573    ) {
20574        let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
20575        let selections = self.selections.all_adjusted(&display_map);
20576        let ranges = selections
20577            .into_iter()
20578            .map(|s| Crease::simple(s.range(), display_map.fold_placeholder.clone()))
20579            .collect::<Vec<_>>();
20580        self.fold_creases(ranges, true, window, cx);
20581    }
20582
20583    pub fn fold_ranges<T: ToOffset + Clone>(
20584        &mut self,
20585        ranges: Vec<Range<T>>,
20586        auto_scroll: bool,
20587        window: &mut Window,
20588        cx: &mut Context<Self>,
20589    ) {
20590        let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
20591        let ranges = ranges
20592            .into_iter()
20593            .map(|r| Crease::simple(r, display_map.fold_placeholder.clone()))
20594            .collect::<Vec<_>>();
20595        self.fold_creases(ranges, auto_scroll, window, cx);
20596    }
20597
20598    pub fn fold_creases<T: ToOffset + Clone>(
20599        &mut self,
20600        creases: Vec<Crease<T>>,
20601        auto_scroll: bool,
20602        window: &mut Window,
20603        cx: &mut Context<Self>,
20604    ) {
20605        if creases.is_empty() {
20606            return;
20607        }
20608
20609        self.display_map.update(cx, |map, cx| map.fold(creases, cx));
20610
20611        if auto_scroll {
20612            self.request_autoscroll(Autoscroll::fit(), cx);
20613        }
20614
20615        cx.notify();
20616
20617        self.scrollbar_marker_state.dirty = true;
20618        self.update_data_on_scroll(window, cx);
20619        self.folds_did_change(cx);
20620    }
20621
20622    /// Removes any folds whose ranges intersect any of the given ranges.
20623    pub fn unfold_ranges<T: ToOffset + Clone>(
20624        &mut self,
20625        ranges: &[Range<T>],
20626        inclusive: bool,
20627        auto_scroll: bool,
20628        cx: &mut Context<Self>,
20629    ) {
20630        self.remove_folds_with(ranges, auto_scroll, cx, |map, cx| {
20631            map.unfold_intersecting(ranges.iter().cloned(), inclusive, cx);
20632        });
20633        self.folds_did_change(cx);
20634    }
20635
20636    pub fn fold_buffer(&mut self, buffer_id: BufferId, cx: &mut Context<Self>) {
20637        self.fold_buffers([buffer_id], cx);
20638    }
20639
20640    pub fn fold_buffers(
20641        &mut self,
20642        buffer_ids: impl IntoIterator<Item = BufferId>,
20643        cx: &mut Context<Self>,
20644    ) {
20645        if self.buffer().read(cx).is_singleton() {
20646            return;
20647        }
20648
20649        let ids_to_fold: Vec<BufferId> = buffer_ids
20650            .into_iter()
20651            .filter(|id| !self.is_buffer_folded(*id, cx))
20652            .collect();
20653
20654        if ids_to_fold.is_empty() {
20655            return;
20656        }
20657
20658        let mut all_folded_excerpt_ids = Vec::new();
20659        for buffer_id in &ids_to_fold {
20660            let folded_excerpts = self.buffer().read(cx).excerpts_for_buffer(*buffer_id, cx);
20661            all_folded_excerpt_ids.extend(folded_excerpts.into_iter().map(|(id, _, _)| id));
20662        }
20663
20664        self.display_map.update(cx, |display_map, cx| {
20665            display_map.fold_buffers(ids_to_fold.clone(), cx)
20666        });
20667
20668        let snapshot = self.display_snapshot(cx);
20669        self.selections.change_with(&snapshot, |selections| {
20670            for buffer_id in ids_to_fold {
20671                selections.remove_selections_from_buffer(buffer_id);
20672            }
20673        });
20674
20675        cx.emit(EditorEvent::BufferFoldToggled {
20676            ids: all_folded_excerpt_ids,
20677            folded: true,
20678        });
20679        cx.notify();
20680    }
20681
20682    pub fn unfold_buffer(&mut self, buffer_id: BufferId, cx: &mut Context<Self>) {
20683        if self.buffer().read(cx).is_singleton() || !self.is_buffer_folded(buffer_id, cx) {
20684            return;
20685        }
20686        let unfolded_excerpts = self.buffer().read(cx).excerpts_for_buffer(buffer_id, cx);
20687        self.display_map.update(cx, |display_map, cx| {
20688            display_map.unfold_buffers([buffer_id], cx);
20689        });
20690        cx.emit(EditorEvent::BufferFoldToggled {
20691            ids: unfolded_excerpts.iter().map(|&(id, _, _)| id).collect(),
20692            folded: false,
20693        });
20694        cx.notify();
20695    }
20696
20697    pub fn is_buffer_folded(&self, buffer: BufferId, cx: &App) -> bool {
20698        self.display_map.read(cx).is_buffer_folded(buffer)
20699    }
20700
20701    pub fn has_any_buffer_folded(&self, cx: &App) -> bool {
20702        if self.buffer().read(cx).is_singleton() {
20703            return false;
20704        }
20705        !self.folded_buffers(cx).is_empty()
20706    }
20707
20708    pub fn folded_buffers<'a>(&self, cx: &'a App) -> &'a HashSet<BufferId> {
20709        self.display_map.read(cx).folded_buffers()
20710    }
20711
20712    pub fn disable_header_for_buffer(&mut self, buffer_id: BufferId, cx: &mut Context<Self>) {
20713        self.display_map.update(cx, |display_map, cx| {
20714            display_map.disable_header_for_buffer(buffer_id, cx);
20715        });
20716        cx.notify();
20717    }
20718
20719    /// Removes any folds with the given ranges.
20720    pub fn remove_folds_with_type<T: ToOffset + Clone>(
20721        &mut self,
20722        ranges: &[Range<T>],
20723        type_id: TypeId,
20724        auto_scroll: bool,
20725        cx: &mut Context<Self>,
20726    ) {
20727        self.remove_folds_with(ranges, auto_scroll, cx, |map, cx| {
20728            map.remove_folds_with_type(ranges.iter().cloned(), type_id, cx)
20729        });
20730        self.folds_did_change(cx);
20731    }
20732
20733    fn remove_folds_with<T: ToOffset + Clone>(
20734        &mut self,
20735        ranges: &[Range<T>],
20736        auto_scroll: bool,
20737        cx: &mut Context<Self>,
20738        update: impl FnOnce(&mut DisplayMap, &mut Context<DisplayMap>),
20739    ) {
20740        if ranges.is_empty() {
20741            return;
20742        }
20743
20744        let mut buffers_affected = HashSet::default();
20745        let multi_buffer = self.buffer().read(cx);
20746        for range in ranges {
20747            if let Some((_, buffer, _)) = multi_buffer.excerpt_containing(range.start.clone(), cx) {
20748                buffers_affected.insert(buffer.read(cx).remote_id());
20749            };
20750        }
20751
20752        self.display_map.update(cx, update);
20753
20754        if auto_scroll {
20755            self.request_autoscroll(Autoscroll::fit(), cx);
20756        }
20757
20758        cx.notify();
20759        self.scrollbar_marker_state.dirty = true;
20760        self.active_indent_guides_state.dirty = true;
20761    }
20762
20763    pub fn update_renderer_widths(
20764        &mut self,
20765        widths: impl IntoIterator<Item = (ChunkRendererId, Pixels)>,
20766        cx: &mut Context<Self>,
20767    ) -> bool {
20768        self.display_map
20769            .update(cx, |map, cx| map.update_fold_widths(widths, cx))
20770    }
20771
20772    pub fn default_fold_placeholder(&self, cx: &App) -> FoldPlaceholder {
20773        self.display_map.read(cx).fold_placeholder.clone()
20774    }
20775
20776    pub fn set_expand_all_diff_hunks(&mut self, cx: &mut App) {
20777        self.buffer.update(cx, |buffer, cx| {
20778            buffer.set_all_diff_hunks_expanded(cx);
20779        });
20780    }
20781
20782    pub fn expand_all_diff_hunks(
20783        &mut self,
20784        _: &ExpandAllDiffHunks,
20785        _window: &mut Window,
20786        cx: &mut Context<Self>,
20787    ) {
20788        self.buffer.update(cx, |buffer, cx| {
20789            buffer.expand_diff_hunks(vec![Anchor::min()..Anchor::max()], cx)
20790        });
20791    }
20792
20793    pub fn collapse_all_diff_hunks(
20794        &mut self,
20795        _: &CollapseAllDiffHunks,
20796        _window: &mut Window,
20797        cx: &mut Context<Self>,
20798    ) {
20799        self.buffer.update(cx, |buffer, cx| {
20800            buffer.collapse_diff_hunks(vec![Anchor::min()..Anchor::max()], cx)
20801        });
20802    }
20803
20804    pub fn toggle_selected_diff_hunks(
20805        &mut self,
20806        _: &ToggleSelectedDiffHunks,
20807        _window: &mut Window,
20808        cx: &mut Context<Self>,
20809    ) {
20810        let ranges: Vec<_> = self
20811            .selections
20812            .disjoint_anchors()
20813            .iter()
20814            .map(|s| s.range())
20815            .collect();
20816        self.toggle_diff_hunks_in_ranges(ranges, cx);
20817    }
20818
20819    pub fn diff_hunks_in_ranges<'a>(
20820        &'a self,
20821        ranges: &'a [Range<Anchor>],
20822        buffer: &'a MultiBufferSnapshot,
20823    ) -> impl 'a + Iterator<Item = MultiBufferDiffHunk> {
20824        ranges.iter().flat_map(move |range| {
20825            let end_excerpt_id = range.end.excerpt_id;
20826            let range = range.to_point(buffer);
20827            let mut peek_end = range.end;
20828            if range.end.row < buffer.max_row().0 {
20829                peek_end = Point::new(range.end.row + 1, 0);
20830            }
20831            buffer
20832                .diff_hunks_in_range(range.start..peek_end)
20833                .filter(move |hunk| hunk.excerpt_id.cmp(&end_excerpt_id, buffer).is_le())
20834        })
20835    }
20836
20837    pub fn has_stageable_diff_hunks_in_ranges(
20838        &self,
20839        ranges: &[Range<Anchor>],
20840        snapshot: &MultiBufferSnapshot,
20841    ) -> bool {
20842        let mut hunks = self.diff_hunks_in_ranges(ranges, snapshot);
20843        hunks.any(|hunk| hunk.status().has_secondary_hunk())
20844    }
20845
20846    pub fn toggle_staged_selected_diff_hunks(
20847        &mut self,
20848        _: &::git::ToggleStaged,
20849        _: &mut Window,
20850        cx: &mut Context<Self>,
20851    ) {
20852        let snapshot = self.buffer.read(cx).snapshot(cx);
20853        let ranges: Vec<_> = self
20854            .selections
20855            .disjoint_anchors()
20856            .iter()
20857            .map(|s| s.range())
20858            .collect();
20859        let stage = self.has_stageable_diff_hunks_in_ranges(&ranges, &snapshot);
20860        self.stage_or_unstage_diff_hunks(stage, ranges, cx);
20861    }
20862
20863    pub fn set_render_diff_hunk_controls(
20864        &mut self,
20865        render_diff_hunk_controls: RenderDiffHunkControlsFn,
20866        cx: &mut Context<Self>,
20867    ) {
20868        self.render_diff_hunk_controls = render_diff_hunk_controls;
20869        cx.notify();
20870    }
20871
20872    pub fn stage_and_next(
20873        &mut self,
20874        _: &::git::StageAndNext,
20875        window: &mut Window,
20876        cx: &mut Context<Self>,
20877    ) {
20878        self.do_stage_or_unstage_and_next(true, window, cx);
20879    }
20880
20881    pub fn unstage_and_next(
20882        &mut self,
20883        _: &::git::UnstageAndNext,
20884        window: &mut Window,
20885        cx: &mut Context<Self>,
20886    ) {
20887        self.do_stage_or_unstage_and_next(false, window, cx);
20888    }
20889
20890    pub fn stage_or_unstage_diff_hunks(
20891        &mut self,
20892        stage: bool,
20893        ranges: Vec<Range<Anchor>>,
20894        cx: &mut Context<Self>,
20895    ) {
20896        if self.delegate_stage_and_restore {
20897            let snapshot = self.buffer.read(cx).snapshot(cx);
20898            let hunks: Vec<_> = self.diff_hunks_in_ranges(&ranges, &snapshot).collect();
20899            if !hunks.is_empty() {
20900                cx.emit(EditorEvent::StageOrUnstageRequested { stage, hunks });
20901            }
20902            return;
20903        }
20904        let task = self.save_buffers_for_ranges_if_needed(&ranges, cx);
20905        cx.spawn(async move |this, cx| {
20906            task.await?;
20907            this.update(cx, |this, cx| {
20908                let snapshot = this.buffer.read(cx).snapshot(cx);
20909                let chunk_by = this
20910                    .diff_hunks_in_ranges(&ranges, &snapshot)
20911                    .chunk_by(|hunk| hunk.buffer_id);
20912                for (buffer_id, hunks) in &chunk_by {
20913                    this.do_stage_or_unstage(stage, buffer_id, hunks, cx);
20914                }
20915            })
20916        })
20917        .detach_and_log_err(cx);
20918    }
20919
20920    fn save_buffers_for_ranges_if_needed(
20921        &mut self,
20922        ranges: &[Range<Anchor>],
20923        cx: &mut Context<Editor>,
20924    ) -> Task<Result<()>> {
20925        let multibuffer = self.buffer.read(cx);
20926        let snapshot = multibuffer.read(cx);
20927        let buffer_ids: HashSet<_> = ranges
20928            .iter()
20929            .flat_map(|range| snapshot.buffer_ids_for_range(range.clone()))
20930            .collect();
20931        drop(snapshot);
20932
20933        let mut buffers = HashSet::default();
20934        for buffer_id in buffer_ids {
20935            if let Some(buffer_entity) = multibuffer.buffer(buffer_id) {
20936                let buffer = buffer_entity.read(cx);
20937                if buffer.file().is_some_and(|file| file.disk_state().exists()) && buffer.is_dirty()
20938                {
20939                    buffers.insert(buffer_entity);
20940                }
20941            }
20942        }
20943
20944        if let Some(project) = &self.project {
20945            project.update(cx, |project, cx| project.save_buffers(buffers, cx))
20946        } else {
20947            Task::ready(Ok(()))
20948        }
20949    }
20950
20951    fn do_stage_or_unstage_and_next(
20952        &mut self,
20953        stage: bool,
20954        window: &mut Window,
20955        cx: &mut Context<Self>,
20956    ) {
20957        let ranges = self.selections.disjoint_anchor_ranges().collect::<Vec<_>>();
20958
20959        if ranges.iter().any(|range| range.start != range.end) {
20960            self.stage_or_unstage_diff_hunks(stage, ranges, cx);
20961            return;
20962        }
20963
20964        self.stage_or_unstage_diff_hunks(stage, ranges, cx);
20965
20966        let all_diff_hunks_expanded = self.buffer().read(cx).all_diff_hunks_expanded();
20967        let wrap_around = !all_diff_hunks_expanded;
20968        let snapshot = self.snapshot(window, cx);
20969        let position = self
20970            .selections
20971            .newest::<Point>(&snapshot.display_snapshot)
20972            .head();
20973
20974        self.go_to_hunk_before_or_after_position(
20975            &snapshot,
20976            position,
20977            Direction::Next,
20978            wrap_around,
20979            window,
20980            cx,
20981        );
20982    }
20983
20984    pub(crate) fn do_stage_or_unstage(
20985        &self,
20986        stage: bool,
20987        buffer_id: BufferId,
20988        hunks: impl Iterator<Item = MultiBufferDiffHunk>,
20989        cx: &mut App,
20990    ) -> Option<()> {
20991        let project = self.project()?;
20992        let buffer = project.read(cx).buffer_for_id(buffer_id, cx)?;
20993        let diff = self.buffer.read(cx).diff_for(buffer_id)?;
20994        let buffer_snapshot = buffer.read(cx).snapshot();
20995        let file_exists = buffer_snapshot
20996            .file()
20997            .is_some_and(|file| file.disk_state().exists());
20998        diff.update(cx, |diff, cx| {
20999            diff.stage_or_unstage_hunks(
21000                stage,
21001                &hunks
21002                    .map(|hunk| buffer_diff::DiffHunk {
21003                        buffer_range: hunk.buffer_range,
21004                        // We don't need to pass in word diffs here because they're only used for rendering and
21005                        // this function changes internal state
21006                        base_word_diffs: Vec::default(),
21007                        buffer_word_diffs: Vec::default(),
21008                        diff_base_byte_range: hunk.diff_base_byte_range.start.0
21009                            ..hunk.diff_base_byte_range.end.0,
21010                        secondary_status: hunk.status.secondary,
21011                        range: Point::zero()..Point::zero(), // unused
21012                    })
21013                    .collect::<Vec<_>>(),
21014                &buffer_snapshot,
21015                file_exists,
21016                cx,
21017            )
21018        });
21019        None
21020    }
21021
21022    pub fn expand_selected_diff_hunks(&mut self, cx: &mut Context<Self>) {
21023        let ranges: Vec<_> = self
21024            .selections
21025            .disjoint_anchors()
21026            .iter()
21027            .map(|s| s.range())
21028            .collect();
21029        self.buffer
21030            .update(cx, |buffer, cx| buffer.expand_diff_hunks(ranges, cx))
21031    }
21032
21033    pub fn clear_expanded_diff_hunks(&mut self, cx: &mut Context<Self>) -> bool {
21034        self.buffer.update(cx, |buffer, cx| {
21035            let ranges = vec![Anchor::min()..Anchor::max()];
21036            if !buffer.all_diff_hunks_expanded()
21037                && buffer.has_expanded_diff_hunks_in_ranges(&ranges, cx)
21038            {
21039                buffer.collapse_diff_hunks(ranges, cx);
21040                true
21041            } else {
21042                false
21043            }
21044        })
21045    }
21046
21047    fn has_any_expanded_diff_hunks(&self, cx: &App) -> bool {
21048        if self.buffer.read(cx).all_diff_hunks_expanded() {
21049            return true;
21050        }
21051        let ranges = vec![Anchor::min()..Anchor::max()];
21052        self.buffer
21053            .read(cx)
21054            .has_expanded_diff_hunks_in_ranges(&ranges, cx)
21055    }
21056
21057    fn toggle_diff_hunks_in_ranges(
21058        &mut self,
21059        ranges: Vec<Range<Anchor>>,
21060        cx: &mut Context<Editor>,
21061    ) {
21062        self.buffer.update(cx, |buffer, cx| {
21063            let expand = !buffer.has_expanded_diff_hunks_in_ranges(&ranges, cx);
21064            buffer.expand_or_collapse_diff_hunks(ranges, expand, cx);
21065        })
21066    }
21067
21068    fn toggle_single_diff_hunk(&mut self, range: Range<Anchor>, cx: &mut Context<Self>) {
21069        self.buffer.update(cx, |buffer, cx| {
21070            buffer.toggle_single_diff_hunk(range, cx);
21071        })
21072    }
21073
21074    pub(crate) fn apply_all_diff_hunks(
21075        &mut self,
21076        _: &ApplyAllDiffHunks,
21077        window: &mut Window,
21078        cx: &mut Context<Self>,
21079    ) {
21080        self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
21081
21082        let buffers = self.buffer.read(cx).all_buffers();
21083        for branch_buffer in buffers {
21084            branch_buffer.update(cx, |branch_buffer, cx| {
21085                branch_buffer.merge_into_base(Vec::new(), cx);
21086            });
21087        }
21088
21089        if let Some(project) = self.project.clone() {
21090            self.save(
21091                SaveOptions {
21092                    format: true,
21093                    autosave: false,
21094                },
21095                project,
21096                window,
21097                cx,
21098            )
21099            .detach_and_log_err(cx);
21100        }
21101    }
21102
21103    pub(crate) fn apply_selected_diff_hunks(
21104        &mut self,
21105        _: &ApplyDiffHunk,
21106        window: &mut Window,
21107        cx: &mut Context<Self>,
21108    ) {
21109        self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
21110        let snapshot = self.snapshot(window, cx);
21111        let hunks = snapshot.hunks_for_ranges(
21112            self.selections
21113                .all(&snapshot.display_snapshot)
21114                .into_iter()
21115                .map(|selection| selection.range()),
21116        );
21117        let mut ranges_by_buffer = HashMap::default();
21118        self.transact(window, cx, |editor, _window, cx| {
21119            for hunk in hunks {
21120                if let Some(buffer) = editor.buffer.read(cx).buffer(hunk.buffer_id) {
21121                    ranges_by_buffer
21122                        .entry(buffer.clone())
21123                        .or_insert_with(Vec::new)
21124                        .push(hunk.buffer_range.to_offset(buffer.read(cx)));
21125                }
21126            }
21127
21128            for (buffer, ranges) in ranges_by_buffer {
21129                buffer.update(cx, |buffer, cx| {
21130                    buffer.merge_into_base(ranges, cx);
21131                });
21132            }
21133        });
21134
21135        if let Some(project) = self.project.clone() {
21136            self.save(
21137                SaveOptions {
21138                    format: true,
21139                    autosave: false,
21140                },
21141                project,
21142                window,
21143                cx,
21144            )
21145            .detach_and_log_err(cx);
21146        }
21147    }
21148
21149    pub fn set_gutter_hovered(&mut self, hovered: bool, cx: &mut Context<Self>) {
21150        if hovered != self.gutter_hovered {
21151            self.gutter_hovered = hovered;
21152            cx.notify();
21153        }
21154    }
21155
21156    pub fn insert_blocks(
21157        &mut self,
21158        blocks: impl IntoIterator<Item = BlockProperties<Anchor>>,
21159        autoscroll: Option<Autoscroll>,
21160        cx: &mut Context<Self>,
21161    ) -> Vec<CustomBlockId> {
21162        let blocks = self
21163            .display_map
21164            .update(cx, |display_map, cx| display_map.insert_blocks(blocks, cx));
21165        if let Some(autoscroll) = autoscroll {
21166            self.request_autoscroll(autoscroll, cx);
21167        }
21168        cx.notify();
21169        blocks
21170    }
21171
21172    pub fn resize_blocks(
21173        &mut self,
21174        heights: HashMap<CustomBlockId, u32>,
21175        autoscroll: Option<Autoscroll>,
21176        cx: &mut Context<Self>,
21177    ) {
21178        self.display_map
21179            .update(cx, |display_map, cx| display_map.resize_blocks(heights, cx));
21180        if let Some(autoscroll) = autoscroll {
21181            self.request_autoscroll(autoscroll, cx);
21182        }
21183        cx.notify();
21184    }
21185
21186    pub fn replace_blocks(
21187        &mut self,
21188        renderers: HashMap<CustomBlockId, RenderBlock>,
21189        autoscroll: Option<Autoscroll>,
21190        cx: &mut Context<Self>,
21191    ) {
21192        self.display_map
21193            .update(cx, |display_map, _cx| display_map.replace_blocks(renderers));
21194        if let Some(autoscroll) = autoscroll {
21195            self.request_autoscroll(autoscroll, cx);
21196        }
21197        cx.notify();
21198    }
21199
21200    pub fn remove_blocks(
21201        &mut self,
21202        block_ids: HashSet<CustomBlockId>,
21203        autoscroll: Option<Autoscroll>,
21204        cx: &mut Context<Self>,
21205    ) {
21206        self.display_map.update(cx, |display_map, cx| {
21207            display_map.remove_blocks(block_ids, cx)
21208        });
21209        if let Some(autoscroll) = autoscroll {
21210            self.request_autoscroll(autoscroll, cx);
21211        }
21212        cx.notify();
21213    }
21214
21215    pub fn row_for_block(
21216        &self,
21217        block_id: CustomBlockId,
21218        cx: &mut Context<Self>,
21219    ) -> Option<DisplayRow> {
21220        self.display_map
21221            .update(cx, |map, cx| map.row_for_block(block_id, cx))
21222    }
21223
21224    pub(crate) fn set_focused_block(&mut self, focused_block: FocusedBlock) {
21225        self.focused_block = Some(focused_block);
21226    }
21227
21228    pub(crate) fn take_focused_block(&mut self) -> Option<FocusedBlock> {
21229        self.focused_block.take()
21230    }
21231
21232    pub fn insert_creases(
21233        &mut self,
21234        creases: impl IntoIterator<Item = Crease<Anchor>>,
21235        cx: &mut Context<Self>,
21236    ) -> Vec<CreaseId> {
21237        self.display_map
21238            .update(cx, |map, cx| map.insert_creases(creases, cx))
21239    }
21240
21241    pub fn remove_creases(
21242        &mut self,
21243        ids: impl IntoIterator<Item = CreaseId>,
21244        cx: &mut Context<Self>,
21245    ) -> Vec<(CreaseId, Range<Anchor>)> {
21246        self.display_map
21247            .update(cx, |map, cx| map.remove_creases(ids, cx))
21248    }
21249
21250    pub fn longest_row(&self, cx: &mut App) -> DisplayRow {
21251        self.display_map
21252            .update(cx, |map, cx| map.snapshot(cx))
21253            .longest_row()
21254    }
21255
21256    pub fn max_point(&self, cx: &mut App) -> DisplayPoint {
21257        self.display_map
21258            .update(cx, |map, cx| map.snapshot(cx))
21259            .max_point()
21260    }
21261
21262    pub fn text(&self, cx: &App) -> String {
21263        self.buffer.read(cx).read(cx).text()
21264    }
21265
21266    pub fn is_empty(&self, cx: &App) -> bool {
21267        self.buffer.read(cx).read(cx).is_empty()
21268    }
21269
21270    pub fn text_option(&self, cx: &App) -> Option<String> {
21271        let text = self.text(cx);
21272        let text = text.trim();
21273
21274        if text.is_empty() {
21275            return None;
21276        }
21277
21278        Some(text.to_string())
21279    }
21280
21281    pub fn set_text(
21282        &mut self,
21283        text: impl Into<Arc<str>>,
21284        window: &mut Window,
21285        cx: &mut Context<Self>,
21286    ) {
21287        self.transact(window, cx, |this, _, cx| {
21288            this.buffer
21289                .read(cx)
21290                .as_singleton()
21291                .expect("you can only call set_text on editors for singleton buffers")
21292                .update(cx, |buffer, cx| buffer.set_text(text, cx));
21293        });
21294    }
21295
21296    pub fn display_text(&self, cx: &mut App) -> String {
21297        self.display_map
21298            .update(cx, |map, cx| map.snapshot(cx))
21299            .text()
21300    }
21301
21302    fn create_minimap(
21303        &self,
21304        minimap_settings: MinimapSettings,
21305        window: &mut Window,
21306        cx: &mut Context<Self>,
21307    ) -> Option<Entity<Self>> {
21308        (minimap_settings.minimap_enabled() && self.buffer_kind(cx) == ItemBufferKind::Singleton)
21309            .then(|| self.initialize_new_minimap(minimap_settings, window, cx))
21310    }
21311
21312    fn initialize_new_minimap(
21313        &self,
21314        minimap_settings: MinimapSettings,
21315        window: &mut Window,
21316        cx: &mut Context<Self>,
21317    ) -> Entity<Self> {
21318        const MINIMAP_FONT_WEIGHT: gpui::FontWeight = gpui::FontWeight::BLACK;
21319        const MINIMAP_FONT_FAMILY: SharedString = SharedString::new_static(".ZedMono");
21320
21321        let mut minimap = Editor::new_internal(
21322            EditorMode::Minimap {
21323                parent: cx.weak_entity(),
21324            },
21325            self.buffer.clone(),
21326            None,
21327            Some(self.display_map.clone()),
21328            window,
21329            cx,
21330        );
21331        let my_snapshot = self.display_map.update(cx, |map, cx| map.snapshot(cx));
21332        let minimap_snapshot = minimap.display_map.update(cx, |map, cx| map.snapshot(cx));
21333        minimap.scroll_manager.clone_state(
21334            &self.scroll_manager,
21335            &my_snapshot,
21336            &minimap_snapshot,
21337            cx,
21338        );
21339        minimap.set_text_style_refinement(TextStyleRefinement {
21340            font_size: Some(MINIMAP_FONT_SIZE),
21341            font_weight: Some(MINIMAP_FONT_WEIGHT),
21342            font_family: Some(MINIMAP_FONT_FAMILY),
21343            ..Default::default()
21344        });
21345        minimap.update_minimap_configuration(minimap_settings, cx);
21346        cx.new(|_| minimap)
21347    }
21348
21349    fn update_minimap_configuration(&mut self, minimap_settings: MinimapSettings, cx: &App) {
21350        let current_line_highlight = minimap_settings
21351            .current_line_highlight
21352            .unwrap_or_else(|| EditorSettings::get_global(cx).current_line_highlight);
21353        self.set_current_line_highlight(Some(current_line_highlight));
21354    }
21355
21356    pub fn minimap(&self) -> Option<&Entity<Self>> {
21357        self.minimap
21358            .as_ref()
21359            .filter(|_| self.minimap_visibility.visible())
21360    }
21361
21362    pub fn wrap_guides(&self, cx: &App) -> SmallVec<[(usize, bool); 2]> {
21363        let mut wrap_guides = smallvec![];
21364
21365        if self.show_wrap_guides == Some(false) {
21366            return wrap_guides;
21367        }
21368
21369        let settings = self.buffer.read(cx).language_settings(cx);
21370        if settings.show_wrap_guides {
21371            match self.soft_wrap_mode(cx) {
21372                SoftWrap::Column(soft_wrap) => {
21373                    wrap_guides.push((soft_wrap as usize, true));
21374                }
21375                SoftWrap::Bounded(soft_wrap) => {
21376                    wrap_guides.push((soft_wrap as usize, true));
21377                }
21378                SoftWrap::GitDiff | SoftWrap::None | SoftWrap::EditorWidth => {}
21379            }
21380            wrap_guides.extend(settings.wrap_guides.iter().map(|guide| (*guide, false)))
21381        }
21382
21383        wrap_guides
21384    }
21385
21386    pub fn soft_wrap_mode(&self, cx: &App) -> SoftWrap {
21387        let settings = self.buffer.read(cx).language_settings(cx);
21388        let mode = self.soft_wrap_mode_override.unwrap_or(settings.soft_wrap);
21389        match mode {
21390            language_settings::SoftWrap::PreferLine | language_settings::SoftWrap::None => {
21391                SoftWrap::None
21392            }
21393            language_settings::SoftWrap::EditorWidth => SoftWrap::EditorWidth,
21394            language_settings::SoftWrap::PreferredLineLength => {
21395                SoftWrap::Column(settings.preferred_line_length)
21396            }
21397            language_settings::SoftWrap::Bounded => {
21398                SoftWrap::Bounded(settings.preferred_line_length)
21399            }
21400        }
21401    }
21402
21403    pub fn set_soft_wrap_mode(
21404        &mut self,
21405        mode: language_settings::SoftWrap,
21406        cx: &mut Context<Self>,
21407    ) {
21408        self.soft_wrap_mode_override = Some(mode);
21409        cx.notify();
21410    }
21411
21412    pub fn set_hard_wrap(&mut self, hard_wrap: Option<usize>, cx: &mut Context<Self>) {
21413        self.hard_wrap = hard_wrap;
21414        cx.notify();
21415    }
21416
21417    pub fn set_text_style_refinement(&mut self, style: TextStyleRefinement) {
21418        self.text_style_refinement = Some(style);
21419    }
21420
21421    /// called by the Element so we know what style we were most recently rendered with.
21422    pub fn set_style(&mut self, style: EditorStyle, window: &mut Window, cx: &mut Context<Self>) {
21423        // We intentionally do not inform the display map about the minimap style
21424        // so that wrapping is not recalculated and stays consistent for the editor
21425        // and its linked minimap.
21426        if !self.mode.is_minimap() {
21427            let font = style.text.font();
21428            let font_size = style.text.font_size.to_pixels(window.rem_size());
21429            let display_map = self
21430                .placeholder_display_map
21431                .as_ref()
21432                .filter(|_| self.is_empty(cx))
21433                .unwrap_or(&self.display_map);
21434
21435            display_map.update(cx, |map, cx| map.set_font(font, font_size, cx));
21436        }
21437        self.style = Some(style);
21438    }
21439
21440    pub fn style(&mut self, cx: &App) -> &EditorStyle {
21441        if self.style.is_none() {
21442            self.style = Some(self.create_style(cx));
21443        }
21444        self.style.as_ref().unwrap()
21445    }
21446
21447    // Called by the element. This method is not designed to be called outside of the editor
21448    // element's layout code because it does not notify when rewrapping is computed synchronously.
21449    pub(crate) fn set_wrap_width(&self, width: Option<Pixels>, cx: &mut App) -> bool {
21450        if self.is_empty(cx) {
21451            self.placeholder_display_map
21452                .as_ref()
21453                .map_or(false, |display_map| {
21454                    display_map.update(cx, |map, cx| map.set_wrap_width(width, cx))
21455                })
21456        } else {
21457            self.display_map
21458                .update(cx, |map, cx| map.set_wrap_width(width, cx))
21459        }
21460    }
21461
21462    pub fn set_soft_wrap(&mut self) {
21463        self.soft_wrap_mode_override = Some(language_settings::SoftWrap::EditorWidth)
21464    }
21465
21466    pub fn toggle_soft_wrap(&mut self, _: &ToggleSoftWrap, _: &mut Window, cx: &mut Context<Self>) {
21467        if self.soft_wrap_mode_override.is_some() {
21468            self.soft_wrap_mode_override.take();
21469        } else {
21470            let soft_wrap = match self.soft_wrap_mode(cx) {
21471                SoftWrap::GitDiff => return,
21472                SoftWrap::None => language_settings::SoftWrap::EditorWidth,
21473                SoftWrap::EditorWidth | SoftWrap::Column(_) | SoftWrap::Bounded(_) => {
21474                    language_settings::SoftWrap::None
21475                }
21476            };
21477            self.soft_wrap_mode_override = Some(soft_wrap);
21478        }
21479        cx.notify();
21480    }
21481
21482    pub fn toggle_tab_bar(&mut self, _: &ToggleTabBar, _: &mut Window, cx: &mut Context<Self>) {
21483        let Some(workspace) = self.workspace() else {
21484            return;
21485        };
21486        let fs = workspace.read(cx).app_state().fs.clone();
21487        let current_show = TabBarSettings::get_global(cx).show;
21488        update_settings_file(fs, cx, move |setting, _| {
21489            setting.tab_bar.get_or_insert_default().show = Some(!current_show);
21490        });
21491    }
21492
21493    pub fn toggle_indent_guides(
21494        &mut self,
21495        _: &ToggleIndentGuides,
21496        _: &mut Window,
21497        cx: &mut Context<Self>,
21498    ) {
21499        let currently_enabled = self.should_show_indent_guides().unwrap_or_else(|| {
21500            self.buffer
21501                .read(cx)
21502                .language_settings(cx)
21503                .indent_guides
21504                .enabled
21505        });
21506        self.show_indent_guides = Some(!currently_enabled);
21507        cx.notify();
21508    }
21509
21510    fn should_show_indent_guides(&self) -> Option<bool> {
21511        self.show_indent_guides
21512    }
21513
21514    pub fn disable_indent_guides_for_buffer(
21515        &mut self,
21516        buffer_id: BufferId,
21517        cx: &mut Context<Self>,
21518    ) {
21519        self.buffers_with_disabled_indent_guides.insert(buffer_id);
21520        cx.notify();
21521    }
21522
21523    pub fn has_indent_guides_disabled_for_buffer(&self, buffer_id: BufferId) -> bool {
21524        self.buffers_with_disabled_indent_guides
21525            .contains(&buffer_id)
21526    }
21527
21528    pub fn toggle_line_numbers(
21529        &mut self,
21530        _: &ToggleLineNumbers,
21531        _: &mut Window,
21532        cx: &mut Context<Self>,
21533    ) {
21534        let mut editor_settings = EditorSettings::get_global(cx).clone();
21535        editor_settings.gutter.line_numbers = !editor_settings.gutter.line_numbers;
21536        EditorSettings::override_global(editor_settings, cx);
21537    }
21538
21539    pub fn line_numbers_enabled(&self, cx: &App) -> bool {
21540        if let Some(show_line_numbers) = self.show_line_numbers {
21541            return show_line_numbers;
21542        }
21543        EditorSettings::get_global(cx).gutter.line_numbers
21544    }
21545
21546    pub fn relative_line_numbers(&self, cx: &App) -> RelativeLineNumbers {
21547        match (
21548            self.use_relative_line_numbers,
21549            EditorSettings::get_global(cx).relative_line_numbers,
21550        ) {
21551            (None, setting) => setting,
21552            (Some(false), _) => RelativeLineNumbers::Disabled,
21553            (Some(true), RelativeLineNumbers::Wrapped) => RelativeLineNumbers::Wrapped,
21554            (Some(true), _) => RelativeLineNumbers::Enabled,
21555        }
21556    }
21557
21558    pub fn toggle_relative_line_numbers(
21559        &mut self,
21560        _: &ToggleRelativeLineNumbers,
21561        _: &mut Window,
21562        cx: &mut Context<Self>,
21563    ) {
21564        let is_relative = self.relative_line_numbers(cx);
21565        self.set_relative_line_number(Some(!is_relative.enabled()), cx)
21566    }
21567
21568    pub fn set_relative_line_number(&mut self, is_relative: Option<bool>, cx: &mut Context<Self>) {
21569        self.use_relative_line_numbers = is_relative;
21570        cx.notify();
21571    }
21572
21573    pub fn set_show_gutter(&mut self, show_gutter: bool, cx: &mut Context<Self>) {
21574        self.show_gutter = show_gutter;
21575        cx.notify();
21576    }
21577
21578    pub fn set_show_scrollbars(&mut self, show: bool, cx: &mut Context<Self>) {
21579        self.show_scrollbars = ScrollbarAxes {
21580            horizontal: show,
21581            vertical: show,
21582        };
21583        cx.notify();
21584    }
21585
21586    pub fn set_show_vertical_scrollbar(&mut self, show: bool, cx: &mut Context<Self>) {
21587        self.show_scrollbars.vertical = show;
21588        cx.notify();
21589    }
21590
21591    pub fn set_show_horizontal_scrollbar(&mut self, show: bool, cx: &mut Context<Self>) {
21592        self.show_scrollbars.horizontal = show;
21593        cx.notify();
21594    }
21595
21596    pub fn set_minimap_visibility(
21597        &mut self,
21598        minimap_visibility: MinimapVisibility,
21599        window: &mut Window,
21600        cx: &mut Context<Self>,
21601    ) {
21602        if self.minimap_visibility != minimap_visibility {
21603            if minimap_visibility.visible() && self.minimap.is_none() {
21604                let minimap_settings = EditorSettings::get_global(cx).minimap;
21605                self.minimap =
21606                    self.create_minimap(minimap_settings.with_show_override(), window, cx);
21607            }
21608            self.minimap_visibility = minimap_visibility;
21609            cx.notify();
21610        }
21611    }
21612
21613    pub fn disable_scrollbars_and_minimap(&mut self, window: &mut Window, cx: &mut Context<Self>) {
21614        self.set_show_scrollbars(false, cx);
21615        self.set_minimap_visibility(MinimapVisibility::Disabled, window, cx);
21616    }
21617
21618    pub fn hide_minimap_by_default(&mut self, window: &mut Window, cx: &mut Context<Self>) {
21619        self.set_minimap_visibility(self.minimap_visibility.hidden(), window, cx);
21620    }
21621
21622    /// Normally the text in full mode and auto height editors is padded on the
21623    /// left side by roughly half a character width for improved hit testing.
21624    ///
21625    /// Use this method to disable this for cases where this is not wanted (e.g.
21626    /// if you want to align the editor text with some other text above or below)
21627    /// or if you want to add this padding to single-line editors.
21628    pub fn set_offset_content(&mut self, offset_content: bool, cx: &mut Context<Self>) {
21629        self.offset_content = offset_content;
21630        cx.notify();
21631    }
21632
21633    pub fn set_show_line_numbers(&mut self, show_line_numbers: bool, cx: &mut Context<Self>) {
21634        self.show_line_numbers = Some(show_line_numbers);
21635        cx.notify();
21636    }
21637
21638    pub fn disable_expand_excerpt_buttons(&mut self, cx: &mut Context<Self>) {
21639        self.disable_expand_excerpt_buttons = true;
21640        cx.notify();
21641    }
21642
21643    pub fn set_number_deleted_lines(&mut self, number: bool, cx: &mut Context<Self>) {
21644        self.number_deleted_lines = number;
21645        cx.notify();
21646    }
21647
21648    pub fn set_delegate_expand_excerpts(&mut self, delegate: bool) {
21649        self.delegate_expand_excerpts = delegate;
21650    }
21651
21652    pub fn set_delegate_stage_and_restore(&mut self, delegate: bool) {
21653        self.delegate_stage_and_restore = delegate;
21654    }
21655
21656    pub fn set_delegate_open_excerpts(&mut self, delegate: bool) {
21657        self.delegate_open_excerpts = delegate;
21658    }
21659
21660    pub fn set_on_local_selections_changed(
21661        &mut self,
21662        callback: Option<Box<dyn Fn(Point, &mut Window, &mut Context<Self>) + 'static>>,
21663    ) {
21664        self.on_local_selections_changed = callback;
21665    }
21666
21667    pub fn set_suppress_selection_callback(&mut self, suppress: bool) {
21668        self.suppress_selection_callback = suppress;
21669    }
21670
21671    pub fn set_show_git_diff_gutter(&mut self, show_git_diff_gutter: bool, cx: &mut Context<Self>) {
21672        self.show_git_diff_gutter = Some(show_git_diff_gutter);
21673        cx.notify();
21674    }
21675
21676    pub fn set_show_code_actions(&mut self, show_code_actions: bool, cx: &mut Context<Self>) {
21677        self.show_code_actions = Some(show_code_actions);
21678        cx.notify();
21679    }
21680
21681    pub fn set_show_runnables(&mut self, show_runnables: bool, cx: &mut Context<Self>) {
21682        self.show_runnables = Some(show_runnables);
21683        cx.notify();
21684    }
21685
21686    pub fn set_show_breakpoints(&mut self, show_breakpoints: bool, cx: &mut Context<Self>) {
21687        self.show_breakpoints = Some(show_breakpoints);
21688        cx.notify();
21689    }
21690
21691    pub fn set_show_diff_review_button(&mut self, show: bool, cx: &mut Context<Self>) {
21692        self.show_diff_review_button = show;
21693        cx.notify();
21694    }
21695
21696    pub fn show_diff_review_button(&self) -> bool {
21697        self.show_diff_review_button
21698    }
21699
21700    pub fn render_diff_review_button(
21701        &self,
21702        display_row: DisplayRow,
21703        width: Pixels,
21704        cx: &mut Context<Self>,
21705    ) -> impl IntoElement {
21706        let text_color = cx.theme().colors().text;
21707        let icon_color = cx.theme().colors().icon_accent;
21708
21709        h_flex()
21710            .id("diff_review_button")
21711            .cursor_pointer()
21712            .w(width - px(1.))
21713            .h(relative(0.9))
21714            .justify_center()
21715            .rounded_sm()
21716            .border_1()
21717            .border_color(text_color.opacity(0.1))
21718            .bg(text_color.opacity(0.15))
21719            .hover(|s| {
21720                s.bg(icon_color.opacity(0.4))
21721                    .border_color(icon_color.opacity(0.5))
21722            })
21723            .child(Icon::new(IconName::Plus).size(IconSize::Small))
21724            .tooltip(Tooltip::text("Add Review (drag to select multiple lines)"))
21725            .on_mouse_down(
21726                gpui::MouseButton::Left,
21727                cx.listener(move |editor, _event: &gpui::MouseDownEvent, window, cx| {
21728                    editor.start_diff_review_drag(display_row, window, cx);
21729                }),
21730            )
21731    }
21732
21733    pub fn start_diff_review_drag(
21734        &mut self,
21735        display_row: DisplayRow,
21736        window: &mut Window,
21737        cx: &mut Context<Self>,
21738    ) {
21739        let snapshot = self.snapshot(window, cx);
21740        let point = snapshot
21741            .display_snapshot
21742            .display_point_to_point(DisplayPoint::new(display_row, 0), Bias::Left);
21743        let anchor = snapshot.buffer_snapshot().anchor_before(point);
21744        self.diff_review_drag_state = Some(DiffReviewDragState {
21745            start_anchor: anchor,
21746            current_anchor: anchor,
21747        });
21748        cx.notify();
21749    }
21750
21751    pub fn update_diff_review_drag(
21752        &mut self,
21753        display_row: DisplayRow,
21754        window: &mut Window,
21755        cx: &mut Context<Self>,
21756    ) {
21757        if self.diff_review_drag_state.is_none() {
21758            return;
21759        }
21760        let snapshot = self.snapshot(window, cx);
21761        let point = snapshot
21762            .display_snapshot
21763            .display_point_to_point(display_row.as_display_point(), Bias::Left);
21764        let anchor = snapshot.buffer_snapshot().anchor_before(point);
21765        if let Some(drag_state) = &mut self.diff_review_drag_state {
21766            drag_state.current_anchor = anchor;
21767            cx.notify();
21768        }
21769    }
21770
21771    pub fn end_diff_review_drag(&mut self, window: &mut Window, cx: &mut Context<Self>) {
21772        if let Some(drag_state) = self.diff_review_drag_state.take() {
21773            let snapshot = self.snapshot(window, cx);
21774            let range = drag_state.row_range(&snapshot.display_snapshot);
21775            self.show_diff_review_overlay(*range.start()..*range.end(), window, cx);
21776        }
21777        cx.notify();
21778    }
21779
21780    pub fn cancel_diff_review_drag(&mut self, cx: &mut Context<Self>) {
21781        self.diff_review_drag_state = None;
21782        cx.notify();
21783    }
21784
21785    /// Calculates the appropriate block height for the diff review overlay.
21786    /// Height is in lines: 2 for input row, 1 for header when comments exist,
21787    /// and 2 lines per comment when expanded.
21788    fn calculate_overlay_height(
21789        &self,
21790        hunk_key: &DiffHunkKey,
21791        comments_expanded: bool,
21792        snapshot: &MultiBufferSnapshot,
21793    ) -> u32 {
21794        let comment_count = self.hunk_comment_count(hunk_key, snapshot);
21795        let base_height: u32 = 2; // Input row with avatar and buttons
21796
21797        if comment_count == 0 {
21798            base_height
21799        } else if comments_expanded {
21800            // Header (1 line) + 2 lines per comment
21801            base_height + 1 + (comment_count as u32 * 2)
21802        } else {
21803            // Just header when collapsed
21804            base_height + 1
21805        }
21806    }
21807
21808    pub fn show_diff_review_overlay(
21809        &mut self,
21810        display_range: Range<DisplayRow>,
21811        window: &mut Window,
21812        cx: &mut Context<Self>,
21813    ) {
21814        let Range { start, end } = display_range.sorted();
21815
21816        let buffer_snapshot = self.buffer.read(cx).snapshot(cx);
21817        let editor_snapshot = self.snapshot(window, cx);
21818
21819        // Convert display rows to multibuffer points
21820        let start_point = editor_snapshot
21821            .display_snapshot
21822            .display_point_to_point(start.as_display_point(), Bias::Left);
21823        let end_point = editor_snapshot
21824            .display_snapshot
21825            .display_point_to_point(end.as_display_point(), Bias::Left);
21826        let end_multi_buffer_row = MultiBufferRow(end_point.row);
21827
21828        // Create anchor range for the selected lines (start of first line to end of last line)
21829        let line_end = Point::new(
21830            end_point.row,
21831            buffer_snapshot.line_len(end_multi_buffer_row),
21832        );
21833        let anchor_range =
21834            buffer_snapshot.anchor_after(start_point)..buffer_snapshot.anchor_before(line_end);
21835
21836        // Compute the hunk key for this display row
21837        let file_path = buffer_snapshot
21838            .file_at(start_point)
21839            .map(|file: &Arc<dyn language::File>| file.path().clone())
21840            .unwrap_or_else(|| Arc::from(util::rel_path::RelPath::empty()));
21841        let hunk_start_anchor = buffer_snapshot.anchor_before(start_point);
21842        let new_hunk_key = DiffHunkKey {
21843            file_path,
21844            hunk_start_anchor,
21845        };
21846
21847        // Check if we already have an overlay for this hunk
21848        if let Some(existing_overlay) = self.diff_review_overlays.iter().find(|overlay| {
21849            Self::hunk_keys_match(&overlay.hunk_key, &new_hunk_key, &buffer_snapshot)
21850        }) {
21851            // Just focus the existing overlay's prompt editor
21852            let focus_handle = existing_overlay.prompt_editor.focus_handle(cx);
21853            window.focus(&focus_handle, cx);
21854            return;
21855        }
21856
21857        // Dismiss overlays that have no comments for their hunks
21858        self.dismiss_overlays_without_comments(cx);
21859
21860        // Get the current user's avatar URI from the project's user_store
21861        let user_avatar_uri = self.project.as_ref().and_then(|project| {
21862            let user_store = project.read(cx).user_store();
21863            user_store
21864                .read(cx)
21865                .current_user()
21866                .map(|user| user.avatar_uri.clone())
21867        });
21868
21869        // Create anchor at the end of the last row so the block appears immediately below it
21870        // Use multibuffer coordinates for anchor creation
21871        let line_len = buffer_snapshot.line_len(end_multi_buffer_row);
21872        let anchor = buffer_snapshot.anchor_after(Point::new(end_multi_buffer_row.0, line_len));
21873
21874        // Use the hunk key we already computed
21875        let hunk_key = new_hunk_key;
21876
21877        // Create the prompt editor for the review input
21878        let prompt_editor = cx.new(|cx| {
21879            let mut editor = Editor::single_line(window, cx);
21880            editor.set_placeholder_text("Add a review comment...", window, cx);
21881            editor
21882        });
21883
21884        // Register the Newline action on the prompt editor to submit the review
21885        let parent_editor = cx.entity().downgrade();
21886        let subscription = prompt_editor.update(cx, |prompt_editor, _cx| {
21887            prompt_editor.register_action({
21888                let parent_editor = parent_editor.clone();
21889                move |_: &crate::actions::Newline, window, cx| {
21890                    if let Some(editor) = parent_editor.upgrade() {
21891                        editor.update(cx, |editor, cx| {
21892                            editor.submit_diff_review_comment(window, cx);
21893                        });
21894                    }
21895                }
21896            })
21897        });
21898
21899        // Calculate initial height based on existing comments for this hunk
21900        let initial_height = self.calculate_overlay_height(&hunk_key, true, &buffer_snapshot);
21901
21902        // Create the overlay block
21903        let prompt_editor_for_render = prompt_editor.clone();
21904        let hunk_key_for_render = hunk_key.clone();
21905        let editor_handle = cx.entity().downgrade();
21906        let block = BlockProperties {
21907            style: BlockStyle::Sticky,
21908            placement: BlockPlacement::Below(anchor),
21909            height: Some(initial_height),
21910            render: Arc::new(move |cx| {
21911                Self::render_diff_review_overlay(
21912                    &prompt_editor_for_render,
21913                    &hunk_key_for_render,
21914                    &editor_handle,
21915                    cx,
21916                )
21917            }),
21918            priority: 0,
21919        };
21920
21921        let block_ids = self.insert_blocks([block], None, cx);
21922        let Some(block_id) = block_ids.into_iter().next() else {
21923            log::error!("Failed to insert diff review overlay block");
21924            return;
21925        };
21926
21927        self.diff_review_overlays.push(DiffReviewOverlay {
21928            anchor_range,
21929            block_id,
21930            prompt_editor: prompt_editor.clone(),
21931            hunk_key,
21932            comments_expanded: true,
21933            inline_edit_editors: HashMap::default(),
21934            inline_edit_subscriptions: HashMap::default(),
21935            user_avatar_uri,
21936            _subscription: subscription,
21937        });
21938
21939        // Focus the prompt editor
21940        let focus_handle = prompt_editor.focus_handle(cx);
21941        window.focus(&focus_handle, cx);
21942
21943        cx.notify();
21944    }
21945
21946    /// Dismisses all diff review overlays.
21947    pub fn dismiss_all_diff_review_overlays(&mut self, cx: &mut Context<Self>) {
21948        if self.diff_review_overlays.is_empty() {
21949            return;
21950        }
21951        let block_ids: HashSet<_> = self
21952            .diff_review_overlays
21953            .drain(..)
21954            .map(|overlay| overlay.block_id)
21955            .collect();
21956        self.remove_blocks(block_ids, None, cx);
21957        cx.notify();
21958    }
21959
21960    /// Dismisses overlays that have no comments stored for their hunks.
21961    /// Keeps overlays that have at least one comment.
21962    fn dismiss_overlays_without_comments(&mut self, cx: &mut Context<Self>) {
21963        let snapshot = self.buffer.read(cx).snapshot(cx);
21964
21965        // First, compute which overlays have comments (to avoid borrow issues with retain)
21966        let overlays_with_comments: Vec<bool> = self
21967            .diff_review_overlays
21968            .iter()
21969            .map(|overlay| self.hunk_comment_count(&overlay.hunk_key, &snapshot) > 0)
21970            .collect();
21971
21972        // Now collect block IDs to remove and retain overlays
21973        let mut block_ids_to_remove = HashSet::default();
21974        let mut index = 0;
21975        self.diff_review_overlays.retain(|overlay| {
21976            let has_comments = overlays_with_comments[index];
21977            index += 1;
21978            if !has_comments {
21979                block_ids_to_remove.insert(overlay.block_id);
21980            }
21981            has_comments
21982        });
21983
21984        if !block_ids_to_remove.is_empty() {
21985            self.remove_blocks(block_ids_to_remove, None, cx);
21986            cx.notify();
21987        }
21988    }
21989
21990    /// Refreshes the diff review overlay block to update its height and render function.
21991    /// Uses resize_blocks and replace_blocks to avoid visual flicker from remove+insert.
21992    fn refresh_diff_review_overlay_height(
21993        &mut self,
21994        hunk_key: &DiffHunkKey,
21995        _window: &mut Window,
21996        cx: &mut Context<Self>,
21997    ) {
21998        // Extract all needed data from overlay first to avoid borrow conflicts
21999        let snapshot = self.buffer.read(cx).snapshot(cx);
22000        let (comments_expanded, block_id, prompt_editor) = {
22001            let Some(overlay) = self
22002                .diff_review_overlays
22003                .iter()
22004                .find(|overlay| Self::hunk_keys_match(&overlay.hunk_key, hunk_key, &snapshot))
22005            else {
22006                return;
22007            };
22008
22009            (
22010                overlay.comments_expanded,
22011                overlay.block_id,
22012                overlay.prompt_editor.clone(),
22013            )
22014        };
22015
22016        // Calculate new height
22017        let snapshot = self.buffer.read(cx).snapshot(cx);
22018        let new_height = self.calculate_overlay_height(hunk_key, comments_expanded, &snapshot);
22019
22020        // Update the block height using resize_blocks (avoids flicker)
22021        let mut heights = HashMap::default();
22022        heights.insert(block_id, new_height);
22023        self.resize_blocks(heights, None, cx);
22024
22025        // Update the render function using replace_blocks (avoids flicker)
22026        let hunk_key_for_render = hunk_key.clone();
22027        let editor_handle = cx.entity().downgrade();
22028        let render: Arc<dyn Fn(&mut BlockContext) -> AnyElement + Send + Sync> =
22029            Arc::new(move |cx| {
22030                Self::render_diff_review_overlay(
22031                    &prompt_editor,
22032                    &hunk_key_for_render,
22033                    &editor_handle,
22034                    cx,
22035                )
22036            });
22037
22038        let mut renderers = HashMap::default();
22039        renderers.insert(block_id, render);
22040        self.replace_blocks(renderers, None, cx);
22041    }
22042
22043    /// Action handler for SubmitDiffReviewComment.
22044    pub fn submit_diff_review_comment_action(
22045        &mut self,
22046        _: &SubmitDiffReviewComment,
22047        window: &mut Window,
22048        cx: &mut Context<Self>,
22049    ) {
22050        self.submit_diff_review_comment(window, cx);
22051    }
22052
22053    /// Stores the diff review comment locally.
22054    /// Comments are stored per-hunk and can later be batch-submitted to the Agent panel.
22055    pub fn submit_diff_review_comment(&mut self, window: &mut Window, cx: &mut Context<Self>) {
22056        // Find the overlay that currently has focus
22057        let overlay_index = self
22058            .diff_review_overlays
22059            .iter()
22060            .position(|overlay| overlay.prompt_editor.focus_handle(cx).is_focused(window));
22061        let Some(overlay_index) = overlay_index else {
22062            return;
22063        };
22064        let overlay = &self.diff_review_overlays[overlay_index];
22065
22066        let comment_text = overlay.prompt_editor.read(cx).text(cx).trim().to_string();
22067        if comment_text.is_empty() {
22068            return;
22069        }
22070
22071        let anchor_range = overlay.anchor_range.clone();
22072        let hunk_key = overlay.hunk_key.clone();
22073
22074        self.add_review_comment(hunk_key.clone(), comment_text, anchor_range, cx);
22075
22076        // Clear the prompt editor but keep the overlay open
22077        if let Some(overlay) = self.diff_review_overlays.get(overlay_index) {
22078            overlay.prompt_editor.update(cx, |editor, cx| {
22079                editor.clear(window, cx);
22080            });
22081        }
22082
22083        // Refresh the overlay to update the block height for the new comment
22084        self.refresh_diff_review_overlay_height(&hunk_key, window, cx);
22085
22086        cx.notify();
22087    }
22088
22089    /// Returns the prompt editor for the diff review overlay, if one is active.
22090    /// This is primarily used for testing.
22091    pub fn diff_review_prompt_editor(&self) -> Option<&Entity<Editor>> {
22092        self.diff_review_overlays
22093            .first()
22094            .map(|overlay| &overlay.prompt_editor)
22095    }
22096
22097    /// Returns the line range for the first diff review overlay, if one is active.
22098    /// Returns (start_row, end_row) as physical line numbers in the underlying file.
22099    pub fn diff_review_line_range(&self, cx: &App) -> Option<(u32, u32)> {
22100        let overlay = self.diff_review_overlays.first()?;
22101        let snapshot = self.buffer.read(cx).snapshot(cx);
22102        let start_point = overlay.anchor_range.start.to_point(&snapshot);
22103        let end_point = overlay.anchor_range.end.to_point(&snapshot);
22104        let start_row = snapshot
22105            .point_to_buffer_point(start_point)
22106            .map(|(_, p, _)| p.row)
22107            .unwrap_or(start_point.row);
22108        let end_row = snapshot
22109            .point_to_buffer_point(end_point)
22110            .map(|(_, p, _)| p.row)
22111            .unwrap_or(end_point.row);
22112        Some((start_row, end_row))
22113    }
22114
22115    /// Sets whether the comments section is expanded in the diff review overlay.
22116    /// This is primarily used for testing.
22117    pub fn set_diff_review_comments_expanded(&mut self, expanded: bool, cx: &mut Context<Self>) {
22118        for overlay in &mut self.diff_review_overlays {
22119            overlay.comments_expanded = expanded;
22120        }
22121        cx.notify();
22122    }
22123
22124    /// Compares two DiffHunkKeys for equality by resolving their anchors.
22125    fn hunk_keys_match(a: &DiffHunkKey, b: &DiffHunkKey, snapshot: &MultiBufferSnapshot) -> bool {
22126        a.file_path == b.file_path
22127            && a.hunk_start_anchor.to_point(snapshot) == b.hunk_start_anchor.to_point(snapshot)
22128    }
22129
22130    /// Returns comments for a specific hunk, ordered by creation time.
22131    pub fn comments_for_hunk<'a>(
22132        &'a self,
22133        key: &DiffHunkKey,
22134        snapshot: &MultiBufferSnapshot,
22135    ) -> &'a [StoredReviewComment] {
22136        let key_point = key.hunk_start_anchor.to_point(snapshot);
22137        self.stored_review_comments
22138            .iter()
22139            .find(|(k, _)| {
22140                k.file_path == key.file_path && k.hunk_start_anchor.to_point(snapshot) == key_point
22141            })
22142            .map(|(_, comments)| comments.as_slice())
22143            .unwrap_or(&[])
22144    }
22145
22146    /// Returns the total count of stored review comments across all hunks.
22147    pub fn total_review_comment_count(&self) -> usize {
22148        self.stored_review_comments
22149            .iter()
22150            .map(|(_, v)| v.len())
22151            .sum()
22152    }
22153
22154    /// Returns the count of comments for a specific hunk.
22155    pub fn hunk_comment_count(&self, key: &DiffHunkKey, snapshot: &MultiBufferSnapshot) -> usize {
22156        let key_point = key.hunk_start_anchor.to_point(snapshot);
22157        self.stored_review_comments
22158            .iter()
22159            .find(|(k, _)| {
22160                k.file_path == key.file_path && k.hunk_start_anchor.to_point(snapshot) == key_point
22161            })
22162            .map(|(_, v)| v.len())
22163            .unwrap_or(0)
22164    }
22165
22166    /// Adds a new review comment to a specific hunk.
22167    pub fn add_review_comment(
22168        &mut self,
22169        hunk_key: DiffHunkKey,
22170        comment: String,
22171        anchor_range: Range<Anchor>,
22172        cx: &mut Context<Self>,
22173    ) -> usize {
22174        let id = self.next_review_comment_id;
22175        self.next_review_comment_id += 1;
22176
22177        let stored_comment = StoredReviewComment::new(id, comment, anchor_range);
22178
22179        let snapshot = self.buffer.read(cx).snapshot(cx);
22180        let key_point = hunk_key.hunk_start_anchor.to_point(&snapshot);
22181
22182        // Find existing entry for this hunk or add a new one
22183        if let Some((_, comments)) = self.stored_review_comments.iter_mut().find(|(k, _)| {
22184            k.file_path == hunk_key.file_path
22185                && k.hunk_start_anchor.to_point(&snapshot) == key_point
22186        }) {
22187            comments.push(stored_comment);
22188        } else {
22189            self.stored_review_comments
22190                .push((hunk_key, vec![stored_comment]));
22191        }
22192
22193        cx.emit(EditorEvent::ReviewCommentsChanged {
22194            total_count: self.total_review_comment_count(),
22195        });
22196        cx.notify();
22197        id
22198    }
22199
22200    /// Removes a review comment by ID from any hunk.
22201    pub fn remove_review_comment(&mut self, id: usize, cx: &mut Context<Self>) -> bool {
22202        for (_, comments) in self.stored_review_comments.iter_mut() {
22203            if let Some(index) = comments.iter().position(|c| c.id == id) {
22204                comments.remove(index);
22205                cx.emit(EditorEvent::ReviewCommentsChanged {
22206                    total_count: self.total_review_comment_count(),
22207                });
22208                cx.notify();
22209                return true;
22210            }
22211        }
22212        false
22213    }
22214
22215    /// Updates a review comment's text by ID.
22216    pub fn update_review_comment(
22217        &mut self,
22218        id: usize,
22219        new_comment: String,
22220        cx: &mut Context<Self>,
22221    ) -> bool {
22222        for (_, comments) in self.stored_review_comments.iter_mut() {
22223            if let Some(comment) = comments.iter_mut().find(|c| c.id == id) {
22224                comment.comment = new_comment;
22225                comment.is_editing = false;
22226                cx.emit(EditorEvent::ReviewCommentsChanged {
22227                    total_count: self.total_review_comment_count(),
22228                });
22229                cx.notify();
22230                return true;
22231            }
22232        }
22233        false
22234    }
22235
22236    /// Sets a comment's editing state.
22237    pub fn set_comment_editing(&mut self, id: usize, is_editing: bool, cx: &mut Context<Self>) {
22238        for (_, comments) in self.stored_review_comments.iter_mut() {
22239            if let Some(comment) = comments.iter_mut().find(|c| c.id == id) {
22240                comment.is_editing = is_editing;
22241                cx.notify();
22242                return;
22243            }
22244        }
22245    }
22246
22247    /// Takes all stored comments from all hunks, clearing the storage.
22248    /// Returns a Vec of (hunk_key, comments) pairs.
22249    pub fn take_all_review_comments(
22250        &mut self,
22251        cx: &mut Context<Self>,
22252    ) -> Vec<(DiffHunkKey, Vec<StoredReviewComment>)> {
22253        // Dismiss all overlays when taking comments (e.g., when sending to agent)
22254        self.dismiss_all_diff_review_overlays(cx);
22255        let comments = std::mem::take(&mut self.stored_review_comments);
22256        // Reset the ID counter since all comments have been taken
22257        self.next_review_comment_id = 0;
22258        cx.emit(EditorEvent::ReviewCommentsChanged { total_count: 0 });
22259        cx.notify();
22260        comments
22261    }
22262
22263    /// Removes review comments whose anchors are no longer valid or whose
22264    /// associated diff hunks no longer exist.
22265    ///
22266    /// This should be called when the buffer changes to prevent orphaned comments
22267    /// from accumulating.
22268    pub fn cleanup_orphaned_review_comments(&mut self, cx: &mut Context<Self>) {
22269        let snapshot = self.buffer.read(cx).snapshot(cx);
22270        let original_count = self.total_review_comment_count();
22271
22272        // Remove comments with invalid hunk anchors
22273        self.stored_review_comments
22274            .retain(|(hunk_key, _)| hunk_key.hunk_start_anchor.is_valid(&snapshot));
22275
22276        // Also clean up individual comments with invalid anchor ranges
22277        for (_, comments) in &mut self.stored_review_comments {
22278            comments.retain(|comment| {
22279                comment.range.start.is_valid(&snapshot) && comment.range.end.is_valid(&snapshot)
22280            });
22281        }
22282
22283        // Remove empty hunk entries
22284        self.stored_review_comments
22285            .retain(|(_, comments)| !comments.is_empty());
22286
22287        let new_count = self.total_review_comment_count();
22288        if new_count != original_count {
22289            cx.emit(EditorEvent::ReviewCommentsChanged {
22290                total_count: new_count,
22291            });
22292            cx.notify();
22293        }
22294    }
22295
22296    /// Toggles the expanded state of the comments section in the overlay.
22297    pub fn toggle_review_comments_expanded(
22298        &mut self,
22299        _: &ToggleReviewCommentsExpanded,
22300        window: &mut Window,
22301        cx: &mut Context<Self>,
22302    ) {
22303        // Find the overlay that currently has focus, or use the first one
22304        let overlay_info = self.diff_review_overlays.iter_mut().find_map(|overlay| {
22305            if overlay.prompt_editor.focus_handle(cx).is_focused(window) {
22306                overlay.comments_expanded = !overlay.comments_expanded;
22307                Some(overlay.hunk_key.clone())
22308            } else {
22309                None
22310            }
22311        });
22312
22313        // If no focused overlay found, toggle the first one
22314        let hunk_key = overlay_info.or_else(|| {
22315            self.diff_review_overlays.first_mut().map(|overlay| {
22316                overlay.comments_expanded = !overlay.comments_expanded;
22317                overlay.hunk_key.clone()
22318            })
22319        });
22320
22321        if let Some(hunk_key) = hunk_key {
22322            self.refresh_diff_review_overlay_height(&hunk_key, window, cx);
22323            cx.notify();
22324        }
22325    }
22326
22327    /// Handles the EditReviewComment action - sets a comment into editing mode.
22328    pub fn edit_review_comment(
22329        &mut self,
22330        action: &EditReviewComment,
22331        window: &mut Window,
22332        cx: &mut Context<Self>,
22333    ) {
22334        let comment_id = action.id;
22335
22336        // Set the comment to editing mode
22337        self.set_comment_editing(comment_id, true, cx);
22338
22339        // Find the overlay that contains this comment and create an inline editor if needed
22340        // First, find which hunk this comment belongs to
22341        let hunk_key = self
22342            .stored_review_comments
22343            .iter()
22344            .find_map(|(key, comments)| {
22345                if comments.iter().any(|c| c.id == comment_id) {
22346                    Some(key.clone())
22347                } else {
22348                    None
22349                }
22350            });
22351
22352        let snapshot = self.buffer.read(cx).snapshot(cx);
22353        if let Some(hunk_key) = hunk_key {
22354            if let Some(overlay) = self
22355                .diff_review_overlays
22356                .iter_mut()
22357                .find(|overlay| Self::hunk_keys_match(&overlay.hunk_key, &hunk_key, &snapshot))
22358            {
22359                if let std::collections::hash_map::Entry::Vacant(entry) =
22360                    overlay.inline_edit_editors.entry(comment_id)
22361                {
22362                    // Find the comment text
22363                    let comment_text = self
22364                        .stored_review_comments
22365                        .iter()
22366                        .flat_map(|(_, comments)| comments)
22367                        .find(|c| c.id == comment_id)
22368                        .map(|c| c.comment.clone())
22369                        .unwrap_or_default();
22370
22371                    // Create inline editor
22372                    let parent_editor = cx.entity().downgrade();
22373                    let inline_editor = cx.new(|cx| {
22374                        let mut editor = Editor::single_line(window, cx);
22375                        editor.set_text(&*comment_text, window, cx);
22376                        // Select all text for easy replacement
22377                        editor.select_all(&crate::actions::SelectAll, window, cx);
22378                        editor
22379                    });
22380
22381                    // Register the Newline action to confirm the edit
22382                    let subscription = inline_editor.update(cx, |inline_editor, _cx| {
22383                        inline_editor.register_action({
22384                            let parent_editor = parent_editor.clone();
22385                            move |_: &crate::actions::Newline, window, cx| {
22386                                if let Some(editor) = parent_editor.upgrade() {
22387                                    editor.update(cx, |editor, cx| {
22388                                        editor.confirm_edit_review_comment(comment_id, window, cx);
22389                                    });
22390                                }
22391                            }
22392                        })
22393                    });
22394
22395                    // Store the subscription to keep the action handler alive
22396                    overlay
22397                        .inline_edit_subscriptions
22398                        .insert(comment_id, subscription);
22399
22400                    // Focus the inline editor
22401                    let focus_handle = inline_editor.focus_handle(cx);
22402                    window.focus(&focus_handle, cx);
22403
22404                    entry.insert(inline_editor);
22405                }
22406            }
22407        }
22408
22409        cx.notify();
22410    }
22411
22412    /// Confirms an inline edit of a review comment.
22413    pub fn confirm_edit_review_comment(
22414        &mut self,
22415        comment_id: usize,
22416        _window: &mut Window,
22417        cx: &mut Context<Self>,
22418    ) {
22419        // Get the new text from the inline editor
22420        // Find the overlay containing this comment's inline editor
22421        let snapshot = self.buffer.read(cx).snapshot(cx);
22422        let hunk_key = self
22423            .stored_review_comments
22424            .iter()
22425            .find_map(|(key, comments)| {
22426                if comments.iter().any(|c| c.id == comment_id) {
22427                    Some(key.clone())
22428                } else {
22429                    None
22430                }
22431            });
22432
22433        let new_text = hunk_key
22434            .as_ref()
22435            .and_then(|hunk_key| {
22436                self.diff_review_overlays
22437                    .iter()
22438                    .find(|overlay| Self::hunk_keys_match(&overlay.hunk_key, hunk_key, &snapshot))
22439            })
22440            .as_ref()
22441            .and_then(|overlay| overlay.inline_edit_editors.get(&comment_id))
22442            .map(|editor| editor.read(cx).text(cx).trim().to_string());
22443
22444        if let Some(new_text) = new_text {
22445            if !new_text.is_empty() {
22446                self.update_review_comment(comment_id, new_text, cx);
22447            }
22448        }
22449
22450        // Remove the inline editor and its subscription
22451        if let Some(hunk_key) = hunk_key {
22452            if let Some(overlay) = self
22453                .diff_review_overlays
22454                .iter_mut()
22455                .find(|overlay| Self::hunk_keys_match(&overlay.hunk_key, &hunk_key, &snapshot))
22456            {
22457                overlay.inline_edit_editors.remove(&comment_id);
22458                overlay.inline_edit_subscriptions.remove(&comment_id);
22459            }
22460        }
22461
22462        // Clear editing state
22463        self.set_comment_editing(comment_id, false, cx);
22464    }
22465
22466    /// Cancels an inline edit of a review comment.
22467    pub fn cancel_edit_review_comment(
22468        &mut self,
22469        comment_id: usize,
22470        _window: &mut Window,
22471        cx: &mut Context<Self>,
22472    ) {
22473        // Find which hunk this comment belongs to
22474        let hunk_key = self
22475            .stored_review_comments
22476            .iter()
22477            .find_map(|(key, comments)| {
22478                if comments.iter().any(|c| c.id == comment_id) {
22479                    Some(key.clone())
22480                } else {
22481                    None
22482                }
22483            });
22484
22485        // Remove the inline editor and its subscription
22486        if let Some(hunk_key) = hunk_key {
22487            let snapshot = self.buffer.read(cx).snapshot(cx);
22488            if let Some(overlay) = self
22489                .diff_review_overlays
22490                .iter_mut()
22491                .find(|overlay| Self::hunk_keys_match(&overlay.hunk_key, &hunk_key, &snapshot))
22492            {
22493                overlay.inline_edit_editors.remove(&comment_id);
22494                overlay.inline_edit_subscriptions.remove(&comment_id);
22495            }
22496        }
22497
22498        // Clear editing state
22499        self.set_comment_editing(comment_id, false, cx);
22500    }
22501
22502    /// Action handler for ConfirmEditReviewComment.
22503    pub fn confirm_edit_review_comment_action(
22504        &mut self,
22505        action: &ConfirmEditReviewComment,
22506        window: &mut Window,
22507        cx: &mut Context<Self>,
22508    ) {
22509        self.confirm_edit_review_comment(action.id, window, cx);
22510    }
22511
22512    /// Action handler for CancelEditReviewComment.
22513    pub fn cancel_edit_review_comment_action(
22514        &mut self,
22515        action: &CancelEditReviewComment,
22516        window: &mut Window,
22517        cx: &mut Context<Self>,
22518    ) {
22519        self.cancel_edit_review_comment(action.id, window, cx);
22520    }
22521
22522    /// Handles the DeleteReviewComment action - removes a comment.
22523    pub fn delete_review_comment(
22524        &mut self,
22525        action: &DeleteReviewComment,
22526        window: &mut Window,
22527        cx: &mut Context<Self>,
22528    ) {
22529        // Get the hunk key before removing the comment
22530        // Find the hunk key from the comment itself
22531        let comment_id = action.id;
22532        let hunk_key = self
22533            .stored_review_comments
22534            .iter()
22535            .find_map(|(key, comments)| {
22536                if comments.iter().any(|c| c.id == comment_id) {
22537                    Some(key.clone())
22538                } else {
22539                    None
22540                }
22541            });
22542
22543        // Also get it from the overlay for refresh purposes
22544        let overlay_hunk_key = self
22545            .diff_review_overlays
22546            .first()
22547            .map(|o| o.hunk_key.clone());
22548
22549        self.remove_review_comment(action.id, cx);
22550
22551        // Refresh the overlay height after removing a comment
22552        if let Some(hunk_key) = hunk_key.or(overlay_hunk_key) {
22553            self.refresh_diff_review_overlay_height(&hunk_key, window, cx);
22554        }
22555    }
22556
22557    fn render_diff_review_overlay(
22558        prompt_editor: &Entity<Editor>,
22559        hunk_key: &DiffHunkKey,
22560        editor_handle: &WeakEntity<Editor>,
22561        cx: &mut BlockContext,
22562    ) -> AnyElement {
22563        fn format_line_ranges(ranges: &[(u32, u32)]) -> Option<String> {
22564            if ranges.is_empty() {
22565                return None;
22566            }
22567            let formatted: Vec<String> = ranges
22568                .iter()
22569                .map(|(start, end)| {
22570                    let start_line = start + 1;
22571                    let end_line = end + 1;
22572                    if start_line == end_line {
22573                        format!("Line {start_line}")
22574                    } else {
22575                        format!("Lines {start_line}-{end_line}")
22576                    }
22577                })
22578                .collect();
22579            // Don't show label for single line in single excerpt
22580            if ranges.len() == 1 && ranges[0].0 == ranges[0].1 {
22581                return None;
22582            }
22583            Some(formatted.join(""))
22584        }
22585
22586        let theme = cx.theme();
22587        let colors = theme.colors();
22588
22589        let (comments, comments_expanded, inline_editors, user_avatar_uri, line_ranges) =
22590            editor_handle
22591                .upgrade()
22592                .map(|editor| {
22593                    let editor = editor.read(cx);
22594                    let snapshot = editor.buffer().read(cx).snapshot(cx);
22595                    let comments = editor.comments_for_hunk(hunk_key, &snapshot).to_vec();
22596                    let (expanded, editors, avatar_uri, line_ranges) = editor
22597                        .diff_review_overlays
22598                        .iter()
22599                        .find(|overlay| {
22600                            Editor::hunk_keys_match(&overlay.hunk_key, hunk_key, &snapshot)
22601                        })
22602                        .map(|o| {
22603                            let start_point = o.anchor_range.start.to_point(&snapshot);
22604                            let end_point = o.anchor_range.end.to_point(&snapshot);
22605                            // Get line ranges per excerpt to detect discontinuities
22606                            let buffer_ranges =
22607                                snapshot.range_to_buffer_ranges(start_point..end_point);
22608                            let ranges: Vec<(u32, u32)> = buffer_ranges
22609                                .iter()
22610                                .map(|(buffer, range, _)| {
22611                                    let start = buffer.offset_to_point(range.start.0).row;
22612                                    let end = buffer.offset_to_point(range.end.0).row;
22613                                    (start, end)
22614                                })
22615                                .collect();
22616                            (
22617                                o.comments_expanded,
22618                                o.inline_edit_editors.clone(),
22619                                o.user_avatar_uri.clone(),
22620                                if ranges.is_empty() {
22621                                    None
22622                                } else {
22623                                    Some(ranges)
22624                                },
22625                            )
22626                        })
22627                        .unwrap_or((true, HashMap::default(), None, None));
22628                    (comments, expanded, editors, avatar_uri, line_ranges)
22629                })
22630                .unwrap_or((Vec::new(), true, HashMap::default(), None, None));
22631
22632        let comment_count = comments.len();
22633        let avatar_size = px(20.);
22634        let action_icon_size = IconSize::XSmall;
22635
22636        v_flex()
22637            .w_full()
22638            .bg(colors.editor_background)
22639            .border_b_1()
22640            .border_color(colors.border)
22641            .px_2()
22642            .pb_2()
22643            .gap_2()
22644            // Line range indicator (only shown for multi-line selections or multiple excerpts)
22645            .when_some(line_ranges, |el, ranges| {
22646                let label = format_line_ranges(&ranges);
22647                if let Some(label) = label {
22648                    el.child(
22649                        h_flex()
22650                            .w_full()
22651                            .px_2()
22652                            .child(Label::new(label).size(LabelSize::Small).color(Color::Muted)),
22653                    )
22654                } else {
22655                    el
22656                }
22657            })
22658            // Top row: editable input with user's avatar
22659            .child(
22660                h_flex()
22661                    .w_full()
22662                    .items_center()
22663                    .gap_2()
22664                    .px_2()
22665                    .py_1p5()
22666                    .rounded_md()
22667                    .bg(colors.surface_background)
22668                    .child(
22669                        div()
22670                            .size(avatar_size)
22671                            .flex_shrink_0()
22672                            .rounded_full()
22673                            .overflow_hidden()
22674                            .child(if let Some(ref avatar_uri) = user_avatar_uri {
22675                                Avatar::new(avatar_uri.clone())
22676                                    .size(avatar_size)
22677                                    .into_any_element()
22678                            } else {
22679                                Icon::new(IconName::Person)
22680                                    .size(IconSize::Small)
22681                                    .color(ui::Color::Muted)
22682                                    .into_any_element()
22683                            }),
22684                    )
22685                    .child(
22686                        div()
22687                            .flex_1()
22688                            .border_1()
22689                            .border_color(colors.border)
22690                            .rounded_md()
22691                            .bg(colors.editor_background)
22692                            .px_2()
22693                            .py_1()
22694                            .child(prompt_editor.clone()),
22695                    )
22696                    .child(
22697                        h_flex()
22698                            .flex_shrink_0()
22699                            .gap_1()
22700                            .child(
22701                                IconButton::new("diff-review-close", IconName::Close)
22702                                    .icon_color(ui::Color::Muted)
22703                                    .icon_size(action_icon_size)
22704                                    .tooltip(Tooltip::text("Close"))
22705                                    .on_click(|_, window, cx| {
22706                                        window
22707                                            .dispatch_action(Box::new(crate::actions::Cancel), cx);
22708                                    }),
22709                            )
22710                            .child(
22711                                IconButton::new("diff-review-add", IconName::Return)
22712                                    .icon_color(ui::Color::Muted)
22713                                    .icon_size(action_icon_size)
22714                                    .tooltip(Tooltip::text("Add comment"))
22715                                    .on_click(|_, window, cx| {
22716                                        window.dispatch_action(
22717                                            Box::new(crate::actions::SubmitDiffReviewComment),
22718                                            cx,
22719                                        );
22720                                    }),
22721                            ),
22722                    ),
22723            )
22724            // Expandable comments section (only shown when there are comments)
22725            .when(comment_count > 0, |el| {
22726                el.child(Self::render_comments_section(
22727                    comments,
22728                    comments_expanded,
22729                    inline_editors,
22730                    user_avatar_uri,
22731                    avatar_size,
22732                    action_icon_size,
22733                    colors,
22734                ))
22735            })
22736            .into_any_element()
22737    }
22738
22739    fn render_comments_section(
22740        comments: Vec<StoredReviewComment>,
22741        expanded: bool,
22742        inline_editors: HashMap<usize, Entity<Editor>>,
22743        user_avatar_uri: Option<SharedUri>,
22744        avatar_size: Pixels,
22745        action_icon_size: IconSize,
22746        colors: &theme::ThemeColors,
22747    ) -> impl IntoElement {
22748        let comment_count = comments.len();
22749
22750        v_flex()
22751            .w_full()
22752            .gap_1()
22753            // Header with expand/collapse toggle
22754            .child(
22755                h_flex()
22756                    .id("review-comments-header")
22757                    .w_full()
22758                    .items_center()
22759                    .gap_1()
22760                    .px_2()
22761                    .py_1()
22762                    .cursor_pointer()
22763                    .rounded_md()
22764                    .hover(|style| style.bg(colors.ghost_element_hover))
22765                    .on_click(|_, window: &mut Window, cx| {
22766                        window.dispatch_action(
22767                            Box::new(crate::actions::ToggleReviewCommentsExpanded),
22768                            cx,
22769                        );
22770                    })
22771                    .child(
22772                        Icon::new(if expanded {
22773                            IconName::ChevronDown
22774                        } else {
22775                            IconName::ChevronRight
22776                        })
22777                        .size(IconSize::Small)
22778                        .color(ui::Color::Muted),
22779                    )
22780                    .child(
22781                        Label::new(format!(
22782                            "{} Comment{}",
22783                            comment_count,
22784                            if comment_count == 1 { "" } else { "s" }
22785                        ))
22786                        .size(LabelSize::Small)
22787                        .color(Color::Muted),
22788                    ),
22789            )
22790            // Comments list (when expanded)
22791            .when(expanded, |el| {
22792                el.children(comments.into_iter().map(|comment| {
22793                    let inline_editor = inline_editors.get(&comment.id).cloned();
22794                    Self::render_comment_row(
22795                        comment,
22796                        inline_editor,
22797                        user_avatar_uri.clone(),
22798                        avatar_size,
22799                        action_icon_size,
22800                        colors,
22801                    )
22802                }))
22803            })
22804    }
22805
22806    fn render_comment_row(
22807        comment: StoredReviewComment,
22808        inline_editor: Option<Entity<Editor>>,
22809        user_avatar_uri: Option<SharedUri>,
22810        avatar_size: Pixels,
22811        action_icon_size: IconSize,
22812        colors: &theme::ThemeColors,
22813    ) -> impl IntoElement {
22814        let comment_id = comment.id;
22815        let is_editing = inline_editor.is_some();
22816
22817        h_flex()
22818            .w_full()
22819            .items_center()
22820            .gap_2()
22821            .px_2()
22822            .py_1p5()
22823            .rounded_md()
22824            .bg(colors.surface_background)
22825            .child(
22826                div()
22827                    .size(avatar_size)
22828                    .flex_shrink_0()
22829                    .rounded_full()
22830                    .overflow_hidden()
22831                    .child(if let Some(ref avatar_uri) = user_avatar_uri {
22832                        Avatar::new(avatar_uri.clone())
22833                            .size(avatar_size)
22834                            .into_any_element()
22835                    } else {
22836                        Icon::new(IconName::Person)
22837                            .size(IconSize::Small)
22838                            .color(ui::Color::Muted)
22839                            .into_any_element()
22840                    }),
22841            )
22842            .child(if let Some(editor) = inline_editor {
22843                // Inline edit mode: show an editable text field
22844                div()
22845                    .flex_1()
22846                    .border_1()
22847                    .border_color(colors.border)
22848                    .rounded_md()
22849                    .bg(colors.editor_background)
22850                    .px_2()
22851                    .py_1()
22852                    .child(editor)
22853                    .into_any_element()
22854            } else {
22855                // Display mode: show the comment text
22856                div()
22857                    .flex_1()
22858                    .text_sm()
22859                    .text_color(colors.text)
22860                    .child(comment.comment)
22861                    .into_any_element()
22862            })
22863            .child(if is_editing {
22864                // Editing mode: show close and confirm buttons
22865                h_flex()
22866                    .gap_1()
22867                    .child(
22868                        IconButton::new(
22869                            format!("diff-review-cancel-edit-{comment_id}"),
22870                            IconName::Close,
22871                        )
22872                        .icon_color(ui::Color::Muted)
22873                        .icon_size(action_icon_size)
22874                        .tooltip(Tooltip::text("Cancel"))
22875                        .on_click(move |_, window, cx| {
22876                            window.dispatch_action(
22877                                Box::new(crate::actions::CancelEditReviewComment {
22878                                    id: comment_id,
22879                                }),
22880                                cx,
22881                            );
22882                        }),
22883                    )
22884                    .child(
22885                        IconButton::new(
22886                            format!("diff-review-confirm-edit-{comment_id}"),
22887                            IconName::Return,
22888                        )
22889                        .icon_color(ui::Color::Muted)
22890                        .icon_size(action_icon_size)
22891                        .tooltip(Tooltip::text("Confirm"))
22892                        .on_click(move |_, window, cx| {
22893                            window.dispatch_action(
22894                                Box::new(crate::actions::ConfirmEditReviewComment {
22895                                    id: comment_id,
22896                                }),
22897                                cx,
22898                            );
22899                        }),
22900                    )
22901                    .into_any_element()
22902            } else {
22903                // Display mode: no action buttons for now (edit/delete not yet implemented)
22904                gpui::Empty.into_any_element()
22905            })
22906    }
22907
22908    pub fn set_masked(&mut self, masked: bool, cx: &mut Context<Self>) {
22909        if self.display_map.read(cx).masked != masked {
22910            self.display_map.update(cx, |map, _| map.masked = masked);
22911        }
22912        cx.notify()
22913    }
22914
22915    pub fn set_show_wrap_guides(&mut self, show_wrap_guides: bool, cx: &mut Context<Self>) {
22916        self.show_wrap_guides = Some(show_wrap_guides);
22917        cx.notify();
22918    }
22919
22920    pub fn set_show_indent_guides(&mut self, show_indent_guides: bool, cx: &mut Context<Self>) {
22921        self.show_indent_guides = Some(show_indent_guides);
22922        cx.notify();
22923    }
22924
22925    pub fn working_directory(&self, cx: &App) -> Option<PathBuf> {
22926        if let Some(buffer) = self.buffer().read(cx).as_singleton() {
22927            if let Some(file) = buffer.read(cx).file().and_then(|f| f.as_local())
22928                && let Some(dir) = file.abs_path(cx).parent()
22929            {
22930                return Some(dir.to_owned());
22931            }
22932        }
22933
22934        None
22935    }
22936
22937    fn target_file<'a>(&self, cx: &'a App) -> Option<&'a dyn language::LocalFile> {
22938        self.active_excerpt(cx)?
22939            .1
22940            .read(cx)
22941            .file()
22942            .and_then(|f| f.as_local())
22943    }
22944
22945    pub fn target_file_abs_path(&self, cx: &mut Context<Self>) -> Option<PathBuf> {
22946        self.active_excerpt(cx).and_then(|(_, buffer, _)| {
22947            let buffer = buffer.read(cx);
22948            if let Some(project_path) = buffer.project_path(cx) {
22949                let project = self.project()?.read(cx);
22950                project.absolute_path(&project_path, cx)
22951            } else {
22952                buffer
22953                    .file()
22954                    .and_then(|file| file.as_local().map(|file| file.abs_path(cx)))
22955            }
22956        })
22957    }
22958
22959    pub fn reveal_in_finder(
22960        &mut self,
22961        _: &RevealInFileManager,
22962        _window: &mut Window,
22963        cx: &mut Context<Self>,
22964    ) {
22965        if let Some(path) = self.target_file_abs_path(cx) {
22966            if let Some(project) = self.project() {
22967                project.update(cx, |project, cx| project.reveal_path(&path, cx));
22968            } else {
22969                cx.reveal_path(&path);
22970            }
22971        }
22972    }
22973
22974    pub fn copy_path(
22975        &mut self,
22976        _: &zed_actions::workspace::CopyPath,
22977        _window: &mut Window,
22978        cx: &mut Context<Self>,
22979    ) {
22980        if let Some(path) = self.target_file_abs_path(cx)
22981            && let Some(path) = path.to_str()
22982        {
22983            cx.write_to_clipboard(ClipboardItem::new_string(path.to_string()));
22984        } else {
22985            cx.propagate();
22986        }
22987    }
22988
22989    pub fn copy_relative_path(
22990        &mut self,
22991        _: &zed_actions::workspace::CopyRelativePath,
22992        _window: &mut Window,
22993        cx: &mut Context<Self>,
22994    ) {
22995        if let Some(path) = self.active_excerpt(cx).and_then(|(_, buffer, _)| {
22996            let project = self.project()?.read(cx);
22997            let path = buffer.read(cx).file()?.path();
22998            let path = path.display(project.path_style(cx));
22999            Some(path)
23000        }) {
23001            cx.write_to_clipboard(ClipboardItem::new_string(path.to_string()));
23002        } else {
23003            cx.propagate();
23004        }
23005    }
23006
23007    /// Returns the project path for the editor's buffer, if any buffer is
23008    /// opened in the editor.
23009    pub fn project_path(&self, cx: &App) -> Option<ProjectPath> {
23010        if let Some(buffer) = self.buffer.read(cx).as_singleton() {
23011            buffer.read(cx).project_path(cx)
23012        } else {
23013            None
23014        }
23015    }
23016
23017    // Returns true if the editor handled a go-to-line request
23018    pub fn go_to_active_debug_line(&mut self, window: &mut Window, cx: &mut Context<Self>) -> bool {
23019        maybe!({
23020            let breakpoint_store = self.breakpoint_store.as_ref()?;
23021
23022            let (active_stack_frame, debug_line_pane_id) = {
23023                let store = breakpoint_store.read(cx);
23024                let active_stack_frame = store.active_position().cloned();
23025                let debug_line_pane_id = store.active_debug_line_pane_id();
23026                (active_stack_frame, debug_line_pane_id)
23027            };
23028
23029            let Some(active_stack_frame) = active_stack_frame else {
23030                self.clear_row_highlights::<ActiveDebugLine>();
23031                return None;
23032            };
23033
23034            if let Some(debug_line_pane_id) = debug_line_pane_id {
23035                if let Some(workspace) = self
23036                    .workspace
23037                    .as_ref()
23038                    .and_then(|(workspace, _)| workspace.upgrade())
23039                {
23040                    let editor_pane_id = workspace
23041                        .read(cx)
23042                        .pane_for_item_id(cx.entity_id())
23043                        .map(|pane| pane.entity_id());
23044
23045                    if editor_pane_id.is_some_and(|id| id != debug_line_pane_id) {
23046                        self.clear_row_highlights::<ActiveDebugLine>();
23047                        return None;
23048                    }
23049                }
23050            }
23051
23052            let position = active_stack_frame.position;
23053            let buffer_id = position.buffer_id?;
23054            let snapshot = self
23055                .project
23056                .as_ref()?
23057                .read(cx)
23058                .buffer_for_id(buffer_id, cx)?
23059                .read(cx)
23060                .snapshot();
23061
23062            let mut handled = false;
23063            for (id, _, ExcerptRange { context, .. }) in
23064                self.buffer.read(cx).excerpts_for_buffer(buffer_id, cx)
23065            {
23066                if context.start.cmp(&position, &snapshot).is_ge()
23067                    || context.end.cmp(&position, &snapshot).is_lt()
23068                {
23069                    continue;
23070                }
23071                let snapshot = self.buffer.read(cx).snapshot(cx);
23072                let multibuffer_anchor = snapshot.anchor_in_excerpt(id, position)?;
23073
23074                handled = true;
23075                self.clear_row_highlights::<ActiveDebugLine>();
23076
23077                self.go_to_line::<ActiveDebugLine>(
23078                    multibuffer_anchor,
23079                    Some(cx.theme().colors().editor_debugger_active_line_background),
23080                    window,
23081                    cx,
23082                );
23083
23084                cx.notify();
23085            }
23086
23087            handled.then_some(())
23088        })
23089        .is_some()
23090    }
23091
23092    pub fn copy_file_name_without_extension(
23093        &mut self,
23094        _: &CopyFileNameWithoutExtension,
23095        _: &mut Window,
23096        cx: &mut Context<Self>,
23097    ) {
23098        if let Some(file_stem) = self.active_excerpt(cx).and_then(|(_, buffer, _)| {
23099            let file = buffer.read(cx).file()?;
23100            file.path().file_stem()
23101        }) {
23102            cx.write_to_clipboard(ClipboardItem::new_string(file_stem.to_string()));
23103        }
23104    }
23105
23106    pub fn copy_file_name(&mut self, _: &CopyFileName, _: &mut Window, cx: &mut Context<Self>) {
23107        if let Some(file_name) = self.active_excerpt(cx).and_then(|(_, buffer, _)| {
23108            let file = buffer.read(cx).file()?;
23109            Some(file.file_name(cx))
23110        }) {
23111            cx.write_to_clipboard(ClipboardItem::new_string(file_name.to_string()));
23112        }
23113    }
23114
23115    pub fn toggle_git_blame(
23116        &mut self,
23117        _: &::git::Blame,
23118        window: &mut Window,
23119        cx: &mut Context<Self>,
23120    ) {
23121        self.show_git_blame_gutter = !self.show_git_blame_gutter;
23122
23123        if self.show_git_blame_gutter && !self.has_blame_entries(cx) {
23124            self.start_git_blame(true, window, cx);
23125        }
23126
23127        cx.notify();
23128    }
23129
23130    pub fn toggle_git_blame_inline(
23131        &mut self,
23132        _: &ToggleGitBlameInline,
23133        window: &mut Window,
23134        cx: &mut Context<Self>,
23135    ) {
23136        self.toggle_git_blame_inline_internal(true, window, cx);
23137        cx.notify();
23138    }
23139
23140    pub fn open_git_blame_commit(
23141        &mut self,
23142        _: &OpenGitBlameCommit,
23143        window: &mut Window,
23144        cx: &mut Context<Self>,
23145    ) {
23146        self.open_git_blame_commit_internal(window, cx);
23147    }
23148
23149    fn open_git_blame_commit_internal(
23150        &mut self,
23151        window: &mut Window,
23152        cx: &mut Context<Self>,
23153    ) -> Option<()> {
23154        let blame = self.blame.as_ref()?;
23155        let snapshot = self.snapshot(window, cx);
23156        let cursor = self
23157            .selections
23158            .newest::<Point>(&snapshot.display_snapshot)
23159            .head();
23160        let (buffer, point, _) = snapshot.buffer_snapshot().point_to_buffer_point(cursor)?;
23161        let (_, blame_entry) = blame
23162            .update(cx, |blame, cx| {
23163                blame
23164                    .blame_for_rows(
23165                        &[RowInfo {
23166                            buffer_id: Some(buffer.remote_id()),
23167                            buffer_row: Some(point.row),
23168                            ..Default::default()
23169                        }],
23170                        cx,
23171                    )
23172                    .next()
23173            })
23174            .flatten()?;
23175        let renderer = cx.global::<GlobalBlameRenderer>().0.clone();
23176        let repo = blame.read(cx).repository(cx, buffer.remote_id())?;
23177        let workspace = self.workspace()?.downgrade();
23178        renderer.open_blame_commit(blame_entry, repo, workspace, window, cx);
23179        None
23180    }
23181
23182    pub fn git_blame_inline_enabled(&self) -> bool {
23183        self.git_blame_inline_enabled
23184    }
23185
23186    pub fn toggle_selection_menu(
23187        &mut self,
23188        _: &ToggleSelectionMenu,
23189        _: &mut Window,
23190        cx: &mut Context<Self>,
23191    ) {
23192        self.show_selection_menu = self
23193            .show_selection_menu
23194            .map(|show_selections_menu| !show_selections_menu)
23195            .or_else(|| Some(!EditorSettings::get_global(cx).toolbar.selections_menu));
23196
23197        cx.notify();
23198    }
23199
23200    pub fn selection_menu_enabled(&self, cx: &App) -> bool {
23201        self.show_selection_menu
23202            .unwrap_or_else(|| EditorSettings::get_global(cx).toolbar.selections_menu)
23203    }
23204
23205    fn start_git_blame(
23206        &mut self,
23207        user_triggered: bool,
23208        window: &mut Window,
23209        cx: &mut Context<Self>,
23210    ) {
23211        if let Some(project) = self.project() {
23212            if let Some(buffer) = self.buffer().read(cx).as_singleton()
23213                && buffer.read(cx).file().is_none()
23214            {
23215                return;
23216            }
23217
23218            let focused = self.focus_handle(cx).contains_focused(window, cx);
23219
23220            let project = project.clone();
23221            let blame = cx
23222                .new(|cx| GitBlame::new(self.buffer.clone(), project, user_triggered, focused, cx));
23223            self.blame_subscription =
23224                Some(cx.observe_in(&blame, window, |_, _, _, cx| cx.notify()));
23225            self.blame = Some(blame);
23226        }
23227    }
23228
23229    fn toggle_git_blame_inline_internal(
23230        &mut self,
23231        user_triggered: bool,
23232        window: &mut Window,
23233        cx: &mut Context<Self>,
23234    ) {
23235        if self.git_blame_inline_enabled {
23236            self.git_blame_inline_enabled = false;
23237            self.show_git_blame_inline = false;
23238            self.show_git_blame_inline_delay_task.take();
23239        } else {
23240            self.git_blame_inline_enabled = true;
23241            self.start_git_blame_inline(user_triggered, window, cx);
23242        }
23243
23244        cx.notify();
23245    }
23246
23247    fn start_git_blame_inline(
23248        &mut self,
23249        user_triggered: bool,
23250        window: &mut Window,
23251        cx: &mut Context<Self>,
23252    ) {
23253        self.start_git_blame(user_triggered, window, cx);
23254
23255        if ProjectSettings::get_global(cx)
23256            .git
23257            .inline_blame_delay()
23258            .is_some()
23259        {
23260            self.start_inline_blame_timer(window, cx);
23261        } else {
23262            self.show_git_blame_inline = true
23263        }
23264    }
23265
23266    pub fn blame(&self) -> Option<&Entity<GitBlame>> {
23267        self.blame.as_ref()
23268    }
23269
23270    pub fn show_git_blame_gutter(&self) -> bool {
23271        self.show_git_blame_gutter
23272    }
23273
23274    pub fn render_git_blame_gutter(&self, cx: &App) -> bool {
23275        !self.mode().is_minimap() && self.show_git_blame_gutter && self.has_blame_entries(cx)
23276    }
23277
23278    pub fn render_git_blame_inline(&self, window: &Window, cx: &App) -> bool {
23279        self.show_git_blame_inline
23280            && (self.focus_handle.is_focused(window) || self.inline_blame_popover.is_some())
23281            && !self.newest_selection_head_on_empty_line(cx)
23282            && self.has_blame_entries(cx)
23283    }
23284
23285    fn has_blame_entries(&self, cx: &App) -> bool {
23286        self.blame()
23287            .is_some_and(|blame| blame.read(cx).has_generated_entries())
23288    }
23289
23290    fn newest_selection_head_on_empty_line(&self, cx: &App) -> bool {
23291        let cursor_anchor = self.selections.newest_anchor().head();
23292
23293        let snapshot = self.buffer.read(cx).snapshot(cx);
23294        let buffer_row = MultiBufferRow(cursor_anchor.to_point(&snapshot).row);
23295
23296        snapshot.line_len(buffer_row) == 0
23297    }
23298
23299    fn get_permalink_to_line(&self, cx: &mut Context<Self>) -> Task<Result<url::Url>> {
23300        let buffer_and_selection = maybe!({
23301            let selection = self.selections.newest::<Point>(&self.display_snapshot(cx));
23302            let selection_range = selection.range();
23303
23304            let multi_buffer = self.buffer().read(cx);
23305            let multi_buffer_snapshot = multi_buffer.snapshot(cx);
23306            let buffer_ranges = multi_buffer_snapshot
23307                .range_to_buffer_ranges(selection_range.start..=selection_range.end);
23308
23309            let (buffer, range, _) = if selection.reversed {
23310                buffer_ranges.first()
23311            } else {
23312                buffer_ranges.last()
23313            }?;
23314
23315            let buffer_range = range.to_point(buffer);
23316
23317            let Some(buffer_diff) = multi_buffer.diff_for(buffer.remote_id()) else {
23318                return Some((
23319                    multi_buffer.buffer(buffer.remote_id()).unwrap(),
23320                    buffer_range.start.row..buffer_range.end.row,
23321                ));
23322            };
23323
23324            let buffer_diff_snapshot = buffer_diff.read(cx).snapshot(cx);
23325            let start =
23326                buffer_diff_snapshot.buffer_point_to_base_text_point(buffer_range.start, buffer);
23327            let end =
23328                buffer_diff_snapshot.buffer_point_to_base_text_point(buffer_range.end, buffer);
23329
23330            Some((
23331                multi_buffer.buffer(buffer.remote_id()).unwrap(),
23332                start.row..end.row,
23333            ))
23334        });
23335
23336        let Some((buffer, selection)) = buffer_and_selection else {
23337            return Task::ready(Err(anyhow!("failed to determine buffer and selection")));
23338        };
23339
23340        let Some(project) = self.project() else {
23341            return Task::ready(Err(anyhow!("editor does not have project")));
23342        };
23343
23344        project.update(cx, |project, cx| {
23345            project.get_permalink_to_line(&buffer, selection, cx)
23346        })
23347    }
23348
23349    pub fn copy_permalink_to_line(
23350        &mut self,
23351        _: &CopyPermalinkToLine,
23352        window: &mut Window,
23353        cx: &mut Context<Self>,
23354    ) {
23355        let permalink_task = self.get_permalink_to_line(cx);
23356        let workspace = self.workspace();
23357
23358        cx.spawn_in(window, async move |_, cx| match permalink_task.await {
23359            Ok(permalink) => {
23360                cx.update(|_, cx| {
23361                    cx.write_to_clipboard(ClipboardItem::new_string(permalink.to_string()));
23362                })
23363                .ok();
23364            }
23365            Err(err) => {
23366                let message = format!("Failed to copy permalink: {err}");
23367
23368                anyhow::Result::<()>::Err(err).log_err();
23369
23370                if let Some(workspace) = workspace {
23371                    workspace
23372                        .update_in(cx, |workspace, _, cx| {
23373                            struct CopyPermalinkToLine;
23374
23375                            workspace.show_toast(
23376                                Toast::new(
23377                                    NotificationId::unique::<CopyPermalinkToLine>(),
23378                                    message,
23379                                ),
23380                                cx,
23381                            )
23382                        })
23383                        .ok();
23384                }
23385            }
23386        })
23387        .detach();
23388    }
23389
23390    pub fn copy_file_location(
23391        &mut self,
23392        _: &CopyFileLocation,
23393        _: &mut Window,
23394        cx: &mut Context<Self>,
23395    ) {
23396        let selection = self.selections.newest::<Point>(&self.display_snapshot(cx));
23397
23398        let start_line = selection.start.row + 1;
23399        let end_line = selection.end.row + 1;
23400
23401        let end_line = if selection.end.column == 0 && end_line > start_line {
23402            end_line - 1
23403        } else {
23404            end_line
23405        };
23406
23407        if let Some(file_location) = self.active_excerpt(cx).and_then(|(_, buffer, _)| {
23408            let project = self.project()?.read(cx);
23409            let file = buffer.read(cx).file()?;
23410            let path = file.path().display(project.path_style(cx));
23411
23412            let location = if start_line == end_line {
23413                format!("{path}:{start_line}")
23414            } else {
23415                format!("{path}:{start_line}-{end_line}")
23416            };
23417            Some(location)
23418        }) {
23419            cx.write_to_clipboard(ClipboardItem::new_string(file_location));
23420        }
23421    }
23422
23423    pub fn open_permalink_to_line(
23424        &mut self,
23425        _: &OpenPermalinkToLine,
23426        window: &mut Window,
23427        cx: &mut Context<Self>,
23428    ) {
23429        let permalink_task = self.get_permalink_to_line(cx);
23430        let workspace = self.workspace();
23431
23432        cx.spawn_in(window, async move |_, cx| match permalink_task.await {
23433            Ok(permalink) => {
23434                cx.update(|_, cx| {
23435                    cx.open_url(permalink.as_ref());
23436                })
23437                .ok();
23438            }
23439            Err(err) => {
23440                let message = format!("Failed to open permalink: {err}");
23441
23442                anyhow::Result::<()>::Err(err).log_err();
23443
23444                if let Some(workspace) = workspace {
23445                    workspace.update(cx, |workspace, cx| {
23446                        struct OpenPermalinkToLine;
23447
23448                        workspace.show_toast(
23449                            Toast::new(NotificationId::unique::<OpenPermalinkToLine>(), message),
23450                            cx,
23451                        )
23452                    });
23453                }
23454            }
23455        })
23456        .detach();
23457    }
23458
23459    pub fn insert_uuid_v4(
23460        &mut self,
23461        _: &InsertUuidV4,
23462        window: &mut Window,
23463        cx: &mut Context<Self>,
23464    ) {
23465        self.insert_uuid(UuidVersion::V4, window, cx);
23466    }
23467
23468    pub fn insert_uuid_v7(
23469        &mut self,
23470        _: &InsertUuidV7,
23471        window: &mut Window,
23472        cx: &mut Context<Self>,
23473    ) {
23474        self.insert_uuid(UuidVersion::V7, window, cx);
23475    }
23476
23477    fn insert_uuid(&mut self, version: UuidVersion, window: &mut Window, cx: &mut Context<Self>) {
23478        self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
23479        self.transact(window, cx, |this, window, cx| {
23480            let edits = this
23481                .selections
23482                .all::<Point>(&this.display_snapshot(cx))
23483                .into_iter()
23484                .map(|selection| {
23485                    let uuid = match version {
23486                        UuidVersion::V4 => uuid::Uuid::new_v4(),
23487                        UuidVersion::V7 => uuid::Uuid::now_v7(),
23488                    };
23489
23490                    (selection.range(), uuid.to_string())
23491                });
23492            this.edit(edits, cx);
23493            this.refresh_edit_prediction(true, false, window, cx);
23494        });
23495    }
23496
23497    pub fn open_selections_in_multibuffer(
23498        &mut self,
23499        _: &OpenSelectionsInMultibuffer,
23500        window: &mut Window,
23501        cx: &mut Context<Self>,
23502    ) {
23503        let multibuffer = self.buffer.read(cx);
23504
23505        let Some(buffer) = multibuffer.as_singleton() else {
23506            return;
23507        };
23508
23509        let Some(workspace) = self.workspace() else {
23510            return;
23511        };
23512
23513        let title = multibuffer.title(cx).to_string();
23514
23515        let locations = self
23516            .selections
23517            .all_anchors(&self.display_snapshot(cx))
23518            .iter()
23519            .map(|selection| {
23520                (
23521                    buffer.clone(),
23522                    (selection.start.text_anchor..selection.end.text_anchor)
23523                        .to_point(buffer.read(cx)),
23524                )
23525            })
23526            .into_group_map();
23527
23528        cx.spawn_in(window, async move |_, cx| {
23529            workspace.update_in(cx, |workspace, window, cx| {
23530                Self::open_locations_in_multibuffer(
23531                    workspace,
23532                    locations,
23533                    format!("Selections for '{title}'"),
23534                    false,
23535                    false,
23536                    MultibufferSelectionMode::All,
23537                    window,
23538                    cx,
23539                );
23540            })
23541        })
23542        .detach();
23543    }
23544
23545    /// Adds a row highlight for the given range. If a row has multiple highlights, the
23546    /// last highlight added will be used.
23547    ///
23548    /// If the range ends at the beginning of a line, then that line will not be highlighted.
23549    pub fn highlight_rows<T: 'static>(
23550        &mut self,
23551        range: Range<Anchor>,
23552        color: Hsla,
23553        options: RowHighlightOptions,
23554        cx: &mut Context<Self>,
23555    ) {
23556        let snapshot = self.buffer().read(cx).snapshot(cx);
23557        let row_highlights = self.highlighted_rows.entry(TypeId::of::<T>()).or_default();
23558        let ix = row_highlights.binary_search_by(|highlight| {
23559            Ordering::Equal
23560                .then_with(|| highlight.range.start.cmp(&range.start, &snapshot))
23561                .then_with(|| highlight.range.end.cmp(&range.end, &snapshot))
23562        });
23563
23564        if let Err(mut ix) = ix {
23565            let index = post_inc(&mut self.highlight_order);
23566
23567            // If this range intersects with the preceding highlight, then merge it with
23568            // the preceding highlight. Otherwise insert a new highlight.
23569            let mut merged = false;
23570            if ix > 0 {
23571                let prev_highlight = &mut row_highlights[ix - 1];
23572                if prev_highlight
23573                    .range
23574                    .end
23575                    .cmp(&range.start, &snapshot)
23576                    .is_ge()
23577                {
23578                    ix -= 1;
23579                    if prev_highlight.range.end.cmp(&range.end, &snapshot).is_lt() {
23580                        prev_highlight.range.end = range.end;
23581                    }
23582                    merged = true;
23583                    prev_highlight.index = index;
23584                    prev_highlight.color = color;
23585                    prev_highlight.options = options;
23586                }
23587            }
23588
23589            if !merged {
23590                row_highlights.insert(
23591                    ix,
23592                    RowHighlight {
23593                        range,
23594                        index,
23595                        color,
23596                        options,
23597                        type_id: TypeId::of::<T>(),
23598                    },
23599                );
23600            }
23601
23602            // If any of the following highlights intersect with this one, merge them.
23603            while let Some(next_highlight) = row_highlights.get(ix + 1) {
23604                let highlight = &row_highlights[ix];
23605                if next_highlight
23606                    .range
23607                    .start
23608                    .cmp(&highlight.range.end, &snapshot)
23609                    .is_le()
23610                {
23611                    if next_highlight
23612                        .range
23613                        .end
23614                        .cmp(&highlight.range.end, &snapshot)
23615                        .is_gt()
23616                    {
23617                        row_highlights[ix].range.end = next_highlight.range.end;
23618                    }
23619                    row_highlights.remove(ix + 1);
23620                } else {
23621                    break;
23622                }
23623            }
23624        }
23625    }
23626
23627    /// Remove any highlighted row ranges of the given type that intersect the
23628    /// given ranges.
23629    pub fn remove_highlighted_rows<T: 'static>(
23630        &mut self,
23631        ranges_to_remove: Vec<Range<Anchor>>,
23632        cx: &mut Context<Self>,
23633    ) {
23634        let snapshot = self.buffer().read(cx).snapshot(cx);
23635        let row_highlights = self.highlighted_rows.entry(TypeId::of::<T>()).or_default();
23636        let mut ranges_to_remove = ranges_to_remove.iter().peekable();
23637        row_highlights.retain(|highlight| {
23638            while let Some(range_to_remove) = ranges_to_remove.peek() {
23639                match range_to_remove.end.cmp(&highlight.range.start, &snapshot) {
23640                    Ordering::Less | Ordering::Equal => {
23641                        ranges_to_remove.next();
23642                    }
23643                    Ordering::Greater => {
23644                        match range_to_remove.start.cmp(&highlight.range.end, &snapshot) {
23645                            Ordering::Less | Ordering::Equal => {
23646                                return false;
23647                            }
23648                            Ordering::Greater => break,
23649                        }
23650                    }
23651                }
23652            }
23653
23654            true
23655        })
23656    }
23657
23658    /// Clear all anchor ranges for a certain highlight context type, so no corresponding rows will be highlighted.
23659    pub fn clear_row_highlights<T: 'static>(&mut self) {
23660        self.highlighted_rows.remove(&TypeId::of::<T>());
23661    }
23662
23663    /// For a highlight given context type, gets all anchor ranges that will be used for row highlighting.
23664    pub fn highlighted_rows<T: 'static>(&self) -> impl '_ + Iterator<Item = (Range<Anchor>, Hsla)> {
23665        self.highlighted_rows
23666            .get(&TypeId::of::<T>())
23667            .map_or(&[] as &[_], |vec| vec.as_slice())
23668            .iter()
23669            .map(|highlight| (highlight.range.clone(), highlight.color))
23670    }
23671
23672    /// Merges all anchor ranges for all context types ever set, picking the last highlight added in case of a row conflict.
23673    /// Returns a map of display rows that are highlighted and their corresponding highlight color.
23674    /// Allows to ignore certain kinds of highlights.
23675    pub fn highlighted_display_rows(
23676        &self,
23677        window: &mut Window,
23678        cx: &mut App,
23679    ) -> BTreeMap<DisplayRow, LineHighlight> {
23680        let snapshot = self.snapshot(window, cx);
23681        let mut used_highlight_orders = HashMap::default();
23682        self.highlighted_rows
23683            .iter()
23684            .flat_map(|(_, highlighted_rows)| highlighted_rows.iter())
23685            .fold(
23686                BTreeMap::<DisplayRow, LineHighlight>::new(),
23687                |mut unique_rows, highlight| {
23688                    let start = highlight.range.start.to_display_point(&snapshot);
23689                    let end = highlight.range.end.to_display_point(&snapshot);
23690                    let start_row = start.row().0;
23691                    let end_row = if !highlight.range.end.text_anchor.is_max() && end.column() == 0
23692                    {
23693                        end.row().0.saturating_sub(1)
23694                    } else {
23695                        end.row().0
23696                    };
23697                    for row in start_row..=end_row {
23698                        let used_index =
23699                            used_highlight_orders.entry(row).or_insert(highlight.index);
23700                        if highlight.index >= *used_index {
23701                            *used_index = highlight.index;
23702                            unique_rows.insert(
23703                                DisplayRow(row),
23704                                LineHighlight {
23705                                    include_gutter: highlight.options.include_gutter,
23706                                    border: None,
23707                                    background: highlight.color.into(),
23708                                    type_id: Some(highlight.type_id),
23709                                },
23710                            );
23711                        }
23712                    }
23713                    unique_rows
23714                },
23715            )
23716    }
23717
23718    pub fn highlighted_display_row_for_autoscroll(
23719        &self,
23720        snapshot: &DisplaySnapshot,
23721    ) -> Option<DisplayRow> {
23722        self.highlighted_rows
23723            .values()
23724            .flat_map(|highlighted_rows| highlighted_rows.iter())
23725            .filter_map(|highlight| {
23726                if highlight.options.autoscroll {
23727                    Some(highlight.range.start.to_display_point(snapshot).row())
23728                } else {
23729                    None
23730                }
23731            })
23732            .min()
23733    }
23734
23735    pub fn set_search_within_ranges(&mut self, ranges: &[Range<Anchor>], cx: &mut Context<Self>) {
23736        self.highlight_background(
23737            HighlightKey::SearchWithinRange,
23738            ranges,
23739            |_, colors| colors.colors().editor_document_highlight_read_background,
23740            cx,
23741        )
23742    }
23743
23744    pub fn set_breadcrumb_header(&mut self, new_header: String) {
23745        self.breadcrumb_header = Some(new_header);
23746    }
23747
23748    pub fn clear_search_within_ranges(&mut self, cx: &mut Context<Self>) {
23749        self.clear_background_highlights(HighlightKey::SearchWithinRange, cx);
23750    }
23751
23752    pub fn highlight_background(
23753        &mut self,
23754        key: HighlightKey,
23755        ranges: &[Range<Anchor>],
23756        color_fetcher: impl Fn(&usize, &Theme) -> Hsla + Send + Sync + 'static,
23757        cx: &mut Context<Self>,
23758    ) {
23759        self.background_highlights
23760            .insert(key, (Arc::new(color_fetcher), Arc::from(ranges)));
23761        self.scrollbar_marker_state.dirty = true;
23762        cx.notify();
23763    }
23764
23765    pub fn clear_background_highlights(
23766        &mut self,
23767        key: HighlightKey,
23768        cx: &mut Context<Self>,
23769    ) -> Option<BackgroundHighlight> {
23770        let text_highlights = self.background_highlights.remove(&key)?;
23771        if !text_highlights.1.is_empty() {
23772            self.scrollbar_marker_state.dirty = true;
23773            cx.notify();
23774        }
23775        Some(text_highlights)
23776    }
23777
23778    pub fn highlight_gutter<T: 'static>(
23779        &mut self,
23780        ranges: impl Into<Vec<Range<Anchor>>>,
23781        color_fetcher: fn(&App) -> Hsla,
23782        cx: &mut Context<Self>,
23783    ) {
23784        self.gutter_highlights
23785            .insert(TypeId::of::<T>(), (color_fetcher, ranges.into()));
23786        cx.notify();
23787    }
23788
23789    pub fn clear_gutter_highlights<T: 'static>(
23790        &mut self,
23791        cx: &mut Context<Self>,
23792    ) -> Option<GutterHighlight> {
23793        cx.notify();
23794        self.gutter_highlights.remove(&TypeId::of::<T>())
23795    }
23796
23797    pub fn insert_gutter_highlight<T: 'static>(
23798        &mut self,
23799        range: Range<Anchor>,
23800        color_fetcher: fn(&App) -> Hsla,
23801        cx: &mut Context<Self>,
23802    ) {
23803        let snapshot = self.buffer().read(cx).snapshot(cx);
23804        let mut highlights = self
23805            .gutter_highlights
23806            .remove(&TypeId::of::<T>())
23807            .map(|(_, highlights)| highlights)
23808            .unwrap_or_default();
23809        let ix = highlights.binary_search_by(|highlight| {
23810            Ordering::Equal
23811                .then_with(|| highlight.start.cmp(&range.start, &snapshot))
23812                .then_with(|| highlight.end.cmp(&range.end, &snapshot))
23813        });
23814        if let Err(ix) = ix {
23815            highlights.insert(ix, range);
23816        }
23817        self.gutter_highlights
23818            .insert(TypeId::of::<T>(), (color_fetcher, highlights));
23819    }
23820
23821    pub fn remove_gutter_highlights<T: 'static>(
23822        &mut self,
23823        ranges_to_remove: Vec<Range<Anchor>>,
23824        cx: &mut Context<Self>,
23825    ) {
23826        let snapshot = self.buffer().read(cx).snapshot(cx);
23827        let Some((color_fetcher, mut gutter_highlights)) =
23828            self.gutter_highlights.remove(&TypeId::of::<T>())
23829        else {
23830            return;
23831        };
23832        let mut ranges_to_remove = ranges_to_remove.iter().peekable();
23833        gutter_highlights.retain(|highlight| {
23834            while let Some(range_to_remove) = ranges_to_remove.peek() {
23835                match range_to_remove.end.cmp(&highlight.start, &snapshot) {
23836                    Ordering::Less | Ordering::Equal => {
23837                        ranges_to_remove.next();
23838                    }
23839                    Ordering::Greater => {
23840                        match range_to_remove.start.cmp(&highlight.end, &snapshot) {
23841                            Ordering::Less | Ordering::Equal => {
23842                                return false;
23843                            }
23844                            Ordering::Greater => break,
23845                        }
23846                    }
23847                }
23848            }
23849
23850            true
23851        });
23852        self.gutter_highlights
23853            .insert(TypeId::of::<T>(), (color_fetcher, gutter_highlights));
23854    }
23855
23856    #[cfg(any(test, feature = "test-support"))]
23857    pub fn all_text_highlights(
23858        &self,
23859        window: &mut Window,
23860        cx: &mut Context<Self>,
23861    ) -> Vec<(HighlightStyle, Vec<Range<DisplayPoint>>)> {
23862        let snapshot = self.snapshot(window, cx);
23863        self.display_map.update(cx, |display_map, _| {
23864            display_map
23865                .all_text_highlights()
23866                .map(|(_, highlight)| {
23867                    let (style, ranges) = highlight.as_ref();
23868                    (
23869                        *style,
23870                        ranges
23871                            .iter()
23872                            .map(|range| range.clone().to_display_points(&snapshot))
23873                            .collect(),
23874                    )
23875                })
23876                .collect()
23877        })
23878    }
23879
23880    #[cfg(any(test, feature = "test-support"))]
23881    pub fn all_text_background_highlights(
23882        &self,
23883        window: &mut Window,
23884        cx: &mut Context<Self>,
23885    ) -> Vec<(Range<DisplayPoint>, Hsla)> {
23886        let snapshot = self.snapshot(window, cx);
23887        let buffer = &snapshot.buffer_snapshot();
23888        let start = buffer.anchor_before(MultiBufferOffset(0));
23889        let end = buffer.anchor_after(buffer.len());
23890        self.sorted_background_highlights_in_range(start..end, &snapshot, cx.theme())
23891    }
23892
23893    #[cfg(any(test, feature = "test-support"))]
23894    pub fn sorted_background_highlights_in_range(
23895        &self,
23896        search_range: Range<Anchor>,
23897        display_snapshot: &DisplaySnapshot,
23898        theme: &Theme,
23899    ) -> Vec<(Range<DisplayPoint>, Hsla)> {
23900        let mut res = self.background_highlights_in_range(search_range, display_snapshot, theme);
23901        res.sort_by(|a, b| {
23902            a.0.start
23903                .cmp(&b.0.start)
23904                .then_with(|| a.0.end.cmp(&b.0.end))
23905                .then_with(|| a.1.cmp(&b.1))
23906        });
23907        res
23908    }
23909
23910    #[cfg(any(test, feature = "test-support"))]
23911    pub fn search_background_highlights(&mut self, cx: &mut Context<Self>) -> Vec<Range<Point>> {
23912        let snapshot = self.buffer().read(cx).snapshot(cx);
23913
23914        let highlights = self
23915            .background_highlights
23916            .get(&HighlightKey::BufferSearchHighlights);
23917
23918        if let Some((_color, ranges)) = highlights {
23919            ranges
23920                .iter()
23921                .map(|range| range.start.to_point(&snapshot)..range.end.to_point(&snapshot))
23922                .collect_vec()
23923        } else {
23924            vec![]
23925        }
23926    }
23927
23928    fn document_highlights_for_position<'a>(
23929        &'a self,
23930        position: Anchor,
23931        buffer: &'a MultiBufferSnapshot,
23932    ) -> impl 'a + Iterator<Item = &'a Range<Anchor>> {
23933        let read_highlights = self
23934            .background_highlights
23935            .get(&HighlightKey::DocumentHighlightRead)
23936            .map(|h| &h.1);
23937        let write_highlights = self
23938            .background_highlights
23939            .get(&HighlightKey::DocumentHighlightWrite)
23940            .map(|h| &h.1);
23941        let left_position = position.bias_left(buffer);
23942        let right_position = position.bias_right(buffer);
23943        read_highlights
23944            .into_iter()
23945            .chain(write_highlights)
23946            .flat_map(move |ranges| {
23947                let start_ix = match ranges.binary_search_by(|probe| {
23948                    let cmp = probe.end.cmp(&left_position, buffer);
23949                    if cmp.is_ge() {
23950                        Ordering::Greater
23951                    } else {
23952                        Ordering::Less
23953                    }
23954                }) {
23955                    Ok(i) | Err(i) => i,
23956                };
23957
23958                ranges[start_ix..]
23959                    .iter()
23960                    .take_while(move |range| range.start.cmp(&right_position, buffer).is_le())
23961            })
23962    }
23963
23964    pub fn has_background_highlights(&self, key: HighlightKey) -> bool {
23965        self.background_highlights
23966            .get(&key)
23967            .is_some_and(|(_, highlights)| !highlights.is_empty())
23968    }
23969
23970    /// Returns all background highlights for a given range.
23971    ///
23972    /// The order of highlights is not deterministic, do sort the ranges if needed for the logic.
23973    pub fn background_highlights_in_range(
23974        &self,
23975        search_range: Range<Anchor>,
23976        display_snapshot: &DisplaySnapshot,
23977        theme: &Theme,
23978    ) -> Vec<(Range<DisplayPoint>, Hsla)> {
23979        let mut results = Vec::new();
23980        for (color_fetcher, ranges) in self.background_highlights.values() {
23981            let start_ix = match ranges.binary_search_by(|probe| {
23982                let cmp = probe
23983                    .end
23984                    .cmp(&search_range.start, &display_snapshot.buffer_snapshot());
23985                if cmp.is_gt() {
23986                    Ordering::Greater
23987                } else {
23988                    Ordering::Less
23989                }
23990            }) {
23991                Ok(i) | Err(i) => i,
23992            };
23993            for (index, range) in ranges[start_ix..].iter().enumerate() {
23994                if range
23995                    .start
23996                    .cmp(&search_range.end, &display_snapshot.buffer_snapshot())
23997                    .is_ge()
23998                {
23999                    break;
24000                }
24001
24002                let color = color_fetcher(&(start_ix + index), theme);
24003                let start = range.start.to_display_point(display_snapshot);
24004                let end = range.end.to_display_point(display_snapshot);
24005                results.push((start..end, color))
24006            }
24007        }
24008        results
24009    }
24010
24011    pub fn gutter_highlights_in_range(
24012        &self,
24013        search_range: Range<Anchor>,
24014        display_snapshot: &DisplaySnapshot,
24015        cx: &App,
24016    ) -> Vec<(Range<DisplayPoint>, Hsla)> {
24017        let mut results = Vec::new();
24018        for (color_fetcher, ranges) in self.gutter_highlights.values() {
24019            let color = color_fetcher(cx);
24020            let start_ix = match ranges.binary_search_by(|probe| {
24021                let cmp = probe
24022                    .end
24023                    .cmp(&search_range.start, &display_snapshot.buffer_snapshot());
24024                if cmp.is_gt() {
24025                    Ordering::Greater
24026                } else {
24027                    Ordering::Less
24028                }
24029            }) {
24030                Ok(i) | Err(i) => i,
24031            };
24032            for range in &ranges[start_ix..] {
24033                if range
24034                    .start
24035                    .cmp(&search_range.end, &display_snapshot.buffer_snapshot())
24036                    .is_ge()
24037                {
24038                    break;
24039                }
24040
24041                let start = range.start.to_display_point(display_snapshot);
24042                let end = range.end.to_display_point(display_snapshot);
24043                results.push((start..end, color))
24044            }
24045        }
24046        results
24047    }
24048
24049    /// Get the text ranges corresponding to the redaction query
24050    pub fn redacted_ranges(
24051        &self,
24052        search_range: Range<Anchor>,
24053        display_snapshot: &DisplaySnapshot,
24054        cx: &App,
24055    ) -> Vec<Range<DisplayPoint>> {
24056        display_snapshot
24057            .buffer_snapshot()
24058            .redacted_ranges(search_range, |file| {
24059                if let Some(file) = file {
24060                    file.is_private()
24061                        && EditorSettings::get(
24062                            Some(SettingsLocation {
24063                                worktree_id: file.worktree_id(cx),
24064                                path: file.path().as_ref(),
24065                            }),
24066                            cx,
24067                        )
24068                        .redact_private_values
24069                } else {
24070                    false
24071                }
24072            })
24073            .map(|range| {
24074                range.start.to_display_point(display_snapshot)
24075                    ..range.end.to_display_point(display_snapshot)
24076            })
24077            .collect()
24078    }
24079
24080    pub fn highlight_text_key(
24081        &mut self,
24082        key: HighlightKey,
24083        ranges: Vec<Range<Anchor>>,
24084        style: HighlightStyle,
24085        merge: bool,
24086        cx: &mut Context<Self>,
24087    ) {
24088        self.display_map.update(cx, |map, cx| {
24089            map.highlight_text(key, ranges, style, merge, cx);
24090        });
24091        cx.notify();
24092    }
24093
24094    pub fn highlight_text(
24095        &mut self,
24096        key: HighlightKey,
24097        ranges: Vec<Range<Anchor>>,
24098        style: HighlightStyle,
24099        cx: &mut Context<Self>,
24100    ) {
24101        self.display_map.update(cx, |map, cx| {
24102            map.highlight_text(key, ranges, style, false, cx)
24103        });
24104        cx.notify();
24105    }
24106
24107    pub fn text_highlights<'a>(
24108        &'a self,
24109        key: HighlightKey,
24110        cx: &'a App,
24111    ) -> Option<(HighlightStyle, &'a [Range<Anchor>])> {
24112        self.display_map.read(cx).text_highlights(key)
24113    }
24114
24115    pub fn clear_highlights(&mut self, key: HighlightKey, cx: &mut Context<Self>) {
24116        let cleared = self
24117            .display_map
24118            .update(cx, |map, _| map.clear_highlights(key));
24119        if cleared {
24120            cx.notify();
24121        }
24122    }
24123
24124    pub fn clear_highlights_with(
24125        &mut self,
24126        f: &mut dyn FnMut(&HighlightKey) -> bool,
24127        cx: &mut Context<Self>,
24128    ) {
24129        let cleared = self
24130            .display_map
24131            .update(cx, |map, _| map.clear_highlights_with(f));
24132        if cleared {
24133            cx.notify();
24134        }
24135    }
24136
24137    pub fn show_local_cursors(&self, window: &mut Window, cx: &mut App) -> bool {
24138        (self.read_only(cx) || self.blink_manager.read(cx).visible())
24139            && self.focus_handle.is_focused(window)
24140    }
24141
24142    pub fn set_show_cursor_when_unfocused(&mut self, is_enabled: bool, cx: &mut Context<Self>) {
24143        self.show_cursor_when_unfocused = is_enabled;
24144        cx.notify();
24145    }
24146
24147    fn on_buffer_changed(&mut self, _: Entity<MultiBuffer>, cx: &mut Context<Self>) {
24148        cx.notify();
24149    }
24150
24151    fn on_debug_session_event(
24152        &mut self,
24153        _session: Entity<Session>,
24154        event: &SessionEvent,
24155        cx: &mut Context<Self>,
24156    ) {
24157        if let SessionEvent::InvalidateInlineValue = event {
24158            self.refresh_inline_values(cx);
24159        }
24160    }
24161
24162    pub fn refresh_inline_values(&mut self, cx: &mut Context<Self>) {
24163        let Some(semantics) = self.semantics_provider.clone() else {
24164            return;
24165        };
24166
24167        if !self.inline_value_cache.enabled {
24168            let inlays = std::mem::take(&mut self.inline_value_cache.inlays);
24169            self.splice_inlays(&inlays, Vec::new(), cx);
24170            return;
24171        }
24172
24173        let current_execution_position = self
24174            .highlighted_rows
24175            .get(&TypeId::of::<ActiveDebugLine>())
24176            .and_then(|lines| lines.last().map(|line| line.range.end));
24177
24178        self.inline_value_cache.refresh_task = cx.spawn(async move |editor, cx| {
24179            let inline_values = editor
24180                .update(cx, |editor, cx| {
24181                    let Some(current_execution_position) = current_execution_position else {
24182                        return Some(Task::ready(Ok(Vec::new())));
24183                    };
24184
24185                    let buffer = editor.buffer.read_with(cx, |buffer, cx| {
24186                        let snapshot = buffer.snapshot(cx);
24187
24188                        let excerpt = snapshot.excerpt_containing(
24189                            current_execution_position..current_execution_position,
24190                        )?;
24191
24192                        editor.buffer.read(cx).buffer(excerpt.buffer_id())
24193                    })?;
24194
24195                    if current_execution_position
24196                        .text_anchor
24197                        .buffer_id
24198                        .is_some_and(|id| id != buffer.read(cx).remote_id())
24199                    {
24200                        return Some(Task::ready(Ok(Vec::new())));
24201                    }
24202
24203                    let range =
24204                        buffer.read(cx).anchor_before(0)..current_execution_position.text_anchor;
24205
24206                    semantics.inline_values(buffer, range, cx)
24207                })
24208                .ok()
24209                .flatten()?
24210                .await
24211                .context("refreshing debugger inlays")
24212                .log_err()?;
24213
24214            let mut buffer_inline_values: HashMap<BufferId, Vec<InlayHint>> = HashMap::default();
24215
24216            for (buffer_id, inline_value) in inline_values
24217                .into_iter()
24218                .filter_map(|hint| Some((hint.position.buffer_id?, hint)))
24219            {
24220                buffer_inline_values
24221                    .entry(buffer_id)
24222                    .or_default()
24223                    .push(inline_value);
24224            }
24225
24226            editor
24227                .update(cx, |editor, cx| {
24228                    let snapshot = editor.buffer.read(cx).snapshot(cx);
24229                    let mut new_inlays = Vec::default();
24230
24231                    for (excerpt_id, buffer_snapshot, _) in snapshot.excerpts() {
24232                        let buffer_id = buffer_snapshot.remote_id();
24233                        buffer_inline_values
24234                            .get(&buffer_id)
24235                            .into_iter()
24236                            .flatten()
24237                            .for_each(|hint| {
24238                                let inlay = Inlay::debugger(
24239                                    post_inc(&mut editor.next_inlay_id),
24240                                    Anchor::in_buffer(excerpt_id, hint.position),
24241                                    hint.text(),
24242                                );
24243                                if !inlay.text().chars().contains(&'\n') {
24244                                    new_inlays.push(inlay);
24245                                }
24246                            });
24247                    }
24248
24249                    let mut inlay_ids = new_inlays.iter().map(|inlay| inlay.id).collect();
24250                    std::mem::swap(&mut editor.inline_value_cache.inlays, &mut inlay_ids);
24251
24252                    editor.splice_inlays(&inlay_ids, new_inlays, cx);
24253                })
24254                .ok()?;
24255            Some(())
24256        });
24257    }
24258
24259    fn on_buffer_event(
24260        &mut self,
24261        multibuffer: &Entity<MultiBuffer>,
24262        event: &multi_buffer::Event,
24263        window: &mut Window,
24264        cx: &mut Context<Self>,
24265    ) {
24266        match event {
24267            multi_buffer::Event::Edited {
24268                edited_buffer,
24269                is_local,
24270            } => {
24271                self.scrollbar_marker_state.dirty = true;
24272                self.active_indent_guides_state.dirty = true;
24273                self.refresh_active_diagnostics(cx);
24274                self.refresh_code_actions(window, cx);
24275                self.refresh_single_line_folds(window, cx);
24276                let snapshot = self.snapshot(window, cx);
24277                self.refresh_matching_bracket_highlights(&snapshot, cx);
24278                self.refresh_outline_symbols_at_cursor(cx);
24279                self.refresh_sticky_headers(&snapshot, cx);
24280                if *is_local && self.has_active_edit_prediction() {
24281                    self.update_visible_edit_prediction(window, cx);
24282                }
24283
24284                // Clean up orphaned review comments after edits
24285                self.cleanup_orphaned_review_comments(cx);
24286
24287                if let Some(buffer) = edited_buffer {
24288                    if buffer.read(cx).file().is_none() {
24289                        cx.emit(EditorEvent::TitleChanged);
24290                    }
24291
24292                    if self.project.is_some() {
24293                        let buffer_id = buffer.read(cx).remote_id();
24294                        self.register_buffer(buffer_id, cx);
24295                        self.update_lsp_data(Some(buffer_id), window, cx);
24296                        self.refresh_inlay_hints(
24297                            InlayHintRefreshReason::BufferEdited(buffer_id),
24298                            cx,
24299                        );
24300                    }
24301                }
24302
24303                cx.emit(EditorEvent::BufferEdited);
24304                cx.emit(SearchEvent::MatchesInvalidated);
24305
24306                let Some(project) = &self.project else { return };
24307                let (telemetry, is_via_ssh) = {
24308                    let project = project.read(cx);
24309                    let telemetry = project.client().telemetry().clone();
24310                    let is_via_ssh = project.is_via_remote_server();
24311                    (telemetry, is_via_ssh)
24312                };
24313                telemetry.log_edit_event("editor", is_via_ssh);
24314            }
24315            multi_buffer::Event::ExcerptsAdded {
24316                buffer,
24317                predecessor,
24318                excerpts,
24319            } => {
24320                let buffer_id = buffer.read(cx).remote_id();
24321                if self.buffer.read(cx).diff_for(buffer_id).is_none()
24322                    && let Some(project) = &self.project
24323                {
24324                    update_uncommitted_diff_for_buffer(
24325                        cx.entity(),
24326                        project,
24327                        [buffer.clone()],
24328                        self.buffer.clone(),
24329                        cx,
24330                    )
24331                    .detach();
24332                }
24333                self.semantic_token_state
24334                    .invalidate_buffer(&buffer.read(cx).remote_id());
24335                self.update_lsp_data(Some(buffer_id), window, cx);
24336                self.refresh_inlay_hints(InlayHintRefreshReason::NewLinesShown, cx);
24337                self.refresh_runnables(None, window, cx);
24338                self.colorize_brackets(false, cx);
24339                self.refresh_selected_text_highlights(&self.display_snapshot(cx), true, window, cx);
24340                cx.emit(EditorEvent::ExcerptsAdded {
24341                    buffer: buffer.clone(),
24342                    predecessor: *predecessor,
24343                    excerpts: excerpts.clone(),
24344                });
24345            }
24346            multi_buffer::Event::ExcerptsRemoved {
24347                ids,
24348                removed_buffer_ids,
24349            } => {
24350                if let Some(inlay_hints) = &mut self.inlay_hints {
24351                    inlay_hints.remove_inlay_chunk_data(removed_buffer_ids);
24352                }
24353                self.refresh_inlay_hints(InlayHintRefreshReason::ExcerptsRemoved(ids.clone()), cx);
24354                for buffer_id in removed_buffer_ids {
24355                    self.registered_buffers.remove(buffer_id);
24356                    self.clear_runnables(Some(*buffer_id));
24357                    self.semantic_token_state.invalidate_buffer(buffer_id);
24358                    self.display_map.update(cx, |display_map, cx| {
24359                        display_map.invalidate_semantic_highlights(*buffer_id);
24360                        display_map.clear_lsp_folding_ranges(*buffer_id, cx);
24361                    });
24362                }
24363
24364                self.display_map.update(cx, |display_map, cx| {
24365                    display_map.unfold_buffers(removed_buffer_ids.iter().copied(), cx);
24366                });
24367
24368                jsx_tag_auto_close::refresh_enabled_in_any_buffer(self, multibuffer, cx);
24369                cx.emit(EditorEvent::ExcerptsRemoved {
24370                    ids: ids.clone(),
24371                    removed_buffer_ids: removed_buffer_ids.clone(),
24372                });
24373            }
24374            multi_buffer::Event::ExcerptsEdited {
24375                excerpt_ids,
24376                buffer_ids,
24377            } => {
24378                self.display_map.update(cx, |map, cx| {
24379                    map.unfold_buffers(buffer_ids.iter().copied(), cx)
24380                });
24381                cx.emit(EditorEvent::ExcerptsEdited {
24382                    ids: excerpt_ids.clone(),
24383                });
24384            }
24385            multi_buffer::Event::ExcerptsExpanded { ids } => {
24386                self.refresh_inlay_hints(InlayHintRefreshReason::NewLinesShown, cx);
24387                self.refresh_document_highlights(cx);
24388                let snapshot = multibuffer.read(cx).snapshot(cx);
24389                for id in ids {
24390                    self.bracket_fetched_tree_sitter_chunks.remove(id);
24391                    if let Some(buffer) = snapshot.buffer_for_excerpt(*id) {
24392                        self.semantic_token_state
24393                            .invalidate_buffer(&buffer.remote_id());
24394                    }
24395                }
24396                self.colorize_brackets(false, cx);
24397                self.update_lsp_data(None, window, cx);
24398                self.refresh_runnables(None, window, cx);
24399                cx.emit(EditorEvent::ExcerptsExpanded { ids: ids.clone() })
24400            }
24401            multi_buffer::Event::Reparsed(buffer_id) => {
24402                self.refresh_runnables(Some(*buffer_id), window, cx);
24403                self.refresh_selected_text_highlights(&self.display_snapshot(cx), true, window, cx);
24404                self.colorize_brackets(true, cx);
24405                jsx_tag_auto_close::refresh_enabled_in_any_buffer(self, multibuffer, cx);
24406
24407                cx.emit(EditorEvent::Reparsed(*buffer_id));
24408            }
24409            multi_buffer::Event::DiffHunksToggled => {
24410                self.refresh_runnables(None, window, cx);
24411            }
24412            multi_buffer::Event::LanguageChanged(buffer_id, is_fresh_language) => {
24413                if !is_fresh_language {
24414                    self.registered_buffers.remove(&buffer_id);
24415                }
24416                jsx_tag_auto_close::refresh_enabled_in_any_buffer(self, multibuffer, cx);
24417                cx.emit(EditorEvent::Reparsed(*buffer_id));
24418                self.update_edit_prediction_settings(cx);
24419                cx.notify();
24420            }
24421            multi_buffer::Event::DirtyChanged => cx.emit(EditorEvent::DirtyChanged),
24422            multi_buffer::Event::Saved => cx.emit(EditorEvent::Saved),
24423            multi_buffer::Event::FileHandleChanged
24424            | multi_buffer::Event::Reloaded
24425            | multi_buffer::Event::BufferDiffChanged => cx.emit(EditorEvent::TitleChanged),
24426            multi_buffer::Event::DiagnosticsUpdated => {
24427                self.update_diagnostics_state(window, cx);
24428            }
24429            _ => {}
24430        };
24431    }
24432
24433    fn update_diagnostics_state(&mut self, window: &mut Window, cx: &mut Context<'_, Editor>) {
24434        if !self.diagnostics_enabled() {
24435            return;
24436        }
24437        self.refresh_active_diagnostics(cx);
24438        self.refresh_inline_diagnostics(true, window, cx);
24439        self.scrollbar_marker_state.dirty = true;
24440        cx.notify();
24441    }
24442
24443    pub fn start_temporary_diff_override(&mut self) {
24444        self.load_diff_task.take();
24445        self.temporary_diff_override = true;
24446    }
24447
24448    pub fn end_temporary_diff_override(&mut self, cx: &mut Context<Self>) {
24449        self.temporary_diff_override = false;
24450        self.set_render_diff_hunk_controls(Arc::new(render_diff_hunk_controls), cx);
24451        self.buffer.update(cx, |buffer, cx| {
24452            buffer.set_all_diff_hunks_collapsed(cx);
24453        });
24454
24455        if let Some(project) = self.project.clone() {
24456            self.load_diff_task = Some(
24457                update_uncommitted_diff_for_buffer(
24458                    cx.entity(),
24459                    &project,
24460                    self.buffer.read(cx).all_buffers(),
24461                    self.buffer.clone(),
24462                    cx,
24463                )
24464                .shared(),
24465            );
24466        }
24467    }
24468
24469    fn on_display_map_changed(
24470        &mut self,
24471        _: Entity<DisplayMap>,
24472        _: &mut Window,
24473        cx: &mut Context<Self>,
24474    ) {
24475        cx.notify();
24476    }
24477
24478    fn fetch_accent_data(&self, cx: &App) -> Option<AccentData> {
24479        if !self.mode.is_full() {
24480            return None;
24481        }
24482
24483        let theme_settings = theme_settings::ThemeSettings::get_global(cx);
24484        let theme = cx.theme();
24485        let accent_colors = theme.accents().clone();
24486
24487        let accent_overrides = theme_settings
24488            .theme_overrides
24489            .get(theme.name.as_ref())
24490            .map(|theme_style| &theme_style.accents)
24491            .into_iter()
24492            .flatten()
24493            .chain(
24494                theme_settings
24495                    .experimental_theme_overrides
24496                    .as_ref()
24497                    .map(|overrides| &overrides.accents)
24498                    .into_iter()
24499                    .flatten(),
24500            )
24501            .flat_map(|accent| accent.0.clone().map(SharedString::from))
24502            .collect();
24503
24504        Some(AccentData {
24505            colors: accent_colors,
24506            overrides: accent_overrides,
24507        })
24508    }
24509
24510    fn fetch_applicable_language_settings(
24511        &self,
24512        cx: &App,
24513    ) -> HashMap<Option<LanguageName>, LanguageSettings> {
24514        if !self.mode.is_full() {
24515            return HashMap::default();
24516        }
24517
24518        self.buffer().read(cx).all_buffers().into_iter().fold(
24519            HashMap::default(),
24520            |mut acc, buffer| {
24521                let buffer = buffer.read(cx);
24522                let language = buffer.language().map(|language| language.name());
24523                if let hash_map::Entry::Vacant(v) = acc.entry(language) {
24524                    v.insert(LanguageSettings::for_buffer(&buffer, cx).into_owned());
24525                }
24526                acc
24527            },
24528        )
24529    }
24530
24531    fn settings_changed(&mut self, window: &mut Window, cx: &mut Context<Self>) {
24532        let new_language_settings = self.fetch_applicable_language_settings(cx);
24533        let language_settings_changed = new_language_settings != self.applicable_language_settings;
24534        self.applicable_language_settings = new_language_settings;
24535
24536        let new_accents = self.fetch_accent_data(cx);
24537        let accents_changed = new_accents != self.accent_data;
24538        self.accent_data = new_accents;
24539
24540        if self.diagnostics_enabled() {
24541            let new_severity = EditorSettings::get_global(cx)
24542                .diagnostics_max_severity
24543                .unwrap_or(DiagnosticSeverity::Hint);
24544            self.set_max_diagnostics_severity(new_severity, cx);
24545        }
24546        self.refresh_runnables(None, window, cx);
24547        self.update_edit_prediction_settings(cx);
24548        self.refresh_edit_prediction(true, false, window, cx);
24549        self.refresh_inline_values(cx);
24550
24551        let old_cursor_shape = self.cursor_shape;
24552        let old_show_breadcrumbs = self.show_breadcrumbs;
24553
24554        {
24555            let editor_settings = EditorSettings::get_global(cx);
24556            self.scroll_manager.vertical_scroll_margin = editor_settings.vertical_scroll_margin;
24557            self.show_breadcrumbs = editor_settings.toolbar.breadcrumbs;
24558            self.cursor_shape = editor_settings.cursor_shape.unwrap_or_default();
24559            self.hide_mouse_mode = editor_settings.hide_mouse.unwrap_or_default();
24560        }
24561
24562        if old_cursor_shape != self.cursor_shape {
24563            cx.emit(EditorEvent::CursorShapeChanged);
24564        }
24565
24566        if old_show_breadcrumbs != self.show_breadcrumbs {
24567            cx.emit(EditorEvent::BreadcrumbsChanged);
24568        }
24569
24570        let (restore_unsaved_buffers, show_inline_diagnostics, inline_blame_enabled) = {
24571            let project_settings = ProjectSettings::get_global(cx);
24572            (
24573                project_settings.session.restore_unsaved_buffers,
24574                project_settings.diagnostics.inline.enabled,
24575                project_settings.git.inline_blame.enabled,
24576            )
24577        };
24578        self.buffer_serialization = self
24579            .should_serialize_buffer()
24580            .then(|| BufferSerialization::new(restore_unsaved_buffers));
24581
24582        if self.mode.is_full() {
24583            if self.show_inline_diagnostics != show_inline_diagnostics {
24584                self.show_inline_diagnostics = show_inline_diagnostics;
24585                self.refresh_inline_diagnostics(false, window, cx);
24586            }
24587
24588            if self.git_blame_inline_enabled != inline_blame_enabled {
24589                self.toggle_git_blame_inline_internal(false, window, cx);
24590            }
24591
24592            let minimap_settings = EditorSettings::get_global(cx).minimap;
24593            if self.minimap_visibility != MinimapVisibility::Disabled {
24594                if self.minimap_visibility.settings_visibility()
24595                    != minimap_settings.minimap_enabled()
24596                {
24597                    self.set_minimap_visibility(
24598                        MinimapVisibility::for_mode(self.mode(), cx),
24599                        window,
24600                        cx,
24601                    );
24602                } else if let Some(minimap_entity) = self.minimap.as_ref() {
24603                    minimap_entity.update(cx, |minimap_editor, cx| {
24604                        minimap_editor.update_minimap_configuration(minimap_settings, cx)
24605                    })
24606                }
24607            }
24608
24609            if language_settings_changed || accents_changed {
24610                self.colorize_brackets(true, cx);
24611            }
24612
24613            if language_settings_changed {
24614                self.clear_disabled_lsp_folding_ranges(window, cx);
24615                self.refresh_document_symbols(None, cx);
24616            }
24617
24618            if let Some(inlay_splice) = self.colors.as_mut().and_then(|colors| {
24619                colors.render_mode_updated(EditorSettings::get_global(cx).lsp_document_colors)
24620            }) {
24621                if !inlay_splice.is_empty() {
24622                    self.splice_inlays(&inlay_splice.to_remove, inlay_splice.to_insert, cx);
24623                }
24624                self.refresh_document_colors(None, window, cx);
24625            }
24626
24627            self.refresh_inlay_hints(
24628                InlayHintRefreshReason::SettingsChange(inlay_hint_settings(
24629                    self.selections.newest_anchor().head(),
24630                    &self.buffer.read(cx).snapshot(cx),
24631                    cx,
24632                )),
24633                cx,
24634            );
24635
24636            let new_semantic_token_rules = ProjectSettings::get_global(cx)
24637                .global_lsp_settings
24638                .semantic_token_rules
24639                .clone();
24640            let semantic_token_rules_changed = self
24641                .semantic_token_state
24642                .update_rules(new_semantic_token_rules);
24643            if language_settings_changed || semantic_token_rules_changed {
24644                self.invalidate_semantic_tokens(None);
24645                self.refresh_semantic_tokens(None, None, cx);
24646            }
24647        }
24648
24649        cx.notify();
24650    }
24651
24652    fn theme_changed(&mut self, _: &mut Window, cx: &mut Context<Self>) {
24653        if !self.mode.is_full() {
24654            return;
24655        }
24656
24657        let new_accents = self.fetch_accent_data(cx);
24658        if new_accents != self.accent_data {
24659            self.accent_data = new_accents;
24660            self.colorize_brackets(true, cx);
24661        }
24662
24663        self.invalidate_semantic_tokens(None);
24664        self.refresh_semantic_tokens(None, None, cx);
24665    }
24666
24667    pub fn set_searchable(&mut self, searchable: bool) {
24668        self.searchable = searchable;
24669    }
24670
24671    pub fn searchable(&self) -> bool {
24672        self.searchable
24673    }
24674
24675    pub fn open_excerpts_in_split(
24676        &mut self,
24677        _: &OpenExcerptsSplit,
24678        window: &mut Window,
24679        cx: &mut Context<Self>,
24680    ) {
24681        self.open_excerpts_common(None, true, window, cx)
24682    }
24683
24684    pub fn open_excerpts(&mut self, _: &OpenExcerpts, window: &mut Window, cx: &mut Context<Self>) {
24685        self.open_excerpts_common(None, false, window, cx)
24686    }
24687
24688    pub(crate) fn open_excerpts_common(
24689        &mut self,
24690        jump_data: Option<JumpData>,
24691        split: bool,
24692        window: &mut Window,
24693        cx: &mut Context<Self>,
24694    ) {
24695        if self.buffer.read(cx).is_singleton() {
24696            cx.propagate();
24697            return;
24698        }
24699
24700        let mut new_selections_by_buffer = HashMap::default();
24701        match &jump_data {
24702            Some(JumpData::MultiBufferPoint {
24703                excerpt_id,
24704                position,
24705                anchor,
24706                line_offset_from_top,
24707            }) => {
24708                let multi_buffer_snapshot = self.buffer.read(cx).snapshot(cx);
24709                if let Some(buffer) = multi_buffer_snapshot
24710                    .buffer_id_for_excerpt(*excerpt_id)
24711                    .and_then(|buffer_id| self.buffer.read(cx).buffer(buffer_id))
24712                {
24713                    let buffer_snapshot = buffer.read(cx).snapshot();
24714                    let jump_to_point = if buffer_snapshot.can_resolve(anchor) {
24715                        language::ToPoint::to_point(anchor, &buffer_snapshot)
24716                    } else {
24717                        buffer_snapshot.clip_point(*position, Bias::Left)
24718                    };
24719                    let jump_to_offset = buffer_snapshot.point_to_offset(jump_to_point);
24720                    new_selections_by_buffer.insert(
24721                        buffer,
24722                        (
24723                            vec![BufferOffset(jump_to_offset)..BufferOffset(jump_to_offset)],
24724                            Some(*line_offset_from_top),
24725                        ),
24726                    );
24727                }
24728            }
24729            Some(JumpData::MultiBufferRow {
24730                row,
24731                line_offset_from_top,
24732            }) => {
24733                let point = MultiBufferPoint::new(row.0, 0);
24734                if let Some((buffer, buffer_point, _)) =
24735                    self.buffer.read(cx).point_to_buffer_point(point, cx)
24736                {
24737                    let buffer_offset = buffer.read(cx).point_to_offset(buffer_point);
24738                    new_selections_by_buffer
24739                        .entry(buffer)
24740                        .or_insert((Vec::new(), Some(*line_offset_from_top)))
24741                        .0
24742                        .push(BufferOffset(buffer_offset)..BufferOffset(buffer_offset))
24743                }
24744            }
24745            None => {
24746                let selections = self
24747                    .selections
24748                    .all::<MultiBufferOffset>(&self.display_snapshot(cx));
24749                let multi_buffer = self.buffer.read(cx);
24750                for selection in selections {
24751                    for (snapshot, range, _, anchor) in multi_buffer
24752                        .snapshot(cx)
24753                        .range_to_buffer_ranges_with_deleted_hunks(selection.range())
24754                    {
24755                        if let Some(anchor) = anchor {
24756                            let Some(buffer_handle) = multi_buffer.buffer_for_anchor(anchor, cx)
24757                            else {
24758                                continue;
24759                            };
24760                            let offset = text::ToOffset::to_offset(
24761                                &anchor.text_anchor,
24762                                &buffer_handle.read(cx).snapshot(),
24763                            );
24764                            let range = BufferOffset(offset)..BufferOffset(offset);
24765                            new_selections_by_buffer
24766                                .entry(buffer_handle)
24767                                .or_insert((Vec::new(), None))
24768                                .0
24769                                .push(range)
24770                        } else {
24771                            let Some(buffer_handle) = multi_buffer.buffer(snapshot.remote_id())
24772                            else {
24773                                continue;
24774                            };
24775                            new_selections_by_buffer
24776                                .entry(buffer_handle)
24777                                .or_insert((Vec::new(), None))
24778                                .0
24779                                .push(range)
24780                        }
24781                    }
24782                }
24783            }
24784        }
24785
24786        if self.delegate_open_excerpts {
24787            let selections_by_buffer: HashMap<_, _> = new_selections_by_buffer
24788                .into_iter()
24789                .map(|(buffer, value)| (buffer.read(cx).remote_id(), value))
24790                .collect();
24791            if !selections_by_buffer.is_empty() {
24792                cx.emit(EditorEvent::OpenExcerptsRequested {
24793                    selections_by_buffer,
24794                    split,
24795                });
24796            }
24797            return;
24798        }
24799
24800        let Some(workspace) = self.workspace() else {
24801            cx.propagate();
24802            return;
24803        };
24804
24805        new_selections_by_buffer
24806            .retain(|buffer, _| buffer.read(cx).file().is_none_or(|file| file.can_open()));
24807
24808        if new_selections_by_buffer.is_empty() {
24809            return;
24810        }
24811
24812        Self::open_buffers_in_workspace(
24813            workspace.downgrade(),
24814            new_selections_by_buffer,
24815            split,
24816            window,
24817            cx,
24818        );
24819    }
24820
24821    pub(crate) fn open_buffers_in_workspace(
24822        workspace: WeakEntity<Workspace>,
24823        new_selections_by_buffer: HashMap<
24824            Entity<language::Buffer>,
24825            (Vec<Range<BufferOffset>>, Option<u32>),
24826        >,
24827        split: bool,
24828        window: &mut Window,
24829        cx: &mut App,
24830    ) {
24831        // We defer the pane interaction because we ourselves are a workspace item
24832        // and activating a new item causes the pane to call a method on us reentrantly,
24833        // which panics if we're on the stack.
24834        window.defer(cx, move |window, cx| {
24835            workspace
24836                .update(cx, |workspace, cx| {
24837                    let pane = if split {
24838                        workspace.adjacent_pane(window, cx)
24839                    } else {
24840                        workspace.active_pane().clone()
24841                    };
24842
24843                    for (buffer, (ranges, scroll_offset)) in new_selections_by_buffer {
24844                        let buffer_read = buffer.read(cx);
24845                        let (has_file, is_project_file) = if let Some(file) = buffer_read.file() {
24846                            (true, project::File::from_dyn(Some(file)).is_some())
24847                        } else {
24848                            (false, false)
24849                        };
24850
24851                        // If project file is none workspace.open_project_item will fail to open the excerpt
24852                        // in a pre existing workspace item if one exists, because Buffer entity_id will be None
24853                        // so we check if there's a tab match in that case first
24854                        let editor = (!has_file || !is_project_file)
24855                            .then(|| {
24856                                // Handle file-less buffers separately: those are not really the project items, so won't have a project path or entity id,
24857                                // so `workspace.open_project_item` will never find them, always opening a new editor.
24858                                // Instead, we try to activate the existing editor in the pane first.
24859                                let (editor, pane_item_index, pane_item_id) =
24860                                    pane.read(cx).items().enumerate().find_map(|(i, item)| {
24861                                        let editor = item.downcast::<Editor>()?;
24862                                        let singleton_buffer =
24863                                            editor.read(cx).buffer().read(cx).as_singleton()?;
24864                                        if singleton_buffer == buffer {
24865                                            Some((editor, i, item.item_id()))
24866                                        } else {
24867                                            None
24868                                        }
24869                                    })?;
24870                                pane.update(cx, |pane, cx| {
24871                                    pane.activate_item(pane_item_index, true, true, window, cx);
24872                                    if !PreviewTabsSettings::get_global(cx)
24873                                        .enable_preview_from_multibuffer
24874                                    {
24875                                        pane.unpreview_item_if_preview(pane_item_id);
24876                                    }
24877                                });
24878                                Some(editor)
24879                            })
24880                            .flatten()
24881                            .unwrap_or_else(|| {
24882                                let keep_old_preview = PreviewTabsSettings::get_global(cx)
24883                                    .enable_keep_preview_on_code_navigation;
24884                                let allow_new_preview = PreviewTabsSettings::get_global(cx)
24885                                    .enable_preview_from_multibuffer;
24886                                workspace.open_project_item::<Self>(
24887                                    pane.clone(),
24888                                    buffer,
24889                                    true,
24890                                    true,
24891                                    keep_old_preview,
24892                                    allow_new_preview,
24893                                    window,
24894                                    cx,
24895                                )
24896                            });
24897
24898                        editor.update(cx, |editor, cx| {
24899                            if has_file && !is_project_file {
24900                                editor.set_read_only(true);
24901                            }
24902                            let autoscroll = match scroll_offset {
24903                                Some(scroll_offset) => {
24904                                    Autoscroll::top_relative(scroll_offset as usize)
24905                                }
24906                                None => Autoscroll::newest(),
24907                            };
24908                            let nav_history = editor.nav_history.take();
24909                            let multibuffer_snapshot = editor.buffer().read(cx).snapshot(cx);
24910                            let Some((excerpt_id, _, buffer_snapshot)) =
24911                                multibuffer_snapshot.as_singleton()
24912                            else {
24913                                return;
24914                            };
24915                            editor.change_selections(
24916                                SelectionEffects::scroll(autoscroll),
24917                                window,
24918                                cx,
24919                                |s| {
24920                                    s.select_ranges(ranges.into_iter().map(|range| {
24921                                        let range = buffer_snapshot.anchor_before(range.start)
24922                                            ..buffer_snapshot.anchor_after(range.end);
24923                                        multibuffer_snapshot
24924                                            .anchor_range_in_excerpt(excerpt_id, range)
24925                                            .unwrap()
24926                                    }));
24927                                },
24928                            );
24929                            editor.nav_history = nav_history;
24930                        });
24931                    }
24932                })
24933                .ok();
24934        });
24935    }
24936
24937    fn marked_text_ranges(&self, cx: &App) -> Option<Vec<Range<MultiBufferOffsetUtf16>>> {
24938        let snapshot = self.buffer.read(cx).read(cx);
24939        let (_, ranges) = self.text_highlights(HighlightKey::InputComposition, cx)?;
24940        Some(
24941            ranges
24942                .iter()
24943                .map(move |range| {
24944                    range.start.to_offset_utf16(&snapshot)..range.end.to_offset_utf16(&snapshot)
24945                })
24946                .collect(),
24947        )
24948    }
24949
24950    fn selection_replacement_ranges(
24951        &self,
24952        range: Range<MultiBufferOffsetUtf16>,
24953        cx: &mut App,
24954    ) -> Vec<Range<MultiBufferOffsetUtf16>> {
24955        let selections = self
24956            .selections
24957            .all::<MultiBufferOffsetUtf16>(&self.display_snapshot(cx));
24958        let newest_selection = selections
24959            .iter()
24960            .max_by_key(|selection| selection.id)
24961            .unwrap();
24962        let start_delta = range.start.0.0 as isize - newest_selection.start.0.0 as isize;
24963        let end_delta = range.end.0.0 as isize - newest_selection.end.0.0 as isize;
24964        let snapshot = self.buffer.read(cx).read(cx);
24965        selections
24966            .into_iter()
24967            .map(|mut selection| {
24968                selection.start.0.0 =
24969                    (selection.start.0.0 as isize).saturating_add(start_delta) as usize;
24970                selection.end.0.0 = (selection.end.0.0 as isize).saturating_add(end_delta) as usize;
24971                snapshot.clip_offset_utf16(selection.start, Bias::Left)
24972                    ..snapshot.clip_offset_utf16(selection.end, Bias::Right)
24973            })
24974            .collect()
24975    }
24976
24977    fn report_editor_event(
24978        &self,
24979        reported_event: ReportEditorEvent,
24980        file_extension: Option<String>,
24981        cx: &App,
24982    ) {
24983        if cfg!(any(test, feature = "test-support")) {
24984            return;
24985        }
24986
24987        let Some(project) = &self.project else { return };
24988
24989        // If None, we are in a file without an extension
24990        let file = self
24991            .buffer
24992            .read(cx)
24993            .as_singleton()
24994            .and_then(|b| b.read(cx).file());
24995        let file_extension = file_extension.or(file
24996            .as_ref()
24997            .and_then(|file| Path::new(file.file_name(cx)).extension())
24998            .and_then(|e| e.to_str())
24999            .map(|a| a.to_string()));
25000
25001        let vim_mode = vim_mode_setting::VimModeSetting::try_get(cx)
25002            .map(|vim_mode| vim_mode.0)
25003            .unwrap_or(false);
25004
25005        let edit_predictions_provider = all_language_settings(file, cx).edit_predictions.provider;
25006        let copilot_enabled = edit_predictions_provider
25007            == language::language_settings::EditPredictionProvider::Copilot;
25008        let copilot_enabled_for_language = self
25009            .buffer
25010            .read(cx)
25011            .language_settings(cx)
25012            .show_edit_predictions;
25013
25014        let project = project.read(cx);
25015        let event_type = reported_event.event_type();
25016
25017        if let ReportEditorEvent::Saved { auto_saved } = reported_event {
25018            telemetry::event!(
25019                event_type,
25020                type = if auto_saved {"autosave"} else {"manual"},
25021                file_extension,
25022                vim_mode,
25023                copilot_enabled,
25024                copilot_enabled_for_language,
25025                edit_predictions_provider,
25026                is_via_ssh = project.is_via_remote_server(),
25027            );
25028        } else {
25029            telemetry::event!(
25030                event_type,
25031                file_extension,
25032                vim_mode,
25033                copilot_enabled,
25034                copilot_enabled_for_language,
25035                edit_predictions_provider,
25036                is_via_ssh = project.is_via_remote_server(),
25037            );
25038        };
25039    }
25040
25041    /// Copy the highlighted chunks to the clipboard as JSON. The format is an array of lines,
25042    /// with each line being an array of {text, highlight} objects.
25043    fn copy_highlight_json(
25044        &mut self,
25045        _: &CopyHighlightJson,
25046        _: &mut Window,
25047        cx: &mut Context<Self>,
25048    ) {
25049        #[derive(Serialize)]
25050        struct Chunk<'a> {
25051            text: String,
25052            highlight: Option<&'a str>,
25053        }
25054
25055        let snapshot = self.buffer.read(cx).snapshot(cx);
25056        let mut selection = self.selections.newest::<Point>(&self.display_snapshot(cx));
25057        let max_point = snapshot.max_point();
25058
25059        let range = if self.selections.line_mode() {
25060            selection.start = Point::new(selection.start.row, 0);
25061            selection.end = cmp::min(max_point, Point::new(selection.end.row + 1, 0));
25062            selection.goal = SelectionGoal::None;
25063            selection.range()
25064        } else if selection.is_empty() {
25065            Point::new(0, 0)..max_point
25066        } else {
25067            selection.range()
25068        };
25069
25070        let chunks = snapshot.chunks(range, true);
25071        let mut lines = Vec::new();
25072        let mut line: VecDeque<Chunk> = VecDeque::new();
25073
25074        let Some(style) = self.style.as_ref() else {
25075            return;
25076        };
25077
25078        for chunk in chunks {
25079            let highlight = chunk
25080                .syntax_highlight_id
25081                .and_then(|id| style.syntax.get_capture_name(id));
25082
25083            let mut chunk_lines = chunk.text.split('\n').peekable();
25084            while let Some(text) = chunk_lines.next() {
25085                let mut merged_with_last_token = false;
25086                if let Some(last_token) = line.back_mut()
25087                    && last_token.highlight == highlight
25088                {
25089                    last_token.text.push_str(text);
25090                    merged_with_last_token = true;
25091                }
25092
25093                if !merged_with_last_token {
25094                    line.push_back(Chunk {
25095                        text: text.into(),
25096                        highlight,
25097                    });
25098                }
25099
25100                if chunk_lines.peek().is_some() {
25101                    if line.len() > 1 && line.front().unwrap().text.is_empty() {
25102                        line.pop_front();
25103                    }
25104                    if line.len() > 1 && line.back().unwrap().text.is_empty() {
25105                        line.pop_back();
25106                    }
25107
25108                    lines.push(mem::take(&mut line));
25109                }
25110            }
25111        }
25112
25113        if line.iter().any(|chunk| !chunk.text.is_empty()) {
25114            lines.push(line);
25115        }
25116
25117        let Some(lines) = serde_json::to_string_pretty(&lines).log_err() else {
25118            return;
25119        };
25120        cx.write_to_clipboard(ClipboardItem::new_string(lines));
25121    }
25122
25123    pub fn open_context_menu(
25124        &mut self,
25125        _: &OpenContextMenu,
25126        window: &mut Window,
25127        cx: &mut Context<Self>,
25128    ) {
25129        self.request_autoscroll(Autoscroll::newest(), cx);
25130        let position = self
25131            .selections
25132            .newest_display(&self.display_snapshot(cx))
25133            .start;
25134        mouse_context_menu::deploy_context_menu(self, None, position, window, cx);
25135    }
25136
25137    pub fn replay_insert_event(
25138        &mut self,
25139        text: &str,
25140        relative_utf16_range: Option<Range<isize>>,
25141        window: &mut Window,
25142        cx: &mut Context<Self>,
25143    ) {
25144        if !self.input_enabled {
25145            cx.emit(EditorEvent::InputIgnored { text: text.into() });
25146            return;
25147        }
25148        if let Some(relative_utf16_range) = relative_utf16_range {
25149            let selections = self
25150                .selections
25151                .all::<MultiBufferOffsetUtf16>(&self.display_snapshot(cx));
25152            self.change_selections(SelectionEffects::no_scroll(), window, cx, |s| {
25153                let new_ranges = selections.into_iter().map(|range| {
25154                    let start = MultiBufferOffsetUtf16(OffsetUtf16(
25155                        range
25156                            .head()
25157                            .0
25158                            .0
25159                            .saturating_add_signed(relative_utf16_range.start),
25160                    ));
25161                    let end = MultiBufferOffsetUtf16(OffsetUtf16(
25162                        range
25163                            .head()
25164                            .0
25165                            .0
25166                            .saturating_add_signed(relative_utf16_range.end),
25167                    ));
25168                    start..end
25169                });
25170                s.select_ranges(new_ranges);
25171            });
25172        }
25173
25174        self.handle_input(text, window, cx);
25175    }
25176
25177    pub fn is_focused(&self, window: &Window) -> bool {
25178        self.focus_handle.is_focused(window)
25179    }
25180
25181    fn handle_focus(&mut self, window: &mut Window, cx: &mut Context<Self>) {
25182        cx.emit(EditorEvent::Focused);
25183
25184        if let Some(descendant) = self
25185            .last_focused_descendant
25186            .take()
25187            .and_then(|descendant| descendant.upgrade())
25188        {
25189            window.focus(&descendant, cx);
25190        } else {
25191            if let Some(blame) = self.blame.as_ref() {
25192                blame.update(cx, GitBlame::focus)
25193            }
25194
25195            self.blink_manager.update(cx, BlinkManager::enable);
25196            self.show_cursor_names(window, cx);
25197            self.buffer.update(cx, |buffer, cx| {
25198                buffer.finalize_last_transaction(cx);
25199                if self.leader_id.is_none() {
25200                    buffer.set_active_selections(
25201                        &self.selections.disjoint_anchors_arc(),
25202                        self.selections.line_mode(),
25203                        self.cursor_shape,
25204                        cx,
25205                    );
25206                }
25207            });
25208
25209            if let Some(position_map) = self.last_position_map.clone()
25210                && !self.mouse_cursor_hidden
25211            {
25212                EditorElement::mouse_moved(
25213                    self,
25214                    &MouseMoveEvent {
25215                        position: window.mouse_position(),
25216                        pressed_button: None,
25217                        modifiers: window.modifiers(),
25218                    },
25219                    &position_map,
25220                    None,
25221                    window,
25222                    cx,
25223                );
25224            }
25225        }
25226    }
25227
25228    fn handle_focus_in(&mut self, _: &mut Window, cx: &mut Context<Self>) {
25229        cx.emit(EditorEvent::FocusedIn)
25230    }
25231
25232    fn handle_focus_out(
25233        &mut self,
25234        event: FocusOutEvent,
25235        _window: &mut Window,
25236        cx: &mut Context<Self>,
25237    ) {
25238        if event.blurred != self.focus_handle {
25239            self.last_focused_descendant = Some(event.blurred);
25240        }
25241        self.selection_drag_state = SelectionDragState::None;
25242        self.refresh_inlay_hints(InlayHintRefreshReason::ModifiersChanged(false), cx);
25243    }
25244
25245    pub fn handle_blur(&mut self, window: &mut Window, cx: &mut Context<Self>) {
25246        self.blink_manager.update(cx, BlinkManager::disable);
25247        self.buffer
25248            .update(cx, |buffer, cx| buffer.remove_active_selections(cx));
25249
25250        if let Some(blame) = self.blame.as_ref() {
25251            blame.update(cx, GitBlame::blur)
25252        }
25253        if !self.hover_state.focused(window, cx) {
25254            hide_hover(self, cx);
25255        }
25256        if !self
25257            .context_menu
25258            .borrow()
25259            .as_ref()
25260            .is_some_and(|context_menu| context_menu.focused(window, cx))
25261        {
25262            self.hide_context_menu(window, cx);
25263        }
25264        self.take_active_edit_prediction(true, cx);
25265        cx.emit(EditorEvent::Blurred);
25266        cx.notify();
25267    }
25268
25269    pub fn observe_pending_input(&mut self, window: &mut Window, cx: &mut Context<Self>) {
25270        let mut pending: String = window
25271            .pending_input_keystrokes()
25272            .into_iter()
25273            .flatten()
25274            .filter_map(|keystroke| keystroke.key_char.clone())
25275            .collect();
25276
25277        if !self.input_enabled || self.read_only || !self.focus_handle.is_focused(window) {
25278            pending = "".to_string();
25279        }
25280
25281        let existing_pending = self
25282            .text_highlights(HighlightKey::PendingInput, cx)
25283            .map(|(_, ranges)| ranges.to_vec());
25284        if existing_pending.is_none() && pending.is_empty() {
25285            return;
25286        }
25287        let transaction =
25288            self.transact(window, cx, |this, window, cx| {
25289                let selections = this
25290                    .selections
25291                    .all::<MultiBufferOffset>(&this.display_snapshot(cx));
25292                let edits = selections
25293                    .iter()
25294                    .map(|selection| (selection.end..selection.end, pending.clone()));
25295                this.edit(edits, cx);
25296                this.change_selections(SelectionEffects::no_scroll(), window, cx, |s| {
25297                    s.select_ranges(selections.into_iter().enumerate().map(|(ix, sel)| {
25298                        sel.start + ix * pending.len()..sel.end + ix * pending.len()
25299                    }));
25300                });
25301                if let Some(existing_ranges) = existing_pending {
25302                    let edits = existing_ranges.iter().map(|range| (range.clone(), ""));
25303                    this.edit(edits, cx);
25304                }
25305            });
25306
25307        let snapshot = self.snapshot(window, cx);
25308        let ranges = self
25309            .selections
25310            .all::<MultiBufferOffset>(&snapshot.display_snapshot)
25311            .into_iter()
25312            .map(|selection| {
25313                snapshot.buffer_snapshot().anchor_after(selection.end)
25314                    ..snapshot
25315                        .buffer_snapshot()
25316                        .anchor_before(selection.end + pending.len())
25317            })
25318            .collect();
25319
25320        if pending.is_empty() {
25321            self.clear_highlights(HighlightKey::PendingInput, cx);
25322        } else {
25323            self.highlight_text(
25324                HighlightKey::PendingInput,
25325                ranges,
25326                HighlightStyle {
25327                    underline: Some(UnderlineStyle {
25328                        thickness: px(1.),
25329                        color: None,
25330                        wavy: false,
25331                    }),
25332                    ..Default::default()
25333                },
25334                cx,
25335            );
25336        }
25337
25338        self.ime_transaction = self.ime_transaction.or(transaction);
25339        if let Some(transaction) = self.ime_transaction {
25340            self.buffer.update(cx, |buffer, cx| {
25341                buffer.group_until_transaction(transaction, cx);
25342            });
25343        }
25344
25345        if self
25346            .text_highlights(HighlightKey::PendingInput, cx)
25347            .is_none()
25348        {
25349            self.ime_transaction.take();
25350        }
25351    }
25352
25353    pub fn register_action_renderer(
25354        &mut self,
25355        listener: impl Fn(&Editor, &mut Window, &mut Context<Editor>) + 'static,
25356    ) -> Subscription {
25357        let id = self.next_editor_action_id.post_inc();
25358        self.editor_actions
25359            .borrow_mut()
25360            .insert(id, Box::new(listener));
25361
25362        let editor_actions = self.editor_actions.clone();
25363        Subscription::new(move || {
25364            editor_actions.borrow_mut().remove(&id);
25365        })
25366    }
25367
25368    pub fn register_action<A: Action>(
25369        &mut self,
25370        listener: impl Fn(&A, &mut Window, &mut App) + 'static,
25371    ) -> Subscription {
25372        let id = self.next_editor_action_id.post_inc();
25373        let listener = Arc::new(listener);
25374        self.editor_actions.borrow_mut().insert(
25375            id,
25376            Box::new(move |_, window, _| {
25377                let listener = listener.clone();
25378                window.on_action(TypeId::of::<A>(), move |action, phase, window, cx| {
25379                    let action = action.downcast_ref().unwrap();
25380                    if phase == DispatchPhase::Bubble {
25381                        listener(action, window, cx)
25382                    }
25383                })
25384            }),
25385        );
25386
25387        let editor_actions = self.editor_actions.clone();
25388        Subscription::new(move || {
25389            editor_actions.borrow_mut().remove(&id);
25390        })
25391    }
25392
25393    pub fn file_header_size(&self) -> u32 {
25394        FILE_HEADER_HEIGHT
25395    }
25396
25397    pub fn restore(
25398        &mut self,
25399        revert_changes: HashMap<BufferId, Vec<(Range<text::Anchor>, Rope)>>,
25400        window: &mut Window,
25401        cx: &mut Context<Self>,
25402    ) {
25403        self.buffer().update(cx, |multi_buffer, cx| {
25404            for (buffer_id, changes) in revert_changes {
25405                if let Some(buffer) = multi_buffer.buffer(buffer_id) {
25406                    buffer.update(cx, |buffer, cx| {
25407                        buffer.edit(
25408                            changes
25409                                .into_iter()
25410                                .map(|(range, text)| (range, text.to_string())),
25411                            None,
25412                            cx,
25413                        );
25414                    });
25415                }
25416            }
25417        });
25418        self.change_selections(SelectionEffects::no_scroll(), window, cx, |selections| {
25419            selections.refresh()
25420        });
25421    }
25422
25423    pub fn to_pixel_point(
25424        &mut self,
25425        source: Anchor,
25426        editor_snapshot: &EditorSnapshot,
25427        window: &mut Window,
25428        cx: &mut App,
25429    ) -> Option<gpui::Point<Pixels>> {
25430        let source_point = source.to_display_point(editor_snapshot);
25431        self.display_to_pixel_point(source_point, editor_snapshot, window, cx)
25432    }
25433
25434    pub fn display_to_pixel_point(
25435        &mut self,
25436        source: DisplayPoint,
25437        editor_snapshot: &EditorSnapshot,
25438        window: &mut Window,
25439        cx: &mut App,
25440    ) -> Option<gpui::Point<Pixels>> {
25441        let line_height = self.style(cx).text.line_height_in_pixels(window.rem_size());
25442        let text_layout_details = self.text_layout_details(window, cx);
25443        let scroll_top = text_layout_details
25444            .scroll_anchor
25445            .scroll_position(editor_snapshot)
25446            .y;
25447
25448        if source.row().as_f64() < scroll_top.floor() {
25449            return None;
25450        }
25451        let source_x = editor_snapshot.x_for_display_point(source, &text_layout_details);
25452        let source_y = line_height * (source.row().as_f64() - scroll_top) as f32;
25453        Some(gpui::Point::new(source_x, source_y))
25454    }
25455
25456    pub fn has_visible_completions_menu(&self) -> bool {
25457        !self.edit_prediction_preview_is_active()
25458            && self.context_menu.borrow().as_ref().is_some_and(|menu| {
25459                menu.visible() && matches!(menu, CodeContextMenu::Completions(_))
25460            })
25461    }
25462
25463    pub fn register_addon<T: Addon>(&mut self, instance: T) {
25464        if self.mode.is_minimap() {
25465            return;
25466        }
25467        self.addons
25468            .insert(std::any::TypeId::of::<T>(), Box::new(instance));
25469    }
25470
25471    pub fn unregister_addon<T: Addon>(&mut self) {
25472        self.addons.remove(&std::any::TypeId::of::<T>());
25473    }
25474
25475    pub fn addon<T: Addon>(&self) -> Option<&T> {
25476        let type_id = std::any::TypeId::of::<T>();
25477        self.addons
25478            .get(&type_id)
25479            .and_then(|item| item.to_any().downcast_ref::<T>())
25480    }
25481
25482    pub fn addon_mut<T: Addon>(&mut self) -> Option<&mut T> {
25483        let type_id = std::any::TypeId::of::<T>();
25484        self.addons
25485            .get_mut(&type_id)
25486            .and_then(|item| item.to_any_mut()?.downcast_mut::<T>())
25487    }
25488
25489    fn character_dimensions(&self, window: &mut Window, cx: &mut App) -> CharacterDimensions {
25490        let text_layout_details = self.text_layout_details(window, cx);
25491        let style = &text_layout_details.editor_style;
25492        let font_id = window.text_system().resolve_font(&style.text.font());
25493        let font_size = style.text.font_size.to_pixels(window.rem_size());
25494        let line_height = style.text.line_height_in_pixels(window.rem_size());
25495        let em_width = window.text_system().em_width(font_id, font_size).unwrap();
25496        let em_advance = window.text_system().em_advance(font_id, font_size).unwrap();
25497
25498        CharacterDimensions {
25499            em_width,
25500            em_advance,
25501            line_height,
25502        }
25503    }
25504
25505    pub fn wait_for_diff_to_load(&self) -> Option<Shared<Task<()>>> {
25506        self.load_diff_task.clone()
25507    }
25508
25509    fn read_metadata_from_db(
25510        &mut self,
25511        item_id: u64,
25512        workspace_id: WorkspaceId,
25513        window: &mut Window,
25514        cx: &mut Context<Editor>,
25515    ) {
25516        if self.buffer_kind(cx) == ItemBufferKind::Singleton
25517            && !self.mode.is_minimap()
25518            && WorkspaceSettings::get(None, cx).restore_on_startup
25519                != RestoreOnStartupBehavior::EmptyTab
25520        {
25521            let buffer_snapshot = OnceCell::new();
25522
25523            // Get file path for path-based fold lookup
25524            let file_path: Option<Arc<Path>> =
25525                self.buffer().read(cx).as_singleton().and_then(|buffer| {
25526                    project::File::from_dyn(buffer.read(cx).file())
25527                        .map(|file| Arc::from(file.abs_path(cx)))
25528                });
25529
25530            // Try file_folds (path-based) first, fallback to editor_folds (migration)
25531            let db = EditorDb::global(cx);
25532            let (folds, needs_migration) = if let Some(ref path) = file_path {
25533                if let Some(folds) = db.get_file_folds(workspace_id, path).log_err()
25534                    && !folds.is_empty()
25535                {
25536                    (Some(folds), false)
25537                } else if let Some(folds) = db.get_editor_folds(item_id, workspace_id).log_err()
25538                    && !folds.is_empty()
25539                {
25540                    // Found old editor_folds data, will migrate to file_folds
25541                    (Some(folds), true)
25542                } else {
25543                    (None, false)
25544                }
25545            } else {
25546                // No file path, try editor_folds as fallback
25547                let folds = db.get_editor_folds(item_id, workspace_id).log_err();
25548                (folds.filter(|f| !f.is_empty()), false)
25549            };
25550
25551            if let Some(folds) = folds {
25552                let snapshot = buffer_snapshot.get_or_init(|| self.buffer.read(cx).snapshot(cx));
25553                let snapshot_len = snapshot.len().0;
25554
25555                // Helper: search for fingerprint in buffer, return offset if found
25556                let find_fingerprint = |fingerprint: &str, search_start: usize| -> Option<usize> {
25557                    // Ensure we start at a character boundary (defensive)
25558                    let search_start = snapshot
25559                        .clip_offset(MultiBufferOffset(search_start), Bias::Left)
25560                        .0;
25561                    let search_end = snapshot_len.saturating_sub(fingerprint.len());
25562
25563                    let mut byte_offset = search_start;
25564                    for ch in snapshot.chars_at(MultiBufferOffset(search_start)) {
25565                        if byte_offset > search_end {
25566                            break;
25567                        }
25568                        if snapshot.contains_str_at(MultiBufferOffset(byte_offset), fingerprint) {
25569                            return Some(byte_offset);
25570                        }
25571                        byte_offset += ch.len_utf8();
25572                    }
25573                    None
25574                };
25575
25576                // Track search position to handle duplicate fingerprints correctly.
25577                // Folds are stored in document order, so we advance after each match.
25578                let mut search_start = 0usize;
25579
25580                // Collect db_folds for migration (only folds with valid fingerprints)
25581                let mut db_folds_for_migration: Vec<(usize, usize, String, String)> = Vec::new();
25582
25583                let valid_folds: Vec<_> = folds
25584                    .into_iter()
25585                    .filter_map(|(stored_start, stored_end, start_fp, end_fp)| {
25586                        // Skip folds without fingerprints (old data before migration)
25587                        let sfp = start_fp?;
25588                        let efp = end_fp?;
25589                        let efp_len = efp.len();
25590
25591                        // Fast path: check if fingerprints match at stored offsets
25592                        // Note: end_fp is content BEFORE fold end, so check at (stored_end - efp_len)
25593                        let start_matches = stored_start < snapshot_len
25594                            && snapshot.contains_str_at(MultiBufferOffset(stored_start), &sfp);
25595                        let efp_check_pos = stored_end.saturating_sub(efp_len);
25596                        let end_matches = efp_check_pos >= stored_start
25597                            && stored_end <= snapshot_len
25598                            && snapshot.contains_str_at(MultiBufferOffset(efp_check_pos), &efp);
25599
25600                        let (new_start, new_end) = if start_matches && end_matches {
25601                            // Offsets unchanged, use stored values
25602                            (stored_start, stored_end)
25603                        } else if sfp == efp {
25604                            // Short fold: identical fingerprints can only match once per search
25605                            // Use stored fold length to compute new_end
25606                            let new_start = find_fingerprint(&sfp, search_start)?;
25607                            let fold_len = stored_end - stored_start;
25608                            let new_end = new_start + fold_len;
25609                            (new_start, new_end)
25610                        } else {
25611                            // Slow path: search for fingerprints in buffer
25612                            let new_start = find_fingerprint(&sfp, search_start)?;
25613                            // Search for end_fp after start, then add efp_len to get actual fold end
25614                            let efp_pos = find_fingerprint(&efp, new_start + sfp.len())?;
25615                            let new_end = efp_pos + efp_len;
25616                            (new_start, new_end)
25617                        };
25618
25619                        // Advance search position for next fold
25620                        search_start = new_end;
25621
25622                        // Validate fold makes sense (end must be after start)
25623                        if new_end <= new_start {
25624                            return None;
25625                        }
25626
25627                        // Collect for migration if needed
25628                        if needs_migration {
25629                            db_folds_for_migration.push((new_start, new_end, sfp, efp));
25630                        }
25631
25632                        Some(
25633                            snapshot.clip_offset(MultiBufferOffset(new_start), Bias::Left)
25634                                ..snapshot.clip_offset(MultiBufferOffset(new_end), Bias::Right),
25635                        )
25636                    })
25637                    .collect();
25638
25639                if !valid_folds.is_empty() {
25640                    self.fold_ranges(valid_folds, false, window, cx);
25641
25642                    // Migrate from editor_folds to file_folds if we loaded from old table
25643                    if needs_migration {
25644                        if let Some(ref path) = file_path {
25645                            let path = path.clone();
25646                            let db = EditorDb::global(cx);
25647                            cx.spawn(async move |_, _| {
25648                                db.save_file_folds(workspace_id, path, db_folds_for_migration)
25649                                    .await
25650                                    .log_err();
25651                            })
25652                            .detach();
25653                        }
25654                    }
25655                }
25656            }
25657
25658            if let Some(selections) = db.get_editor_selections(item_id, workspace_id).log_err()
25659                && !selections.is_empty()
25660            {
25661                let snapshot = buffer_snapshot.get_or_init(|| self.buffer.read(cx).snapshot(cx));
25662                // skip adding the initial selection to selection history
25663                self.selection_history.mode = SelectionHistoryMode::Skipping;
25664                self.change_selections(SelectionEffects::no_scroll(), window, cx, |s| {
25665                    s.select_ranges(selections.into_iter().map(|(start, end)| {
25666                        snapshot.clip_offset(MultiBufferOffset(start), Bias::Left)
25667                            ..snapshot.clip_offset(MultiBufferOffset(end), Bias::Right)
25668                    }));
25669                });
25670                self.selection_history.mode = SelectionHistoryMode::Normal;
25671            };
25672        }
25673
25674        self.read_scroll_position_from_db(item_id, workspace_id, window, cx);
25675    }
25676
25677    /// Load folds from the file_folds database table by file path.
25678    /// Used when manually opening a file that was previously closed.
25679    fn load_folds_from_db(
25680        &mut self,
25681        workspace_id: WorkspaceId,
25682        file_path: PathBuf,
25683        window: &mut Window,
25684        cx: &mut Context<Editor>,
25685    ) {
25686        if self.mode.is_minimap()
25687            || WorkspaceSettings::get(None, cx).restore_on_startup
25688                == RestoreOnStartupBehavior::EmptyTab
25689        {
25690            return;
25691        }
25692
25693        let Some(folds) = EditorDb::global(cx)
25694            .get_file_folds(workspace_id, &file_path)
25695            .log_err()
25696        else {
25697            return;
25698        };
25699        if folds.is_empty() {
25700            return;
25701        }
25702
25703        let snapshot = self.buffer.read(cx).snapshot(cx);
25704        let snapshot_len = snapshot.len().0;
25705
25706        // Helper: search for fingerprint in buffer, return offset if found
25707        let find_fingerprint = |fingerprint: &str, search_start: usize| -> Option<usize> {
25708            let search_start = snapshot
25709                .clip_offset(MultiBufferOffset(search_start), Bias::Left)
25710                .0;
25711            let search_end = snapshot_len.saturating_sub(fingerprint.len());
25712
25713            let mut byte_offset = search_start;
25714            for ch in snapshot.chars_at(MultiBufferOffset(search_start)) {
25715                if byte_offset > search_end {
25716                    break;
25717                }
25718                if snapshot.contains_str_at(MultiBufferOffset(byte_offset), fingerprint) {
25719                    return Some(byte_offset);
25720                }
25721                byte_offset += ch.len_utf8();
25722            }
25723            None
25724        };
25725
25726        let mut search_start = 0usize;
25727
25728        let valid_folds: Vec<_> = folds
25729            .into_iter()
25730            .filter_map(|(stored_start, stored_end, start_fp, end_fp)| {
25731                let sfp = start_fp?;
25732                let efp = end_fp?;
25733                let efp_len = efp.len();
25734
25735                let start_matches = stored_start < snapshot_len
25736                    && snapshot.contains_str_at(MultiBufferOffset(stored_start), &sfp);
25737                let efp_check_pos = stored_end.saturating_sub(efp_len);
25738                let end_matches = efp_check_pos >= stored_start
25739                    && stored_end <= snapshot_len
25740                    && snapshot.contains_str_at(MultiBufferOffset(efp_check_pos), &efp);
25741
25742                let (new_start, new_end) = if start_matches && end_matches {
25743                    (stored_start, stored_end)
25744                } else if sfp == efp {
25745                    let new_start = find_fingerprint(&sfp, search_start)?;
25746                    let fold_len = stored_end - stored_start;
25747                    let new_end = new_start + fold_len;
25748                    (new_start, new_end)
25749                } else {
25750                    let new_start = find_fingerprint(&sfp, search_start)?;
25751                    let efp_pos = find_fingerprint(&efp, new_start + sfp.len())?;
25752                    let new_end = efp_pos + efp_len;
25753                    (new_start, new_end)
25754                };
25755
25756                search_start = new_end;
25757
25758                if new_end <= new_start {
25759                    return None;
25760                }
25761
25762                Some(
25763                    snapshot.clip_offset(MultiBufferOffset(new_start), Bias::Left)
25764                        ..snapshot.clip_offset(MultiBufferOffset(new_end), Bias::Right),
25765                )
25766            })
25767            .collect();
25768
25769        if !valid_folds.is_empty() {
25770            self.fold_ranges(valid_folds, false, window, cx);
25771        }
25772    }
25773
25774    fn lsp_data_enabled(&self) -> bool {
25775        self.enable_lsp_data && self.mode().is_full()
25776    }
25777
25778    fn update_lsp_data(
25779        &mut self,
25780        for_buffer: Option<BufferId>,
25781        window: &mut Window,
25782        cx: &mut Context<'_, Self>,
25783    ) {
25784        if !self.lsp_data_enabled() {
25785            return;
25786        }
25787
25788        if let Some(buffer_id) = for_buffer {
25789            self.pull_diagnostics(buffer_id, window, cx);
25790        }
25791        self.refresh_semantic_tokens(for_buffer, None, cx);
25792        self.refresh_document_colors(for_buffer, window, cx);
25793        self.refresh_folding_ranges(for_buffer, window, cx);
25794        self.refresh_document_symbols(for_buffer, cx);
25795    }
25796
25797    fn register_visible_buffers(&mut self, cx: &mut Context<Self>) {
25798        if !self.lsp_data_enabled() {
25799            return;
25800        }
25801        for (_, (visible_buffer, _, _)) in self.visible_excerpts(true, cx) {
25802            self.register_buffer(visible_buffer.read(cx).remote_id(), cx);
25803        }
25804    }
25805
25806    fn register_buffer(&mut self, buffer_id: BufferId, cx: &mut Context<Self>) {
25807        if !self.lsp_data_enabled() {
25808            return;
25809        }
25810
25811        if !self.registered_buffers.contains_key(&buffer_id)
25812            && let Some(project) = self.project.as_ref()
25813        {
25814            if let Some(buffer) = self.buffer.read(cx).buffer(buffer_id) {
25815                project.update(cx, |project, cx| {
25816                    self.registered_buffers.insert(
25817                        buffer_id,
25818                        project.register_buffer_with_language_servers(&buffer, cx),
25819                    );
25820                });
25821            } else {
25822                self.registered_buffers.remove(&buffer_id);
25823            }
25824        }
25825    }
25826
25827    fn create_style(&self, cx: &App) -> EditorStyle {
25828        let settings = ThemeSettings::get_global(cx);
25829
25830        let mut text_style = match self.mode {
25831            EditorMode::SingleLine | EditorMode::AutoHeight { .. } => TextStyle {
25832                color: cx.theme().colors().editor_foreground,
25833                font_family: settings.ui_font.family.clone(),
25834                font_features: settings.ui_font.features.clone(),
25835                font_fallbacks: settings.ui_font.fallbacks.clone(),
25836                font_size: rems(0.875).into(),
25837                font_weight: settings.ui_font.weight,
25838                line_height: relative(settings.buffer_line_height.value()),
25839                ..Default::default()
25840            },
25841            EditorMode::Full { .. } | EditorMode::Minimap { .. } => TextStyle {
25842                color: cx.theme().colors().editor_foreground,
25843                font_family: settings.buffer_font.family.clone(),
25844                font_features: settings.buffer_font.features.clone(),
25845                font_fallbacks: settings.buffer_font.fallbacks.clone(),
25846                font_size: settings.buffer_font_size(cx).into(),
25847                font_weight: settings.buffer_font.weight,
25848                line_height: relative(settings.buffer_line_height.value()),
25849                ..Default::default()
25850            },
25851        };
25852        if let Some(text_style_refinement) = &self.text_style_refinement {
25853            text_style.refine(text_style_refinement)
25854        }
25855
25856        let background = match self.mode {
25857            EditorMode::SingleLine => cx.theme().system().transparent,
25858            EditorMode::AutoHeight { .. } => cx.theme().system().transparent,
25859            EditorMode::Full { .. } => cx.theme().colors().editor_background,
25860            EditorMode::Minimap { .. } => cx.theme().colors().editor_background.opacity(0.7),
25861        };
25862
25863        EditorStyle {
25864            background,
25865            border: cx.theme().colors().border,
25866            local_player: cx.theme().players().local(),
25867            text: text_style,
25868            scrollbar_width: EditorElement::SCROLLBAR_WIDTH,
25869            syntax: cx.theme().syntax().clone(),
25870            status: cx.theme().status().clone(),
25871            inlay_hints_style: make_inlay_hints_style(cx),
25872            edit_prediction_styles: make_suggestion_styles(cx),
25873            unnecessary_code_fade: settings.unnecessary_code_fade,
25874            show_underlines: self.diagnostics_enabled(),
25875        }
25876    }
25877
25878    fn breadcrumbs_inner(&self, cx: &App) -> Option<Vec<HighlightedText>> {
25879        let multibuffer = self.buffer().read(cx);
25880        let is_singleton = multibuffer.is_singleton();
25881        let (buffer_id, symbols) = self.outline_symbols_at_cursor.as_ref()?;
25882        let buffer = multibuffer.buffer(*buffer_id)?;
25883
25884        let buffer = buffer.read(cx);
25885        // In a multi-buffer layout, we don't want to include the filename in the breadcrumbs
25886        let mut breadcrumbs = if is_singleton {
25887            let text = self.breadcrumb_header.clone().unwrap_or_else(|| {
25888                buffer
25889                    .snapshot()
25890                    .resolve_file_path(
25891                        self.project
25892                            .as_ref()
25893                            .map(|project| project.read(cx).visible_worktrees(cx).count() > 1)
25894                            .unwrap_or_default(),
25895                        cx,
25896                    )
25897                    .unwrap_or_else(|| {
25898                        if multibuffer.is_singleton() {
25899                            multibuffer.title(cx).to_string()
25900                        } else {
25901                            "untitled".to_string()
25902                        }
25903                    })
25904            });
25905            vec![HighlightedText {
25906                text: text.into(),
25907                highlights: vec![],
25908            }]
25909        } else {
25910            vec![]
25911        };
25912
25913        breadcrumbs.extend(symbols.iter().map(|symbol| HighlightedText {
25914            text: symbol.text.clone().into(),
25915            highlights: symbol.highlight_ranges.clone(),
25916        }));
25917        Some(breadcrumbs)
25918    }
25919
25920    fn disable_lsp_data(&mut self) {
25921        self.enable_lsp_data = false;
25922    }
25923
25924    fn disable_runnables(&mut self) {
25925        self.enable_runnables = false;
25926    }
25927
25928    fn update_data_on_scroll(&mut self, window: &mut Window, cx: &mut Context<'_, Self>) {
25929        self.register_visible_buffers(cx);
25930        self.colorize_brackets(false, cx);
25931        self.refresh_inlay_hints(InlayHintRefreshReason::NewLinesShown, cx);
25932        if !self.buffer().read(cx).is_singleton() {
25933            self.update_lsp_data(None, window, cx);
25934            self.refresh_runnables(None, window, cx);
25935        }
25936    }
25937}
25938
25939fn edit_for_markdown_paste<'a>(
25940    buffer: &MultiBufferSnapshot,
25941    range: Range<MultiBufferOffset>,
25942    to_insert: &'a str,
25943    url: Option<url::Url>,
25944) -> (Range<MultiBufferOffset>, Cow<'a, str>) {
25945    if url.is_none() {
25946        return (range, Cow::Borrowed(to_insert));
25947    };
25948
25949    let old_text = buffer.text_for_range(range.clone()).collect::<String>();
25950
25951    let new_text = if range.is_empty() || url::Url::parse(&old_text).is_ok() {
25952        Cow::Borrowed(to_insert)
25953    } else {
25954        Cow::Owned(format!("[{old_text}]({to_insert})"))
25955    };
25956    (range, new_text)
25957}
25958
25959fn process_completion_for_edit(
25960    completion: &Completion,
25961    intent: CompletionIntent,
25962    buffer: &Entity<Buffer>,
25963    cursor_position: &text::Anchor,
25964    cx: &mut Context<Editor>,
25965) -> CompletionEdit {
25966    let buffer = buffer.read(cx);
25967    let buffer_snapshot = buffer.snapshot();
25968    let (snippet, new_text) = if completion.is_snippet() {
25969        let mut snippet_source = completion.new_text.clone();
25970        // Workaround for typescript language server issues so that methods don't expand within
25971        // strings and functions with type expressions. The previous point is used because the query
25972        // for function identifier doesn't match when the cursor is immediately after. See PR #30312
25973        let previous_point = text::ToPoint::to_point(cursor_position, &buffer_snapshot);
25974        let previous_point = if previous_point.column > 0 {
25975            cursor_position.to_previous_offset(&buffer_snapshot)
25976        } else {
25977            cursor_position.to_offset(&buffer_snapshot)
25978        };
25979        if let Some(scope) = buffer_snapshot.language_scope_at(previous_point)
25980            && scope.prefers_label_for_snippet_in_completion()
25981            && let Some(label) = completion.label()
25982            && matches!(
25983                completion.kind(),
25984                Some(CompletionItemKind::FUNCTION) | Some(CompletionItemKind::METHOD)
25985            )
25986        {
25987            snippet_source = label;
25988        }
25989        match Snippet::parse(&snippet_source).log_err() {
25990            Some(parsed_snippet) => (Some(parsed_snippet.clone()), parsed_snippet.text),
25991            None => (None, completion.new_text.clone()),
25992        }
25993    } else {
25994        (None, completion.new_text.clone())
25995    };
25996
25997    let mut range_to_replace = {
25998        let replace_range = &completion.replace_range;
25999        if let CompletionSource::Lsp {
26000            insert_range: Some(insert_range),
26001            ..
26002        } = &completion.source
26003        {
26004            debug_assert_eq!(
26005                insert_range.start, replace_range.start,
26006                "insert_range and replace_range should start at the same position"
26007            );
26008            debug_assert!(
26009                insert_range
26010                    .start
26011                    .cmp(cursor_position, &buffer_snapshot)
26012                    .is_le(),
26013                "insert_range should start before or at cursor position"
26014            );
26015            debug_assert!(
26016                replace_range
26017                    .start
26018                    .cmp(cursor_position, &buffer_snapshot)
26019                    .is_le(),
26020                "replace_range should start before or at cursor position"
26021            );
26022
26023            let should_replace = match intent {
26024                CompletionIntent::CompleteWithInsert => false,
26025                CompletionIntent::CompleteWithReplace => true,
26026                CompletionIntent::Complete | CompletionIntent::Compose => {
26027                    let insert_mode = LanguageSettings::for_buffer(&buffer, cx)
26028                        .completions
26029                        .lsp_insert_mode;
26030                    match insert_mode {
26031                        LspInsertMode::Insert => false,
26032                        LspInsertMode::Replace => true,
26033                        LspInsertMode::ReplaceSubsequence => {
26034                            let mut text_to_replace = buffer.chars_for_range(
26035                                buffer.anchor_before(replace_range.start)
26036                                    ..buffer.anchor_after(replace_range.end),
26037                            );
26038                            let mut current_needle = text_to_replace.next();
26039                            for haystack_ch in completion.label.text.chars() {
26040                                if let Some(needle_ch) = current_needle
26041                                    && haystack_ch.eq_ignore_ascii_case(&needle_ch)
26042                                {
26043                                    current_needle = text_to_replace.next();
26044                                }
26045                            }
26046                            current_needle.is_none()
26047                        }
26048                        LspInsertMode::ReplaceSuffix => {
26049                            if replace_range
26050                                .end
26051                                .cmp(cursor_position, &buffer_snapshot)
26052                                .is_gt()
26053                            {
26054                                let range_after_cursor = *cursor_position..replace_range.end;
26055                                let text_after_cursor = buffer
26056                                    .text_for_range(
26057                                        buffer.anchor_before(range_after_cursor.start)
26058                                            ..buffer.anchor_after(range_after_cursor.end),
26059                                    )
26060                                    .collect::<String>()
26061                                    .to_ascii_lowercase();
26062                                completion
26063                                    .label
26064                                    .text
26065                                    .to_ascii_lowercase()
26066                                    .ends_with(&text_after_cursor)
26067                            } else {
26068                                true
26069                            }
26070                        }
26071                    }
26072                }
26073            };
26074
26075            if should_replace {
26076                replace_range.clone()
26077            } else {
26078                insert_range.clone()
26079            }
26080        } else {
26081            replace_range.clone()
26082        }
26083    };
26084
26085    if range_to_replace
26086        .end
26087        .cmp(cursor_position, &buffer_snapshot)
26088        .is_lt()
26089    {
26090        range_to_replace.end = *cursor_position;
26091    }
26092
26093    let replace_range = range_to_replace.to_offset(buffer);
26094    CompletionEdit {
26095        new_text,
26096        replace_range: BufferOffset(replace_range.start)..BufferOffset(replace_range.end),
26097        snippet,
26098    }
26099}
26100
26101struct CompletionEdit {
26102    new_text: String,
26103    replace_range: Range<BufferOffset>,
26104    snippet: Option<Snippet>,
26105}
26106
26107fn comment_delimiter_for_newline(
26108    start_point: &Point,
26109    buffer: &MultiBufferSnapshot,
26110    language: &LanguageScope,
26111) -> Option<Arc<str>> {
26112    let delimiters = language.line_comment_prefixes();
26113    let max_len_of_delimiter = delimiters.iter().map(|delimiter| delimiter.len()).max()?;
26114    let (snapshot, range) = buffer.buffer_line_for_row(MultiBufferRow(start_point.row))?;
26115
26116    let num_of_whitespaces = snapshot
26117        .chars_for_range(range.clone())
26118        .take_while(|c| c.is_whitespace())
26119        .count();
26120    let comment_candidate = snapshot
26121        .chars_for_range(range.clone())
26122        .skip(num_of_whitespaces)
26123        .take(max_len_of_delimiter + 2)
26124        .collect::<String>();
26125    let (delimiter, trimmed_len, is_repl) = delimiters
26126        .iter()
26127        .filter_map(|delimiter| {
26128            let prefix = delimiter.trim_end();
26129            if comment_candidate.starts_with(prefix) {
26130                let is_repl = if let Some(stripped_comment) = comment_candidate.strip_prefix(prefix)
26131                {
26132                    stripped_comment.starts_with(" %%")
26133                } else {
26134                    false
26135                };
26136                Some((delimiter, prefix.len(), is_repl))
26137            } else {
26138                None
26139            }
26140        })
26141        .max_by_key(|(_, len, _)| *len)?;
26142
26143    if let Some(BlockCommentConfig {
26144        start: block_start, ..
26145    }) = language.block_comment()
26146    {
26147        let block_start_trimmed = block_start.trim_end();
26148        if block_start_trimmed.starts_with(delimiter.trim_end()) {
26149            let line_content = snapshot
26150                .chars_for_range(range.clone())
26151                .skip(num_of_whitespaces)
26152                .take(block_start_trimmed.len())
26153                .collect::<String>();
26154
26155            if line_content.starts_with(block_start_trimmed) {
26156                return None;
26157            }
26158        }
26159    }
26160
26161    let cursor_is_placed_after_comment_marker =
26162        num_of_whitespaces + trimmed_len <= start_point.column as usize;
26163    if cursor_is_placed_after_comment_marker {
26164        if !is_repl {
26165            return Some(delimiter.clone());
26166        }
26167
26168        let line_content_after_cursor: String = snapshot
26169            .chars_for_range(range)
26170            .skip(start_point.column as usize)
26171            .collect();
26172
26173        if line_content_after_cursor.trim().is_empty() {
26174            return None;
26175        } else {
26176            return Some(delimiter.clone());
26177        }
26178    } else {
26179        None
26180    }
26181}
26182
26183fn documentation_delimiter_for_newline(
26184    start_point: &Point,
26185    buffer: &MultiBufferSnapshot,
26186    language: &LanguageScope,
26187    newline_config: &mut NewlineConfig,
26188) -> Option<Arc<str>> {
26189    let BlockCommentConfig {
26190        start: start_tag,
26191        end: end_tag,
26192        prefix: delimiter,
26193        tab_size: len,
26194    } = language.documentation_comment()?;
26195    let is_within_block_comment = buffer
26196        .language_scope_at(*start_point)
26197        .is_some_and(|scope| scope.override_name() == Some("comment"));
26198    if !is_within_block_comment {
26199        return None;
26200    }
26201
26202    let (snapshot, range) = buffer.buffer_line_for_row(MultiBufferRow(start_point.row))?;
26203
26204    let num_of_whitespaces = snapshot
26205        .chars_for_range(range.clone())
26206        .take_while(|c| c.is_whitespace())
26207        .count();
26208
26209    // 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.
26210    let column = start_point.column;
26211    let cursor_is_after_start_tag = {
26212        let start_tag_len = start_tag.len();
26213        let start_tag_line = snapshot
26214            .chars_for_range(range.clone())
26215            .skip(num_of_whitespaces)
26216            .take(start_tag_len)
26217            .collect::<String>();
26218        if start_tag_line.starts_with(start_tag.as_ref()) {
26219            num_of_whitespaces + start_tag_len <= column as usize
26220        } else {
26221            false
26222        }
26223    };
26224
26225    let cursor_is_after_delimiter = {
26226        let delimiter_trim = delimiter.trim_end();
26227        let delimiter_line = snapshot
26228            .chars_for_range(range.clone())
26229            .skip(num_of_whitespaces)
26230            .take(delimiter_trim.len())
26231            .collect::<String>();
26232        if delimiter_line.starts_with(delimiter_trim) {
26233            num_of_whitespaces + delimiter_trim.len() <= column as usize
26234        } else {
26235            false
26236        }
26237    };
26238
26239    let mut needs_extra_line = false;
26240    let mut extra_line_additional_indent = IndentSize::spaces(0);
26241
26242    let cursor_is_before_end_tag_if_exists = {
26243        let mut char_position = 0u32;
26244        let mut end_tag_offset = None;
26245
26246        'outer: for chunk in snapshot.text_for_range(range) {
26247            if let Some(byte_pos) = chunk.find(&**end_tag) {
26248                let chars_before_match = chunk[..byte_pos].chars().count() as u32;
26249                end_tag_offset = Some(char_position + chars_before_match);
26250                break 'outer;
26251            }
26252            char_position += chunk.chars().count() as u32;
26253        }
26254
26255        if let Some(end_tag_offset) = end_tag_offset {
26256            let cursor_is_before_end_tag = column <= end_tag_offset;
26257            if cursor_is_after_start_tag {
26258                if cursor_is_before_end_tag {
26259                    needs_extra_line = true;
26260                }
26261                let cursor_is_at_start_of_end_tag = column == end_tag_offset;
26262                if cursor_is_at_start_of_end_tag {
26263                    extra_line_additional_indent.len = *len;
26264                }
26265            }
26266            cursor_is_before_end_tag
26267        } else {
26268            true
26269        }
26270    };
26271
26272    if (cursor_is_after_start_tag || cursor_is_after_delimiter)
26273        && cursor_is_before_end_tag_if_exists
26274    {
26275        let additional_indent = if cursor_is_after_start_tag {
26276            IndentSize::spaces(*len)
26277        } else {
26278            IndentSize::spaces(0)
26279        };
26280
26281        *newline_config = NewlineConfig::Newline {
26282            additional_indent,
26283            extra_line_additional_indent: if needs_extra_line {
26284                Some(extra_line_additional_indent)
26285            } else {
26286                None
26287            },
26288            prevent_auto_indent: true,
26289        };
26290        Some(delimiter.clone())
26291    } else {
26292        None
26293    }
26294}
26295
26296const ORDERED_LIST_MAX_MARKER_LEN: usize = 16;
26297
26298fn list_delimiter_for_newline(
26299    start_point: &Point,
26300    buffer: &MultiBufferSnapshot,
26301    language: &LanguageScope,
26302    newline_config: &mut NewlineConfig,
26303) -> Option<Arc<str>> {
26304    let (snapshot, range) = buffer.buffer_line_for_row(MultiBufferRow(start_point.row))?;
26305
26306    let num_of_whitespaces = snapshot
26307        .chars_for_range(range.clone())
26308        .take_while(|c| c.is_whitespace())
26309        .count();
26310
26311    let task_list_entries: Vec<_> = language
26312        .task_list()
26313        .into_iter()
26314        .flat_map(|config| {
26315            config
26316                .prefixes
26317                .iter()
26318                .map(|prefix| (prefix.as_ref(), config.continuation.as_ref()))
26319        })
26320        .collect();
26321    let unordered_list_entries: Vec<_> = language
26322        .unordered_list()
26323        .iter()
26324        .map(|marker| (marker.as_ref(), marker.as_ref()))
26325        .collect();
26326
26327    let all_entries: Vec<_> = task_list_entries
26328        .into_iter()
26329        .chain(unordered_list_entries)
26330        .collect();
26331
26332    if let Some(max_prefix_len) = all_entries.iter().map(|(p, _)| p.len()).max() {
26333        let candidate: String = snapshot
26334            .chars_for_range(range.clone())
26335            .skip(num_of_whitespaces)
26336            .take(max_prefix_len)
26337            .collect();
26338
26339        if let Some((prefix, continuation)) = all_entries
26340            .iter()
26341            .filter(|(prefix, _)| candidate.starts_with(*prefix))
26342            .max_by_key(|(prefix, _)| prefix.len())
26343        {
26344            let end_of_prefix = num_of_whitespaces + prefix.len();
26345            let cursor_is_after_prefix = end_of_prefix <= start_point.column as usize;
26346            let has_content_after_marker = snapshot
26347                .chars_for_range(range)
26348                .skip(end_of_prefix)
26349                .any(|c| !c.is_whitespace());
26350
26351            if has_content_after_marker && cursor_is_after_prefix {
26352                return Some((*continuation).into());
26353            }
26354
26355            if start_point.column as usize == end_of_prefix {
26356                if num_of_whitespaces == 0 {
26357                    *newline_config = NewlineConfig::ClearCurrentLine;
26358                } else {
26359                    *newline_config = NewlineConfig::UnindentCurrentLine {
26360                        continuation: (*continuation).into(),
26361                    };
26362                }
26363            }
26364
26365            return None;
26366        }
26367    }
26368
26369    let candidate: String = snapshot
26370        .chars_for_range(range.clone())
26371        .skip(num_of_whitespaces)
26372        .take(ORDERED_LIST_MAX_MARKER_LEN)
26373        .collect();
26374
26375    for ordered_config in language.ordered_list() {
26376        let regex = match Regex::new(&ordered_config.pattern) {
26377            Ok(r) => r,
26378            Err(_) => continue,
26379        };
26380
26381        if let Some(captures) = regex.captures(&candidate) {
26382            let full_match = captures.get(0)?;
26383            let marker_len = full_match.len();
26384            let end_of_prefix = num_of_whitespaces + marker_len;
26385            let cursor_is_after_prefix = end_of_prefix <= start_point.column as usize;
26386
26387            let has_content_after_marker = snapshot
26388                .chars_for_range(range)
26389                .skip(end_of_prefix)
26390                .any(|c| !c.is_whitespace());
26391
26392            if has_content_after_marker && cursor_is_after_prefix {
26393                let number: u32 = captures.get(1)?.as_str().parse().ok()?;
26394                let continuation = ordered_config
26395                    .format
26396                    .replace("{1}", &(number + 1).to_string());
26397                return Some(continuation.into());
26398            }
26399
26400            if start_point.column as usize == end_of_prefix {
26401                let continuation = ordered_config.format.replace("{1}", "1");
26402                if num_of_whitespaces == 0 {
26403                    *newline_config = NewlineConfig::ClearCurrentLine;
26404                } else {
26405                    *newline_config = NewlineConfig::UnindentCurrentLine {
26406                        continuation: continuation.into(),
26407                    };
26408                }
26409            }
26410
26411            return None;
26412        }
26413    }
26414
26415    None
26416}
26417
26418fn is_list_prefix_row(
26419    row: MultiBufferRow,
26420    buffer: &MultiBufferSnapshot,
26421    language: &LanguageScope,
26422) -> bool {
26423    let Some((snapshot, range)) = buffer.buffer_line_for_row(row) else {
26424        return false;
26425    };
26426
26427    let num_of_whitespaces = snapshot
26428        .chars_for_range(range.clone())
26429        .take_while(|c| c.is_whitespace())
26430        .count();
26431
26432    let task_list_prefixes: Vec<_> = language
26433        .task_list()
26434        .into_iter()
26435        .flat_map(|config| {
26436            config
26437                .prefixes
26438                .iter()
26439                .map(|p| p.as_ref())
26440                .collect::<Vec<_>>()
26441        })
26442        .collect();
26443    let unordered_list_markers: Vec<_> = language
26444        .unordered_list()
26445        .iter()
26446        .map(|marker| marker.as_ref())
26447        .collect();
26448    let all_prefixes: Vec<_> = task_list_prefixes
26449        .into_iter()
26450        .chain(unordered_list_markers)
26451        .collect();
26452    if let Some(max_prefix_len) = all_prefixes.iter().map(|p| p.len()).max() {
26453        let candidate: String = snapshot
26454            .chars_for_range(range.clone())
26455            .skip(num_of_whitespaces)
26456            .take(max_prefix_len)
26457            .collect();
26458        if all_prefixes
26459            .iter()
26460            .any(|prefix| candidate.starts_with(*prefix))
26461        {
26462            return true;
26463        }
26464    }
26465
26466    let ordered_list_candidate: String = snapshot
26467        .chars_for_range(range)
26468        .skip(num_of_whitespaces)
26469        .take(ORDERED_LIST_MAX_MARKER_LEN)
26470        .collect();
26471    for ordered_config in language.ordered_list() {
26472        let regex = match Regex::new(&ordered_config.pattern) {
26473            Ok(r) => r,
26474            Err(_) => continue,
26475        };
26476        if let Some(captures) = regex.captures(&ordered_list_candidate) {
26477            return captures.get(0).is_some();
26478        }
26479    }
26480
26481    false
26482}
26483
26484#[derive(Debug)]
26485enum NewlineConfig {
26486    /// Insert newline with optional additional indent and optional extra blank line
26487    Newline {
26488        additional_indent: IndentSize,
26489        extra_line_additional_indent: Option<IndentSize>,
26490        prevent_auto_indent: bool,
26491    },
26492    /// Clear the current line
26493    ClearCurrentLine,
26494    /// Unindent the current line and add continuation
26495    UnindentCurrentLine { continuation: Arc<str> },
26496}
26497
26498impl NewlineConfig {
26499    fn has_extra_line(&self) -> bool {
26500        matches!(
26501            self,
26502            Self::Newline {
26503                extra_line_additional_indent: Some(_),
26504                ..
26505            }
26506        )
26507    }
26508
26509    fn insert_extra_newline_brackets(
26510        buffer: &MultiBufferSnapshot,
26511        range: Range<MultiBufferOffset>,
26512        language: &language::LanguageScope,
26513    ) -> bool {
26514        let leading_whitespace_len = buffer
26515            .reversed_chars_at(range.start)
26516            .take_while(|c| c.is_whitespace() && *c != '\n')
26517            .map(|c| c.len_utf8())
26518            .sum::<usize>();
26519        let trailing_whitespace_len = buffer
26520            .chars_at(range.end)
26521            .take_while(|c| c.is_whitespace() && *c != '\n')
26522            .map(|c| c.len_utf8())
26523            .sum::<usize>();
26524        let range = range.start - leading_whitespace_len..range.end + trailing_whitespace_len;
26525
26526        language.brackets().any(|(pair, enabled)| {
26527            let pair_start = pair.start.trim_end();
26528            let pair_end = pair.end.trim_start();
26529
26530            enabled
26531                && pair.newline
26532                && buffer.contains_str_at(range.end, pair_end)
26533                && buffer.contains_str_at(
26534                    range.start.saturating_sub_usize(pair_start.len()),
26535                    pair_start,
26536                )
26537        })
26538    }
26539
26540    fn insert_extra_newline_tree_sitter(
26541        buffer: &MultiBufferSnapshot,
26542        range: Range<MultiBufferOffset>,
26543    ) -> bool {
26544        let (buffer, range) = match buffer
26545            .range_to_buffer_ranges(range.start..=range.end)
26546            .as_slice()
26547        {
26548            [(buffer, range, _)] => (*buffer, range.clone()),
26549            _ => return false,
26550        };
26551        let pair = {
26552            let mut result: Option<BracketMatch<usize>> = None;
26553
26554            for pair in buffer
26555                .all_bracket_ranges(range.start.0..range.end.0)
26556                .filter(move |pair| {
26557                    pair.open_range.start <= range.start.0 && pair.close_range.end >= range.end.0
26558                })
26559            {
26560                let len = pair.close_range.end - pair.open_range.start;
26561
26562                if let Some(existing) = &result {
26563                    let existing_len = existing.close_range.end - existing.open_range.start;
26564                    if len > existing_len {
26565                        continue;
26566                    }
26567                }
26568
26569                result = Some(pair);
26570            }
26571
26572            result
26573        };
26574        let Some(pair) = pair else {
26575            return false;
26576        };
26577        pair.newline_only
26578            && buffer
26579                .chars_for_range(pair.open_range.end..range.start.0)
26580                .chain(buffer.chars_for_range(range.end.0..pair.close_range.start))
26581                .all(|c| c.is_whitespace() && c != '\n')
26582    }
26583}
26584
26585fn update_uncommitted_diff_for_buffer(
26586    editor: Entity<Editor>,
26587    project: &Entity<Project>,
26588    buffers: impl IntoIterator<Item = Entity<Buffer>>,
26589    buffer: Entity<MultiBuffer>,
26590    cx: &mut App,
26591) -> Task<()> {
26592    let mut tasks = Vec::new();
26593    project.update(cx, |project, cx| {
26594        for buffer in buffers {
26595            if project::File::from_dyn(buffer.read(cx).file()).is_some() {
26596                tasks.push(project.open_uncommitted_diff(buffer.clone(), cx))
26597            }
26598        }
26599    });
26600    cx.spawn(async move |cx| {
26601        let diffs = future::join_all(tasks).await;
26602        if editor.read_with(cx, |editor, _cx| editor.temporary_diff_override) {
26603            return;
26604        }
26605
26606        buffer.update(cx, |buffer, cx| {
26607            for diff in diffs.into_iter().flatten() {
26608                buffer.add_diff(diff, cx);
26609            }
26610        });
26611    })
26612}
26613
26614fn char_len_with_expanded_tabs(offset: usize, text: &str, tab_size: NonZeroU32) -> usize {
26615    let tab_size = tab_size.get() as usize;
26616    let mut width = offset;
26617
26618    for ch in text.chars() {
26619        width += if ch == '\t' {
26620            tab_size - (width % tab_size)
26621        } else {
26622            1
26623        };
26624    }
26625
26626    width - offset
26627}
26628
26629#[cfg(test)]
26630mod tests {
26631    use super::*;
26632
26633    #[test]
26634    fn test_string_size_with_expanded_tabs() {
26635        let nz = |val| NonZeroU32::new(val).unwrap();
26636        assert_eq!(char_len_with_expanded_tabs(0, "", nz(4)), 0);
26637        assert_eq!(char_len_with_expanded_tabs(0, "hello", nz(4)), 5);
26638        assert_eq!(char_len_with_expanded_tabs(0, "\thello", nz(4)), 9);
26639        assert_eq!(char_len_with_expanded_tabs(0, "abc\tab", nz(4)), 6);
26640        assert_eq!(char_len_with_expanded_tabs(0, "hello\t", nz(4)), 8);
26641        assert_eq!(char_len_with_expanded_tabs(0, "\t\t", nz(8)), 16);
26642        assert_eq!(char_len_with_expanded_tabs(0, "x\t", nz(8)), 8);
26643        assert_eq!(char_len_with_expanded_tabs(7, "x\t", nz(8)), 9);
26644    }
26645}
26646
26647/// Tokenizes a string into runs of text that should stick together, or that is whitespace.
26648struct WordBreakingTokenizer<'a> {
26649    input: &'a str,
26650}
26651
26652impl<'a> WordBreakingTokenizer<'a> {
26653    fn new(input: &'a str) -> Self {
26654        Self { input }
26655    }
26656}
26657
26658fn is_char_ideographic(ch: char) -> bool {
26659    use unicode_script::Script::*;
26660    use unicode_script::UnicodeScript;
26661    matches!(ch.script(), Han | Tangut | Yi)
26662}
26663
26664fn is_grapheme_ideographic(text: &str) -> bool {
26665    text.chars().any(is_char_ideographic)
26666}
26667
26668fn is_grapheme_whitespace(text: &str) -> bool {
26669    text.chars().any(|x| x.is_whitespace())
26670}
26671
26672fn should_stay_with_preceding_ideograph(text: &str) -> bool {
26673    text.chars()
26674        .next()
26675        .is_some_and(|ch| matches!(ch, '。' | '、' | ',' | '?' | '!' | ':' | ';' | '…'))
26676}
26677
26678#[derive(PartialEq, Eq, Debug, Clone, Copy)]
26679enum WordBreakToken<'a> {
26680    Word { token: &'a str, grapheme_len: usize },
26681    InlineWhitespace { token: &'a str, grapheme_len: usize },
26682    Newline,
26683}
26684
26685impl<'a> Iterator for WordBreakingTokenizer<'a> {
26686    /// Yields a span, the count of graphemes in the token, and whether it was
26687    /// whitespace. Note that it also breaks at word boundaries.
26688    type Item = WordBreakToken<'a>;
26689
26690    fn next(&mut self) -> Option<Self::Item> {
26691        use unicode_segmentation::UnicodeSegmentation;
26692        if self.input.is_empty() {
26693            return None;
26694        }
26695
26696        let mut iter = self.input.graphemes(true).peekable();
26697        let mut offset = 0;
26698        let mut grapheme_len = 0;
26699        if let Some(first_grapheme) = iter.next() {
26700            let is_newline = first_grapheme == "\n";
26701            let is_whitespace = is_grapheme_whitespace(first_grapheme);
26702            offset += first_grapheme.len();
26703            grapheme_len += 1;
26704            if is_grapheme_ideographic(first_grapheme) && !is_whitespace {
26705                if let Some(grapheme) = iter.peek().copied()
26706                    && should_stay_with_preceding_ideograph(grapheme)
26707                {
26708                    offset += grapheme.len();
26709                    grapheme_len += 1;
26710                }
26711            } else {
26712                let mut words = self.input[offset..].split_word_bound_indices().peekable();
26713                let mut next_word_bound = words.peek().copied();
26714                if next_word_bound.is_some_and(|(i, _)| i == 0) {
26715                    next_word_bound = words.next();
26716                }
26717                while let Some(grapheme) = iter.peek().copied() {
26718                    if next_word_bound.is_some_and(|(i, _)| i == offset) {
26719                        break;
26720                    };
26721                    if is_grapheme_whitespace(grapheme) != is_whitespace
26722                        || (grapheme == "\n") != is_newline
26723                    {
26724                        break;
26725                    };
26726                    offset += grapheme.len();
26727                    grapheme_len += 1;
26728                    iter.next();
26729                }
26730            }
26731            let token = &self.input[..offset];
26732            self.input = &self.input[offset..];
26733            if token == "\n" {
26734                Some(WordBreakToken::Newline)
26735            } else if is_whitespace {
26736                Some(WordBreakToken::InlineWhitespace {
26737                    token,
26738                    grapheme_len,
26739                })
26740            } else {
26741                Some(WordBreakToken::Word {
26742                    token,
26743                    grapheme_len,
26744                })
26745            }
26746        } else {
26747            None
26748        }
26749    }
26750}
26751
26752#[test]
26753fn test_word_breaking_tokenizer() {
26754    let tests: &[(&str, &[WordBreakToken<'static>])] = &[
26755        ("", &[]),
26756        ("  ", &[whitespace("  ", 2)]),
26757        ("Ʒ", &[word("Ʒ", 1)]),
26758        ("Ǽ", &[word("Ǽ", 1)]),
26759        ("", &[word("", 1)]),
26760        ("⋑⋑", &[word("⋑⋑", 2)]),
26761        (
26762            "原理,进而",
26763            &[word("", 1), word("理,", 2), word("", 1), word("", 1)],
26764        ),
26765        (
26766            "hello world",
26767            &[word("hello", 5), whitespace(" ", 1), word("world", 5)],
26768        ),
26769        (
26770            "hello, world",
26771            &[word("hello,", 6), whitespace(" ", 1), word("world", 5)],
26772        ),
26773        (
26774            "  hello world",
26775            &[
26776                whitespace("  ", 2),
26777                word("hello", 5),
26778                whitespace(" ", 1),
26779                word("world", 5),
26780            ],
26781        ),
26782        (
26783            "这是什么 \n 钢笔",
26784            &[
26785                word("", 1),
26786                word("", 1),
26787                word("", 1),
26788                word("", 1),
26789                whitespace(" ", 1),
26790                newline(),
26791                whitespace(" ", 1),
26792                word("", 1),
26793                word("", 1),
26794            ],
26795        ),
26796        (" mutton", &[whitespace("", 1), word("mutton", 6)]),
26797    ];
26798
26799    fn word(token: &'static str, grapheme_len: usize) -> WordBreakToken<'static> {
26800        WordBreakToken::Word {
26801            token,
26802            grapheme_len,
26803        }
26804    }
26805
26806    fn whitespace(token: &'static str, grapheme_len: usize) -> WordBreakToken<'static> {
26807        WordBreakToken::InlineWhitespace {
26808            token,
26809            grapheme_len,
26810        }
26811    }
26812
26813    fn newline() -> WordBreakToken<'static> {
26814        WordBreakToken::Newline
26815    }
26816
26817    for (input, result) in tests {
26818        assert_eq!(
26819            WordBreakingTokenizer::new(input)
26820                .collect::<Vec<_>>()
26821                .as_slice(),
26822            *result,
26823        );
26824    }
26825}
26826
26827fn wrap_with_prefix(
26828    first_line_prefix: String,
26829    subsequent_lines_prefix: String,
26830    unwrapped_text: String,
26831    wrap_column: usize,
26832    tab_size: NonZeroU32,
26833    preserve_existing_whitespace: bool,
26834) -> String {
26835    let first_line_prefix_len = char_len_with_expanded_tabs(0, &first_line_prefix, tab_size);
26836    let subsequent_lines_prefix_len =
26837        char_len_with_expanded_tabs(0, &subsequent_lines_prefix, tab_size);
26838    let mut wrapped_text = String::new();
26839    let mut current_line = first_line_prefix;
26840    let mut is_first_line = true;
26841
26842    let tokenizer = WordBreakingTokenizer::new(&unwrapped_text);
26843    let mut current_line_len = first_line_prefix_len;
26844    let mut in_whitespace = false;
26845    for token in tokenizer {
26846        let have_preceding_whitespace = in_whitespace;
26847        match token {
26848            WordBreakToken::Word {
26849                token,
26850                grapheme_len,
26851            } => {
26852                in_whitespace = false;
26853                let current_prefix_len = if is_first_line {
26854                    first_line_prefix_len
26855                } else {
26856                    subsequent_lines_prefix_len
26857                };
26858                if current_line_len + grapheme_len > wrap_column
26859                    && current_line_len != current_prefix_len
26860                {
26861                    wrapped_text.push_str(current_line.trim_end());
26862                    wrapped_text.push('\n');
26863                    is_first_line = false;
26864                    current_line = subsequent_lines_prefix.clone();
26865                    current_line_len = subsequent_lines_prefix_len;
26866                }
26867                current_line.push_str(token);
26868                current_line_len += grapheme_len;
26869            }
26870            WordBreakToken::InlineWhitespace {
26871                mut token,
26872                mut grapheme_len,
26873            } => {
26874                in_whitespace = true;
26875                if have_preceding_whitespace && !preserve_existing_whitespace {
26876                    continue;
26877                }
26878                if !preserve_existing_whitespace {
26879                    // Keep a single whitespace grapheme as-is
26880                    if let Some(first) =
26881                        unicode_segmentation::UnicodeSegmentation::graphemes(token, true).next()
26882                    {
26883                        token = first;
26884                    } else {
26885                        token = " ";
26886                    }
26887                    grapheme_len = 1;
26888                }
26889                let current_prefix_len = if is_first_line {
26890                    first_line_prefix_len
26891                } else {
26892                    subsequent_lines_prefix_len
26893                };
26894                if current_line_len + grapheme_len > wrap_column {
26895                    wrapped_text.push_str(current_line.trim_end());
26896                    wrapped_text.push('\n');
26897                    is_first_line = false;
26898                    current_line = subsequent_lines_prefix.clone();
26899                    current_line_len = subsequent_lines_prefix_len;
26900                } else if current_line_len != current_prefix_len || preserve_existing_whitespace {
26901                    current_line.push_str(token);
26902                    current_line_len += grapheme_len;
26903                }
26904            }
26905            WordBreakToken::Newline => {
26906                in_whitespace = true;
26907                let current_prefix_len = if is_first_line {
26908                    first_line_prefix_len
26909                } else {
26910                    subsequent_lines_prefix_len
26911                };
26912                if preserve_existing_whitespace {
26913                    wrapped_text.push_str(current_line.trim_end());
26914                    wrapped_text.push('\n');
26915                    is_first_line = false;
26916                    current_line = subsequent_lines_prefix.clone();
26917                    current_line_len = subsequent_lines_prefix_len;
26918                } else if have_preceding_whitespace {
26919                    continue;
26920                } else if current_line_len + 1 > wrap_column
26921                    && current_line_len != current_prefix_len
26922                {
26923                    wrapped_text.push_str(current_line.trim_end());
26924                    wrapped_text.push('\n');
26925                    is_first_line = false;
26926                    current_line = subsequent_lines_prefix.clone();
26927                    current_line_len = subsequent_lines_prefix_len;
26928                } else if current_line_len != current_prefix_len {
26929                    current_line.push(' ');
26930                    current_line_len += 1;
26931                }
26932            }
26933        }
26934    }
26935
26936    if !current_line.is_empty() {
26937        wrapped_text.push_str(&current_line);
26938    }
26939    wrapped_text
26940}
26941
26942#[test]
26943fn test_wrap_with_prefix() {
26944    assert_eq!(
26945        wrap_with_prefix(
26946            "# ".to_string(),
26947            "# ".to_string(),
26948            "abcdefg".to_string(),
26949            4,
26950            NonZeroU32::new(4).unwrap(),
26951            false,
26952        ),
26953        "# abcdefg"
26954    );
26955    assert_eq!(
26956        wrap_with_prefix(
26957            "".to_string(),
26958            "".to_string(),
26959            "\thello world".to_string(),
26960            8,
26961            NonZeroU32::new(4).unwrap(),
26962            false,
26963        ),
26964        "hello\nworld"
26965    );
26966    assert_eq!(
26967        wrap_with_prefix(
26968            "// ".to_string(),
26969            "// ".to_string(),
26970            "xx \nyy zz aa bb cc".to_string(),
26971            12,
26972            NonZeroU32::new(4).unwrap(),
26973            false,
26974        ),
26975        "// xx yy zz\n// aa bb cc"
26976    );
26977    assert_eq!(
26978        wrap_with_prefix(
26979            String::new(),
26980            String::new(),
26981            "这是什么 \n 钢笔".to_string(),
26982            3,
26983            NonZeroU32::new(4).unwrap(),
26984            false,
26985        ),
26986        "这是什\n么 钢\n"
26987    );
26988    assert_eq!(
26989        wrap_with_prefix(
26990            String::new(),
26991            String::new(),
26992            format!("foo{}bar", '\u{2009}'), // thin space
26993            80,
26994            NonZeroU32::new(4).unwrap(),
26995            false,
26996        ),
26997        format!("foo{}bar", '\u{2009}')
26998    );
26999}
27000
27001pub trait CollaborationHub {
27002    fn collaborators<'a>(&self, cx: &'a App) -> &'a HashMap<PeerId, Collaborator>;
27003    fn user_participant_indices<'a>(&self, cx: &'a App) -> &'a HashMap<u64, ParticipantIndex>;
27004    fn user_names(&self, cx: &App) -> HashMap<u64, SharedString>;
27005}
27006
27007impl CollaborationHub for Entity<Project> {
27008    fn collaborators<'a>(&self, cx: &'a App) -> &'a HashMap<PeerId, Collaborator> {
27009        self.read(cx).collaborators()
27010    }
27011
27012    fn user_participant_indices<'a>(&self, cx: &'a App) -> &'a HashMap<u64, ParticipantIndex> {
27013        self.read(cx).user_store().read(cx).participant_indices()
27014    }
27015
27016    fn user_names(&self, cx: &App) -> HashMap<u64, SharedString> {
27017        let this = self.read(cx);
27018        let user_ids = this.collaborators().values().map(|c| c.user_id);
27019        this.user_store().read(cx).participant_names(user_ids, cx)
27020    }
27021}
27022
27023pub trait SemanticsProvider {
27024    fn hover(
27025        &self,
27026        buffer: &Entity<Buffer>,
27027        position: text::Anchor,
27028        cx: &mut App,
27029    ) -> Option<Task<Option<Vec<project::Hover>>>>;
27030
27031    fn inline_values(
27032        &self,
27033        buffer_handle: Entity<Buffer>,
27034        range: Range<text::Anchor>,
27035        cx: &mut App,
27036    ) -> Option<Task<anyhow::Result<Vec<InlayHint>>>>;
27037
27038    fn applicable_inlay_chunks(
27039        &self,
27040        buffer: &Entity<Buffer>,
27041        ranges: &[Range<text::Anchor>],
27042        cx: &mut App,
27043    ) -> Vec<Range<BufferRow>>;
27044
27045    fn invalidate_inlay_hints(&self, for_buffers: &HashSet<BufferId>, cx: &mut App);
27046
27047    fn inlay_hints(
27048        &self,
27049        invalidate: InvalidationStrategy,
27050        buffer: Entity<Buffer>,
27051        ranges: Vec<Range<text::Anchor>>,
27052        known_chunks: Option<(clock::Global, HashSet<Range<BufferRow>>)>,
27053        cx: &mut App,
27054    ) -> Option<HashMap<Range<BufferRow>, Task<Result<CacheInlayHints>>>>;
27055
27056    fn semantic_tokens(
27057        &self,
27058        buffer: Entity<Buffer>,
27059        refresh: Option<RefreshForServer>,
27060        cx: &mut App,
27061    ) -> Option<Shared<Task<std::result::Result<BufferSemanticTokens, Arc<anyhow::Error>>>>>;
27062
27063    fn supports_inlay_hints(&self, buffer: &Entity<Buffer>, cx: &mut App) -> bool;
27064
27065    fn supports_semantic_tokens(&self, buffer: &Entity<Buffer>, cx: &mut App) -> bool;
27066
27067    fn document_highlights(
27068        &self,
27069        buffer: &Entity<Buffer>,
27070        position: text::Anchor,
27071        cx: &mut App,
27072    ) -> Option<Task<Result<Vec<DocumentHighlight>>>>;
27073
27074    fn definitions(
27075        &self,
27076        buffer: &Entity<Buffer>,
27077        position: text::Anchor,
27078        kind: GotoDefinitionKind,
27079        cx: &mut App,
27080    ) -> Option<Task<Result<Option<Vec<LocationLink>>>>>;
27081
27082    fn range_for_rename(
27083        &self,
27084        buffer: &Entity<Buffer>,
27085        position: text::Anchor,
27086        cx: &mut App,
27087    ) -> Option<Task<Result<Option<Range<text::Anchor>>>>>;
27088
27089    fn perform_rename(
27090        &self,
27091        buffer: &Entity<Buffer>,
27092        position: text::Anchor,
27093        new_name: String,
27094        cx: &mut App,
27095    ) -> Option<Task<Result<ProjectTransaction>>>;
27096}
27097
27098pub trait CompletionProvider {
27099    fn completions(
27100        &self,
27101        excerpt_id: ExcerptId,
27102        buffer: &Entity<Buffer>,
27103        buffer_position: text::Anchor,
27104        trigger: CompletionContext,
27105        window: &mut Window,
27106        cx: &mut Context<Editor>,
27107    ) -> Task<Result<Vec<CompletionResponse>>>;
27108
27109    fn resolve_completions(
27110        &self,
27111        _buffer: Entity<Buffer>,
27112        _completion_indices: Vec<usize>,
27113        _completions: Rc<RefCell<Box<[Completion]>>>,
27114        _cx: &mut Context<Editor>,
27115    ) -> Task<Result<bool>> {
27116        Task::ready(Ok(false))
27117    }
27118
27119    fn apply_additional_edits_for_completion(
27120        &self,
27121        _buffer: Entity<Buffer>,
27122        _completions: Rc<RefCell<Box<[Completion]>>>,
27123        _completion_index: usize,
27124        _push_to_history: bool,
27125        _all_commit_ranges: Vec<Range<language::Anchor>>,
27126        _cx: &mut Context<Editor>,
27127    ) -> Task<Result<Option<language::Transaction>>> {
27128        Task::ready(Ok(None))
27129    }
27130
27131    fn is_completion_trigger(
27132        &self,
27133        buffer: &Entity<Buffer>,
27134        position: language::Anchor,
27135        text: &str,
27136        trigger_in_words: bool,
27137        cx: &mut Context<Editor>,
27138    ) -> bool;
27139
27140    fn selection_changed(&self, _mat: Option<&StringMatch>, _window: &mut Window, _cx: &mut App) {}
27141
27142    fn sort_completions(&self) -> bool {
27143        true
27144    }
27145
27146    fn filter_completions(&self) -> bool {
27147        true
27148    }
27149
27150    fn show_snippets(&self) -> bool {
27151        false
27152    }
27153}
27154
27155pub trait CodeActionProvider {
27156    fn id(&self) -> Arc<str>;
27157
27158    fn code_actions(
27159        &self,
27160        buffer: &Entity<Buffer>,
27161        range: Range<text::Anchor>,
27162        window: &mut Window,
27163        cx: &mut App,
27164    ) -> Task<Result<Vec<CodeAction>>>;
27165
27166    fn apply_code_action(
27167        &self,
27168        buffer_handle: Entity<Buffer>,
27169        action: CodeAction,
27170        excerpt_id: ExcerptId,
27171        push_to_history: bool,
27172        window: &mut Window,
27173        cx: &mut App,
27174    ) -> Task<Result<ProjectTransaction>>;
27175}
27176
27177impl CodeActionProvider for Entity<Project> {
27178    fn id(&self) -> Arc<str> {
27179        "project".into()
27180    }
27181
27182    fn code_actions(
27183        &self,
27184        buffer: &Entity<Buffer>,
27185        range: Range<text::Anchor>,
27186        _window: &mut Window,
27187        cx: &mut App,
27188    ) -> Task<Result<Vec<CodeAction>>> {
27189        self.update(cx, |project, cx| {
27190            let code_lens_actions = project.code_lens_actions(buffer, range.clone(), cx);
27191            let code_actions = project.code_actions(buffer, range, None, cx);
27192            cx.background_spawn(async move {
27193                let (code_lens_actions, code_actions) = join(code_lens_actions, code_actions).await;
27194                Ok(code_lens_actions
27195                    .context("code lens fetch")?
27196                    .into_iter()
27197                    .flatten()
27198                    .chain(
27199                        code_actions
27200                            .context("code action fetch")?
27201                            .into_iter()
27202                            .flatten(),
27203                    )
27204                    .collect())
27205            })
27206        })
27207    }
27208
27209    fn apply_code_action(
27210        &self,
27211        buffer_handle: Entity<Buffer>,
27212        action: CodeAction,
27213        _excerpt_id: ExcerptId,
27214        push_to_history: bool,
27215        _window: &mut Window,
27216        cx: &mut App,
27217    ) -> Task<Result<ProjectTransaction>> {
27218        self.update(cx, |project, cx| {
27219            project.apply_code_action(buffer_handle, action, push_to_history, cx)
27220        })
27221    }
27222}
27223
27224fn snippet_completions(
27225    project: &Project,
27226    buffer: &Entity<Buffer>,
27227    buffer_anchor: text::Anchor,
27228    classifier: CharClassifier,
27229    cx: &mut App,
27230) -> Task<Result<CompletionResponse>> {
27231    let languages = buffer.read(cx).languages_at(buffer_anchor);
27232    let snippet_store = project.snippets().read(cx);
27233
27234    let scopes: Vec<_> = languages
27235        .iter()
27236        .filter_map(|language| {
27237            let language_name = language.lsp_id();
27238            let snippets = snippet_store.snippets_for(Some(language_name), cx);
27239
27240            if snippets.is_empty() {
27241                None
27242            } else {
27243                Some((language.default_scope(), snippets))
27244            }
27245        })
27246        .collect();
27247
27248    if scopes.is_empty() {
27249        return Task::ready(Ok(CompletionResponse {
27250            completions: vec![],
27251            display_options: CompletionDisplayOptions::default(),
27252            is_incomplete: false,
27253        }));
27254    }
27255
27256    let snapshot = buffer.read(cx).text_snapshot();
27257    let executor = cx.background_executor().clone();
27258
27259    cx.background_spawn(async move {
27260        let is_word_char = |c| classifier.is_word(c);
27261
27262        let mut is_incomplete = false;
27263        let mut completions: Vec<Completion> = Vec::new();
27264
27265        const MAX_PREFIX_LEN: usize = 128;
27266        let buffer_offset = text::ToOffset::to_offset(&buffer_anchor, &snapshot);
27267        let window_start = buffer_offset.saturating_sub(MAX_PREFIX_LEN);
27268        let window_start = snapshot.clip_offset(window_start, Bias::Left);
27269
27270        let max_buffer_window: String = snapshot
27271            .text_for_range(window_start..buffer_offset)
27272            .collect();
27273
27274        if max_buffer_window.is_empty() {
27275            return Ok(CompletionResponse {
27276                completions: vec![],
27277                display_options: CompletionDisplayOptions::default(),
27278                is_incomplete: true,
27279            });
27280        }
27281
27282        for (_scope, snippets) in scopes.into_iter() {
27283            // Sort snippets by word count to match longer snippet prefixes first.
27284            let mut sorted_snippet_candidates = snippets
27285                .iter()
27286                .enumerate()
27287                .flat_map(|(snippet_ix, snippet)| {
27288                    snippet
27289                        .prefix
27290                        .iter()
27291                        .enumerate()
27292                        .map(move |(prefix_ix, prefix)| {
27293                            let word_count =
27294                                snippet_candidate_suffixes(prefix, &is_word_char).count();
27295                            ((snippet_ix, prefix_ix), prefix, word_count)
27296                        })
27297                })
27298                .collect_vec();
27299            sorted_snippet_candidates
27300                .sort_unstable_by_key(|(_, _, word_count)| Reverse(*word_count));
27301
27302            // Each prefix may be matched multiple times; the completion menu must filter out duplicates.
27303
27304            let buffer_windows = snippet_candidate_suffixes(&max_buffer_window, &is_word_char)
27305                .take(
27306                    sorted_snippet_candidates
27307                        .first()
27308                        .map(|(_, _, word_count)| *word_count)
27309                        .unwrap_or_default(),
27310                )
27311                .collect_vec();
27312
27313            const MAX_RESULTS: usize = 100;
27314            // Each match also remembers how many characters from the buffer it consumed
27315            let mut matches: Vec<(StringMatch, usize)> = vec![];
27316
27317            let mut snippet_list_cutoff_index = 0;
27318            for (buffer_index, buffer_window) in buffer_windows.iter().enumerate().rev() {
27319                let word_count = buffer_index + 1;
27320                // Increase `snippet_list_cutoff_index` until we have all of the
27321                // snippets with sufficiently many words.
27322                while sorted_snippet_candidates
27323                    .get(snippet_list_cutoff_index)
27324                    .is_some_and(|(_ix, _prefix, snippet_word_count)| {
27325                        *snippet_word_count >= word_count
27326                    })
27327                {
27328                    snippet_list_cutoff_index += 1;
27329                }
27330
27331                // Take only the candidates with at least `word_count` many words
27332                let snippet_candidates_at_word_len =
27333                    &sorted_snippet_candidates[..snippet_list_cutoff_index];
27334
27335                let candidates = snippet_candidates_at_word_len
27336                    .iter()
27337                    .map(|(_snippet_ix, prefix, _snippet_word_count)| prefix)
27338                    .enumerate() // index in `sorted_snippet_candidates`
27339                    // First char must match
27340                    .filter(|(_ix, prefix)| {
27341                        itertools::equal(
27342                            prefix
27343                                .chars()
27344                                .next()
27345                                .into_iter()
27346                                .flat_map(|c| c.to_lowercase()),
27347                            buffer_window
27348                                .chars()
27349                                .next()
27350                                .into_iter()
27351                                .flat_map(|c| c.to_lowercase()),
27352                        )
27353                    })
27354                    .map(|(ix, prefix)| StringMatchCandidate::new(ix, prefix))
27355                    .collect::<Vec<StringMatchCandidate>>();
27356
27357                matches.extend(
27358                    fuzzy::match_strings(
27359                        &candidates,
27360                        &buffer_window,
27361                        buffer_window.chars().any(|c| c.is_uppercase()),
27362                        true,
27363                        MAX_RESULTS - matches.len(), // always prioritize longer snippets
27364                        &Default::default(),
27365                        executor.clone(),
27366                    )
27367                    .await
27368                    .into_iter()
27369                    .map(|string_match| (string_match, buffer_window.len())),
27370                );
27371
27372                if matches.len() >= MAX_RESULTS {
27373                    break;
27374                }
27375            }
27376
27377            let to_lsp = |point: &text::Anchor| {
27378                let end = text::ToPointUtf16::to_point_utf16(point, &snapshot);
27379                point_to_lsp(end)
27380            };
27381            let lsp_end = to_lsp(&buffer_anchor);
27382
27383            if matches.len() >= MAX_RESULTS {
27384                is_incomplete = true;
27385            }
27386
27387            completions.extend(matches.iter().map(|(string_match, buffer_window_len)| {
27388                let ((snippet_index, prefix_index), matching_prefix, _snippet_word_count) =
27389                    sorted_snippet_candidates[string_match.candidate_id];
27390                let snippet = &snippets[snippet_index];
27391                let start = buffer_offset - buffer_window_len;
27392                let start = snapshot.anchor_before(start);
27393                let range = start..buffer_anchor;
27394                let lsp_start = to_lsp(&start);
27395                let lsp_range = lsp::Range {
27396                    start: lsp_start,
27397                    end: lsp_end,
27398                };
27399                Completion {
27400                    replace_range: range,
27401                    new_text: snippet.body.clone(),
27402                    source: CompletionSource::Lsp {
27403                        insert_range: None,
27404                        server_id: LanguageServerId(usize::MAX),
27405                        resolved: true,
27406                        lsp_completion: Box::new(lsp::CompletionItem {
27407                            label: snippet.prefix.first().unwrap().clone(),
27408                            kind: Some(CompletionItemKind::SNIPPET),
27409                            label_details: snippet.description.as_ref().map(|description| {
27410                                lsp::CompletionItemLabelDetails {
27411                                    detail: Some(description.clone()),
27412                                    description: None,
27413                                }
27414                            }),
27415                            insert_text_format: Some(InsertTextFormat::SNIPPET),
27416                            text_edit: Some(lsp::CompletionTextEdit::InsertAndReplace(
27417                                lsp::InsertReplaceEdit {
27418                                    new_text: snippet.body.clone(),
27419                                    insert: lsp_range,
27420                                    replace: lsp_range,
27421                                },
27422                            )),
27423                            filter_text: Some(snippet.body.clone()),
27424                            sort_text: Some(char::MAX.to_string()),
27425                            ..lsp::CompletionItem::default()
27426                        }),
27427                        lsp_defaults: None,
27428                    },
27429                    label: CodeLabel {
27430                        text: matching_prefix.clone(),
27431                        runs: Vec::new(),
27432                        filter_range: 0..matching_prefix.len(),
27433                    },
27434                    icon_path: None,
27435                    documentation: Some(CompletionDocumentation::SingleLineAndMultiLinePlainText {
27436                        single_line: snippet.name.clone().into(),
27437                        plain_text: snippet
27438                            .description
27439                            .clone()
27440                            .map(|description| description.into()),
27441                    }),
27442                    insert_text_mode: None,
27443                    confirm: None,
27444                    match_start: Some(start),
27445                    snippet_deduplication_key: Some((snippet_index, prefix_index)),
27446                }
27447            }));
27448        }
27449
27450        Ok(CompletionResponse {
27451            completions,
27452            display_options: CompletionDisplayOptions::default(),
27453            is_incomplete,
27454        })
27455    })
27456}
27457
27458impl CompletionProvider for Entity<Project> {
27459    fn completions(
27460        &self,
27461        _excerpt_id: ExcerptId,
27462        buffer: &Entity<Buffer>,
27463        buffer_position: text::Anchor,
27464        options: CompletionContext,
27465        _window: &mut Window,
27466        cx: &mut Context<Editor>,
27467    ) -> Task<Result<Vec<CompletionResponse>>> {
27468        self.update(cx, |project, cx| {
27469            let task = project.completions(buffer, buffer_position, options, cx);
27470            cx.background_spawn(task)
27471        })
27472    }
27473
27474    fn resolve_completions(
27475        &self,
27476        buffer: Entity<Buffer>,
27477        completion_indices: Vec<usize>,
27478        completions: Rc<RefCell<Box<[Completion]>>>,
27479        cx: &mut Context<Editor>,
27480    ) -> Task<Result<bool>> {
27481        self.update(cx, |project, cx| {
27482            project.lsp_store().update(cx, |lsp_store, cx| {
27483                lsp_store.resolve_completions(buffer, completion_indices, completions, cx)
27484            })
27485        })
27486    }
27487
27488    fn apply_additional_edits_for_completion(
27489        &self,
27490        buffer: Entity<Buffer>,
27491        completions: Rc<RefCell<Box<[Completion]>>>,
27492        completion_index: usize,
27493        push_to_history: bool,
27494        all_commit_ranges: Vec<Range<language::Anchor>>,
27495        cx: &mut Context<Editor>,
27496    ) -> Task<Result<Option<language::Transaction>>> {
27497        self.update(cx, |project, cx| {
27498            project.lsp_store().update(cx, |lsp_store, cx| {
27499                lsp_store.apply_additional_edits_for_completion(
27500                    buffer,
27501                    completions,
27502                    completion_index,
27503                    push_to_history,
27504                    all_commit_ranges,
27505                    cx,
27506                )
27507            })
27508        })
27509    }
27510
27511    fn is_completion_trigger(
27512        &self,
27513        buffer: &Entity<Buffer>,
27514        position: language::Anchor,
27515        text: &str,
27516        trigger_in_words: bool,
27517        cx: &mut Context<Editor>,
27518    ) -> bool {
27519        let mut chars = text.chars();
27520        let char = if let Some(char) = chars.next() {
27521            char
27522        } else {
27523            return false;
27524        };
27525        if chars.next().is_some() {
27526            return false;
27527        }
27528
27529        let buffer = buffer.read(cx);
27530        let snapshot = buffer.snapshot();
27531        let classifier = snapshot
27532            .char_classifier_at(position)
27533            .scope_context(Some(CharScopeContext::Completion));
27534        if trigger_in_words && classifier.is_word(char) {
27535            return true;
27536        }
27537
27538        buffer.completion_triggers().contains(text)
27539    }
27540
27541    fn show_snippets(&self) -> bool {
27542        true
27543    }
27544}
27545
27546impl SemanticsProvider for WeakEntity<Project> {
27547    fn hover(
27548        &self,
27549        buffer: &Entity<Buffer>,
27550        position: text::Anchor,
27551        cx: &mut App,
27552    ) -> Option<Task<Option<Vec<project::Hover>>>> {
27553        self.update(cx, |project, cx| project.hover(buffer, position, cx))
27554            .ok()
27555    }
27556
27557    fn document_highlights(
27558        &self,
27559        buffer: &Entity<Buffer>,
27560        position: text::Anchor,
27561        cx: &mut App,
27562    ) -> Option<Task<Result<Vec<DocumentHighlight>>>> {
27563        self.update(cx, |project, cx| {
27564            project.document_highlights(buffer, position, cx)
27565        })
27566        .ok()
27567    }
27568
27569    fn definitions(
27570        &self,
27571        buffer: &Entity<Buffer>,
27572        position: text::Anchor,
27573        kind: GotoDefinitionKind,
27574        cx: &mut App,
27575    ) -> Option<Task<Result<Option<Vec<LocationLink>>>>> {
27576        self.update(cx, |project, cx| match kind {
27577            GotoDefinitionKind::Symbol => project.definitions(buffer, position, cx),
27578            GotoDefinitionKind::Declaration => project.declarations(buffer, position, cx),
27579            GotoDefinitionKind::Type => project.type_definitions(buffer, position, cx),
27580            GotoDefinitionKind::Implementation => project.implementations(buffer, position, cx),
27581        })
27582        .ok()
27583    }
27584
27585    fn supports_inlay_hints(&self, buffer: &Entity<Buffer>, cx: &mut App) -> bool {
27586        self.update(cx, |project, cx| {
27587            if project
27588                .active_debug_session(cx)
27589                .is_some_and(|(session, _)| session.read(cx).any_stopped_thread())
27590            {
27591                return true;
27592            }
27593
27594            buffer.update(cx, |buffer, cx| {
27595                project.any_language_server_supports_inlay_hints(buffer, cx)
27596            })
27597        })
27598        .unwrap_or(false)
27599    }
27600
27601    fn supports_semantic_tokens(&self, buffer: &Entity<Buffer>, cx: &mut App) -> bool {
27602        self.update(cx, |project, cx| {
27603            buffer.update(cx, |buffer, cx| {
27604                project.any_language_server_supports_semantic_tokens(buffer, cx)
27605            })
27606        })
27607        .unwrap_or(false)
27608    }
27609
27610    fn inline_values(
27611        &self,
27612        buffer_handle: Entity<Buffer>,
27613        range: Range<text::Anchor>,
27614        cx: &mut App,
27615    ) -> Option<Task<anyhow::Result<Vec<InlayHint>>>> {
27616        self.update(cx, |project, cx| {
27617            let (session, active_stack_frame) = project.active_debug_session(cx)?;
27618
27619            Some(project.inline_values(session, active_stack_frame, buffer_handle, range, cx))
27620        })
27621        .ok()
27622        .flatten()
27623    }
27624
27625    fn applicable_inlay_chunks(
27626        &self,
27627        buffer: &Entity<Buffer>,
27628        ranges: &[Range<text::Anchor>],
27629        cx: &mut App,
27630    ) -> Vec<Range<BufferRow>> {
27631        self.update(cx, |project, cx| {
27632            project.lsp_store().update(cx, |lsp_store, cx| {
27633                lsp_store.applicable_inlay_chunks(buffer, ranges, cx)
27634            })
27635        })
27636        .unwrap_or_default()
27637    }
27638
27639    fn invalidate_inlay_hints(&self, for_buffers: &HashSet<BufferId>, cx: &mut App) {
27640        self.update(cx, |project, cx| {
27641            project.lsp_store().update(cx, |lsp_store, _| {
27642                lsp_store.invalidate_inlay_hints(for_buffers)
27643            })
27644        })
27645        .ok();
27646    }
27647
27648    fn inlay_hints(
27649        &self,
27650        invalidate: InvalidationStrategy,
27651        buffer: Entity<Buffer>,
27652        ranges: Vec<Range<text::Anchor>>,
27653        known_chunks: Option<(clock::Global, HashSet<Range<BufferRow>>)>,
27654        cx: &mut App,
27655    ) -> Option<HashMap<Range<BufferRow>, Task<Result<CacheInlayHints>>>> {
27656        self.update(cx, |project, cx| {
27657            project.lsp_store().update(cx, |lsp_store, cx| {
27658                lsp_store.inlay_hints(invalidate, buffer, ranges, known_chunks, cx)
27659            })
27660        })
27661        .ok()
27662    }
27663
27664    fn semantic_tokens(
27665        &self,
27666        buffer: Entity<Buffer>,
27667        refresh: Option<RefreshForServer>,
27668        cx: &mut App,
27669    ) -> Option<Shared<Task<std::result::Result<BufferSemanticTokens, Arc<anyhow::Error>>>>> {
27670        self.update(cx, |this, cx| {
27671            this.lsp_store().update(cx, |lsp_store, cx| {
27672                lsp_store.semantic_tokens(buffer, refresh, cx)
27673            })
27674        })
27675        .ok()
27676    }
27677
27678    fn range_for_rename(
27679        &self,
27680        buffer: &Entity<Buffer>,
27681        position: text::Anchor,
27682        cx: &mut App,
27683    ) -> Option<Task<Result<Option<Range<text::Anchor>>>>> {
27684        self.update(cx, |project, cx| {
27685            let buffer = buffer.clone();
27686            let task = project.prepare_rename(buffer.clone(), position, cx);
27687            cx.spawn(async move |_, cx| {
27688                Ok(match task.await? {
27689                    PrepareRenameResponse::Success(range) => Some(range),
27690                    PrepareRenameResponse::InvalidPosition => None,
27691                    PrepareRenameResponse::OnlyUnpreparedRenameSupported => {
27692                        // Fallback on using TreeSitter info to determine identifier range
27693                        buffer.read_with(cx, |buffer, _| {
27694                            let snapshot = buffer.snapshot();
27695                            let (range, kind) = snapshot.surrounding_word(position, None);
27696                            if kind != Some(CharKind::Word) {
27697                                return None;
27698                            }
27699                            Some(
27700                                snapshot.anchor_before(range.start)
27701                                    ..snapshot.anchor_after(range.end),
27702                            )
27703                        })
27704                    }
27705                })
27706            })
27707        })
27708        .ok()
27709    }
27710
27711    fn perform_rename(
27712        &self,
27713        buffer: &Entity<Buffer>,
27714        position: text::Anchor,
27715        new_name: String,
27716        cx: &mut App,
27717    ) -> Option<Task<Result<ProjectTransaction>>> {
27718        self.update(cx, |project, cx| {
27719            project.perform_rename(buffer.clone(), position, new_name, cx)
27720        })
27721        .ok()
27722    }
27723}
27724
27725fn consume_contiguous_rows(
27726    contiguous_row_selections: &mut Vec<Selection<Point>>,
27727    selection: &Selection<Point>,
27728    display_map: &DisplaySnapshot,
27729    selections: &mut Peekable<std::slice::Iter<Selection<Point>>>,
27730) -> (MultiBufferRow, MultiBufferRow) {
27731    contiguous_row_selections.push(selection.clone());
27732    let start_row = starting_row(selection, display_map);
27733    let mut end_row = ending_row(selection, display_map);
27734
27735    while let Some(next_selection) = selections.peek() {
27736        if next_selection.start.row <= end_row.0 {
27737            end_row = ending_row(next_selection, display_map);
27738            contiguous_row_selections.push(selections.next().unwrap().clone());
27739        } else {
27740            break;
27741        }
27742    }
27743    (start_row, end_row)
27744}
27745
27746fn starting_row(selection: &Selection<Point>, display_map: &DisplaySnapshot) -> MultiBufferRow {
27747    if selection.start.column > 0 {
27748        MultiBufferRow(display_map.prev_line_boundary(selection.start).0.row)
27749    } else {
27750        MultiBufferRow(selection.start.row)
27751    }
27752}
27753
27754fn ending_row(next_selection: &Selection<Point>, display_map: &DisplaySnapshot) -> MultiBufferRow {
27755    if next_selection.end.column > 0 || next_selection.is_empty() {
27756        MultiBufferRow(display_map.next_line_boundary(next_selection.end).0.row + 1)
27757    } else {
27758        MultiBufferRow(next_selection.end.row)
27759    }
27760}
27761
27762impl EditorSnapshot {
27763    pub fn remote_selections_in_range<'a>(
27764        &'a self,
27765        range: &'a Range<Anchor>,
27766        collaboration_hub: &dyn CollaborationHub,
27767        cx: &'a App,
27768    ) -> impl 'a + Iterator<Item = RemoteSelection> {
27769        let participant_names = collaboration_hub.user_names(cx);
27770        let participant_indices = collaboration_hub.user_participant_indices(cx);
27771        let collaborators_by_peer_id = collaboration_hub.collaborators(cx);
27772        let collaborators_by_replica_id = collaborators_by_peer_id
27773            .values()
27774            .map(|collaborator| (collaborator.replica_id, collaborator))
27775            .collect::<HashMap<_, _>>();
27776        self.buffer_snapshot()
27777            .selections_in_range(range, false)
27778            .filter_map(move |(replica_id, line_mode, cursor_shape, selection)| {
27779                if replica_id == ReplicaId::AGENT {
27780                    Some(RemoteSelection {
27781                        replica_id,
27782                        selection,
27783                        cursor_shape,
27784                        line_mode,
27785                        collaborator_id: CollaboratorId::Agent,
27786                        user_name: Some("Agent".into()),
27787                        color: cx.theme().players().agent(),
27788                    })
27789                } else {
27790                    let collaborator = collaborators_by_replica_id.get(&replica_id)?;
27791                    let participant_index = participant_indices.get(&collaborator.user_id).copied();
27792                    let user_name = participant_names.get(&collaborator.user_id).cloned();
27793                    Some(RemoteSelection {
27794                        replica_id,
27795                        selection,
27796                        cursor_shape,
27797                        line_mode,
27798                        collaborator_id: CollaboratorId::PeerId(collaborator.peer_id),
27799                        user_name,
27800                        color: if let Some(index) = participant_index {
27801                            cx.theme().players().color_for_participant(index.0)
27802                        } else {
27803                            cx.theme().players().absent()
27804                        },
27805                    })
27806                }
27807            })
27808    }
27809
27810    pub fn hunks_for_ranges(
27811        &self,
27812        ranges: impl IntoIterator<Item = Range<Point>>,
27813    ) -> Vec<MultiBufferDiffHunk> {
27814        let mut hunks = Vec::new();
27815        let mut processed_buffer_rows: HashMap<BufferId, HashSet<Range<text::Anchor>>> =
27816            HashMap::default();
27817        for query_range in ranges {
27818            let query_rows =
27819                MultiBufferRow(query_range.start.row)..MultiBufferRow(query_range.end.row + 1);
27820            for hunk in self.buffer_snapshot().diff_hunks_in_range(
27821                Point::new(query_rows.start.0, 0)..Point::new(query_rows.end.0, 0),
27822            ) {
27823                // Include deleted hunks that are adjacent to the query range, because
27824                // otherwise they would be missed.
27825                let mut intersects_range = hunk.row_range.overlaps(&query_rows);
27826                if hunk.status().is_deleted() {
27827                    intersects_range |= hunk.row_range.start == query_rows.end;
27828                    intersects_range |= hunk.row_range.end == query_rows.start;
27829                }
27830                if intersects_range {
27831                    if !processed_buffer_rows
27832                        .entry(hunk.buffer_id)
27833                        .or_default()
27834                        .insert(hunk.buffer_range.start..hunk.buffer_range.end)
27835                    {
27836                        continue;
27837                    }
27838                    hunks.push(hunk);
27839                }
27840            }
27841        }
27842
27843        hunks
27844    }
27845
27846    fn display_diff_hunks_for_rows<'a>(
27847        &'a self,
27848        display_rows: Range<DisplayRow>,
27849        folded_buffers: &'a HashSet<BufferId>,
27850    ) -> impl 'a + Iterator<Item = DisplayDiffHunk> {
27851        let buffer_start = DisplayPoint::new(display_rows.start, 0).to_point(self);
27852        let buffer_end = DisplayPoint::new(display_rows.end, 0).to_point(self);
27853
27854        self.buffer_snapshot()
27855            .diff_hunks_in_range(buffer_start..buffer_end)
27856            .filter_map(|hunk| {
27857                if folded_buffers.contains(&hunk.buffer_id)
27858                    || (hunk.row_range.is_empty() && self.buffer.all_diff_hunks_expanded())
27859                {
27860                    return None;
27861                }
27862
27863                let hunk_start_point = Point::new(hunk.row_range.start.0, 0);
27864                let hunk_end_point = if hunk.row_range.end > hunk.row_range.start {
27865                    let last_row = MultiBufferRow(hunk.row_range.end.0 - 1);
27866                    let line_len = self.buffer_snapshot().line_len(last_row);
27867                    Point::new(last_row.0, line_len)
27868                } else {
27869                    Point::new(hunk.row_range.end.0, 0)
27870                };
27871
27872                let hunk_display_start = self.point_to_display_point(hunk_start_point, Bias::Left);
27873                let hunk_display_end = self.point_to_display_point(hunk_end_point, Bias::Right);
27874
27875                let display_hunk = if hunk_display_start.column() != 0 {
27876                    DisplayDiffHunk::Folded {
27877                        display_row: hunk_display_start.row(),
27878                    }
27879                } else {
27880                    let mut end_row = hunk_display_end.row();
27881                    if hunk.row_range.end > hunk.row_range.start || hunk_display_end.column() > 0 {
27882                        end_row.0 += 1;
27883                    }
27884                    let is_created_file = hunk.is_created_file();
27885
27886                    DisplayDiffHunk::Unfolded {
27887                        status: hunk.status(),
27888                        diff_base_byte_range: hunk.diff_base_byte_range.start.0
27889                            ..hunk.diff_base_byte_range.end.0,
27890                        word_diffs: hunk.word_diffs,
27891                        display_row_range: hunk_display_start.row()..end_row,
27892                        multi_buffer_range: Anchor::range_in_buffer(
27893                            hunk.excerpt_id,
27894                            hunk.buffer_range,
27895                        ),
27896                        is_created_file,
27897                    }
27898                };
27899
27900                Some(display_hunk)
27901            })
27902    }
27903
27904    pub fn language_at<T: ToOffset>(&self, position: T) -> Option<&Arc<Language>> {
27905        self.display_snapshot
27906            .buffer_snapshot()
27907            .language_at(position)
27908    }
27909
27910    pub fn is_focused(&self) -> bool {
27911        self.is_focused
27912    }
27913
27914    pub fn placeholder_text(&self) -> Option<String> {
27915        self.placeholder_display_snapshot
27916            .as_ref()
27917            .map(|display_map| display_map.text())
27918    }
27919
27920    pub fn scroll_position(&self) -> gpui::Point<ScrollOffset> {
27921        self.scroll_anchor.scroll_position(&self.display_snapshot)
27922    }
27923
27924    pub fn gutter_dimensions(
27925        &self,
27926        font_id: FontId,
27927        font_size: Pixels,
27928        style: &EditorStyle,
27929        window: &mut Window,
27930        cx: &App,
27931    ) -> GutterDimensions {
27932        if self.show_gutter
27933            && let Some(ch_width) = cx.text_system().ch_width(font_id, font_size).log_err()
27934            && let Some(ch_advance) = cx.text_system().ch_advance(font_id, font_size).log_err()
27935        {
27936            let show_git_gutter = self.show_git_diff_gutter.unwrap_or_else(|| {
27937                matches!(
27938                    ProjectSettings::get_global(cx).git.git_gutter,
27939                    GitGutterSetting::TrackedFiles
27940                )
27941            });
27942            let gutter_settings = EditorSettings::get_global(cx).gutter;
27943            let show_line_numbers = self
27944                .show_line_numbers
27945                .unwrap_or(gutter_settings.line_numbers);
27946            let line_gutter_width = if show_line_numbers {
27947                // Avoid flicker-like gutter resizes when the line number gains another digit by
27948                // only resizing the gutter on files with > 10**min_line_number_digits lines.
27949                let min_width_for_number_on_gutter =
27950                    ch_advance * gutter_settings.min_line_number_digits as f32;
27951                self.max_line_number_width(style, window)
27952                    .max(min_width_for_number_on_gutter)
27953            } else {
27954                0.0.into()
27955            };
27956
27957            let show_runnables = self.show_runnables.unwrap_or(gutter_settings.runnables);
27958            let show_breakpoints = self.show_breakpoints.unwrap_or(gutter_settings.breakpoints);
27959
27960            let git_blame_entries_width =
27961                self.git_blame_gutter_max_author_length
27962                    .map(|max_author_length| {
27963                        let renderer = cx.global::<GlobalBlameRenderer>().0.clone();
27964                        const MAX_RELATIVE_TIMESTAMP: &str = "60 minutes ago";
27965
27966                        /// The number of characters to dedicate to gaps and margins.
27967                        const SPACING_WIDTH: usize = 4;
27968
27969                        let max_char_count = max_author_length.min(renderer.max_author_length())
27970                            + ::git::SHORT_SHA_LENGTH
27971                            + MAX_RELATIVE_TIMESTAMP.len()
27972                            + SPACING_WIDTH;
27973
27974                        ch_advance * max_char_count
27975                    });
27976
27977            let is_singleton = self.buffer_snapshot().is_singleton();
27978
27979            let mut left_padding = git_blame_entries_width.unwrap_or(Pixels::ZERO);
27980            left_padding += if !is_singleton {
27981                ch_width * 4.0
27982            } else if show_runnables || show_breakpoints {
27983                ch_width * 3.0
27984            } else if show_git_gutter && show_line_numbers {
27985                ch_width * 2.0
27986            } else if show_git_gutter || show_line_numbers {
27987                ch_width
27988            } else {
27989                px(0.)
27990            };
27991
27992            let shows_folds = is_singleton && gutter_settings.folds;
27993
27994            let right_padding = if shows_folds && show_line_numbers {
27995                ch_width * 4.0
27996            } else if shows_folds || (!is_singleton && show_line_numbers) {
27997                ch_width * 3.0
27998            } else if show_line_numbers {
27999                ch_width
28000            } else {
28001                px(0.)
28002            };
28003
28004            GutterDimensions {
28005                left_padding,
28006                right_padding,
28007                width: line_gutter_width + left_padding + right_padding,
28008                margin: GutterDimensions::default_gutter_margin(font_id, font_size, cx),
28009                git_blame_entries_width,
28010            }
28011        } else if self.offset_content {
28012            GutterDimensions::default_with_margin(font_id, font_size, cx)
28013        } else {
28014            GutterDimensions::default()
28015        }
28016    }
28017
28018    pub fn render_crease_toggle(
28019        &self,
28020        buffer_row: MultiBufferRow,
28021        row_contains_cursor: bool,
28022        editor: Entity<Editor>,
28023        window: &mut Window,
28024        cx: &mut App,
28025    ) -> Option<AnyElement> {
28026        let folded = self.is_line_folded(buffer_row);
28027        let mut is_foldable = false;
28028
28029        if let Some(crease) = self
28030            .crease_snapshot
28031            .query_row(buffer_row, self.buffer_snapshot())
28032        {
28033            is_foldable = true;
28034            match crease {
28035                Crease::Inline { render_toggle, .. } | Crease::Block { render_toggle, .. } => {
28036                    if let Some(render_toggle) = render_toggle {
28037                        let toggle_callback =
28038                            Arc::new(move |folded, window: &mut Window, cx: &mut App| {
28039                                if folded {
28040                                    editor.update(cx, |editor, cx| {
28041                                        editor.fold_at(buffer_row, window, cx)
28042                                    });
28043                                } else {
28044                                    editor.update(cx, |editor, cx| {
28045                                        editor.unfold_at(buffer_row, window, cx)
28046                                    });
28047                                }
28048                            });
28049                        return Some((render_toggle)(
28050                            buffer_row,
28051                            folded,
28052                            toggle_callback,
28053                            window,
28054                            cx,
28055                        ));
28056                    }
28057                }
28058            }
28059        }
28060
28061        is_foldable |= !self.use_lsp_folding_ranges && self.starts_indent(buffer_row);
28062
28063        if folded || (is_foldable && (row_contains_cursor || self.gutter_hovered)) {
28064            Some(
28065                Disclosure::new(("gutter_crease", buffer_row.0), !folded)
28066                    .toggle_state(folded)
28067                    .on_click(window.listener_for(&editor, move |this, _e, window, cx| {
28068                        if folded {
28069                            this.unfold_at(buffer_row, window, cx);
28070                        } else {
28071                            this.fold_at(buffer_row, window, cx);
28072                        }
28073                    }))
28074                    .into_any_element(),
28075            )
28076        } else {
28077            None
28078        }
28079    }
28080
28081    pub fn render_crease_trailer(
28082        &self,
28083        buffer_row: MultiBufferRow,
28084        window: &mut Window,
28085        cx: &mut App,
28086    ) -> Option<AnyElement> {
28087        let folded = self.is_line_folded(buffer_row);
28088        if let Crease::Inline { render_trailer, .. } = self
28089            .crease_snapshot
28090            .query_row(buffer_row, self.buffer_snapshot())?
28091        {
28092            let render_trailer = render_trailer.as_ref()?;
28093            Some(render_trailer(buffer_row, folded, window, cx))
28094        } else {
28095            None
28096        }
28097    }
28098
28099    pub fn max_line_number_width(&self, style: &EditorStyle, window: &mut Window) -> Pixels {
28100        let digit_count = self.widest_line_number().ilog10() + 1;
28101        column_pixels(style, digit_count as usize, window)
28102    }
28103
28104    /// Returns the line delta from `base` to `line` in the multibuffer, ignoring wrapped lines.
28105    ///
28106    /// This is positive if `base` is before `line`.
28107    fn relative_line_delta(
28108        &self,
28109        current_selection_head: DisplayRow,
28110        first_visible_row: DisplayRow,
28111        consider_wrapped_lines: bool,
28112    ) -> i64 {
28113        let current_selection_head = current_selection_head.as_display_point().to_point(self);
28114        let first_visible_row = first_visible_row.as_display_point().to_point(self);
28115
28116        if consider_wrapped_lines {
28117            let wrap_snapshot = self.wrap_snapshot();
28118            let base_wrap_row = wrap_snapshot
28119                .make_wrap_point(current_selection_head, Bias::Left)
28120                .row();
28121            let wrap_row = wrap_snapshot
28122                .make_wrap_point(first_visible_row, Bias::Left)
28123                .row();
28124
28125            wrap_row.0 as i64 - base_wrap_row.0 as i64
28126        } else {
28127            let fold_snapshot = self.fold_snapshot();
28128            let base_fold_row = fold_snapshot
28129                .to_fold_point(self.to_inlay_point(current_selection_head), Bias::Left)
28130                .row();
28131            let fold_row = fold_snapshot
28132                .to_fold_point(self.to_inlay_point(first_visible_row), Bias::Left)
28133                .row();
28134
28135            fold_row as i64 - base_fold_row as i64
28136        }
28137    }
28138
28139    /// Returns the unsigned relative line number to display for each row in `rows`.
28140    ///
28141    /// Wrapped rows are excluded from the hashmap if `count_relative_lines` is `false`.
28142    pub fn calculate_relative_line_numbers(
28143        &self,
28144        rows: &Range<DisplayRow>,
28145        current_selection_head: DisplayRow,
28146        count_wrapped_lines: bool,
28147    ) -> HashMap<DisplayRow, u32> {
28148        let initial_offset =
28149            self.relative_line_delta(current_selection_head, rows.start, count_wrapped_lines);
28150
28151        self.row_infos(rows.start)
28152            .take(rows.len())
28153            .enumerate()
28154            .map(|(i, row_info)| (DisplayRow(rows.start.0 + i as u32), row_info))
28155            .filter(|(_row, row_info)| {
28156                row_info.buffer_row.is_some()
28157                    || (count_wrapped_lines && row_info.wrapped_buffer_row.is_some())
28158            })
28159            .enumerate()
28160            .filter_map(|(i, (row, row_info))| {
28161                // We want to ensure here that the current line has absolute
28162                // numbering, even if we are in a soft-wrapped line. With the
28163                // exception that if we are in a deleted line, we should number this
28164                // relative with 0, as otherwise it would have no line number at all
28165                let relative_line_number = (initial_offset + i as i64).unsigned_abs() as u32;
28166
28167                (relative_line_number != 0
28168                    || row_info
28169                        .diff_status
28170                        .is_some_and(|status| status.is_deleted()))
28171                .then_some((row, relative_line_number))
28172            })
28173            .collect()
28174    }
28175}
28176
28177pub fn column_pixels(style: &EditorStyle, column: usize, window: &Window) -> Pixels {
28178    let font_size = style.text.font_size.to_pixels(window.rem_size());
28179    let layout = window.text_system().shape_line(
28180        SharedString::from(" ".repeat(column)),
28181        font_size,
28182        &[TextRun {
28183            len: column,
28184            font: style.text.font(),
28185            color: Hsla::default(),
28186            ..Default::default()
28187        }],
28188        None,
28189    );
28190
28191    layout.width
28192}
28193
28194impl Deref for EditorSnapshot {
28195    type Target = DisplaySnapshot;
28196
28197    fn deref(&self) -> &Self::Target {
28198        &self.display_snapshot
28199    }
28200}
28201
28202#[derive(Clone, Debug, PartialEq, Eq)]
28203pub enum EditorEvent {
28204    /// Emitted when the stored review comments change (added, removed, or updated).
28205    ReviewCommentsChanged {
28206        /// The new total count of review comments.
28207        total_count: usize,
28208    },
28209    InputIgnored {
28210        text: Arc<str>,
28211    },
28212    InputHandled {
28213        utf16_range_to_replace: Option<Range<isize>>,
28214        text: Arc<str>,
28215    },
28216    ExcerptsAdded {
28217        buffer: Entity<Buffer>,
28218        predecessor: ExcerptId,
28219        excerpts: Vec<(ExcerptId, ExcerptRange<language::Anchor>)>,
28220    },
28221    ExcerptsRemoved {
28222        ids: Vec<ExcerptId>,
28223        removed_buffer_ids: Vec<BufferId>,
28224    },
28225    BufferFoldToggled {
28226        ids: Vec<ExcerptId>,
28227        folded: bool,
28228    },
28229    ExcerptsEdited {
28230        ids: Vec<ExcerptId>,
28231    },
28232    ExcerptsExpanded {
28233        ids: Vec<ExcerptId>,
28234    },
28235    ExpandExcerptsRequested {
28236        excerpt_ids: Vec<ExcerptId>,
28237        lines: u32,
28238        direction: ExpandExcerptDirection,
28239    },
28240    StageOrUnstageRequested {
28241        stage: bool,
28242        hunks: Vec<MultiBufferDiffHunk>,
28243    },
28244    OpenExcerptsRequested {
28245        selections_by_buffer: HashMap<BufferId, (Vec<Range<BufferOffset>>, Option<u32>)>,
28246        split: bool,
28247    },
28248    RestoreRequested {
28249        hunks: Vec<MultiBufferDiffHunk>,
28250    },
28251    BufferEdited,
28252    Edited {
28253        transaction_id: clock::Lamport,
28254    },
28255    Reparsed(BufferId),
28256    Focused,
28257    FocusedIn,
28258    Blurred,
28259    DirtyChanged,
28260    Saved,
28261    TitleChanged,
28262    SelectionsChanged {
28263        local: bool,
28264    },
28265    ScrollPositionChanged {
28266        local: bool,
28267        autoscroll: bool,
28268    },
28269    TransactionUndone {
28270        transaction_id: clock::Lamport,
28271    },
28272    TransactionBegun {
28273        transaction_id: clock::Lamport,
28274    },
28275    CursorShapeChanged,
28276    BreadcrumbsChanged,
28277    OutlineSymbolsChanged,
28278    PushedToNavHistory {
28279        anchor: Anchor,
28280        is_deactivate: bool,
28281    },
28282}
28283
28284impl EventEmitter<EditorEvent> for Editor {}
28285
28286impl Focusable for Editor {
28287    fn focus_handle(&self, _cx: &App) -> FocusHandle {
28288        self.focus_handle.clone()
28289    }
28290}
28291
28292impl Render for Editor {
28293    fn render(&mut self, _: &mut Window, cx: &mut Context<Self>) -> impl IntoElement {
28294        EditorElement::new(&cx.entity(), self.create_style(cx))
28295    }
28296}
28297
28298impl EntityInputHandler for Editor {
28299    fn text_for_range(
28300        &mut self,
28301        range_utf16: Range<usize>,
28302        adjusted_range: &mut Option<Range<usize>>,
28303        _: &mut Window,
28304        cx: &mut Context<Self>,
28305    ) -> Option<String> {
28306        let snapshot = self.buffer.read(cx).read(cx);
28307        let start = snapshot.clip_offset_utf16(
28308            MultiBufferOffsetUtf16(OffsetUtf16(range_utf16.start)),
28309            Bias::Left,
28310        );
28311        let end = snapshot.clip_offset_utf16(
28312            MultiBufferOffsetUtf16(OffsetUtf16(range_utf16.end)),
28313            Bias::Right,
28314        );
28315        if (start.0.0..end.0.0) != range_utf16 {
28316            adjusted_range.replace(start.0.0..end.0.0);
28317        }
28318        Some(snapshot.text_for_range(start..end).collect())
28319    }
28320
28321    fn selected_text_range(
28322        &mut self,
28323        ignore_disabled_input: bool,
28324        _: &mut Window,
28325        cx: &mut Context<Self>,
28326    ) -> Option<UTF16Selection> {
28327        // Prevent the IME menu from appearing when holding down an alphabetic key
28328        // while input is disabled.
28329        if !ignore_disabled_input && !self.input_enabled {
28330            return None;
28331        }
28332
28333        let selection = self
28334            .selections
28335            .newest::<MultiBufferOffsetUtf16>(&self.display_snapshot(cx));
28336        let range = selection.range();
28337
28338        Some(UTF16Selection {
28339            range: range.start.0.0..range.end.0.0,
28340            reversed: selection.reversed,
28341        })
28342    }
28343
28344    fn marked_text_range(&self, _: &mut Window, cx: &mut Context<Self>) -> Option<Range<usize>> {
28345        let snapshot = self.buffer.read(cx).read(cx);
28346        let range = self
28347            .text_highlights(HighlightKey::InputComposition, cx)?
28348            .1
28349            .first()?;
28350        Some(range.start.to_offset_utf16(&snapshot).0.0..range.end.to_offset_utf16(&snapshot).0.0)
28351    }
28352
28353    fn unmark_text(&mut self, _: &mut Window, cx: &mut Context<Self>) {
28354        self.clear_highlights(HighlightKey::InputComposition, cx);
28355        self.ime_transaction.take();
28356    }
28357
28358    fn replace_text_in_range(
28359        &mut self,
28360        range_utf16: Option<Range<usize>>,
28361        text: &str,
28362        window: &mut Window,
28363        cx: &mut Context<Self>,
28364    ) {
28365        if !self.input_enabled {
28366            cx.emit(EditorEvent::InputIgnored { text: text.into() });
28367            return;
28368        }
28369
28370        self.transact(window, cx, |this, window, cx| {
28371            let new_selected_ranges = if let Some(range_utf16) = range_utf16 {
28372                if let Some(marked_ranges) = this.marked_text_ranges(cx) {
28373                    // During IME composition, macOS reports the replacement range
28374                    // relative to the first marked region (the only one visible via
28375                    // marked_text_range). The correct targets for replacement are the
28376                    // marked ranges themselves — one per cursor — so use them directly.
28377                    Some(marked_ranges)
28378                } else if range_utf16.start == range_utf16.end {
28379                    // An empty replacement range means "insert at cursor" with no text
28380                    // to replace. macOS reports the cursor position from its own
28381                    // (single-cursor) view of the buffer, which diverges from our actual
28382                    // cursor positions after multi-cursor edits have shifted offsets.
28383                    // Treating this as range_utf16=None lets each cursor insert in place.
28384                    None
28385                } else {
28386                    // Outside of IME composition (e.g. Accessibility Keyboard word
28387                    // completion), the range is an absolute document offset for the
28388                    // newest cursor. Fan it out to all cursors via
28389                    // selection_replacement_ranges, which applies the delta relative
28390                    // to the newest selection to every cursor.
28391                    let range_utf16 = MultiBufferOffsetUtf16(OffsetUtf16(range_utf16.start))
28392                        ..MultiBufferOffsetUtf16(OffsetUtf16(range_utf16.end));
28393                    Some(this.selection_replacement_ranges(range_utf16, cx))
28394                }
28395            } else {
28396                this.marked_text_ranges(cx)
28397            };
28398
28399            let range_to_replace = new_selected_ranges.as_ref().and_then(|ranges_to_replace| {
28400                let newest_selection_id = this.selections.newest_anchor().id;
28401                this.selections
28402                    .all::<MultiBufferOffsetUtf16>(&this.display_snapshot(cx))
28403                    .iter()
28404                    .zip(ranges_to_replace.iter())
28405                    .find_map(|(selection, range)| {
28406                        if selection.id == newest_selection_id {
28407                            Some(
28408                                (range.start.0.0 as isize - selection.head().0.0 as isize)
28409                                    ..(range.end.0.0 as isize - selection.head().0.0 as isize),
28410                            )
28411                        } else {
28412                            None
28413                        }
28414                    })
28415            });
28416
28417            cx.emit(EditorEvent::InputHandled {
28418                utf16_range_to_replace: range_to_replace,
28419                text: text.into(),
28420            });
28421
28422            if let Some(new_selected_ranges) = new_selected_ranges {
28423                // Only backspace if at least one range covers actual text. When all
28424                // ranges are empty (e.g. a trailing-space insertion from Accessibility
28425                // Keyboard sends replacementRange=cursor..cursor), backspace would
28426                // incorrectly delete the character just before the cursor.
28427                let should_backspace = new_selected_ranges.iter().any(|r| r.start != r.end);
28428                this.change_selections(SelectionEffects::no_scroll(), window, cx, |selections| {
28429                    selections.select_ranges(new_selected_ranges)
28430                });
28431                if should_backspace {
28432                    this.backspace(&Default::default(), window, cx);
28433                }
28434            }
28435
28436            this.handle_input(text, window, cx);
28437        });
28438
28439        if let Some(transaction) = self.ime_transaction {
28440            self.buffer.update(cx, |buffer, cx| {
28441                buffer.group_until_transaction(transaction, cx);
28442            });
28443        }
28444
28445        self.unmark_text(window, cx);
28446    }
28447
28448    fn replace_and_mark_text_in_range(
28449        &mut self,
28450        range_utf16: Option<Range<usize>>,
28451        text: &str,
28452        new_selected_range_utf16: Option<Range<usize>>,
28453        window: &mut Window,
28454        cx: &mut Context<Self>,
28455    ) {
28456        if !self.input_enabled {
28457            return;
28458        }
28459
28460        let transaction = self.transact(window, cx, |this, window, cx| {
28461            let ranges_to_replace = if let Some(mut marked_ranges) = this.marked_text_ranges(cx) {
28462                let snapshot = this.buffer.read(cx).read(cx);
28463                if let Some(relative_range_utf16) = range_utf16.as_ref() {
28464                    for marked_range in &mut marked_ranges {
28465                        marked_range.end = marked_range.start + relative_range_utf16.end;
28466                        marked_range.start += relative_range_utf16.start;
28467                        marked_range.start =
28468                            snapshot.clip_offset_utf16(marked_range.start, Bias::Left);
28469                        marked_range.end =
28470                            snapshot.clip_offset_utf16(marked_range.end, Bias::Right);
28471                    }
28472                }
28473                Some(marked_ranges)
28474            } else if let Some(range_utf16) = range_utf16 {
28475                let range_utf16 = MultiBufferOffsetUtf16(OffsetUtf16(range_utf16.start))
28476                    ..MultiBufferOffsetUtf16(OffsetUtf16(range_utf16.end));
28477                Some(this.selection_replacement_ranges(range_utf16, cx))
28478            } else {
28479                None
28480            };
28481
28482            let range_to_replace = ranges_to_replace.as_ref().and_then(|ranges_to_replace| {
28483                let newest_selection_id = this.selections.newest_anchor().id;
28484                this.selections
28485                    .all::<MultiBufferOffsetUtf16>(&this.display_snapshot(cx))
28486                    .iter()
28487                    .zip(ranges_to_replace.iter())
28488                    .find_map(|(selection, range)| {
28489                        if selection.id == newest_selection_id {
28490                            Some(
28491                                (range.start.0.0 as isize - selection.head().0.0 as isize)
28492                                    ..(range.end.0.0 as isize - selection.head().0.0 as isize),
28493                            )
28494                        } else {
28495                            None
28496                        }
28497                    })
28498            });
28499
28500            cx.emit(EditorEvent::InputHandled {
28501                utf16_range_to_replace: range_to_replace,
28502                text: text.into(),
28503            });
28504
28505            if let Some(ranges) = ranges_to_replace {
28506                this.change_selections(SelectionEffects::no_scroll(), window, cx, |s| {
28507                    s.select_ranges(ranges)
28508                });
28509            }
28510
28511            let marked_ranges = {
28512                let snapshot = this.buffer.read(cx).read(cx);
28513                this.selections
28514                    .disjoint_anchors_arc()
28515                    .iter()
28516                    .map(|selection| {
28517                        selection.start.bias_left(&snapshot)..selection.end.bias_right(&snapshot)
28518                    })
28519                    .collect::<Vec<_>>()
28520            };
28521
28522            if text.is_empty() {
28523                this.unmark_text(window, cx);
28524            } else {
28525                this.highlight_text(
28526                    HighlightKey::InputComposition,
28527                    marked_ranges.clone(),
28528                    HighlightStyle {
28529                        underline: Some(UnderlineStyle {
28530                            thickness: px(1.),
28531                            color: None,
28532                            wavy: false,
28533                        }),
28534                        ..Default::default()
28535                    },
28536                    cx,
28537                );
28538            }
28539
28540            // Disable auto-closing when composing text (i.e. typing a `"` on a Brazilian keyboard)
28541            let use_autoclose = this.use_autoclose;
28542            let use_auto_surround = this.use_auto_surround;
28543            this.set_use_autoclose(false);
28544            this.set_use_auto_surround(false);
28545            this.handle_input(text, window, cx);
28546            this.set_use_autoclose(use_autoclose);
28547            this.set_use_auto_surround(use_auto_surround);
28548
28549            if let Some(new_selected_range) = new_selected_range_utf16 {
28550                let snapshot = this.buffer.read(cx).read(cx);
28551                let new_selected_ranges = marked_ranges
28552                    .into_iter()
28553                    .map(|marked_range| {
28554                        let insertion_start = marked_range.start.to_offset_utf16(&snapshot).0;
28555                        let new_start = MultiBufferOffsetUtf16(OffsetUtf16(
28556                            insertion_start.0 + new_selected_range.start,
28557                        ));
28558                        let new_end = MultiBufferOffsetUtf16(OffsetUtf16(
28559                            insertion_start.0 + new_selected_range.end,
28560                        ));
28561                        snapshot.clip_offset_utf16(new_start, Bias::Left)
28562                            ..snapshot.clip_offset_utf16(new_end, Bias::Right)
28563                    })
28564                    .collect::<Vec<_>>();
28565
28566                drop(snapshot);
28567                this.change_selections(SelectionEffects::no_scroll(), window, cx, |selections| {
28568                    selections.select_ranges(new_selected_ranges)
28569                });
28570            }
28571        });
28572
28573        self.ime_transaction = self.ime_transaction.or(transaction);
28574        if let Some(transaction) = self.ime_transaction {
28575            self.buffer.update(cx, |buffer, cx| {
28576                buffer.group_until_transaction(transaction, cx);
28577            });
28578        }
28579
28580        if self
28581            .text_highlights(HighlightKey::InputComposition, cx)
28582            .is_none()
28583        {
28584            self.ime_transaction.take();
28585        }
28586    }
28587
28588    fn bounds_for_range(
28589        &mut self,
28590        range_utf16: Range<usize>,
28591        element_bounds: gpui::Bounds<Pixels>,
28592        window: &mut Window,
28593        cx: &mut Context<Self>,
28594    ) -> Option<gpui::Bounds<Pixels>> {
28595        let text_layout_details = self.text_layout_details(window, cx);
28596        let CharacterDimensions {
28597            em_width,
28598            em_advance,
28599            line_height,
28600        } = self.character_dimensions(window, cx);
28601
28602        let snapshot = self.snapshot(window, cx);
28603        let scroll_position = snapshot.scroll_position();
28604        let scroll_left = scroll_position.x * ScrollOffset::from(em_advance);
28605
28606        let start =
28607            MultiBufferOffsetUtf16(OffsetUtf16(range_utf16.start)).to_display_point(&snapshot);
28608        let x = Pixels::from(
28609            ScrollOffset::from(
28610                snapshot.x_for_display_point(start, &text_layout_details)
28611                    + self.gutter_dimensions.full_width(),
28612            ) - scroll_left,
28613        );
28614        let y = line_height * (start.row().as_f64() - scroll_position.y) as f32;
28615
28616        Some(Bounds {
28617            origin: element_bounds.origin + point(x, y),
28618            size: size(em_width, line_height),
28619        })
28620    }
28621
28622    fn character_index_for_point(
28623        &mut self,
28624        point: gpui::Point<Pixels>,
28625        _window: &mut Window,
28626        _cx: &mut Context<Self>,
28627    ) -> Option<usize> {
28628        let position_map = self.last_position_map.as_ref()?;
28629        if !position_map.text_hitbox.contains(&point) {
28630            return None;
28631        }
28632        let display_point = position_map.point_for_position(point).previous_valid;
28633        let anchor = position_map
28634            .snapshot
28635            .display_point_to_anchor(display_point, Bias::Left);
28636        let utf16_offset = anchor.to_offset_utf16(&position_map.snapshot.buffer_snapshot());
28637        Some(utf16_offset.0.0)
28638    }
28639
28640    fn accepts_text_input(&self, _window: &mut Window, _cx: &mut Context<Self>) -> bool {
28641        self.expects_character_input
28642    }
28643}
28644
28645trait SelectionExt {
28646    fn display_range(&self, map: &DisplaySnapshot) -> Range<DisplayPoint>;
28647    fn spanned_rows(
28648        &self,
28649        include_end_if_at_line_start: bool,
28650        map: &DisplaySnapshot,
28651    ) -> Range<MultiBufferRow>;
28652}
28653
28654impl<T: ToPoint + ToOffset> SelectionExt for Selection<T> {
28655    fn display_range(&self, map: &DisplaySnapshot) -> Range<DisplayPoint> {
28656        let start = self
28657            .start
28658            .to_point(map.buffer_snapshot())
28659            .to_display_point(map);
28660        let end = self
28661            .end
28662            .to_point(map.buffer_snapshot())
28663            .to_display_point(map);
28664        if self.reversed {
28665            end..start
28666        } else {
28667            start..end
28668        }
28669    }
28670
28671    fn spanned_rows(
28672        &self,
28673        include_end_if_at_line_start: bool,
28674        map: &DisplaySnapshot,
28675    ) -> Range<MultiBufferRow> {
28676        let start = self.start.to_point(map.buffer_snapshot());
28677        let mut end = self.end.to_point(map.buffer_snapshot());
28678        if !include_end_if_at_line_start && start.row != end.row && end.column == 0 {
28679            end.row -= 1;
28680        }
28681
28682        let buffer_start = map.prev_line_boundary(start).0;
28683        let buffer_end = map.next_line_boundary(end).0;
28684        MultiBufferRow(buffer_start.row)..MultiBufferRow(buffer_end.row + 1)
28685    }
28686}
28687
28688impl<T: InvalidationRegion> InvalidationStack<T> {
28689    fn invalidate<S>(&mut self, selections: &[Selection<S>], buffer: &MultiBufferSnapshot)
28690    where
28691        S: Clone + ToOffset,
28692    {
28693        while let Some(region) = self.last() {
28694            let all_selections_inside_invalidation_ranges =
28695                if selections.len() == region.ranges().len() {
28696                    selections
28697                        .iter()
28698                        .zip(region.ranges().iter().map(|r| r.to_offset(buffer)))
28699                        .all(|(selection, invalidation_range)| {
28700                            let head = selection.head().to_offset(buffer);
28701                            invalidation_range.start <= head && invalidation_range.end >= head
28702                        })
28703                } else {
28704                    false
28705                };
28706
28707            if all_selections_inside_invalidation_ranges {
28708                break;
28709            } else {
28710                self.pop();
28711            }
28712        }
28713    }
28714}
28715
28716#[derive(Clone)]
28717struct ErasedEditorImpl(Entity<Editor>);
28718
28719impl ui_input::ErasedEditor for ErasedEditorImpl {
28720    fn text(&self, cx: &App) -> String {
28721        self.0.read(cx).text(cx)
28722    }
28723
28724    fn set_text(&self, text: &str, window: &mut Window, cx: &mut App) {
28725        self.0.update(cx, |this, cx| {
28726            this.set_text(text, window, cx);
28727        })
28728    }
28729
28730    fn clear(&self, window: &mut Window, cx: &mut App) {
28731        self.0.update(cx, |this, cx| this.clear(window, cx));
28732    }
28733
28734    fn set_placeholder_text(&self, text: &str, window: &mut Window, cx: &mut App) {
28735        self.0.update(cx, |this, cx| {
28736            this.set_placeholder_text(text, window, cx);
28737        });
28738    }
28739
28740    fn focus_handle(&self, cx: &App) -> FocusHandle {
28741        self.0.read(cx).focus_handle(cx)
28742    }
28743
28744    fn render(&self, _: &mut Window, cx: &App) -> AnyElement {
28745        let settings = ThemeSettings::get_global(cx);
28746        let theme_color = cx.theme().colors();
28747
28748        let text_style = TextStyle {
28749            font_family: settings.ui_font.family.clone(),
28750            font_features: settings.ui_font.features.clone(),
28751            font_size: rems(0.875).into(),
28752            font_weight: settings.ui_font.weight,
28753            font_style: FontStyle::Normal,
28754            line_height: relative(1.2),
28755            color: theme_color.text,
28756            ..Default::default()
28757        };
28758        let editor_style = EditorStyle {
28759            background: theme_color.ghost_element_background,
28760            local_player: cx.theme().players().local(),
28761            syntax: cx.theme().syntax().clone(),
28762            text: text_style,
28763            ..Default::default()
28764        };
28765        EditorElement::new(&self.0, editor_style).into_any()
28766    }
28767
28768    fn as_any(&self) -> &dyn Any {
28769        &self.0
28770    }
28771
28772    fn move_selection_to_end(&self, window: &mut Window, cx: &mut App) {
28773        self.0.update(cx, |editor, cx| {
28774            let editor_offset = editor.buffer().read(cx).len(cx);
28775            editor.change_selections(
28776                SelectionEffects::scroll(Autoscroll::Next),
28777                window,
28778                cx,
28779                |s| s.select_ranges(Some(editor_offset..editor_offset)),
28780            );
28781        });
28782    }
28783
28784    fn subscribe(
28785        &self,
28786        mut callback: Box<dyn FnMut(ui_input::ErasedEditorEvent, &mut Window, &mut App) + 'static>,
28787        window: &mut Window,
28788        cx: &mut App,
28789    ) -> Subscription {
28790        window.subscribe(&self.0, cx, move |_, event: &EditorEvent, window, cx| {
28791            let event = match event {
28792                EditorEvent::BufferEdited => ui_input::ErasedEditorEvent::BufferEdited,
28793                EditorEvent::Blurred => ui_input::ErasedEditorEvent::Blurred,
28794                _ => return,
28795            };
28796            (callback)(event, window, cx);
28797        })
28798    }
28799
28800    fn set_masked(&self, masked: bool, _window: &mut Window, cx: &mut App) {
28801        self.0.update(cx, |editor, cx| {
28802            editor.set_masked(masked, cx);
28803        });
28804    }
28805}
28806impl<T> Default for InvalidationStack<T> {
28807    fn default() -> Self {
28808        Self(Default::default())
28809    }
28810}
28811
28812impl<T> Deref for InvalidationStack<T> {
28813    type Target = Vec<T>;
28814
28815    fn deref(&self) -> &Self::Target {
28816        &self.0
28817    }
28818}
28819
28820impl<T> DerefMut for InvalidationStack<T> {
28821    fn deref_mut(&mut self) -> &mut Self::Target {
28822        &mut self.0
28823    }
28824}
28825
28826impl InvalidationRegion for SnippetState {
28827    fn ranges(&self) -> &[Range<Anchor>] {
28828        &self.ranges[self.active_index]
28829    }
28830}
28831
28832fn edit_prediction_edit_text(
28833    current_snapshot: &BufferSnapshot,
28834    edits: &[(Range<Anchor>, impl AsRef<str>)],
28835    edit_preview: &EditPreview,
28836    include_deletions: bool,
28837    cx: &App,
28838) -> HighlightedText {
28839    let edits = edits
28840        .iter()
28841        .map(|(anchor, text)| (anchor.start.text_anchor..anchor.end.text_anchor, text))
28842        .collect::<Vec<_>>();
28843
28844    edit_preview.highlight_edits(current_snapshot, &edits, include_deletions, cx)
28845}
28846
28847fn edit_prediction_fallback_text(edits: &[(Range<Anchor>, Arc<str>)], cx: &App) -> HighlightedText {
28848    // Fallback for providers that don't provide edit_preview (like Copilot)
28849    // Just show the raw edit text with basic styling
28850    let mut text = String::new();
28851    let mut highlights = Vec::new();
28852
28853    let insertion_highlight_style = HighlightStyle {
28854        color: Some(cx.theme().colors().text),
28855        ..Default::default()
28856    };
28857
28858    for (_, edit_text) in edits {
28859        let start_offset = text.len();
28860        text.push_str(edit_text);
28861        let end_offset = text.len();
28862
28863        if start_offset < end_offset {
28864            highlights.push((start_offset..end_offset, insertion_highlight_style));
28865        }
28866    }
28867
28868    HighlightedText {
28869        text: text.into(),
28870        highlights,
28871    }
28872}
28873
28874pub fn diagnostic_style(severity: lsp::DiagnosticSeverity, colors: &StatusColors) -> Hsla {
28875    match severity {
28876        lsp::DiagnosticSeverity::ERROR => colors.error,
28877        lsp::DiagnosticSeverity::WARNING => colors.warning,
28878        lsp::DiagnosticSeverity::INFORMATION => colors.info,
28879        lsp::DiagnosticSeverity::HINT => colors.info,
28880        _ => colors.ignored,
28881    }
28882}
28883
28884pub fn styled_runs_for_code_label<'a>(
28885    label: &'a CodeLabel,
28886    syntax_theme: &'a theme::SyntaxTheme,
28887    local_player: &'a theme::PlayerColor,
28888) -> impl 'a + Iterator<Item = (Range<usize>, HighlightStyle)> {
28889    let fade_out = HighlightStyle {
28890        fade_out: Some(0.35),
28891        ..Default::default()
28892    };
28893
28894    if label.runs.is_empty() {
28895        let desc_start = label.filter_range.end;
28896        let fade_run =
28897            (desc_start < label.text.len()).then(|| (desc_start..label.text.len(), fade_out));
28898        return Either::Left(fade_run.into_iter());
28899    }
28900
28901    let mut prev_end = label.filter_range.end;
28902    Either::Right(
28903        label
28904            .runs
28905            .iter()
28906            .enumerate()
28907            .flat_map(move |(ix, (range, highlight_id))| {
28908                let style = if *highlight_id == language::HighlightId::TABSTOP_INSERT_ID {
28909                    HighlightStyle {
28910                        color: Some(local_player.cursor),
28911                        ..Default::default()
28912                    }
28913                } else if *highlight_id == language::HighlightId::TABSTOP_REPLACE_ID {
28914                    HighlightStyle {
28915                        background_color: Some(local_player.selection),
28916                        ..Default::default()
28917                    }
28918                } else if let Some(style) = syntax_theme.get(*highlight_id).cloned() {
28919                    style
28920                } else {
28921                    return Default::default();
28922                };
28923
28924                let mut runs = SmallVec::<[(Range<usize>, HighlightStyle); 3]>::new();
28925                let muted_style = style.highlight(fade_out);
28926                if range.start >= label.filter_range.end {
28927                    if range.start > prev_end {
28928                        runs.push((prev_end..range.start, fade_out));
28929                    }
28930                    runs.push((range.clone(), muted_style));
28931                } else if range.end <= label.filter_range.end {
28932                    runs.push((range.clone(), style));
28933                } else {
28934                    runs.push((range.start..label.filter_range.end, style));
28935                    runs.push((label.filter_range.end..range.end, muted_style));
28936                }
28937                prev_end = cmp::max(prev_end, range.end);
28938
28939                if ix + 1 == label.runs.len() && label.text.len() > prev_end {
28940                    runs.push((prev_end..label.text.len(), fade_out));
28941                }
28942
28943                runs
28944            }),
28945    )
28946}
28947
28948pub(crate) fn split_words(text: &str) -> impl std::iter::Iterator<Item = &str> + '_ {
28949    let mut prev_index = 0;
28950    let mut prev_codepoint: Option<char> = None;
28951    text.char_indices()
28952        .chain([(text.len(), '\0')])
28953        .filter_map(move |(index, codepoint)| {
28954            let prev_codepoint = prev_codepoint.replace(codepoint)?;
28955            let is_boundary = index == text.len()
28956                || !prev_codepoint.is_uppercase() && codepoint.is_uppercase()
28957                || !prev_codepoint.is_alphanumeric() && codepoint.is_alphanumeric();
28958            if is_boundary {
28959                let chunk = &text[prev_index..index];
28960                prev_index = index;
28961                Some(chunk)
28962            } else {
28963                None
28964            }
28965        })
28966}
28967
28968/// Given a string of text immediately before the cursor, iterates over possible
28969/// strings a snippet could match to. More precisely: returns an iterator over
28970/// suffixes of `text` created by splitting at word boundaries (before & after
28971/// every non-word character).
28972///
28973/// Shorter suffixes are returned first.
28974pub(crate) fn snippet_candidate_suffixes<'a>(
28975    text: &'a str,
28976    is_word_char: &'a dyn Fn(char) -> bool,
28977) -> impl std::iter::Iterator<Item = &'a str> + 'a {
28978    let mut prev_index = text.len();
28979    let mut prev_codepoint = None;
28980    text.char_indices()
28981        .rev()
28982        .chain([(0, '\0')])
28983        .filter_map(move |(index, codepoint)| {
28984            let prev_index = std::mem::replace(&mut prev_index, index);
28985            let prev_codepoint = prev_codepoint.replace(codepoint)?;
28986            if is_word_char(prev_codepoint) && is_word_char(codepoint) {
28987                None
28988            } else {
28989                let chunk = &text[prev_index..]; // go to end of string
28990                Some(chunk)
28991            }
28992        })
28993}
28994
28995pub trait RangeToAnchorExt: Sized {
28996    fn to_anchors(self, snapshot: &MultiBufferSnapshot) -> Range<Anchor>;
28997
28998    fn to_display_points(self, snapshot: &EditorSnapshot) -> Range<DisplayPoint> {
28999        let anchor_range = self.to_anchors(&snapshot.buffer_snapshot());
29000        anchor_range.start.to_display_point(snapshot)..anchor_range.end.to_display_point(snapshot)
29001    }
29002}
29003
29004impl<T: ToOffset> RangeToAnchorExt for Range<T> {
29005    fn to_anchors(self, snapshot: &MultiBufferSnapshot) -> Range<Anchor> {
29006        let start_offset = self.start.to_offset(snapshot);
29007        let end_offset = self.end.to_offset(snapshot);
29008        if start_offset == end_offset {
29009            snapshot.anchor_before(start_offset)..snapshot.anchor_before(end_offset)
29010        } else {
29011            snapshot.anchor_after(self.start)..snapshot.anchor_before(self.end)
29012        }
29013    }
29014}
29015
29016pub trait RowExt {
29017    fn as_f64(&self) -> f64;
29018
29019    fn next_row(&self) -> Self;
29020
29021    fn previous_row(&self) -> Self;
29022
29023    fn minus(&self, other: Self) -> u32;
29024}
29025
29026impl RowExt for DisplayRow {
29027    fn as_f64(&self) -> f64 {
29028        self.0 as _
29029    }
29030
29031    fn next_row(&self) -> Self {
29032        Self(self.0 + 1)
29033    }
29034
29035    fn previous_row(&self) -> Self {
29036        Self(self.0.saturating_sub(1))
29037    }
29038
29039    fn minus(&self, other: Self) -> u32 {
29040        self.0 - other.0
29041    }
29042}
29043
29044impl RowExt for MultiBufferRow {
29045    fn as_f64(&self) -> f64 {
29046        self.0 as _
29047    }
29048
29049    fn next_row(&self) -> Self {
29050        Self(self.0 + 1)
29051    }
29052
29053    fn previous_row(&self) -> Self {
29054        Self(self.0.saturating_sub(1))
29055    }
29056
29057    fn minus(&self, other: Self) -> u32 {
29058        self.0 - other.0
29059    }
29060}
29061
29062trait RowRangeExt {
29063    type Row;
29064
29065    fn len(&self) -> usize;
29066
29067    fn iter_rows(&self) -> impl DoubleEndedIterator<Item = Self::Row>;
29068}
29069
29070impl RowRangeExt for Range<MultiBufferRow> {
29071    type Row = MultiBufferRow;
29072
29073    fn len(&self) -> usize {
29074        (self.end.0 - self.start.0) as usize
29075    }
29076
29077    fn iter_rows(&self) -> impl DoubleEndedIterator<Item = MultiBufferRow> {
29078        (self.start.0..self.end.0).map(MultiBufferRow)
29079    }
29080}
29081
29082impl RowRangeExt for Range<DisplayRow> {
29083    type Row = DisplayRow;
29084
29085    fn len(&self) -> usize {
29086        (self.end.0 - self.start.0) as usize
29087    }
29088
29089    fn iter_rows(&self) -> impl DoubleEndedIterator<Item = DisplayRow> {
29090        (self.start.0..self.end.0).map(DisplayRow)
29091    }
29092}
29093
29094/// If select range has more than one line, we
29095/// just point the cursor to range.start.
29096fn collapse_multiline_range(range: Range<Point>) -> Range<Point> {
29097    if range.start.row == range.end.row {
29098        range
29099    } else {
29100        range.start..range.start
29101    }
29102}
29103pub struct KillRing(ClipboardItem);
29104impl Global for KillRing {}
29105
29106const UPDATE_DEBOUNCE: Duration = Duration::from_millis(50);
29107
29108enum BreakpointPromptEditAction {
29109    Log,
29110    Condition,
29111    HitCondition,
29112}
29113
29114struct BreakpointPromptEditor {
29115    pub(crate) prompt: Entity<Editor>,
29116    editor: WeakEntity<Editor>,
29117    breakpoint_anchor: Anchor,
29118    breakpoint: Breakpoint,
29119    edit_action: BreakpointPromptEditAction,
29120    block_ids: HashSet<CustomBlockId>,
29121    editor_margins: Arc<Mutex<EditorMargins>>,
29122    _subscriptions: Vec<Subscription>,
29123}
29124
29125impl BreakpointPromptEditor {
29126    const MAX_LINES: u8 = 4;
29127
29128    fn new(
29129        editor: WeakEntity<Editor>,
29130        breakpoint_anchor: Anchor,
29131        breakpoint: Breakpoint,
29132        edit_action: BreakpointPromptEditAction,
29133        window: &mut Window,
29134        cx: &mut Context<Self>,
29135    ) -> Self {
29136        let base_text = match edit_action {
29137            BreakpointPromptEditAction::Log => breakpoint.message.as_ref(),
29138            BreakpointPromptEditAction::Condition => breakpoint.condition.as_ref(),
29139            BreakpointPromptEditAction::HitCondition => breakpoint.hit_condition.as_ref(),
29140        }
29141        .map(|msg| msg.to_string())
29142        .unwrap_or_default();
29143
29144        let buffer = cx.new(|cx| Buffer::local(base_text, cx));
29145        let buffer = cx.new(|cx| MultiBuffer::singleton(buffer, cx));
29146
29147        let prompt = cx.new(|cx| {
29148            let mut prompt = Editor::new(
29149                EditorMode::AutoHeight {
29150                    min_lines: 1,
29151                    max_lines: Some(Self::MAX_LINES as usize),
29152                },
29153                buffer,
29154                None,
29155                window,
29156                cx,
29157            );
29158            prompt.set_soft_wrap_mode(language::language_settings::SoftWrap::EditorWidth, cx);
29159            prompt.set_show_cursor_when_unfocused(false, cx);
29160            prompt.set_placeholder_text(
29161                match edit_action {
29162                    BreakpointPromptEditAction::Log => "Message to log when a breakpoint is hit. Expressions within {} are interpolated.",
29163                    BreakpointPromptEditAction::Condition => "Condition when a breakpoint is hit. Expressions within {} are interpolated.",
29164                    BreakpointPromptEditAction::HitCondition => "How many breakpoint hits to ignore",
29165                },
29166                window,
29167                cx,
29168            );
29169
29170            prompt
29171        });
29172
29173        Self {
29174            prompt,
29175            editor,
29176            breakpoint_anchor,
29177            breakpoint,
29178            edit_action,
29179            editor_margins: Arc::new(Mutex::new(EditorMargins::default())),
29180            block_ids: Default::default(),
29181            _subscriptions: vec![],
29182        }
29183    }
29184
29185    pub(crate) fn add_block_ids(&mut self, block_ids: Vec<CustomBlockId>) {
29186        self.block_ids.extend(block_ids)
29187    }
29188
29189    fn confirm(&mut self, _: &menu::Confirm, window: &mut Window, cx: &mut Context<Self>) {
29190        if let Some(editor) = self.editor.upgrade() {
29191            let message = self
29192                .prompt
29193                .read(cx)
29194                .buffer
29195                .read(cx)
29196                .as_singleton()
29197                .expect("A multi buffer in breakpoint prompt isn't possible")
29198                .read(cx)
29199                .as_rope()
29200                .to_string();
29201
29202            editor.update(cx, |editor, cx| {
29203                editor.edit_breakpoint_at_anchor(
29204                    self.breakpoint_anchor,
29205                    self.breakpoint.clone(),
29206                    match self.edit_action {
29207                        BreakpointPromptEditAction::Log => {
29208                            BreakpointEditAction::EditLogMessage(message.into())
29209                        }
29210                        BreakpointPromptEditAction::Condition => {
29211                            BreakpointEditAction::EditCondition(message.into())
29212                        }
29213                        BreakpointPromptEditAction::HitCondition => {
29214                            BreakpointEditAction::EditHitCondition(message.into())
29215                        }
29216                    },
29217                    cx,
29218                );
29219
29220                editor.remove_blocks(self.block_ids.clone(), None, cx);
29221                cx.focus_self(window);
29222            });
29223        }
29224    }
29225
29226    fn cancel(&mut self, _: &menu::Cancel, window: &mut Window, cx: &mut Context<Self>) {
29227        self.editor
29228            .update(cx, |editor, cx| {
29229                editor.remove_blocks(self.block_ids.clone(), None, cx);
29230                window.focus(&editor.focus_handle, cx);
29231            })
29232            .log_err();
29233    }
29234
29235    fn render_prompt_editor(&self, cx: &mut Context<Self>) -> impl IntoElement {
29236        let settings = ThemeSettings::get_global(cx);
29237        let text_style = TextStyle {
29238            color: if self.prompt.read(cx).read_only(cx) {
29239                cx.theme().colors().text_disabled
29240            } else {
29241                cx.theme().colors().text
29242            },
29243            font_family: settings.buffer_font.family.clone(),
29244            font_fallbacks: settings.buffer_font.fallbacks.clone(),
29245            font_size: settings.buffer_font_size(cx).into(),
29246            font_weight: settings.buffer_font.weight,
29247            line_height: relative(settings.buffer_line_height.value()),
29248            ..Default::default()
29249        };
29250        EditorElement::new(
29251            &self.prompt,
29252            EditorStyle {
29253                background: cx.theme().colors().editor_background,
29254                local_player: cx.theme().players().local(),
29255                text: text_style,
29256                ..Default::default()
29257            },
29258        )
29259    }
29260
29261    fn render_close_button(&self, cx: &mut Context<Self>) -> impl IntoElement {
29262        let focus_handle = self.prompt.focus_handle(cx);
29263        IconButton::new("cancel", IconName::Close)
29264            .icon_color(Color::Muted)
29265            .shape(IconButtonShape::Square)
29266            .tooltip(move |_window, cx| {
29267                Tooltip::for_action_in("Cancel", &menu::Cancel, &focus_handle, cx)
29268            })
29269            .on_click(cx.listener(|this, _, window, cx| {
29270                this.cancel(&menu::Cancel, window, cx);
29271            }))
29272    }
29273
29274    fn render_confirm_button(&self, cx: &mut Context<Self>) -> impl IntoElement {
29275        let focus_handle = self.prompt.focus_handle(cx);
29276        IconButton::new("confirm", IconName::Return)
29277            .icon_color(Color::Muted)
29278            .shape(IconButtonShape::Square)
29279            .tooltip(move |_window, cx| {
29280                Tooltip::for_action_in("Confirm", &menu::Confirm, &focus_handle, cx)
29281            })
29282            .on_click(cx.listener(|this, _, window, cx| {
29283                this.confirm(&menu::Confirm, window, cx);
29284            }))
29285    }
29286}
29287
29288impl Render for BreakpointPromptEditor {
29289    fn render(&mut self, window: &mut Window, cx: &mut Context<Self>) -> impl IntoElement {
29290        let ui_font_size = ThemeSettings::get_global(cx).ui_font_size(cx);
29291        let editor_margins = *self.editor_margins.lock();
29292        let gutter_dimensions = editor_margins.gutter;
29293        let left_gutter_width = gutter_dimensions.full_width() + (gutter_dimensions.margin / 2.0);
29294        let right_padding = editor_margins.right + px(9.);
29295        h_flex()
29296            .key_context("Editor")
29297            .bg(cx.theme().colors().editor_background)
29298            .border_y_1()
29299            .border_color(cx.theme().status().info_border)
29300            .size_full()
29301            .py(window.line_height() / 2.5)
29302            .pr(right_padding)
29303            .on_action(cx.listener(Self::confirm))
29304            .on_action(cx.listener(Self::cancel))
29305            .child(
29306                WithRemSize::new(ui_font_size)
29307                    .h_full()
29308                    .w(left_gutter_width)
29309                    .flex()
29310                    .flex_row()
29311                    .flex_shrink_0()
29312                    .items_center()
29313                    .justify_center()
29314                    .gap_1()
29315                    .child(self.render_close_button(cx)),
29316            )
29317            .child(
29318                h_flex()
29319                    .w_full()
29320                    .justify_between()
29321                    .child(div().flex_1().child(self.render_prompt_editor(cx)))
29322                    .child(
29323                        WithRemSize::new(ui_font_size)
29324                            .flex()
29325                            .flex_row()
29326                            .items_center()
29327                            .child(self.render_confirm_button(cx)),
29328                    ),
29329            )
29330    }
29331}
29332
29333impl Focusable for BreakpointPromptEditor {
29334    fn focus_handle(&self, cx: &App) -> FocusHandle {
29335        self.prompt.focus_handle(cx)
29336    }
29337}
29338
29339fn all_edits_insertions_or_deletions(
29340    edits: &Vec<(Range<Anchor>, Arc<str>)>,
29341    snapshot: &MultiBufferSnapshot,
29342) -> bool {
29343    let mut all_insertions = true;
29344    let mut all_deletions = true;
29345
29346    for (range, new_text) in edits.iter() {
29347        let range_is_empty = range.to_offset(snapshot).is_empty();
29348        let text_is_empty = new_text.is_empty();
29349
29350        if range_is_empty != text_is_empty {
29351            if range_is_empty {
29352                all_deletions = false;
29353            } else {
29354                all_insertions = false;
29355            }
29356        } else {
29357            return false;
29358        }
29359
29360        if !all_insertions && !all_deletions {
29361            return false;
29362        }
29363    }
29364    all_insertions || all_deletions
29365}
29366
29367struct MissingEditPredictionKeybindingTooltip;
29368
29369impl Render for MissingEditPredictionKeybindingTooltip {
29370    fn render(&mut self, _: &mut Window, cx: &mut Context<Self>) -> impl IntoElement {
29371        ui::tooltip_container(cx, |container, cx| {
29372            container
29373                .flex_shrink_0()
29374                .max_w_80()
29375                .min_h(rems_from_px(124.))
29376                .justify_between()
29377                .child(
29378                    v_flex()
29379                        .flex_1()
29380                        .text_ui_sm(cx)
29381                        .child(Label::new("Conflict with Accept Keybinding"))
29382                        .child("Your keymap currently overrides the default accept keybinding. To continue, assign one keybinding for the `editor::AcceptEditPrediction` action.")
29383                )
29384                .child(
29385                    h_flex()
29386                        .pb_1()
29387                        .gap_1()
29388                        .items_end()
29389                        .w_full()
29390                        .child(Button::new("open-keymap", "Assign Keybinding").size(ButtonSize::Compact).on_click(|_ev, window, cx| {
29391                            window.dispatch_action(zed_actions::OpenKeymapFile.boxed_clone(), cx)
29392                        }))
29393                        .child(Button::new("see-docs", "See Docs").size(ButtonSize::Compact).on_click(|_ev, _window, cx| {
29394                            cx.open_url("https://zed.dev/docs/completions#edit-predictions-missing-keybinding");
29395                        })),
29396                )
29397        })
29398    }
29399}
29400
29401#[derive(Debug, Clone, Copy, PartialEq)]
29402pub struct LineHighlight {
29403    pub background: Background,
29404    pub border: Option<gpui::Hsla>,
29405    pub include_gutter: bool,
29406    pub type_id: Option<TypeId>,
29407}
29408
29409struct LineManipulationResult {
29410    pub new_text: String,
29411    pub line_count_before: usize,
29412    pub line_count_after: usize,
29413}
29414
29415fn render_diff_hunk_controls(
29416    row: u32,
29417    status: &DiffHunkStatus,
29418    hunk_range: Range<Anchor>,
29419    is_created_file: bool,
29420    line_height: Pixels,
29421    editor: &Entity<Editor>,
29422    _window: &mut Window,
29423    cx: &mut App,
29424) -> AnyElement {
29425    h_flex()
29426        .h(line_height)
29427        .mr_1()
29428        .gap_1()
29429        .px_0p5()
29430        .pb_1()
29431        .border_x_1()
29432        .border_b_1()
29433        .border_color(cx.theme().colors().border_variant)
29434        .rounded_b_lg()
29435        .bg(cx.theme().colors().editor_background)
29436        .gap_1()
29437        .block_mouse_except_scroll()
29438        .shadow_md()
29439        .child(if status.has_secondary_hunk() {
29440            Button::new(("stage", row as u64), "Stage")
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                            "Stage 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                                true,
29459                                vec![hunk_range.start..hunk_range.start],
29460                                cx,
29461                            );
29462                        });
29463                    }
29464                })
29465        } else {
29466            Button::new(("unstage", row as u64), "Unstage")
29467                .alpha(if status.is_pending() { 0.66 } else { 1.0 })
29468                .tooltip({
29469                    let focus_handle = editor.focus_handle(cx);
29470                    move |_window, cx| {
29471                        Tooltip::for_action_in(
29472                            "Unstage Hunk",
29473                            &::git::ToggleStaged,
29474                            &focus_handle,
29475                            cx,
29476                        )
29477                    }
29478                })
29479                .on_click({
29480                    let editor = editor.clone();
29481                    move |_event, _window, cx| {
29482                        editor.update(cx, |editor, cx| {
29483                            editor.stage_or_unstage_diff_hunks(
29484                                false,
29485                                vec![hunk_range.start..hunk_range.start],
29486                                cx,
29487                            );
29488                        });
29489                    }
29490                })
29491        })
29492        .child(
29493            Button::new(("restore", row as u64), "Restore")
29494                .tooltip({
29495                    let focus_handle = editor.focus_handle(cx);
29496                    move |_window, cx| {
29497                        Tooltip::for_action_in("Restore Hunk", &::git::Restore, &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 point = hunk_range.start.to_point(&snapshot.buffer_snapshot());
29506                            editor.restore_hunks_in_ranges(vec![point..point], window, cx);
29507                        });
29508                    }
29509                })
29510                .disabled(is_created_file),
29511        )
29512        .when(
29513            !editor.read(cx).buffer().read(cx).all_diff_hunks_expanded(),
29514            |el| {
29515                el.child(
29516                    IconButton::new(("next-hunk", row as u64), IconName::ArrowDown)
29517                        .shape(IconButtonShape::Square)
29518                        .icon_size(IconSize::Small)
29519                        // .disabled(!has_multiple_hunks)
29520                        .tooltip({
29521                            let focus_handle = editor.focus_handle(cx);
29522                            move |_window, cx| {
29523                                Tooltip::for_action_in("Next Hunk", &GoToHunk, &focus_handle, cx)
29524                            }
29525                        })
29526                        .on_click({
29527                            let editor = editor.clone();
29528                            move |_event, window, cx| {
29529                                editor.update(cx, |editor, cx| {
29530                                    let snapshot = editor.snapshot(window, cx);
29531                                    let position =
29532                                        hunk_range.end.to_point(&snapshot.buffer_snapshot());
29533                                    editor.go_to_hunk_before_or_after_position(
29534                                        &snapshot,
29535                                        position,
29536                                        Direction::Next,
29537                                        true,
29538                                        window,
29539                                        cx,
29540                                    );
29541                                    editor.expand_selected_diff_hunks(cx);
29542                                });
29543                            }
29544                        }),
29545                )
29546                .child(
29547                    IconButton::new(("prev-hunk", row as u64), IconName::ArrowUp)
29548                        .shape(IconButtonShape::Square)
29549                        .icon_size(IconSize::Small)
29550                        // .disabled(!has_multiple_hunks)
29551                        .tooltip({
29552                            let focus_handle = editor.focus_handle(cx);
29553                            move |_window, cx| {
29554                                Tooltip::for_action_in(
29555                                    "Previous Hunk",
29556                                    &GoToPreviousHunk,
29557                                    &focus_handle,
29558                                    cx,
29559                                )
29560                            }
29561                        })
29562                        .on_click({
29563                            let editor = editor.clone();
29564                            move |_event, window, cx| {
29565                                editor.update(cx, |editor, cx| {
29566                                    let snapshot = editor.snapshot(window, cx);
29567                                    let point =
29568                                        hunk_range.start.to_point(&snapshot.buffer_snapshot());
29569                                    editor.go_to_hunk_before_or_after_position(
29570                                        &snapshot,
29571                                        point,
29572                                        Direction::Prev,
29573                                        true,
29574                                        window,
29575                                        cx,
29576                                    );
29577                                    editor.expand_selected_diff_hunks(cx);
29578                                });
29579                            }
29580                        }),
29581                )
29582            },
29583        )
29584        .into_any_element()
29585}
29586
29587pub fn multibuffer_context_lines(cx: &App) -> u32 {
29588    EditorSettings::try_get(cx)
29589        .map(|settings| settings.excerpt_context_lines)
29590        .unwrap_or(2)
29591        .min(32)
29592}