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//! * [`inlay_hint_cache`] - is a storage of inlay hints out of LSP requests, responsible for querying LSP and updating `display_map`'s state accordingly.
11//!
12//! All other submodules and structs are mostly concerned with holding editor data about the way it displays current buffer region(s).
13//!
14//! If you're looking to improve Vim mode, you should check out Vim crate that wraps Editor and overrides its behavior.
15pub mod actions;
16mod blink_manager;
17mod clangd_ext;
18pub mod code_context_menus;
19pub mod display_map;
20mod editor_settings;
21mod editor_settings_controls;
22mod element;
23mod git;
24mod highlight_matching_bracket;
25mod hover_links;
26pub mod hover_popover;
27mod indent_guides;
28mod inlay_hint_cache;
29pub mod items;
30mod jsx_tag_auto_close;
31mod linked_editing_ranges;
32mod lsp_colors;
33mod lsp_ext;
34mod mouse_context_menu;
35pub mod movement;
36mod persistence;
37mod proposed_changes_editor;
38mod rust_analyzer_ext;
39pub mod scroll;
40mod selections_collection;
41pub mod tasks;
42
43#[cfg(test)]
44mod code_completion_tests;
45#[cfg(test)]
46mod editor_tests;
47#[cfg(test)]
48mod inline_completion_tests;
49mod signature_help;
50#[cfg(any(test, feature = "test-support"))]
51pub mod test;
52
53pub(crate) use actions::*;
54pub use actions::{AcceptEditPrediction, OpenExcerpts, OpenExcerptsSplit};
55use aho_corasick::AhoCorasick;
56use anyhow::{Context as _, Result, anyhow};
57use blink_manager::BlinkManager;
58use buffer_diff::DiffHunkStatus;
59use client::{Collaborator, ParticipantIndex};
60use clock::{AGENT_REPLICA_ID, ReplicaId};
61use collections::{BTreeMap, HashMap, HashSet, VecDeque};
62use convert_case::{Case, Casing};
63use dap::TelemetrySpawnLocation;
64use display_map::*;
65pub use display_map::{ChunkRenderer, ChunkRendererContext, DisplayPoint, FoldPlaceholder};
66pub use editor_settings::{
67 CurrentLineHighlight, DocumentColorsRenderMode, EditorSettings, HideMouseMode,
68 ScrollBeyondLastLine, ScrollbarAxes, SearchSettings, ShowScrollbar,
69};
70use editor_settings::{GoToDefinitionFallback, Minimap as MinimapSettings};
71pub use editor_settings_controls::*;
72use element::{AcceptEditPredictionBinding, LineWithInvisibles, PositionMap, layout_line};
73pub use element::{
74 CursorLayout, EditorElement, HighlightedRange, HighlightedRangeLine, PointForPosition,
75};
76use futures::{
77 FutureExt, StreamExt as _,
78 future::{self, Shared, join},
79 stream::FuturesUnordered,
80};
81use fuzzy::{StringMatch, StringMatchCandidate};
82use lsp_colors::LspColorData;
83
84use ::git::blame::BlameEntry;
85use ::git::{Restore, blame::ParsedCommitMessage};
86use code_context_menus::{
87 AvailableCodeAction, CodeActionContents, CodeActionsItem, CodeActionsMenu, CodeContextMenu,
88 CompletionsMenu, ContextMenuOrigin,
89};
90use git::blame::{GitBlame, GlobalBlameRenderer};
91use gpui::{
92 Action, Animation, AnimationExt, AnyElement, App, AppContext, AsyncWindowContext,
93 AvailableSpace, Background, Bounds, ClickEvent, ClipboardEntry, ClipboardItem, Context,
94 DispatchPhase, Edges, Entity, EntityInputHandler, EventEmitter, FocusHandle, FocusOutEvent,
95 Focusable, FontId, FontWeight, Global, HighlightStyle, Hsla, KeyContext, Modifiers,
96 MouseButton, MouseDownEvent, PaintQuad, ParentElement, Pixels, Render, ScrollHandle,
97 SharedString, Size, Stateful, Styled, Subscription, Task, TextStyle, TextStyleRefinement,
98 UTF16Selection, UnderlineStyle, UniformListScrollHandle, WeakEntity, WeakFocusHandle, Window,
99 div, point, prelude::*, pulsating_between, px, relative, size,
100};
101use highlight_matching_bracket::refresh_matching_bracket_highlights;
102use hover_links::{HoverLink, HoveredLinkState, InlayHighlight, find_file};
103pub use hover_popover::hover_markdown_style;
104use hover_popover::{HoverState, hide_hover};
105use indent_guides::ActiveIndentGuidesState;
106use inlay_hint_cache::{InlayHintCache, InlaySplice, InvalidationStrategy};
107pub use inline_completion::Direction;
108use inline_completion::{EditPredictionProvider, InlineCompletionProviderHandle};
109pub use items::MAX_TAB_TITLE_LEN;
110use itertools::Itertools;
111use language::{
112 AutoindentMode, BracketMatch, BracketPair, Buffer, Capability, CharKind, CodeLabel,
113 CursorShape, DiagnosticEntry, DiffOptions, DocumentationConfig, EditPredictionsMode,
114 EditPreview, HighlightedText, IndentKind, IndentSize, Language, OffsetRangeExt, Point,
115 Selection, SelectionGoal, TextObject, TransactionId, TreeSitterOptions, WordsQuery,
116 language_settings::{
117 self, InlayHintSettings, LspInsertMode, RewrapBehavior, WordsCompletionMode,
118 all_language_settings, language_settings,
119 },
120 point_from_lsp, text_diff_with_options,
121};
122use language::{BufferRow, CharClassifier, Runnable, RunnableRange, point_to_lsp};
123use linked_editing_ranges::refresh_linked_ranges;
124use markdown::Markdown;
125use mouse_context_menu::MouseContextMenu;
126use persistence::DB;
127use project::{
128 BreakpointWithPosition, CompletionResponse, ProjectPath,
129 debugger::{
130 breakpoint_store::{
131 BreakpointEditAction, BreakpointSessionState, BreakpointState, BreakpointStore,
132 BreakpointStoreEvent,
133 },
134 session::{Session, SessionEvent},
135 },
136 git_store::{GitStoreEvent, RepositoryEvent},
137 project_settings::DiagnosticSeverity,
138};
139
140pub use git::blame::BlameRenderer;
141pub use proposed_changes_editor::{
142 ProposedChangeLocation, ProposedChangesEditor, ProposedChangesEditorToolbar,
143};
144use std::{cell::OnceCell, iter::Peekable, ops::Not};
145use task::{ResolvedTask, RunnableTag, TaskTemplate, TaskVariables};
146
147pub use lsp::CompletionContext;
148use lsp::{
149 CodeActionKind, CompletionItemKind, CompletionTriggerKind, InsertTextFormat, InsertTextMode,
150 LanguageServerId, LanguageServerName,
151};
152
153use language::BufferSnapshot;
154pub use lsp_ext::lsp_tasks;
155use movement::TextLayoutDetails;
156pub use multi_buffer::{
157 Anchor, AnchorRangeExt, ExcerptId, ExcerptRange, MultiBuffer, MultiBufferSnapshot, PathKey,
158 RowInfo, ToOffset, ToPoint,
159};
160use multi_buffer::{
161 ExcerptInfo, ExpandExcerptDirection, MultiBufferDiffHunk, MultiBufferPoint, MultiBufferRow,
162 MultiOrSingleBufferOffsetRange, ToOffsetUtf16,
163};
164use parking_lot::Mutex;
165use project::{
166 CodeAction, Completion, CompletionIntent, CompletionSource, DocumentHighlight, InlayHint,
167 Location, LocationLink, PrepareRenameResponse, Project, ProjectItem, ProjectTransaction,
168 TaskSourceKind,
169 debugger::breakpoint_store::Breakpoint,
170 lsp_store::{CompletionDocumentation, FormatTrigger, LspFormatTarget, OpenLspBufferHandle},
171 project_settings::{GitGutterSetting, ProjectSettings},
172};
173use rand::prelude::*;
174use rpc::{ErrorExt, proto::*};
175use scroll::{Autoscroll, OngoingScroll, ScrollAnchor, ScrollManager, ScrollbarAutoHide};
176use selections_collection::{
177 MutableSelectionsCollection, SelectionsCollection, resolve_selections,
178};
179use serde::{Deserialize, Serialize};
180use settings::{Settings, SettingsLocation, SettingsStore, update_settings_file};
181use smallvec::{SmallVec, smallvec};
182use snippet::Snippet;
183use std::sync::Arc;
184use std::{
185 any::TypeId,
186 borrow::Cow,
187 cell::RefCell,
188 cmp::{self, Ordering, Reverse},
189 mem,
190 num::NonZeroU32,
191 ops::{ControlFlow, Deref, DerefMut, Range, RangeInclusive},
192 path::{Path, PathBuf},
193 rc::Rc,
194 time::{Duration, Instant},
195};
196pub use sum_tree::Bias;
197use sum_tree::TreeMap;
198use text::{BufferId, FromAnchor, OffsetUtf16, Rope};
199use theme::{
200 ActiveTheme, PlayerColor, StatusColors, SyntaxTheme, Theme, ThemeSettings,
201 observe_buffer_font_size_adjustment,
202};
203use ui::{
204 ButtonSize, ButtonStyle, ContextMenu, Disclosure, IconButton, IconButtonShape, IconName,
205 IconSize, Indicator, Key, Tooltip, h_flex, prelude::*,
206};
207use util::{RangeExt, ResultExt, TryFutureExt, maybe, post_inc};
208use workspace::{
209 CollaboratorId, Item as WorkspaceItem, ItemId, ItemNavHistory, OpenInTerminal, OpenTerminal,
210 RestoreOnStartupBehavior, SERIALIZATION_THROTTLE_TIME, SplitDirection, TabBarSettings, Toast,
211 ViewId, Workspace, WorkspaceId, WorkspaceSettings,
212 item::{ItemHandle, PreviewTabsSettings, SaveOptions},
213 notifications::{DetachAndPromptErr, NotificationId, NotifyTaskExt},
214 searchable::SearchEvent,
215};
216
217use crate::{
218 code_context_menus::CompletionsMenuSource,
219 hover_links::{find_url, find_url_from_range},
220};
221use crate::{
222 editor_settings::MultiCursorModifier,
223 signature_help::{SignatureHelpHiddenBy, SignatureHelpState},
224};
225
226pub const FILE_HEADER_HEIGHT: u32 = 2;
227pub const MULTI_BUFFER_EXCERPT_HEADER_HEIGHT: u32 = 1;
228pub const DEFAULT_MULTIBUFFER_CONTEXT: u32 = 2;
229const CURSOR_BLINK_INTERVAL: Duration = Duration::from_millis(500);
230const MAX_LINE_LEN: usize = 1024;
231const MIN_NAVIGATION_HISTORY_ROW_DELTA: i64 = 10;
232const MAX_SELECTION_HISTORY_LEN: usize = 1024;
233pub(crate) const CURSORS_VISIBLE_FOR: Duration = Duration::from_millis(2000);
234#[doc(hidden)]
235pub const CODE_ACTIONS_DEBOUNCE_TIMEOUT: Duration = Duration::from_millis(250);
236const SELECTION_HIGHLIGHT_DEBOUNCE_TIMEOUT: Duration = Duration::from_millis(100);
237
238pub(crate) const CODE_ACTION_TIMEOUT: Duration = Duration::from_secs(5);
239pub(crate) const FORMAT_TIMEOUT: Duration = Duration::from_secs(5);
240pub(crate) const SCROLL_CENTER_TOP_BOTTOM_DEBOUNCE_TIMEOUT: Duration = Duration::from_secs(1);
241
242pub(crate) const EDIT_PREDICTION_KEY_CONTEXT: &str = "edit_prediction";
243pub(crate) const EDIT_PREDICTION_CONFLICT_KEY_CONTEXT: &str = "edit_prediction_conflict";
244pub(crate) const MINIMAP_FONT_SIZE: AbsoluteLength = AbsoluteLength::Pixels(px(2.));
245
246pub type RenderDiffHunkControlsFn = Arc<
247 dyn Fn(
248 u32,
249 &DiffHunkStatus,
250 Range<Anchor>,
251 bool,
252 Pixels,
253 &Entity<Editor>,
254 &mut Window,
255 &mut App,
256 ) -> AnyElement,
257>;
258
259struct InlineValueCache {
260 enabled: bool,
261 inlays: Vec<InlayId>,
262 refresh_task: Task<Option<()>>,
263}
264
265impl InlineValueCache {
266 fn new(enabled: bool) -> Self {
267 Self {
268 enabled,
269 inlays: Vec::new(),
270 refresh_task: Task::ready(None),
271 }
272 }
273}
274
275#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
276pub enum InlayId {
277 InlineCompletion(usize),
278 DebuggerValue(usize),
279 // LSP
280 Hint(usize),
281 Color(usize),
282}
283
284impl InlayId {
285 fn id(&self) -> usize {
286 match self {
287 Self::InlineCompletion(id) => *id,
288 Self::DebuggerValue(id) => *id,
289 Self::Hint(id) => *id,
290 Self::Color(id) => *id,
291 }
292 }
293}
294
295pub enum ActiveDebugLine {}
296pub enum DebugStackFrameLine {}
297enum DocumentHighlightRead {}
298enum DocumentHighlightWrite {}
299enum InputComposition {}
300pub enum PendingInput {}
301enum SelectedTextHighlight {}
302
303pub enum ConflictsOuter {}
304pub enum ConflictsOurs {}
305pub enum ConflictsTheirs {}
306pub enum ConflictsOursMarker {}
307pub enum ConflictsTheirsMarker {}
308
309#[derive(Debug, Copy, Clone, PartialEq, Eq)]
310pub enum Navigated {
311 Yes,
312 No,
313}
314
315impl Navigated {
316 pub fn from_bool(yes: bool) -> Navigated {
317 if yes { Navigated::Yes } else { Navigated::No }
318 }
319}
320
321#[derive(Debug, Clone, PartialEq, Eq)]
322enum DisplayDiffHunk {
323 Folded {
324 display_row: DisplayRow,
325 },
326 Unfolded {
327 is_created_file: bool,
328 diff_base_byte_range: Range<usize>,
329 display_row_range: Range<DisplayRow>,
330 multi_buffer_range: Range<Anchor>,
331 status: DiffHunkStatus,
332 },
333}
334
335pub enum HideMouseCursorOrigin {
336 TypingAction,
337 MovementAction,
338}
339
340pub fn init_settings(cx: &mut App) {
341 EditorSettings::register(cx);
342}
343
344pub fn init(cx: &mut App) {
345 init_settings(cx);
346
347 cx.set_global(GlobalBlameRenderer(Arc::new(())));
348
349 workspace::register_project_item::<Editor>(cx);
350 workspace::FollowableViewRegistry::register::<Editor>(cx);
351 workspace::register_serializable_item::<Editor>(cx);
352
353 cx.observe_new(
354 |workspace: &mut Workspace, _: Option<&mut Window>, _cx: &mut Context<Workspace>| {
355 workspace.register_action(Editor::new_file);
356 workspace.register_action(Editor::new_file_vertical);
357 workspace.register_action(Editor::new_file_horizontal);
358 workspace.register_action(Editor::cancel_language_server_work);
359 },
360 )
361 .detach();
362
363 cx.on_action(move |_: &workspace::NewFile, cx| {
364 let app_state = workspace::AppState::global(cx);
365 if let Some(app_state) = app_state.upgrade() {
366 workspace::open_new(
367 Default::default(),
368 app_state,
369 cx,
370 |workspace, window, cx| {
371 Editor::new_file(workspace, &Default::default(), window, cx)
372 },
373 )
374 .detach();
375 }
376 });
377 cx.on_action(move |_: &workspace::NewWindow, cx| {
378 let app_state = workspace::AppState::global(cx);
379 if let Some(app_state) = app_state.upgrade() {
380 workspace::open_new(
381 Default::default(),
382 app_state,
383 cx,
384 |workspace, window, cx| {
385 cx.activate(true);
386 Editor::new_file(workspace, &Default::default(), window, cx)
387 },
388 )
389 .detach();
390 }
391 });
392}
393
394pub fn set_blame_renderer(renderer: impl BlameRenderer + 'static, cx: &mut App) {
395 cx.set_global(GlobalBlameRenderer(Arc::new(renderer)));
396}
397
398pub trait DiagnosticRenderer {
399 fn render_group(
400 &self,
401 diagnostic_group: Vec<DiagnosticEntry<Point>>,
402 buffer_id: BufferId,
403 snapshot: EditorSnapshot,
404 editor: WeakEntity<Editor>,
405 cx: &mut App,
406 ) -> Vec<BlockProperties<Anchor>>;
407
408 fn render_hover(
409 &self,
410 diagnostic_group: Vec<DiagnosticEntry<Point>>,
411 range: Range<Point>,
412 buffer_id: BufferId,
413 cx: &mut App,
414 ) -> Option<Entity<markdown::Markdown>>;
415
416 fn open_link(
417 &self,
418 editor: &mut Editor,
419 link: SharedString,
420 window: &mut Window,
421 cx: &mut Context<Editor>,
422 );
423}
424
425pub(crate) struct GlobalDiagnosticRenderer(pub Arc<dyn DiagnosticRenderer>);
426
427impl GlobalDiagnosticRenderer {
428 fn global(cx: &App) -> Option<Arc<dyn DiagnosticRenderer>> {
429 cx.try_global::<Self>().map(|g| g.0.clone())
430 }
431}
432
433impl gpui::Global for GlobalDiagnosticRenderer {}
434pub fn set_diagnostic_renderer(renderer: impl DiagnosticRenderer + 'static, cx: &mut App) {
435 cx.set_global(GlobalDiagnosticRenderer(Arc::new(renderer)));
436}
437
438pub struct SearchWithinRange;
439
440trait InvalidationRegion {
441 fn ranges(&self) -> &[Range<Anchor>];
442}
443
444#[derive(Clone, Debug, PartialEq)]
445pub enum SelectPhase {
446 Begin {
447 position: DisplayPoint,
448 add: bool,
449 click_count: usize,
450 },
451 BeginColumnar {
452 position: DisplayPoint,
453 reset: bool,
454 mode: ColumnarMode,
455 goal_column: u32,
456 },
457 Extend {
458 position: DisplayPoint,
459 click_count: usize,
460 },
461 Update {
462 position: DisplayPoint,
463 goal_column: u32,
464 scroll_delta: gpui::Point<f32>,
465 },
466 End,
467}
468
469#[derive(Clone, Debug, PartialEq)]
470pub enum ColumnarMode {
471 FromMouse,
472 FromSelection,
473}
474
475#[derive(Clone, Debug)]
476pub enum SelectMode {
477 Character,
478 Word(Range<Anchor>),
479 Line(Range<Anchor>),
480 All,
481}
482
483#[derive(Clone, PartialEq, Eq, Debug)]
484pub enum EditorMode {
485 SingleLine {
486 auto_width: bool,
487 },
488 AutoHeight {
489 min_lines: usize,
490 max_lines: Option<usize>,
491 },
492 Full {
493 /// When set to `true`, the editor will scale its UI elements with the buffer font size.
494 scale_ui_elements_with_buffer_font_size: bool,
495 /// When set to `true`, the editor will render a background for the active line.
496 show_active_line_background: bool,
497 /// When set to `true`, the editor's height will be determined by its content.
498 sized_by_content: bool,
499 },
500 Minimap {
501 parent: WeakEntity<Editor>,
502 },
503}
504
505impl EditorMode {
506 pub fn full() -> Self {
507 Self::Full {
508 scale_ui_elements_with_buffer_font_size: true,
509 show_active_line_background: true,
510 sized_by_content: false,
511 }
512 }
513
514 #[inline]
515 pub fn is_full(&self) -> bool {
516 matches!(self, Self::Full { .. })
517 }
518
519 #[inline]
520 pub fn is_single_line(&self) -> bool {
521 matches!(self, Self::SingleLine { .. })
522 }
523
524 #[inline]
525 fn is_minimap(&self) -> bool {
526 matches!(self, Self::Minimap { .. })
527 }
528}
529
530#[derive(Copy, Clone, Debug)]
531pub enum SoftWrap {
532 /// Prefer not to wrap at all.
533 ///
534 /// Note: this is currently internal, as actually limited by [`crate::MAX_LINE_LEN`] until it wraps.
535 /// The mode is used inside git diff hunks, where it's seems currently more useful to not wrap as much as possible.
536 GitDiff,
537 /// Prefer a single line generally, unless an overly long line is encountered.
538 None,
539 /// Soft wrap lines that exceed the editor width.
540 EditorWidth,
541 /// Soft wrap lines at the preferred line length.
542 Column(u32),
543 /// Soft wrap line at the preferred line length or the editor width (whichever is smaller).
544 Bounded(u32),
545}
546
547#[derive(Clone)]
548pub struct EditorStyle {
549 pub background: Hsla,
550 pub border: Hsla,
551 pub local_player: PlayerColor,
552 pub text: TextStyle,
553 pub scrollbar_width: Pixels,
554 pub syntax: Arc<SyntaxTheme>,
555 pub status: StatusColors,
556 pub inlay_hints_style: HighlightStyle,
557 pub inline_completion_styles: InlineCompletionStyles,
558 pub unnecessary_code_fade: f32,
559 pub show_underlines: bool,
560}
561
562impl Default for EditorStyle {
563 fn default() -> Self {
564 Self {
565 background: Hsla::default(),
566 border: Hsla::default(),
567 local_player: PlayerColor::default(),
568 text: TextStyle::default(),
569 scrollbar_width: Pixels::default(),
570 syntax: Default::default(),
571 // HACK: Status colors don't have a real default.
572 // We should look into removing the status colors from the editor
573 // style and retrieve them directly from the theme.
574 status: StatusColors::dark(),
575 inlay_hints_style: HighlightStyle::default(),
576 inline_completion_styles: InlineCompletionStyles {
577 insertion: HighlightStyle::default(),
578 whitespace: HighlightStyle::default(),
579 },
580 unnecessary_code_fade: Default::default(),
581 show_underlines: true,
582 }
583 }
584}
585
586pub fn make_inlay_hints_style(cx: &mut App) -> HighlightStyle {
587 let show_background = language_settings::language_settings(None, None, cx)
588 .inlay_hints
589 .show_background;
590
591 HighlightStyle {
592 color: Some(cx.theme().status().hint),
593 background_color: show_background.then(|| cx.theme().status().hint_background),
594 ..HighlightStyle::default()
595 }
596}
597
598pub fn make_suggestion_styles(cx: &mut App) -> InlineCompletionStyles {
599 InlineCompletionStyles {
600 insertion: HighlightStyle {
601 color: Some(cx.theme().status().predictive),
602 ..HighlightStyle::default()
603 },
604 whitespace: HighlightStyle {
605 background_color: Some(cx.theme().status().created_background),
606 ..HighlightStyle::default()
607 },
608 }
609}
610
611type CompletionId = usize;
612
613pub(crate) enum EditDisplayMode {
614 TabAccept,
615 DiffPopover,
616 Inline,
617}
618
619enum InlineCompletion {
620 Edit {
621 edits: Vec<(Range<Anchor>, String)>,
622 edit_preview: Option<EditPreview>,
623 display_mode: EditDisplayMode,
624 snapshot: BufferSnapshot,
625 },
626 Move {
627 target: Anchor,
628 snapshot: BufferSnapshot,
629 },
630}
631
632struct InlineCompletionState {
633 inlay_ids: Vec<InlayId>,
634 completion: InlineCompletion,
635 completion_id: Option<SharedString>,
636 invalidation_range: Range<Anchor>,
637}
638
639enum EditPredictionSettings {
640 Disabled,
641 Enabled {
642 show_in_menu: bool,
643 preview_requires_modifier: bool,
644 },
645}
646
647enum InlineCompletionHighlight {}
648
649#[derive(Debug, Clone)]
650struct InlineDiagnostic {
651 message: SharedString,
652 group_id: usize,
653 is_primary: bool,
654 start: Point,
655 severity: lsp::DiagnosticSeverity,
656}
657
658pub enum MenuInlineCompletionsPolicy {
659 Never,
660 ByProvider,
661}
662
663pub enum EditPredictionPreview {
664 /// Modifier is not pressed
665 Inactive { released_too_fast: bool },
666 /// Modifier pressed
667 Active {
668 since: Instant,
669 previous_scroll_position: Option<ScrollAnchor>,
670 },
671}
672
673impl EditPredictionPreview {
674 pub fn released_too_fast(&self) -> bool {
675 match self {
676 EditPredictionPreview::Inactive { released_too_fast } => *released_too_fast,
677 EditPredictionPreview::Active { .. } => false,
678 }
679 }
680
681 pub fn set_previous_scroll_position(&mut self, scroll_position: Option<ScrollAnchor>) {
682 if let EditPredictionPreview::Active {
683 previous_scroll_position,
684 ..
685 } = self
686 {
687 *previous_scroll_position = scroll_position;
688 }
689 }
690}
691
692pub struct ContextMenuOptions {
693 pub min_entries_visible: usize,
694 pub max_entries_visible: usize,
695 pub placement: Option<ContextMenuPlacement>,
696}
697
698#[derive(Debug, Clone, PartialEq, Eq)]
699pub enum ContextMenuPlacement {
700 Above,
701 Below,
702}
703
704#[derive(Copy, Clone, Eq, PartialEq, PartialOrd, Ord, Debug, Default)]
705struct EditorActionId(usize);
706
707impl EditorActionId {
708 pub fn post_inc(&mut self) -> Self {
709 let answer = self.0;
710
711 *self = Self(answer + 1);
712
713 Self(answer)
714 }
715}
716
717// type GetFieldEditorTheme = dyn Fn(&theme::Theme) -> theme::FieldEditor;
718// type OverrideTextStyle = dyn Fn(&EditorStyle) -> Option<HighlightStyle>;
719
720type BackgroundHighlight = (fn(&Theme) -> Hsla, Arc<[Range<Anchor>]>);
721type GutterHighlight = (fn(&App) -> Hsla, Vec<Range<Anchor>>);
722
723#[derive(Default)]
724struct ScrollbarMarkerState {
725 scrollbar_size: Size<Pixels>,
726 dirty: bool,
727 markers: Arc<[PaintQuad]>,
728 pending_refresh: Option<Task<Result<()>>>,
729}
730
731impl ScrollbarMarkerState {
732 fn should_refresh(&self, scrollbar_size: Size<Pixels>) -> bool {
733 self.pending_refresh.is_none() && (self.scrollbar_size != scrollbar_size || self.dirty)
734 }
735}
736
737#[derive(Clone, Copy, PartialEq, Eq)]
738pub enum MinimapVisibility {
739 Disabled,
740 Enabled {
741 /// The configuration currently present in the users settings.
742 setting_configuration: bool,
743 /// Whether to override the currently set visibility from the users setting.
744 toggle_override: bool,
745 },
746}
747
748impl MinimapVisibility {
749 fn for_mode(mode: &EditorMode, cx: &App) -> Self {
750 if mode.is_full() {
751 Self::Enabled {
752 setting_configuration: EditorSettings::get_global(cx).minimap.minimap_enabled(),
753 toggle_override: false,
754 }
755 } else {
756 Self::Disabled
757 }
758 }
759
760 fn hidden(&self) -> Self {
761 match *self {
762 Self::Enabled {
763 setting_configuration,
764 ..
765 } => Self::Enabled {
766 setting_configuration,
767 toggle_override: setting_configuration,
768 },
769 Self::Disabled => Self::Disabled,
770 }
771 }
772
773 fn disabled(&self) -> bool {
774 match *self {
775 Self::Disabled => true,
776 _ => false,
777 }
778 }
779
780 fn settings_visibility(&self) -> bool {
781 match *self {
782 Self::Enabled {
783 setting_configuration,
784 ..
785 } => setting_configuration,
786 _ => false,
787 }
788 }
789
790 fn visible(&self) -> bool {
791 match *self {
792 Self::Enabled {
793 setting_configuration,
794 toggle_override,
795 } => setting_configuration ^ toggle_override,
796 _ => false,
797 }
798 }
799
800 fn toggle_visibility(&self) -> Self {
801 match *self {
802 Self::Enabled {
803 toggle_override,
804 setting_configuration,
805 } => Self::Enabled {
806 setting_configuration,
807 toggle_override: !toggle_override,
808 },
809 Self::Disabled => Self::Disabled,
810 }
811 }
812}
813
814#[derive(Clone, Debug)]
815struct RunnableTasks {
816 templates: Vec<(TaskSourceKind, TaskTemplate)>,
817 offset: multi_buffer::Anchor,
818 // We need the column at which the task context evaluation should take place (when we're spawning it via gutter).
819 column: u32,
820 // Values of all named captures, including those starting with '_'
821 extra_variables: HashMap<String, String>,
822 // Full range of the tagged region. We use it to determine which `extra_variables` to grab for context resolution in e.g. a modal.
823 context_range: Range<BufferOffset>,
824}
825
826impl RunnableTasks {
827 fn resolve<'a>(
828 &'a self,
829 cx: &'a task::TaskContext,
830 ) -> impl Iterator<Item = (TaskSourceKind, ResolvedTask)> + 'a {
831 self.templates.iter().filter_map(|(kind, template)| {
832 template
833 .resolve_task(&kind.to_id_base(), cx)
834 .map(|task| (kind.clone(), task))
835 })
836 }
837}
838
839#[derive(Clone)]
840pub struct ResolvedTasks {
841 templates: SmallVec<[(TaskSourceKind, ResolvedTask); 1]>,
842 position: Anchor,
843}
844
845#[derive(Copy, Clone, Debug, PartialEq, PartialOrd)]
846struct BufferOffset(usize);
847
848// Addons allow storing per-editor state in other crates (e.g. Vim)
849pub trait Addon: 'static {
850 fn extend_key_context(&self, _: &mut KeyContext, _: &App) {}
851
852 fn render_buffer_header_controls(
853 &self,
854 _: &ExcerptInfo,
855 _: &Window,
856 _: &App,
857 ) -> Option<AnyElement> {
858 None
859 }
860
861 fn to_any(&self) -> &dyn std::any::Any;
862
863 fn to_any_mut(&mut self) -> Option<&mut dyn std::any::Any> {
864 None
865 }
866}
867
868struct ChangeLocation {
869 current: Option<Vec<Anchor>>,
870 original: Vec<Anchor>,
871}
872impl ChangeLocation {
873 fn locations(&self) -> &[Anchor] {
874 self.current.as_ref().unwrap_or(&self.original)
875 }
876}
877
878/// A set of caret positions, registered when the editor was edited.
879pub struct ChangeList {
880 changes: Vec<ChangeLocation>,
881 /// Currently "selected" change.
882 position: Option<usize>,
883}
884
885impl ChangeList {
886 pub fn new() -> Self {
887 Self {
888 changes: Vec::new(),
889 position: None,
890 }
891 }
892
893 /// Moves to the next change in the list (based on the direction given) and returns the caret positions for the next change.
894 /// If reaches the end of the list in the direction, returns the corresponding change until called for a different direction.
895 pub fn next_change(&mut self, count: usize, direction: Direction) -> Option<&[Anchor]> {
896 if self.changes.is_empty() {
897 return None;
898 }
899
900 let prev = self.position.unwrap_or(self.changes.len());
901 let next = if direction == Direction::Prev {
902 prev.saturating_sub(count)
903 } else {
904 (prev + count).min(self.changes.len() - 1)
905 };
906 self.position = Some(next);
907 self.changes.get(next).map(|change| change.locations())
908 }
909
910 /// Adds a new change to the list, resetting the change list position.
911 pub fn push_to_change_list(&mut self, group: bool, new_positions: Vec<Anchor>) {
912 self.position.take();
913 if let Some(last) = self.changes.last_mut()
914 && group
915 {
916 last.current = Some(new_positions)
917 } else {
918 self.changes.push(ChangeLocation {
919 original: new_positions,
920 current: None,
921 });
922 }
923 }
924
925 pub fn last(&self) -> Option<&[Anchor]> {
926 self.changes.last().map(|change| change.locations())
927 }
928
929 pub fn last_before_grouping(&self) -> Option<&[Anchor]> {
930 self.changes.last().map(|change| change.original.as_slice())
931 }
932
933 pub fn invert_last_group(&mut self) {
934 if let Some(last) = self.changes.last_mut() {
935 if let Some(current) = last.current.as_mut() {
936 mem::swap(&mut last.original, current);
937 }
938 }
939 }
940}
941
942#[derive(Clone)]
943struct InlineBlamePopoverState {
944 scroll_handle: ScrollHandle,
945 commit_message: Option<ParsedCommitMessage>,
946 markdown: Entity<Markdown>,
947}
948
949struct InlineBlamePopover {
950 position: gpui::Point<Pixels>,
951 hide_task: Option<Task<()>>,
952 popover_bounds: Option<Bounds<Pixels>>,
953 popover_state: InlineBlamePopoverState,
954}
955
956enum SelectionDragState {
957 /// State when no drag related activity is detected.
958 None,
959 /// State when the mouse is down on a selection that is about to be dragged.
960 ReadyToDrag {
961 selection: Selection<Anchor>,
962 click_position: gpui::Point<Pixels>,
963 mouse_down_time: Instant,
964 },
965 /// State when the mouse is dragging the selection in the editor.
966 Dragging {
967 selection: Selection<Anchor>,
968 drop_cursor: Selection<Anchor>,
969 hide_drop_cursor: bool,
970 },
971}
972
973enum ColumnarSelectionState {
974 FromMouse {
975 selection_tail: Anchor,
976 display_point: Option<DisplayPoint>,
977 },
978 FromSelection {
979 selection_tail: Anchor,
980 },
981}
982
983/// Represents a breakpoint indicator that shows up when hovering over lines in the gutter that don't have
984/// a breakpoint on them.
985#[derive(Clone, Copy, Debug, PartialEq, Eq)]
986struct PhantomBreakpointIndicator {
987 display_row: DisplayRow,
988 /// There's a small debounce between hovering over the line and showing the indicator.
989 /// We don't want to show the indicator when moving the mouse from editor to e.g. project panel.
990 is_active: bool,
991 collides_with_existing_breakpoint: bool,
992}
993
994/// Zed's primary implementation of text input, allowing users to edit a [`MultiBuffer`].
995///
996/// See the [module level documentation](self) for more information.
997pub struct Editor {
998 focus_handle: FocusHandle,
999 last_focused_descendant: Option<WeakFocusHandle>,
1000 /// The text buffer being edited
1001 buffer: Entity<MultiBuffer>,
1002 /// Map of how text in the buffer should be displayed.
1003 /// Handles soft wraps, folds, fake inlay text insertions, etc.
1004 pub display_map: Entity<DisplayMap>,
1005 pub selections: SelectionsCollection,
1006 pub scroll_manager: ScrollManager,
1007 /// When inline assist editors are linked, they all render cursors because
1008 /// typing enters text into each of them, even the ones that aren't focused.
1009 pub(crate) show_cursor_when_unfocused: bool,
1010 columnar_selection_state: Option<ColumnarSelectionState>,
1011 add_selections_state: Option<AddSelectionsState>,
1012 select_next_state: Option<SelectNextState>,
1013 select_prev_state: Option<SelectNextState>,
1014 selection_history: SelectionHistory,
1015 defer_selection_effects: bool,
1016 deferred_selection_effects_state: Option<DeferredSelectionEffectsState>,
1017 autoclose_regions: Vec<AutocloseRegion>,
1018 snippet_stack: InvalidationStack<SnippetState>,
1019 select_syntax_node_history: SelectSyntaxNodeHistory,
1020 ime_transaction: Option<TransactionId>,
1021 pub diagnostics_max_severity: DiagnosticSeverity,
1022 active_diagnostics: ActiveDiagnostic,
1023 show_inline_diagnostics: bool,
1024 inline_diagnostics_update: Task<()>,
1025 inline_diagnostics_enabled: bool,
1026 diagnostics_enabled: bool,
1027 inline_diagnostics: Vec<(Anchor, InlineDiagnostic)>,
1028 soft_wrap_mode_override: Option<language_settings::SoftWrap>,
1029 hard_wrap: Option<usize>,
1030
1031 // TODO: make this a access method
1032 pub project: Option<Entity<Project>>,
1033 semantics_provider: Option<Rc<dyn SemanticsProvider>>,
1034 completion_provider: Option<Rc<dyn CompletionProvider>>,
1035 collaboration_hub: Option<Box<dyn CollaborationHub>>,
1036 blink_manager: Entity<BlinkManager>,
1037 show_cursor_names: bool,
1038 hovered_cursors: HashMap<HoveredCursor, Task<()>>,
1039 pub show_local_selections: bool,
1040 mode: EditorMode,
1041 show_breadcrumbs: bool,
1042 show_gutter: bool,
1043 show_scrollbars: ScrollbarAxes,
1044 minimap_visibility: MinimapVisibility,
1045 offset_content: bool,
1046 disable_expand_excerpt_buttons: bool,
1047 show_line_numbers: Option<bool>,
1048 use_relative_line_numbers: Option<bool>,
1049 show_git_diff_gutter: Option<bool>,
1050 show_code_actions: Option<bool>,
1051 show_runnables: Option<bool>,
1052 show_breakpoints: Option<bool>,
1053 show_wrap_guides: Option<bool>,
1054 show_indent_guides: Option<bool>,
1055 placeholder_text: Option<Arc<str>>,
1056 highlight_order: usize,
1057 highlighted_rows: HashMap<TypeId, Vec<RowHighlight>>,
1058 background_highlights: TreeMap<HighlightKey, BackgroundHighlight>,
1059 gutter_highlights: TreeMap<TypeId, GutterHighlight>,
1060 scrollbar_marker_state: ScrollbarMarkerState,
1061 active_indent_guides_state: ActiveIndentGuidesState,
1062 nav_history: Option<ItemNavHistory>,
1063 context_menu: RefCell<Option<CodeContextMenu>>,
1064 context_menu_options: Option<ContextMenuOptions>,
1065 mouse_context_menu: Option<MouseContextMenu>,
1066 completion_tasks: Vec<(CompletionId, Task<()>)>,
1067 inline_blame_popover: Option<InlineBlamePopover>,
1068 inline_blame_popover_show_task: Option<Task<()>>,
1069 signature_help_state: SignatureHelpState,
1070 auto_signature_help: Option<bool>,
1071 find_all_references_task_sources: Vec<Anchor>,
1072 next_completion_id: CompletionId,
1073 available_code_actions: Option<(Location, Rc<[AvailableCodeAction]>)>,
1074 code_actions_task: Option<Task<Result<()>>>,
1075 quick_selection_highlight_task: Option<(Range<Anchor>, Task<()>)>,
1076 debounced_selection_highlight_task: Option<(Range<Anchor>, Task<()>)>,
1077 document_highlights_task: Option<Task<()>>,
1078 linked_editing_range_task: Option<Task<Option<()>>>,
1079 linked_edit_ranges: linked_editing_ranges::LinkedEditingRanges,
1080 pending_rename: Option<RenameState>,
1081 searchable: bool,
1082 cursor_shape: CursorShape,
1083 current_line_highlight: Option<CurrentLineHighlight>,
1084 collapse_matches: bool,
1085 autoindent_mode: Option<AutoindentMode>,
1086 workspace: Option<(WeakEntity<Workspace>, Option<WorkspaceId>)>,
1087 input_enabled: bool,
1088 use_modal_editing: bool,
1089 read_only: bool,
1090 leader_id: Option<CollaboratorId>,
1091 remote_id: Option<ViewId>,
1092 pub hover_state: HoverState,
1093 pending_mouse_down: Option<Rc<RefCell<Option<MouseDownEvent>>>>,
1094 gutter_hovered: bool,
1095 hovered_link_state: Option<HoveredLinkState>,
1096 edit_prediction_provider: Option<RegisteredInlineCompletionProvider>,
1097 code_action_providers: Vec<Rc<dyn CodeActionProvider>>,
1098 active_inline_completion: Option<InlineCompletionState>,
1099 /// Used to prevent flickering as the user types while the menu is open
1100 stale_inline_completion_in_menu: Option<InlineCompletionState>,
1101 edit_prediction_settings: EditPredictionSettings,
1102 inline_completions_hidden_for_vim_mode: bool,
1103 show_inline_completions_override: Option<bool>,
1104 menu_inline_completions_policy: MenuInlineCompletionsPolicy,
1105 edit_prediction_preview: EditPredictionPreview,
1106 edit_prediction_indent_conflict: bool,
1107 edit_prediction_requires_modifier_in_indent_conflict: bool,
1108 inlay_hint_cache: InlayHintCache,
1109 next_inlay_id: usize,
1110 _subscriptions: Vec<Subscription>,
1111 pixel_position_of_newest_cursor: Option<gpui::Point<Pixels>>,
1112 gutter_dimensions: GutterDimensions,
1113 style: Option<EditorStyle>,
1114 text_style_refinement: Option<TextStyleRefinement>,
1115 next_editor_action_id: EditorActionId,
1116 editor_actions: Rc<
1117 RefCell<BTreeMap<EditorActionId, Box<dyn Fn(&Editor, &mut Window, &mut Context<Self>)>>>,
1118 >,
1119 use_autoclose: bool,
1120 use_auto_surround: bool,
1121 auto_replace_emoji_shortcode: bool,
1122 jsx_tag_auto_close_enabled_in_any_buffer: bool,
1123 show_git_blame_gutter: bool,
1124 show_git_blame_inline: bool,
1125 show_git_blame_inline_delay_task: Option<Task<()>>,
1126 git_blame_inline_enabled: bool,
1127 render_diff_hunk_controls: RenderDiffHunkControlsFn,
1128 serialize_dirty_buffers: bool,
1129 show_selection_menu: Option<bool>,
1130 blame: Option<Entity<GitBlame>>,
1131 blame_subscription: Option<Subscription>,
1132 custom_context_menu: Option<
1133 Box<
1134 dyn 'static
1135 + Fn(
1136 &mut Self,
1137 DisplayPoint,
1138 &mut Window,
1139 &mut Context<Self>,
1140 ) -> Option<Entity<ui::ContextMenu>>,
1141 >,
1142 >,
1143 last_bounds: Option<Bounds<Pixels>>,
1144 last_position_map: Option<Rc<PositionMap>>,
1145 expect_bounds_change: Option<Bounds<Pixels>>,
1146 tasks: BTreeMap<(BufferId, BufferRow), RunnableTasks>,
1147 tasks_update_task: Option<Task<()>>,
1148 breakpoint_store: Option<Entity<BreakpointStore>>,
1149 gutter_breakpoint_indicator: (Option<PhantomBreakpointIndicator>, Option<Task<()>>),
1150 hovered_diff_hunk_row: Option<DisplayRow>,
1151 pull_diagnostics_task: Task<()>,
1152 in_project_search: bool,
1153 previous_search_ranges: Option<Arc<[Range<Anchor>]>>,
1154 breadcrumb_header: Option<String>,
1155 focused_block: Option<FocusedBlock>,
1156 next_scroll_position: NextScrollCursorCenterTopBottom,
1157 addons: HashMap<TypeId, Box<dyn Addon>>,
1158 registered_buffers: HashMap<BufferId, OpenLspBufferHandle>,
1159 load_diff_task: Option<Shared<Task<()>>>,
1160 /// Whether we are temporarily displaying a diff other than git's
1161 temporary_diff_override: bool,
1162 selection_mark_mode: bool,
1163 toggle_fold_multiple_buffers: Task<()>,
1164 _scroll_cursor_center_top_bottom_task: Task<()>,
1165 serialize_selections: Task<()>,
1166 serialize_folds: Task<()>,
1167 mouse_cursor_hidden: bool,
1168 minimap: Option<Entity<Self>>,
1169 hide_mouse_mode: HideMouseMode,
1170 pub change_list: ChangeList,
1171 inline_value_cache: InlineValueCache,
1172 selection_drag_state: SelectionDragState,
1173 next_color_inlay_id: usize,
1174 colors: Option<LspColorData>,
1175 folding_newlines: Task<()>,
1176}
1177
1178#[derive(Copy, Clone, Debug, PartialEq, Eq, Default)]
1179enum NextScrollCursorCenterTopBottom {
1180 #[default]
1181 Center,
1182 Top,
1183 Bottom,
1184}
1185
1186impl NextScrollCursorCenterTopBottom {
1187 fn next(&self) -> Self {
1188 match self {
1189 Self::Center => Self::Top,
1190 Self::Top => Self::Bottom,
1191 Self::Bottom => Self::Center,
1192 }
1193 }
1194}
1195
1196#[derive(Clone)]
1197pub struct EditorSnapshot {
1198 pub mode: EditorMode,
1199 show_gutter: bool,
1200 show_line_numbers: Option<bool>,
1201 show_git_diff_gutter: Option<bool>,
1202 show_code_actions: Option<bool>,
1203 show_runnables: Option<bool>,
1204 show_breakpoints: Option<bool>,
1205 git_blame_gutter_max_author_length: Option<usize>,
1206 pub display_snapshot: DisplaySnapshot,
1207 pub placeholder_text: Option<Arc<str>>,
1208 is_focused: bool,
1209 scroll_anchor: ScrollAnchor,
1210 ongoing_scroll: OngoingScroll,
1211 current_line_highlight: CurrentLineHighlight,
1212 gutter_hovered: bool,
1213}
1214
1215#[derive(Default, Debug, Clone, Copy)]
1216pub struct GutterDimensions {
1217 pub left_padding: Pixels,
1218 pub right_padding: Pixels,
1219 pub width: Pixels,
1220 pub margin: Pixels,
1221 pub git_blame_entries_width: Option<Pixels>,
1222}
1223
1224impl GutterDimensions {
1225 fn default_with_margin(font_id: FontId, font_size: Pixels, cx: &App) -> Self {
1226 Self {
1227 margin: Self::default_gutter_margin(font_id, font_size, cx),
1228 ..Default::default()
1229 }
1230 }
1231
1232 fn default_gutter_margin(font_id: FontId, font_size: Pixels, cx: &App) -> Pixels {
1233 -cx.text_system().descent(font_id, font_size)
1234 }
1235 /// The full width of the space taken up by the gutter.
1236 pub fn full_width(&self) -> Pixels {
1237 self.margin + self.width
1238 }
1239
1240 /// The width of the space reserved for the fold indicators,
1241 /// use alongside 'justify_end' and `gutter_width` to
1242 /// right align content with the line numbers
1243 pub fn fold_area_width(&self) -> Pixels {
1244 self.margin + self.right_padding
1245 }
1246}
1247
1248struct CharacterDimensions {
1249 em_width: Pixels,
1250 em_advance: Pixels,
1251 line_height: Pixels,
1252}
1253
1254#[derive(Debug)]
1255pub struct RemoteSelection {
1256 pub replica_id: ReplicaId,
1257 pub selection: Selection<Anchor>,
1258 pub cursor_shape: CursorShape,
1259 pub collaborator_id: CollaboratorId,
1260 pub line_mode: bool,
1261 pub user_name: Option<SharedString>,
1262 pub color: PlayerColor,
1263}
1264
1265#[derive(Clone, Debug)]
1266struct SelectionHistoryEntry {
1267 selections: Arc<[Selection<Anchor>]>,
1268 select_next_state: Option<SelectNextState>,
1269 select_prev_state: Option<SelectNextState>,
1270 add_selections_state: Option<AddSelectionsState>,
1271}
1272
1273#[derive(Copy, Clone, Debug, PartialEq, Eq)]
1274enum SelectionHistoryMode {
1275 Normal,
1276 Undoing,
1277 Redoing,
1278 Skipping,
1279}
1280
1281#[derive(Clone, PartialEq, Eq, Hash)]
1282struct HoveredCursor {
1283 replica_id: u16,
1284 selection_id: usize,
1285}
1286
1287impl Default for SelectionHistoryMode {
1288 fn default() -> Self {
1289 Self::Normal
1290 }
1291}
1292
1293#[derive(Debug)]
1294/// SelectionEffects controls the side-effects of updating the selection.
1295///
1296/// The default behaviour does "what you mostly want":
1297/// - it pushes to the nav history if the cursor moved by >10 lines
1298/// - it re-triggers completion requests
1299/// - it scrolls to fit
1300///
1301/// You might want to modify these behaviours. For example when doing a "jump"
1302/// like go to definition, we always want to add to nav history; but when scrolling
1303/// in vim mode we never do.
1304///
1305/// Similarly, you might want to disable scrolling if you don't want the viewport to
1306/// move.
1307pub struct SelectionEffects {
1308 nav_history: Option<bool>,
1309 completions: bool,
1310 scroll: Option<Autoscroll>,
1311}
1312
1313impl Default for SelectionEffects {
1314 fn default() -> Self {
1315 Self {
1316 nav_history: None,
1317 completions: true,
1318 scroll: Some(Autoscroll::fit()),
1319 }
1320 }
1321}
1322impl SelectionEffects {
1323 pub fn scroll(scroll: Autoscroll) -> Self {
1324 Self {
1325 scroll: Some(scroll),
1326 ..Default::default()
1327 }
1328 }
1329
1330 pub fn no_scroll() -> Self {
1331 Self {
1332 scroll: None,
1333 ..Default::default()
1334 }
1335 }
1336
1337 pub fn completions(self, completions: bool) -> Self {
1338 Self {
1339 completions,
1340 ..self
1341 }
1342 }
1343
1344 pub fn nav_history(self, nav_history: bool) -> Self {
1345 Self {
1346 nav_history: Some(nav_history),
1347 ..self
1348 }
1349 }
1350}
1351
1352struct DeferredSelectionEffectsState {
1353 changed: bool,
1354 effects: SelectionEffects,
1355 old_cursor_position: Anchor,
1356 history_entry: SelectionHistoryEntry,
1357}
1358
1359#[derive(Default)]
1360struct SelectionHistory {
1361 #[allow(clippy::type_complexity)]
1362 selections_by_transaction:
1363 HashMap<TransactionId, (Arc<[Selection<Anchor>]>, Option<Arc<[Selection<Anchor>]>>)>,
1364 mode: SelectionHistoryMode,
1365 undo_stack: VecDeque<SelectionHistoryEntry>,
1366 redo_stack: VecDeque<SelectionHistoryEntry>,
1367}
1368
1369impl SelectionHistory {
1370 #[track_caller]
1371 fn insert_transaction(
1372 &mut self,
1373 transaction_id: TransactionId,
1374 selections: Arc<[Selection<Anchor>]>,
1375 ) {
1376 if selections.is_empty() {
1377 log::error!(
1378 "SelectionHistory::insert_transaction called with empty selections. Caller: {}",
1379 std::panic::Location::caller()
1380 );
1381 return;
1382 }
1383 self.selections_by_transaction
1384 .insert(transaction_id, (selections, None));
1385 }
1386
1387 #[allow(clippy::type_complexity)]
1388 fn transaction(
1389 &self,
1390 transaction_id: TransactionId,
1391 ) -> Option<&(Arc<[Selection<Anchor>]>, Option<Arc<[Selection<Anchor>]>>)> {
1392 self.selections_by_transaction.get(&transaction_id)
1393 }
1394
1395 #[allow(clippy::type_complexity)]
1396 fn transaction_mut(
1397 &mut self,
1398 transaction_id: TransactionId,
1399 ) -> Option<&mut (Arc<[Selection<Anchor>]>, Option<Arc<[Selection<Anchor>]>>)> {
1400 self.selections_by_transaction.get_mut(&transaction_id)
1401 }
1402
1403 fn push(&mut self, entry: SelectionHistoryEntry) {
1404 if !entry.selections.is_empty() {
1405 match self.mode {
1406 SelectionHistoryMode::Normal => {
1407 self.push_undo(entry);
1408 self.redo_stack.clear();
1409 }
1410 SelectionHistoryMode::Undoing => self.push_redo(entry),
1411 SelectionHistoryMode::Redoing => self.push_undo(entry),
1412 SelectionHistoryMode::Skipping => {}
1413 }
1414 }
1415 }
1416
1417 fn push_undo(&mut self, entry: SelectionHistoryEntry) {
1418 if self
1419 .undo_stack
1420 .back()
1421 .map_or(true, |e| e.selections != entry.selections)
1422 {
1423 self.undo_stack.push_back(entry);
1424 if self.undo_stack.len() > MAX_SELECTION_HISTORY_LEN {
1425 self.undo_stack.pop_front();
1426 }
1427 }
1428 }
1429
1430 fn push_redo(&mut self, entry: SelectionHistoryEntry) {
1431 if self
1432 .redo_stack
1433 .back()
1434 .map_or(true, |e| e.selections != entry.selections)
1435 {
1436 self.redo_stack.push_back(entry);
1437 if self.redo_stack.len() > MAX_SELECTION_HISTORY_LEN {
1438 self.redo_stack.pop_front();
1439 }
1440 }
1441 }
1442}
1443
1444#[derive(Clone, Copy)]
1445pub struct RowHighlightOptions {
1446 pub autoscroll: bool,
1447 pub include_gutter: bool,
1448}
1449
1450impl Default for RowHighlightOptions {
1451 fn default() -> Self {
1452 Self {
1453 autoscroll: Default::default(),
1454 include_gutter: true,
1455 }
1456 }
1457}
1458
1459struct RowHighlight {
1460 index: usize,
1461 range: Range<Anchor>,
1462 color: Hsla,
1463 options: RowHighlightOptions,
1464 type_id: TypeId,
1465}
1466
1467#[derive(Clone, Debug)]
1468struct AddSelectionsState {
1469 groups: Vec<AddSelectionsGroup>,
1470}
1471
1472#[derive(Clone, Debug)]
1473struct AddSelectionsGroup {
1474 above: bool,
1475 stack: Vec<usize>,
1476}
1477
1478#[derive(Clone)]
1479struct SelectNextState {
1480 query: AhoCorasick,
1481 wordwise: bool,
1482 done: bool,
1483}
1484
1485impl std::fmt::Debug for SelectNextState {
1486 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
1487 f.debug_struct(std::any::type_name::<Self>())
1488 .field("wordwise", &self.wordwise)
1489 .field("done", &self.done)
1490 .finish()
1491 }
1492}
1493
1494#[derive(Debug)]
1495struct AutocloseRegion {
1496 selection_id: usize,
1497 range: Range<Anchor>,
1498 pair: BracketPair,
1499}
1500
1501#[derive(Debug)]
1502struct SnippetState {
1503 ranges: Vec<Vec<Range<Anchor>>>,
1504 active_index: usize,
1505 choices: Vec<Option<Vec<String>>>,
1506}
1507
1508#[doc(hidden)]
1509pub struct RenameState {
1510 pub range: Range<Anchor>,
1511 pub old_name: Arc<str>,
1512 pub editor: Entity<Editor>,
1513 block_id: CustomBlockId,
1514}
1515
1516struct InvalidationStack<T>(Vec<T>);
1517
1518struct RegisteredInlineCompletionProvider {
1519 provider: Arc<dyn InlineCompletionProviderHandle>,
1520 _subscription: Subscription,
1521}
1522
1523#[derive(Debug, PartialEq, Eq)]
1524pub struct ActiveDiagnosticGroup {
1525 pub active_range: Range<Anchor>,
1526 pub active_message: String,
1527 pub group_id: usize,
1528 pub blocks: HashSet<CustomBlockId>,
1529}
1530
1531#[derive(Debug, PartialEq, Eq)]
1532
1533pub(crate) enum ActiveDiagnostic {
1534 None,
1535 All,
1536 Group(ActiveDiagnosticGroup),
1537}
1538
1539#[derive(Serialize, Deserialize, Clone, Debug)]
1540pub struct ClipboardSelection {
1541 /// The number of bytes in this selection.
1542 pub len: usize,
1543 /// Whether this was a full-line selection.
1544 pub is_entire_line: bool,
1545 /// The indentation of the first line when this content was originally copied.
1546 pub first_line_indent: u32,
1547}
1548
1549// selections, scroll behavior, was newest selection reversed
1550type SelectSyntaxNodeHistoryState = (
1551 Box<[Selection<usize>]>,
1552 SelectSyntaxNodeScrollBehavior,
1553 bool,
1554);
1555
1556#[derive(Default)]
1557struct SelectSyntaxNodeHistory {
1558 stack: Vec<SelectSyntaxNodeHistoryState>,
1559 // disable temporarily to allow changing selections without losing the stack
1560 pub disable_clearing: bool,
1561}
1562
1563impl SelectSyntaxNodeHistory {
1564 pub fn try_clear(&mut self) {
1565 if !self.disable_clearing {
1566 self.stack.clear();
1567 }
1568 }
1569
1570 pub fn push(&mut self, selection: SelectSyntaxNodeHistoryState) {
1571 self.stack.push(selection);
1572 }
1573
1574 pub fn pop(&mut self) -> Option<SelectSyntaxNodeHistoryState> {
1575 self.stack.pop()
1576 }
1577}
1578
1579enum SelectSyntaxNodeScrollBehavior {
1580 CursorTop,
1581 FitSelection,
1582 CursorBottom,
1583}
1584
1585#[derive(Debug)]
1586pub(crate) struct NavigationData {
1587 cursor_anchor: Anchor,
1588 cursor_position: Point,
1589 scroll_anchor: ScrollAnchor,
1590 scroll_top_row: u32,
1591}
1592
1593#[derive(Debug, Clone, Copy, PartialEq, Eq)]
1594pub enum GotoDefinitionKind {
1595 Symbol,
1596 Declaration,
1597 Type,
1598 Implementation,
1599}
1600
1601#[derive(Debug, Clone)]
1602enum InlayHintRefreshReason {
1603 ModifiersChanged(bool),
1604 Toggle(bool),
1605 SettingsChange(InlayHintSettings),
1606 NewLinesShown,
1607 BufferEdited(HashSet<Arc<Language>>),
1608 RefreshRequested,
1609 ExcerptsRemoved(Vec<ExcerptId>),
1610}
1611
1612impl InlayHintRefreshReason {
1613 fn description(&self) -> &'static str {
1614 match self {
1615 Self::ModifiersChanged(_) => "modifiers changed",
1616 Self::Toggle(_) => "toggle",
1617 Self::SettingsChange(_) => "settings change",
1618 Self::NewLinesShown => "new lines shown",
1619 Self::BufferEdited(_) => "buffer edited",
1620 Self::RefreshRequested => "refresh requested",
1621 Self::ExcerptsRemoved(_) => "excerpts removed",
1622 }
1623 }
1624}
1625
1626pub enum FormatTarget {
1627 Buffers(HashSet<Entity<Buffer>>),
1628 Ranges(Vec<Range<MultiBufferPoint>>),
1629}
1630
1631pub(crate) struct FocusedBlock {
1632 id: BlockId,
1633 focus_handle: WeakFocusHandle,
1634}
1635
1636#[derive(Clone)]
1637enum JumpData {
1638 MultiBufferRow {
1639 row: MultiBufferRow,
1640 line_offset_from_top: u32,
1641 },
1642 MultiBufferPoint {
1643 excerpt_id: ExcerptId,
1644 position: Point,
1645 anchor: text::Anchor,
1646 line_offset_from_top: u32,
1647 },
1648}
1649
1650pub enum MultibufferSelectionMode {
1651 First,
1652 All,
1653}
1654
1655#[derive(Clone, Copy, Debug, Default)]
1656pub struct RewrapOptions {
1657 pub override_language_settings: bool,
1658 pub preserve_existing_whitespace: bool,
1659}
1660
1661impl Editor {
1662 pub fn single_line(window: &mut Window, cx: &mut Context<Self>) -> Self {
1663 let buffer = cx.new(|cx| Buffer::local("", cx));
1664 let buffer = cx.new(|cx| MultiBuffer::singleton(buffer, cx));
1665 Self::new(
1666 EditorMode::SingleLine { auto_width: false },
1667 buffer,
1668 None,
1669 window,
1670 cx,
1671 )
1672 }
1673
1674 pub fn multi_line(window: &mut Window, cx: &mut Context<Self>) -> Self {
1675 let buffer = cx.new(|cx| Buffer::local("", cx));
1676 let buffer = cx.new(|cx| MultiBuffer::singleton(buffer, cx));
1677 Self::new(EditorMode::full(), buffer, None, window, cx)
1678 }
1679
1680 pub fn auto_width(window: &mut Window, cx: &mut Context<Self>) -> Self {
1681 let buffer = cx.new(|cx| Buffer::local("", cx));
1682 let buffer = cx.new(|cx| MultiBuffer::singleton(buffer, cx));
1683 Self::new(
1684 EditorMode::SingleLine { auto_width: true },
1685 buffer,
1686 None,
1687 window,
1688 cx,
1689 )
1690 }
1691
1692 pub fn auto_height(
1693 min_lines: usize,
1694 max_lines: usize,
1695 window: &mut Window,
1696 cx: &mut Context<Self>,
1697 ) -> Self {
1698 let buffer = cx.new(|cx| Buffer::local("", cx));
1699 let buffer = cx.new(|cx| MultiBuffer::singleton(buffer, cx));
1700 Self::new(
1701 EditorMode::AutoHeight {
1702 min_lines,
1703 max_lines: Some(max_lines),
1704 },
1705 buffer,
1706 None,
1707 window,
1708 cx,
1709 )
1710 }
1711
1712 /// Creates a new auto-height editor with a minimum number of lines but no maximum.
1713 /// The editor grows as tall as needed to fit its content.
1714 pub fn auto_height_unbounded(
1715 min_lines: usize,
1716 window: &mut Window,
1717 cx: &mut Context<Self>,
1718 ) -> Self {
1719 let buffer = cx.new(|cx| Buffer::local("", cx));
1720 let buffer = cx.new(|cx| MultiBuffer::singleton(buffer, cx));
1721 Self::new(
1722 EditorMode::AutoHeight {
1723 min_lines,
1724 max_lines: None,
1725 },
1726 buffer,
1727 None,
1728 window,
1729 cx,
1730 )
1731 }
1732
1733 pub fn for_buffer(
1734 buffer: Entity<Buffer>,
1735 project: Option<Entity<Project>>,
1736 window: &mut Window,
1737 cx: &mut Context<Self>,
1738 ) -> Self {
1739 let buffer = cx.new(|cx| MultiBuffer::singleton(buffer, cx));
1740 Self::new(EditorMode::full(), buffer, project, window, cx)
1741 }
1742
1743 pub fn for_multibuffer(
1744 buffer: Entity<MultiBuffer>,
1745 project: Option<Entity<Project>>,
1746 window: &mut Window,
1747 cx: &mut Context<Self>,
1748 ) -> Self {
1749 Self::new(EditorMode::full(), buffer, project, window, cx)
1750 }
1751
1752 pub fn clone(&self, window: &mut Window, cx: &mut Context<Self>) -> Self {
1753 let mut clone = Self::new(
1754 self.mode.clone(),
1755 self.buffer.clone(),
1756 self.project.clone(),
1757 window,
1758 cx,
1759 );
1760 self.display_map.update(cx, |display_map, cx| {
1761 let snapshot = display_map.snapshot(cx);
1762 clone.display_map.update(cx, |display_map, cx| {
1763 display_map.set_state(&snapshot, cx);
1764 });
1765 });
1766 clone.folds_did_change(cx);
1767 clone.selections.clone_state(&self.selections);
1768 clone.scroll_manager.clone_state(&self.scroll_manager);
1769 clone.searchable = self.searchable;
1770 clone.read_only = self.read_only;
1771 clone
1772 }
1773
1774 pub fn new(
1775 mode: EditorMode,
1776 buffer: Entity<MultiBuffer>,
1777 project: Option<Entity<Project>>,
1778 window: &mut Window,
1779 cx: &mut Context<Self>,
1780 ) -> Self {
1781 Editor::new_internal(mode, buffer, project, None, window, cx)
1782 }
1783
1784 fn new_internal(
1785 mode: EditorMode,
1786 buffer: Entity<MultiBuffer>,
1787 project: Option<Entity<Project>>,
1788 display_map: Option<Entity<DisplayMap>>,
1789 window: &mut Window,
1790 cx: &mut Context<Self>,
1791 ) -> Self {
1792 debug_assert!(
1793 display_map.is_none() || mode.is_minimap(),
1794 "Providing a display map for a new editor is only intended for the minimap and might have unindended side effects otherwise!"
1795 );
1796
1797 let full_mode = mode.is_full();
1798 let diagnostics_max_severity = if full_mode {
1799 EditorSettings::get_global(cx)
1800 .diagnostics_max_severity
1801 .unwrap_or(DiagnosticSeverity::Hint)
1802 } else {
1803 DiagnosticSeverity::Off
1804 };
1805 let style = window.text_style();
1806 let font_size = style.font_size.to_pixels(window.rem_size());
1807 let editor = cx.entity().downgrade();
1808 let fold_placeholder = FoldPlaceholder {
1809 constrain_width: true,
1810 render: Arc::new(move |fold_id, fold_range, cx| {
1811 let editor = editor.clone();
1812 div()
1813 .id(fold_id)
1814 .bg(cx.theme().colors().ghost_element_background)
1815 .hover(|style| style.bg(cx.theme().colors().ghost_element_hover))
1816 .active(|style| style.bg(cx.theme().colors().ghost_element_active))
1817 .rounded_xs()
1818 .size_full()
1819 .cursor_pointer()
1820 .child("⋯")
1821 .on_mouse_down(MouseButton::Left, |_, _, cx| cx.stop_propagation())
1822 .on_click(move |_, _window, cx| {
1823 editor
1824 .update(cx, |editor, cx| {
1825 editor.unfold_ranges(
1826 &[fold_range.start..fold_range.end],
1827 true,
1828 false,
1829 cx,
1830 );
1831 cx.stop_propagation();
1832 })
1833 .ok();
1834 })
1835 .into_any()
1836 }),
1837 merge_adjacent: true,
1838 ..FoldPlaceholder::default()
1839 };
1840 let display_map = display_map.unwrap_or_else(|| {
1841 cx.new(|cx| {
1842 DisplayMap::new(
1843 buffer.clone(),
1844 style.font(),
1845 font_size,
1846 None,
1847 FILE_HEADER_HEIGHT,
1848 MULTI_BUFFER_EXCERPT_HEADER_HEIGHT,
1849 fold_placeholder,
1850 diagnostics_max_severity,
1851 cx,
1852 )
1853 })
1854 });
1855
1856 let selections = SelectionsCollection::new(display_map.clone(), buffer.clone());
1857
1858 let blink_manager = cx.new(|cx| BlinkManager::new(CURSOR_BLINK_INTERVAL, cx));
1859
1860 let soft_wrap_mode_override = matches!(mode, EditorMode::SingleLine { .. })
1861 .then(|| language_settings::SoftWrap::None);
1862
1863 let mut project_subscriptions = Vec::new();
1864 if mode.is_full() {
1865 if let Some(project) = project.as_ref() {
1866 project_subscriptions.push(cx.subscribe_in(
1867 project,
1868 window,
1869 |editor, _, event, window, cx| match event {
1870 project::Event::RefreshCodeLens => {
1871 // we always query lens with actions, without storing them, always refreshing them
1872 }
1873 project::Event::RefreshInlayHints => {
1874 editor
1875 .refresh_inlay_hints(InlayHintRefreshReason::RefreshRequested, cx);
1876 }
1877 project::Event::LanguageServerAdded(..)
1878 | project::Event::LanguageServerRemoved(..) => {
1879 if editor.tasks_update_task.is_none() {
1880 editor.tasks_update_task =
1881 Some(editor.refresh_runnables(window, cx));
1882 }
1883 editor.update_lsp_data(true, None, window, cx);
1884 }
1885 project::Event::SnippetEdit(id, snippet_edits) => {
1886 if let Some(buffer) = editor.buffer.read(cx).buffer(*id) {
1887 let focus_handle = editor.focus_handle(cx);
1888 if focus_handle.is_focused(window) {
1889 let snapshot = buffer.read(cx).snapshot();
1890 for (range, snippet) in snippet_edits {
1891 let editor_range =
1892 language::range_from_lsp(*range).to_offset(&snapshot);
1893 editor
1894 .insert_snippet(
1895 &[editor_range],
1896 snippet.clone(),
1897 window,
1898 cx,
1899 )
1900 .ok();
1901 }
1902 }
1903 }
1904 }
1905 _ => {}
1906 },
1907 ));
1908 if let Some(task_inventory) = project
1909 .read(cx)
1910 .task_store()
1911 .read(cx)
1912 .task_inventory()
1913 .cloned()
1914 {
1915 project_subscriptions.push(cx.observe_in(
1916 &task_inventory,
1917 window,
1918 |editor, _, window, cx| {
1919 editor.tasks_update_task = Some(editor.refresh_runnables(window, cx));
1920 },
1921 ));
1922 };
1923
1924 project_subscriptions.push(cx.subscribe_in(
1925 &project.read(cx).breakpoint_store(),
1926 window,
1927 |editor, _, event, window, cx| match event {
1928 BreakpointStoreEvent::ClearDebugLines => {
1929 editor.clear_row_highlights::<ActiveDebugLine>();
1930 editor.refresh_inline_values(cx);
1931 }
1932 BreakpointStoreEvent::SetDebugLine => {
1933 if editor.go_to_active_debug_line(window, cx) {
1934 cx.stop_propagation();
1935 }
1936
1937 editor.refresh_inline_values(cx);
1938 }
1939 _ => {}
1940 },
1941 ));
1942 let git_store = project.read(cx).git_store().clone();
1943 let project = project.clone();
1944 project_subscriptions.push(cx.subscribe(&git_store, move |this, _, event, cx| {
1945 match event {
1946 GitStoreEvent::RepositoryUpdated(
1947 _,
1948 RepositoryEvent::Updated {
1949 new_instance: true, ..
1950 },
1951 _,
1952 ) => {
1953 this.load_diff_task = Some(
1954 update_uncommitted_diff_for_buffer(
1955 cx.entity(),
1956 &project,
1957 this.buffer.read(cx).all_buffers(),
1958 this.buffer.clone(),
1959 cx,
1960 )
1961 .shared(),
1962 );
1963 }
1964 _ => {}
1965 }
1966 }));
1967 }
1968 }
1969
1970 let buffer_snapshot = buffer.read(cx).snapshot(cx);
1971
1972 let inlay_hint_settings =
1973 inlay_hint_settings(selections.newest_anchor().head(), &buffer_snapshot, cx);
1974 let focus_handle = cx.focus_handle();
1975 cx.on_focus(&focus_handle, window, Self::handle_focus)
1976 .detach();
1977 cx.on_focus_in(&focus_handle, window, Self::handle_focus_in)
1978 .detach();
1979 cx.on_focus_out(&focus_handle, window, Self::handle_focus_out)
1980 .detach();
1981 cx.on_blur(&focus_handle, window, Self::handle_blur)
1982 .detach();
1983 cx.observe_pending_input(window, Self::observe_pending_input)
1984 .detach();
1985
1986 let show_indent_guides = if matches!(mode, EditorMode::SingleLine { .. }) {
1987 Some(false)
1988 } else {
1989 None
1990 };
1991
1992 let breakpoint_store = match (&mode, project.as_ref()) {
1993 (EditorMode::Full { .. }, Some(project)) => Some(project.read(cx).breakpoint_store()),
1994 _ => None,
1995 };
1996
1997 let mut code_action_providers = Vec::new();
1998 let mut load_uncommitted_diff = None;
1999 if let Some(project) = project.clone() {
2000 load_uncommitted_diff = Some(
2001 update_uncommitted_diff_for_buffer(
2002 cx.entity(),
2003 &project,
2004 buffer.read(cx).all_buffers(),
2005 buffer.clone(),
2006 cx,
2007 )
2008 .shared(),
2009 );
2010 code_action_providers.push(Rc::new(project) as Rc<_>);
2011 }
2012
2013 let mut editor = Self {
2014 focus_handle,
2015 show_cursor_when_unfocused: false,
2016 last_focused_descendant: None,
2017 buffer: buffer.clone(),
2018 display_map: display_map.clone(),
2019 selections,
2020 scroll_manager: ScrollManager::new(cx),
2021 columnar_selection_state: None,
2022 add_selections_state: None,
2023 select_next_state: None,
2024 select_prev_state: None,
2025 selection_history: SelectionHistory::default(),
2026 defer_selection_effects: false,
2027 deferred_selection_effects_state: None,
2028 autoclose_regions: Vec::new(),
2029 snippet_stack: InvalidationStack::default(),
2030 select_syntax_node_history: SelectSyntaxNodeHistory::default(),
2031 ime_transaction: None,
2032 active_diagnostics: ActiveDiagnostic::None,
2033 show_inline_diagnostics: ProjectSettings::get_global(cx).diagnostics.inline.enabled,
2034 inline_diagnostics_update: Task::ready(()),
2035 inline_diagnostics: Vec::new(),
2036 soft_wrap_mode_override,
2037 diagnostics_max_severity,
2038 hard_wrap: None,
2039 completion_provider: project.clone().map(|project| Rc::new(project) as _),
2040 semantics_provider: project.clone().map(|project| Rc::new(project) as _),
2041 collaboration_hub: project.clone().map(|project| Box::new(project) as _),
2042 project,
2043 blink_manager: blink_manager.clone(),
2044 show_local_selections: true,
2045 show_scrollbars: ScrollbarAxes {
2046 horizontal: full_mode,
2047 vertical: full_mode,
2048 },
2049 minimap_visibility: MinimapVisibility::for_mode(&mode, cx),
2050 offset_content: !matches!(mode, EditorMode::SingleLine { .. }),
2051 show_breadcrumbs: EditorSettings::get_global(cx).toolbar.breadcrumbs,
2052 show_gutter: mode.is_full(),
2053 show_line_numbers: None,
2054 use_relative_line_numbers: None,
2055 disable_expand_excerpt_buttons: false,
2056 show_git_diff_gutter: None,
2057 show_code_actions: None,
2058 show_runnables: None,
2059 show_breakpoints: None,
2060 show_wrap_guides: None,
2061 show_indent_guides,
2062 placeholder_text: None,
2063 highlight_order: 0,
2064 highlighted_rows: HashMap::default(),
2065 background_highlights: TreeMap::default(),
2066 gutter_highlights: TreeMap::default(),
2067 scrollbar_marker_state: ScrollbarMarkerState::default(),
2068 active_indent_guides_state: ActiveIndentGuidesState::default(),
2069 nav_history: None,
2070 context_menu: RefCell::new(None),
2071 context_menu_options: None,
2072 mouse_context_menu: None,
2073 completion_tasks: Vec::new(),
2074 inline_blame_popover: None,
2075 inline_blame_popover_show_task: None,
2076 signature_help_state: SignatureHelpState::default(),
2077 auto_signature_help: None,
2078 find_all_references_task_sources: Vec::new(),
2079 next_completion_id: 0,
2080 next_inlay_id: 0,
2081 code_action_providers,
2082 available_code_actions: None,
2083 code_actions_task: None,
2084 quick_selection_highlight_task: None,
2085 debounced_selection_highlight_task: None,
2086 document_highlights_task: None,
2087 linked_editing_range_task: None,
2088 pending_rename: None,
2089 searchable: true,
2090 cursor_shape: EditorSettings::get_global(cx)
2091 .cursor_shape
2092 .unwrap_or_default(),
2093 current_line_highlight: None,
2094 autoindent_mode: Some(AutoindentMode::EachLine),
2095 collapse_matches: false,
2096 workspace: None,
2097 input_enabled: true,
2098 use_modal_editing: mode.is_full(),
2099 read_only: mode.is_minimap(),
2100 use_autoclose: true,
2101 use_auto_surround: true,
2102 auto_replace_emoji_shortcode: false,
2103 jsx_tag_auto_close_enabled_in_any_buffer: false,
2104 leader_id: None,
2105 remote_id: None,
2106 hover_state: HoverState::default(),
2107 pending_mouse_down: None,
2108 hovered_link_state: None,
2109 edit_prediction_provider: None,
2110 active_inline_completion: None,
2111 stale_inline_completion_in_menu: None,
2112 edit_prediction_preview: EditPredictionPreview::Inactive {
2113 released_too_fast: false,
2114 },
2115 inline_diagnostics_enabled: mode.is_full(),
2116 diagnostics_enabled: mode.is_full(),
2117 inline_value_cache: InlineValueCache::new(inlay_hint_settings.show_value_hints),
2118 inlay_hint_cache: InlayHintCache::new(inlay_hint_settings),
2119
2120 gutter_hovered: false,
2121 pixel_position_of_newest_cursor: None,
2122 last_bounds: None,
2123 last_position_map: None,
2124 expect_bounds_change: None,
2125 gutter_dimensions: GutterDimensions::default(),
2126 style: None,
2127 show_cursor_names: false,
2128 hovered_cursors: HashMap::default(),
2129 next_editor_action_id: EditorActionId::default(),
2130 editor_actions: Rc::default(),
2131 inline_completions_hidden_for_vim_mode: false,
2132 show_inline_completions_override: None,
2133 menu_inline_completions_policy: MenuInlineCompletionsPolicy::ByProvider,
2134 edit_prediction_settings: EditPredictionSettings::Disabled,
2135 edit_prediction_indent_conflict: false,
2136 edit_prediction_requires_modifier_in_indent_conflict: true,
2137 custom_context_menu: None,
2138 show_git_blame_gutter: false,
2139 show_git_blame_inline: false,
2140 show_selection_menu: None,
2141 show_git_blame_inline_delay_task: None,
2142 git_blame_inline_enabled: ProjectSettings::get_global(cx).git.inline_blame_enabled(),
2143 render_diff_hunk_controls: Arc::new(render_diff_hunk_controls),
2144 serialize_dirty_buffers: !mode.is_minimap()
2145 && ProjectSettings::get_global(cx)
2146 .session
2147 .restore_unsaved_buffers,
2148 blame: None,
2149 blame_subscription: None,
2150 tasks: BTreeMap::default(),
2151
2152 breakpoint_store,
2153 gutter_breakpoint_indicator: (None, None),
2154 hovered_diff_hunk_row: None,
2155 _subscriptions: vec![
2156 cx.observe(&buffer, Self::on_buffer_changed),
2157 cx.subscribe_in(&buffer, window, Self::on_buffer_event),
2158 cx.observe_in(&display_map, window, Self::on_display_map_changed),
2159 cx.observe(&blink_manager, |_, _, cx| cx.notify()),
2160 cx.observe_global_in::<SettingsStore>(window, Self::settings_changed),
2161 observe_buffer_font_size_adjustment(cx, |_, cx| cx.notify()),
2162 cx.observe_window_activation(window, |editor, window, cx| {
2163 let active = window.is_window_active();
2164 editor.blink_manager.update(cx, |blink_manager, cx| {
2165 if active {
2166 blink_manager.enable(cx);
2167 } else {
2168 blink_manager.disable(cx);
2169 }
2170 });
2171 if active {
2172 editor.show_mouse_cursor(cx);
2173 }
2174 }),
2175 ],
2176 tasks_update_task: None,
2177 pull_diagnostics_task: Task::ready(()),
2178 colors: None,
2179 next_color_inlay_id: 0,
2180 linked_edit_ranges: Default::default(),
2181 in_project_search: false,
2182 previous_search_ranges: None,
2183 breadcrumb_header: None,
2184 focused_block: None,
2185 next_scroll_position: NextScrollCursorCenterTopBottom::default(),
2186 addons: HashMap::default(),
2187 registered_buffers: HashMap::default(),
2188 _scroll_cursor_center_top_bottom_task: Task::ready(()),
2189 selection_mark_mode: false,
2190 toggle_fold_multiple_buffers: Task::ready(()),
2191 serialize_selections: Task::ready(()),
2192 serialize_folds: Task::ready(()),
2193 text_style_refinement: None,
2194 load_diff_task: load_uncommitted_diff,
2195 temporary_diff_override: false,
2196 mouse_cursor_hidden: false,
2197 minimap: None,
2198 hide_mouse_mode: EditorSettings::get_global(cx)
2199 .hide_mouse
2200 .unwrap_or_default(),
2201 change_list: ChangeList::new(),
2202 mode,
2203 selection_drag_state: SelectionDragState::None,
2204 folding_newlines: Task::ready(()),
2205 };
2206 if let Some(breakpoints) = editor.breakpoint_store.as_ref() {
2207 editor
2208 ._subscriptions
2209 .push(cx.observe(breakpoints, |_, _, cx| {
2210 cx.notify();
2211 }));
2212 }
2213 editor.tasks_update_task = Some(editor.refresh_runnables(window, cx));
2214 editor._subscriptions.extend(project_subscriptions);
2215
2216 editor._subscriptions.push(cx.subscribe_in(
2217 &cx.entity(),
2218 window,
2219 |editor, _, e: &EditorEvent, window, cx| match e {
2220 EditorEvent::ScrollPositionChanged { local, .. } => {
2221 if *local {
2222 let new_anchor = editor.scroll_manager.anchor();
2223 let snapshot = editor.snapshot(window, cx);
2224 editor.update_restoration_data(cx, move |data| {
2225 data.scroll_position = (
2226 new_anchor.top_row(&snapshot.buffer_snapshot),
2227 new_anchor.offset,
2228 );
2229 });
2230 editor.hide_signature_help(cx, SignatureHelpHiddenBy::Escape);
2231 editor.inline_blame_popover.take();
2232 }
2233 }
2234 EditorEvent::Edited { .. } => {
2235 if !vim_enabled(cx) {
2236 let (map, selections) = editor.selections.all_adjusted_display(cx);
2237 let pop_state = editor
2238 .change_list
2239 .last()
2240 .map(|previous| {
2241 previous.len() == selections.len()
2242 && previous.iter().enumerate().all(|(ix, p)| {
2243 p.to_display_point(&map).row()
2244 == selections[ix].head().row()
2245 })
2246 })
2247 .unwrap_or(false);
2248 let new_positions = selections
2249 .into_iter()
2250 .map(|s| map.display_point_to_anchor(s.head(), Bias::Left))
2251 .collect();
2252 editor
2253 .change_list
2254 .push_to_change_list(pop_state, new_positions);
2255 }
2256 }
2257 _ => (),
2258 },
2259 ));
2260
2261 if let Some(dap_store) = editor
2262 .project
2263 .as_ref()
2264 .map(|project| project.read(cx).dap_store())
2265 {
2266 let weak_editor = cx.weak_entity();
2267
2268 editor
2269 ._subscriptions
2270 .push(
2271 cx.observe_new::<project::debugger::session::Session>(move |_, _, cx| {
2272 let session_entity = cx.entity();
2273 weak_editor
2274 .update(cx, |editor, cx| {
2275 editor._subscriptions.push(
2276 cx.subscribe(&session_entity, Self::on_debug_session_event),
2277 );
2278 })
2279 .ok();
2280 }),
2281 );
2282
2283 for session in dap_store.read(cx).sessions().cloned().collect::<Vec<_>>() {
2284 editor
2285 ._subscriptions
2286 .push(cx.subscribe(&session, Self::on_debug_session_event));
2287 }
2288 }
2289
2290 // skip adding the initial selection to selection history
2291 editor.selection_history.mode = SelectionHistoryMode::Skipping;
2292 editor.end_selection(window, cx);
2293 editor.selection_history.mode = SelectionHistoryMode::Normal;
2294
2295 editor.scroll_manager.show_scrollbars(window, cx);
2296 jsx_tag_auto_close::refresh_enabled_in_any_buffer(&mut editor, &buffer, cx);
2297
2298 if full_mode {
2299 let should_auto_hide_scrollbars = cx.should_auto_hide_scrollbars();
2300 cx.set_global(ScrollbarAutoHide(should_auto_hide_scrollbars));
2301
2302 if editor.git_blame_inline_enabled {
2303 editor.start_git_blame_inline(false, window, cx);
2304 }
2305
2306 editor.go_to_active_debug_line(window, cx);
2307
2308 if let Some(buffer) = buffer.read(cx).as_singleton() {
2309 if let Some(project) = editor.project.as_ref() {
2310 let handle = project.update(cx, |project, cx| {
2311 project.register_buffer_with_language_servers(&buffer, cx)
2312 });
2313 editor
2314 .registered_buffers
2315 .insert(buffer.read(cx).remote_id(), handle);
2316 }
2317 }
2318
2319 editor.minimap =
2320 editor.create_minimap(EditorSettings::get_global(cx).minimap, window, cx);
2321 editor.colors = Some(LspColorData::new(cx));
2322 editor.update_lsp_data(false, None, window, cx);
2323 }
2324
2325 editor.report_editor_event("Editor Opened", None, cx);
2326 editor
2327 }
2328
2329 pub fn deploy_mouse_context_menu(
2330 &mut self,
2331 position: gpui::Point<Pixels>,
2332 context_menu: Entity<ContextMenu>,
2333 window: &mut Window,
2334 cx: &mut Context<Self>,
2335 ) {
2336 self.mouse_context_menu = Some(MouseContextMenu::new(
2337 self,
2338 crate::mouse_context_menu::MenuPosition::PinnedToScreen(position),
2339 context_menu,
2340 window,
2341 cx,
2342 ));
2343 }
2344
2345 pub fn mouse_menu_is_focused(&self, window: &Window, cx: &App) -> bool {
2346 self.mouse_context_menu
2347 .as_ref()
2348 .is_some_and(|menu| menu.context_menu.focus_handle(cx).is_focused(window))
2349 }
2350
2351 pub fn key_context(&self, window: &Window, cx: &App) -> KeyContext {
2352 self.key_context_internal(self.has_active_inline_completion(), window, cx)
2353 }
2354
2355 fn key_context_internal(
2356 &self,
2357 has_active_edit_prediction: bool,
2358 window: &Window,
2359 cx: &App,
2360 ) -> KeyContext {
2361 let mut key_context = KeyContext::new_with_defaults();
2362 key_context.add("Editor");
2363 let mode = match self.mode {
2364 EditorMode::SingleLine { .. } => "single_line",
2365 EditorMode::AutoHeight { .. } => "auto_height",
2366 EditorMode::Minimap { .. } => "minimap",
2367 EditorMode::Full { .. } => "full",
2368 };
2369
2370 if EditorSettings::jupyter_enabled(cx) {
2371 key_context.add("jupyter");
2372 }
2373
2374 key_context.set("mode", mode);
2375 if self.pending_rename.is_some() {
2376 key_context.add("renaming");
2377 }
2378
2379 match self.context_menu.borrow().as_ref() {
2380 Some(CodeContextMenu::Completions(_)) => {
2381 key_context.add("menu");
2382 key_context.add("showing_completions");
2383 }
2384 Some(CodeContextMenu::CodeActions(_)) => {
2385 key_context.add("menu");
2386 key_context.add("showing_code_actions")
2387 }
2388 None => {}
2389 }
2390
2391 if self.signature_help_state.has_multiple_signatures() {
2392 key_context.add("showing_signature_help");
2393 }
2394
2395 // Disable vim contexts when a sub-editor (e.g. rename/inline assistant) is focused.
2396 if !self.focus_handle(cx).contains_focused(window, cx)
2397 || (self.is_focused(window) || self.mouse_menu_is_focused(window, cx))
2398 {
2399 for addon in self.addons.values() {
2400 addon.extend_key_context(&mut key_context, cx)
2401 }
2402 }
2403
2404 if let Some(singleton_buffer) = self.buffer.read(cx).as_singleton() {
2405 if let Some(extension) = singleton_buffer
2406 .read(cx)
2407 .file()
2408 .and_then(|file| file.path().extension()?.to_str())
2409 {
2410 key_context.set("extension", extension.to_string());
2411 }
2412 } else {
2413 key_context.add("multibuffer");
2414 }
2415
2416 if has_active_edit_prediction {
2417 if self.edit_prediction_in_conflict() {
2418 key_context.add(EDIT_PREDICTION_CONFLICT_KEY_CONTEXT);
2419 } else {
2420 key_context.add(EDIT_PREDICTION_KEY_CONTEXT);
2421 key_context.add("copilot_suggestion");
2422 }
2423 }
2424
2425 if self.selection_mark_mode {
2426 key_context.add("selection_mode");
2427 }
2428
2429 key_context
2430 }
2431
2432 fn show_mouse_cursor(&mut self, cx: &mut Context<Self>) {
2433 if self.mouse_cursor_hidden {
2434 self.mouse_cursor_hidden = false;
2435 cx.notify();
2436 }
2437 }
2438
2439 pub fn hide_mouse_cursor(&mut self, origin: HideMouseCursorOrigin, cx: &mut Context<Self>) {
2440 let hide_mouse_cursor = match origin {
2441 HideMouseCursorOrigin::TypingAction => {
2442 matches!(
2443 self.hide_mouse_mode,
2444 HideMouseMode::OnTyping | HideMouseMode::OnTypingAndMovement
2445 )
2446 }
2447 HideMouseCursorOrigin::MovementAction => {
2448 matches!(self.hide_mouse_mode, HideMouseMode::OnTypingAndMovement)
2449 }
2450 };
2451 if self.mouse_cursor_hidden != hide_mouse_cursor {
2452 self.mouse_cursor_hidden = hide_mouse_cursor;
2453 cx.notify();
2454 }
2455 }
2456
2457 pub fn edit_prediction_in_conflict(&self) -> bool {
2458 if !self.show_edit_predictions_in_menu() {
2459 return false;
2460 }
2461
2462 let showing_completions = self
2463 .context_menu
2464 .borrow()
2465 .as_ref()
2466 .map_or(false, |context| {
2467 matches!(context, CodeContextMenu::Completions(_))
2468 });
2469
2470 showing_completions
2471 || self.edit_prediction_requires_modifier()
2472 // Require modifier key when the cursor is on leading whitespace, to allow `tab`
2473 // bindings to insert tab characters.
2474 || (self.edit_prediction_requires_modifier_in_indent_conflict && self.edit_prediction_indent_conflict)
2475 }
2476
2477 pub fn accept_edit_prediction_keybind(
2478 &self,
2479 accept_partial: bool,
2480 window: &Window,
2481 cx: &App,
2482 ) -> AcceptEditPredictionBinding {
2483 let key_context = self.key_context_internal(true, window, cx);
2484 let in_conflict = self.edit_prediction_in_conflict();
2485
2486 let bindings = if accept_partial {
2487 window.bindings_for_action_in_context(&AcceptPartialEditPrediction, key_context)
2488 } else {
2489 window.bindings_for_action_in_context(&AcceptEditPrediction, key_context)
2490 };
2491
2492 // TODO: if the binding contains multiple keystrokes, display all of them, not
2493 // just the first one.
2494 AcceptEditPredictionBinding(bindings.into_iter().rev().find(|binding| {
2495 !in_conflict
2496 || binding
2497 .keystrokes()
2498 .first()
2499 .map_or(false, |keystroke| keystroke.modifiers.modified())
2500 }))
2501 }
2502
2503 pub fn new_file(
2504 workspace: &mut Workspace,
2505 _: &workspace::NewFile,
2506 window: &mut Window,
2507 cx: &mut Context<Workspace>,
2508 ) {
2509 Self::new_in_workspace(workspace, window, cx).detach_and_prompt_err(
2510 "Failed to create buffer",
2511 window,
2512 cx,
2513 |e, _, _| match e.error_code() {
2514 ErrorCode::RemoteUpgradeRequired => Some(format!(
2515 "The remote instance of Zed does not support this yet. It must be upgraded to {}",
2516 e.error_tag("required").unwrap_or("the latest version")
2517 )),
2518 _ => None,
2519 },
2520 );
2521 }
2522
2523 pub fn new_in_workspace(
2524 workspace: &mut Workspace,
2525 window: &mut Window,
2526 cx: &mut Context<Workspace>,
2527 ) -> Task<Result<Entity<Editor>>> {
2528 let project = workspace.project().clone();
2529 let create = project.update(cx, |project, cx| project.create_buffer(cx));
2530
2531 cx.spawn_in(window, async move |workspace, cx| {
2532 let buffer = create.await?;
2533 workspace.update_in(cx, |workspace, window, cx| {
2534 let editor =
2535 cx.new(|cx| Editor::for_buffer(buffer, Some(project.clone()), window, cx));
2536 workspace.add_item_to_active_pane(Box::new(editor.clone()), None, true, window, cx);
2537 editor
2538 })
2539 })
2540 }
2541
2542 fn new_file_vertical(
2543 workspace: &mut Workspace,
2544 _: &workspace::NewFileSplitVertical,
2545 window: &mut Window,
2546 cx: &mut Context<Workspace>,
2547 ) {
2548 Self::new_file_in_direction(workspace, SplitDirection::vertical(cx), window, cx)
2549 }
2550
2551 fn new_file_horizontal(
2552 workspace: &mut Workspace,
2553 _: &workspace::NewFileSplitHorizontal,
2554 window: &mut Window,
2555 cx: &mut Context<Workspace>,
2556 ) {
2557 Self::new_file_in_direction(workspace, SplitDirection::horizontal(cx), window, cx)
2558 }
2559
2560 fn new_file_in_direction(
2561 workspace: &mut Workspace,
2562 direction: SplitDirection,
2563 window: &mut Window,
2564 cx: &mut Context<Workspace>,
2565 ) {
2566 let project = workspace.project().clone();
2567 let create = project.update(cx, |project, cx| project.create_buffer(cx));
2568
2569 cx.spawn_in(window, async move |workspace, cx| {
2570 let buffer = create.await?;
2571 workspace.update_in(cx, move |workspace, window, cx| {
2572 workspace.split_item(
2573 direction,
2574 Box::new(
2575 cx.new(|cx| Editor::for_buffer(buffer, Some(project.clone()), window, cx)),
2576 ),
2577 window,
2578 cx,
2579 )
2580 })?;
2581 anyhow::Ok(())
2582 })
2583 .detach_and_prompt_err("Failed to create buffer", window, cx, |e, _, _| {
2584 match e.error_code() {
2585 ErrorCode::RemoteUpgradeRequired => Some(format!(
2586 "The remote instance of Zed does not support this yet. It must be upgraded to {}",
2587 e.error_tag("required").unwrap_or("the latest version")
2588 )),
2589 _ => None,
2590 }
2591 });
2592 }
2593
2594 pub fn leader_id(&self) -> Option<CollaboratorId> {
2595 self.leader_id
2596 }
2597
2598 pub fn buffer(&self) -> &Entity<MultiBuffer> {
2599 &self.buffer
2600 }
2601
2602 pub fn workspace(&self) -> Option<Entity<Workspace>> {
2603 self.workspace.as_ref()?.0.upgrade()
2604 }
2605
2606 pub fn title<'a>(&self, cx: &'a App) -> Cow<'a, str> {
2607 self.buffer().read(cx).title(cx)
2608 }
2609
2610 pub fn snapshot(&self, window: &mut Window, cx: &mut App) -> EditorSnapshot {
2611 let git_blame_gutter_max_author_length = self
2612 .render_git_blame_gutter(cx)
2613 .then(|| {
2614 if let Some(blame) = self.blame.as_ref() {
2615 let max_author_length =
2616 blame.update(cx, |blame, cx| blame.max_author_length(cx));
2617 Some(max_author_length)
2618 } else {
2619 None
2620 }
2621 })
2622 .flatten();
2623
2624 EditorSnapshot {
2625 mode: self.mode.clone(),
2626 show_gutter: self.show_gutter,
2627 show_line_numbers: self.show_line_numbers,
2628 show_git_diff_gutter: self.show_git_diff_gutter,
2629 show_code_actions: self.show_code_actions,
2630 show_runnables: self.show_runnables,
2631 show_breakpoints: self.show_breakpoints,
2632 git_blame_gutter_max_author_length,
2633 display_snapshot: self.display_map.update(cx, |map, cx| map.snapshot(cx)),
2634 scroll_anchor: self.scroll_manager.anchor(),
2635 ongoing_scroll: self.scroll_manager.ongoing_scroll(),
2636 placeholder_text: self.placeholder_text.clone(),
2637 is_focused: self.focus_handle.is_focused(window),
2638 current_line_highlight: self
2639 .current_line_highlight
2640 .unwrap_or_else(|| EditorSettings::get_global(cx).current_line_highlight),
2641 gutter_hovered: self.gutter_hovered,
2642 }
2643 }
2644
2645 pub fn language_at<T: ToOffset>(&self, point: T, cx: &App) -> Option<Arc<Language>> {
2646 self.buffer.read(cx).language_at(point, cx)
2647 }
2648
2649 pub fn file_at<T: ToOffset>(&self, point: T, cx: &App) -> Option<Arc<dyn language::File>> {
2650 self.buffer.read(cx).read(cx).file_at(point).cloned()
2651 }
2652
2653 pub fn active_excerpt(
2654 &self,
2655 cx: &App,
2656 ) -> Option<(ExcerptId, Entity<Buffer>, Range<text::Anchor>)> {
2657 self.buffer
2658 .read(cx)
2659 .excerpt_containing(self.selections.newest_anchor().head(), cx)
2660 }
2661
2662 pub fn mode(&self) -> &EditorMode {
2663 &self.mode
2664 }
2665
2666 pub fn set_mode(&mut self, mode: EditorMode) {
2667 self.mode = mode;
2668 }
2669
2670 pub fn collaboration_hub(&self) -> Option<&dyn CollaborationHub> {
2671 self.collaboration_hub.as_deref()
2672 }
2673
2674 pub fn set_collaboration_hub(&mut self, hub: Box<dyn CollaborationHub>) {
2675 self.collaboration_hub = Some(hub);
2676 }
2677
2678 pub fn set_in_project_search(&mut self, in_project_search: bool) {
2679 self.in_project_search = in_project_search;
2680 }
2681
2682 pub fn set_custom_context_menu(
2683 &mut self,
2684 f: impl 'static
2685 + Fn(
2686 &mut Self,
2687 DisplayPoint,
2688 &mut Window,
2689 &mut Context<Self>,
2690 ) -> Option<Entity<ui::ContextMenu>>,
2691 ) {
2692 self.custom_context_menu = Some(Box::new(f))
2693 }
2694
2695 pub fn set_completion_provider(&mut self, provider: Option<Rc<dyn CompletionProvider>>) {
2696 self.completion_provider = provider;
2697 }
2698
2699 pub fn semantics_provider(&self) -> Option<Rc<dyn SemanticsProvider>> {
2700 self.semantics_provider.clone()
2701 }
2702
2703 pub fn set_semantics_provider(&mut self, provider: Option<Rc<dyn SemanticsProvider>>) {
2704 self.semantics_provider = provider;
2705 }
2706
2707 pub fn set_edit_prediction_provider<T>(
2708 &mut self,
2709 provider: Option<Entity<T>>,
2710 window: &mut Window,
2711 cx: &mut Context<Self>,
2712 ) where
2713 T: EditPredictionProvider,
2714 {
2715 self.edit_prediction_provider =
2716 provider.map(|provider| RegisteredInlineCompletionProvider {
2717 _subscription: cx.observe_in(&provider, window, |this, _, window, cx| {
2718 if this.focus_handle.is_focused(window) {
2719 this.update_visible_inline_completion(window, cx);
2720 }
2721 }),
2722 provider: Arc::new(provider),
2723 });
2724 self.update_edit_prediction_settings(cx);
2725 self.refresh_inline_completion(false, false, window, cx);
2726 }
2727
2728 pub fn placeholder_text(&self) -> Option<&str> {
2729 self.placeholder_text.as_deref()
2730 }
2731
2732 pub fn set_placeholder_text(
2733 &mut self,
2734 placeholder_text: impl Into<Arc<str>>,
2735 cx: &mut Context<Self>,
2736 ) {
2737 let placeholder_text = Some(placeholder_text.into());
2738 if self.placeholder_text != placeholder_text {
2739 self.placeholder_text = placeholder_text;
2740 cx.notify();
2741 }
2742 }
2743
2744 pub fn set_cursor_shape(&mut self, cursor_shape: CursorShape, cx: &mut Context<Self>) {
2745 self.cursor_shape = cursor_shape;
2746
2747 // Disrupt blink for immediate user feedback that the cursor shape has changed
2748 self.blink_manager.update(cx, BlinkManager::show_cursor);
2749
2750 cx.notify();
2751 }
2752
2753 pub fn set_current_line_highlight(
2754 &mut self,
2755 current_line_highlight: Option<CurrentLineHighlight>,
2756 ) {
2757 self.current_line_highlight = current_line_highlight;
2758 }
2759
2760 pub fn set_collapse_matches(&mut self, collapse_matches: bool) {
2761 self.collapse_matches = collapse_matches;
2762 }
2763
2764 fn register_buffers_with_language_servers(&mut self, cx: &mut Context<Self>) {
2765 let buffers = self.buffer.read(cx).all_buffers();
2766 let Some(project) = self.project.as_ref() else {
2767 return;
2768 };
2769 project.update(cx, |project, cx| {
2770 for buffer in buffers {
2771 self.registered_buffers
2772 .entry(buffer.read(cx).remote_id())
2773 .or_insert_with(|| project.register_buffer_with_language_servers(&buffer, cx));
2774 }
2775 })
2776 }
2777
2778 pub fn range_for_match<T: std::marker::Copy>(&self, range: &Range<T>) -> Range<T> {
2779 if self.collapse_matches {
2780 return range.start..range.start;
2781 }
2782 range.clone()
2783 }
2784
2785 pub fn set_clip_at_line_ends(&mut self, clip: bool, cx: &mut Context<Self>) {
2786 if self.display_map.read(cx).clip_at_line_ends != clip {
2787 self.display_map
2788 .update(cx, |map, _| map.clip_at_line_ends = clip);
2789 }
2790 }
2791
2792 pub fn set_input_enabled(&mut self, input_enabled: bool) {
2793 self.input_enabled = input_enabled;
2794 }
2795
2796 pub fn set_inline_completions_hidden_for_vim_mode(
2797 &mut self,
2798 hidden: bool,
2799 window: &mut Window,
2800 cx: &mut Context<Self>,
2801 ) {
2802 if hidden != self.inline_completions_hidden_for_vim_mode {
2803 self.inline_completions_hidden_for_vim_mode = hidden;
2804 if hidden {
2805 self.update_visible_inline_completion(window, cx);
2806 } else {
2807 self.refresh_inline_completion(true, false, window, cx);
2808 }
2809 }
2810 }
2811
2812 pub fn set_menu_inline_completions_policy(&mut self, value: MenuInlineCompletionsPolicy) {
2813 self.menu_inline_completions_policy = value;
2814 }
2815
2816 pub fn set_autoindent(&mut self, autoindent: bool) {
2817 if autoindent {
2818 self.autoindent_mode = Some(AutoindentMode::EachLine);
2819 } else {
2820 self.autoindent_mode = None;
2821 }
2822 }
2823
2824 pub fn read_only(&self, cx: &App) -> bool {
2825 self.read_only || self.buffer.read(cx).read_only()
2826 }
2827
2828 pub fn set_read_only(&mut self, read_only: bool) {
2829 self.read_only = read_only;
2830 }
2831
2832 pub fn set_use_autoclose(&mut self, autoclose: bool) {
2833 self.use_autoclose = autoclose;
2834 }
2835
2836 pub fn set_use_auto_surround(&mut self, auto_surround: bool) {
2837 self.use_auto_surround = auto_surround;
2838 }
2839
2840 pub fn set_auto_replace_emoji_shortcode(&mut self, auto_replace: bool) {
2841 self.auto_replace_emoji_shortcode = auto_replace;
2842 }
2843
2844 pub fn toggle_edit_predictions(
2845 &mut self,
2846 _: &ToggleEditPrediction,
2847 window: &mut Window,
2848 cx: &mut Context<Self>,
2849 ) {
2850 if self.show_inline_completions_override.is_some() {
2851 self.set_show_edit_predictions(None, window, cx);
2852 } else {
2853 let show_edit_predictions = !self.edit_predictions_enabled();
2854 self.set_show_edit_predictions(Some(show_edit_predictions), window, cx);
2855 }
2856 }
2857
2858 pub fn set_show_edit_predictions(
2859 &mut self,
2860 show_edit_predictions: Option<bool>,
2861 window: &mut Window,
2862 cx: &mut Context<Self>,
2863 ) {
2864 self.show_inline_completions_override = show_edit_predictions;
2865 self.update_edit_prediction_settings(cx);
2866
2867 if let Some(false) = show_edit_predictions {
2868 self.discard_inline_completion(false, cx);
2869 } else {
2870 self.refresh_inline_completion(false, true, window, cx);
2871 }
2872 }
2873
2874 fn inline_completions_disabled_in_scope(
2875 &self,
2876 buffer: &Entity<Buffer>,
2877 buffer_position: language::Anchor,
2878 cx: &App,
2879 ) -> bool {
2880 let snapshot = buffer.read(cx).snapshot();
2881 let settings = snapshot.settings_at(buffer_position, cx);
2882
2883 let Some(scope) = snapshot.language_scope_at(buffer_position) else {
2884 return false;
2885 };
2886
2887 scope.override_name().map_or(false, |scope_name| {
2888 settings
2889 .edit_predictions_disabled_in
2890 .iter()
2891 .any(|s| s == scope_name)
2892 })
2893 }
2894
2895 pub fn set_use_modal_editing(&mut self, to: bool) {
2896 self.use_modal_editing = to;
2897 }
2898
2899 pub fn use_modal_editing(&self) -> bool {
2900 self.use_modal_editing
2901 }
2902
2903 fn selections_did_change(
2904 &mut self,
2905 local: bool,
2906 old_cursor_position: &Anchor,
2907 effects: SelectionEffects,
2908 window: &mut Window,
2909 cx: &mut Context<Self>,
2910 ) {
2911 window.invalidate_character_coordinates();
2912
2913 // Copy selections to primary selection buffer
2914 #[cfg(any(target_os = "linux", target_os = "freebsd"))]
2915 if local {
2916 let selections = self.selections.all::<usize>(cx);
2917 let buffer_handle = self.buffer.read(cx).read(cx);
2918
2919 let mut text = String::new();
2920 for (index, selection) in selections.iter().enumerate() {
2921 let text_for_selection = buffer_handle
2922 .text_for_range(selection.start..selection.end)
2923 .collect::<String>();
2924
2925 text.push_str(&text_for_selection);
2926 if index != selections.len() - 1 {
2927 text.push('\n');
2928 }
2929 }
2930
2931 if !text.is_empty() {
2932 cx.write_to_primary(ClipboardItem::new_string(text));
2933 }
2934 }
2935
2936 if self.focus_handle.is_focused(window) && self.leader_id.is_none() {
2937 self.buffer.update(cx, |buffer, cx| {
2938 buffer.set_active_selections(
2939 &self.selections.disjoint_anchors(),
2940 self.selections.line_mode,
2941 self.cursor_shape,
2942 cx,
2943 )
2944 });
2945 }
2946 let display_map = self
2947 .display_map
2948 .update(cx, |display_map, cx| display_map.snapshot(cx));
2949 let buffer = &display_map.buffer_snapshot;
2950 if self.selections.count() == 1 {
2951 self.add_selections_state = None;
2952 }
2953 self.select_next_state = None;
2954 self.select_prev_state = None;
2955 self.select_syntax_node_history.try_clear();
2956 self.invalidate_autoclose_regions(&self.selections.disjoint_anchors(), buffer);
2957 self.snippet_stack
2958 .invalidate(&self.selections.disjoint_anchors(), buffer);
2959 self.take_rename(false, window, cx);
2960
2961 let newest_selection = self.selections.newest_anchor();
2962 let new_cursor_position = newest_selection.head();
2963 let selection_start = newest_selection.start;
2964
2965 if effects.nav_history.is_none() || effects.nav_history == Some(true) {
2966 self.push_to_nav_history(
2967 *old_cursor_position,
2968 Some(new_cursor_position.to_point(buffer)),
2969 false,
2970 effects.nav_history == Some(true),
2971 cx,
2972 );
2973 }
2974
2975 if local {
2976 if let Some(buffer_id) = new_cursor_position.buffer_id {
2977 if !self.registered_buffers.contains_key(&buffer_id) {
2978 if let Some(project) = self.project.as_ref() {
2979 project.update(cx, |project, cx| {
2980 let Some(buffer) = self.buffer.read(cx).buffer(buffer_id) else {
2981 return;
2982 };
2983 self.registered_buffers.insert(
2984 buffer_id,
2985 project.register_buffer_with_language_servers(&buffer, cx),
2986 );
2987 })
2988 }
2989 }
2990 }
2991
2992 let mut context_menu = self.context_menu.borrow_mut();
2993 let completion_menu = match context_menu.as_ref() {
2994 Some(CodeContextMenu::Completions(menu)) => Some(menu),
2995 Some(CodeContextMenu::CodeActions(_)) => {
2996 *context_menu = None;
2997 None
2998 }
2999 None => None,
3000 };
3001 let completion_position = completion_menu.map(|menu| menu.initial_position);
3002 drop(context_menu);
3003
3004 if effects.completions {
3005 if let Some(completion_position) = completion_position {
3006 let start_offset = selection_start.to_offset(buffer);
3007 let position_matches = start_offset == completion_position.to_offset(buffer);
3008 let continue_showing = if position_matches {
3009 if self.snippet_stack.is_empty() {
3010 buffer.char_kind_before(start_offset, true) == Some(CharKind::Word)
3011 } else {
3012 // Snippet choices can be shown even when the cursor is in whitespace.
3013 // Dismissing the menu with actions like backspace is handled by
3014 // invalidation regions.
3015 true
3016 }
3017 } else {
3018 false
3019 };
3020
3021 if continue_showing {
3022 self.show_completions(&ShowCompletions { trigger: None }, window, cx);
3023 } else {
3024 self.hide_context_menu(window, cx);
3025 }
3026 }
3027 }
3028
3029 hide_hover(self, cx);
3030
3031 if old_cursor_position.to_display_point(&display_map).row()
3032 != new_cursor_position.to_display_point(&display_map).row()
3033 {
3034 self.available_code_actions.take();
3035 }
3036 self.refresh_code_actions(window, cx);
3037 self.refresh_document_highlights(cx);
3038 self.refresh_selected_text_highlights(false, window, cx);
3039 refresh_matching_bracket_highlights(self, window, cx);
3040 self.update_visible_inline_completion(window, cx);
3041 self.edit_prediction_requires_modifier_in_indent_conflict = true;
3042 linked_editing_ranges::refresh_linked_ranges(self, window, cx);
3043 self.inline_blame_popover.take();
3044 if self.git_blame_inline_enabled {
3045 self.start_inline_blame_timer(window, cx);
3046 }
3047 }
3048
3049 self.blink_manager.update(cx, BlinkManager::pause_blinking);
3050 cx.emit(EditorEvent::SelectionsChanged { local });
3051
3052 let selections = &self.selections.disjoint;
3053 if selections.len() == 1 {
3054 cx.emit(SearchEvent::ActiveMatchChanged)
3055 }
3056 if local {
3057 if let Some((_, _, buffer_snapshot)) = buffer.as_singleton() {
3058 let inmemory_selections = selections
3059 .iter()
3060 .map(|s| {
3061 text::ToPoint::to_point(&s.range().start.text_anchor, buffer_snapshot)
3062 ..text::ToPoint::to_point(&s.range().end.text_anchor, buffer_snapshot)
3063 })
3064 .collect();
3065 self.update_restoration_data(cx, |data| {
3066 data.selections = inmemory_selections;
3067 });
3068
3069 if WorkspaceSettings::get(None, cx).restore_on_startup
3070 != RestoreOnStartupBehavior::None
3071 {
3072 if let Some(workspace_id) =
3073 self.workspace.as_ref().and_then(|workspace| workspace.1)
3074 {
3075 let snapshot = self.buffer().read(cx).snapshot(cx);
3076 let selections = selections.clone();
3077 let background_executor = cx.background_executor().clone();
3078 let editor_id = cx.entity().entity_id().as_u64() as ItemId;
3079 self.serialize_selections = cx.background_spawn(async move {
3080 background_executor.timer(SERIALIZATION_THROTTLE_TIME).await;
3081 let db_selections = selections
3082 .iter()
3083 .map(|selection| {
3084 (
3085 selection.start.to_offset(&snapshot),
3086 selection.end.to_offset(&snapshot),
3087 )
3088 })
3089 .collect();
3090
3091 DB.save_editor_selections(editor_id, workspace_id, db_selections)
3092 .await
3093 .with_context(|| format!("persisting editor selections for editor {editor_id}, workspace {workspace_id:?}"))
3094 .log_err();
3095 });
3096 }
3097 }
3098 }
3099 }
3100
3101 cx.notify();
3102 }
3103
3104 fn folds_did_change(&mut self, cx: &mut Context<Self>) {
3105 use text::ToOffset as _;
3106 use text::ToPoint as _;
3107
3108 if self.mode.is_minimap()
3109 || WorkspaceSettings::get(None, cx).restore_on_startup == RestoreOnStartupBehavior::None
3110 {
3111 return;
3112 }
3113
3114 let Some(singleton) = self.buffer().read(cx).as_singleton() else {
3115 return;
3116 };
3117
3118 let snapshot = singleton.read(cx).snapshot();
3119 let inmemory_folds = self.display_map.update(cx, |display_map, cx| {
3120 let display_snapshot = display_map.snapshot(cx);
3121
3122 display_snapshot
3123 .folds_in_range(0..display_snapshot.buffer_snapshot.len())
3124 .map(|fold| {
3125 fold.range.start.text_anchor.to_point(&snapshot)
3126 ..fold.range.end.text_anchor.to_point(&snapshot)
3127 })
3128 .collect()
3129 });
3130 self.update_restoration_data(cx, |data| {
3131 data.folds = inmemory_folds;
3132 });
3133
3134 let Some(workspace_id) = self.workspace.as_ref().and_then(|workspace| workspace.1) else {
3135 return;
3136 };
3137 let background_executor = cx.background_executor().clone();
3138 let editor_id = cx.entity().entity_id().as_u64() as ItemId;
3139 let db_folds = self.display_map.update(cx, |display_map, cx| {
3140 display_map
3141 .snapshot(cx)
3142 .folds_in_range(0..snapshot.len())
3143 .map(|fold| {
3144 (
3145 fold.range.start.text_anchor.to_offset(&snapshot),
3146 fold.range.end.text_anchor.to_offset(&snapshot),
3147 )
3148 })
3149 .collect()
3150 });
3151 self.serialize_folds = cx.background_spawn(async move {
3152 background_executor.timer(SERIALIZATION_THROTTLE_TIME).await;
3153 DB.save_editor_folds(editor_id, workspace_id, db_folds)
3154 .await
3155 .with_context(|| {
3156 format!(
3157 "persisting editor folds for editor {editor_id}, workspace {workspace_id:?}"
3158 )
3159 })
3160 .log_err();
3161 });
3162 }
3163
3164 pub fn sync_selections(
3165 &mut self,
3166 other: Entity<Editor>,
3167 cx: &mut Context<Self>,
3168 ) -> gpui::Subscription {
3169 let other_selections = other.read(cx).selections.disjoint.to_vec();
3170 self.selections.change_with(cx, |selections| {
3171 selections.select_anchors(other_selections);
3172 });
3173
3174 let other_subscription =
3175 cx.subscribe(&other, |this, other, other_evt, cx| match other_evt {
3176 EditorEvent::SelectionsChanged { local: true } => {
3177 let other_selections = other.read(cx).selections.disjoint.to_vec();
3178 if other_selections.is_empty() {
3179 return;
3180 }
3181 this.selections.change_with(cx, |selections| {
3182 selections.select_anchors(other_selections);
3183 });
3184 }
3185 _ => {}
3186 });
3187
3188 let this_subscription =
3189 cx.subscribe_self::<EditorEvent>(move |this, this_evt, cx| match this_evt {
3190 EditorEvent::SelectionsChanged { local: true } => {
3191 let these_selections = this.selections.disjoint.to_vec();
3192 if these_selections.is_empty() {
3193 return;
3194 }
3195 other.update(cx, |other_editor, cx| {
3196 other_editor.selections.change_with(cx, |selections| {
3197 selections.select_anchors(these_selections);
3198 })
3199 });
3200 }
3201 _ => {}
3202 });
3203
3204 Subscription::join(other_subscription, this_subscription)
3205 }
3206
3207 /// Changes selections using the provided mutation function. Changes to `self.selections` occur
3208 /// immediately, but when run within `transact` or `with_selection_effects_deferred` other
3209 /// effects of selection change occur at the end of the transaction.
3210 pub fn change_selections<R>(
3211 &mut self,
3212 effects: SelectionEffects,
3213 window: &mut Window,
3214 cx: &mut Context<Self>,
3215 change: impl FnOnce(&mut MutableSelectionsCollection<'_>) -> R,
3216 ) -> R {
3217 if let Some(state) = &mut self.deferred_selection_effects_state {
3218 state.effects.scroll = effects.scroll.or(state.effects.scroll);
3219 state.effects.completions = effects.completions;
3220 state.effects.nav_history = effects.nav_history.or(state.effects.nav_history);
3221 let (changed, result) = self.selections.change_with(cx, change);
3222 state.changed |= changed;
3223 return result;
3224 }
3225 let mut state = DeferredSelectionEffectsState {
3226 changed: false,
3227 effects,
3228 old_cursor_position: self.selections.newest_anchor().head(),
3229 history_entry: SelectionHistoryEntry {
3230 selections: self.selections.disjoint_anchors(),
3231 select_next_state: self.select_next_state.clone(),
3232 select_prev_state: self.select_prev_state.clone(),
3233 add_selections_state: self.add_selections_state.clone(),
3234 },
3235 };
3236 let (changed, result) = self.selections.change_with(cx, change);
3237 state.changed = state.changed || changed;
3238 if self.defer_selection_effects {
3239 self.deferred_selection_effects_state = Some(state);
3240 } else {
3241 self.apply_selection_effects(state, window, cx);
3242 }
3243 result
3244 }
3245
3246 /// Defers the effects of selection change, so that the effects of multiple calls to
3247 /// `change_selections` are applied at the end. This way these intermediate states aren't added
3248 /// to selection history and the state of popovers based on selection position aren't
3249 /// erroneously updated.
3250 pub fn with_selection_effects_deferred<R>(
3251 &mut self,
3252 window: &mut Window,
3253 cx: &mut Context<Self>,
3254 update: impl FnOnce(&mut Self, &mut Window, &mut Context<Self>) -> R,
3255 ) -> R {
3256 let already_deferred = self.defer_selection_effects;
3257 self.defer_selection_effects = true;
3258 let result = update(self, window, cx);
3259 if !already_deferred {
3260 self.defer_selection_effects = false;
3261 if let Some(state) = self.deferred_selection_effects_state.take() {
3262 self.apply_selection_effects(state, window, cx);
3263 }
3264 }
3265 result
3266 }
3267
3268 fn apply_selection_effects(
3269 &mut self,
3270 state: DeferredSelectionEffectsState,
3271 window: &mut Window,
3272 cx: &mut Context<Self>,
3273 ) {
3274 if state.changed {
3275 self.selection_history.push(state.history_entry);
3276
3277 if let Some(autoscroll) = state.effects.scroll {
3278 self.request_autoscroll(autoscroll, cx);
3279 }
3280
3281 let old_cursor_position = &state.old_cursor_position;
3282
3283 self.selections_did_change(true, &old_cursor_position, state.effects, window, cx);
3284
3285 if self.should_open_signature_help_automatically(&old_cursor_position, cx) {
3286 self.show_signature_help(&ShowSignatureHelp, window, cx);
3287 }
3288 }
3289 }
3290
3291 pub fn edit<I, S, T>(&mut self, edits: I, cx: &mut Context<Self>)
3292 where
3293 I: IntoIterator<Item = (Range<S>, T)>,
3294 S: ToOffset,
3295 T: Into<Arc<str>>,
3296 {
3297 if self.read_only(cx) {
3298 return;
3299 }
3300
3301 self.buffer
3302 .update(cx, |buffer, cx| buffer.edit(edits, None, cx));
3303 }
3304
3305 pub fn edit_with_autoindent<I, S, T>(&mut self, edits: I, cx: &mut Context<Self>)
3306 where
3307 I: IntoIterator<Item = (Range<S>, T)>,
3308 S: ToOffset,
3309 T: Into<Arc<str>>,
3310 {
3311 if self.read_only(cx) {
3312 return;
3313 }
3314
3315 self.buffer.update(cx, |buffer, cx| {
3316 buffer.edit(edits, self.autoindent_mode.clone(), cx)
3317 });
3318 }
3319
3320 pub fn edit_with_block_indent<I, S, T>(
3321 &mut self,
3322 edits: I,
3323 original_indent_columns: Vec<Option<u32>>,
3324 cx: &mut Context<Self>,
3325 ) where
3326 I: IntoIterator<Item = (Range<S>, T)>,
3327 S: ToOffset,
3328 T: Into<Arc<str>>,
3329 {
3330 if self.read_only(cx) {
3331 return;
3332 }
3333
3334 self.buffer.update(cx, |buffer, cx| {
3335 buffer.edit(
3336 edits,
3337 Some(AutoindentMode::Block {
3338 original_indent_columns,
3339 }),
3340 cx,
3341 )
3342 });
3343 }
3344
3345 fn select(&mut self, phase: SelectPhase, window: &mut Window, cx: &mut Context<Self>) {
3346 self.hide_context_menu(window, cx);
3347
3348 match phase {
3349 SelectPhase::Begin {
3350 position,
3351 add,
3352 click_count,
3353 } => self.begin_selection(position, add, click_count, window, cx),
3354 SelectPhase::BeginColumnar {
3355 position,
3356 goal_column,
3357 reset,
3358 mode,
3359 } => self.begin_columnar_selection(position, goal_column, reset, mode, window, cx),
3360 SelectPhase::Extend {
3361 position,
3362 click_count,
3363 } => self.extend_selection(position, click_count, window, cx),
3364 SelectPhase::Update {
3365 position,
3366 goal_column,
3367 scroll_delta,
3368 } => self.update_selection(position, goal_column, scroll_delta, window, cx),
3369 SelectPhase::End => self.end_selection(window, cx),
3370 }
3371 }
3372
3373 fn extend_selection(
3374 &mut self,
3375 position: DisplayPoint,
3376 click_count: usize,
3377 window: &mut Window,
3378 cx: &mut Context<Self>,
3379 ) {
3380 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
3381 let tail = self.selections.newest::<usize>(cx).tail();
3382 self.begin_selection(position, false, click_count, window, cx);
3383
3384 let position = position.to_offset(&display_map, Bias::Left);
3385 let tail_anchor = display_map.buffer_snapshot.anchor_before(tail);
3386
3387 let mut pending_selection = self
3388 .selections
3389 .pending_anchor()
3390 .expect("extend_selection not called with pending selection");
3391 if position >= tail {
3392 pending_selection.start = tail_anchor;
3393 } else {
3394 pending_selection.end = tail_anchor;
3395 pending_selection.reversed = true;
3396 }
3397
3398 let mut pending_mode = self.selections.pending_mode().unwrap();
3399 match &mut pending_mode {
3400 SelectMode::Word(range) | SelectMode::Line(range) => *range = tail_anchor..tail_anchor,
3401 _ => {}
3402 }
3403
3404 let effects = if EditorSettings::get_global(cx).autoscroll_on_clicks {
3405 SelectionEffects::scroll(Autoscroll::fit())
3406 } else {
3407 SelectionEffects::no_scroll()
3408 };
3409
3410 self.change_selections(effects, window, cx, |s| {
3411 s.set_pending(pending_selection, pending_mode)
3412 });
3413 }
3414
3415 fn begin_selection(
3416 &mut self,
3417 position: DisplayPoint,
3418 add: bool,
3419 click_count: usize,
3420 window: &mut Window,
3421 cx: &mut Context<Self>,
3422 ) {
3423 if !self.focus_handle.is_focused(window) {
3424 self.last_focused_descendant = None;
3425 window.focus(&self.focus_handle);
3426 }
3427
3428 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
3429 let buffer = &display_map.buffer_snapshot;
3430 let position = display_map.clip_point(position, Bias::Left);
3431
3432 let start;
3433 let end;
3434 let mode;
3435 let mut auto_scroll;
3436 match click_count {
3437 1 => {
3438 start = buffer.anchor_before(position.to_point(&display_map));
3439 end = start;
3440 mode = SelectMode::Character;
3441 auto_scroll = true;
3442 }
3443 2 => {
3444 let position = display_map
3445 .clip_point(position, Bias::Left)
3446 .to_offset(&display_map, Bias::Left);
3447 let (range, _) = buffer.surrounding_word(position, false);
3448 start = buffer.anchor_before(range.start);
3449 end = buffer.anchor_before(range.end);
3450 mode = SelectMode::Word(start..end);
3451 auto_scroll = true;
3452 }
3453 3 => {
3454 let position = display_map
3455 .clip_point(position, Bias::Left)
3456 .to_point(&display_map);
3457 let line_start = display_map.prev_line_boundary(position).0;
3458 let next_line_start = buffer.clip_point(
3459 display_map.next_line_boundary(position).0 + Point::new(1, 0),
3460 Bias::Left,
3461 );
3462 start = buffer.anchor_before(line_start);
3463 end = buffer.anchor_before(next_line_start);
3464 mode = SelectMode::Line(start..end);
3465 auto_scroll = true;
3466 }
3467 _ => {
3468 start = buffer.anchor_before(0);
3469 end = buffer.anchor_before(buffer.len());
3470 mode = SelectMode::All;
3471 auto_scroll = false;
3472 }
3473 }
3474 auto_scroll &= EditorSettings::get_global(cx).autoscroll_on_clicks;
3475
3476 let point_to_delete: Option<usize> = {
3477 let selected_points: Vec<Selection<Point>> =
3478 self.selections.disjoint_in_range(start..end, cx);
3479
3480 if !add || click_count > 1 {
3481 None
3482 } else if !selected_points.is_empty() {
3483 Some(selected_points[0].id)
3484 } else {
3485 let clicked_point_already_selected =
3486 self.selections.disjoint.iter().find(|selection| {
3487 selection.start.to_point(buffer) == start.to_point(buffer)
3488 || selection.end.to_point(buffer) == end.to_point(buffer)
3489 });
3490
3491 clicked_point_already_selected.map(|selection| selection.id)
3492 }
3493 };
3494
3495 let selections_count = self.selections.count();
3496 let effects = if auto_scroll {
3497 SelectionEffects::default()
3498 } else {
3499 SelectionEffects::no_scroll()
3500 };
3501
3502 self.change_selections(effects, window, cx, |s| {
3503 if let Some(point_to_delete) = point_to_delete {
3504 s.delete(point_to_delete);
3505
3506 if selections_count == 1 {
3507 s.set_pending_anchor_range(start..end, mode);
3508 }
3509 } else {
3510 if !add {
3511 s.clear_disjoint();
3512 }
3513
3514 s.set_pending_anchor_range(start..end, mode);
3515 }
3516 });
3517 }
3518
3519 fn begin_columnar_selection(
3520 &mut self,
3521 position: DisplayPoint,
3522 goal_column: u32,
3523 reset: bool,
3524 mode: ColumnarMode,
3525 window: &mut Window,
3526 cx: &mut Context<Self>,
3527 ) {
3528 if !self.focus_handle.is_focused(window) {
3529 self.last_focused_descendant = None;
3530 window.focus(&self.focus_handle);
3531 }
3532
3533 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
3534
3535 if reset {
3536 let pointer_position = display_map
3537 .buffer_snapshot
3538 .anchor_before(position.to_point(&display_map));
3539
3540 self.change_selections(
3541 SelectionEffects::scroll(Autoscroll::newest()),
3542 window,
3543 cx,
3544 |s| {
3545 s.clear_disjoint();
3546 s.set_pending_anchor_range(
3547 pointer_position..pointer_position,
3548 SelectMode::Character,
3549 );
3550 },
3551 );
3552 };
3553
3554 let tail = self.selections.newest::<Point>(cx).tail();
3555 let selection_anchor = display_map.buffer_snapshot.anchor_before(tail);
3556 self.columnar_selection_state = match mode {
3557 ColumnarMode::FromMouse => Some(ColumnarSelectionState::FromMouse {
3558 selection_tail: selection_anchor,
3559 display_point: if reset {
3560 if position.column() != goal_column {
3561 Some(DisplayPoint::new(position.row(), goal_column))
3562 } else {
3563 None
3564 }
3565 } else {
3566 None
3567 },
3568 }),
3569 ColumnarMode::FromSelection => Some(ColumnarSelectionState::FromSelection {
3570 selection_tail: selection_anchor,
3571 }),
3572 };
3573
3574 if !reset {
3575 self.select_columns(position, goal_column, &display_map, window, cx);
3576 }
3577 }
3578
3579 fn update_selection(
3580 &mut self,
3581 position: DisplayPoint,
3582 goal_column: u32,
3583 scroll_delta: gpui::Point<f32>,
3584 window: &mut Window,
3585 cx: &mut Context<Self>,
3586 ) {
3587 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
3588
3589 if self.columnar_selection_state.is_some() {
3590 self.select_columns(position, goal_column, &display_map, window, cx);
3591 } else if let Some(mut pending) = self.selections.pending_anchor() {
3592 let buffer = &display_map.buffer_snapshot;
3593 let head;
3594 let tail;
3595 let mode = self.selections.pending_mode().unwrap();
3596 match &mode {
3597 SelectMode::Character => {
3598 head = position.to_point(&display_map);
3599 tail = pending.tail().to_point(buffer);
3600 }
3601 SelectMode::Word(original_range) => {
3602 let offset = display_map
3603 .clip_point(position, Bias::Left)
3604 .to_offset(&display_map, Bias::Left);
3605 let original_range = original_range.to_offset(buffer);
3606
3607 let head_offset = if buffer.is_inside_word(offset, false)
3608 || original_range.contains(&offset)
3609 {
3610 let (word_range, _) = buffer.surrounding_word(offset, false);
3611 if word_range.start < original_range.start {
3612 word_range.start
3613 } else {
3614 word_range.end
3615 }
3616 } else {
3617 offset
3618 };
3619
3620 head = head_offset.to_point(buffer);
3621 if head_offset <= original_range.start {
3622 tail = original_range.end.to_point(buffer);
3623 } else {
3624 tail = original_range.start.to_point(buffer);
3625 }
3626 }
3627 SelectMode::Line(original_range) => {
3628 let original_range = original_range.to_point(&display_map.buffer_snapshot);
3629
3630 let position = display_map
3631 .clip_point(position, Bias::Left)
3632 .to_point(&display_map);
3633 let line_start = display_map.prev_line_boundary(position).0;
3634 let next_line_start = buffer.clip_point(
3635 display_map.next_line_boundary(position).0 + Point::new(1, 0),
3636 Bias::Left,
3637 );
3638
3639 if line_start < original_range.start {
3640 head = line_start
3641 } else {
3642 head = next_line_start
3643 }
3644
3645 if head <= original_range.start {
3646 tail = original_range.end;
3647 } else {
3648 tail = original_range.start;
3649 }
3650 }
3651 SelectMode::All => {
3652 return;
3653 }
3654 };
3655
3656 if head < tail {
3657 pending.start = buffer.anchor_before(head);
3658 pending.end = buffer.anchor_before(tail);
3659 pending.reversed = true;
3660 } else {
3661 pending.start = buffer.anchor_before(tail);
3662 pending.end = buffer.anchor_before(head);
3663 pending.reversed = false;
3664 }
3665
3666 self.change_selections(SelectionEffects::no_scroll(), window, cx, |s| {
3667 s.set_pending(pending, mode);
3668 });
3669 } else {
3670 log::error!("update_selection dispatched with no pending selection");
3671 return;
3672 }
3673
3674 self.apply_scroll_delta(scroll_delta, window, cx);
3675 cx.notify();
3676 }
3677
3678 fn end_selection(&mut self, window: &mut Window, cx: &mut Context<Self>) {
3679 self.columnar_selection_state.take();
3680 if self.selections.pending_anchor().is_some() {
3681 let selections = self.selections.all::<usize>(cx);
3682 self.change_selections(SelectionEffects::no_scroll(), window, cx, |s| {
3683 s.select(selections);
3684 s.clear_pending();
3685 });
3686 }
3687 }
3688
3689 fn select_columns(
3690 &mut self,
3691 head: DisplayPoint,
3692 goal_column: u32,
3693 display_map: &DisplaySnapshot,
3694 window: &mut Window,
3695 cx: &mut Context<Self>,
3696 ) {
3697 let Some(columnar_state) = self.columnar_selection_state.as_ref() else {
3698 return;
3699 };
3700
3701 let tail = match columnar_state {
3702 ColumnarSelectionState::FromMouse {
3703 selection_tail,
3704 display_point,
3705 } => display_point.unwrap_or_else(|| selection_tail.to_display_point(&display_map)),
3706 ColumnarSelectionState::FromSelection { selection_tail } => {
3707 selection_tail.to_display_point(&display_map)
3708 }
3709 };
3710
3711 let start_row = cmp::min(tail.row(), head.row());
3712 let end_row = cmp::max(tail.row(), head.row());
3713 let start_column = cmp::min(tail.column(), goal_column);
3714 let end_column = cmp::max(tail.column(), goal_column);
3715 let reversed = start_column < tail.column();
3716
3717 let selection_ranges = (start_row.0..=end_row.0)
3718 .map(DisplayRow)
3719 .filter_map(|row| {
3720 if (matches!(columnar_state, ColumnarSelectionState::FromMouse { .. })
3721 || start_column <= display_map.line_len(row))
3722 && !display_map.is_block_line(row)
3723 {
3724 let start = display_map
3725 .clip_point(DisplayPoint::new(row, start_column), Bias::Left)
3726 .to_point(display_map);
3727 let end = display_map
3728 .clip_point(DisplayPoint::new(row, end_column), Bias::Right)
3729 .to_point(display_map);
3730 if reversed {
3731 Some(end..start)
3732 } else {
3733 Some(start..end)
3734 }
3735 } else {
3736 None
3737 }
3738 })
3739 .collect::<Vec<_>>();
3740
3741 let ranges = match columnar_state {
3742 ColumnarSelectionState::FromMouse { .. } => {
3743 let mut non_empty_ranges = selection_ranges
3744 .iter()
3745 .filter(|selection_range| selection_range.start != selection_range.end)
3746 .peekable();
3747 if non_empty_ranges.peek().is_some() {
3748 non_empty_ranges.cloned().collect()
3749 } else {
3750 selection_ranges
3751 }
3752 }
3753 _ => selection_ranges,
3754 };
3755
3756 self.change_selections(SelectionEffects::no_scroll(), window, cx, |s| {
3757 s.select_ranges(ranges);
3758 });
3759 cx.notify();
3760 }
3761
3762 pub fn has_non_empty_selection(&self, cx: &mut App) -> bool {
3763 self.selections
3764 .all_adjusted(cx)
3765 .iter()
3766 .any(|selection| !selection.is_empty())
3767 }
3768
3769 pub fn has_pending_nonempty_selection(&self) -> bool {
3770 let pending_nonempty_selection = match self.selections.pending_anchor() {
3771 Some(Selection { start, end, .. }) => start != end,
3772 None => false,
3773 };
3774
3775 pending_nonempty_selection
3776 || (self.columnar_selection_state.is_some() && self.selections.disjoint.len() > 1)
3777 }
3778
3779 pub fn has_pending_selection(&self) -> bool {
3780 self.selections.pending_anchor().is_some() || self.columnar_selection_state.is_some()
3781 }
3782
3783 pub fn cancel(&mut self, _: &Cancel, window: &mut Window, cx: &mut Context<Self>) {
3784 self.selection_mark_mode = false;
3785 self.selection_drag_state = SelectionDragState::None;
3786
3787 if self.clear_expanded_diff_hunks(cx) {
3788 cx.notify();
3789 return;
3790 }
3791 if self.dismiss_menus_and_popups(true, window, cx) {
3792 return;
3793 }
3794
3795 if self.mode.is_full()
3796 && self.change_selections(Default::default(), window, cx, |s| s.try_cancel())
3797 {
3798 return;
3799 }
3800
3801 cx.propagate();
3802 }
3803
3804 pub fn dismiss_menus_and_popups(
3805 &mut self,
3806 is_user_requested: bool,
3807 window: &mut Window,
3808 cx: &mut Context<Self>,
3809 ) -> bool {
3810 if self.take_rename(false, window, cx).is_some() {
3811 return true;
3812 }
3813
3814 if hide_hover(self, cx) {
3815 return true;
3816 }
3817
3818 if self.hide_signature_help(cx, SignatureHelpHiddenBy::Escape) {
3819 return true;
3820 }
3821
3822 if self.hide_context_menu(window, cx).is_some() {
3823 return true;
3824 }
3825
3826 if self.mouse_context_menu.take().is_some() {
3827 return true;
3828 }
3829
3830 if is_user_requested && self.discard_inline_completion(true, cx) {
3831 return true;
3832 }
3833
3834 if self.snippet_stack.pop().is_some() {
3835 return true;
3836 }
3837
3838 if self.mode.is_full() && matches!(self.active_diagnostics, ActiveDiagnostic::Group(_)) {
3839 self.dismiss_diagnostics(cx);
3840 return true;
3841 }
3842
3843 false
3844 }
3845
3846 fn linked_editing_ranges_for(
3847 &self,
3848 selection: Range<text::Anchor>,
3849 cx: &App,
3850 ) -> Option<HashMap<Entity<Buffer>, Vec<Range<text::Anchor>>>> {
3851 if self.linked_edit_ranges.is_empty() {
3852 return None;
3853 }
3854 let ((base_range, linked_ranges), buffer_snapshot, buffer) =
3855 selection.end.buffer_id.and_then(|end_buffer_id| {
3856 if selection.start.buffer_id != Some(end_buffer_id) {
3857 return None;
3858 }
3859 let buffer = self.buffer.read(cx).buffer(end_buffer_id)?;
3860 let snapshot = buffer.read(cx).snapshot();
3861 self.linked_edit_ranges
3862 .get(end_buffer_id, selection.start..selection.end, &snapshot)
3863 .map(|ranges| (ranges, snapshot, buffer))
3864 })?;
3865 use text::ToOffset as TO;
3866 // find offset from the start of current range to current cursor position
3867 let start_byte_offset = TO::to_offset(&base_range.start, &buffer_snapshot);
3868
3869 let start_offset = TO::to_offset(&selection.start, &buffer_snapshot);
3870 let start_difference = start_offset - start_byte_offset;
3871 let end_offset = TO::to_offset(&selection.end, &buffer_snapshot);
3872 let end_difference = end_offset - start_byte_offset;
3873 // Current range has associated linked ranges.
3874 let mut linked_edits = HashMap::<_, Vec<_>>::default();
3875 for range in linked_ranges.iter() {
3876 let start_offset = TO::to_offset(&range.start, &buffer_snapshot);
3877 let end_offset = start_offset + end_difference;
3878 let start_offset = start_offset + start_difference;
3879 if start_offset > buffer_snapshot.len() || end_offset > buffer_snapshot.len() {
3880 continue;
3881 }
3882 if self.selections.disjoint_anchor_ranges().any(|s| {
3883 if s.start.buffer_id != selection.start.buffer_id
3884 || s.end.buffer_id != selection.end.buffer_id
3885 {
3886 return false;
3887 }
3888 TO::to_offset(&s.start.text_anchor, &buffer_snapshot) <= end_offset
3889 && TO::to_offset(&s.end.text_anchor, &buffer_snapshot) >= start_offset
3890 }) {
3891 continue;
3892 }
3893 let start = buffer_snapshot.anchor_after(start_offset);
3894 let end = buffer_snapshot.anchor_after(end_offset);
3895 linked_edits
3896 .entry(buffer.clone())
3897 .or_default()
3898 .push(start..end);
3899 }
3900 Some(linked_edits)
3901 }
3902
3903 pub fn handle_input(&mut self, text: &str, window: &mut Window, cx: &mut Context<Self>) {
3904 let text: Arc<str> = text.into();
3905
3906 if self.read_only(cx) {
3907 return;
3908 }
3909
3910 self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
3911
3912 let selections = self.selections.all_adjusted(cx);
3913 let mut bracket_inserted = false;
3914 let mut edits = Vec::new();
3915 let mut linked_edits = HashMap::<_, Vec<_>>::default();
3916 let mut new_selections = Vec::with_capacity(selections.len());
3917 let mut new_autoclose_regions = Vec::new();
3918 let snapshot = self.buffer.read(cx).read(cx);
3919 let mut clear_linked_edit_ranges = false;
3920
3921 for (selection, autoclose_region) in
3922 self.selections_with_autoclose_regions(selections, &snapshot)
3923 {
3924 if let Some(scope) = snapshot.language_scope_at(selection.head()) {
3925 // Determine if the inserted text matches the opening or closing
3926 // bracket of any of this language's bracket pairs.
3927 let mut bracket_pair = None;
3928 let mut is_bracket_pair_start = false;
3929 let mut is_bracket_pair_end = false;
3930 if !text.is_empty() {
3931 let mut bracket_pair_matching_end = None;
3932 // `text` can be empty when a user is using IME (e.g. Chinese Wubi Simplified)
3933 // and they are removing the character that triggered IME popup.
3934 for (pair, enabled) in scope.brackets() {
3935 if !pair.close && !pair.surround {
3936 continue;
3937 }
3938
3939 if enabled && pair.start.ends_with(text.as_ref()) {
3940 let prefix_len = pair.start.len() - text.len();
3941 let preceding_text_matches_prefix = prefix_len == 0
3942 || (selection.start.column >= (prefix_len as u32)
3943 && snapshot.contains_str_at(
3944 Point::new(
3945 selection.start.row,
3946 selection.start.column - (prefix_len as u32),
3947 ),
3948 &pair.start[..prefix_len],
3949 ));
3950 if preceding_text_matches_prefix {
3951 bracket_pair = Some(pair.clone());
3952 is_bracket_pair_start = true;
3953 break;
3954 }
3955 }
3956 if pair.end.as_str() == text.as_ref() && bracket_pair_matching_end.is_none()
3957 {
3958 // take first bracket pair matching end, but don't break in case a later bracket
3959 // pair matches start
3960 bracket_pair_matching_end = Some(pair.clone());
3961 }
3962 }
3963 if let Some(end) = bracket_pair_matching_end
3964 && bracket_pair.is_none()
3965 {
3966 bracket_pair = Some(end);
3967 is_bracket_pair_end = true;
3968 }
3969 }
3970
3971 if let Some(bracket_pair) = bracket_pair {
3972 let snapshot_settings = snapshot.language_settings_at(selection.start, cx);
3973 let autoclose = self.use_autoclose && snapshot_settings.use_autoclose;
3974 let auto_surround =
3975 self.use_auto_surround && snapshot_settings.use_auto_surround;
3976 if selection.is_empty() {
3977 if is_bracket_pair_start {
3978 // If the inserted text is a suffix of an opening bracket and the
3979 // selection is preceded by the rest of the opening bracket, then
3980 // insert the closing bracket.
3981 let following_text_allows_autoclose = snapshot
3982 .chars_at(selection.start)
3983 .next()
3984 .map_or(true, |c| scope.should_autoclose_before(c));
3985
3986 let preceding_text_allows_autoclose = selection.start.column == 0
3987 || snapshot.reversed_chars_at(selection.start).next().map_or(
3988 true,
3989 |c| {
3990 bracket_pair.start != bracket_pair.end
3991 || !snapshot
3992 .char_classifier_at(selection.start)
3993 .is_word(c)
3994 },
3995 );
3996
3997 let is_closing_quote = if bracket_pair.end == bracket_pair.start
3998 && bracket_pair.start.len() == 1
3999 {
4000 let target = bracket_pair.start.chars().next().unwrap();
4001 let current_line_count = snapshot
4002 .reversed_chars_at(selection.start)
4003 .take_while(|&c| c != '\n')
4004 .filter(|&c| c == target)
4005 .count();
4006 current_line_count % 2 == 1
4007 } else {
4008 false
4009 };
4010
4011 if autoclose
4012 && bracket_pair.close
4013 && following_text_allows_autoclose
4014 && preceding_text_allows_autoclose
4015 && !is_closing_quote
4016 {
4017 let anchor = snapshot.anchor_before(selection.end);
4018 new_selections.push((selection.map(|_| anchor), text.len()));
4019 new_autoclose_regions.push((
4020 anchor,
4021 text.len(),
4022 selection.id,
4023 bracket_pair.clone(),
4024 ));
4025 edits.push((
4026 selection.range(),
4027 format!("{}{}", text, bracket_pair.end).into(),
4028 ));
4029 bracket_inserted = true;
4030 continue;
4031 }
4032 }
4033
4034 if let Some(region) = autoclose_region {
4035 // If the selection is followed by an auto-inserted closing bracket,
4036 // then don't insert that closing bracket again; just move the selection
4037 // past the closing bracket.
4038 let should_skip = selection.end == region.range.end.to_point(&snapshot)
4039 && text.as_ref() == region.pair.end.as_str();
4040 if should_skip {
4041 let anchor = snapshot.anchor_after(selection.end);
4042 new_selections
4043 .push((selection.map(|_| anchor), region.pair.end.len()));
4044 continue;
4045 }
4046 }
4047
4048 let always_treat_brackets_as_autoclosed = snapshot
4049 .language_settings_at(selection.start, cx)
4050 .always_treat_brackets_as_autoclosed;
4051 if always_treat_brackets_as_autoclosed
4052 && is_bracket_pair_end
4053 && snapshot.contains_str_at(selection.end, text.as_ref())
4054 {
4055 // Otherwise, when `always_treat_brackets_as_autoclosed` is set to `true
4056 // and the inserted text is a closing bracket and the selection is followed
4057 // by the closing bracket then move the selection past the closing bracket.
4058 let anchor = snapshot.anchor_after(selection.end);
4059 new_selections.push((selection.map(|_| anchor), text.len()));
4060 continue;
4061 }
4062 }
4063 // If an opening bracket is 1 character long and is typed while
4064 // text is selected, then surround that text with the bracket pair.
4065 else if auto_surround
4066 && bracket_pair.surround
4067 && is_bracket_pair_start
4068 && bracket_pair.start.chars().count() == 1
4069 {
4070 edits.push((selection.start..selection.start, text.clone()));
4071 edits.push((
4072 selection.end..selection.end,
4073 bracket_pair.end.as_str().into(),
4074 ));
4075 bracket_inserted = true;
4076 new_selections.push((
4077 Selection {
4078 id: selection.id,
4079 start: snapshot.anchor_after(selection.start),
4080 end: snapshot.anchor_before(selection.end),
4081 reversed: selection.reversed,
4082 goal: selection.goal,
4083 },
4084 0,
4085 ));
4086 continue;
4087 }
4088 }
4089 }
4090
4091 if self.auto_replace_emoji_shortcode
4092 && selection.is_empty()
4093 && text.as_ref().ends_with(':')
4094 {
4095 if let Some(possible_emoji_short_code) =
4096 Self::find_possible_emoji_shortcode_at_position(&snapshot, selection.start)
4097 {
4098 if !possible_emoji_short_code.is_empty() {
4099 if let Some(emoji) = emojis::get_by_shortcode(&possible_emoji_short_code) {
4100 let emoji_shortcode_start = Point::new(
4101 selection.start.row,
4102 selection.start.column - possible_emoji_short_code.len() as u32 - 1,
4103 );
4104
4105 // Remove shortcode from buffer
4106 edits.push((
4107 emoji_shortcode_start..selection.start,
4108 "".to_string().into(),
4109 ));
4110 new_selections.push((
4111 Selection {
4112 id: selection.id,
4113 start: snapshot.anchor_after(emoji_shortcode_start),
4114 end: snapshot.anchor_before(selection.start),
4115 reversed: selection.reversed,
4116 goal: selection.goal,
4117 },
4118 0,
4119 ));
4120
4121 // Insert emoji
4122 let selection_start_anchor = snapshot.anchor_after(selection.start);
4123 new_selections.push((selection.map(|_| selection_start_anchor), 0));
4124 edits.push((selection.start..selection.end, emoji.to_string().into()));
4125
4126 continue;
4127 }
4128 }
4129 }
4130 }
4131
4132 // If not handling any auto-close operation, then just replace the selected
4133 // text with the given input and move the selection to the end of the
4134 // newly inserted text.
4135 let anchor = snapshot.anchor_after(selection.end);
4136 if !self.linked_edit_ranges.is_empty() {
4137 let start_anchor = snapshot.anchor_before(selection.start);
4138
4139 let is_word_char = text.chars().next().map_or(true, |char| {
4140 let classifier = snapshot
4141 .char_classifier_at(start_anchor.to_offset(&snapshot))
4142 .ignore_punctuation(true);
4143 classifier.is_word(char)
4144 });
4145
4146 if is_word_char {
4147 if let Some(ranges) = self
4148 .linked_editing_ranges_for(start_anchor.text_anchor..anchor.text_anchor, cx)
4149 {
4150 for (buffer, edits) in ranges {
4151 linked_edits
4152 .entry(buffer.clone())
4153 .or_default()
4154 .extend(edits.into_iter().map(|range| (range, text.clone())));
4155 }
4156 }
4157 } else {
4158 clear_linked_edit_ranges = true;
4159 }
4160 }
4161
4162 new_selections.push((selection.map(|_| anchor), 0));
4163 edits.push((selection.start..selection.end, text.clone()));
4164 }
4165
4166 drop(snapshot);
4167
4168 self.transact(window, cx, |this, window, cx| {
4169 if clear_linked_edit_ranges {
4170 this.linked_edit_ranges.clear();
4171 }
4172 let initial_buffer_versions =
4173 jsx_tag_auto_close::construct_initial_buffer_versions_map(this, &edits, cx);
4174
4175 this.buffer.update(cx, |buffer, cx| {
4176 buffer.edit(edits, this.autoindent_mode.clone(), cx);
4177 });
4178 for (buffer, edits) in linked_edits {
4179 buffer.update(cx, |buffer, cx| {
4180 let snapshot = buffer.snapshot();
4181 let edits = edits
4182 .into_iter()
4183 .map(|(range, text)| {
4184 use text::ToPoint as TP;
4185 let end_point = TP::to_point(&range.end, &snapshot);
4186 let start_point = TP::to_point(&range.start, &snapshot);
4187 (start_point..end_point, text)
4188 })
4189 .sorted_by_key(|(range, _)| range.start);
4190 buffer.edit(edits, None, cx);
4191 })
4192 }
4193 let new_anchor_selections = new_selections.iter().map(|e| &e.0);
4194 let new_selection_deltas = new_selections.iter().map(|e| e.1);
4195 let map = this.display_map.update(cx, |map, cx| map.snapshot(cx));
4196 let new_selections = resolve_selections::<usize, _>(new_anchor_selections, &map)
4197 .zip(new_selection_deltas)
4198 .map(|(selection, delta)| Selection {
4199 id: selection.id,
4200 start: selection.start + delta,
4201 end: selection.end + delta,
4202 reversed: selection.reversed,
4203 goal: SelectionGoal::None,
4204 })
4205 .collect::<Vec<_>>();
4206
4207 let mut i = 0;
4208 for (position, delta, selection_id, pair) in new_autoclose_regions {
4209 let position = position.to_offset(&map.buffer_snapshot) + delta;
4210 let start = map.buffer_snapshot.anchor_before(position);
4211 let end = map.buffer_snapshot.anchor_after(position);
4212 while let Some(existing_state) = this.autoclose_regions.get(i) {
4213 match existing_state.range.start.cmp(&start, &map.buffer_snapshot) {
4214 Ordering::Less => i += 1,
4215 Ordering::Greater => break,
4216 Ordering::Equal => {
4217 match end.cmp(&existing_state.range.end, &map.buffer_snapshot) {
4218 Ordering::Less => i += 1,
4219 Ordering::Equal => break,
4220 Ordering::Greater => break,
4221 }
4222 }
4223 }
4224 }
4225 this.autoclose_regions.insert(
4226 i,
4227 AutocloseRegion {
4228 selection_id,
4229 range: start..end,
4230 pair,
4231 },
4232 );
4233 }
4234
4235 let had_active_inline_completion = this.has_active_inline_completion();
4236 this.change_selections(
4237 SelectionEffects::scroll(Autoscroll::fit()).completions(false),
4238 window,
4239 cx,
4240 |s| s.select(new_selections),
4241 );
4242
4243 if !bracket_inserted {
4244 if let Some(on_type_format_task) =
4245 this.trigger_on_type_formatting(text.to_string(), window, cx)
4246 {
4247 on_type_format_task.detach_and_log_err(cx);
4248 }
4249 }
4250
4251 let editor_settings = EditorSettings::get_global(cx);
4252 if bracket_inserted
4253 && (editor_settings.auto_signature_help
4254 || editor_settings.show_signature_help_after_edits)
4255 {
4256 this.show_signature_help(&ShowSignatureHelp, window, cx);
4257 }
4258
4259 let trigger_in_words =
4260 this.show_edit_predictions_in_menu() || !had_active_inline_completion;
4261 if this.hard_wrap.is_some() {
4262 let latest: Range<Point> = this.selections.newest(cx).range();
4263 if latest.is_empty()
4264 && this
4265 .buffer()
4266 .read(cx)
4267 .snapshot(cx)
4268 .line_len(MultiBufferRow(latest.start.row))
4269 == latest.start.column
4270 {
4271 this.rewrap_impl(
4272 RewrapOptions {
4273 override_language_settings: true,
4274 preserve_existing_whitespace: true,
4275 },
4276 cx,
4277 )
4278 }
4279 }
4280 this.trigger_completion_on_input(&text, trigger_in_words, window, cx);
4281 linked_editing_ranges::refresh_linked_ranges(this, window, cx);
4282 this.refresh_inline_completion(true, false, window, cx);
4283 jsx_tag_auto_close::handle_from(this, initial_buffer_versions, window, cx);
4284 });
4285 }
4286
4287 fn find_possible_emoji_shortcode_at_position(
4288 snapshot: &MultiBufferSnapshot,
4289 position: Point,
4290 ) -> Option<String> {
4291 let mut chars = Vec::new();
4292 let mut found_colon = false;
4293 for char in snapshot.reversed_chars_at(position).take(100) {
4294 // Found a possible emoji shortcode in the middle of the buffer
4295 if found_colon {
4296 if char.is_whitespace() {
4297 chars.reverse();
4298 return Some(chars.iter().collect());
4299 }
4300 // If the previous character is not a whitespace, we are in the middle of a word
4301 // and we only want to complete the shortcode if the word is made up of other emojis
4302 let mut containing_word = String::new();
4303 for ch in snapshot
4304 .reversed_chars_at(position)
4305 .skip(chars.len() + 1)
4306 .take(100)
4307 {
4308 if ch.is_whitespace() {
4309 break;
4310 }
4311 containing_word.push(ch);
4312 }
4313 let containing_word = containing_word.chars().rev().collect::<String>();
4314 if util::word_consists_of_emojis(containing_word.as_str()) {
4315 chars.reverse();
4316 return Some(chars.iter().collect());
4317 }
4318 }
4319
4320 if char.is_whitespace() || !char.is_ascii() {
4321 return None;
4322 }
4323 if char == ':' {
4324 found_colon = true;
4325 } else {
4326 chars.push(char);
4327 }
4328 }
4329 // Found a possible emoji shortcode at the beginning of the buffer
4330 chars.reverse();
4331 Some(chars.iter().collect())
4332 }
4333
4334 pub fn newline(&mut self, _: &Newline, window: &mut Window, cx: &mut Context<Self>) {
4335 self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
4336 self.transact(window, cx, |this, window, cx| {
4337 let (edits_with_flags, selection_info): (Vec<_>, Vec<_>) = {
4338 let selections = this.selections.all::<usize>(cx);
4339 let multi_buffer = this.buffer.read(cx);
4340 let buffer = multi_buffer.snapshot(cx);
4341 selections
4342 .iter()
4343 .map(|selection| {
4344 let start_point = selection.start.to_point(&buffer);
4345 let mut existing_indent =
4346 buffer.indent_size_for_line(MultiBufferRow(start_point.row));
4347 existing_indent.len = cmp::min(existing_indent.len, start_point.column);
4348 let start = selection.start;
4349 let end = selection.end;
4350 let selection_is_empty = start == end;
4351 let language_scope = buffer.language_scope_at(start);
4352 let (
4353 comment_delimiter,
4354 doc_delimiter,
4355 insert_extra_newline,
4356 indent_on_newline,
4357 indent_on_extra_newline,
4358 ) = if let Some(language) = &language_scope {
4359 let mut insert_extra_newline =
4360 insert_extra_newline_brackets(&buffer, start..end, language)
4361 || insert_extra_newline_tree_sitter(&buffer, start..end);
4362
4363 // Comment extension on newline is allowed only for cursor selections
4364 let comment_delimiter = maybe!({
4365 if !selection_is_empty {
4366 return None;
4367 }
4368
4369 if !multi_buffer.language_settings(cx).extend_comment_on_newline {
4370 return None;
4371 }
4372
4373 let delimiters = language.line_comment_prefixes();
4374 let max_len_of_delimiter =
4375 delimiters.iter().map(|delimiter| delimiter.len()).max()?;
4376 let (snapshot, range) =
4377 buffer.buffer_line_for_row(MultiBufferRow(start_point.row))?;
4378
4379 let num_of_whitespaces = snapshot
4380 .chars_for_range(range.clone())
4381 .take_while(|c| c.is_whitespace())
4382 .count();
4383 let comment_candidate = snapshot
4384 .chars_for_range(range)
4385 .skip(num_of_whitespaces)
4386 .take(max_len_of_delimiter)
4387 .collect::<String>();
4388 let (delimiter, trimmed_len) = delimiters
4389 .iter()
4390 .filter_map(|delimiter| {
4391 let prefix = delimiter.trim_end();
4392 if comment_candidate.starts_with(prefix) {
4393 Some((delimiter, prefix.len()))
4394 } else {
4395 None
4396 }
4397 })
4398 .max_by_key(|(_, len)| *len)?;
4399
4400 let cursor_is_placed_after_comment_marker =
4401 num_of_whitespaces + trimmed_len <= start_point.column as usize;
4402 if cursor_is_placed_after_comment_marker {
4403 Some(delimiter.clone())
4404 } else {
4405 None
4406 }
4407 });
4408
4409 let mut indent_on_newline = IndentSize::spaces(0);
4410 let mut indent_on_extra_newline = IndentSize::spaces(0);
4411
4412 let doc_delimiter = maybe!({
4413 if !selection_is_empty {
4414 return None;
4415 }
4416
4417 if !multi_buffer.language_settings(cx).extend_comment_on_newline {
4418 return None;
4419 }
4420
4421 let DocumentationConfig {
4422 start: start_tag,
4423 end: end_tag,
4424 prefix: delimiter,
4425 tab_size: len,
4426 } = language.documentation()?;
4427
4428 let is_within_block_comment = buffer
4429 .language_scope_at(start_point)
4430 .is_some_and(|scope| scope.override_name() == Some("comment"));
4431 if !is_within_block_comment {
4432 return None;
4433 }
4434
4435 let (snapshot, range) =
4436 buffer.buffer_line_for_row(MultiBufferRow(start_point.row))?;
4437
4438 let num_of_whitespaces = snapshot
4439 .chars_for_range(range.clone())
4440 .take_while(|c| c.is_whitespace())
4441 .count();
4442
4443 // 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.
4444 let column = start_point.column;
4445 let cursor_is_after_start_tag = {
4446 let start_tag_len = start_tag.len();
4447 let start_tag_line = snapshot
4448 .chars_for_range(range.clone())
4449 .skip(num_of_whitespaces)
4450 .take(start_tag_len)
4451 .collect::<String>();
4452 if start_tag_line.starts_with(start_tag.as_ref()) {
4453 num_of_whitespaces + start_tag_len <= column as usize
4454 } else {
4455 false
4456 }
4457 };
4458
4459 let cursor_is_after_delimiter = {
4460 let delimiter_trim = delimiter.trim_end();
4461 let delimiter_line = snapshot
4462 .chars_for_range(range.clone())
4463 .skip(num_of_whitespaces)
4464 .take(delimiter_trim.len())
4465 .collect::<String>();
4466 if delimiter_line.starts_with(delimiter_trim) {
4467 num_of_whitespaces + delimiter_trim.len() <= column as usize
4468 } else {
4469 false
4470 }
4471 };
4472
4473 let cursor_is_before_end_tag_if_exists = {
4474 let mut char_position = 0u32;
4475 let mut end_tag_offset = None;
4476
4477 'outer: for chunk in snapshot.text_for_range(range.clone()) {
4478 if let Some(byte_pos) = chunk.find(&**end_tag) {
4479 let chars_before_match =
4480 chunk[..byte_pos].chars().count() as u32;
4481 end_tag_offset =
4482 Some(char_position + chars_before_match);
4483 break 'outer;
4484 }
4485 char_position += chunk.chars().count() as u32;
4486 }
4487
4488 if let Some(end_tag_offset) = end_tag_offset {
4489 let cursor_is_before_end_tag = column <= end_tag_offset;
4490 if cursor_is_after_start_tag {
4491 if cursor_is_before_end_tag {
4492 insert_extra_newline = true;
4493 }
4494 let cursor_is_at_start_of_end_tag =
4495 column == end_tag_offset;
4496 if cursor_is_at_start_of_end_tag {
4497 indent_on_extra_newline.len = (*len).into();
4498 }
4499 }
4500 cursor_is_before_end_tag
4501 } else {
4502 true
4503 }
4504 };
4505
4506 if (cursor_is_after_start_tag || cursor_is_after_delimiter)
4507 && cursor_is_before_end_tag_if_exists
4508 {
4509 if cursor_is_after_start_tag {
4510 indent_on_newline.len = (*len).into();
4511 }
4512 Some(delimiter.clone())
4513 } else {
4514 None
4515 }
4516 });
4517
4518 (
4519 comment_delimiter,
4520 doc_delimiter,
4521 insert_extra_newline,
4522 indent_on_newline,
4523 indent_on_extra_newline,
4524 )
4525 } else {
4526 (
4527 None,
4528 None,
4529 false,
4530 IndentSize::default(),
4531 IndentSize::default(),
4532 )
4533 };
4534
4535 let prevent_auto_indent = doc_delimiter.is_some();
4536 let delimiter = comment_delimiter.or(doc_delimiter);
4537
4538 let capacity_for_delimiter =
4539 delimiter.as_deref().map(str::len).unwrap_or_default();
4540 let mut new_text = String::with_capacity(
4541 1 + capacity_for_delimiter
4542 + existing_indent.len as usize
4543 + indent_on_newline.len as usize
4544 + indent_on_extra_newline.len as usize,
4545 );
4546 new_text.push('\n');
4547 new_text.extend(existing_indent.chars());
4548 new_text.extend(indent_on_newline.chars());
4549
4550 if let Some(delimiter) = &delimiter {
4551 new_text.push_str(delimiter);
4552 }
4553
4554 if insert_extra_newline {
4555 new_text.push('\n');
4556 new_text.extend(existing_indent.chars());
4557 new_text.extend(indent_on_extra_newline.chars());
4558 }
4559
4560 let anchor = buffer.anchor_after(end);
4561 let new_selection = selection.map(|_| anchor);
4562 (
4563 ((start..end, new_text), prevent_auto_indent),
4564 (insert_extra_newline, new_selection),
4565 )
4566 })
4567 .unzip()
4568 };
4569
4570 let mut auto_indent_edits = Vec::new();
4571 let mut edits = Vec::new();
4572 for (edit, prevent_auto_indent) in edits_with_flags {
4573 if prevent_auto_indent {
4574 edits.push(edit);
4575 } else {
4576 auto_indent_edits.push(edit);
4577 }
4578 }
4579 if !edits.is_empty() {
4580 this.edit(edits, cx);
4581 }
4582 if !auto_indent_edits.is_empty() {
4583 this.edit_with_autoindent(auto_indent_edits, cx);
4584 }
4585
4586 let buffer = this.buffer.read(cx).snapshot(cx);
4587 let new_selections = selection_info
4588 .into_iter()
4589 .map(|(extra_newline_inserted, new_selection)| {
4590 let mut cursor = new_selection.end.to_point(&buffer);
4591 if extra_newline_inserted {
4592 cursor.row -= 1;
4593 cursor.column = buffer.line_len(MultiBufferRow(cursor.row));
4594 }
4595 new_selection.map(|_| cursor)
4596 })
4597 .collect();
4598
4599 this.change_selections(Default::default(), window, cx, |s| s.select(new_selections));
4600 this.refresh_inline_completion(true, false, window, cx);
4601 });
4602 }
4603
4604 pub fn newline_above(&mut self, _: &NewlineAbove, window: &mut Window, cx: &mut Context<Self>) {
4605 self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
4606
4607 let buffer = self.buffer.read(cx);
4608 let snapshot = buffer.snapshot(cx);
4609
4610 let mut edits = Vec::new();
4611 let mut rows = Vec::new();
4612
4613 for (rows_inserted, selection) in self.selections.all_adjusted(cx).into_iter().enumerate() {
4614 let cursor = selection.head();
4615 let row = cursor.row;
4616
4617 let start_of_line = snapshot.clip_point(Point::new(row, 0), Bias::Left);
4618
4619 let newline = "\n".to_string();
4620 edits.push((start_of_line..start_of_line, newline));
4621
4622 rows.push(row + rows_inserted as u32);
4623 }
4624
4625 self.transact(window, cx, |editor, window, cx| {
4626 editor.edit(edits, cx);
4627
4628 editor.change_selections(Default::default(), window, cx, |s| {
4629 let mut index = 0;
4630 s.move_cursors_with(|map, _, _| {
4631 let row = rows[index];
4632 index += 1;
4633
4634 let point = Point::new(row, 0);
4635 let boundary = map.next_line_boundary(point).1;
4636 let clipped = map.clip_point(boundary, Bias::Left);
4637
4638 (clipped, SelectionGoal::None)
4639 });
4640 });
4641
4642 let mut indent_edits = Vec::new();
4643 let multibuffer_snapshot = editor.buffer.read(cx).snapshot(cx);
4644 for row in rows {
4645 let indents = multibuffer_snapshot.suggested_indents(row..row + 1, cx);
4646 for (row, indent) in indents {
4647 if indent.len == 0 {
4648 continue;
4649 }
4650
4651 let text = match indent.kind {
4652 IndentKind::Space => " ".repeat(indent.len as usize),
4653 IndentKind::Tab => "\t".repeat(indent.len as usize),
4654 };
4655 let point = Point::new(row.0, 0);
4656 indent_edits.push((point..point, text));
4657 }
4658 }
4659 editor.edit(indent_edits, cx);
4660 });
4661 }
4662
4663 pub fn newline_below(&mut self, _: &NewlineBelow, window: &mut Window, cx: &mut Context<Self>) {
4664 self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
4665
4666 let buffer = self.buffer.read(cx);
4667 let snapshot = buffer.snapshot(cx);
4668
4669 let mut edits = Vec::new();
4670 let mut rows = Vec::new();
4671 let mut rows_inserted = 0;
4672
4673 for selection in self.selections.all_adjusted(cx) {
4674 let cursor = selection.head();
4675 let row = cursor.row;
4676
4677 let point = Point::new(row + 1, 0);
4678 let start_of_line = snapshot.clip_point(point, Bias::Left);
4679
4680 let newline = "\n".to_string();
4681 edits.push((start_of_line..start_of_line, newline));
4682
4683 rows_inserted += 1;
4684 rows.push(row + rows_inserted);
4685 }
4686
4687 self.transact(window, cx, |editor, window, cx| {
4688 editor.edit(edits, cx);
4689
4690 editor.change_selections(Default::default(), window, cx, |s| {
4691 let mut index = 0;
4692 s.move_cursors_with(|map, _, _| {
4693 let row = rows[index];
4694 index += 1;
4695
4696 let point = Point::new(row, 0);
4697 let boundary = map.next_line_boundary(point).1;
4698 let clipped = map.clip_point(boundary, Bias::Left);
4699
4700 (clipped, SelectionGoal::None)
4701 });
4702 });
4703
4704 let mut indent_edits = Vec::new();
4705 let multibuffer_snapshot = editor.buffer.read(cx).snapshot(cx);
4706 for row in rows {
4707 let indents = multibuffer_snapshot.suggested_indents(row..row + 1, cx);
4708 for (row, indent) in indents {
4709 if indent.len == 0 {
4710 continue;
4711 }
4712
4713 let text = match indent.kind {
4714 IndentKind::Space => " ".repeat(indent.len as usize),
4715 IndentKind::Tab => "\t".repeat(indent.len as usize),
4716 };
4717 let point = Point::new(row.0, 0);
4718 indent_edits.push((point..point, text));
4719 }
4720 }
4721 editor.edit(indent_edits, cx);
4722 });
4723 }
4724
4725 pub fn insert(&mut self, text: &str, window: &mut Window, cx: &mut Context<Self>) {
4726 let autoindent = text.is_empty().not().then(|| AutoindentMode::Block {
4727 original_indent_columns: Vec::new(),
4728 });
4729 self.insert_with_autoindent_mode(text, autoindent, window, cx);
4730 }
4731
4732 fn insert_with_autoindent_mode(
4733 &mut self,
4734 text: &str,
4735 autoindent_mode: Option<AutoindentMode>,
4736 window: &mut Window,
4737 cx: &mut Context<Self>,
4738 ) {
4739 if self.read_only(cx) {
4740 return;
4741 }
4742
4743 let text: Arc<str> = text.into();
4744 self.transact(window, cx, |this, window, cx| {
4745 let old_selections = this.selections.all_adjusted(cx);
4746 let selection_anchors = this.buffer.update(cx, |buffer, cx| {
4747 let anchors = {
4748 let snapshot = buffer.read(cx);
4749 old_selections
4750 .iter()
4751 .map(|s| {
4752 let anchor = snapshot.anchor_after(s.head());
4753 s.map(|_| anchor)
4754 })
4755 .collect::<Vec<_>>()
4756 };
4757 buffer.edit(
4758 old_selections
4759 .iter()
4760 .map(|s| (s.start..s.end, text.clone())),
4761 autoindent_mode,
4762 cx,
4763 );
4764 anchors
4765 });
4766
4767 this.change_selections(Default::default(), window, cx, |s| {
4768 s.select_anchors(selection_anchors);
4769 });
4770
4771 cx.notify();
4772 });
4773 }
4774
4775 fn trigger_completion_on_input(
4776 &mut self,
4777 text: &str,
4778 trigger_in_words: bool,
4779 window: &mut Window,
4780 cx: &mut Context<Self>,
4781 ) {
4782 let completions_source = self
4783 .context_menu
4784 .borrow()
4785 .as_ref()
4786 .and_then(|menu| match menu {
4787 CodeContextMenu::Completions(completions_menu) => Some(completions_menu.source),
4788 CodeContextMenu::CodeActions(_) => None,
4789 });
4790
4791 match completions_source {
4792 Some(CompletionsMenuSource::Words) => {
4793 self.show_word_completions(&ShowWordCompletions, window, cx)
4794 }
4795 Some(CompletionsMenuSource::Normal)
4796 | Some(CompletionsMenuSource::SnippetChoices)
4797 | None
4798 if self.is_completion_trigger(
4799 text,
4800 trigger_in_words,
4801 completions_source.is_some(),
4802 cx,
4803 ) =>
4804 {
4805 self.show_completions(
4806 &ShowCompletions {
4807 trigger: Some(text.to_owned()).filter(|x| !x.is_empty()),
4808 },
4809 window,
4810 cx,
4811 )
4812 }
4813 _ => {
4814 self.hide_context_menu(window, cx);
4815 }
4816 }
4817 }
4818
4819 fn is_completion_trigger(
4820 &self,
4821 text: &str,
4822 trigger_in_words: bool,
4823 menu_is_open: bool,
4824 cx: &mut Context<Self>,
4825 ) -> bool {
4826 let position = self.selections.newest_anchor().head();
4827 let multibuffer = self.buffer.read(cx);
4828 let Some(buffer) = position
4829 .buffer_id
4830 .and_then(|buffer_id| multibuffer.buffer(buffer_id).clone())
4831 else {
4832 return false;
4833 };
4834
4835 if let Some(completion_provider) = &self.completion_provider {
4836 completion_provider.is_completion_trigger(
4837 &buffer,
4838 position.text_anchor,
4839 text,
4840 trigger_in_words,
4841 menu_is_open,
4842 cx,
4843 )
4844 } else {
4845 false
4846 }
4847 }
4848
4849 /// If any empty selections is touching the start of its innermost containing autoclose
4850 /// region, expand it to select the brackets.
4851 fn select_autoclose_pair(&mut self, window: &mut Window, cx: &mut Context<Self>) {
4852 let selections = self.selections.all::<usize>(cx);
4853 let buffer = self.buffer.read(cx).read(cx);
4854 let new_selections = self
4855 .selections_with_autoclose_regions(selections, &buffer)
4856 .map(|(mut selection, region)| {
4857 if !selection.is_empty() {
4858 return selection;
4859 }
4860
4861 if let Some(region) = region {
4862 let mut range = region.range.to_offset(&buffer);
4863 if selection.start == range.start && range.start >= region.pair.start.len() {
4864 range.start -= region.pair.start.len();
4865 if buffer.contains_str_at(range.start, ®ion.pair.start)
4866 && buffer.contains_str_at(range.end, ®ion.pair.end)
4867 {
4868 range.end += region.pair.end.len();
4869 selection.start = range.start;
4870 selection.end = range.end;
4871
4872 return selection;
4873 }
4874 }
4875 }
4876
4877 let always_treat_brackets_as_autoclosed = buffer
4878 .language_settings_at(selection.start, cx)
4879 .always_treat_brackets_as_autoclosed;
4880
4881 if !always_treat_brackets_as_autoclosed {
4882 return selection;
4883 }
4884
4885 if let Some(scope) = buffer.language_scope_at(selection.start) {
4886 for (pair, enabled) in scope.brackets() {
4887 if !enabled || !pair.close {
4888 continue;
4889 }
4890
4891 if buffer.contains_str_at(selection.start, &pair.end) {
4892 let pair_start_len = pair.start.len();
4893 if buffer.contains_str_at(
4894 selection.start.saturating_sub(pair_start_len),
4895 &pair.start,
4896 ) {
4897 selection.start -= pair_start_len;
4898 selection.end += pair.end.len();
4899
4900 return selection;
4901 }
4902 }
4903 }
4904 }
4905
4906 selection
4907 })
4908 .collect();
4909
4910 drop(buffer);
4911 self.change_selections(SelectionEffects::no_scroll(), window, cx, |selections| {
4912 selections.select(new_selections)
4913 });
4914 }
4915
4916 /// Iterate the given selections, and for each one, find the smallest surrounding
4917 /// autoclose region. This uses the ordering of the selections and the autoclose
4918 /// regions to avoid repeated comparisons.
4919 fn selections_with_autoclose_regions<'a, D: ToOffset + Clone>(
4920 &'a self,
4921 selections: impl IntoIterator<Item = Selection<D>>,
4922 buffer: &'a MultiBufferSnapshot,
4923 ) -> impl Iterator<Item = (Selection<D>, Option<&'a AutocloseRegion>)> {
4924 let mut i = 0;
4925 let mut regions = self.autoclose_regions.as_slice();
4926 selections.into_iter().map(move |selection| {
4927 let range = selection.start.to_offset(buffer)..selection.end.to_offset(buffer);
4928
4929 let mut enclosing = None;
4930 while let Some(pair_state) = regions.get(i) {
4931 if pair_state.range.end.to_offset(buffer) < range.start {
4932 regions = ®ions[i + 1..];
4933 i = 0;
4934 } else if pair_state.range.start.to_offset(buffer) > range.end {
4935 break;
4936 } else {
4937 if pair_state.selection_id == selection.id {
4938 enclosing = Some(pair_state);
4939 }
4940 i += 1;
4941 }
4942 }
4943
4944 (selection, enclosing)
4945 })
4946 }
4947
4948 /// Remove any autoclose regions that no longer contain their selection.
4949 fn invalidate_autoclose_regions(
4950 &mut self,
4951 mut selections: &[Selection<Anchor>],
4952 buffer: &MultiBufferSnapshot,
4953 ) {
4954 self.autoclose_regions.retain(|state| {
4955 let mut i = 0;
4956 while let Some(selection) = selections.get(i) {
4957 if selection.end.cmp(&state.range.start, buffer).is_lt() {
4958 selections = &selections[1..];
4959 continue;
4960 }
4961 if selection.start.cmp(&state.range.end, buffer).is_gt() {
4962 break;
4963 }
4964 if selection.id == state.selection_id {
4965 return true;
4966 } else {
4967 i += 1;
4968 }
4969 }
4970 false
4971 });
4972 }
4973
4974 fn completion_query(buffer: &MultiBufferSnapshot, position: impl ToOffset) -> Option<String> {
4975 let offset = position.to_offset(buffer);
4976 let (word_range, kind) = buffer.surrounding_word(offset, true);
4977 if offset > word_range.start && kind == Some(CharKind::Word) {
4978 Some(
4979 buffer
4980 .text_for_range(word_range.start..offset)
4981 .collect::<String>(),
4982 )
4983 } else {
4984 None
4985 }
4986 }
4987
4988 pub fn toggle_inline_values(
4989 &mut self,
4990 _: &ToggleInlineValues,
4991 _: &mut Window,
4992 cx: &mut Context<Self>,
4993 ) {
4994 self.inline_value_cache.enabled = !self.inline_value_cache.enabled;
4995
4996 self.refresh_inline_values(cx);
4997 }
4998
4999 pub fn toggle_inlay_hints(
5000 &mut self,
5001 _: &ToggleInlayHints,
5002 _: &mut Window,
5003 cx: &mut Context<Self>,
5004 ) {
5005 self.refresh_inlay_hints(
5006 InlayHintRefreshReason::Toggle(!self.inlay_hints_enabled()),
5007 cx,
5008 );
5009 }
5010
5011 pub fn inlay_hints_enabled(&self) -> bool {
5012 self.inlay_hint_cache.enabled
5013 }
5014
5015 pub fn inline_values_enabled(&self) -> bool {
5016 self.inline_value_cache.enabled
5017 }
5018
5019 #[cfg(any(test, feature = "test-support"))]
5020 pub fn inline_value_inlays(&self, cx: &App) -> Vec<Inlay> {
5021 self.display_map
5022 .read(cx)
5023 .current_inlays()
5024 .filter(|inlay| matches!(inlay.id, InlayId::DebuggerValue(_)))
5025 .cloned()
5026 .collect()
5027 }
5028
5029 #[cfg(any(test, feature = "test-support"))]
5030 pub fn all_inlays(&self, cx: &App) -> Vec<Inlay> {
5031 self.display_map
5032 .read(cx)
5033 .current_inlays()
5034 .cloned()
5035 .collect()
5036 }
5037
5038 fn refresh_inlay_hints(&mut self, reason: InlayHintRefreshReason, cx: &mut Context<Self>) {
5039 if self.semantics_provider.is_none() || !self.mode.is_full() {
5040 return;
5041 }
5042
5043 let reason_description = reason.description();
5044 let ignore_debounce = matches!(
5045 reason,
5046 InlayHintRefreshReason::SettingsChange(_)
5047 | InlayHintRefreshReason::Toggle(_)
5048 | InlayHintRefreshReason::ExcerptsRemoved(_)
5049 | InlayHintRefreshReason::ModifiersChanged(_)
5050 );
5051 let (invalidate_cache, required_languages) = match reason {
5052 InlayHintRefreshReason::ModifiersChanged(enabled) => {
5053 match self.inlay_hint_cache.modifiers_override(enabled) {
5054 Some(enabled) => {
5055 if enabled {
5056 (InvalidationStrategy::RefreshRequested, None)
5057 } else {
5058 self.splice_inlays(
5059 &self
5060 .visible_inlay_hints(cx)
5061 .iter()
5062 .map(|inlay| inlay.id)
5063 .collect::<Vec<InlayId>>(),
5064 Vec::new(),
5065 cx,
5066 );
5067 return;
5068 }
5069 }
5070 None => return,
5071 }
5072 }
5073 InlayHintRefreshReason::Toggle(enabled) => {
5074 if self.inlay_hint_cache.toggle(enabled) {
5075 if enabled {
5076 (InvalidationStrategy::RefreshRequested, None)
5077 } else {
5078 self.splice_inlays(
5079 &self
5080 .visible_inlay_hints(cx)
5081 .iter()
5082 .map(|inlay| inlay.id)
5083 .collect::<Vec<InlayId>>(),
5084 Vec::new(),
5085 cx,
5086 );
5087 return;
5088 }
5089 } else {
5090 return;
5091 }
5092 }
5093 InlayHintRefreshReason::SettingsChange(new_settings) => {
5094 match self.inlay_hint_cache.update_settings(
5095 &self.buffer,
5096 new_settings,
5097 self.visible_inlay_hints(cx),
5098 cx,
5099 ) {
5100 ControlFlow::Break(Some(InlaySplice {
5101 to_remove,
5102 to_insert,
5103 })) => {
5104 self.splice_inlays(&to_remove, to_insert, cx);
5105 return;
5106 }
5107 ControlFlow::Break(None) => return,
5108 ControlFlow::Continue(()) => (InvalidationStrategy::RefreshRequested, None),
5109 }
5110 }
5111 InlayHintRefreshReason::ExcerptsRemoved(excerpts_removed) => {
5112 if let Some(InlaySplice {
5113 to_remove,
5114 to_insert,
5115 }) = self.inlay_hint_cache.remove_excerpts(&excerpts_removed)
5116 {
5117 self.splice_inlays(&to_remove, to_insert, cx);
5118 }
5119 self.display_map.update(cx, |display_map, _| {
5120 display_map.remove_inlays_for_excerpts(&excerpts_removed)
5121 });
5122 return;
5123 }
5124 InlayHintRefreshReason::NewLinesShown => (InvalidationStrategy::None, None),
5125 InlayHintRefreshReason::BufferEdited(buffer_languages) => {
5126 (InvalidationStrategy::BufferEdited, Some(buffer_languages))
5127 }
5128 InlayHintRefreshReason::RefreshRequested => {
5129 (InvalidationStrategy::RefreshRequested, None)
5130 }
5131 };
5132
5133 if let Some(InlaySplice {
5134 to_remove,
5135 to_insert,
5136 }) = self.inlay_hint_cache.spawn_hint_refresh(
5137 reason_description,
5138 self.visible_excerpts(required_languages.as_ref(), cx),
5139 invalidate_cache,
5140 ignore_debounce,
5141 cx,
5142 ) {
5143 self.splice_inlays(&to_remove, to_insert, cx);
5144 }
5145 }
5146
5147 fn visible_inlay_hints(&self, cx: &Context<Editor>) -> Vec<Inlay> {
5148 self.display_map
5149 .read(cx)
5150 .current_inlays()
5151 .filter(move |inlay| matches!(inlay.id, InlayId::Hint(_)))
5152 .cloned()
5153 .collect()
5154 }
5155
5156 pub fn visible_excerpts(
5157 &self,
5158 restrict_to_languages: Option<&HashSet<Arc<Language>>>,
5159 cx: &mut Context<Editor>,
5160 ) -> HashMap<ExcerptId, (Entity<Buffer>, clock::Global, Range<usize>)> {
5161 let Some(project) = self.project.as_ref() else {
5162 return HashMap::default();
5163 };
5164 let project = project.read(cx);
5165 let multi_buffer = self.buffer().read(cx);
5166 let multi_buffer_snapshot = multi_buffer.snapshot(cx);
5167 let multi_buffer_visible_start = self
5168 .scroll_manager
5169 .anchor()
5170 .anchor
5171 .to_point(&multi_buffer_snapshot);
5172 let multi_buffer_visible_end = multi_buffer_snapshot.clip_point(
5173 multi_buffer_visible_start
5174 + Point::new(self.visible_line_count().unwrap_or(0.).ceil() as u32, 0),
5175 Bias::Left,
5176 );
5177 let multi_buffer_visible_range = multi_buffer_visible_start..multi_buffer_visible_end;
5178 multi_buffer_snapshot
5179 .range_to_buffer_ranges(multi_buffer_visible_range)
5180 .into_iter()
5181 .filter(|(_, excerpt_visible_range, _)| !excerpt_visible_range.is_empty())
5182 .filter_map(|(buffer, excerpt_visible_range, excerpt_id)| {
5183 let buffer_file = project::File::from_dyn(buffer.file())?;
5184 let buffer_worktree = project.worktree_for_id(buffer_file.worktree_id(cx), cx)?;
5185 let worktree_entry = buffer_worktree
5186 .read(cx)
5187 .entry_for_id(buffer_file.project_entry_id(cx)?)?;
5188 if worktree_entry.is_ignored {
5189 return None;
5190 }
5191
5192 let language = buffer.language()?;
5193 if let Some(restrict_to_languages) = restrict_to_languages {
5194 if !restrict_to_languages.contains(language) {
5195 return None;
5196 }
5197 }
5198 Some((
5199 excerpt_id,
5200 (
5201 multi_buffer.buffer(buffer.remote_id()).unwrap(),
5202 buffer.version().clone(),
5203 excerpt_visible_range,
5204 ),
5205 ))
5206 })
5207 .collect()
5208 }
5209
5210 pub fn text_layout_details(&self, window: &mut Window) -> TextLayoutDetails {
5211 TextLayoutDetails {
5212 text_system: window.text_system().clone(),
5213 editor_style: self.style.clone().unwrap(),
5214 rem_size: window.rem_size(),
5215 scroll_anchor: self.scroll_manager.anchor(),
5216 visible_rows: self.visible_line_count(),
5217 vertical_scroll_margin: self.scroll_manager.vertical_scroll_margin,
5218 }
5219 }
5220
5221 pub fn splice_inlays(
5222 &self,
5223 to_remove: &[InlayId],
5224 to_insert: Vec<Inlay>,
5225 cx: &mut Context<Self>,
5226 ) {
5227 self.display_map.update(cx, |display_map, cx| {
5228 display_map.splice_inlays(to_remove, to_insert, cx)
5229 });
5230 cx.notify();
5231 }
5232
5233 fn trigger_on_type_formatting(
5234 &self,
5235 input: String,
5236 window: &mut Window,
5237 cx: &mut Context<Self>,
5238 ) -> Option<Task<Result<()>>> {
5239 if input.len() != 1 {
5240 return None;
5241 }
5242
5243 let project = self.project.as_ref()?;
5244 let position = self.selections.newest_anchor().head();
5245 let (buffer, buffer_position) = self
5246 .buffer
5247 .read(cx)
5248 .text_anchor_for_position(position, cx)?;
5249
5250 let settings = language_settings::language_settings(
5251 buffer
5252 .read(cx)
5253 .language_at(buffer_position)
5254 .map(|l| l.name()),
5255 buffer.read(cx).file(),
5256 cx,
5257 );
5258 if !settings.use_on_type_format {
5259 return None;
5260 }
5261
5262 // OnTypeFormatting returns a list of edits, no need to pass them between Zed instances,
5263 // hence we do LSP request & edit on host side only — add formats to host's history.
5264 let push_to_lsp_host_history = true;
5265 // If this is not the host, append its history with new edits.
5266 let push_to_client_history = project.read(cx).is_via_collab();
5267
5268 let on_type_formatting = project.update(cx, |project, cx| {
5269 project.on_type_format(
5270 buffer.clone(),
5271 buffer_position,
5272 input,
5273 push_to_lsp_host_history,
5274 cx,
5275 )
5276 });
5277 Some(cx.spawn_in(window, async move |editor, cx| {
5278 if let Some(transaction) = on_type_formatting.await? {
5279 if push_to_client_history {
5280 buffer
5281 .update(cx, |buffer, _| {
5282 buffer.push_transaction(transaction, Instant::now());
5283 buffer.finalize_last_transaction();
5284 })
5285 .ok();
5286 }
5287 editor.update(cx, |editor, cx| {
5288 editor.refresh_document_highlights(cx);
5289 })?;
5290 }
5291 Ok(())
5292 }))
5293 }
5294
5295 pub fn show_word_completions(
5296 &mut self,
5297 _: &ShowWordCompletions,
5298 window: &mut Window,
5299 cx: &mut Context<Self>,
5300 ) {
5301 self.open_or_update_completions_menu(Some(CompletionsMenuSource::Words), None, window, cx);
5302 }
5303
5304 pub fn show_completions(
5305 &mut self,
5306 options: &ShowCompletions,
5307 window: &mut Window,
5308 cx: &mut Context<Self>,
5309 ) {
5310 self.open_or_update_completions_menu(None, options.trigger.as_deref(), window, cx);
5311 }
5312
5313 fn open_or_update_completions_menu(
5314 &mut self,
5315 requested_source: Option<CompletionsMenuSource>,
5316 trigger: Option<&str>,
5317 window: &mut Window,
5318 cx: &mut Context<Self>,
5319 ) {
5320 if self.pending_rename.is_some() {
5321 return;
5322 }
5323
5324 let multibuffer_snapshot = self.buffer.read(cx).read(cx);
5325
5326 // Typically `start` == `end`, but with snippet tabstop choices the default choice is
5327 // inserted and selected. To handle that case, the start of the selection is used so that
5328 // the menu starts with all choices.
5329 let position = self
5330 .selections
5331 .newest_anchor()
5332 .start
5333 .bias_right(&multibuffer_snapshot);
5334 if position.diff_base_anchor.is_some() {
5335 return;
5336 }
5337 let (buffer, buffer_position) =
5338 if let Some(output) = self.buffer.read(cx).text_anchor_for_position(position, cx) {
5339 output
5340 } else {
5341 return;
5342 };
5343 let buffer_snapshot = buffer.read(cx).snapshot();
5344
5345 let query: Option<Arc<String>> =
5346 Self::completion_query(&multibuffer_snapshot, position).map(|query| query.into());
5347
5348 drop(multibuffer_snapshot);
5349
5350 let provider = match requested_source {
5351 Some(CompletionsMenuSource::Normal) | None => self.completion_provider.clone(),
5352 Some(CompletionsMenuSource::Words) => None,
5353 Some(CompletionsMenuSource::SnippetChoices) => {
5354 log::error!("bug: SnippetChoices requested_source is not handled");
5355 None
5356 }
5357 };
5358
5359 let sort_completions = provider
5360 .as_ref()
5361 .map_or(false, |provider| provider.sort_completions());
5362
5363 let filter_completions = provider
5364 .as_ref()
5365 .map_or(true, |provider| provider.filter_completions());
5366
5367 let trigger_kind = match trigger {
5368 Some(trigger) if buffer.read(cx).completion_triggers().contains(trigger) => {
5369 CompletionTriggerKind::TRIGGER_CHARACTER
5370 }
5371 _ => CompletionTriggerKind::INVOKED,
5372 };
5373 let completion_context = CompletionContext {
5374 trigger_character: trigger.and_then(|trigger| {
5375 if trigger_kind == CompletionTriggerKind::TRIGGER_CHARACTER {
5376 Some(String::from(trigger))
5377 } else {
5378 None
5379 }
5380 }),
5381 trigger_kind,
5382 };
5383
5384 // Hide the current completions menu when a trigger char is typed. Without this, cached
5385 // completions from before the trigger char may be reused (#32774). Snippet choices could
5386 // involve trigger chars, so this is skipped in that case.
5387 if trigger_kind == CompletionTriggerKind::TRIGGER_CHARACTER && self.snippet_stack.is_empty()
5388 {
5389 let menu_is_open = matches!(
5390 self.context_menu.borrow().as_ref(),
5391 Some(CodeContextMenu::Completions(_))
5392 );
5393 if menu_is_open {
5394 self.hide_context_menu(window, cx);
5395 }
5396 }
5397
5398 if let Some(CodeContextMenu::Completions(menu)) = self.context_menu.borrow_mut().as_mut() {
5399 if filter_completions {
5400 menu.filter(query.clone(), provider.clone(), window, cx);
5401 }
5402 // When `is_incomplete` is false, no need to re-query completions when the current query
5403 // is a suffix of the initial query.
5404 if !menu.is_incomplete {
5405 // If the new query is a suffix of the old query (typing more characters) and
5406 // the previous result was complete, the existing completions can be filtered.
5407 //
5408 // Note that this is always true for snippet completions.
5409 let query_matches = match (&menu.initial_query, &query) {
5410 (Some(initial_query), Some(query)) => query.starts_with(initial_query.as_ref()),
5411 (None, _) => true,
5412 _ => false,
5413 };
5414 if query_matches {
5415 let position_matches = if menu.initial_position == position {
5416 true
5417 } else {
5418 let snapshot = self.buffer.read(cx).read(cx);
5419 menu.initial_position.to_offset(&snapshot) == position.to_offset(&snapshot)
5420 };
5421 if position_matches {
5422 return;
5423 }
5424 }
5425 }
5426 };
5427
5428 let (word_replace_range, word_to_exclude) = if let (word_range, Some(CharKind::Word)) =
5429 buffer_snapshot.surrounding_word(buffer_position)
5430 {
5431 let word_to_exclude = buffer_snapshot
5432 .text_for_range(word_range.clone())
5433 .collect::<String>();
5434 (
5435 buffer_snapshot.anchor_before(word_range.start)
5436 ..buffer_snapshot.anchor_after(buffer_position),
5437 Some(word_to_exclude),
5438 )
5439 } else {
5440 (buffer_position..buffer_position, None)
5441 };
5442
5443 let language = buffer_snapshot
5444 .language_at(buffer_position)
5445 .map(|language| language.name());
5446
5447 let completion_settings =
5448 language_settings(language.clone(), buffer_snapshot.file(), cx).completions;
5449
5450 let show_completion_documentation = buffer_snapshot
5451 .settings_at(buffer_position, cx)
5452 .show_completion_documentation;
5453
5454 // The document can be large, so stay in reasonable bounds when searching for words,
5455 // otherwise completion pop-up might be slow to appear.
5456 const WORD_LOOKUP_ROWS: u32 = 5_000;
5457 let buffer_row = text::ToPoint::to_point(&buffer_position, &buffer_snapshot).row;
5458 let min_word_search = buffer_snapshot.clip_point(
5459 Point::new(buffer_row.saturating_sub(WORD_LOOKUP_ROWS), 0),
5460 Bias::Left,
5461 );
5462 let max_word_search = buffer_snapshot.clip_point(
5463 Point::new(buffer_row + WORD_LOOKUP_ROWS, 0).min(buffer_snapshot.max_point()),
5464 Bias::Right,
5465 );
5466 let word_search_range = buffer_snapshot.point_to_offset(min_word_search)
5467 ..buffer_snapshot.point_to_offset(max_word_search);
5468
5469 let skip_digits = query
5470 .as_ref()
5471 .map_or(true, |query| !query.chars().any(|c| c.is_digit(10)));
5472
5473 let (mut words, provider_responses) = match &provider {
5474 Some(provider) => {
5475 let provider_responses = provider.completions(
5476 position.excerpt_id,
5477 &buffer,
5478 buffer_position,
5479 completion_context,
5480 window,
5481 cx,
5482 );
5483
5484 let words = match completion_settings.words {
5485 WordsCompletionMode::Disabled => Task::ready(BTreeMap::default()),
5486 WordsCompletionMode::Enabled | WordsCompletionMode::Fallback => cx
5487 .background_spawn(async move {
5488 buffer_snapshot.words_in_range(WordsQuery {
5489 fuzzy_contents: None,
5490 range: word_search_range,
5491 skip_digits,
5492 })
5493 }),
5494 };
5495
5496 (words, provider_responses)
5497 }
5498 None => (
5499 cx.background_spawn(async move {
5500 buffer_snapshot.words_in_range(WordsQuery {
5501 fuzzy_contents: None,
5502 range: word_search_range,
5503 skip_digits,
5504 })
5505 }),
5506 Task::ready(Ok(Vec::new())),
5507 ),
5508 };
5509
5510 let snippet_sort_order = EditorSettings::get_global(cx).snippet_sort_order;
5511
5512 let id = post_inc(&mut self.next_completion_id);
5513 let task = cx.spawn_in(window, async move |editor, cx| {
5514 let Ok(()) = editor.update(cx, |this, _| {
5515 this.completion_tasks.retain(|(task_id, _)| *task_id >= id);
5516 }) else {
5517 return;
5518 };
5519
5520 // TODO: Ideally completions from different sources would be selectively re-queried, so
5521 // that having one source with `is_incomplete: true` doesn't cause all to be re-queried.
5522 let mut completions = Vec::new();
5523 let mut is_incomplete = false;
5524 if let Some(provider_responses) = provider_responses.await.log_err() {
5525 if !provider_responses.is_empty() {
5526 for response in provider_responses {
5527 completions.extend(response.completions);
5528 is_incomplete = is_incomplete || response.is_incomplete;
5529 }
5530 if completion_settings.words == WordsCompletionMode::Fallback {
5531 words = Task::ready(BTreeMap::default());
5532 }
5533 }
5534 }
5535
5536 let mut words = words.await;
5537 if let Some(word_to_exclude) = &word_to_exclude {
5538 words.remove(word_to_exclude);
5539 }
5540 for lsp_completion in &completions {
5541 words.remove(&lsp_completion.new_text);
5542 }
5543 completions.extend(words.into_iter().map(|(word, word_range)| Completion {
5544 replace_range: word_replace_range.clone(),
5545 new_text: word.clone(),
5546 label: CodeLabel::plain(word, None),
5547 icon_path: None,
5548 documentation: None,
5549 source: CompletionSource::BufferWord {
5550 word_range,
5551 resolved: false,
5552 },
5553 insert_text_mode: Some(InsertTextMode::AS_IS),
5554 confirm: None,
5555 }));
5556
5557 let menu = if completions.is_empty() {
5558 None
5559 } else {
5560 let Ok((mut menu, matches_task)) = editor.update(cx, |editor, cx| {
5561 let languages = editor
5562 .workspace
5563 .as_ref()
5564 .and_then(|(workspace, _)| workspace.upgrade())
5565 .map(|workspace| workspace.read(cx).app_state().languages.clone());
5566 let menu = CompletionsMenu::new(
5567 id,
5568 requested_source.unwrap_or(CompletionsMenuSource::Normal),
5569 sort_completions,
5570 show_completion_documentation,
5571 position,
5572 query.clone(),
5573 is_incomplete,
5574 buffer.clone(),
5575 completions.into(),
5576 snippet_sort_order,
5577 languages,
5578 language,
5579 cx,
5580 );
5581
5582 let query = if filter_completions { query } else { None };
5583 let matches_task = if let Some(query) = query {
5584 menu.do_async_filtering(query, cx)
5585 } else {
5586 Task::ready(menu.unfiltered_matches())
5587 };
5588 (menu, matches_task)
5589 }) else {
5590 return;
5591 };
5592
5593 let matches = matches_task.await;
5594
5595 let Ok(()) = editor.update_in(cx, |editor, window, cx| {
5596 // Newer menu already set, so exit.
5597 match editor.context_menu.borrow().as_ref() {
5598 Some(CodeContextMenu::Completions(prev_menu)) => {
5599 if prev_menu.id > id {
5600 return;
5601 }
5602 }
5603 _ => {}
5604 };
5605
5606 // Only valid to take prev_menu because it the new menu is immediately set
5607 // below, or the menu is hidden.
5608 match editor.context_menu.borrow_mut().take() {
5609 Some(CodeContextMenu::Completions(prev_menu)) => {
5610 let position_matches =
5611 if prev_menu.initial_position == menu.initial_position {
5612 true
5613 } else {
5614 let snapshot = editor.buffer.read(cx).read(cx);
5615 prev_menu.initial_position.to_offset(&snapshot)
5616 == menu.initial_position.to_offset(&snapshot)
5617 };
5618 if position_matches {
5619 // Preserve markdown cache before `set_filter_results` because it will
5620 // try to populate the documentation cache.
5621 menu.preserve_markdown_cache(prev_menu);
5622 }
5623 }
5624 _ => {}
5625 };
5626
5627 menu.set_filter_results(matches, provider, window, cx);
5628 }) else {
5629 return;
5630 };
5631
5632 menu.visible().then_some(menu)
5633 };
5634
5635 editor
5636 .update_in(cx, |editor, window, cx| {
5637 if editor.focus_handle.is_focused(window) {
5638 if let Some(menu) = menu {
5639 *editor.context_menu.borrow_mut() =
5640 Some(CodeContextMenu::Completions(menu));
5641
5642 crate::hover_popover::hide_hover(editor, cx);
5643 if editor.show_edit_predictions_in_menu() {
5644 editor.update_visible_inline_completion(window, cx);
5645 } else {
5646 editor.discard_inline_completion(false, cx);
5647 }
5648
5649 cx.notify();
5650 return;
5651 }
5652 }
5653
5654 if editor.completion_tasks.len() <= 1 {
5655 // If there are no more completion tasks and the last menu was empty, we should hide it.
5656 let was_hidden = editor.hide_context_menu(window, cx).is_none();
5657 // If it was already hidden and we don't show inline completions in the menu, we should
5658 // also show the inline-completion when available.
5659 if was_hidden && editor.show_edit_predictions_in_menu() {
5660 editor.update_visible_inline_completion(window, cx);
5661 }
5662 }
5663 })
5664 .ok();
5665 });
5666
5667 self.completion_tasks.push((id, task));
5668 }
5669
5670 #[cfg(feature = "test-support")]
5671 pub fn current_completions(&self) -> Option<Vec<project::Completion>> {
5672 let menu = self.context_menu.borrow();
5673 if let CodeContextMenu::Completions(menu) = menu.as_ref()? {
5674 let completions = menu.completions.borrow();
5675 Some(completions.to_vec())
5676 } else {
5677 None
5678 }
5679 }
5680
5681 pub fn with_completions_menu_matching_id<R>(
5682 &self,
5683 id: CompletionId,
5684 f: impl FnOnce(Option<&mut CompletionsMenu>) -> R,
5685 ) -> R {
5686 let mut context_menu = self.context_menu.borrow_mut();
5687 let Some(CodeContextMenu::Completions(completions_menu)) = &mut *context_menu else {
5688 return f(None);
5689 };
5690 if completions_menu.id != id {
5691 return f(None);
5692 }
5693 f(Some(completions_menu))
5694 }
5695
5696 pub fn confirm_completion(
5697 &mut self,
5698 action: &ConfirmCompletion,
5699 window: &mut Window,
5700 cx: &mut Context<Self>,
5701 ) -> Option<Task<Result<()>>> {
5702 self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
5703 self.do_completion(action.item_ix, CompletionIntent::Complete, window, cx)
5704 }
5705
5706 pub fn confirm_completion_insert(
5707 &mut self,
5708 _: &ConfirmCompletionInsert,
5709 window: &mut Window,
5710 cx: &mut Context<Self>,
5711 ) -> Option<Task<Result<()>>> {
5712 self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
5713 self.do_completion(None, CompletionIntent::CompleteWithInsert, window, cx)
5714 }
5715
5716 pub fn confirm_completion_replace(
5717 &mut self,
5718 _: &ConfirmCompletionReplace,
5719 window: &mut Window,
5720 cx: &mut Context<Self>,
5721 ) -> Option<Task<Result<()>>> {
5722 self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
5723 self.do_completion(None, CompletionIntent::CompleteWithReplace, window, cx)
5724 }
5725
5726 pub fn compose_completion(
5727 &mut self,
5728 action: &ComposeCompletion,
5729 window: &mut Window,
5730 cx: &mut Context<Self>,
5731 ) -> Option<Task<Result<()>>> {
5732 self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
5733 self.do_completion(action.item_ix, CompletionIntent::Compose, window, cx)
5734 }
5735
5736 fn do_completion(
5737 &mut self,
5738 item_ix: Option<usize>,
5739 intent: CompletionIntent,
5740 window: &mut Window,
5741 cx: &mut Context<Editor>,
5742 ) -> Option<Task<Result<()>>> {
5743 use language::ToOffset as _;
5744
5745 let CodeContextMenu::Completions(completions_menu) = self.hide_context_menu(window, cx)?
5746 else {
5747 return None;
5748 };
5749
5750 let candidate_id = {
5751 let entries = completions_menu.entries.borrow();
5752 let mat = entries.get(item_ix.unwrap_or(completions_menu.selected_item))?;
5753 if self.show_edit_predictions_in_menu() {
5754 self.discard_inline_completion(true, cx);
5755 }
5756 mat.candidate_id
5757 };
5758
5759 let completion = completions_menu
5760 .completions
5761 .borrow()
5762 .get(candidate_id)?
5763 .clone();
5764 cx.stop_propagation();
5765
5766 let buffer_handle = completions_menu.buffer.clone();
5767
5768 let CompletionEdit {
5769 new_text,
5770 snippet,
5771 replace_range,
5772 } = process_completion_for_edit(
5773 &completion,
5774 intent,
5775 &buffer_handle,
5776 &completions_menu.initial_position.text_anchor,
5777 cx,
5778 );
5779
5780 let buffer = buffer_handle.read(cx);
5781 let snapshot = self.buffer.read(cx).snapshot(cx);
5782 let newest_anchor = self.selections.newest_anchor();
5783 let replace_range_multibuffer = {
5784 let excerpt = snapshot.excerpt_containing(newest_anchor.range()).unwrap();
5785 let multibuffer_anchor = snapshot
5786 .anchor_in_excerpt(excerpt.id(), buffer.anchor_before(replace_range.start))
5787 .unwrap()
5788 ..snapshot
5789 .anchor_in_excerpt(excerpt.id(), buffer.anchor_before(replace_range.end))
5790 .unwrap();
5791 multibuffer_anchor.start.to_offset(&snapshot)
5792 ..multibuffer_anchor.end.to_offset(&snapshot)
5793 };
5794 if newest_anchor.head().buffer_id != Some(buffer.remote_id()) {
5795 return None;
5796 }
5797
5798 let old_text = buffer
5799 .text_for_range(replace_range.clone())
5800 .collect::<String>();
5801 let lookbehind = newest_anchor
5802 .start
5803 .text_anchor
5804 .to_offset(buffer)
5805 .saturating_sub(replace_range.start);
5806 let lookahead = replace_range
5807 .end
5808 .saturating_sub(newest_anchor.end.text_anchor.to_offset(buffer));
5809 let prefix = &old_text[..old_text.len().saturating_sub(lookahead)];
5810 let suffix = &old_text[lookbehind.min(old_text.len())..];
5811
5812 let selections = self.selections.all::<usize>(cx);
5813 let mut ranges = Vec::new();
5814 let mut linked_edits = HashMap::<_, Vec<_>>::default();
5815
5816 for selection in &selections {
5817 let range = if selection.id == newest_anchor.id {
5818 replace_range_multibuffer.clone()
5819 } else {
5820 let mut range = selection.range();
5821
5822 // if prefix is present, don't duplicate it
5823 if snapshot.contains_str_at(range.start.saturating_sub(lookbehind), prefix) {
5824 range.start = range.start.saturating_sub(lookbehind);
5825
5826 // if suffix is also present, mimic the newest cursor and replace it
5827 if selection.id != newest_anchor.id
5828 && snapshot.contains_str_at(range.end, suffix)
5829 {
5830 range.end += lookahead;
5831 }
5832 }
5833 range
5834 };
5835
5836 ranges.push(range.clone());
5837
5838 if !self.linked_edit_ranges.is_empty() {
5839 let start_anchor = snapshot.anchor_before(range.start);
5840 let end_anchor = snapshot.anchor_after(range.end);
5841 if let Some(ranges) = self
5842 .linked_editing_ranges_for(start_anchor.text_anchor..end_anchor.text_anchor, cx)
5843 {
5844 for (buffer, edits) in ranges {
5845 linked_edits
5846 .entry(buffer.clone())
5847 .or_default()
5848 .extend(edits.into_iter().map(|range| (range, new_text.to_owned())));
5849 }
5850 }
5851 }
5852 }
5853
5854 let common_prefix_len = old_text
5855 .chars()
5856 .zip(new_text.chars())
5857 .take_while(|(a, b)| a == b)
5858 .map(|(a, _)| a.len_utf8())
5859 .sum::<usize>();
5860
5861 cx.emit(EditorEvent::InputHandled {
5862 utf16_range_to_replace: None,
5863 text: new_text[common_prefix_len..].into(),
5864 });
5865
5866 self.transact(window, cx, |this, window, cx| {
5867 if let Some(mut snippet) = snippet {
5868 snippet.text = new_text.to_string();
5869 this.insert_snippet(&ranges, snippet, window, cx).log_err();
5870 } else {
5871 this.buffer.update(cx, |buffer, cx| {
5872 let auto_indent = match completion.insert_text_mode {
5873 Some(InsertTextMode::AS_IS) => None,
5874 _ => this.autoindent_mode.clone(),
5875 };
5876 let edits = ranges.into_iter().map(|range| (range, new_text.as_str()));
5877 buffer.edit(edits, auto_indent, cx);
5878 });
5879 }
5880 for (buffer, edits) in linked_edits {
5881 buffer.update(cx, |buffer, cx| {
5882 let snapshot = buffer.snapshot();
5883 let edits = edits
5884 .into_iter()
5885 .map(|(range, text)| {
5886 use text::ToPoint as TP;
5887 let end_point = TP::to_point(&range.end, &snapshot);
5888 let start_point = TP::to_point(&range.start, &snapshot);
5889 (start_point..end_point, text)
5890 })
5891 .sorted_by_key(|(range, _)| range.start);
5892 buffer.edit(edits, None, cx);
5893 })
5894 }
5895
5896 this.refresh_inline_completion(true, false, window, cx);
5897 });
5898
5899 let show_new_completions_on_confirm = completion
5900 .confirm
5901 .as_ref()
5902 .map_or(false, |confirm| confirm(intent, window, cx));
5903 if show_new_completions_on_confirm {
5904 self.show_completions(&ShowCompletions { trigger: None }, window, cx);
5905 }
5906
5907 let provider = self.completion_provider.as_ref()?;
5908 drop(completion);
5909 let apply_edits = provider.apply_additional_edits_for_completion(
5910 buffer_handle,
5911 completions_menu.completions.clone(),
5912 candidate_id,
5913 true,
5914 cx,
5915 );
5916
5917 let editor_settings = EditorSettings::get_global(cx);
5918 if editor_settings.show_signature_help_after_edits || editor_settings.auto_signature_help {
5919 // After the code completion is finished, users often want to know what signatures are needed.
5920 // so we should automatically call signature_help
5921 self.show_signature_help(&ShowSignatureHelp, window, cx);
5922 }
5923
5924 Some(cx.foreground_executor().spawn(async move {
5925 apply_edits.await?;
5926 Ok(())
5927 }))
5928 }
5929
5930 pub fn toggle_code_actions(
5931 &mut self,
5932 action: &ToggleCodeActions,
5933 window: &mut Window,
5934 cx: &mut Context<Self>,
5935 ) {
5936 let quick_launch = action.quick_launch;
5937 let mut context_menu = self.context_menu.borrow_mut();
5938 if let Some(CodeContextMenu::CodeActions(code_actions)) = context_menu.as_ref() {
5939 if code_actions.deployed_from == action.deployed_from {
5940 // Toggle if we're selecting the same one
5941 *context_menu = None;
5942 cx.notify();
5943 return;
5944 } else {
5945 // Otherwise, clear it and start a new one
5946 *context_menu = None;
5947 cx.notify();
5948 }
5949 }
5950 drop(context_menu);
5951 let snapshot = self.snapshot(window, cx);
5952 let deployed_from = action.deployed_from.clone();
5953 let action = action.clone();
5954 self.completion_tasks.clear();
5955 self.discard_inline_completion(false, cx);
5956
5957 let multibuffer_point = match &action.deployed_from {
5958 Some(CodeActionSource::Indicator(row)) | Some(CodeActionSource::RunMenu(row)) => {
5959 DisplayPoint::new(*row, 0).to_point(&snapshot)
5960 }
5961 _ => self.selections.newest::<Point>(cx).head(),
5962 };
5963 let Some((buffer, buffer_row)) = snapshot
5964 .buffer_snapshot
5965 .buffer_line_for_row(MultiBufferRow(multibuffer_point.row))
5966 .and_then(|(buffer_snapshot, range)| {
5967 self.buffer()
5968 .read(cx)
5969 .buffer(buffer_snapshot.remote_id())
5970 .map(|buffer| (buffer, range.start.row))
5971 })
5972 else {
5973 return;
5974 };
5975 let buffer_id = buffer.read(cx).remote_id();
5976 let tasks = self
5977 .tasks
5978 .get(&(buffer_id, buffer_row))
5979 .map(|t| Arc::new(t.to_owned()));
5980
5981 if !self.focus_handle.is_focused(window) {
5982 return;
5983 }
5984 let project = self.project.clone();
5985
5986 let code_actions_task = match deployed_from {
5987 Some(CodeActionSource::RunMenu(_)) => Task::ready(None),
5988 _ => self.code_actions(buffer_row, window, cx),
5989 };
5990
5991 let runnable_task = match deployed_from {
5992 Some(CodeActionSource::Indicator(_)) => Task::ready(Ok(Default::default())),
5993 _ => {
5994 let mut task_context_task = Task::ready(None);
5995 if let Some(tasks) = &tasks {
5996 if let Some(project) = project {
5997 task_context_task =
5998 Self::build_tasks_context(&project, &buffer, buffer_row, &tasks, cx);
5999 }
6000 }
6001
6002 cx.spawn_in(window, {
6003 let buffer = buffer.clone();
6004 async move |editor, cx| {
6005 let task_context = task_context_task.await;
6006
6007 let resolved_tasks =
6008 tasks
6009 .zip(task_context.clone())
6010 .map(|(tasks, task_context)| ResolvedTasks {
6011 templates: tasks.resolve(&task_context).collect(),
6012 position: snapshot.buffer_snapshot.anchor_before(Point::new(
6013 multibuffer_point.row,
6014 tasks.column,
6015 )),
6016 });
6017 let debug_scenarios = editor
6018 .update(cx, |editor, cx| {
6019 editor.debug_scenarios(&resolved_tasks, &buffer, cx)
6020 })?
6021 .await;
6022 anyhow::Ok((resolved_tasks, debug_scenarios, task_context))
6023 }
6024 })
6025 }
6026 };
6027
6028 cx.spawn_in(window, async move |editor, cx| {
6029 let (resolved_tasks, debug_scenarios, task_context) = runnable_task.await?;
6030 let code_actions = code_actions_task.await;
6031 let spawn_straight_away = quick_launch
6032 && resolved_tasks
6033 .as_ref()
6034 .map_or(false, |tasks| tasks.templates.len() == 1)
6035 && code_actions
6036 .as_ref()
6037 .map_or(true, |actions| actions.is_empty())
6038 && debug_scenarios.is_empty();
6039
6040 editor.update_in(cx, |editor, window, cx| {
6041 crate::hover_popover::hide_hover(editor, cx);
6042 let actions = CodeActionContents::new(
6043 resolved_tasks,
6044 code_actions,
6045 debug_scenarios,
6046 task_context.unwrap_or_default(),
6047 );
6048
6049 // Don't show the menu if there are no actions available
6050 if actions.is_empty() {
6051 cx.notify();
6052 return Task::ready(Ok(()));
6053 }
6054
6055 *editor.context_menu.borrow_mut() =
6056 Some(CodeContextMenu::CodeActions(CodeActionsMenu {
6057 buffer,
6058 actions,
6059 selected_item: Default::default(),
6060 scroll_handle: UniformListScrollHandle::default(),
6061 deployed_from,
6062 }));
6063 cx.notify();
6064 if spawn_straight_away {
6065 if let Some(task) = editor.confirm_code_action(
6066 &ConfirmCodeAction { item_ix: Some(0) },
6067 window,
6068 cx,
6069 ) {
6070 return task;
6071 }
6072 }
6073
6074 Task::ready(Ok(()))
6075 })
6076 })
6077 .detach_and_log_err(cx);
6078 }
6079
6080 fn debug_scenarios(
6081 &mut self,
6082 resolved_tasks: &Option<ResolvedTasks>,
6083 buffer: &Entity<Buffer>,
6084 cx: &mut App,
6085 ) -> Task<Vec<task::DebugScenario>> {
6086 maybe!({
6087 let project = self.project.as_ref()?;
6088 let dap_store = project.read(cx).dap_store();
6089 let mut scenarios = vec![];
6090 let resolved_tasks = resolved_tasks.as_ref()?;
6091 let buffer = buffer.read(cx);
6092 let language = buffer.language()?;
6093 let file = buffer.file();
6094 let debug_adapter = language_settings(language.name().into(), file, cx)
6095 .debuggers
6096 .first()
6097 .map(SharedString::from)
6098 .or_else(|| language.config().debuggers.first().map(SharedString::from))?;
6099
6100 dap_store.update(cx, |dap_store, cx| {
6101 for (_, task) in &resolved_tasks.templates {
6102 let maybe_scenario = dap_store.debug_scenario_for_build_task(
6103 task.original_task().clone(),
6104 debug_adapter.clone().into(),
6105 task.display_label().to_owned().into(),
6106 cx,
6107 );
6108 scenarios.push(maybe_scenario);
6109 }
6110 });
6111 Some(cx.background_spawn(async move {
6112 let scenarios = futures::future::join_all(scenarios)
6113 .await
6114 .into_iter()
6115 .flatten()
6116 .collect::<Vec<_>>();
6117 scenarios
6118 }))
6119 })
6120 .unwrap_or_else(|| Task::ready(vec![]))
6121 }
6122
6123 fn code_actions(
6124 &mut self,
6125 buffer_row: u32,
6126 window: &mut Window,
6127 cx: &mut Context<Self>,
6128 ) -> Task<Option<Rc<[AvailableCodeAction]>>> {
6129 let mut task = self.code_actions_task.take();
6130 cx.spawn_in(window, async move |editor, cx| {
6131 while let Some(prev_task) = task {
6132 prev_task.await.log_err();
6133 task = editor
6134 .update(cx, |this, _| this.code_actions_task.take())
6135 .ok()?;
6136 }
6137
6138 editor
6139 .update(cx, |editor, cx| {
6140 editor
6141 .available_code_actions
6142 .clone()
6143 .and_then(|(location, code_actions)| {
6144 let snapshot = location.buffer.read(cx).snapshot();
6145 let point_range = location.range.to_point(&snapshot);
6146 let point_range = point_range.start.row..=point_range.end.row;
6147 if point_range.contains(&buffer_row) {
6148 Some(code_actions)
6149 } else {
6150 None
6151 }
6152 })
6153 })
6154 .ok()
6155 .flatten()
6156 })
6157 }
6158
6159 pub fn confirm_code_action(
6160 &mut self,
6161 action: &ConfirmCodeAction,
6162 window: &mut Window,
6163 cx: &mut Context<Self>,
6164 ) -> Option<Task<Result<()>>> {
6165 self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
6166
6167 let actions_menu =
6168 if let CodeContextMenu::CodeActions(menu) = self.hide_context_menu(window, cx)? {
6169 menu
6170 } else {
6171 return None;
6172 };
6173
6174 let action_ix = action.item_ix.unwrap_or(actions_menu.selected_item);
6175 let action = actions_menu.actions.get(action_ix)?;
6176 let title = action.label();
6177 let buffer = actions_menu.buffer;
6178 let workspace = self.workspace()?;
6179
6180 match action {
6181 CodeActionsItem::Task(task_source_kind, resolved_task) => {
6182 workspace.update(cx, |workspace, cx| {
6183 workspace.schedule_resolved_task(
6184 task_source_kind,
6185 resolved_task,
6186 false,
6187 window,
6188 cx,
6189 );
6190
6191 Some(Task::ready(Ok(())))
6192 })
6193 }
6194 CodeActionsItem::CodeAction {
6195 excerpt_id,
6196 action,
6197 provider,
6198 } => {
6199 let apply_code_action =
6200 provider.apply_code_action(buffer, action, excerpt_id, true, window, cx);
6201 let workspace = workspace.downgrade();
6202 Some(cx.spawn_in(window, async move |editor, cx| {
6203 let project_transaction = apply_code_action.await?;
6204 Self::open_project_transaction(
6205 &editor,
6206 workspace,
6207 project_transaction,
6208 title,
6209 cx,
6210 )
6211 .await
6212 }))
6213 }
6214 CodeActionsItem::DebugScenario(scenario) => {
6215 let context = actions_menu.actions.context.clone();
6216
6217 workspace.update(cx, |workspace, cx| {
6218 dap::send_telemetry(&scenario, TelemetrySpawnLocation::Gutter, cx);
6219 workspace.start_debug_session(
6220 scenario,
6221 context,
6222 Some(buffer),
6223 None,
6224 window,
6225 cx,
6226 );
6227 });
6228 Some(Task::ready(Ok(())))
6229 }
6230 }
6231 }
6232
6233 pub async fn open_project_transaction(
6234 this: &WeakEntity<Editor>,
6235 workspace: WeakEntity<Workspace>,
6236 transaction: ProjectTransaction,
6237 title: String,
6238 cx: &mut AsyncWindowContext,
6239 ) -> Result<()> {
6240 let mut entries = transaction.0.into_iter().collect::<Vec<_>>();
6241 cx.update(|_, cx| {
6242 entries.sort_unstable_by_key(|(buffer, _)| {
6243 buffer.read(cx).file().map(|f| f.path().clone())
6244 });
6245 })?;
6246
6247 // If the project transaction's edits are all contained within this editor, then
6248 // avoid opening a new editor to display them.
6249
6250 if let Some((buffer, transaction)) = entries.first() {
6251 if entries.len() == 1 {
6252 let excerpt = this.update(cx, |editor, cx| {
6253 editor
6254 .buffer()
6255 .read(cx)
6256 .excerpt_containing(editor.selections.newest_anchor().head(), cx)
6257 })?;
6258 if let Some((_, excerpted_buffer, excerpt_range)) = excerpt {
6259 if excerpted_buffer == *buffer {
6260 let all_edits_within_excerpt = buffer.read_with(cx, |buffer, _| {
6261 let excerpt_range = excerpt_range.to_offset(buffer);
6262 buffer
6263 .edited_ranges_for_transaction::<usize>(transaction)
6264 .all(|range| {
6265 excerpt_range.start <= range.start
6266 && excerpt_range.end >= range.end
6267 })
6268 })?;
6269
6270 if all_edits_within_excerpt {
6271 return Ok(());
6272 }
6273 }
6274 }
6275 }
6276 } else {
6277 return Ok(());
6278 }
6279
6280 let mut ranges_to_highlight = Vec::new();
6281 let excerpt_buffer = cx.new(|cx| {
6282 let mut multibuffer = MultiBuffer::new(Capability::ReadWrite).with_title(title);
6283 for (buffer_handle, transaction) in &entries {
6284 let edited_ranges = buffer_handle
6285 .read(cx)
6286 .edited_ranges_for_transaction::<Point>(transaction)
6287 .collect::<Vec<_>>();
6288 let (ranges, _) = multibuffer.set_excerpts_for_path(
6289 PathKey::for_buffer(buffer_handle, cx),
6290 buffer_handle.clone(),
6291 edited_ranges,
6292 DEFAULT_MULTIBUFFER_CONTEXT,
6293 cx,
6294 );
6295
6296 ranges_to_highlight.extend(ranges);
6297 }
6298 multibuffer.push_transaction(entries.iter().map(|(b, t)| (b, t)), cx);
6299 multibuffer
6300 })?;
6301
6302 workspace.update_in(cx, |workspace, window, cx| {
6303 let project = workspace.project().clone();
6304 let editor =
6305 cx.new(|cx| Editor::for_multibuffer(excerpt_buffer, Some(project), window, cx));
6306 workspace.add_item_to_active_pane(Box::new(editor.clone()), None, true, window, cx);
6307 editor.update(cx, |editor, cx| {
6308 editor.highlight_background::<Self>(
6309 &ranges_to_highlight,
6310 |theme| theme.colors().editor_highlighted_line_background,
6311 cx,
6312 );
6313 });
6314 })?;
6315
6316 Ok(())
6317 }
6318
6319 pub fn clear_code_action_providers(&mut self) {
6320 self.code_action_providers.clear();
6321 self.available_code_actions.take();
6322 }
6323
6324 pub fn add_code_action_provider(
6325 &mut self,
6326 provider: Rc<dyn CodeActionProvider>,
6327 window: &mut Window,
6328 cx: &mut Context<Self>,
6329 ) {
6330 if self
6331 .code_action_providers
6332 .iter()
6333 .any(|existing_provider| existing_provider.id() == provider.id())
6334 {
6335 return;
6336 }
6337
6338 self.code_action_providers.push(provider);
6339 self.refresh_code_actions(window, cx);
6340 }
6341
6342 pub fn remove_code_action_provider(
6343 &mut self,
6344 id: Arc<str>,
6345 window: &mut Window,
6346 cx: &mut Context<Self>,
6347 ) {
6348 self.code_action_providers
6349 .retain(|provider| provider.id() != id);
6350 self.refresh_code_actions(window, cx);
6351 }
6352
6353 pub fn code_actions_enabled_for_toolbar(&self, cx: &App) -> bool {
6354 !self.code_action_providers.is_empty()
6355 && EditorSettings::get_global(cx).toolbar.code_actions
6356 }
6357
6358 pub fn has_available_code_actions(&self) -> bool {
6359 self.available_code_actions
6360 .as_ref()
6361 .is_some_and(|(_, actions)| !actions.is_empty())
6362 }
6363
6364 fn render_inline_code_actions(
6365 &self,
6366 icon_size: ui::IconSize,
6367 display_row: DisplayRow,
6368 is_active: bool,
6369 cx: &mut Context<Self>,
6370 ) -> AnyElement {
6371 let show_tooltip = !self.context_menu_visible();
6372 IconButton::new("inline_code_actions", ui::IconName::BoltFilled)
6373 .icon_size(icon_size)
6374 .shape(ui::IconButtonShape::Square)
6375 .style(ButtonStyle::Transparent)
6376 .icon_color(ui::Color::Hidden)
6377 .toggle_state(is_active)
6378 .when(show_tooltip, |this| {
6379 this.tooltip({
6380 let focus_handle = self.focus_handle.clone();
6381 move |window, cx| {
6382 Tooltip::for_action_in(
6383 "Toggle Code Actions",
6384 &ToggleCodeActions {
6385 deployed_from: None,
6386 quick_launch: false,
6387 },
6388 &focus_handle,
6389 window,
6390 cx,
6391 )
6392 }
6393 })
6394 })
6395 .on_click(cx.listener(move |editor, _: &ClickEvent, window, cx| {
6396 window.focus(&editor.focus_handle(cx));
6397 editor.toggle_code_actions(
6398 &crate::actions::ToggleCodeActions {
6399 deployed_from: Some(crate::actions::CodeActionSource::Indicator(
6400 display_row,
6401 )),
6402 quick_launch: false,
6403 },
6404 window,
6405 cx,
6406 );
6407 }))
6408 .into_any_element()
6409 }
6410
6411 pub fn context_menu(&self) -> &RefCell<Option<CodeContextMenu>> {
6412 &self.context_menu
6413 }
6414
6415 fn refresh_code_actions(&mut self, window: &mut Window, cx: &mut Context<Self>) -> Option<()> {
6416 let newest_selection = self.selections.newest_anchor().clone();
6417 let newest_selection_adjusted = self.selections.newest_adjusted(cx).clone();
6418 let buffer = self.buffer.read(cx);
6419 if newest_selection.head().diff_base_anchor.is_some() {
6420 return None;
6421 }
6422 let (start_buffer, start) =
6423 buffer.text_anchor_for_position(newest_selection_adjusted.start, cx)?;
6424 let (end_buffer, end) =
6425 buffer.text_anchor_for_position(newest_selection_adjusted.end, cx)?;
6426 if start_buffer != end_buffer {
6427 return None;
6428 }
6429
6430 self.code_actions_task = Some(cx.spawn_in(window, async move |this, cx| {
6431 cx.background_executor()
6432 .timer(CODE_ACTIONS_DEBOUNCE_TIMEOUT)
6433 .await;
6434
6435 let (providers, tasks) = this.update_in(cx, |this, window, cx| {
6436 let providers = this.code_action_providers.clone();
6437 let tasks = this
6438 .code_action_providers
6439 .iter()
6440 .map(|provider| provider.code_actions(&start_buffer, start..end, window, cx))
6441 .collect::<Vec<_>>();
6442 (providers, tasks)
6443 })?;
6444
6445 let mut actions = Vec::new();
6446 for (provider, provider_actions) in
6447 providers.into_iter().zip(future::join_all(tasks).await)
6448 {
6449 if let Some(provider_actions) = provider_actions.log_err() {
6450 actions.extend(provider_actions.into_iter().map(|action| {
6451 AvailableCodeAction {
6452 excerpt_id: newest_selection.start.excerpt_id,
6453 action,
6454 provider: provider.clone(),
6455 }
6456 }));
6457 }
6458 }
6459
6460 this.update(cx, |this, cx| {
6461 this.available_code_actions = if actions.is_empty() {
6462 None
6463 } else {
6464 Some((
6465 Location {
6466 buffer: start_buffer,
6467 range: start..end,
6468 },
6469 actions.into(),
6470 ))
6471 };
6472 cx.notify();
6473 })
6474 }));
6475 None
6476 }
6477
6478 fn start_inline_blame_timer(&mut self, window: &mut Window, cx: &mut Context<Self>) {
6479 if let Some(delay) = ProjectSettings::get_global(cx).git.inline_blame_delay() {
6480 self.show_git_blame_inline = false;
6481
6482 self.show_git_blame_inline_delay_task =
6483 Some(cx.spawn_in(window, async move |this, cx| {
6484 cx.background_executor().timer(delay).await;
6485
6486 this.update(cx, |this, cx| {
6487 this.show_git_blame_inline = true;
6488 cx.notify();
6489 })
6490 .log_err();
6491 }));
6492 }
6493 }
6494
6495 fn show_blame_popover(
6496 &mut self,
6497 blame_entry: &BlameEntry,
6498 position: gpui::Point<Pixels>,
6499 cx: &mut Context<Self>,
6500 ) {
6501 if let Some(state) = &mut self.inline_blame_popover {
6502 state.hide_task.take();
6503 } else {
6504 let delay = EditorSettings::get_global(cx).hover_popover_delay;
6505 let blame_entry = blame_entry.clone();
6506 let show_task = cx.spawn(async move |editor, cx| {
6507 cx.background_executor()
6508 .timer(std::time::Duration::from_millis(delay))
6509 .await;
6510 editor
6511 .update(cx, |editor, cx| {
6512 editor.inline_blame_popover_show_task.take();
6513 let Some(blame) = editor.blame.as_ref() else {
6514 return;
6515 };
6516 let blame = blame.read(cx);
6517 let details = blame.details_for_entry(&blame_entry);
6518 let markdown = cx.new(|cx| {
6519 Markdown::new(
6520 details
6521 .as_ref()
6522 .map(|message| message.message.clone())
6523 .unwrap_or_default(),
6524 None,
6525 None,
6526 cx,
6527 )
6528 });
6529 editor.inline_blame_popover = Some(InlineBlamePopover {
6530 position,
6531 hide_task: None,
6532 popover_bounds: None,
6533 popover_state: InlineBlamePopoverState {
6534 scroll_handle: ScrollHandle::new(),
6535 commit_message: details,
6536 markdown,
6537 },
6538 });
6539 cx.notify();
6540 })
6541 .ok();
6542 });
6543 self.inline_blame_popover_show_task = Some(show_task);
6544 }
6545 }
6546
6547 fn hide_blame_popover(&mut self, cx: &mut Context<Self>) {
6548 self.inline_blame_popover_show_task.take();
6549 if let Some(state) = &mut self.inline_blame_popover {
6550 let hide_task = cx.spawn(async move |editor, cx| {
6551 cx.background_executor()
6552 .timer(std::time::Duration::from_millis(100))
6553 .await;
6554 editor
6555 .update(cx, |editor, cx| {
6556 editor.inline_blame_popover.take();
6557 cx.notify();
6558 })
6559 .ok();
6560 });
6561 state.hide_task = Some(hide_task);
6562 }
6563 }
6564
6565 fn refresh_document_highlights(&mut self, cx: &mut Context<Self>) -> Option<()> {
6566 if self.pending_rename.is_some() {
6567 return None;
6568 }
6569
6570 let provider = self.semantics_provider.clone()?;
6571 let buffer = self.buffer.read(cx);
6572 let newest_selection = self.selections.newest_anchor().clone();
6573 let cursor_position = newest_selection.head();
6574 let (cursor_buffer, cursor_buffer_position) =
6575 buffer.text_anchor_for_position(cursor_position, cx)?;
6576 let (tail_buffer, tail_buffer_position) =
6577 buffer.text_anchor_for_position(newest_selection.tail(), cx)?;
6578 if cursor_buffer != tail_buffer {
6579 return None;
6580 }
6581
6582 let snapshot = cursor_buffer.read(cx).snapshot();
6583 let (start_word_range, _) = snapshot.surrounding_word(cursor_buffer_position);
6584 let (end_word_range, _) = snapshot.surrounding_word(tail_buffer_position);
6585 if start_word_range != end_word_range {
6586 self.document_highlights_task.take();
6587 self.clear_background_highlights::<DocumentHighlightRead>(cx);
6588 self.clear_background_highlights::<DocumentHighlightWrite>(cx);
6589 return None;
6590 }
6591
6592 let debounce = EditorSettings::get_global(cx).lsp_highlight_debounce;
6593 self.document_highlights_task = Some(cx.spawn(async move |this, cx| {
6594 cx.background_executor()
6595 .timer(Duration::from_millis(debounce))
6596 .await;
6597
6598 let highlights = if let Some(highlights) = cx
6599 .update(|cx| {
6600 provider.document_highlights(&cursor_buffer, cursor_buffer_position, cx)
6601 })
6602 .ok()
6603 .flatten()
6604 {
6605 highlights.await.log_err()
6606 } else {
6607 None
6608 };
6609
6610 if let Some(highlights) = highlights {
6611 this.update(cx, |this, cx| {
6612 if this.pending_rename.is_some() {
6613 return;
6614 }
6615
6616 let buffer_id = cursor_position.buffer_id;
6617 let buffer = this.buffer.read(cx);
6618 if !buffer
6619 .text_anchor_for_position(cursor_position, cx)
6620 .map_or(false, |(buffer, _)| buffer == cursor_buffer)
6621 {
6622 return;
6623 }
6624
6625 let cursor_buffer_snapshot = cursor_buffer.read(cx);
6626 let mut write_ranges = Vec::new();
6627 let mut read_ranges = Vec::new();
6628 for highlight in highlights {
6629 for (excerpt_id, excerpt_range) in
6630 buffer.excerpts_for_buffer(cursor_buffer.read(cx).remote_id(), cx)
6631 {
6632 let start = highlight
6633 .range
6634 .start
6635 .max(&excerpt_range.context.start, cursor_buffer_snapshot);
6636 let end = highlight
6637 .range
6638 .end
6639 .min(&excerpt_range.context.end, cursor_buffer_snapshot);
6640 if start.cmp(&end, cursor_buffer_snapshot).is_ge() {
6641 continue;
6642 }
6643
6644 let range = Anchor {
6645 buffer_id,
6646 excerpt_id,
6647 text_anchor: start,
6648 diff_base_anchor: None,
6649 }..Anchor {
6650 buffer_id,
6651 excerpt_id,
6652 text_anchor: end,
6653 diff_base_anchor: None,
6654 };
6655 if highlight.kind == lsp::DocumentHighlightKind::WRITE {
6656 write_ranges.push(range);
6657 } else {
6658 read_ranges.push(range);
6659 }
6660 }
6661 }
6662
6663 this.highlight_background::<DocumentHighlightRead>(
6664 &read_ranges,
6665 |theme| theme.colors().editor_document_highlight_read_background,
6666 cx,
6667 );
6668 this.highlight_background::<DocumentHighlightWrite>(
6669 &write_ranges,
6670 |theme| theme.colors().editor_document_highlight_write_background,
6671 cx,
6672 );
6673 cx.notify();
6674 })
6675 .log_err();
6676 }
6677 }));
6678 None
6679 }
6680
6681 fn prepare_highlight_query_from_selection(
6682 &mut self,
6683 cx: &mut Context<Editor>,
6684 ) -> Option<(String, Range<Anchor>)> {
6685 if matches!(self.mode, EditorMode::SingleLine { .. }) {
6686 return None;
6687 }
6688 if !EditorSettings::get_global(cx).selection_highlight {
6689 return None;
6690 }
6691 if self.selections.count() != 1 || self.selections.line_mode {
6692 return None;
6693 }
6694 let selection = self.selections.newest::<Point>(cx);
6695 if selection.is_empty() || selection.start.row != selection.end.row {
6696 return None;
6697 }
6698 let multi_buffer_snapshot = self.buffer().read(cx).snapshot(cx);
6699 let selection_anchor_range = selection.range().to_anchors(&multi_buffer_snapshot);
6700 let query = multi_buffer_snapshot
6701 .text_for_range(selection_anchor_range.clone())
6702 .collect::<String>();
6703 if query.trim().is_empty() {
6704 return None;
6705 }
6706 Some((query, selection_anchor_range))
6707 }
6708
6709 fn update_selection_occurrence_highlights(
6710 &mut self,
6711 query_text: String,
6712 query_range: Range<Anchor>,
6713 multi_buffer_range_to_query: Range<Point>,
6714 use_debounce: bool,
6715 window: &mut Window,
6716 cx: &mut Context<Editor>,
6717 ) -> Task<()> {
6718 let multi_buffer_snapshot = self.buffer().read(cx).snapshot(cx);
6719 cx.spawn_in(window, async move |editor, cx| {
6720 if use_debounce {
6721 cx.background_executor()
6722 .timer(SELECTION_HIGHLIGHT_DEBOUNCE_TIMEOUT)
6723 .await;
6724 }
6725 let match_task = cx.background_spawn(async move {
6726 let buffer_ranges = multi_buffer_snapshot
6727 .range_to_buffer_ranges(multi_buffer_range_to_query)
6728 .into_iter()
6729 .filter(|(_, excerpt_visible_range, _)| !excerpt_visible_range.is_empty());
6730 let mut match_ranges = Vec::new();
6731 let Ok(regex) = project::search::SearchQuery::text(
6732 query_text.clone(),
6733 false,
6734 false,
6735 false,
6736 Default::default(),
6737 Default::default(),
6738 false,
6739 None,
6740 ) else {
6741 return Vec::default();
6742 };
6743 for (buffer_snapshot, search_range, excerpt_id) in buffer_ranges {
6744 match_ranges.extend(
6745 regex
6746 .search(&buffer_snapshot, Some(search_range.clone()))
6747 .await
6748 .into_iter()
6749 .filter_map(|match_range| {
6750 let match_start = buffer_snapshot
6751 .anchor_after(search_range.start + match_range.start);
6752 let match_end = buffer_snapshot
6753 .anchor_before(search_range.start + match_range.end);
6754 let match_anchor_range = Anchor::range_in_buffer(
6755 excerpt_id,
6756 buffer_snapshot.remote_id(),
6757 match_start..match_end,
6758 );
6759 (match_anchor_range != query_range).then_some(match_anchor_range)
6760 }),
6761 );
6762 }
6763 match_ranges
6764 });
6765 let match_ranges = match_task.await;
6766 editor
6767 .update_in(cx, |editor, _, cx| {
6768 editor.clear_background_highlights::<SelectedTextHighlight>(cx);
6769 if !match_ranges.is_empty() {
6770 editor.highlight_background::<SelectedTextHighlight>(
6771 &match_ranges,
6772 |theme| theme.colors().editor_document_highlight_bracket_background,
6773 cx,
6774 )
6775 }
6776 })
6777 .log_err();
6778 })
6779 }
6780
6781 fn refresh_single_line_folds(&mut self, window: &mut Window, cx: &mut Context<Editor>) {
6782 struct NewlineFold;
6783 let type_id = std::any::TypeId::of::<NewlineFold>();
6784 if !self.mode.is_single_line() {
6785 return;
6786 }
6787 let snapshot = self.snapshot(window, cx);
6788 if snapshot.buffer_snapshot.max_point().row == 0 {
6789 return;
6790 }
6791 let task = cx.background_spawn(async move {
6792 let new_newlines = snapshot
6793 .buffer_chars_at(0)
6794 .filter_map(|(c, i)| {
6795 if c == '\n' {
6796 Some(
6797 snapshot.buffer_snapshot.anchor_after(i)
6798 ..snapshot.buffer_snapshot.anchor_before(i + 1),
6799 )
6800 } else {
6801 None
6802 }
6803 })
6804 .collect::<Vec<_>>();
6805 let existing_newlines = snapshot
6806 .folds_in_range(0..snapshot.buffer_snapshot.len())
6807 .filter_map(|fold| {
6808 if fold.placeholder.type_tag == Some(type_id) {
6809 Some(fold.range.start..fold.range.end)
6810 } else {
6811 None
6812 }
6813 })
6814 .collect::<Vec<_>>();
6815
6816 (new_newlines, existing_newlines)
6817 });
6818 self.folding_newlines = cx.spawn(async move |this, cx| {
6819 let (new_newlines, existing_newlines) = task.await;
6820 if new_newlines == existing_newlines {
6821 return;
6822 }
6823 let placeholder = FoldPlaceholder {
6824 render: Arc::new(move |_, _, cx| {
6825 div()
6826 .bg(cx.theme().status().hint_background)
6827 .border_b_1()
6828 .size_full()
6829 .font(ThemeSettings::get_global(cx).buffer_font.clone())
6830 .border_color(cx.theme().status().hint)
6831 .child("\\n")
6832 .into_any()
6833 }),
6834 constrain_width: false,
6835 merge_adjacent: false,
6836 type_tag: Some(type_id),
6837 };
6838 let creases = new_newlines
6839 .into_iter()
6840 .map(|range| Crease::simple(range, placeholder.clone()))
6841 .collect();
6842 this.update(cx, |this, cx| {
6843 this.display_map.update(cx, |display_map, cx| {
6844 display_map.remove_folds_with_type(existing_newlines, type_id, cx);
6845 display_map.fold(creases, cx);
6846 });
6847 })
6848 .ok();
6849 });
6850 }
6851
6852 fn refresh_selected_text_highlights(
6853 &mut self,
6854 on_buffer_edit: bool,
6855 window: &mut Window,
6856 cx: &mut Context<Editor>,
6857 ) {
6858 let Some((query_text, query_range)) = self.prepare_highlight_query_from_selection(cx)
6859 else {
6860 self.clear_background_highlights::<SelectedTextHighlight>(cx);
6861 self.quick_selection_highlight_task.take();
6862 self.debounced_selection_highlight_task.take();
6863 return;
6864 };
6865 let multi_buffer_snapshot = self.buffer().read(cx).snapshot(cx);
6866 if on_buffer_edit
6867 || self
6868 .quick_selection_highlight_task
6869 .as_ref()
6870 .map_or(true, |(prev_anchor_range, _)| {
6871 prev_anchor_range != &query_range
6872 })
6873 {
6874 let multi_buffer_visible_start = self
6875 .scroll_manager
6876 .anchor()
6877 .anchor
6878 .to_point(&multi_buffer_snapshot);
6879 let multi_buffer_visible_end = multi_buffer_snapshot.clip_point(
6880 multi_buffer_visible_start
6881 + Point::new(self.visible_line_count().unwrap_or(0.).ceil() as u32, 0),
6882 Bias::Left,
6883 );
6884 let multi_buffer_visible_range = multi_buffer_visible_start..multi_buffer_visible_end;
6885 self.quick_selection_highlight_task = Some((
6886 query_range.clone(),
6887 self.update_selection_occurrence_highlights(
6888 query_text.clone(),
6889 query_range.clone(),
6890 multi_buffer_visible_range,
6891 false,
6892 window,
6893 cx,
6894 ),
6895 ));
6896 }
6897 if on_buffer_edit
6898 || self
6899 .debounced_selection_highlight_task
6900 .as_ref()
6901 .map_or(true, |(prev_anchor_range, _)| {
6902 prev_anchor_range != &query_range
6903 })
6904 {
6905 let multi_buffer_start = multi_buffer_snapshot
6906 .anchor_before(0)
6907 .to_point(&multi_buffer_snapshot);
6908 let multi_buffer_end = multi_buffer_snapshot
6909 .anchor_after(multi_buffer_snapshot.len())
6910 .to_point(&multi_buffer_snapshot);
6911 let multi_buffer_full_range = multi_buffer_start..multi_buffer_end;
6912 self.debounced_selection_highlight_task = Some((
6913 query_range.clone(),
6914 self.update_selection_occurrence_highlights(
6915 query_text,
6916 query_range,
6917 multi_buffer_full_range,
6918 true,
6919 window,
6920 cx,
6921 ),
6922 ));
6923 }
6924 }
6925
6926 pub fn refresh_inline_completion(
6927 &mut self,
6928 debounce: bool,
6929 user_requested: bool,
6930 window: &mut Window,
6931 cx: &mut Context<Self>,
6932 ) -> Option<()> {
6933 let provider = self.edit_prediction_provider()?;
6934 let cursor = self.selections.newest_anchor().head();
6935 let (buffer, cursor_buffer_position) =
6936 self.buffer.read(cx).text_anchor_for_position(cursor, cx)?;
6937
6938 if !self.edit_predictions_enabled_in_buffer(&buffer, cursor_buffer_position, cx) {
6939 self.discard_inline_completion(false, cx);
6940 return None;
6941 }
6942
6943 if !user_requested
6944 && (!self.should_show_edit_predictions()
6945 || !self.is_focused(window)
6946 || buffer.read(cx).is_empty())
6947 {
6948 self.discard_inline_completion(false, cx);
6949 return None;
6950 }
6951
6952 self.update_visible_inline_completion(window, cx);
6953 provider.refresh(
6954 self.project.clone(),
6955 buffer,
6956 cursor_buffer_position,
6957 debounce,
6958 cx,
6959 );
6960 Some(())
6961 }
6962
6963 fn show_edit_predictions_in_menu(&self) -> bool {
6964 match self.edit_prediction_settings {
6965 EditPredictionSettings::Disabled => false,
6966 EditPredictionSettings::Enabled { show_in_menu, .. } => show_in_menu,
6967 }
6968 }
6969
6970 pub fn edit_predictions_enabled(&self) -> bool {
6971 match self.edit_prediction_settings {
6972 EditPredictionSettings::Disabled => false,
6973 EditPredictionSettings::Enabled { .. } => true,
6974 }
6975 }
6976
6977 fn edit_prediction_requires_modifier(&self) -> bool {
6978 match self.edit_prediction_settings {
6979 EditPredictionSettings::Disabled => false,
6980 EditPredictionSettings::Enabled {
6981 preview_requires_modifier,
6982 ..
6983 } => preview_requires_modifier,
6984 }
6985 }
6986
6987 pub fn update_edit_prediction_settings(&mut self, cx: &mut Context<Self>) {
6988 if self.edit_prediction_provider.is_none() {
6989 self.edit_prediction_settings = EditPredictionSettings::Disabled;
6990 } else {
6991 let selection = self.selections.newest_anchor();
6992 let cursor = selection.head();
6993
6994 if let Some((buffer, cursor_buffer_position)) =
6995 self.buffer.read(cx).text_anchor_for_position(cursor, cx)
6996 {
6997 self.edit_prediction_settings =
6998 self.edit_prediction_settings_at_position(&buffer, cursor_buffer_position, cx);
6999 }
7000 }
7001 }
7002
7003 fn edit_prediction_settings_at_position(
7004 &self,
7005 buffer: &Entity<Buffer>,
7006 buffer_position: language::Anchor,
7007 cx: &App,
7008 ) -> EditPredictionSettings {
7009 if !self.mode.is_full()
7010 || !self.show_inline_completions_override.unwrap_or(true)
7011 || self.inline_completions_disabled_in_scope(buffer, buffer_position, cx)
7012 {
7013 return EditPredictionSettings::Disabled;
7014 }
7015
7016 let buffer = buffer.read(cx);
7017
7018 let file = buffer.file();
7019
7020 if !language_settings(buffer.language().map(|l| l.name()), file, cx).show_edit_predictions {
7021 return EditPredictionSettings::Disabled;
7022 };
7023
7024 let by_provider = matches!(
7025 self.menu_inline_completions_policy,
7026 MenuInlineCompletionsPolicy::ByProvider
7027 );
7028
7029 let show_in_menu = by_provider
7030 && self
7031 .edit_prediction_provider
7032 .as_ref()
7033 .map_or(false, |provider| {
7034 provider.provider.show_completions_in_menu()
7035 });
7036
7037 let preview_requires_modifier =
7038 all_language_settings(file, cx).edit_predictions_mode() == EditPredictionsMode::Subtle;
7039
7040 EditPredictionSettings::Enabled {
7041 show_in_menu,
7042 preview_requires_modifier,
7043 }
7044 }
7045
7046 fn should_show_edit_predictions(&self) -> bool {
7047 self.snippet_stack.is_empty() && self.edit_predictions_enabled()
7048 }
7049
7050 pub fn edit_prediction_preview_is_active(&self) -> bool {
7051 matches!(
7052 self.edit_prediction_preview,
7053 EditPredictionPreview::Active { .. }
7054 )
7055 }
7056
7057 pub fn edit_predictions_enabled_at_cursor(&self, cx: &App) -> bool {
7058 let cursor = self.selections.newest_anchor().head();
7059 if let Some((buffer, cursor_position)) =
7060 self.buffer.read(cx).text_anchor_for_position(cursor, cx)
7061 {
7062 self.edit_predictions_enabled_in_buffer(&buffer, cursor_position, cx)
7063 } else {
7064 false
7065 }
7066 }
7067
7068 pub fn supports_minimap(&self, cx: &App) -> bool {
7069 !self.minimap_visibility.disabled() && self.is_singleton(cx)
7070 }
7071
7072 fn edit_predictions_enabled_in_buffer(
7073 &self,
7074 buffer: &Entity<Buffer>,
7075 buffer_position: language::Anchor,
7076 cx: &App,
7077 ) -> bool {
7078 maybe!({
7079 if self.read_only(cx) {
7080 return Some(false);
7081 }
7082 let provider = self.edit_prediction_provider()?;
7083 if !provider.is_enabled(&buffer, buffer_position, cx) {
7084 return Some(false);
7085 }
7086 let buffer = buffer.read(cx);
7087 let Some(file) = buffer.file() else {
7088 return Some(true);
7089 };
7090 let settings = all_language_settings(Some(file), cx);
7091 Some(settings.edit_predictions_enabled_for_file(file, cx))
7092 })
7093 .unwrap_or(false)
7094 }
7095
7096 fn cycle_inline_completion(
7097 &mut self,
7098 direction: Direction,
7099 window: &mut Window,
7100 cx: &mut Context<Self>,
7101 ) -> Option<()> {
7102 let provider = self.edit_prediction_provider()?;
7103 let cursor = self.selections.newest_anchor().head();
7104 let (buffer, cursor_buffer_position) =
7105 self.buffer.read(cx).text_anchor_for_position(cursor, cx)?;
7106 if self.inline_completions_hidden_for_vim_mode || !self.should_show_edit_predictions() {
7107 return None;
7108 }
7109
7110 provider.cycle(buffer, cursor_buffer_position, direction, cx);
7111 self.update_visible_inline_completion(window, cx);
7112
7113 Some(())
7114 }
7115
7116 pub fn show_inline_completion(
7117 &mut self,
7118 _: &ShowEditPrediction,
7119 window: &mut Window,
7120 cx: &mut Context<Self>,
7121 ) {
7122 if !self.has_active_inline_completion() {
7123 self.refresh_inline_completion(false, true, window, cx);
7124 return;
7125 }
7126
7127 self.update_visible_inline_completion(window, cx);
7128 }
7129
7130 pub fn display_cursor_names(
7131 &mut self,
7132 _: &DisplayCursorNames,
7133 window: &mut Window,
7134 cx: &mut Context<Self>,
7135 ) {
7136 self.show_cursor_names(window, cx);
7137 }
7138
7139 fn show_cursor_names(&mut self, window: &mut Window, cx: &mut Context<Self>) {
7140 self.show_cursor_names = true;
7141 cx.notify();
7142 cx.spawn_in(window, async move |this, cx| {
7143 cx.background_executor().timer(CURSORS_VISIBLE_FOR).await;
7144 this.update(cx, |this, cx| {
7145 this.show_cursor_names = false;
7146 cx.notify()
7147 })
7148 .ok()
7149 })
7150 .detach();
7151 }
7152
7153 pub fn next_edit_prediction(
7154 &mut self,
7155 _: &NextEditPrediction,
7156 window: &mut Window,
7157 cx: &mut Context<Self>,
7158 ) {
7159 if self.has_active_inline_completion() {
7160 self.cycle_inline_completion(Direction::Next, window, cx);
7161 } else {
7162 let is_copilot_disabled = self
7163 .refresh_inline_completion(false, true, window, cx)
7164 .is_none();
7165 if is_copilot_disabled {
7166 cx.propagate();
7167 }
7168 }
7169 }
7170
7171 pub fn previous_edit_prediction(
7172 &mut self,
7173 _: &PreviousEditPrediction,
7174 window: &mut Window,
7175 cx: &mut Context<Self>,
7176 ) {
7177 if self.has_active_inline_completion() {
7178 self.cycle_inline_completion(Direction::Prev, window, cx);
7179 } else {
7180 let is_copilot_disabled = self
7181 .refresh_inline_completion(false, true, window, cx)
7182 .is_none();
7183 if is_copilot_disabled {
7184 cx.propagate();
7185 }
7186 }
7187 }
7188
7189 pub fn accept_edit_prediction(
7190 &mut self,
7191 _: &AcceptEditPrediction,
7192 window: &mut Window,
7193 cx: &mut Context<Self>,
7194 ) {
7195 if self.show_edit_predictions_in_menu() {
7196 self.hide_context_menu(window, cx);
7197 }
7198
7199 let Some(active_inline_completion) = self.active_inline_completion.as_ref() else {
7200 return;
7201 };
7202
7203 self.report_inline_completion_event(
7204 active_inline_completion.completion_id.clone(),
7205 true,
7206 cx,
7207 );
7208
7209 match &active_inline_completion.completion {
7210 InlineCompletion::Move { target, .. } => {
7211 let target = *target;
7212
7213 if let Some(position_map) = &self.last_position_map {
7214 if position_map
7215 .visible_row_range
7216 .contains(&target.to_display_point(&position_map.snapshot).row())
7217 || !self.edit_prediction_requires_modifier()
7218 {
7219 self.unfold_ranges(&[target..target], true, false, cx);
7220 // Note that this is also done in vim's handler of the Tab action.
7221 self.change_selections(
7222 SelectionEffects::scroll(Autoscroll::newest()),
7223 window,
7224 cx,
7225 |selections| {
7226 selections.select_anchor_ranges([target..target]);
7227 },
7228 );
7229 self.clear_row_highlights::<EditPredictionPreview>();
7230
7231 self.edit_prediction_preview
7232 .set_previous_scroll_position(None);
7233 } else {
7234 self.edit_prediction_preview
7235 .set_previous_scroll_position(Some(
7236 position_map.snapshot.scroll_anchor,
7237 ));
7238
7239 self.highlight_rows::<EditPredictionPreview>(
7240 target..target,
7241 cx.theme().colors().editor_highlighted_line_background,
7242 RowHighlightOptions {
7243 autoscroll: true,
7244 ..Default::default()
7245 },
7246 cx,
7247 );
7248 self.request_autoscroll(Autoscroll::fit(), cx);
7249 }
7250 }
7251 }
7252 InlineCompletion::Edit { edits, .. } => {
7253 if let Some(provider) = self.edit_prediction_provider() {
7254 provider.accept(cx);
7255 }
7256
7257 // Store the transaction ID and selections before applying the edit
7258 let transaction_id_prev = self.buffer.read(cx).last_transaction_id(cx);
7259
7260 let snapshot = self.buffer.read(cx).snapshot(cx);
7261 let last_edit_end = edits.last().unwrap().0.end.bias_right(&snapshot);
7262
7263 self.buffer.update(cx, |buffer, cx| {
7264 buffer.edit(edits.iter().cloned(), None, cx)
7265 });
7266
7267 self.change_selections(SelectionEffects::no_scroll(), window, cx, |s| {
7268 s.select_anchor_ranges([last_edit_end..last_edit_end]);
7269 });
7270
7271 let selections = self.selections.disjoint_anchors();
7272 if let Some(transaction_id_now) = self.buffer.read(cx).last_transaction_id(cx) {
7273 let has_new_transaction = transaction_id_prev != Some(transaction_id_now);
7274 if has_new_transaction {
7275 self.selection_history
7276 .insert_transaction(transaction_id_now, selections);
7277 }
7278 }
7279
7280 self.update_visible_inline_completion(window, cx);
7281 if self.active_inline_completion.is_none() {
7282 self.refresh_inline_completion(true, true, window, cx);
7283 }
7284
7285 cx.notify();
7286 }
7287 }
7288
7289 self.edit_prediction_requires_modifier_in_indent_conflict = false;
7290 }
7291
7292 pub fn accept_partial_inline_completion(
7293 &mut self,
7294 _: &AcceptPartialEditPrediction,
7295 window: &mut Window,
7296 cx: &mut Context<Self>,
7297 ) {
7298 let Some(active_inline_completion) = self.active_inline_completion.as_ref() else {
7299 return;
7300 };
7301 if self.selections.count() != 1 {
7302 return;
7303 }
7304
7305 self.report_inline_completion_event(
7306 active_inline_completion.completion_id.clone(),
7307 true,
7308 cx,
7309 );
7310
7311 match &active_inline_completion.completion {
7312 InlineCompletion::Move { target, .. } => {
7313 let target = *target;
7314 self.change_selections(
7315 SelectionEffects::scroll(Autoscroll::newest()),
7316 window,
7317 cx,
7318 |selections| {
7319 selections.select_anchor_ranges([target..target]);
7320 },
7321 );
7322 }
7323 InlineCompletion::Edit { edits, .. } => {
7324 // Find an insertion that starts at the cursor position.
7325 let snapshot = self.buffer.read(cx).snapshot(cx);
7326 let cursor_offset = self.selections.newest::<usize>(cx).head();
7327 let insertion = edits.iter().find_map(|(range, text)| {
7328 let range = range.to_offset(&snapshot);
7329 if range.is_empty() && range.start == cursor_offset {
7330 Some(text)
7331 } else {
7332 None
7333 }
7334 });
7335
7336 if let Some(text) = insertion {
7337 let mut partial_completion = text
7338 .chars()
7339 .by_ref()
7340 .take_while(|c| c.is_alphabetic())
7341 .collect::<String>();
7342 if partial_completion.is_empty() {
7343 partial_completion = text
7344 .chars()
7345 .by_ref()
7346 .take_while(|c| c.is_whitespace() || !c.is_alphabetic())
7347 .collect::<String>();
7348 }
7349
7350 cx.emit(EditorEvent::InputHandled {
7351 utf16_range_to_replace: None,
7352 text: partial_completion.clone().into(),
7353 });
7354
7355 self.insert_with_autoindent_mode(&partial_completion, None, window, cx);
7356
7357 self.refresh_inline_completion(true, true, window, cx);
7358 cx.notify();
7359 } else {
7360 self.accept_edit_prediction(&Default::default(), window, cx);
7361 }
7362 }
7363 }
7364 }
7365
7366 fn discard_inline_completion(
7367 &mut self,
7368 should_report_inline_completion_event: bool,
7369 cx: &mut Context<Self>,
7370 ) -> bool {
7371 if should_report_inline_completion_event {
7372 let completion_id = self
7373 .active_inline_completion
7374 .as_ref()
7375 .and_then(|active_completion| active_completion.completion_id.clone());
7376
7377 self.report_inline_completion_event(completion_id, false, cx);
7378 }
7379
7380 if let Some(provider) = self.edit_prediction_provider() {
7381 provider.discard(cx);
7382 }
7383
7384 self.take_active_inline_completion(cx)
7385 }
7386
7387 fn report_inline_completion_event(&self, id: Option<SharedString>, accepted: bool, cx: &App) {
7388 let Some(provider) = self.edit_prediction_provider() else {
7389 return;
7390 };
7391
7392 let Some((_, buffer, _)) = self
7393 .buffer
7394 .read(cx)
7395 .excerpt_containing(self.selections.newest_anchor().head(), cx)
7396 else {
7397 return;
7398 };
7399
7400 let extension = buffer
7401 .read(cx)
7402 .file()
7403 .and_then(|file| Some(file.path().extension()?.to_string_lossy().to_string()));
7404
7405 let event_type = match accepted {
7406 true => "Edit Prediction Accepted",
7407 false => "Edit Prediction Discarded",
7408 };
7409 telemetry::event!(
7410 event_type,
7411 provider = provider.name(),
7412 prediction_id = id,
7413 suggestion_accepted = accepted,
7414 file_extension = extension,
7415 );
7416 }
7417
7418 pub fn has_active_inline_completion(&self) -> bool {
7419 self.active_inline_completion.is_some()
7420 }
7421
7422 fn take_active_inline_completion(&mut self, cx: &mut Context<Self>) -> bool {
7423 let Some(active_inline_completion) = self.active_inline_completion.take() else {
7424 return false;
7425 };
7426
7427 self.splice_inlays(&active_inline_completion.inlay_ids, Default::default(), cx);
7428 self.clear_highlights::<InlineCompletionHighlight>(cx);
7429 self.stale_inline_completion_in_menu = Some(active_inline_completion);
7430 true
7431 }
7432
7433 /// Returns true when we're displaying the edit prediction popover below the cursor
7434 /// like we are not previewing and the LSP autocomplete menu is visible
7435 /// or we are in `when_holding_modifier` mode.
7436 pub fn edit_prediction_visible_in_cursor_popover(&self, has_completion: bool) -> bool {
7437 if self.edit_prediction_preview_is_active()
7438 || !self.show_edit_predictions_in_menu()
7439 || !self.edit_predictions_enabled()
7440 {
7441 return false;
7442 }
7443
7444 if self.has_visible_completions_menu() {
7445 return true;
7446 }
7447
7448 has_completion && self.edit_prediction_requires_modifier()
7449 }
7450
7451 fn handle_modifiers_changed(
7452 &mut self,
7453 modifiers: Modifiers,
7454 position_map: &PositionMap,
7455 window: &mut Window,
7456 cx: &mut Context<Self>,
7457 ) {
7458 if self.show_edit_predictions_in_menu() {
7459 self.update_edit_prediction_preview(&modifiers, window, cx);
7460 }
7461
7462 self.update_selection_mode(&modifiers, position_map, window, cx);
7463
7464 let mouse_position = window.mouse_position();
7465 if !position_map.text_hitbox.is_hovered(window) {
7466 return;
7467 }
7468
7469 self.update_hovered_link(
7470 position_map.point_for_position(mouse_position),
7471 &position_map.snapshot,
7472 modifiers,
7473 window,
7474 cx,
7475 )
7476 }
7477
7478 fn multi_cursor_modifier(invert: bool, modifiers: &Modifiers, cx: &mut Context<Self>) -> bool {
7479 let multi_cursor_setting = EditorSettings::get_global(cx).multi_cursor_modifier;
7480 if invert {
7481 match multi_cursor_setting {
7482 MultiCursorModifier::Alt => modifiers.alt,
7483 MultiCursorModifier::CmdOrCtrl => modifiers.secondary(),
7484 }
7485 } else {
7486 match multi_cursor_setting {
7487 MultiCursorModifier::Alt => modifiers.secondary(),
7488 MultiCursorModifier::CmdOrCtrl => modifiers.alt,
7489 }
7490 }
7491 }
7492
7493 fn columnar_selection_mode(
7494 modifiers: &Modifiers,
7495 cx: &mut Context<Self>,
7496 ) -> Option<ColumnarMode> {
7497 if modifiers.shift && modifiers.number_of_modifiers() == 2 {
7498 if Self::multi_cursor_modifier(false, modifiers, cx) {
7499 Some(ColumnarMode::FromMouse)
7500 } else if Self::multi_cursor_modifier(true, modifiers, cx) {
7501 Some(ColumnarMode::FromSelection)
7502 } else {
7503 None
7504 }
7505 } else {
7506 None
7507 }
7508 }
7509
7510 fn update_selection_mode(
7511 &mut self,
7512 modifiers: &Modifiers,
7513 position_map: &PositionMap,
7514 window: &mut Window,
7515 cx: &mut Context<Self>,
7516 ) {
7517 let Some(mode) = Self::columnar_selection_mode(modifiers, cx) else {
7518 return;
7519 };
7520 if self.selections.pending.is_none() {
7521 return;
7522 }
7523
7524 let mouse_position = window.mouse_position();
7525 let point_for_position = position_map.point_for_position(mouse_position);
7526 let position = point_for_position.previous_valid;
7527
7528 self.select(
7529 SelectPhase::BeginColumnar {
7530 position,
7531 reset: false,
7532 mode,
7533 goal_column: point_for_position.exact_unclipped.column(),
7534 },
7535 window,
7536 cx,
7537 );
7538 }
7539
7540 fn update_edit_prediction_preview(
7541 &mut self,
7542 modifiers: &Modifiers,
7543 window: &mut Window,
7544 cx: &mut Context<Self>,
7545 ) {
7546 let mut modifiers_held = false;
7547 if let Some(accept_keystroke) = self
7548 .accept_edit_prediction_keybind(false, window, cx)
7549 .keystroke()
7550 {
7551 modifiers_held = modifiers_held
7552 || (&accept_keystroke.modifiers == modifiers
7553 && accept_keystroke.modifiers.modified());
7554 };
7555 if let Some(accept_partial_keystroke) = self
7556 .accept_edit_prediction_keybind(true, window, cx)
7557 .keystroke()
7558 {
7559 modifiers_held = modifiers_held
7560 || (&accept_partial_keystroke.modifiers == modifiers
7561 && accept_partial_keystroke.modifiers.modified());
7562 }
7563
7564 if modifiers_held {
7565 if matches!(
7566 self.edit_prediction_preview,
7567 EditPredictionPreview::Inactive { .. }
7568 ) {
7569 self.edit_prediction_preview = EditPredictionPreview::Active {
7570 previous_scroll_position: None,
7571 since: Instant::now(),
7572 };
7573
7574 self.update_visible_inline_completion(window, cx);
7575 cx.notify();
7576 }
7577 } else if let EditPredictionPreview::Active {
7578 previous_scroll_position,
7579 since,
7580 } = self.edit_prediction_preview
7581 {
7582 if let (Some(previous_scroll_position), Some(position_map)) =
7583 (previous_scroll_position, self.last_position_map.as_ref())
7584 {
7585 self.set_scroll_position(
7586 previous_scroll_position
7587 .scroll_position(&position_map.snapshot.display_snapshot),
7588 window,
7589 cx,
7590 );
7591 }
7592
7593 self.edit_prediction_preview = EditPredictionPreview::Inactive {
7594 released_too_fast: since.elapsed() < Duration::from_millis(200),
7595 };
7596 self.clear_row_highlights::<EditPredictionPreview>();
7597 self.update_visible_inline_completion(window, cx);
7598 cx.notify();
7599 }
7600 }
7601
7602 fn update_visible_inline_completion(
7603 &mut self,
7604 _window: &mut Window,
7605 cx: &mut Context<Self>,
7606 ) -> Option<()> {
7607 let selection = self.selections.newest_anchor();
7608 let cursor = selection.head();
7609 let multibuffer = self.buffer.read(cx).snapshot(cx);
7610 let offset_selection = selection.map(|endpoint| endpoint.to_offset(&multibuffer));
7611 let excerpt_id = cursor.excerpt_id;
7612
7613 let show_in_menu = self.show_edit_predictions_in_menu();
7614 let completions_menu_has_precedence = !show_in_menu
7615 && (self.context_menu.borrow().is_some()
7616 || (!self.completion_tasks.is_empty() && !self.has_active_inline_completion()));
7617
7618 if completions_menu_has_precedence
7619 || !offset_selection.is_empty()
7620 || self
7621 .active_inline_completion
7622 .as_ref()
7623 .map_or(false, |completion| {
7624 let invalidation_range = completion.invalidation_range.to_offset(&multibuffer);
7625 let invalidation_range = invalidation_range.start..=invalidation_range.end;
7626 !invalidation_range.contains(&offset_selection.head())
7627 })
7628 {
7629 self.discard_inline_completion(false, cx);
7630 return None;
7631 }
7632
7633 self.take_active_inline_completion(cx);
7634 let Some(provider) = self.edit_prediction_provider() else {
7635 self.edit_prediction_settings = EditPredictionSettings::Disabled;
7636 return None;
7637 };
7638
7639 let (buffer, cursor_buffer_position) =
7640 self.buffer.read(cx).text_anchor_for_position(cursor, cx)?;
7641
7642 self.edit_prediction_settings =
7643 self.edit_prediction_settings_at_position(&buffer, cursor_buffer_position, cx);
7644
7645 self.edit_prediction_indent_conflict = multibuffer.is_line_whitespace_upto(cursor);
7646
7647 if self.edit_prediction_indent_conflict {
7648 let cursor_point = cursor.to_point(&multibuffer);
7649
7650 let indents = multibuffer.suggested_indents(cursor_point.row..cursor_point.row + 1, cx);
7651
7652 if let Some((_, indent)) = indents.iter().next() {
7653 if indent.len == cursor_point.column {
7654 self.edit_prediction_indent_conflict = false;
7655 }
7656 }
7657 }
7658
7659 let inline_completion = provider.suggest(&buffer, cursor_buffer_position, cx)?;
7660 let edits = inline_completion
7661 .edits
7662 .into_iter()
7663 .flat_map(|(range, new_text)| {
7664 let start = multibuffer.anchor_in_excerpt(excerpt_id, range.start)?;
7665 let end = multibuffer.anchor_in_excerpt(excerpt_id, range.end)?;
7666 Some((start..end, new_text))
7667 })
7668 .collect::<Vec<_>>();
7669 if edits.is_empty() {
7670 return None;
7671 }
7672
7673 let first_edit_start = edits.first().unwrap().0.start;
7674 let first_edit_start_point = first_edit_start.to_point(&multibuffer);
7675 let edit_start_row = first_edit_start_point.row.saturating_sub(2);
7676
7677 let last_edit_end = edits.last().unwrap().0.end;
7678 let last_edit_end_point = last_edit_end.to_point(&multibuffer);
7679 let edit_end_row = cmp::min(multibuffer.max_point().row, last_edit_end_point.row + 2);
7680
7681 let cursor_row = cursor.to_point(&multibuffer).row;
7682
7683 let snapshot = multibuffer.buffer_for_excerpt(excerpt_id).cloned()?;
7684
7685 let mut inlay_ids = Vec::new();
7686 let invalidation_row_range;
7687 let move_invalidation_row_range = if cursor_row < edit_start_row {
7688 Some(cursor_row..edit_end_row)
7689 } else if cursor_row > edit_end_row {
7690 Some(edit_start_row..cursor_row)
7691 } else {
7692 None
7693 };
7694 let is_move =
7695 move_invalidation_row_range.is_some() || self.inline_completions_hidden_for_vim_mode;
7696 let completion = if is_move {
7697 invalidation_row_range =
7698 move_invalidation_row_range.unwrap_or(edit_start_row..edit_end_row);
7699 let target = first_edit_start;
7700 InlineCompletion::Move { target, snapshot }
7701 } else {
7702 let show_completions_in_buffer = !self.edit_prediction_visible_in_cursor_popover(true)
7703 && !self.inline_completions_hidden_for_vim_mode;
7704
7705 if show_completions_in_buffer {
7706 if edits
7707 .iter()
7708 .all(|(range, _)| range.to_offset(&multibuffer).is_empty())
7709 {
7710 let mut inlays = Vec::new();
7711 for (range, new_text) in &edits {
7712 let inlay = Inlay::inline_completion(
7713 post_inc(&mut self.next_inlay_id),
7714 range.start,
7715 new_text.as_str(),
7716 );
7717 inlay_ids.push(inlay.id);
7718 inlays.push(inlay);
7719 }
7720
7721 self.splice_inlays(&[], inlays, cx);
7722 } else {
7723 let background_color = cx.theme().status().deleted_background;
7724 self.highlight_text::<InlineCompletionHighlight>(
7725 edits.iter().map(|(range, _)| range.clone()).collect(),
7726 HighlightStyle {
7727 background_color: Some(background_color),
7728 ..Default::default()
7729 },
7730 cx,
7731 );
7732 }
7733 }
7734
7735 invalidation_row_range = edit_start_row..edit_end_row;
7736
7737 let display_mode = if all_edits_insertions_or_deletions(&edits, &multibuffer) {
7738 if provider.show_tab_accept_marker() {
7739 EditDisplayMode::TabAccept
7740 } else {
7741 EditDisplayMode::Inline
7742 }
7743 } else {
7744 EditDisplayMode::DiffPopover
7745 };
7746
7747 InlineCompletion::Edit {
7748 edits,
7749 edit_preview: inline_completion.edit_preview,
7750 display_mode,
7751 snapshot,
7752 }
7753 };
7754
7755 let invalidation_range = multibuffer
7756 .anchor_before(Point::new(invalidation_row_range.start, 0))
7757 ..multibuffer.anchor_after(Point::new(
7758 invalidation_row_range.end,
7759 multibuffer.line_len(MultiBufferRow(invalidation_row_range.end)),
7760 ));
7761
7762 self.stale_inline_completion_in_menu = None;
7763 self.active_inline_completion = Some(InlineCompletionState {
7764 inlay_ids,
7765 completion,
7766 completion_id: inline_completion.id,
7767 invalidation_range,
7768 });
7769
7770 cx.notify();
7771
7772 Some(())
7773 }
7774
7775 pub fn edit_prediction_provider(&self) -> Option<Arc<dyn InlineCompletionProviderHandle>> {
7776 Some(self.edit_prediction_provider.as_ref()?.provider.clone())
7777 }
7778
7779 fn clear_tasks(&mut self) {
7780 self.tasks.clear()
7781 }
7782
7783 fn insert_tasks(&mut self, key: (BufferId, BufferRow), value: RunnableTasks) {
7784 if self.tasks.insert(key, value).is_some() {
7785 // This case should hopefully be rare, but just in case...
7786 log::error!(
7787 "multiple different run targets found on a single line, only the last target will be rendered"
7788 )
7789 }
7790 }
7791
7792 /// Get all display points of breakpoints that will be rendered within editor
7793 ///
7794 /// This function is used to handle overlaps between breakpoints and Code action/runner symbol.
7795 /// It's also used to set the color of line numbers with breakpoints to the breakpoint color.
7796 /// TODO debugger: Use this function to color toggle symbols that house nested breakpoints
7797 fn active_breakpoints(
7798 &self,
7799 range: Range<DisplayRow>,
7800 window: &mut Window,
7801 cx: &mut Context<Self>,
7802 ) -> HashMap<DisplayRow, (Anchor, Breakpoint, Option<BreakpointSessionState>)> {
7803 let mut breakpoint_display_points = HashMap::default();
7804
7805 let Some(breakpoint_store) = self.breakpoint_store.clone() else {
7806 return breakpoint_display_points;
7807 };
7808
7809 let snapshot = self.snapshot(window, cx);
7810
7811 let multi_buffer_snapshot = &snapshot.display_snapshot.buffer_snapshot;
7812 let Some(project) = self.project.as_ref() else {
7813 return breakpoint_display_points;
7814 };
7815
7816 let range = snapshot.display_point_to_point(DisplayPoint::new(range.start, 0), Bias::Left)
7817 ..snapshot.display_point_to_point(DisplayPoint::new(range.end, 0), Bias::Right);
7818
7819 for (buffer_snapshot, range, excerpt_id) in
7820 multi_buffer_snapshot.range_to_buffer_ranges(range)
7821 {
7822 let Some(buffer) = project
7823 .read(cx)
7824 .buffer_for_id(buffer_snapshot.remote_id(), cx)
7825 else {
7826 continue;
7827 };
7828 let breakpoints = breakpoint_store.read(cx).breakpoints(
7829 &buffer,
7830 Some(
7831 buffer_snapshot.anchor_before(range.start)
7832 ..buffer_snapshot.anchor_after(range.end),
7833 ),
7834 buffer_snapshot,
7835 cx,
7836 );
7837 for (breakpoint, state) in breakpoints {
7838 let multi_buffer_anchor =
7839 Anchor::in_buffer(excerpt_id, buffer_snapshot.remote_id(), breakpoint.position);
7840 let position = multi_buffer_anchor
7841 .to_point(&multi_buffer_snapshot)
7842 .to_display_point(&snapshot);
7843
7844 breakpoint_display_points.insert(
7845 position.row(),
7846 (multi_buffer_anchor, breakpoint.bp.clone(), state),
7847 );
7848 }
7849 }
7850
7851 breakpoint_display_points
7852 }
7853
7854 fn breakpoint_context_menu(
7855 &self,
7856 anchor: Anchor,
7857 window: &mut Window,
7858 cx: &mut Context<Self>,
7859 ) -> Entity<ui::ContextMenu> {
7860 let weak_editor = cx.weak_entity();
7861 let focus_handle = self.focus_handle(cx);
7862
7863 let row = self
7864 .buffer
7865 .read(cx)
7866 .snapshot(cx)
7867 .summary_for_anchor::<Point>(&anchor)
7868 .row;
7869
7870 let breakpoint = self
7871 .breakpoint_at_row(row, window, cx)
7872 .map(|(anchor, bp)| (anchor, Arc::from(bp)));
7873
7874 let log_breakpoint_msg = if breakpoint.as_ref().is_some_and(|bp| bp.1.message.is_some()) {
7875 "Edit Log Breakpoint"
7876 } else {
7877 "Set Log Breakpoint"
7878 };
7879
7880 let condition_breakpoint_msg = if breakpoint
7881 .as_ref()
7882 .is_some_and(|bp| bp.1.condition.is_some())
7883 {
7884 "Edit Condition Breakpoint"
7885 } else {
7886 "Set Condition Breakpoint"
7887 };
7888
7889 let hit_condition_breakpoint_msg = if breakpoint
7890 .as_ref()
7891 .is_some_and(|bp| bp.1.hit_condition.is_some())
7892 {
7893 "Edit Hit Condition Breakpoint"
7894 } else {
7895 "Set Hit Condition Breakpoint"
7896 };
7897
7898 let set_breakpoint_msg = if breakpoint.as_ref().is_some() {
7899 "Unset Breakpoint"
7900 } else {
7901 "Set Breakpoint"
7902 };
7903
7904 let run_to_cursor = window.is_action_available(&RunToCursor, cx);
7905
7906 let toggle_state_msg = breakpoint.as_ref().map_or(None, |bp| match bp.1.state {
7907 BreakpointState::Enabled => Some("Disable"),
7908 BreakpointState::Disabled => Some("Enable"),
7909 });
7910
7911 let (anchor, breakpoint) =
7912 breakpoint.unwrap_or_else(|| (anchor, Arc::new(Breakpoint::new_standard())));
7913
7914 ui::ContextMenu::build(window, cx, |menu, _, _cx| {
7915 menu.on_blur_subscription(Subscription::new(|| {}))
7916 .context(focus_handle)
7917 .when(run_to_cursor, |this| {
7918 let weak_editor = weak_editor.clone();
7919 this.entry("Run to cursor", None, move |window, cx| {
7920 weak_editor
7921 .update(cx, |editor, cx| {
7922 editor.change_selections(
7923 SelectionEffects::no_scroll(),
7924 window,
7925 cx,
7926 |s| s.select_ranges([Point::new(row, 0)..Point::new(row, 0)]),
7927 );
7928 })
7929 .ok();
7930
7931 window.dispatch_action(Box::new(RunToCursor), cx);
7932 })
7933 .separator()
7934 })
7935 .when_some(toggle_state_msg, |this, msg| {
7936 this.entry(msg, None, {
7937 let weak_editor = weak_editor.clone();
7938 let breakpoint = breakpoint.clone();
7939 move |_window, cx| {
7940 weak_editor
7941 .update(cx, |this, cx| {
7942 this.edit_breakpoint_at_anchor(
7943 anchor,
7944 breakpoint.as_ref().clone(),
7945 BreakpointEditAction::InvertState,
7946 cx,
7947 );
7948 })
7949 .log_err();
7950 }
7951 })
7952 })
7953 .entry(set_breakpoint_msg, None, {
7954 let weak_editor = weak_editor.clone();
7955 let breakpoint = breakpoint.clone();
7956 move |_window, cx| {
7957 weak_editor
7958 .update(cx, |this, cx| {
7959 this.edit_breakpoint_at_anchor(
7960 anchor,
7961 breakpoint.as_ref().clone(),
7962 BreakpointEditAction::Toggle,
7963 cx,
7964 );
7965 })
7966 .log_err();
7967 }
7968 })
7969 .entry(log_breakpoint_msg, None, {
7970 let breakpoint = breakpoint.clone();
7971 let weak_editor = weak_editor.clone();
7972 move |window, cx| {
7973 weak_editor
7974 .update(cx, |this, cx| {
7975 this.add_edit_breakpoint_block(
7976 anchor,
7977 breakpoint.as_ref(),
7978 BreakpointPromptEditAction::Log,
7979 window,
7980 cx,
7981 );
7982 })
7983 .log_err();
7984 }
7985 })
7986 .entry(condition_breakpoint_msg, None, {
7987 let breakpoint = breakpoint.clone();
7988 let weak_editor = weak_editor.clone();
7989 move |window, cx| {
7990 weak_editor
7991 .update(cx, |this, cx| {
7992 this.add_edit_breakpoint_block(
7993 anchor,
7994 breakpoint.as_ref(),
7995 BreakpointPromptEditAction::Condition,
7996 window,
7997 cx,
7998 );
7999 })
8000 .log_err();
8001 }
8002 })
8003 .entry(hit_condition_breakpoint_msg, None, move |window, cx| {
8004 weak_editor
8005 .update(cx, |this, cx| {
8006 this.add_edit_breakpoint_block(
8007 anchor,
8008 breakpoint.as_ref(),
8009 BreakpointPromptEditAction::HitCondition,
8010 window,
8011 cx,
8012 );
8013 })
8014 .log_err();
8015 })
8016 })
8017 }
8018
8019 fn render_breakpoint(
8020 &self,
8021 position: Anchor,
8022 row: DisplayRow,
8023 breakpoint: &Breakpoint,
8024 state: Option<BreakpointSessionState>,
8025 cx: &mut Context<Self>,
8026 ) -> IconButton {
8027 let is_rejected = state.is_some_and(|s| !s.verified);
8028 // Is it a breakpoint that shows up when hovering over gutter?
8029 let (is_phantom, collides_with_existing) = self.gutter_breakpoint_indicator.0.map_or(
8030 (false, false),
8031 |PhantomBreakpointIndicator {
8032 is_active,
8033 display_row,
8034 collides_with_existing_breakpoint,
8035 }| {
8036 (
8037 is_active && display_row == row,
8038 collides_with_existing_breakpoint,
8039 )
8040 },
8041 );
8042
8043 let (color, icon) = {
8044 let icon = match (&breakpoint.message.is_some(), breakpoint.is_disabled()) {
8045 (false, false) => ui::IconName::DebugBreakpoint,
8046 (true, false) => ui::IconName::DebugLogBreakpoint,
8047 (false, true) => ui::IconName::DebugDisabledBreakpoint,
8048 (true, true) => ui::IconName::DebugDisabledLogBreakpoint,
8049 };
8050
8051 let color = if is_phantom {
8052 Color::Hint
8053 } else if is_rejected {
8054 Color::Disabled
8055 } else {
8056 Color::Debugger
8057 };
8058
8059 (color, icon)
8060 };
8061
8062 let breakpoint = Arc::from(breakpoint.clone());
8063
8064 let alt_as_text = gpui::Keystroke {
8065 modifiers: Modifiers::secondary_key(),
8066 ..Default::default()
8067 };
8068 let primary_action_text = if breakpoint.is_disabled() {
8069 "Enable breakpoint"
8070 } else if is_phantom && !collides_with_existing {
8071 "Set breakpoint"
8072 } else {
8073 "Unset breakpoint"
8074 };
8075 let focus_handle = self.focus_handle.clone();
8076
8077 let meta = if is_rejected {
8078 SharedString::from("No executable code is associated with this line.")
8079 } else if collides_with_existing && !breakpoint.is_disabled() {
8080 SharedString::from(format!(
8081 "{alt_as_text}-click to disable,\nright-click for more options."
8082 ))
8083 } else {
8084 SharedString::from("Right-click for more options.")
8085 };
8086 IconButton::new(("breakpoint_indicator", row.0 as usize), icon)
8087 .icon_size(IconSize::XSmall)
8088 .size(ui::ButtonSize::None)
8089 .when(is_rejected, |this| {
8090 this.indicator(Indicator::icon(Icon::new(IconName::Warning)).color(Color::Warning))
8091 })
8092 .icon_color(color)
8093 .style(ButtonStyle::Transparent)
8094 .on_click(cx.listener({
8095 let breakpoint = breakpoint.clone();
8096
8097 move |editor, event: &ClickEvent, window, cx| {
8098 let edit_action = if event.modifiers().platform || breakpoint.is_disabled() {
8099 BreakpointEditAction::InvertState
8100 } else {
8101 BreakpointEditAction::Toggle
8102 };
8103
8104 window.focus(&editor.focus_handle(cx));
8105 editor.edit_breakpoint_at_anchor(
8106 position,
8107 breakpoint.as_ref().clone(),
8108 edit_action,
8109 cx,
8110 );
8111 }
8112 }))
8113 .on_right_click(cx.listener(move |editor, event: &ClickEvent, window, cx| {
8114 editor.set_breakpoint_context_menu(
8115 row,
8116 Some(position),
8117 event.down.position,
8118 window,
8119 cx,
8120 );
8121 }))
8122 .tooltip(move |window, cx| {
8123 Tooltip::with_meta_in(
8124 primary_action_text,
8125 Some(&ToggleBreakpoint),
8126 meta.clone(),
8127 &focus_handle,
8128 window,
8129 cx,
8130 )
8131 })
8132 }
8133
8134 fn build_tasks_context(
8135 project: &Entity<Project>,
8136 buffer: &Entity<Buffer>,
8137 buffer_row: u32,
8138 tasks: &Arc<RunnableTasks>,
8139 cx: &mut Context<Self>,
8140 ) -> Task<Option<task::TaskContext>> {
8141 let position = Point::new(buffer_row, tasks.column);
8142 let range_start = buffer.read(cx).anchor_at(position, Bias::Right);
8143 let location = Location {
8144 buffer: buffer.clone(),
8145 range: range_start..range_start,
8146 };
8147 // Fill in the environmental variables from the tree-sitter captures
8148 let mut captured_task_variables = TaskVariables::default();
8149 for (capture_name, value) in tasks.extra_variables.clone() {
8150 captured_task_variables.insert(
8151 task::VariableName::Custom(capture_name.into()),
8152 value.clone(),
8153 );
8154 }
8155 project.update(cx, |project, cx| {
8156 project.task_store().update(cx, |task_store, cx| {
8157 task_store.task_context_for_location(captured_task_variables, location, cx)
8158 })
8159 })
8160 }
8161
8162 pub fn spawn_nearest_task(
8163 &mut self,
8164 action: &SpawnNearestTask,
8165 window: &mut Window,
8166 cx: &mut Context<Self>,
8167 ) {
8168 let Some((workspace, _)) = self.workspace.clone() else {
8169 return;
8170 };
8171 let Some(project) = self.project.clone() else {
8172 return;
8173 };
8174
8175 // Try to find a closest, enclosing node using tree-sitter that has a
8176 // task
8177 let Some((buffer, buffer_row, tasks)) = self
8178 .find_enclosing_node_task(cx)
8179 // Or find the task that's closest in row-distance.
8180 .or_else(|| self.find_closest_task(cx))
8181 else {
8182 return;
8183 };
8184
8185 let reveal_strategy = action.reveal;
8186 let task_context = Self::build_tasks_context(&project, &buffer, buffer_row, &tasks, cx);
8187 cx.spawn_in(window, async move |_, cx| {
8188 let context = task_context.await?;
8189 let (task_source_kind, mut resolved_task) = tasks.resolve(&context).next()?;
8190
8191 let resolved = &mut resolved_task.resolved;
8192 resolved.reveal = reveal_strategy;
8193
8194 workspace
8195 .update_in(cx, |workspace, window, cx| {
8196 workspace.schedule_resolved_task(
8197 task_source_kind,
8198 resolved_task,
8199 false,
8200 window,
8201 cx,
8202 );
8203 })
8204 .ok()
8205 })
8206 .detach();
8207 }
8208
8209 fn find_closest_task(
8210 &mut self,
8211 cx: &mut Context<Self>,
8212 ) -> Option<(Entity<Buffer>, u32, Arc<RunnableTasks>)> {
8213 let cursor_row = self.selections.newest_adjusted(cx).head().row;
8214
8215 let ((buffer_id, row), tasks) = self
8216 .tasks
8217 .iter()
8218 .min_by_key(|((_, row), _)| cursor_row.abs_diff(*row))?;
8219
8220 let buffer = self.buffer.read(cx).buffer(*buffer_id)?;
8221 let tasks = Arc::new(tasks.to_owned());
8222 Some((buffer, *row, tasks))
8223 }
8224
8225 fn find_enclosing_node_task(
8226 &mut self,
8227 cx: &mut Context<Self>,
8228 ) -> Option<(Entity<Buffer>, u32, Arc<RunnableTasks>)> {
8229 let snapshot = self.buffer.read(cx).snapshot(cx);
8230 let offset = self.selections.newest::<usize>(cx).head();
8231 let excerpt = snapshot.excerpt_containing(offset..offset)?;
8232 let buffer_id = excerpt.buffer().remote_id();
8233
8234 let layer = excerpt.buffer().syntax_layer_at(offset)?;
8235 let mut cursor = layer.node().walk();
8236
8237 while cursor.goto_first_child_for_byte(offset).is_some() {
8238 if cursor.node().end_byte() == offset {
8239 cursor.goto_next_sibling();
8240 }
8241 }
8242
8243 // Ascend to the smallest ancestor that contains the range and has a task.
8244 loop {
8245 let node = cursor.node();
8246 let node_range = node.byte_range();
8247 let symbol_start_row = excerpt.buffer().offset_to_point(node.start_byte()).row;
8248
8249 // Check if this node contains our offset
8250 if node_range.start <= offset && node_range.end >= offset {
8251 // If it contains offset, check for task
8252 if let Some(tasks) = self.tasks.get(&(buffer_id, symbol_start_row)) {
8253 let buffer = self.buffer.read(cx).buffer(buffer_id)?;
8254 return Some((buffer, symbol_start_row, Arc::new(tasks.to_owned())));
8255 }
8256 }
8257
8258 if !cursor.goto_parent() {
8259 break;
8260 }
8261 }
8262 None
8263 }
8264
8265 fn render_run_indicator(
8266 &self,
8267 _style: &EditorStyle,
8268 is_active: bool,
8269 row: DisplayRow,
8270 breakpoint: Option<(Anchor, Breakpoint, Option<BreakpointSessionState>)>,
8271 cx: &mut Context<Self>,
8272 ) -> IconButton {
8273 let color = Color::Muted;
8274 let position = breakpoint.as_ref().map(|(anchor, _, _)| *anchor);
8275
8276 IconButton::new(("run_indicator", row.0 as usize), ui::IconName::Play)
8277 .shape(ui::IconButtonShape::Square)
8278 .icon_size(IconSize::XSmall)
8279 .icon_color(color)
8280 .toggle_state(is_active)
8281 .on_click(cx.listener(move |editor, e: &ClickEvent, window, cx| {
8282 let quick_launch = e.down.button == MouseButton::Left;
8283 window.focus(&editor.focus_handle(cx));
8284 editor.toggle_code_actions(
8285 &ToggleCodeActions {
8286 deployed_from: Some(CodeActionSource::RunMenu(row)),
8287 quick_launch,
8288 },
8289 window,
8290 cx,
8291 );
8292 }))
8293 .on_right_click(cx.listener(move |editor, event: &ClickEvent, window, cx| {
8294 editor.set_breakpoint_context_menu(row, position, event.down.position, window, cx);
8295 }))
8296 }
8297
8298 pub fn context_menu_visible(&self) -> bool {
8299 !self.edit_prediction_preview_is_active()
8300 && self
8301 .context_menu
8302 .borrow()
8303 .as_ref()
8304 .map_or(false, |menu| menu.visible())
8305 }
8306
8307 pub fn context_menu_origin(&self) -> Option<ContextMenuOrigin> {
8308 self.context_menu
8309 .borrow()
8310 .as_ref()
8311 .map(|menu| menu.origin())
8312 }
8313
8314 pub fn set_context_menu_options(&mut self, options: ContextMenuOptions) {
8315 self.context_menu_options = Some(options);
8316 }
8317
8318 const EDIT_PREDICTION_POPOVER_PADDING_X: Pixels = Pixels(24.);
8319 const EDIT_PREDICTION_POPOVER_PADDING_Y: Pixels = Pixels(2.);
8320
8321 fn render_edit_prediction_popover(
8322 &mut self,
8323 text_bounds: &Bounds<Pixels>,
8324 content_origin: gpui::Point<Pixels>,
8325 right_margin: Pixels,
8326 editor_snapshot: &EditorSnapshot,
8327 visible_row_range: Range<DisplayRow>,
8328 scroll_top: f32,
8329 scroll_bottom: f32,
8330 line_layouts: &[LineWithInvisibles],
8331 line_height: Pixels,
8332 scroll_pixel_position: gpui::Point<Pixels>,
8333 newest_selection_head: Option<DisplayPoint>,
8334 editor_width: Pixels,
8335 style: &EditorStyle,
8336 window: &mut Window,
8337 cx: &mut App,
8338 ) -> Option<(AnyElement, gpui::Point<Pixels>)> {
8339 if self.mode().is_minimap() {
8340 return None;
8341 }
8342 let active_inline_completion = self.active_inline_completion.as_ref()?;
8343
8344 if self.edit_prediction_visible_in_cursor_popover(true) {
8345 return None;
8346 }
8347
8348 match &active_inline_completion.completion {
8349 InlineCompletion::Move { target, .. } => {
8350 let target_display_point = target.to_display_point(editor_snapshot);
8351
8352 if self.edit_prediction_requires_modifier() {
8353 if !self.edit_prediction_preview_is_active() {
8354 return None;
8355 }
8356
8357 self.render_edit_prediction_modifier_jump_popover(
8358 text_bounds,
8359 content_origin,
8360 visible_row_range,
8361 line_layouts,
8362 line_height,
8363 scroll_pixel_position,
8364 newest_selection_head,
8365 target_display_point,
8366 window,
8367 cx,
8368 )
8369 } else {
8370 self.render_edit_prediction_eager_jump_popover(
8371 text_bounds,
8372 content_origin,
8373 editor_snapshot,
8374 visible_row_range,
8375 scroll_top,
8376 scroll_bottom,
8377 line_height,
8378 scroll_pixel_position,
8379 target_display_point,
8380 editor_width,
8381 window,
8382 cx,
8383 )
8384 }
8385 }
8386 InlineCompletion::Edit {
8387 display_mode: EditDisplayMode::Inline,
8388 ..
8389 } => None,
8390 InlineCompletion::Edit {
8391 display_mode: EditDisplayMode::TabAccept,
8392 edits,
8393 ..
8394 } => {
8395 let range = &edits.first()?.0;
8396 let target_display_point = range.end.to_display_point(editor_snapshot);
8397
8398 self.render_edit_prediction_end_of_line_popover(
8399 "Accept",
8400 editor_snapshot,
8401 visible_row_range,
8402 target_display_point,
8403 line_height,
8404 scroll_pixel_position,
8405 content_origin,
8406 editor_width,
8407 window,
8408 cx,
8409 )
8410 }
8411 InlineCompletion::Edit {
8412 edits,
8413 edit_preview,
8414 display_mode: EditDisplayMode::DiffPopover,
8415 snapshot,
8416 } => self.render_edit_prediction_diff_popover(
8417 text_bounds,
8418 content_origin,
8419 right_margin,
8420 editor_snapshot,
8421 visible_row_range,
8422 line_layouts,
8423 line_height,
8424 scroll_pixel_position,
8425 newest_selection_head,
8426 editor_width,
8427 style,
8428 edits,
8429 edit_preview,
8430 snapshot,
8431 window,
8432 cx,
8433 ),
8434 }
8435 }
8436
8437 fn render_edit_prediction_modifier_jump_popover(
8438 &mut self,
8439 text_bounds: &Bounds<Pixels>,
8440 content_origin: gpui::Point<Pixels>,
8441 visible_row_range: Range<DisplayRow>,
8442 line_layouts: &[LineWithInvisibles],
8443 line_height: Pixels,
8444 scroll_pixel_position: gpui::Point<Pixels>,
8445 newest_selection_head: Option<DisplayPoint>,
8446 target_display_point: DisplayPoint,
8447 window: &mut Window,
8448 cx: &mut App,
8449 ) -> Option<(AnyElement, gpui::Point<Pixels>)> {
8450 let scrolled_content_origin =
8451 content_origin - gpui::Point::new(scroll_pixel_position.x, Pixels(0.0));
8452
8453 const SCROLL_PADDING_Y: Pixels = px(12.);
8454
8455 if target_display_point.row() < visible_row_range.start {
8456 return self.render_edit_prediction_scroll_popover(
8457 |_| SCROLL_PADDING_Y,
8458 IconName::ArrowUp,
8459 visible_row_range,
8460 line_layouts,
8461 newest_selection_head,
8462 scrolled_content_origin,
8463 window,
8464 cx,
8465 );
8466 } else if target_display_point.row() >= visible_row_range.end {
8467 return self.render_edit_prediction_scroll_popover(
8468 |size| text_bounds.size.height - size.height - SCROLL_PADDING_Y,
8469 IconName::ArrowDown,
8470 visible_row_range,
8471 line_layouts,
8472 newest_selection_head,
8473 scrolled_content_origin,
8474 window,
8475 cx,
8476 );
8477 }
8478
8479 const POLE_WIDTH: Pixels = px(2.);
8480
8481 let line_layout =
8482 line_layouts.get(target_display_point.row().minus(visible_row_range.start) as usize)?;
8483 let target_column = target_display_point.column() as usize;
8484
8485 let target_x = line_layout.x_for_index(target_column);
8486 let target_y =
8487 (target_display_point.row().as_f32() * line_height) - scroll_pixel_position.y;
8488
8489 let flag_on_right = target_x < text_bounds.size.width / 2.;
8490
8491 let mut border_color = Self::edit_prediction_callout_popover_border_color(cx);
8492 border_color.l += 0.001;
8493
8494 let mut element = v_flex()
8495 .items_end()
8496 .when(flag_on_right, |el| el.items_start())
8497 .child(if flag_on_right {
8498 self.render_edit_prediction_line_popover("Jump", None, window, cx)?
8499 .rounded_bl(px(0.))
8500 .rounded_tl(px(0.))
8501 .border_l_2()
8502 .border_color(border_color)
8503 } else {
8504 self.render_edit_prediction_line_popover("Jump", None, window, cx)?
8505 .rounded_br(px(0.))
8506 .rounded_tr(px(0.))
8507 .border_r_2()
8508 .border_color(border_color)
8509 })
8510 .child(div().w(POLE_WIDTH).bg(border_color).h(line_height))
8511 .into_any();
8512
8513 let size = element.layout_as_root(AvailableSpace::min_size(), window, cx);
8514
8515 let mut origin = scrolled_content_origin + point(target_x, target_y)
8516 - point(
8517 if flag_on_right {
8518 POLE_WIDTH
8519 } else {
8520 size.width - POLE_WIDTH
8521 },
8522 size.height - line_height,
8523 );
8524
8525 origin.x = origin.x.max(content_origin.x);
8526
8527 element.prepaint_at(origin, window, cx);
8528
8529 Some((element, origin))
8530 }
8531
8532 fn render_edit_prediction_scroll_popover(
8533 &mut self,
8534 to_y: impl Fn(Size<Pixels>) -> Pixels,
8535 scroll_icon: IconName,
8536 visible_row_range: Range<DisplayRow>,
8537 line_layouts: &[LineWithInvisibles],
8538 newest_selection_head: Option<DisplayPoint>,
8539 scrolled_content_origin: gpui::Point<Pixels>,
8540 window: &mut Window,
8541 cx: &mut App,
8542 ) -> Option<(AnyElement, gpui::Point<Pixels>)> {
8543 let mut element = self
8544 .render_edit_prediction_line_popover("Scroll", Some(scroll_icon), window, cx)?
8545 .into_any();
8546
8547 let size = element.layout_as_root(AvailableSpace::min_size(), window, cx);
8548
8549 let cursor = newest_selection_head?;
8550 let cursor_row_layout =
8551 line_layouts.get(cursor.row().minus(visible_row_range.start) as usize)?;
8552 let cursor_column = cursor.column() as usize;
8553
8554 let cursor_character_x = cursor_row_layout.x_for_index(cursor_column);
8555
8556 let origin = scrolled_content_origin + point(cursor_character_x, to_y(size));
8557
8558 element.prepaint_at(origin, window, cx);
8559 Some((element, origin))
8560 }
8561
8562 fn render_edit_prediction_eager_jump_popover(
8563 &mut self,
8564 text_bounds: &Bounds<Pixels>,
8565 content_origin: gpui::Point<Pixels>,
8566 editor_snapshot: &EditorSnapshot,
8567 visible_row_range: Range<DisplayRow>,
8568 scroll_top: f32,
8569 scroll_bottom: f32,
8570 line_height: Pixels,
8571 scroll_pixel_position: gpui::Point<Pixels>,
8572 target_display_point: DisplayPoint,
8573 editor_width: Pixels,
8574 window: &mut Window,
8575 cx: &mut App,
8576 ) -> Option<(AnyElement, gpui::Point<Pixels>)> {
8577 if target_display_point.row().as_f32() < scroll_top {
8578 let mut element = self
8579 .render_edit_prediction_line_popover(
8580 "Jump to Edit",
8581 Some(IconName::ArrowUp),
8582 window,
8583 cx,
8584 )?
8585 .into_any();
8586
8587 let size = element.layout_as_root(AvailableSpace::min_size(), window, cx);
8588 let offset = point(
8589 (text_bounds.size.width - size.width) / 2.,
8590 Self::EDIT_PREDICTION_POPOVER_PADDING_Y,
8591 );
8592
8593 let origin = text_bounds.origin + offset;
8594 element.prepaint_at(origin, window, cx);
8595 Some((element, origin))
8596 } else if (target_display_point.row().as_f32() + 1.) > scroll_bottom {
8597 let mut element = self
8598 .render_edit_prediction_line_popover(
8599 "Jump to Edit",
8600 Some(IconName::ArrowDown),
8601 window,
8602 cx,
8603 )?
8604 .into_any();
8605
8606 let size = element.layout_as_root(AvailableSpace::min_size(), window, cx);
8607 let offset = point(
8608 (text_bounds.size.width - size.width) / 2.,
8609 text_bounds.size.height - size.height - Self::EDIT_PREDICTION_POPOVER_PADDING_Y,
8610 );
8611
8612 let origin = text_bounds.origin + offset;
8613 element.prepaint_at(origin, window, cx);
8614 Some((element, origin))
8615 } else {
8616 self.render_edit_prediction_end_of_line_popover(
8617 "Jump to Edit",
8618 editor_snapshot,
8619 visible_row_range,
8620 target_display_point,
8621 line_height,
8622 scroll_pixel_position,
8623 content_origin,
8624 editor_width,
8625 window,
8626 cx,
8627 )
8628 }
8629 }
8630
8631 fn render_edit_prediction_end_of_line_popover(
8632 self: &mut Editor,
8633 label: &'static str,
8634 editor_snapshot: &EditorSnapshot,
8635 visible_row_range: Range<DisplayRow>,
8636 target_display_point: DisplayPoint,
8637 line_height: Pixels,
8638 scroll_pixel_position: gpui::Point<Pixels>,
8639 content_origin: gpui::Point<Pixels>,
8640 editor_width: Pixels,
8641 window: &mut Window,
8642 cx: &mut App,
8643 ) -> Option<(AnyElement, gpui::Point<Pixels>)> {
8644 let target_line_end = DisplayPoint::new(
8645 target_display_point.row(),
8646 editor_snapshot.line_len(target_display_point.row()),
8647 );
8648
8649 let mut element = self
8650 .render_edit_prediction_line_popover(label, None, window, cx)?
8651 .into_any();
8652
8653 let size = element.layout_as_root(AvailableSpace::min_size(), window, cx);
8654
8655 let line_origin = self.display_to_pixel_point(target_line_end, editor_snapshot, window)?;
8656
8657 let start_point = content_origin - point(scroll_pixel_position.x, Pixels::ZERO);
8658 let mut origin = start_point
8659 + line_origin
8660 + point(Self::EDIT_PREDICTION_POPOVER_PADDING_X, Pixels::ZERO);
8661 origin.x = origin.x.max(content_origin.x);
8662
8663 let max_x = content_origin.x + editor_width - size.width;
8664
8665 if origin.x > max_x {
8666 let offset = line_height + Self::EDIT_PREDICTION_POPOVER_PADDING_Y;
8667
8668 let icon = if visible_row_range.contains(&(target_display_point.row() + 2)) {
8669 origin.y += offset;
8670 IconName::ArrowUp
8671 } else {
8672 origin.y -= offset;
8673 IconName::ArrowDown
8674 };
8675
8676 element = self
8677 .render_edit_prediction_line_popover(label, Some(icon), window, cx)?
8678 .into_any();
8679
8680 let size = element.layout_as_root(AvailableSpace::min_size(), window, cx);
8681
8682 origin.x = content_origin.x + editor_width - size.width - px(2.);
8683 }
8684
8685 element.prepaint_at(origin, window, cx);
8686 Some((element, origin))
8687 }
8688
8689 fn render_edit_prediction_diff_popover(
8690 self: &Editor,
8691 text_bounds: &Bounds<Pixels>,
8692 content_origin: gpui::Point<Pixels>,
8693 right_margin: Pixels,
8694 editor_snapshot: &EditorSnapshot,
8695 visible_row_range: Range<DisplayRow>,
8696 line_layouts: &[LineWithInvisibles],
8697 line_height: Pixels,
8698 scroll_pixel_position: gpui::Point<Pixels>,
8699 newest_selection_head: Option<DisplayPoint>,
8700 editor_width: Pixels,
8701 style: &EditorStyle,
8702 edits: &Vec<(Range<Anchor>, String)>,
8703 edit_preview: &Option<language::EditPreview>,
8704 snapshot: &language::BufferSnapshot,
8705 window: &mut Window,
8706 cx: &mut App,
8707 ) -> Option<(AnyElement, gpui::Point<Pixels>)> {
8708 let edit_start = edits
8709 .first()
8710 .unwrap()
8711 .0
8712 .start
8713 .to_display_point(editor_snapshot);
8714 let edit_end = edits
8715 .last()
8716 .unwrap()
8717 .0
8718 .end
8719 .to_display_point(editor_snapshot);
8720
8721 let is_visible = visible_row_range.contains(&edit_start.row())
8722 || visible_row_range.contains(&edit_end.row());
8723 if !is_visible {
8724 return None;
8725 }
8726
8727 let highlighted_edits =
8728 crate::inline_completion_edit_text(&snapshot, edits, edit_preview.as_ref()?, false, cx);
8729
8730 let styled_text = highlighted_edits.to_styled_text(&style.text);
8731 let line_count = highlighted_edits.text.lines().count();
8732
8733 const BORDER_WIDTH: Pixels = px(1.);
8734
8735 let keybind = self.render_edit_prediction_accept_keybind(window, cx);
8736 let has_keybind = keybind.is_some();
8737
8738 let mut element = h_flex()
8739 .items_start()
8740 .child(
8741 h_flex()
8742 .bg(cx.theme().colors().editor_background)
8743 .border(BORDER_WIDTH)
8744 .shadow_xs()
8745 .border_color(cx.theme().colors().border)
8746 .rounded_l_lg()
8747 .when(line_count > 1, |el| el.rounded_br_lg())
8748 .pr_1()
8749 .child(styled_text),
8750 )
8751 .child(
8752 h_flex()
8753 .h(line_height + BORDER_WIDTH * 2.)
8754 .px_1p5()
8755 .gap_1()
8756 // Workaround: For some reason, there's a gap if we don't do this
8757 .ml(-BORDER_WIDTH)
8758 .shadow(vec![gpui::BoxShadow {
8759 color: gpui::black().opacity(0.05),
8760 offset: point(px(1.), px(1.)),
8761 blur_radius: px(2.),
8762 spread_radius: px(0.),
8763 }])
8764 .bg(Editor::edit_prediction_line_popover_bg_color(cx))
8765 .border(BORDER_WIDTH)
8766 .border_color(cx.theme().colors().border)
8767 .rounded_r_lg()
8768 .id("edit_prediction_diff_popover_keybind")
8769 .when(!has_keybind, |el| {
8770 let status_colors = cx.theme().status();
8771
8772 el.bg(status_colors.error_background)
8773 .border_color(status_colors.error.opacity(0.6))
8774 .child(Icon::new(IconName::Info).color(Color::Error))
8775 .cursor_default()
8776 .hoverable_tooltip(move |_window, cx| {
8777 cx.new(|_| MissingEditPredictionKeybindingTooltip).into()
8778 })
8779 })
8780 .children(keybind),
8781 )
8782 .into_any();
8783
8784 let longest_row =
8785 editor_snapshot.longest_row_in_range(edit_start.row()..edit_end.row() + 1);
8786 let longest_line_width = if visible_row_range.contains(&longest_row) {
8787 line_layouts[(longest_row.0 - visible_row_range.start.0) as usize].width
8788 } else {
8789 layout_line(
8790 longest_row,
8791 editor_snapshot,
8792 style,
8793 editor_width,
8794 |_| false,
8795 window,
8796 cx,
8797 )
8798 .width
8799 };
8800
8801 let viewport_bounds =
8802 Bounds::new(Default::default(), window.viewport_size()).extend(Edges {
8803 right: -right_margin,
8804 ..Default::default()
8805 });
8806
8807 let x_after_longest =
8808 text_bounds.origin.x + longest_line_width + Self::EDIT_PREDICTION_POPOVER_PADDING_X
8809 - scroll_pixel_position.x;
8810
8811 let element_bounds = element.layout_as_root(AvailableSpace::min_size(), window, cx);
8812
8813 // Fully visible if it can be displayed within the window (allow overlapping other
8814 // panes). However, this is only allowed if the popover starts within text_bounds.
8815 let can_position_to_the_right = x_after_longest < text_bounds.right()
8816 && x_after_longest + element_bounds.width < viewport_bounds.right();
8817
8818 let mut origin = if can_position_to_the_right {
8819 point(
8820 x_after_longest,
8821 text_bounds.origin.y + edit_start.row().as_f32() * line_height
8822 - scroll_pixel_position.y,
8823 )
8824 } else {
8825 let cursor_row = newest_selection_head.map(|head| head.row());
8826 let above_edit = edit_start
8827 .row()
8828 .0
8829 .checked_sub(line_count as u32)
8830 .map(DisplayRow);
8831 let below_edit = Some(edit_end.row() + 1);
8832 let above_cursor =
8833 cursor_row.and_then(|row| row.0.checked_sub(line_count as u32).map(DisplayRow));
8834 let below_cursor = cursor_row.map(|cursor_row| cursor_row + 1);
8835
8836 // Place the edit popover adjacent to the edit if there is a location
8837 // available that is onscreen and does not obscure the cursor. Otherwise,
8838 // place it adjacent to the cursor.
8839 let row_target = [above_edit, below_edit, above_cursor, below_cursor]
8840 .into_iter()
8841 .flatten()
8842 .find(|&start_row| {
8843 let end_row = start_row + line_count as u32;
8844 visible_row_range.contains(&start_row)
8845 && visible_row_range.contains(&end_row)
8846 && cursor_row.map_or(true, |cursor_row| {
8847 !((start_row..end_row).contains(&cursor_row))
8848 })
8849 })?;
8850
8851 content_origin
8852 + point(
8853 -scroll_pixel_position.x,
8854 row_target.as_f32() * line_height - scroll_pixel_position.y,
8855 )
8856 };
8857
8858 origin.x -= BORDER_WIDTH;
8859
8860 window.defer_draw(element, origin, 1);
8861
8862 // Do not return an element, since it will already be drawn due to defer_draw.
8863 None
8864 }
8865
8866 fn edit_prediction_cursor_popover_height(&self) -> Pixels {
8867 px(30.)
8868 }
8869
8870 fn current_user_player_color(&self, cx: &mut App) -> PlayerColor {
8871 if self.read_only(cx) {
8872 cx.theme().players().read_only()
8873 } else {
8874 self.style.as_ref().unwrap().local_player
8875 }
8876 }
8877
8878 fn render_edit_prediction_accept_keybind(
8879 &self,
8880 window: &mut Window,
8881 cx: &App,
8882 ) -> Option<AnyElement> {
8883 let accept_binding = self.accept_edit_prediction_keybind(false, window, cx);
8884 let accept_keystroke = accept_binding.keystroke()?;
8885
8886 let is_platform_style_mac = PlatformStyle::platform() == PlatformStyle::Mac;
8887
8888 let modifiers_color = if accept_keystroke.modifiers == window.modifiers() {
8889 Color::Accent
8890 } else {
8891 Color::Muted
8892 };
8893
8894 h_flex()
8895 .px_0p5()
8896 .when(is_platform_style_mac, |parent| parent.gap_0p5())
8897 .font(theme::ThemeSettings::get_global(cx).buffer_font.clone())
8898 .text_size(TextSize::XSmall.rems(cx))
8899 .child(h_flex().children(ui::render_modifiers(
8900 &accept_keystroke.modifiers,
8901 PlatformStyle::platform(),
8902 Some(modifiers_color),
8903 Some(IconSize::XSmall.rems().into()),
8904 true,
8905 )))
8906 .when(is_platform_style_mac, |parent| {
8907 parent.child(accept_keystroke.key.clone())
8908 })
8909 .when(!is_platform_style_mac, |parent| {
8910 parent.child(
8911 Key::new(
8912 util::capitalize(&accept_keystroke.key),
8913 Some(Color::Default),
8914 )
8915 .size(Some(IconSize::XSmall.rems().into())),
8916 )
8917 })
8918 .into_any()
8919 .into()
8920 }
8921
8922 fn render_edit_prediction_line_popover(
8923 &self,
8924 label: impl Into<SharedString>,
8925 icon: Option<IconName>,
8926 window: &mut Window,
8927 cx: &App,
8928 ) -> Option<Stateful<Div>> {
8929 let padding_right = if icon.is_some() { px(4.) } else { px(8.) };
8930
8931 let keybind = self.render_edit_prediction_accept_keybind(window, cx);
8932 let has_keybind = keybind.is_some();
8933
8934 let result = h_flex()
8935 .id("ep-line-popover")
8936 .py_0p5()
8937 .pl_1()
8938 .pr(padding_right)
8939 .gap_1()
8940 .rounded_md()
8941 .border_1()
8942 .bg(Self::edit_prediction_line_popover_bg_color(cx))
8943 .border_color(Self::edit_prediction_callout_popover_border_color(cx))
8944 .shadow_xs()
8945 .when(!has_keybind, |el| {
8946 let status_colors = cx.theme().status();
8947
8948 el.bg(status_colors.error_background)
8949 .border_color(status_colors.error.opacity(0.6))
8950 .pl_2()
8951 .child(Icon::new(IconName::ZedPredictError).color(Color::Error))
8952 .cursor_default()
8953 .hoverable_tooltip(move |_window, cx| {
8954 cx.new(|_| MissingEditPredictionKeybindingTooltip).into()
8955 })
8956 })
8957 .children(keybind)
8958 .child(
8959 Label::new(label)
8960 .size(LabelSize::Small)
8961 .when(!has_keybind, |el| {
8962 el.color(cx.theme().status().error.into()).strikethrough()
8963 }),
8964 )
8965 .when(!has_keybind, |el| {
8966 el.child(
8967 h_flex().ml_1().child(
8968 Icon::new(IconName::Info)
8969 .size(IconSize::Small)
8970 .color(cx.theme().status().error.into()),
8971 ),
8972 )
8973 })
8974 .when_some(icon, |element, icon| {
8975 element.child(
8976 div()
8977 .mt(px(1.5))
8978 .child(Icon::new(icon).size(IconSize::Small)),
8979 )
8980 });
8981
8982 Some(result)
8983 }
8984
8985 fn edit_prediction_line_popover_bg_color(cx: &App) -> Hsla {
8986 let accent_color = cx.theme().colors().text_accent;
8987 let editor_bg_color = cx.theme().colors().editor_background;
8988 editor_bg_color.blend(accent_color.opacity(0.1))
8989 }
8990
8991 fn edit_prediction_callout_popover_border_color(cx: &App) -> Hsla {
8992 let accent_color = cx.theme().colors().text_accent;
8993 let editor_bg_color = cx.theme().colors().editor_background;
8994 editor_bg_color.blend(accent_color.opacity(0.6))
8995 }
8996
8997 fn render_edit_prediction_cursor_popover(
8998 &self,
8999 min_width: Pixels,
9000 max_width: Pixels,
9001 cursor_point: Point,
9002 style: &EditorStyle,
9003 accept_keystroke: Option<&gpui::Keystroke>,
9004 _window: &Window,
9005 cx: &mut Context<Editor>,
9006 ) -> Option<AnyElement> {
9007 let provider = self.edit_prediction_provider.as_ref()?;
9008
9009 if provider.provider.needs_terms_acceptance(cx) {
9010 return Some(
9011 h_flex()
9012 .min_w(min_width)
9013 .flex_1()
9014 .px_2()
9015 .py_1()
9016 .gap_3()
9017 .elevation_2(cx)
9018 .hover(|style| style.bg(cx.theme().colors().element_hover))
9019 .id("accept-terms")
9020 .cursor_pointer()
9021 .on_mouse_down(MouseButton::Left, |_, window, _| window.prevent_default())
9022 .on_click(cx.listener(|this, _event, window, cx| {
9023 cx.stop_propagation();
9024 this.report_editor_event("Edit Prediction Provider ToS Clicked", None, cx);
9025 window.dispatch_action(
9026 zed_actions::OpenZedPredictOnboarding.boxed_clone(),
9027 cx,
9028 );
9029 }))
9030 .child(
9031 h_flex()
9032 .flex_1()
9033 .gap_2()
9034 .child(Icon::new(IconName::ZedPredict))
9035 .child(Label::new("Accept Terms of Service"))
9036 .child(div().w_full())
9037 .child(
9038 Icon::new(IconName::ArrowUpRight)
9039 .color(Color::Muted)
9040 .size(IconSize::Small),
9041 )
9042 .into_any_element(),
9043 )
9044 .into_any(),
9045 );
9046 }
9047
9048 let is_refreshing = provider.provider.is_refreshing(cx);
9049
9050 fn pending_completion_container() -> Div {
9051 h_flex()
9052 .h_full()
9053 .flex_1()
9054 .gap_2()
9055 .child(Icon::new(IconName::ZedPredict))
9056 }
9057
9058 let completion = match &self.active_inline_completion {
9059 Some(prediction) => {
9060 if !self.has_visible_completions_menu() {
9061 const RADIUS: Pixels = px(6.);
9062 const BORDER_WIDTH: Pixels = px(1.);
9063
9064 return Some(
9065 h_flex()
9066 .elevation_2(cx)
9067 .border(BORDER_WIDTH)
9068 .border_color(cx.theme().colors().border)
9069 .when(accept_keystroke.is_none(), |el| {
9070 el.border_color(cx.theme().status().error)
9071 })
9072 .rounded(RADIUS)
9073 .rounded_tl(px(0.))
9074 .overflow_hidden()
9075 .child(div().px_1p5().child(match &prediction.completion {
9076 InlineCompletion::Move { target, snapshot } => {
9077 use text::ToPoint as _;
9078 if target.text_anchor.to_point(&snapshot).row > cursor_point.row
9079 {
9080 Icon::new(IconName::ZedPredictDown)
9081 } else {
9082 Icon::new(IconName::ZedPredictUp)
9083 }
9084 }
9085 InlineCompletion::Edit { .. } => Icon::new(IconName::ZedPredict),
9086 }))
9087 .child(
9088 h_flex()
9089 .gap_1()
9090 .py_1()
9091 .px_2()
9092 .rounded_r(RADIUS - BORDER_WIDTH)
9093 .border_l_1()
9094 .border_color(cx.theme().colors().border)
9095 .bg(Self::edit_prediction_line_popover_bg_color(cx))
9096 .when(self.edit_prediction_preview.released_too_fast(), |el| {
9097 el.child(
9098 Label::new("Hold")
9099 .size(LabelSize::Small)
9100 .when(accept_keystroke.is_none(), |el| {
9101 el.strikethrough()
9102 })
9103 .line_height_style(LineHeightStyle::UiLabel),
9104 )
9105 })
9106 .id("edit_prediction_cursor_popover_keybind")
9107 .when(accept_keystroke.is_none(), |el| {
9108 let status_colors = cx.theme().status();
9109
9110 el.bg(status_colors.error_background)
9111 .border_color(status_colors.error.opacity(0.6))
9112 .child(Icon::new(IconName::Info).color(Color::Error))
9113 .cursor_default()
9114 .hoverable_tooltip(move |_window, cx| {
9115 cx.new(|_| MissingEditPredictionKeybindingTooltip)
9116 .into()
9117 })
9118 })
9119 .when_some(
9120 accept_keystroke.as_ref(),
9121 |el, accept_keystroke| {
9122 el.child(h_flex().children(ui::render_modifiers(
9123 &accept_keystroke.modifiers,
9124 PlatformStyle::platform(),
9125 Some(Color::Default),
9126 Some(IconSize::XSmall.rems().into()),
9127 false,
9128 )))
9129 },
9130 ),
9131 )
9132 .into_any(),
9133 );
9134 }
9135
9136 self.render_edit_prediction_cursor_popover_preview(
9137 prediction,
9138 cursor_point,
9139 style,
9140 cx,
9141 )?
9142 }
9143
9144 None if is_refreshing => match &self.stale_inline_completion_in_menu {
9145 Some(stale_completion) => self.render_edit_prediction_cursor_popover_preview(
9146 stale_completion,
9147 cursor_point,
9148 style,
9149 cx,
9150 )?,
9151
9152 None => {
9153 pending_completion_container().child(Label::new("...").size(LabelSize::Small))
9154 }
9155 },
9156
9157 None => pending_completion_container().child(Label::new("No Prediction")),
9158 };
9159
9160 let completion = if is_refreshing {
9161 completion
9162 .with_animation(
9163 "loading-completion",
9164 Animation::new(Duration::from_secs(2))
9165 .repeat()
9166 .with_easing(pulsating_between(0.4, 0.8)),
9167 |label, delta| label.opacity(delta),
9168 )
9169 .into_any_element()
9170 } else {
9171 completion.into_any_element()
9172 };
9173
9174 let has_completion = self.active_inline_completion.is_some();
9175
9176 let is_platform_style_mac = PlatformStyle::platform() == PlatformStyle::Mac;
9177 Some(
9178 h_flex()
9179 .min_w(min_width)
9180 .max_w(max_width)
9181 .flex_1()
9182 .elevation_2(cx)
9183 .border_color(cx.theme().colors().border)
9184 .child(
9185 div()
9186 .flex_1()
9187 .py_1()
9188 .px_2()
9189 .overflow_hidden()
9190 .child(completion),
9191 )
9192 .when_some(accept_keystroke, |el, accept_keystroke| {
9193 if !accept_keystroke.modifiers.modified() {
9194 return el;
9195 }
9196
9197 el.child(
9198 h_flex()
9199 .h_full()
9200 .border_l_1()
9201 .rounded_r_lg()
9202 .border_color(cx.theme().colors().border)
9203 .bg(Self::edit_prediction_line_popover_bg_color(cx))
9204 .gap_1()
9205 .py_1()
9206 .px_2()
9207 .child(
9208 h_flex()
9209 .font(theme::ThemeSettings::get_global(cx).buffer_font.clone())
9210 .when(is_platform_style_mac, |parent| parent.gap_1())
9211 .child(h_flex().children(ui::render_modifiers(
9212 &accept_keystroke.modifiers,
9213 PlatformStyle::platform(),
9214 Some(if !has_completion {
9215 Color::Muted
9216 } else {
9217 Color::Default
9218 }),
9219 None,
9220 false,
9221 ))),
9222 )
9223 .child(Label::new("Preview").into_any_element())
9224 .opacity(if has_completion { 1.0 } else { 0.4 }),
9225 )
9226 })
9227 .into_any(),
9228 )
9229 }
9230
9231 fn render_edit_prediction_cursor_popover_preview(
9232 &self,
9233 completion: &InlineCompletionState,
9234 cursor_point: Point,
9235 style: &EditorStyle,
9236 cx: &mut Context<Editor>,
9237 ) -> Option<Div> {
9238 use text::ToPoint as _;
9239
9240 fn render_relative_row_jump(
9241 prefix: impl Into<String>,
9242 current_row: u32,
9243 target_row: u32,
9244 ) -> Div {
9245 let (row_diff, arrow) = if target_row < current_row {
9246 (current_row - target_row, IconName::ArrowUp)
9247 } else {
9248 (target_row - current_row, IconName::ArrowDown)
9249 };
9250
9251 h_flex()
9252 .child(
9253 Label::new(format!("{}{}", prefix.into(), row_diff))
9254 .color(Color::Muted)
9255 .size(LabelSize::Small),
9256 )
9257 .child(Icon::new(arrow).color(Color::Muted).size(IconSize::Small))
9258 }
9259
9260 match &completion.completion {
9261 InlineCompletion::Move {
9262 target, snapshot, ..
9263 } => Some(
9264 h_flex()
9265 .px_2()
9266 .gap_2()
9267 .flex_1()
9268 .child(
9269 if target.text_anchor.to_point(&snapshot).row > cursor_point.row {
9270 Icon::new(IconName::ZedPredictDown)
9271 } else {
9272 Icon::new(IconName::ZedPredictUp)
9273 },
9274 )
9275 .child(Label::new("Jump to Edit")),
9276 ),
9277
9278 InlineCompletion::Edit {
9279 edits,
9280 edit_preview,
9281 snapshot,
9282 display_mode: _,
9283 } => {
9284 let first_edit_row = edits.first()?.0.start.text_anchor.to_point(&snapshot).row;
9285
9286 let (highlighted_edits, has_more_lines) = crate::inline_completion_edit_text(
9287 &snapshot,
9288 &edits,
9289 edit_preview.as_ref()?,
9290 true,
9291 cx,
9292 )
9293 .first_line_preview();
9294
9295 let styled_text = gpui::StyledText::new(highlighted_edits.text)
9296 .with_default_highlights(&style.text, highlighted_edits.highlights);
9297
9298 let preview = h_flex()
9299 .gap_1()
9300 .min_w_16()
9301 .child(styled_text)
9302 .when(has_more_lines, |parent| parent.child("…"));
9303
9304 let left = if first_edit_row != cursor_point.row {
9305 render_relative_row_jump("", cursor_point.row, first_edit_row)
9306 .into_any_element()
9307 } else {
9308 Icon::new(IconName::ZedPredict).into_any_element()
9309 };
9310
9311 Some(
9312 h_flex()
9313 .h_full()
9314 .flex_1()
9315 .gap_2()
9316 .pr_1()
9317 .overflow_x_hidden()
9318 .font(theme::ThemeSettings::get_global(cx).buffer_font.clone())
9319 .child(left)
9320 .child(preview),
9321 )
9322 }
9323 }
9324 }
9325
9326 pub fn render_context_menu(
9327 &self,
9328 style: &EditorStyle,
9329 max_height_in_lines: u32,
9330 window: &mut Window,
9331 cx: &mut Context<Editor>,
9332 ) -> Option<AnyElement> {
9333 let menu = self.context_menu.borrow();
9334 let menu = menu.as_ref()?;
9335 if !menu.visible() {
9336 return None;
9337 };
9338 Some(menu.render(style, max_height_in_lines, window, cx))
9339 }
9340
9341 fn render_context_menu_aside(
9342 &mut self,
9343 max_size: Size<Pixels>,
9344 window: &mut Window,
9345 cx: &mut Context<Editor>,
9346 ) -> Option<AnyElement> {
9347 self.context_menu.borrow_mut().as_mut().and_then(|menu| {
9348 if menu.visible() {
9349 menu.render_aside(max_size, window, cx)
9350 } else {
9351 None
9352 }
9353 })
9354 }
9355
9356 fn hide_context_menu(
9357 &mut self,
9358 window: &mut Window,
9359 cx: &mut Context<Self>,
9360 ) -> Option<CodeContextMenu> {
9361 cx.notify();
9362 self.completion_tasks.clear();
9363 let context_menu = self.context_menu.borrow_mut().take();
9364 self.stale_inline_completion_in_menu.take();
9365 self.update_visible_inline_completion(window, cx);
9366 if let Some(CodeContextMenu::Completions(_)) = &context_menu {
9367 if let Some(completion_provider) = &self.completion_provider {
9368 completion_provider.selection_changed(None, window, cx);
9369 }
9370 }
9371 context_menu
9372 }
9373
9374 fn show_snippet_choices(
9375 &mut self,
9376 choices: &Vec<String>,
9377 selection: Range<Anchor>,
9378 cx: &mut Context<Self>,
9379 ) {
9380 let buffer_id = match (&selection.start.buffer_id, &selection.end.buffer_id) {
9381 (Some(a), Some(b)) if a == b => a,
9382 _ => {
9383 log::error!("expected anchor range to have matching buffer IDs");
9384 return;
9385 }
9386 };
9387 let multi_buffer = self.buffer().read(cx);
9388 let Some(buffer) = multi_buffer.buffer(*buffer_id) else {
9389 return;
9390 };
9391
9392 let id = post_inc(&mut self.next_completion_id);
9393 let snippet_sort_order = EditorSettings::get_global(cx).snippet_sort_order;
9394 *self.context_menu.borrow_mut() = Some(CodeContextMenu::Completions(
9395 CompletionsMenu::new_snippet_choices(
9396 id,
9397 true,
9398 choices,
9399 selection,
9400 buffer,
9401 snippet_sort_order,
9402 ),
9403 ));
9404 }
9405
9406 pub fn insert_snippet(
9407 &mut self,
9408 insertion_ranges: &[Range<usize>],
9409 snippet: Snippet,
9410 window: &mut Window,
9411 cx: &mut Context<Self>,
9412 ) -> Result<()> {
9413 struct Tabstop<T> {
9414 is_end_tabstop: bool,
9415 ranges: Vec<Range<T>>,
9416 choices: Option<Vec<String>>,
9417 }
9418
9419 let tabstops = self.buffer.update(cx, |buffer, cx| {
9420 let snippet_text: Arc<str> = snippet.text.clone().into();
9421 let edits = insertion_ranges
9422 .iter()
9423 .cloned()
9424 .map(|range| (range, snippet_text.clone()));
9425 let autoindent_mode = AutoindentMode::Block {
9426 original_indent_columns: Vec::new(),
9427 };
9428 buffer.edit(edits, Some(autoindent_mode), cx);
9429
9430 let snapshot = &*buffer.read(cx);
9431 let snippet = &snippet;
9432 snippet
9433 .tabstops
9434 .iter()
9435 .map(|tabstop| {
9436 let is_end_tabstop = tabstop.ranges.first().map_or(false, |tabstop| {
9437 tabstop.is_empty() && tabstop.start == snippet.text.len() as isize
9438 });
9439 let mut tabstop_ranges = tabstop
9440 .ranges
9441 .iter()
9442 .flat_map(|tabstop_range| {
9443 let mut delta = 0_isize;
9444 insertion_ranges.iter().map(move |insertion_range| {
9445 let insertion_start = insertion_range.start as isize + delta;
9446 delta +=
9447 snippet.text.len() as isize - insertion_range.len() as isize;
9448
9449 let start = ((insertion_start + tabstop_range.start) as usize)
9450 .min(snapshot.len());
9451 let end = ((insertion_start + tabstop_range.end) as usize)
9452 .min(snapshot.len());
9453 snapshot.anchor_before(start)..snapshot.anchor_after(end)
9454 })
9455 })
9456 .collect::<Vec<_>>();
9457 tabstop_ranges.sort_unstable_by(|a, b| a.start.cmp(&b.start, snapshot));
9458
9459 Tabstop {
9460 is_end_tabstop,
9461 ranges: tabstop_ranges,
9462 choices: tabstop.choices.clone(),
9463 }
9464 })
9465 .collect::<Vec<_>>()
9466 });
9467 if let Some(tabstop) = tabstops.first() {
9468 self.change_selections(Default::default(), window, cx, |s| {
9469 // Reverse order so that the first range is the newest created selection.
9470 // Completions will use it and autoscroll will prioritize it.
9471 s.select_ranges(tabstop.ranges.iter().rev().cloned());
9472 });
9473
9474 if let Some(choices) = &tabstop.choices {
9475 if let Some(selection) = tabstop.ranges.first() {
9476 self.show_snippet_choices(choices, selection.clone(), cx)
9477 }
9478 }
9479
9480 // If we're already at the last tabstop and it's at the end of the snippet,
9481 // we're done, we don't need to keep the state around.
9482 if !tabstop.is_end_tabstop {
9483 let choices = tabstops
9484 .iter()
9485 .map(|tabstop| tabstop.choices.clone())
9486 .collect();
9487
9488 let ranges = tabstops
9489 .into_iter()
9490 .map(|tabstop| tabstop.ranges)
9491 .collect::<Vec<_>>();
9492
9493 self.snippet_stack.push(SnippetState {
9494 active_index: 0,
9495 ranges,
9496 choices,
9497 });
9498 }
9499
9500 // Check whether the just-entered snippet ends with an auto-closable bracket.
9501 if self.autoclose_regions.is_empty() {
9502 let snapshot = self.buffer.read(cx).snapshot(cx);
9503 for selection in &mut self.selections.all::<Point>(cx) {
9504 let selection_head = selection.head();
9505 let Some(scope) = snapshot.language_scope_at(selection_head) else {
9506 continue;
9507 };
9508
9509 let mut bracket_pair = None;
9510 let next_chars = snapshot.chars_at(selection_head).collect::<String>();
9511 let prev_chars = snapshot
9512 .reversed_chars_at(selection_head)
9513 .collect::<String>();
9514 for (pair, enabled) in scope.brackets() {
9515 if enabled
9516 && pair.close
9517 && prev_chars.starts_with(pair.start.as_str())
9518 && next_chars.starts_with(pair.end.as_str())
9519 {
9520 bracket_pair = Some(pair.clone());
9521 break;
9522 }
9523 }
9524 if let Some(pair) = bracket_pair {
9525 let snapshot_settings = snapshot.language_settings_at(selection_head, cx);
9526 let autoclose_enabled =
9527 self.use_autoclose && snapshot_settings.use_autoclose;
9528 if autoclose_enabled {
9529 let start = snapshot.anchor_after(selection_head);
9530 let end = snapshot.anchor_after(selection_head);
9531 self.autoclose_regions.push(AutocloseRegion {
9532 selection_id: selection.id,
9533 range: start..end,
9534 pair,
9535 });
9536 }
9537 }
9538 }
9539 }
9540 }
9541 Ok(())
9542 }
9543
9544 pub fn move_to_next_snippet_tabstop(
9545 &mut self,
9546 window: &mut Window,
9547 cx: &mut Context<Self>,
9548 ) -> bool {
9549 self.move_to_snippet_tabstop(Bias::Right, window, cx)
9550 }
9551
9552 pub fn move_to_prev_snippet_tabstop(
9553 &mut self,
9554 window: &mut Window,
9555 cx: &mut Context<Self>,
9556 ) -> bool {
9557 self.move_to_snippet_tabstop(Bias::Left, window, cx)
9558 }
9559
9560 pub fn move_to_snippet_tabstop(
9561 &mut self,
9562 bias: Bias,
9563 window: &mut Window,
9564 cx: &mut Context<Self>,
9565 ) -> bool {
9566 if let Some(mut snippet) = self.snippet_stack.pop() {
9567 match bias {
9568 Bias::Left => {
9569 if snippet.active_index > 0 {
9570 snippet.active_index -= 1;
9571 } else {
9572 self.snippet_stack.push(snippet);
9573 return false;
9574 }
9575 }
9576 Bias::Right => {
9577 if snippet.active_index + 1 < snippet.ranges.len() {
9578 snippet.active_index += 1;
9579 } else {
9580 self.snippet_stack.push(snippet);
9581 return false;
9582 }
9583 }
9584 }
9585 if let Some(current_ranges) = snippet.ranges.get(snippet.active_index) {
9586 self.change_selections(Default::default(), window, cx, |s| {
9587 // Reverse order so that the first range is the newest created selection.
9588 // Completions will use it and autoscroll will prioritize it.
9589 s.select_ranges(current_ranges.iter().rev().cloned())
9590 });
9591
9592 if let Some(choices) = &snippet.choices[snippet.active_index] {
9593 if let Some(selection) = current_ranges.first() {
9594 self.show_snippet_choices(&choices, selection.clone(), cx);
9595 }
9596 }
9597
9598 // If snippet state is not at the last tabstop, push it back on the stack
9599 if snippet.active_index + 1 < snippet.ranges.len() {
9600 self.snippet_stack.push(snippet);
9601 }
9602 return true;
9603 }
9604 }
9605
9606 false
9607 }
9608
9609 pub fn clear(&mut self, window: &mut Window, cx: &mut Context<Self>) {
9610 self.transact(window, cx, |this, window, cx| {
9611 this.select_all(&SelectAll, window, cx);
9612 this.insert("", window, cx);
9613 });
9614 }
9615
9616 pub fn backspace(&mut self, _: &Backspace, window: &mut Window, cx: &mut Context<Self>) {
9617 self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
9618 self.transact(window, cx, |this, window, cx| {
9619 this.select_autoclose_pair(window, cx);
9620 let mut linked_ranges = HashMap::<_, Vec<_>>::default();
9621 if !this.linked_edit_ranges.is_empty() {
9622 let selections = this.selections.all::<MultiBufferPoint>(cx);
9623 let snapshot = this.buffer.read(cx).snapshot(cx);
9624
9625 for selection in selections.iter() {
9626 let selection_start = snapshot.anchor_before(selection.start).text_anchor;
9627 let selection_end = snapshot.anchor_after(selection.end).text_anchor;
9628 if selection_start.buffer_id != selection_end.buffer_id {
9629 continue;
9630 }
9631 if let Some(ranges) =
9632 this.linked_editing_ranges_for(selection_start..selection_end, cx)
9633 {
9634 for (buffer, entries) in ranges {
9635 linked_ranges.entry(buffer).or_default().extend(entries);
9636 }
9637 }
9638 }
9639 }
9640
9641 let mut selections = this.selections.all::<MultiBufferPoint>(cx);
9642 let display_map = this.display_map.update(cx, |map, cx| map.snapshot(cx));
9643 for selection in &mut selections {
9644 if selection.is_empty() {
9645 let old_head = selection.head();
9646 let mut new_head =
9647 movement::left(&display_map, old_head.to_display_point(&display_map))
9648 .to_point(&display_map);
9649 if let Some((buffer, line_buffer_range)) = display_map
9650 .buffer_snapshot
9651 .buffer_line_for_row(MultiBufferRow(old_head.row))
9652 {
9653 let indent_size = buffer.indent_size_for_line(line_buffer_range.start.row);
9654 let indent_len = match indent_size.kind {
9655 IndentKind::Space => {
9656 buffer.settings_at(line_buffer_range.start, cx).tab_size
9657 }
9658 IndentKind::Tab => NonZeroU32::new(1).unwrap(),
9659 };
9660 if old_head.column <= indent_size.len && old_head.column > 0 {
9661 let indent_len = indent_len.get();
9662 new_head = cmp::min(
9663 new_head,
9664 MultiBufferPoint::new(
9665 old_head.row,
9666 ((old_head.column - 1) / indent_len) * indent_len,
9667 ),
9668 );
9669 }
9670 }
9671
9672 selection.set_head(new_head, SelectionGoal::None);
9673 }
9674 }
9675
9676 this.change_selections(Default::default(), window, cx, |s| s.select(selections));
9677 this.insert("", window, cx);
9678 let empty_str: Arc<str> = Arc::from("");
9679 for (buffer, edits) in linked_ranges {
9680 let snapshot = buffer.read(cx).snapshot();
9681 use text::ToPoint as TP;
9682
9683 let edits = edits
9684 .into_iter()
9685 .map(|range| {
9686 let end_point = TP::to_point(&range.end, &snapshot);
9687 let mut start_point = TP::to_point(&range.start, &snapshot);
9688
9689 if end_point == start_point {
9690 let offset = text::ToOffset::to_offset(&range.start, &snapshot)
9691 .saturating_sub(1);
9692 start_point =
9693 snapshot.clip_point(TP::to_point(&offset, &snapshot), Bias::Left);
9694 };
9695
9696 (start_point..end_point, empty_str.clone())
9697 })
9698 .sorted_by_key(|(range, _)| range.start)
9699 .collect::<Vec<_>>();
9700 buffer.update(cx, |this, cx| {
9701 this.edit(edits, None, cx);
9702 })
9703 }
9704 this.refresh_inline_completion(true, false, window, cx);
9705 linked_editing_ranges::refresh_linked_ranges(this, window, cx);
9706 });
9707 }
9708
9709 pub fn delete(&mut self, _: &Delete, window: &mut Window, cx: &mut Context<Self>) {
9710 self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
9711 self.transact(window, cx, |this, window, cx| {
9712 this.change_selections(Default::default(), window, cx, |s| {
9713 s.move_with(|map, selection| {
9714 if selection.is_empty() {
9715 let cursor = movement::right(map, selection.head());
9716 selection.end = cursor;
9717 selection.reversed = true;
9718 selection.goal = SelectionGoal::None;
9719 }
9720 })
9721 });
9722 this.insert("", window, cx);
9723 this.refresh_inline_completion(true, false, window, cx);
9724 });
9725 }
9726
9727 pub fn backtab(&mut self, _: &Backtab, window: &mut Window, cx: &mut Context<Self>) {
9728 if self.mode.is_single_line() {
9729 cx.propagate();
9730 return;
9731 }
9732
9733 self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
9734 if self.move_to_prev_snippet_tabstop(window, cx) {
9735 return;
9736 }
9737 self.outdent(&Outdent, window, cx);
9738 }
9739
9740 pub fn tab(&mut self, _: &Tab, window: &mut Window, cx: &mut Context<Self>) {
9741 if self.mode.is_single_line() {
9742 cx.propagate();
9743 return;
9744 }
9745
9746 if self.move_to_next_snippet_tabstop(window, cx) {
9747 self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
9748 return;
9749 }
9750 if self.read_only(cx) {
9751 return;
9752 }
9753 self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
9754 let mut selections = self.selections.all_adjusted(cx);
9755 let buffer = self.buffer.read(cx);
9756 let snapshot = buffer.snapshot(cx);
9757 let rows_iter = selections.iter().map(|s| s.head().row);
9758 let suggested_indents = snapshot.suggested_indents(rows_iter, cx);
9759
9760 let has_some_cursor_in_whitespace = selections
9761 .iter()
9762 .filter(|selection| selection.is_empty())
9763 .any(|selection| {
9764 let cursor = selection.head();
9765 let current_indent = snapshot.indent_size_for_line(MultiBufferRow(cursor.row));
9766 cursor.column < current_indent.len
9767 });
9768
9769 let mut edits = Vec::new();
9770 let mut prev_edited_row = 0;
9771 let mut row_delta = 0;
9772 for selection in &mut selections {
9773 if selection.start.row != prev_edited_row {
9774 row_delta = 0;
9775 }
9776 prev_edited_row = selection.end.row;
9777
9778 // If the selection is non-empty, then increase the indentation of the selected lines.
9779 if !selection.is_empty() {
9780 row_delta =
9781 Self::indent_selection(buffer, &snapshot, selection, &mut edits, row_delta, cx);
9782 continue;
9783 }
9784
9785 let cursor = selection.head();
9786 let current_indent = snapshot.indent_size_for_line(MultiBufferRow(cursor.row));
9787 if let Some(suggested_indent) =
9788 suggested_indents.get(&MultiBufferRow(cursor.row)).copied()
9789 {
9790 // Don't do anything if already at suggested indent
9791 // and there is any other cursor which is not
9792 if has_some_cursor_in_whitespace
9793 && cursor.column == current_indent.len
9794 && current_indent.len == suggested_indent.len
9795 {
9796 continue;
9797 }
9798
9799 // Adjust line and move cursor to suggested indent
9800 // if cursor is not at suggested indent
9801 if cursor.column < suggested_indent.len
9802 && cursor.column <= current_indent.len
9803 && current_indent.len <= suggested_indent.len
9804 {
9805 selection.start = Point::new(cursor.row, suggested_indent.len);
9806 selection.end = selection.start;
9807 if row_delta == 0 {
9808 edits.extend(Buffer::edit_for_indent_size_adjustment(
9809 cursor.row,
9810 current_indent,
9811 suggested_indent,
9812 ));
9813 row_delta = suggested_indent.len - current_indent.len;
9814 }
9815 continue;
9816 }
9817
9818 // If current indent is more than suggested indent
9819 // only move cursor to current indent and skip indent
9820 if cursor.column < current_indent.len && current_indent.len > suggested_indent.len {
9821 selection.start = Point::new(cursor.row, current_indent.len);
9822 selection.end = selection.start;
9823 continue;
9824 }
9825 }
9826
9827 // Otherwise, insert a hard or soft tab.
9828 let settings = buffer.language_settings_at(cursor, cx);
9829 let tab_size = if settings.hard_tabs {
9830 IndentSize::tab()
9831 } else {
9832 let tab_size = settings.tab_size.get();
9833 let indent_remainder = snapshot
9834 .text_for_range(Point::new(cursor.row, 0)..cursor)
9835 .flat_map(str::chars)
9836 .fold(row_delta % tab_size, |counter: u32, c| {
9837 if c == '\t' {
9838 0
9839 } else {
9840 (counter + 1) % tab_size
9841 }
9842 });
9843
9844 let chars_to_next_tab_stop = tab_size - indent_remainder;
9845 IndentSize::spaces(chars_to_next_tab_stop)
9846 };
9847 selection.start = Point::new(cursor.row, cursor.column + row_delta + tab_size.len);
9848 selection.end = selection.start;
9849 edits.push((cursor..cursor, tab_size.chars().collect::<String>()));
9850 row_delta += tab_size.len;
9851 }
9852
9853 self.transact(window, cx, |this, window, cx| {
9854 this.buffer.update(cx, |b, cx| b.edit(edits, None, cx));
9855 this.change_selections(Default::default(), window, cx, |s| s.select(selections));
9856 this.refresh_inline_completion(true, false, window, cx);
9857 });
9858 }
9859
9860 pub fn indent(&mut self, _: &Indent, window: &mut Window, cx: &mut Context<Self>) {
9861 if self.read_only(cx) {
9862 return;
9863 }
9864 if self.mode.is_single_line() {
9865 cx.propagate();
9866 return;
9867 }
9868
9869 self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
9870 let mut selections = self.selections.all::<Point>(cx);
9871 let mut prev_edited_row = 0;
9872 let mut row_delta = 0;
9873 let mut edits = Vec::new();
9874 let buffer = self.buffer.read(cx);
9875 let snapshot = buffer.snapshot(cx);
9876 for selection in &mut selections {
9877 if selection.start.row != prev_edited_row {
9878 row_delta = 0;
9879 }
9880 prev_edited_row = selection.end.row;
9881
9882 row_delta =
9883 Self::indent_selection(buffer, &snapshot, selection, &mut edits, row_delta, cx);
9884 }
9885
9886 self.transact(window, cx, |this, window, cx| {
9887 this.buffer.update(cx, |b, cx| b.edit(edits, None, cx));
9888 this.change_selections(Default::default(), window, cx, |s| s.select(selections));
9889 });
9890 }
9891
9892 fn indent_selection(
9893 buffer: &MultiBuffer,
9894 snapshot: &MultiBufferSnapshot,
9895 selection: &mut Selection<Point>,
9896 edits: &mut Vec<(Range<Point>, String)>,
9897 delta_for_start_row: u32,
9898 cx: &App,
9899 ) -> u32 {
9900 let settings = buffer.language_settings_at(selection.start, cx);
9901 let tab_size = settings.tab_size.get();
9902 let indent_kind = if settings.hard_tabs {
9903 IndentKind::Tab
9904 } else {
9905 IndentKind::Space
9906 };
9907 let mut start_row = selection.start.row;
9908 let mut end_row = selection.end.row + 1;
9909
9910 // If a selection ends at the beginning of a line, don't indent
9911 // that last line.
9912 if selection.end.column == 0 && selection.end.row > selection.start.row {
9913 end_row -= 1;
9914 }
9915
9916 // Avoid re-indenting a row that has already been indented by a
9917 // previous selection, but still update this selection's column
9918 // to reflect that indentation.
9919 if delta_for_start_row > 0 {
9920 start_row += 1;
9921 selection.start.column += delta_for_start_row;
9922 if selection.end.row == selection.start.row {
9923 selection.end.column += delta_for_start_row;
9924 }
9925 }
9926
9927 let mut delta_for_end_row = 0;
9928 let has_multiple_rows = start_row + 1 != end_row;
9929 for row in start_row..end_row {
9930 let current_indent = snapshot.indent_size_for_line(MultiBufferRow(row));
9931 let indent_delta = match (current_indent.kind, indent_kind) {
9932 (IndentKind::Space, IndentKind::Space) => {
9933 let columns_to_next_tab_stop = tab_size - (current_indent.len % tab_size);
9934 IndentSize::spaces(columns_to_next_tab_stop)
9935 }
9936 (IndentKind::Tab, IndentKind::Space) => IndentSize::spaces(tab_size),
9937 (_, IndentKind::Tab) => IndentSize::tab(),
9938 };
9939
9940 let start = if has_multiple_rows || current_indent.len < selection.start.column {
9941 0
9942 } else {
9943 selection.start.column
9944 };
9945 let row_start = Point::new(row, start);
9946 edits.push((
9947 row_start..row_start,
9948 indent_delta.chars().collect::<String>(),
9949 ));
9950
9951 // Update this selection's endpoints to reflect the indentation.
9952 if row == selection.start.row {
9953 selection.start.column += indent_delta.len;
9954 }
9955 if row == selection.end.row {
9956 selection.end.column += indent_delta.len;
9957 delta_for_end_row = indent_delta.len;
9958 }
9959 }
9960
9961 if selection.start.row == selection.end.row {
9962 delta_for_start_row + delta_for_end_row
9963 } else {
9964 delta_for_end_row
9965 }
9966 }
9967
9968 pub fn outdent(&mut self, _: &Outdent, window: &mut Window, cx: &mut Context<Self>) {
9969 if self.read_only(cx) {
9970 return;
9971 }
9972 if self.mode.is_single_line() {
9973 cx.propagate();
9974 return;
9975 }
9976
9977 self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
9978 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
9979 let selections = self.selections.all::<Point>(cx);
9980 let mut deletion_ranges = Vec::new();
9981 let mut last_outdent = None;
9982 {
9983 let buffer = self.buffer.read(cx);
9984 let snapshot = buffer.snapshot(cx);
9985 for selection in &selections {
9986 let settings = buffer.language_settings_at(selection.start, cx);
9987 let tab_size = settings.tab_size.get();
9988 let mut rows = selection.spanned_rows(false, &display_map);
9989
9990 // Avoid re-outdenting a row that has already been outdented by a
9991 // previous selection.
9992 if let Some(last_row) = last_outdent {
9993 if last_row == rows.start {
9994 rows.start = rows.start.next_row();
9995 }
9996 }
9997 let has_multiple_rows = rows.len() > 1;
9998 for row in rows.iter_rows() {
9999 let indent_size = snapshot.indent_size_for_line(row);
10000 if indent_size.len > 0 {
10001 let deletion_len = match indent_size.kind {
10002 IndentKind::Space => {
10003 let columns_to_prev_tab_stop = indent_size.len % tab_size;
10004 if columns_to_prev_tab_stop == 0 {
10005 tab_size
10006 } else {
10007 columns_to_prev_tab_stop
10008 }
10009 }
10010 IndentKind::Tab => 1,
10011 };
10012 let start = if has_multiple_rows
10013 || deletion_len > selection.start.column
10014 || indent_size.len < selection.start.column
10015 {
10016 0
10017 } else {
10018 selection.start.column - deletion_len
10019 };
10020 deletion_ranges.push(
10021 Point::new(row.0, start)..Point::new(row.0, start + deletion_len),
10022 );
10023 last_outdent = Some(row);
10024 }
10025 }
10026 }
10027 }
10028
10029 self.transact(window, cx, |this, window, cx| {
10030 this.buffer.update(cx, |buffer, cx| {
10031 let empty_str: Arc<str> = Arc::default();
10032 buffer.edit(
10033 deletion_ranges
10034 .into_iter()
10035 .map(|range| (range, empty_str.clone())),
10036 None,
10037 cx,
10038 );
10039 });
10040 let selections = this.selections.all::<usize>(cx);
10041 this.change_selections(Default::default(), window, cx, |s| s.select(selections));
10042 });
10043 }
10044
10045 pub fn autoindent(&mut self, _: &AutoIndent, window: &mut Window, cx: &mut Context<Self>) {
10046 if self.read_only(cx) {
10047 return;
10048 }
10049 if self.mode.is_single_line() {
10050 cx.propagate();
10051 return;
10052 }
10053
10054 self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
10055 let selections = self
10056 .selections
10057 .all::<usize>(cx)
10058 .into_iter()
10059 .map(|s| s.range());
10060
10061 self.transact(window, cx, |this, window, cx| {
10062 this.buffer.update(cx, |buffer, cx| {
10063 buffer.autoindent_ranges(selections, cx);
10064 });
10065 let selections = this.selections.all::<usize>(cx);
10066 this.change_selections(Default::default(), window, cx, |s| s.select(selections));
10067 });
10068 }
10069
10070 pub fn delete_line(&mut self, _: &DeleteLine, window: &mut Window, cx: &mut Context<Self>) {
10071 self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
10072 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
10073 let selections = self.selections.all::<Point>(cx);
10074
10075 let mut new_cursors = Vec::new();
10076 let mut edit_ranges = Vec::new();
10077 let mut selections = selections.iter().peekable();
10078 while let Some(selection) = selections.next() {
10079 let mut rows = selection.spanned_rows(false, &display_map);
10080 let goal_display_column = selection.head().to_display_point(&display_map).column();
10081
10082 // Accumulate contiguous regions of rows that we want to delete.
10083 while let Some(next_selection) = selections.peek() {
10084 let next_rows = next_selection.spanned_rows(false, &display_map);
10085 if next_rows.start <= rows.end {
10086 rows.end = next_rows.end;
10087 selections.next().unwrap();
10088 } else {
10089 break;
10090 }
10091 }
10092
10093 let buffer = &display_map.buffer_snapshot;
10094 let mut edit_start = Point::new(rows.start.0, 0).to_offset(buffer);
10095 let edit_end;
10096 let cursor_buffer_row;
10097 if buffer.max_point().row >= rows.end.0 {
10098 // If there's a line after the range, delete the \n from the end of the row range
10099 // and position the cursor on the next line.
10100 edit_end = Point::new(rows.end.0, 0).to_offset(buffer);
10101 cursor_buffer_row = rows.end;
10102 } else {
10103 // If there isn't a line after the range, delete the \n from the line before the
10104 // start of the row range and position the cursor there.
10105 edit_start = edit_start.saturating_sub(1);
10106 edit_end = buffer.len();
10107 cursor_buffer_row = rows.start.previous_row();
10108 }
10109
10110 let mut cursor = Point::new(cursor_buffer_row.0, 0).to_display_point(&display_map);
10111 *cursor.column_mut() =
10112 cmp::min(goal_display_column, display_map.line_len(cursor.row()));
10113
10114 new_cursors.push((
10115 selection.id,
10116 buffer.anchor_after(cursor.to_point(&display_map)),
10117 ));
10118 edit_ranges.push(edit_start..edit_end);
10119 }
10120
10121 self.transact(window, cx, |this, window, cx| {
10122 let buffer = this.buffer.update(cx, |buffer, cx| {
10123 let empty_str: Arc<str> = Arc::default();
10124 buffer.edit(
10125 edit_ranges
10126 .into_iter()
10127 .map(|range| (range, empty_str.clone())),
10128 None,
10129 cx,
10130 );
10131 buffer.snapshot(cx)
10132 });
10133 let new_selections = new_cursors
10134 .into_iter()
10135 .map(|(id, cursor)| {
10136 let cursor = cursor.to_point(&buffer);
10137 Selection {
10138 id,
10139 start: cursor,
10140 end: cursor,
10141 reversed: false,
10142 goal: SelectionGoal::None,
10143 }
10144 })
10145 .collect();
10146
10147 this.change_selections(Default::default(), window, cx, |s| {
10148 s.select(new_selections);
10149 });
10150 });
10151 }
10152
10153 pub fn join_lines_impl(
10154 &mut self,
10155 insert_whitespace: bool,
10156 window: &mut Window,
10157 cx: &mut Context<Self>,
10158 ) {
10159 if self.read_only(cx) {
10160 return;
10161 }
10162 let mut row_ranges = Vec::<Range<MultiBufferRow>>::new();
10163 for selection in self.selections.all::<Point>(cx) {
10164 let start = MultiBufferRow(selection.start.row);
10165 // Treat single line selections as if they include the next line. Otherwise this action
10166 // would do nothing for single line selections individual cursors.
10167 let end = if selection.start.row == selection.end.row {
10168 MultiBufferRow(selection.start.row + 1)
10169 } else {
10170 MultiBufferRow(selection.end.row)
10171 };
10172
10173 if let Some(last_row_range) = row_ranges.last_mut() {
10174 if start <= last_row_range.end {
10175 last_row_range.end = end;
10176 continue;
10177 }
10178 }
10179 row_ranges.push(start..end);
10180 }
10181
10182 let snapshot = self.buffer.read(cx).snapshot(cx);
10183 let mut cursor_positions = Vec::new();
10184 for row_range in &row_ranges {
10185 let anchor = snapshot.anchor_before(Point::new(
10186 row_range.end.previous_row().0,
10187 snapshot.line_len(row_range.end.previous_row()),
10188 ));
10189 cursor_positions.push(anchor..anchor);
10190 }
10191
10192 self.transact(window, cx, |this, window, cx| {
10193 for row_range in row_ranges.into_iter().rev() {
10194 for row in row_range.iter_rows().rev() {
10195 let end_of_line = Point::new(row.0, snapshot.line_len(row));
10196 let next_line_row = row.next_row();
10197 let indent = snapshot.indent_size_for_line(next_line_row);
10198 let start_of_next_line = Point::new(next_line_row.0, indent.len);
10199
10200 let replace =
10201 if snapshot.line_len(next_line_row) > indent.len && insert_whitespace {
10202 " "
10203 } else {
10204 ""
10205 };
10206
10207 this.buffer.update(cx, |buffer, cx| {
10208 buffer.edit([(end_of_line..start_of_next_line, replace)], None, cx)
10209 });
10210 }
10211 }
10212
10213 this.change_selections(Default::default(), window, cx, |s| {
10214 s.select_anchor_ranges(cursor_positions)
10215 });
10216 });
10217 }
10218
10219 pub fn join_lines(&mut self, _: &JoinLines, window: &mut Window, cx: &mut Context<Self>) {
10220 self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
10221 self.join_lines_impl(true, window, cx);
10222 }
10223
10224 pub fn sort_lines_case_sensitive(
10225 &mut self,
10226 _: &SortLinesCaseSensitive,
10227 window: &mut Window,
10228 cx: &mut Context<Self>,
10229 ) {
10230 self.manipulate_immutable_lines(window, cx, |lines| lines.sort())
10231 }
10232
10233 pub fn sort_lines_by_length(
10234 &mut self,
10235 _: &SortLinesByLength,
10236 window: &mut Window,
10237 cx: &mut Context<Self>,
10238 ) {
10239 self.manipulate_immutable_lines(window, cx, |lines| {
10240 lines.sort_by_key(|&line| line.chars().count())
10241 })
10242 }
10243
10244 pub fn sort_lines_case_insensitive(
10245 &mut self,
10246 _: &SortLinesCaseInsensitive,
10247 window: &mut Window,
10248 cx: &mut Context<Self>,
10249 ) {
10250 self.manipulate_immutable_lines(window, cx, |lines| {
10251 lines.sort_by_key(|line| line.to_lowercase())
10252 })
10253 }
10254
10255 pub fn unique_lines_case_insensitive(
10256 &mut self,
10257 _: &UniqueLinesCaseInsensitive,
10258 window: &mut Window,
10259 cx: &mut Context<Self>,
10260 ) {
10261 self.manipulate_immutable_lines(window, cx, |lines| {
10262 let mut seen = HashSet::default();
10263 lines.retain(|line| seen.insert(line.to_lowercase()));
10264 })
10265 }
10266
10267 pub fn unique_lines_case_sensitive(
10268 &mut self,
10269 _: &UniqueLinesCaseSensitive,
10270 window: &mut Window,
10271 cx: &mut Context<Self>,
10272 ) {
10273 self.manipulate_immutable_lines(window, cx, |lines| {
10274 let mut seen = HashSet::default();
10275 lines.retain(|line| seen.insert(*line));
10276 })
10277 }
10278
10279 pub fn reload_file(&mut self, _: &ReloadFile, window: &mut Window, cx: &mut Context<Self>) {
10280 let Some(project) = self.project.clone() else {
10281 return;
10282 };
10283 self.reload(project, window, cx)
10284 .detach_and_notify_err(window, cx);
10285 }
10286
10287 pub fn restore_file(
10288 &mut self,
10289 _: &::git::RestoreFile,
10290 window: &mut Window,
10291 cx: &mut Context<Self>,
10292 ) {
10293 self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
10294 let mut buffer_ids = HashSet::default();
10295 let snapshot = self.buffer().read(cx).snapshot(cx);
10296 for selection in self.selections.all::<usize>(cx) {
10297 buffer_ids.extend(snapshot.buffer_ids_for_range(selection.range()))
10298 }
10299
10300 let buffer = self.buffer().read(cx);
10301 let ranges = buffer_ids
10302 .into_iter()
10303 .flat_map(|buffer_id| buffer.excerpt_ranges_for_buffer(buffer_id, cx))
10304 .collect::<Vec<_>>();
10305
10306 self.restore_hunks_in_ranges(ranges, window, cx);
10307 }
10308
10309 pub fn git_restore(&mut self, _: &Restore, window: &mut Window, cx: &mut Context<Self>) {
10310 self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
10311 let selections = self
10312 .selections
10313 .all(cx)
10314 .into_iter()
10315 .map(|s| s.range())
10316 .collect();
10317 self.restore_hunks_in_ranges(selections, window, cx);
10318 }
10319
10320 pub fn restore_hunks_in_ranges(
10321 &mut self,
10322 ranges: Vec<Range<Point>>,
10323 window: &mut Window,
10324 cx: &mut Context<Editor>,
10325 ) {
10326 let mut revert_changes = HashMap::default();
10327 let chunk_by = self
10328 .snapshot(window, cx)
10329 .hunks_for_ranges(ranges)
10330 .into_iter()
10331 .chunk_by(|hunk| hunk.buffer_id);
10332 for (buffer_id, hunks) in &chunk_by {
10333 let hunks = hunks.collect::<Vec<_>>();
10334 for hunk in &hunks {
10335 self.prepare_restore_change(&mut revert_changes, hunk, cx);
10336 }
10337 self.do_stage_or_unstage(false, buffer_id, hunks.into_iter(), cx);
10338 }
10339 drop(chunk_by);
10340 if !revert_changes.is_empty() {
10341 self.transact(window, cx, |editor, window, cx| {
10342 editor.restore(revert_changes, window, cx);
10343 });
10344 }
10345 }
10346
10347 pub fn open_active_item_in_terminal(
10348 &mut self,
10349 _: &OpenInTerminal,
10350 window: &mut Window,
10351 cx: &mut Context<Self>,
10352 ) {
10353 if let Some(working_directory) = self.active_excerpt(cx).and_then(|(_, buffer, _)| {
10354 let project_path = buffer.read(cx).project_path(cx)?;
10355 let project = self.project.as_ref()?.read(cx);
10356 let entry = project.entry_for_path(&project_path, cx)?;
10357 let parent = match &entry.canonical_path {
10358 Some(canonical_path) => canonical_path.to_path_buf(),
10359 None => project.absolute_path(&project_path, cx)?,
10360 }
10361 .parent()?
10362 .to_path_buf();
10363 Some(parent)
10364 }) {
10365 window.dispatch_action(OpenTerminal { working_directory }.boxed_clone(), cx);
10366 }
10367 }
10368
10369 fn set_breakpoint_context_menu(
10370 &mut self,
10371 display_row: DisplayRow,
10372 position: Option<Anchor>,
10373 clicked_point: gpui::Point<Pixels>,
10374 window: &mut Window,
10375 cx: &mut Context<Self>,
10376 ) {
10377 let source = self
10378 .buffer
10379 .read(cx)
10380 .snapshot(cx)
10381 .anchor_before(Point::new(display_row.0, 0u32));
10382
10383 let context_menu = self.breakpoint_context_menu(position.unwrap_or(source), window, cx);
10384
10385 self.mouse_context_menu = MouseContextMenu::pinned_to_editor(
10386 self,
10387 source,
10388 clicked_point,
10389 context_menu,
10390 window,
10391 cx,
10392 );
10393 }
10394
10395 fn add_edit_breakpoint_block(
10396 &mut self,
10397 anchor: Anchor,
10398 breakpoint: &Breakpoint,
10399 edit_action: BreakpointPromptEditAction,
10400 window: &mut Window,
10401 cx: &mut Context<Self>,
10402 ) {
10403 let weak_editor = cx.weak_entity();
10404 let bp_prompt = cx.new(|cx| {
10405 BreakpointPromptEditor::new(
10406 weak_editor,
10407 anchor,
10408 breakpoint.clone(),
10409 edit_action,
10410 window,
10411 cx,
10412 )
10413 });
10414
10415 let height = bp_prompt.update(cx, |this, cx| {
10416 this.prompt
10417 .update(cx, |prompt, cx| prompt.max_point(cx).row().0 + 1 + 2)
10418 });
10419 let cloned_prompt = bp_prompt.clone();
10420 let blocks = vec![BlockProperties {
10421 style: BlockStyle::Sticky,
10422 placement: BlockPlacement::Above(anchor),
10423 height: Some(height),
10424 render: Arc::new(move |cx| {
10425 *cloned_prompt.read(cx).editor_margins.lock() = *cx.margins;
10426 cloned_prompt.clone().into_any_element()
10427 }),
10428 priority: 0,
10429 render_in_minimap: true,
10430 }];
10431
10432 let focus_handle = bp_prompt.focus_handle(cx);
10433 window.focus(&focus_handle);
10434
10435 let block_ids = self.insert_blocks(blocks, None, cx);
10436 bp_prompt.update(cx, |prompt, _| {
10437 prompt.add_block_ids(block_ids);
10438 });
10439 }
10440
10441 pub(crate) fn breakpoint_at_row(
10442 &self,
10443 row: u32,
10444 window: &mut Window,
10445 cx: &mut Context<Self>,
10446 ) -> Option<(Anchor, Breakpoint)> {
10447 let snapshot = self.snapshot(window, cx);
10448 let breakpoint_position = snapshot.buffer_snapshot.anchor_before(Point::new(row, 0));
10449
10450 self.breakpoint_at_anchor(breakpoint_position, &snapshot, cx)
10451 }
10452
10453 pub(crate) fn breakpoint_at_anchor(
10454 &self,
10455 breakpoint_position: Anchor,
10456 snapshot: &EditorSnapshot,
10457 cx: &mut Context<Self>,
10458 ) -> Option<(Anchor, Breakpoint)> {
10459 let project = self.project.clone()?;
10460
10461 let buffer_id = breakpoint_position.buffer_id.or_else(|| {
10462 snapshot
10463 .buffer_snapshot
10464 .buffer_id_for_excerpt(breakpoint_position.excerpt_id)
10465 })?;
10466
10467 let enclosing_excerpt = breakpoint_position.excerpt_id;
10468 let buffer = project.read(cx).buffer_for_id(buffer_id, cx)?;
10469 let buffer_snapshot = buffer.read(cx).snapshot();
10470
10471 let row = buffer_snapshot
10472 .summary_for_anchor::<text::PointUtf16>(&breakpoint_position.text_anchor)
10473 .row;
10474
10475 let line_len = snapshot.buffer_snapshot.line_len(MultiBufferRow(row));
10476 let anchor_end = snapshot
10477 .buffer_snapshot
10478 .anchor_after(Point::new(row, line_len));
10479
10480 let bp = self
10481 .breakpoint_store
10482 .as_ref()?
10483 .read_with(cx, |breakpoint_store, cx| {
10484 breakpoint_store
10485 .breakpoints(
10486 &buffer,
10487 Some(breakpoint_position.text_anchor..anchor_end.text_anchor),
10488 &buffer_snapshot,
10489 cx,
10490 )
10491 .next()
10492 .and_then(|(bp, _)| {
10493 let breakpoint_row = buffer_snapshot
10494 .summary_for_anchor::<text::PointUtf16>(&bp.position)
10495 .row;
10496
10497 if breakpoint_row == row {
10498 snapshot
10499 .buffer_snapshot
10500 .anchor_in_excerpt(enclosing_excerpt, bp.position)
10501 .map(|position| (position, bp.bp.clone()))
10502 } else {
10503 None
10504 }
10505 })
10506 });
10507 bp
10508 }
10509
10510 pub fn edit_log_breakpoint(
10511 &mut self,
10512 _: &EditLogBreakpoint,
10513 window: &mut Window,
10514 cx: &mut Context<Self>,
10515 ) {
10516 for (anchor, breakpoint) in self.breakpoints_at_cursors(window, cx) {
10517 let breakpoint = breakpoint.unwrap_or_else(|| Breakpoint {
10518 message: None,
10519 state: BreakpointState::Enabled,
10520 condition: None,
10521 hit_condition: None,
10522 });
10523
10524 self.add_edit_breakpoint_block(
10525 anchor,
10526 &breakpoint,
10527 BreakpointPromptEditAction::Log,
10528 window,
10529 cx,
10530 );
10531 }
10532 }
10533
10534 fn breakpoints_at_cursors(
10535 &self,
10536 window: &mut Window,
10537 cx: &mut Context<Self>,
10538 ) -> Vec<(Anchor, Option<Breakpoint>)> {
10539 let snapshot = self.snapshot(window, cx);
10540 let cursors = self
10541 .selections
10542 .disjoint_anchors()
10543 .into_iter()
10544 .map(|selection| {
10545 let cursor_position: Point = selection.head().to_point(&snapshot.buffer_snapshot);
10546
10547 let breakpoint_position = self
10548 .breakpoint_at_row(cursor_position.row, window, cx)
10549 .map(|bp| bp.0)
10550 .unwrap_or_else(|| {
10551 snapshot
10552 .display_snapshot
10553 .buffer_snapshot
10554 .anchor_after(Point::new(cursor_position.row, 0))
10555 });
10556
10557 let breakpoint = self
10558 .breakpoint_at_anchor(breakpoint_position, &snapshot, cx)
10559 .map(|(anchor, breakpoint)| (anchor, Some(breakpoint)));
10560
10561 breakpoint.unwrap_or_else(|| (breakpoint_position, None))
10562 })
10563 // 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.
10564 .collect::<HashMap<Anchor, _>>();
10565
10566 cursors.into_iter().collect()
10567 }
10568
10569 pub fn enable_breakpoint(
10570 &mut self,
10571 _: &crate::actions::EnableBreakpoint,
10572 window: &mut Window,
10573 cx: &mut Context<Self>,
10574 ) {
10575 for (anchor, breakpoint) in self.breakpoints_at_cursors(window, cx) {
10576 let Some(breakpoint) = breakpoint.filter(|breakpoint| breakpoint.is_disabled()) else {
10577 continue;
10578 };
10579 self.edit_breakpoint_at_anchor(
10580 anchor,
10581 breakpoint,
10582 BreakpointEditAction::InvertState,
10583 cx,
10584 );
10585 }
10586 }
10587
10588 pub fn disable_breakpoint(
10589 &mut self,
10590 _: &crate::actions::DisableBreakpoint,
10591 window: &mut Window,
10592 cx: &mut Context<Self>,
10593 ) {
10594 for (anchor, breakpoint) in self.breakpoints_at_cursors(window, cx) {
10595 let Some(breakpoint) = breakpoint.filter(|breakpoint| breakpoint.is_enabled()) else {
10596 continue;
10597 };
10598 self.edit_breakpoint_at_anchor(
10599 anchor,
10600 breakpoint,
10601 BreakpointEditAction::InvertState,
10602 cx,
10603 );
10604 }
10605 }
10606
10607 pub fn toggle_breakpoint(
10608 &mut self,
10609 _: &crate::actions::ToggleBreakpoint,
10610 window: &mut Window,
10611 cx: &mut Context<Self>,
10612 ) {
10613 for (anchor, breakpoint) in self.breakpoints_at_cursors(window, cx) {
10614 if let Some(breakpoint) = breakpoint {
10615 self.edit_breakpoint_at_anchor(
10616 anchor,
10617 breakpoint,
10618 BreakpointEditAction::Toggle,
10619 cx,
10620 );
10621 } else {
10622 self.edit_breakpoint_at_anchor(
10623 anchor,
10624 Breakpoint::new_standard(),
10625 BreakpointEditAction::Toggle,
10626 cx,
10627 );
10628 }
10629 }
10630 }
10631
10632 pub fn edit_breakpoint_at_anchor(
10633 &mut self,
10634 breakpoint_position: Anchor,
10635 breakpoint: Breakpoint,
10636 edit_action: BreakpointEditAction,
10637 cx: &mut Context<Self>,
10638 ) {
10639 let Some(breakpoint_store) = &self.breakpoint_store else {
10640 return;
10641 };
10642
10643 let Some(buffer_id) = breakpoint_position.buffer_id.or_else(|| {
10644 if breakpoint_position == Anchor::min() {
10645 self.buffer()
10646 .read(cx)
10647 .excerpt_buffer_ids()
10648 .into_iter()
10649 .next()
10650 } else {
10651 None
10652 }
10653 }) else {
10654 return;
10655 };
10656
10657 let Some(buffer) = self.buffer().read(cx).buffer(buffer_id) else {
10658 return;
10659 };
10660
10661 breakpoint_store.update(cx, |breakpoint_store, cx| {
10662 breakpoint_store.toggle_breakpoint(
10663 buffer,
10664 BreakpointWithPosition {
10665 position: breakpoint_position.text_anchor,
10666 bp: breakpoint,
10667 },
10668 edit_action,
10669 cx,
10670 );
10671 });
10672
10673 cx.notify();
10674 }
10675
10676 #[cfg(any(test, feature = "test-support"))]
10677 pub fn breakpoint_store(&self) -> Option<Entity<BreakpointStore>> {
10678 self.breakpoint_store.clone()
10679 }
10680
10681 pub fn prepare_restore_change(
10682 &self,
10683 revert_changes: &mut HashMap<BufferId, Vec<(Range<text::Anchor>, Rope)>>,
10684 hunk: &MultiBufferDiffHunk,
10685 cx: &mut App,
10686 ) -> Option<()> {
10687 if hunk.is_created_file() {
10688 return None;
10689 }
10690 let buffer = self.buffer.read(cx);
10691 let diff = buffer.diff_for(hunk.buffer_id)?;
10692 let buffer = buffer.buffer(hunk.buffer_id)?;
10693 let buffer = buffer.read(cx);
10694 let original_text = diff
10695 .read(cx)
10696 .base_text()
10697 .as_rope()
10698 .slice(hunk.diff_base_byte_range.clone());
10699 let buffer_snapshot = buffer.snapshot();
10700 let buffer_revert_changes = revert_changes.entry(buffer.remote_id()).or_default();
10701 if let Err(i) = buffer_revert_changes.binary_search_by(|probe| {
10702 probe
10703 .0
10704 .start
10705 .cmp(&hunk.buffer_range.start, &buffer_snapshot)
10706 .then(probe.0.end.cmp(&hunk.buffer_range.end, &buffer_snapshot))
10707 }) {
10708 buffer_revert_changes.insert(i, (hunk.buffer_range.clone(), original_text));
10709 Some(())
10710 } else {
10711 None
10712 }
10713 }
10714
10715 pub fn reverse_lines(&mut self, _: &ReverseLines, window: &mut Window, cx: &mut Context<Self>) {
10716 self.manipulate_immutable_lines(window, cx, |lines| lines.reverse())
10717 }
10718
10719 pub fn shuffle_lines(&mut self, _: &ShuffleLines, window: &mut Window, cx: &mut Context<Self>) {
10720 self.manipulate_immutable_lines(window, cx, |lines| lines.shuffle(&mut thread_rng()))
10721 }
10722
10723 fn manipulate_lines<M>(
10724 &mut self,
10725 window: &mut Window,
10726 cx: &mut Context<Self>,
10727 mut manipulate: M,
10728 ) where
10729 M: FnMut(&str) -> LineManipulationResult,
10730 {
10731 self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
10732
10733 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
10734 let buffer = self.buffer.read(cx).snapshot(cx);
10735
10736 let mut edits = Vec::new();
10737
10738 let selections = self.selections.all::<Point>(cx);
10739 let mut selections = selections.iter().peekable();
10740 let mut contiguous_row_selections = Vec::new();
10741 let mut new_selections = Vec::new();
10742 let mut added_lines = 0;
10743 let mut removed_lines = 0;
10744
10745 while let Some(selection) = selections.next() {
10746 let (start_row, end_row) = consume_contiguous_rows(
10747 &mut contiguous_row_selections,
10748 selection,
10749 &display_map,
10750 &mut selections,
10751 );
10752
10753 let start_point = Point::new(start_row.0, 0);
10754 let end_point = Point::new(
10755 end_row.previous_row().0,
10756 buffer.line_len(end_row.previous_row()),
10757 );
10758 let text = buffer
10759 .text_for_range(start_point..end_point)
10760 .collect::<String>();
10761
10762 let LineManipulationResult {
10763 new_text,
10764 line_count_before,
10765 line_count_after,
10766 } = manipulate(&text);
10767
10768 edits.push((start_point..end_point, new_text));
10769
10770 // Selections must change based on added and removed line count
10771 let start_row =
10772 MultiBufferRow(start_point.row + added_lines as u32 - removed_lines as u32);
10773 let end_row = MultiBufferRow(start_row.0 + line_count_after.saturating_sub(1) as u32);
10774 new_selections.push(Selection {
10775 id: selection.id,
10776 start: start_row,
10777 end: end_row,
10778 goal: SelectionGoal::None,
10779 reversed: selection.reversed,
10780 });
10781
10782 if line_count_after > line_count_before {
10783 added_lines += line_count_after - line_count_before;
10784 } else if line_count_before > line_count_after {
10785 removed_lines += line_count_before - line_count_after;
10786 }
10787 }
10788
10789 self.transact(window, cx, |this, window, cx| {
10790 let buffer = this.buffer.update(cx, |buffer, cx| {
10791 buffer.edit(edits, None, cx);
10792 buffer.snapshot(cx)
10793 });
10794
10795 // Recalculate offsets on newly edited buffer
10796 let new_selections = new_selections
10797 .iter()
10798 .map(|s| {
10799 let start_point = Point::new(s.start.0, 0);
10800 let end_point = Point::new(s.end.0, buffer.line_len(s.end));
10801 Selection {
10802 id: s.id,
10803 start: buffer.point_to_offset(start_point),
10804 end: buffer.point_to_offset(end_point),
10805 goal: s.goal,
10806 reversed: s.reversed,
10807 }
10808 })
10809 .collect();
10810
10811 this.change_selections(Default::default(), window, cx, |s| {
10812 s.select(new_selections);
10813 });
10814
10815 this.request_autoscroll(Autoscroll::fit(), cx);
10816 });
10817 }
10818
10819 pub fn toggle_case(&mut self, _: &ToggleCase, window: &mut Window, cx: &mut Context<Self>) {
10820 self.manipulate_text(window, cx, |text| {
10821 let has_upper_case_characters = text.chars().any(|c| c.is_uppercase());
10822 if has_upper_case_characters {
10823 text.to_lowercase()
10824 } else {
10825 text.to_uppercase()
10826 }
10827 })
10828 }
10829
10830 fn manipulate_immutable_lines<Fn>(
10831 &mut self,
10832 window: &mut Window,
10833 cx: &mut Context<Self>,
10834 mut callback: Fn,
10835 ) where
10836 Fn: FnMut(&mut Vec<&str>),
10837 {
10838 self.manipulate_lines(window, cx, |text| {
10839 let mut lines: Vec<&str> = text.split('\n').collect();
10840 let line_count_before = lines.len();
10841
10842 callback(&mut lines);
10843
10844 LineManipulationResult {
10845 new_text: lines.join("\n"),
10846 line_count_before,
10847 line_count_after: lines.len(),
10848 }
10849 });
10850 }
10851
10852 fn manipulate_mutable_lines<Fn>(
10853 &mut self,
10854 window: &mut Window,
10855 cx: &mut Context<Self>,
10856 mut callback: Fn,
10857 ) where
10858 Fn: FnMut(&mut Vec<Cow<'_, str>>),
10859 {
10860 self.manipulate_lines(window, cx, |text| {
10861 let mut lines: Vec<Cow<str>> = text.split('\n').map(Cow::from).collect();
10862 let line_count_before = lines.len();
10863
10864 callback(&mut lines);
10865
10866 LineManipulationResult {
10867 new_text: lines.join("\n"),
10868 line_count_before,
10869 line_count_after: lines.len(),
10870 }
10871 });
10872 }
10873
10874 pub fn convert_indentation_to_spaces(
10875 &mut self,
10876 _: &ConvertIndentationToSpaces,
10877 window: &mut Window,
10878 cx: &mut Context<Self>,
10879 ) {
10880 let settings = self.buffer.read(cx).language_settings(cx);
10881 let tab_size = settings.tab_size.get() as usize;
10882
10883 self.manipulate_mutable_lines(window, cx, |lines| {
10884 // Allocates a reasonably sized scratch buffer once for the whole loop
10885 let mut reindented_line = String::with_capacity(MAX_LINE_LEN);
10886 // Avoids recomputing spaces that could be inserted many times
10887 let space_cache: Vec<Vec<char>> = (1..=tab_size)
10888 .map(|n| IndentSize::spaces(n as u32).chars().collect())
10889 .collect();
10890
10891 for line in lines.iter_mut().filter(|line| !line.is_empty()) {
10892 let mut chars = line.as_ref().chars();
10893 let mut col = 0;
10894 let mut changed = false;
10895
10896 while let Some(ch) = chars.next() {
10897 match ch {
10898 ' ' => {
10899 reindented_line.push(' ');
10900 col += 1;
10901 }
10902 '\t' => {
10903 // \t are converted to spaces depending on the current column
10904 let spaces_len = tab_size - (col % tab_size);
10905 reindented_line.extend(&space_cache[spaces_len - 1]);
10906 col += spaces_len;
10907 changed = true;
10908 }
10909 _ => {
10910 // If we dont append before break, the character is consumed
10911 reindented_line.push(ch);
10912 break;
10913 }
10914 }
10915 }
10916
10917 if !changed {
10918 reindented_line.clear();
10919 continue;
10920 }
10921 // Append the rest of the line and replace old reference with new one
10922 reindented_line.extend(chars);
10923 *line = Cow::Owned(reindented_line.clone());
10924 reindented_line.clear();
10925 }
10926 });
10927 }
10928
10929 pub fn convert_indentation_to_tabs(
10930 &mut self,
10931 _: &ConvertIndentationToTabs,
10932 window: &mut Window,
10933 cx: &mut Context<Self>,
10934 ) {
10935 let settings = self.buffer.read(cx).language_settings(cx);
10936 let tab_size = settings.tab_size.get() as usize;
10937
10938 self.manipulate_mutable_lines(window, cx, |lines| {
10939 // Allocates a reasonably sized buffer once for the whole loop
10940 let mut reindented_line = String::with_capacity(MAX_LINE_LEN);
10941 // Avoids recomputing spaces that could be inserted many times
10942 let space_cache: Vec<Vec<char>> = (1..=tab_size)
10943 .map(|n| IndentSize::spaces(n as u32).chars().collect())
10944 .collect();
10945
10946 for line in lines.iter_mut().filter(|line| !line.is_empty()) {
10947 let mut chars = line.chars();
10948 let mut spaces_count = 0;
10949 let mut first_non_indent_char = None;
10950 let mut changed = false;
10951
10952 while let Some(ch) = chars.next() {
10953 match ch {
10954 ' ' => {
10955 // Keep track of spaces. Append \t when we reach tab_size
10956 spaces_count += 1;
10957 changed = true;
10958 if spaces_count == tab_size {
10959 reindented_line.push('\t');
10960 spaces_count = 0;
10961 }
10962 }
10963 '\t' => {
10964 reindented_line.push('\t');
10965 spaces_count = 0;
10966 }
10967 _ => {
10968 // Dont append it yet, we might have remaining spaces
10969 first_non_indent_char = Some(ch);
10970 break;
10971 }
10972 }
10973 }
10974
10975 if !changed {
10976 reindented_line.clear();
10977 continue;
10978 }
10979 // Remaining spaces that didn't make a full tab stop
10980 if spaces_count > 0 {
10981 reindented_line.extend(&space_cache[spaces_count - 1]);
10982 }
10983 // If we consume an extra character that was not indentation, add it back
10984 if let Some(extra_char) = first_non_indent_char {
10985 reindented_line.push(extra_char);
10986 }
10987 // Append the rest of the line and replace old reference with new one
10988 reindented_line.extend(chars);
10989 *line = Cow::Owned(reindented_line.clone());
10990 reindented_line.clear();
10991 }
10992 });
10993 }
10994
10995 pub fn convert_to_upper_case(
10996 &mut self,
10997 _: &ConvertToUpperCase,
10998 window: &mut Window,
10999 cx: &mut Context<Self>,
11000 ) {
11001 self.manipulate_text(window, cx, |text| text.to_uppercase())
11002 }
11003
11004 pub fn convert_to_lower_case(
11005 &mut self,
11006 _: &ConvertToLowerCase,
11007 window: &mut Window,
11008 cx: &mut Context<Self>,
11009 ) {
11010 self.manipulate_text(window, cx, |text| text.to_lowercase())
11011 }
11012
11013 pub fn convert_to_title_case(
11014 &mut self,
11015 _: &ConvertToTitleCase,
11016 window: &mut Window,
11017 cx: &mut Context<Self>,
11018 ) {
11019 self.manipulate_text(window, cx, |text| {
11020 text.split('\n')
11021 .map(|line| line.to_case(Case::Title))
11022 .join("\n")
11023 })
11024 }
11025
11026 pub fn convert_to_snake_case(
11027 &mut self,
11028 _: &ConvertToSnakeCase,
11029 window: &mut Window,
11030 cx: &mut Context<Self>,
11031 ) {
11032 self.manipulate_text(window, cx, |text| text.to_case(Case::Snake))
11033 }
11034
11035 pub fn convert_to_kebab_case(
11036 &mut self,
11037 _: &ConvertToKebabCase,
11038 window: &mut Window,
11039 cx: &mut Context<Self>,
11040 ) {
11041 self.manipulate_text(window, cx, |text| text.to_case(Case::Kebab))
11042 }
11043
11044 pub fn convert_to_upper_camel_case(
11045 &mut self,
11046 _: &ConvertToUpperCamelCase,
11047 window: &mut Window,
11048 cx: &mut Context<Self>,
11049 ) {
11050 self.manipulate_text(window, cx, |text| {
11051 text.split('\n')
11052 .map(|line| line.to_case(Case::UpperCamel))
11053 .join("\n")
11054 })
11055 }
11056
11057 pub fn convert_to_lower_camel_case(
11058 &mut self,
11059 _: &ConvertToLowerCamelCase,
11060 window: &mut Window,
11061 cx: &mut Context<Self>,
11062 ) {
11063 self.manipulate_text(window, cx, |text| text.to_case(Case::Camel))
11064 }
11065
11066 pub fn convert_to_opposite_case(
11067 &mut self,
11068 _: &ConvertToOppositeCase,
11069 window: &mut Window,
11070 cx: &mut Context<Self>,
11071 ) {
11072 self.manipulate_text(window, cx, |text| {
11073 text.chars()
11074 .fold(String::with_capacity(text.len()), |mut t, c| {
11075 if c.is_uppercase() {
11076 t.extend(c.to_lowercase());
11077 } else {
11078 t.extend(c.to_uppercase());
11079 }
11080 t
11081 })
11082 })
11083 }
11084
11085 pub fn convert_to_rot13(
11086 &mut self,
11087 _: &ConvertToRot13,
11088 window: &mut Window,
11089 cx: &mut Context<Self>,
11090 ) {
11091 self.manipulate_text(window, cx, |text| {
11092 text.chars()
11093 .map(|c| match c {
11094 'A'..='M' | 'a'..='m' => ((c as u8) + 13) as char,
11095 'N'..='Z' | 'n'..='z' => ((c as u8) - 13) as char,
11096 _ => c,
11097 })
11098 .collect()
11099 })
11100 }
11101
11102 pub fn convert_to_rot47(
11103 &mut self,
11104 _: &ConvertToRot47,
11105 window: &mut Window,
11106 cx: &mut Context<Self>,
11107 ) {
11108 self.manipulate_text(window, cx, |text| {
11109 text.chars()
11110 .map(|c| {
11111 let code_point = c as u32;
11112 if code_point >= 33 && code_point <= 126 {
11113 return char::from_u32(33 + ((code_point + 14) % 94)).unwrap();
11114 }
11115 c
11116 })
11117 .collect()
11118 })
11119 }
11120
11121 fn manipulate_text<Fn>(&mut self, window: &mut Window, cx: &mut Context<Self>, mut callback: Fn)
11122 where
11123 Fn: FnMut(&str) -> String,
11124 {
11125 let buffer = self.buffer.read(cx).snapshot(cx);
11126
11127 let mut new_selections = Vec::new();
11128 let mut edits = Vec::new();
11129 let mut selection_adjustment = 0i32;
11130
11131 for selection in self.selections.all::<usize>(cx) {
11132 let selection_is_empty = selection.is_empty();
11133
11134 let (start, end) = if selection_is_empty {
11135 let (word_range, _) = buffer.surrounding_word(selection.start, false);
11136 (word_range.start, word_range.end)
11137 } else {
11138 (selection.start, selection.end)
11139 };
11140
11141 let text = buffer.text_for_range(start..end).collect::<String>();
11142 let old_length = text.len() as i32;
11143 let text = callback(&text);
11144
11145 new_selections.push(Selection {
11146 start: (start as i32 - selection_adjustment) as usize,
11147 end: ((start + text.len()) as i32 - selection_adjustment) as usize,
11148 goal: SelectionGoal::None,
11149 ..selection
11150 });
11151
11152 selection_adjustment += old_length - text.len() as i32;
11153
11154 edits.push((start..end, text));
11155 }
11156
11157 self.transact(window, cx, |this, window, cx| {
11158 this.buffer.update(cx, |buffer, cx| {
11159 buffer.edit(edits, None, cx);
11160 });
11161
11162 this.change_selections(Default::default(), window, cx, |s| {
11163 s.select(new_selections);
11164 });
11165
11166 this.request_autoscroll(Autoscroll::fit(), cx);
11167 });
11168 }
11169
11170 pub fn move_selection_on_drop(
11171 &mut self,
11172 selection: &Selection<Anchor>,
11173 target: DisplayPoint,
11174 is_cut: bool,
11175 window: &mut Window,
11176 cx: &mut Context<Self>,
11177 ) {
11178 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
11179 let buffer = &display_map.buffer_snapshot;
11180 let mut edits = Vec::new();
11181 let insert_point = display_map
11182 .clip_point(target, Bias::Left)
11183 .to_point(&display_map);
11184 let text = buffer
11185 .text_for_range(selection.start..selection.end)
11186 .collect::<String>();
11187 if is_cut {
11188 edits.push(((selection.start..selection.end), String::new()));
11189 }
11190 let insert_anchor = buffer.anchor_before(insert_point);
11191 edits.push(((insert_anchor..insert_anchor), text));
11192 let last_edit_start = insert_anchor.bias_left(buffer);
11193 let last_edit_end = insert_anchor.bias_right(buffer);
11194 self.transact(window, cx, |this, window, cx| {
11195 this.buffer.update(cx, |buffer, cx| {
11196 buffer.edit(edits, None, cx);
11197 });
11198 this.change_selections(Default::default(), window, cx, |s| {
11199 s.select_anchor_ranges([last_edit_start..last_edit_end]);
11200 });
11201 });
11202 }
11203
11204 pub fn clear_selection_drag_state(&mut self) {
11205 self.selection_drag_state = SelectionDragState::None;
11206 }
11207
11208 pub fn duplicate(
11209 &mut self,
11210 upwards: bool,
11211 whole_lines: bool,
11212 window: &mut Window,
11213 cx: &mut Context<Self>,
11214 ) {
11215 self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
11216
11217 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
11218 let buffer = &display_map.buffer_snapshot;
11219 let selections = self.selections.all::<Point>(cx);
11220
11221 let mut edits = Vec::new();
11222 let mut selections_iter = selections.iter().peekable();
11223 while let Some(selection) = selections_iter.next() {
11224 let mut rows = selection.spanned_rows(false, &display_map);
11225 // duplicate line-wise
11226 if whole_lines || selection.start == selection.end {
11227 // Avoid duplicating the same lines twice.
11228 while let Some(next_selection) = selections_iter.peek() {
11229 let next_rows = next_selection.spanned_rows(false, &display_map);
11230 if next_rows.start < rows.end {
11231 rows.end = next_rows.end;
11232 selections_iter.next().unwrap();
11233 } else {
11234 break;
11235 }
11236 }
11237
11238 // Copy the text from the selected row region and splice it either at the start
11239 // or end of the region.
11240 let start = Point::new(rows.start.0, 0);
11241 let end = Point::new(
11242 rows.end.previous_row().0,
11243 buffer.line_len(rows.end.previous_row()),
11244 );
11245 let text = buffer
11246 .text_for_range(start..end)
11247 .chain(Some("\n"))
11248 .collect::<String>();
11249 let insert_location = if upwards {
11250 Point::new(rows.end.0, 0)
11251 } else {
11252 start
11253 };
11254 edits.push((insert_location..insert_location, text));
11255 } else {
11256 // duplicate character-wise
11257 let start = selection.start;
11258 let end = selection.end;
11259 let text = buffer.text_for_range(start..end).collect::<String>();
11260 edits.push((selection.end..selection.end, text));
11261 }
11262 }
11263
11264 self.transact(window, cx, |this, _, cx| {
11265 this.buffer.update(cx, |buffer, cx| {
11266 buffer.edit(edits, None, cx);
11267 });
11268
11269 this.request_autoscroll(Autoscroll::fit(), cx);
11270 });
11271 }
11272
11273 pub fn duplicate_line_up(
11274 &mut self,
11275 _: &DuplicateLineUp,
11276 window: &mut Window,
11277 cx: &mut Context<Self>,
11278 ) {
11279 self.duplicate(true, true, window, cx);
11280 }
11281
11282 pub fn duplicate_line_down(
11283 &mut self,
11284 _: &DuplicateLineDown,
11285 window: &mut Window,
11286 cx: &mut Context<Self>,
11287 ) {
11288 self.duplicate(false, true, window, cx);
11289 }
11290
11291 pub fn duplicate_selection(
11292 &mut self,
11293 _: &DuplicateSelection,
11294 window: &mut Window,
11295 cx: &mut Context<Self>,
11296 ) {
11297 self.duplicate(false, false, window, cx);
11298 }
11299
11300 pub fn move_line_up(&mut self, _: &MoveLineUp, window: &mut Window, cx: &mut Context<Self>) {
11301 self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
11302 if self.mode.is_single_line() {
11303 cx.propagate();
11304 return;
11305 }
11306
11307 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
11308 let buffer = self.buffer.read(cx).snapshot(cx);
11309
11310 let mut edits = Vec::new();
11311 let mut unfold_ranges = Vec::new();
11312 let mut refold_creases = Vec::new();
11313
11314 let selections = self.selections.all::<Point>(cx);
11315 let mut selections = selections.iter().peekable();
11316 let mut contiguous_row_selections = Vec::new();
11317 let mut new_selections = Vec::new();
11318
11319 while let Some(selection) = selections.next() {
11320 // Find all the selections that span a contiguous row range
11321 let (start_row, end_row) = consume_contiguous_rows(
11322 &mut contiguous_row_selections,
11323 selection,
11324 &display_map,
11325 &mut selections,
11326 );
11327
11328 // Move the text spanned by the row range to be before the line preceding the row range
11329 if start_row.0 > 0 {
11330 let range_to_move = Point::new(
11331 start_row.previous_row().0,
11332 buffer.line_len(start_row.previous_row()),
11333 )
11334 ..Point::new(
11335 end_row.previous_row().0,
11336 buffer.line_len(end_row.previous_row()),
11337 );
11338 let insertion_point = display_map
11339 .prev_line_boundary(Point::new(start_row.previous_row().0, 0))
11340 .0;
11341
11342 // Don't move lines across excerpts
11343 if buffer
11344 .excerpt_containing(insertion_point..range_to_move.end)
11345 .is_some()
11346 {
11347 let text = buffer
11348 .text_for_range(range_to_move.clone())
11349 .flat_map(|s| s.chars())
11350 .skip(1)
11351 .chain(['\n'])
11352 .collect::<String>();
11353
11354 edits.push((
11355 buffer.anchor_after(range_to_move.start)
11356 ..buffer.anchor_before(range_to_move.end),
11357 String::new(),
11358 ));
11359 let insertion_anchor = buffer.anchor_after(insertion_point);
11360 edits.push((insertion_anchor..insertion_anchor, text));
11361
11362 let row_delta = range_to_move.start.row - insertion_point.row + 1;
11363
11364 // Move selections up
11365 new_selections.extend(contiguous_row_selections.drain(..).map(
11366 |mut selection| {
11367 selection.start.row -= row_delta;
11368 selection.end.row -= row_delta;
11369 selection
11370 },
11371 ));
11372
11373 // Move folds up
11374 unfold_ranges.push(range_to_move.clone());
11375 for fold in display_map.folds_in_range(
11376 buffer.anchor_before(range_to_move.start)
11377 ..buffer.anchor_after(range_to_move.end),
11378 ) {
11379 let mut start = fold.range.start.to_point(&buffer);
11380 let mut end = fold.range.end.to_point(&buffer);
11381 start.row -= row_delta;
11382 end.row -= row_delta;
11383 refold_creases.push(Crease::simple(start..end, fold.placeholder.clone()));
11384 }
11385 }
11386 }
11387
11388 // If we didn't move line(s), preserve the existing selections
11389 new_selections.append(&mut contiguous_row_selections);
11390 }
11391
11392 self.transact(window, cx, |this, window, cx| {
11393 this.unfold_ranges(&unfold_ranges, true, true, cx);
11394 this.buffer.update(cx, |buffer, cx| {
11395 for (range, text) in edits {
11396 buffer.edit([(range, text)], None, cx);
11397 }
11398 });
11399 this.fold_creases(refold_creases, true, window, cx);
11400 this.change_selections(Default::default(), window, cx, |s| {
11401 s.select(new_selections);
11402 })
11403 });
11404 }
11405
11406 pub fn move_line_down(
11407 &mut self,
11408 _: &MoveLineDown,
11409 window: &mut Window,
11410 cx: &mut Context<Self>,
11411 ) {
11412 self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
11413 if self.mode.is_single_line() {
11414 cx.propagate();
11415 return;
11416 }
11417
11418 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
11419 let buffer = self.buffer.read(cx).snapshot(cx);
11420
11421 let mut edits = Vec::new();
11422 let mut unfold_ranges = Vec::new();
11423 let mut refold_creases = Vec::new();
11424
11425 let selections = self.selections.all::<Point>(cx);
11426 let mut selections = selections.iter().peekable();
11427 let mut contiguous_row_selections = Vec::new();
11428 let mut new_selections = Vec::new();
11429
11430 while let Some(selection) = selections.next() {
11431 // Find all the selections that span a contiguous row range
11432 let (start_row, end_row) = consume_contiguous_rows(
11433 &mut contiguous_row_selections,
11434 selection,
11435 &display_map,
11436 &mut selections,
11437 );
11438
11439 // Move the text spanned by the row range to be after the last line of the row range
11440 if end_row.0 <= buffer.max_point().row {
11441 let range_to_move =
11442 MultiBufferPoint::new(start_row.0, 0)..MultiBufferPoint::new(end_row.0, 0);
11443 let insertion_point = display_map
11444 .next_line_boundary(MultiBufferPoint::new(end_row.0, 0))
11445 .0;
11446
11447 // Don't move lines across excerpt boundaries
11448 if buffer
11449 .excerpt_containing(range_to_move.start..insertion_point)
11450 .is_some()
11451 {
11452 let mut text = String::from("\n");
11453 text.extend(buffer.text_for_range(range_to_move.clone()));
11454 text.pop(); // Drop trailing newline
11455 edits.push((
11456 buffer.anchor_after(range_to_move.start)
11457 ..buffer.anchor_before(range_to_move.end),
11458 String::new(),
11459 ));
11460 let insertion_anchor = buffer.anchor_after(insertion_point);
11461 edits.push((insertion_anchor..insertion_anchor, text));
11462
11463 let row_delta = insertion_point.row - range_to_move.end.row + 1;
11464
11465 // Move selections down
11466 new_selections.extend(contiguous_row_selections.drain(..).map(
11467 |mut selection| {
11468 selection.start.row += row_delta;
11469 selection.end.row += row_delta;
11470 selection
11471 },
11472 ));
11473
11474 // Move folds down
11475 unfold_ranges.push(range_to_move.clone());
11476 for fold in display_map.folds_in_range(
11477 buffer.anchor_before(range_to_move.start)
11478 ..buffer.anchor_after(range_to_move.end),
11479 ) {
11480 let mut start = fold.range.start.to_point(&buffer);
11481 let mut end = fold.range.end.to_point(&buffer);
11482 start.row += row_delta;
11483 end.row += row_delta;
11484 refold_creases.push(Crease::simple(start..end, fold.placeholder.clone()));
11485 }
11486 }
11487 }
11488
11489 // If we didn't move line(s), preserve the existing selections
11490 new_selections.append(&mut contiguous_row_selections);
11491 }
11492
11493 self.transact(window, cx, |this, window, cx| {
11494 this.unfold_ranges(&unfold_ranges, true, true, cx);
11495 this.buffer.update(cx, |buffer, cx| {
11496 for (range, text) in edits {
11497 buffer.edit([(range, text)], None, cx);
11498 }
11499 });
11500 this.fold_creases(refold_creases, true, window, cx);
11501 this.change_selections(Default::default(), window, cx, |s| s.select(new_selections));
11502 });
11503 }
11504
11505 pub fn transpose(&mut self, _: &Transpose, window: &mut Window, cx: &mut Context<Self>) {
11506 self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
11507 let text_layout_details = &self.text_layout_details(window);
11508 self.transact(window, cx, |this, window, cx| {
11509 let edits = this.change_selections(Default::default(), window, cx, |s| {
11510 let mut edits: Vec<(Range<usize>, String)> = Default::default();
11511 s.move_with(|display_map, selection| {
11512 if !selection.is_empty() {
11513 return;
11514 }
11515
11516 let mut head = selection.head();
11517 let mut transpose_offset = head.to_offset(display_map, Bias::Right);
11518 if head.column() == display_map.line_len(head.row()) {
11519 transpose_offset = display_map
11520 .buffer_snapshot
11521 .clip_offset(transpose_offset.saturating_sub(1), Bias::Left);
11522 }
11523
11524 if transpose_offset == 0 {
11525 return;
11526 }
11527
11528 *head.column_mut() += 1;
11529 head = display_map.clip_point(head, Bias::Right);
11530 let goal = SelectionGoal::HorizontalPosition(
11531 display_map
11532 .x_for_display_point(head, text_layout_details)
11533 .into(),
11534 );
11535 selection.collapse_to(head, goal);
11536
11537 let transpose_start = display_map
11538 .buffer_snapshot
11539 .clip_offset(transpose_offset.saturating_sub(1), Bias::Left);
11540 if edits.last().map_or(true, |e| e.0.end <= transpose_start) {
11541 let transpose_end = display_map
11542 .buffer_snapshot
11543 .clip_offset(transpose_offset + 1, Bias::Right);
11544 if let Some(ch) =
11545 display_map.buffer_snapshot.chars_at(transpose_start).next()
11546 {
11547 edits.push((transpose_start..transpose_offset, String::new()));
11548 edits.push((transpose_end..transpose_end, ch.to_string()));
11549 }
11550 }
11551 });
11552 edits
11553 });
11554 this.buffer
11555 .update(cx, |buffer, cx| buffer.edit(edits, None, cx));
11556 let selections = this.selections.all::<usize>(cx);
11557 this.change_selections(Default::default(), window, cx, |s| {
11558 s.select(selections);
11559 });
11560 });
11561 }
11562
11563 pub fn rewrap(&mut self, _: &Rewrap, _: &mut Window, cx: &mut Context<Self>) {
11564 self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
11565 if self.mode.is_single_line() {
11566 cx.propagate();
11567 return;
11568 }
11569
11570 self.rewrap_impl(RewrapOptions::default(), cx)
11571 }
11572
11573 pub fn rewrap_impl(&mut self, options: RewrapOptions, cx: &mut Context<Self>) {
11574 let buffer = self.buffer.read(cx).snapshot(cx);
11575 let selections = self.selections.all::<Point>(cx);
11576
11577 // Split selections to respect paragraph, indent, and comment prefix boundaries.
11578 let wrap_ranges = selections.into_iter().flat_map(|selection| {
11579 let mut non_blank_rows_iter = (selection.start.row..=selection.end.row)
11580 .filter(|row| !buffer.is_line_blank(MultiBufferRow(*row)))
11581 .peekable();
11582
11583 let first_row = if let Some(&row) = non_blank_rows_iter.peek() {
11584 row
11585 } else {
11586 return Vec::new();
11587 };
11588
11589 let language_settings = buffer.language_settings_at(selection.head(), cx);
11590 let language_scope = buffer.language_scope_at(selection.head());
11591
11592 let indent_and_prefix_for_row =
11593 |row: u32| -> (IndentSize, Option<String>, Option<String>) {
11594 let indent = buffer.indent_size_for_line(MultiBufferRow(row));
11595 let (comment_prefix, rewrap_prefix) =
11596 if let Some(language_scope) = &language_scope {
11597 let indent_end = Point::new(row, indent.len);
11598 let comment_prefix = language_scope
11599 .line_comment_prefixes()
11600 .iter()
11601 .find(|prefix| buffer.contains_str_at(indent_end, prefix))
11602 .map(|prefix| prefix.to_string());
11603 let line_end = Point::new(row, buffer.line_len(MultiBufferRow(row)));
11604 let line_text_after_indent = buffer
11605 .text_for_range(indent_end..line_end)
11606 .collect::<String>();
11607 let rewrap_prefix = language_scope
11608 .rewrap_prefixes()
11609 .iter()
11610 .find_map(|prefix_regex| {
11611 prefix_regex.find(&line_text_after_indent).map(|mat| {
11612 if mat.start() == 0 {
11613 Some(mat.as_str().to_string())
11614 } else {
11615 None
11616 }
11617 })
11618 })
11619 .flatten();
11620 (comment_prefix, rewrap_prefix)
11621 } else {
11622 (None, None)
11623 };
11624 (indent, comment_prefix, rewrap_prefix)
11625 };
11626
11627 let mut ranges = Vec::new();
11628 let from_empty_selection = selection.is_empty();
11629
11630 let mut current_range_start = first_row;
11631 let mut prev_row = first_row;
11632 let (
11633 mut current_range_indent,
11634 mut current_range_comment_prefix,
11635 mut current_range_rewrap_prefix,
11636 ) = indent_and_prefix_for_row(first_row);
11637
11638 for row in non_blank_rows_iter.skip(1) {
11639 let has_paragraph_break = row > prev_row + 1;
11640
11641 let (row_indent, row_comment_prefix, row_rewrap_prefix) =
11642 indent_and_prefix_for_row(row);
11643
11644 let has_indent_change = row_indent != current_range_indent;
11645 let has_comment_change = row_comment_prefix != current_range_comment_prefix;
11646
11647 let has_boundary_change = has_comment_change
11648 || row_rewrap_prefix.is_some()
11649 || (has_indent_change && current_range_comment_prefix.is_some());
11650
11651 if has_paragraph_break || has_boundary_change {
11652 ranges.push((
11653 language_settings.clone(),
11654 Point::new(current_range_start, 0)
11655 ..Point::new(prev_row, buffer.line_len(MultiBufferRow(prev_row))),
11656 current_range_indent,
11657 current_range_comment_prefix.clone(),
11658 current_range_rewrap_prefix.clone(),
11659 from_empty_selection,
11660 ));
11661 current_range_start = row;
11662 current_range_indent = row_indent;
11663 current_range_comment_prefix = row_comment_prefix;
11664 current_range_rewrap_prefix = row_rewrap_prefix;
11665 }
11666 prev_row = row;
11667 }
11668
11669 ranges.push((
11670 language_settings.clone(),
11671 Point::new(current_range_start, 0)
11672 ..Point::new(prev_row, buffer.line_len(MultiBufferRow(prev_row))),
11673 current_range_indent,
11674 current_range_comment_prefix,
11675 current_range_rewrap_prefix,
11676 from_empty_selection,
11677 ));
11678
11679 ranges
11680 });
11681
11682 let mut edits = Vec::new();
11683 let mut rewrapped_row_ranges = Vec::<RangeInclusive<u32>>::new();
11684
11685 for (
11686 language_settings,
11687 wrap_range,
11688 indent_size,
11689 comment_prefix,
11690 rewrap_prefix,
11691 from_empty_selection,
11692 ) in wrap_ranges
11693 {
11694 let mut start_row = wrap_range.start.row;
11695 let mut end_row = wrap_range.end.row;
11696
11697 // Skip selections that overlap with a range that has already been rewrapped.
11698 let selection_range = start_row..end_row;
11699 if rewrapped_row_ranges
11700 .iter()
11701 .any(|range| range.overlaps(&selection_range))
11702 {
11703 continue;
11704 }
11705
11706 let tab_size = language_settings.tab_size;
11707
11708 let indent_prefix = indent_size.chars().collect::<String>();
11709 let mut line_prefix = indent_prefix.clone();
11710 let mut inside_comment = false;
11711 if let Some(prefix) = &comment_prefix {
11712 line_prefix.push_str(prefix);
11713 inside_comment = true;
11714 }
11715 if let Some(prefix) = &rewrap_prefix {
11716 line_prefix.push_str(prefix);
11717 }
11718
11719 let allow_rewrap_based_on_language = match language_settings.allow_rewrap {
11720 RewrapBehavior::InComments => inside_comment,
11721 RewrapBehavior::InSelections => !wrap_range.is_empty(),
11722 RewrapBehavior::Anywhere => true,
11723 };
11724
11725 let should_rewrap = options.override_language_settings
11726 || allow_rewrap_based_on_language
11727 || self.hard_wrap.is_some();
11728 if !should_rewrap {
11729 continue;
11730 }
11731
11732 if from_empty_selection {
11733 'expand_upwards: while start_row > 0 {
11734 let prev_row = start_row - 1;
11735 if buffer.contains_str_at(Point::new(prev_row, 0), &line_prefix)
11736 && buffer.line_len(MultiBufferRow(prev_row)) as usize > line_prefix.len()
11737 && !buffer.is_line_blank(MultiBufferRow(prev_row))
11738 {
11739 start_row = prev_row;
11740 } else {
11741 break 'expand_upwards;
11742 }
11743 }
11744
11745 'expand_downwards: while end_row < buffer.max_point().row {
11746 let next_row = end_row + 1;
11747 if buffer.contains_str_at(Point::new(next_row, 0), &line_prefix)
11748 && buffer.line_len(MultiBufferRow(next_row)) as usize > line_prefix.len()
11749 && !buffer.is_line_blank(MultiBufferRow(next_row))
11750 {
11751 end_row = next_row;
11752 } else {
11753 break 'expand_downwards;
11754 }
11755 }
11756 }
11757
11758 let start = Point::new(start_row, 0);
11759 let start_offset = start.to_offset(&buffer);
11760 let end = Point::new(end_row, buffer.line_len(MultiBufferRow(end_row)));
11761 let selection_text = buffer.text_for_range(start..end).collect::<String>();
11762 let Some(lines_without_prefixes) = selection_text
11763 .lines()
11764 .enumerate()
11765 .map(|(ix, line)| {
11766 let line_trimmed = line.trim_start();
11767 if rewrap_prefix.is_some() && ix > 0 {
11768 Ok(line_trimmed)
11769 } else {
11770 line_trimmed
11771 .strip_prefix(&line_prefix.trim_start())
11772 .with_context(|| {
11773 format!("line did not start with prefix {line_prefix:?}: {line:?}")
11774 })
11775 }
11776 })
11777 .collect::<Result<Vec<_>, _>>()
11778 .log_err()
11779 else {
11780 continue;
11781 };
11782
11783 let wrap_column = self.hard_wrap.unwrap_or_else(|| {
11784 buffer
11785 .language_settings_at(Point::new(start_row, 0), cx)
11786 .preferred_line_length as usize
11787 });
11788
11789 let subsequent_lines_prefix = if let Some(rewrap_prefix_str) = &rewrap_prefix {
11790 format!("{}{}", indent_prefix, " ".repeat(rewrap_prefix_str.len()))
11791 } else {
11792 line_prefix.clone()
11793 };
11794
11795 let wrapped_text = wrap_with_prefix(
11796 line_prefix,
11797 subsequent_lines_prefix,
11798 lines_without_prefixes.join("\n"),
11799 wrap_column,
11800 tab_size,
11801 options.preserve_existing_whitespace,
11802 );
11803
11804 // TODO: should always use char-based diff while still supporting cursor behavior that
11805 // matches vim.
11806 let mut diff_options = DiffOptions::default();
11807 if options.override_language_settings {
11808 diff_options.max_word_diff_len = 0;
11809 diff_options.max_word_diff_line_count = 0;
11810 } else {
11811 diff_options.max_word_diff_len = usize::MAX;
11812 diff_options.max_word_diff_line_count = usize::MAX;
11813 }
11814
11815 for (old_range, new_text) in
11816 text_diff_with_options(&selection_text, &wrapped_text, diff_options)
11817 {
11818 let edit_start = buffer.anchor_after(start_offset + old_range.start);
11819 let edit_end = buffer.anchor_after(start_offset + old_range.end);
11820 edits.push((edit_start..edit_end, new_text));
11821 }
11822
11823 rewrapped_row_ranges.push(start_row..=end_row);
11824 }
11825
11826 self.buffer
11827 .update(cx, |buffer, cx| buffer.edit(edits, None, cx));
11828 }
11829
11830 pub fn cut_common(&mut self, window: &mut Window, cx: &mut Context<Self>) -> ClipboardItem {
11831 let mut text = String::new();
11832 let buffer = self.buffer.read(cx).snapshot(cx);
11833 let mut selections = self.selections.all::<Point>(cx);
11834 let mut clipboard_selections = Vec::with_capacity(selections.len());
11835 {
11836 let max_point = buffer.max_point();
11837 let mut is_first = true;
11838 for selection in &mut selections {
11839 let is_entire_line = selection.is_empty() || self.selections.line_mode;
11840 if is_entire_line {
11841 selection.start = Point::new(selection.start.row, 0);
11842 if !selection.is_empty() && selection.end.column == 0 {
11843 selection.end = cmp::min(max_point, selection.end);
11844 } else {
11845 selection.end = cmp::min(max_point, Point::new(selection.end.row + 1, 0));
11846 }
11847 selection.goal = SelectionGoal::None;
11848 }
11849 if is_first {
11850 is_first = false;
11851 } else {
11852 text += "\n";
11853 }
11854 let mut len = 0;
11855 for chunk in buffer.text_for_range(selection.start..selection.end) {
11856 text.push_str(chunk);
11857 len += chunk.len();
11858 }
11859 clipboard_selections.push(ClipboardSelection {
11860 len,
11861 is_entire_line,
11862 first_line_indent: buffer
11863 .indent_size_for_line(MultiBufferRow(selection.start.row))
11864 .len,
11865 });
11866 }
11867 }
11868
11869 self.transact(window, cx, |this, window, cx| {
11870 this.change_selections(Default::default(), window, cx, |s| {
11871 s.select(selections);
11872 });
11873 this.insert("", window, cx);
11874 });
11875 ClipboardItem::new_string_with_json_metadata(text, clipboard_selections)
11876 }
11877
11878 pub fn cut(&mut self, _: &Cut, window: &mut Window, cx: &mut Context<Self>) {
11879 self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
11880 let item = self.cut_common(window, cx);
11881 cx.write_to_clipboard(item);
11882 }
11883
11884 pub fn kill_ring_cut(&mut self, _: &KillRingCut, window: &mut Window, cx: &mut Context<Self>) {
11885 self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
11886 self.change_selections(SelectionEffects::no_scroll(), window, cx, |s| {
11887 s.move_with(|snapshot, sel| {
11888 if sel.is_empty() {
11889 sel.end = DisplayPoint::new(sel.end.row(), snapshot.line_len(sel.end.row()))
11890 }
11891 });
11892 });
11893 let item = self.cut_common(window, cx);
11894 cx.set_global(KillRing(item))
11895 }
11896
11897 pub fn kill_ring_yank(
11898 &mut self,
11899 _: &KillRingYank,
11900 window: &mut Window,
11901 cx: &mut Context<Self>,
11902 ) {
11903 self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
11904 let (text, metadata) = if let Some(KillRing(item)) = cx.try_global() {
11905 if let Some(ClipboardEntry::String(kill_ring)) = item.entries().first() {
11906 (kill_ring.text().to_string(), kill_ring.metadata_json())
11907 } else {
11908 return;
11909 }
11910 } else {
11911 return;
11912 };
11913 self.do_paste(&text, metadata, false, window, cx);
11914 }
11915
11916 pub fn copy_and_trim(&mut self, _: &CopyAndTrim, _: &mut Window, cx: &mut Context<Self>) {
11917 self.do_copy(true, cx);
11918 }
11919
11920 pub fn copy(&mut self, _: &Copy, _: &mut Window, cx: &mut Context<Self>) {
11921 self.do_copy(false, cx);
11922 }
11923
11924 fn do_copy(&self, strip_leading_indents: bool, cx: &mut Context<Self>) {
11925 let selections = self.selections.all::<Point>(cx);
11926 let buffer = self.buffer.read(cx).read(cx);
11927 let mut text = String::new();
11928
11929 let mut clipboard_selections = Vec::with_capacity(selections.len());
11930 {
11931 let max_point = buffer.max_point();
11932 let mut is_first = true;
11933 for selection in &selections {
11934 let mut start = selection.start;
11935 let mut end = selection.end;
11936 let is_entire_line = selection.is_empty() || self.selections.line_mode;
11937 if is_entire_line {
11938 start = Point::new(start.row, 0);
11939 end = cmp::min(max_point, Point::new(end.row + 1, 0));
11940 }
11941
11942 let mut trimmed_selections = Vec::new();
11943 if strip_leading_indents && end.row.saturating_sub(start.row) > 0 {
11944 let row = MultiBufferRow(start.row);
11945 let first_indent = buffer.indent_size_for_line(row);
11946 if first_indent.len == 0 || start.column > first_indent.len {
11947 trimmed_selections.push(start..end);
11948 } else {
11949 trimmed_selections.push(
11950 Point::new(row.0, first_indent.len)
11951 ..Point::new(row.0, buffer.line_len(row)),
11952 );
11953 for row in start.row + 1..=end.row {
11954 let mut line_len = buffer.line_len(MultiBufferRow(row));
11955 if row == end.row {
11956 line_len = end.column;
11957 }
11958 if line_len == 0 {
11959 trimmed_selections
11960 .push(Point::new(row, 0)..Point::new(row, line_len));
11961 continue;
11962 }
11963 let row_indent_size = buffer.indent_size_for_line(MultiBufferRow(row));
11964 if row_indent_size.len >= first_indent.len {
11965 trimmed_selections.push(
11966 Point::new(row, first_indent.len)..Point::new(row, line_len),
11967 );
11968 } else {
11969 trimmed_selections.clear();
11970 trimmed_selections.push(start..end);
11971 break;
11972 }
11973 }
11974 }
11975 } else {
11976 trimmed_selections.push(start..end);
11977 }
11978
11979 for trimmed_range in trimmed_selections {
11980 if is_first {
11981 is_first = false;
11982 } else {
11983 text += "\n";
11984 }
11985 let mut len = 0;
11986 for chunk in buffer.text_for_range(trimmed_range.start..trimmed_range.end) {
11987 text.push_str(chunk);
11988 len += chunk.len();
11989 }
11990 clipboard_selections.push(ClipboardSelection {
11991 len,
11992 is_entire_line,
11993 first_line_indent: buffer
11994 .indent_size_for_line(MultiBufferRow(trimmed_range.start.row))
11995 .len,
11996 });
11997 }
11998 }
11999 }
12000
12001 cx.write_to_clipboard(ClipboardItem::new_string_with_json_metadata(
12002 text,
12003 clipboard_selections,
12004 ));
12005 }
12006
12007 pub fn do_paste(
12008 &mut self,
12009 text: &String,
12010 clipboard_selections: Option<Vec<ClipboardSelection>>,
12011 handle_entire_lines: bool,
12012 window: &mut Window,
12013 cx: &mut Context<Self>,
12014 ) {
12015 if self.read_only(cx) {
12016 return;
12017 }
12018
12019 let clipboard_text = Cow::Borrowed(text);
12020
12021 self.transact(window, cx, |this, window, cx| {
12022 if let Some(mut clipboard_selections) = clipboard_selections {
12023 let old_selections = this.selections.all::<usize>(cx);
12024 let all_selections_were_entire_line =
12025 clipboard_selections.iter().all(|s| s.is_entire_line);
12026 let first_selection_indent_column =
12027 clipboard_selections.first().map(|s| s.first_line_indent);
12028 if clipboard_selections.len() != old_selections.len() {
12029 clipboard_selections.drain(..);
12030 }
12031 let cursor_offset = this.selections.last::<usize>(cx).head();
12032 let mut auto_indent_on_paste = true;
12033
12034 this.buffer.update(cx, |buffer, cx| {
12035 let snapshot = buffer.read(cx);
12036 auto_indent_on_paste = snapshot
12037 .language_settings_at(cursor_offset, cx)
12038 .auto_indent_on_paste;
12039
12040 let mut start_offset = 0;
12041 let mut edits = Vec::new();
12042 let mut original_indent_columns = Vec::new();
12043 for (ix, selection) in old_selections.iter().enumerate() {
12044 let to_insert;
12045 let entire_line;
12046 let original_indent_column;
12047 if let Some(clipboard_selection) = clipboard_selections.get(ix) {
12048 let end_offset = start_offset + clipboard_selection.len;
12049 to_insert = &clipboard_text[start_offset..end_offset];
12050 entire_line = clipboard_selection.is_entire_line;
12051 start_offset = end_offset + 1;
12052 original_indent_column = Some(clipboard_selection.first_line_indent);
12053 } else {
12054 to_insert = clipboard_text.as_str();
12055 entire_line = all_selections_were_entire_line;
12056 original_indent_column = first_selection_indent_column
12057 }
12058
12059 // If the corresponding selection was empty when this slice of the
12060 // clipboard text was written, then the entire line containing the
12061 // selection was copied. If this selection is also currently empty,
12062 // then paste the line before the current line of the buffer.
12063 let range = if selection.is_empty() && handle_entire_lines && entire_line {
12064 let column = selection.start.to_point(&snapshot).column as usize;
12065 let line_start = selection.start - column;
12066 line_start..line_start
12067 } else {
12068 selection.range()
12069 };
12070
12071 edits.push((range, to_insert));
12072 original_indent_columns.push(original_indent_column);
12073 }
12074 drop(snapshot);
12075
12076 buffer.edit(
12077 edits,
12078 if auto_indent_on_paste {
12079 Some(AutoindentMode::Block {
12080 original_indent_columns,
12081 })
12082 } else {
12083 None
12084 },
12085 cx,
12086 );
12087 });
12088
12089 let selections = this.selections.all::<usize>(cx);
12090 this.change_selections(Default::default(), window, cx, |s| s.select(selections));
12091 } else {
12092 this.insert(&clipboard_text, window, cx);
12093 }
12094 });
12095 }
12096
12097 pub fn paste(&mut self, _: &Paste, window: &mut Window, cx: &mut Context<Self>) {
12098 self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
12099 if let Some(item) = cx.read_from_clipboard() {
12100 let entries = item.entries();
12101
12102 match entries.first() {
12103 // For now, we only support applying metadata if there's one string. In the future, we can incorporate all the selections
12104 // of all the pasted entries.
12105 Some(ClipboardEntry::String(clipboard_string)) if entries.len() == 1 => self
12106 .do_paste(
12107 clipboard_string.text(),
12108 clipboard_string.metadata_json::<Vec<ClipboardSelection>>(),
12109 true,
12110 window,
12111 cx,
12112 ),
12113 _ => self.do_paste(&item.text().unwrap_or_default(), None, true, window, cx),
12114 }
12115 }
12116 }
12117
12118 pub fn undo(&mut self, _: &Undo, window: &mut Window, cx: &mut Context<Self>) {
12119 if self.read_only(cx) {
12120 return;
12121 }
12122
12123 self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
12124
12125 if let Some(transaction_id) = self.buffer.update(cx, |buffer, cx| buffer.undo(cx)) {
12126 if let Some((selections, _)) =
12127 self.selection_history.transaction(transaction_id).cloned()
12128 {
12129 self.change_selections(SelectionEffects::no_scroll(), window, cx, |s| {
12130 s.select_anchors(selections.to_vec());
12131 });
12132 } else {
12133 log::error!(
12134 "No entry in selection_history found for undo. \
12135 This may correspond to a bug where undo does not update the selection. \
12136 If this is occurring, please add details to \
12137 https://github.com/zed-industries/zed/issues/22692"
12138 );
12139 }
12140 self.request_autoscroll(Autoscroll::fit(), cx);
12141 self.unmark_text(window, cx);
12142 self.refresh_inline_completion(true, false, window, cx);
12143 cx.emit(EditorEvent::Edited { transaction_id });
12144 cx.emit(EditorEvent::TransactionUndone { transaction_id });
12145 }
12146 }
12147
12148 pub fn redo(&mut self, _: &Redo, window: &mut Window, cx: &mut Context<Self>) {
12149 if self.read_only(cx) {
12150 return;
12151 }
12152
12153 self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
12154
12155 if let Some(transaction_id) = self.buffer.update(cx, |buffer, cx| buffer.redo(cx)) {
12156 if let Some((_, Some(selections))) =
12157 self.selection_history.transaction(transaction_id).cloned()
12158 {
12159 self.change_selections(SelectionEffects::no_scroll(), window, cx, |s| {
12160 s.select_anchors(selections.to_vec());
12161 });
12162 } else {
12163 log::error!(
12164 "No entry in selection_history found for redo. \
12165 This may correspond to a bug where undo does not update the selection. \
12166 If this is occurring, please add details to \
12167 https://github.com/zed-industries/zed/issues/22692"
12168 );
12169 }
12170 self.request_autoscroll(Autoscroll::fit(), cx);
12171 self.unmark_text(window, cx);
12172 self.refresh_inline_completion(true, false, window, cx);
12173 cx.emit(EditorEvent::Edited { transaction_id });
12174 }
12175 }
12176
12177 pub fn finalize_last_transaction(&mut self, cx: &mut Context<Self>) {
12178 self.buffer
12179 .update(cx, |buffer, cx| buffer.finalize_last_transaction(cx));
12180 }
12181
12182 pub fn group_until_transaction(&mut self, tx_id: TransactionId, cx: &mut Context<Self>) {
12183 self.buffer
12184 .update(cx, |buffer, cx| buffer.group_until_transaction(tx_id, cx));
12185 }
12186
12187 pub fn move_left(&mut self, _: &MoveLeft, window: &mut Window, cx: &mut Context<Self>) {
12188 self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
12189 self.change_selections(Default::default(), window, cx, |s| {
12190 s.move_with(|map, selection| {
12191 let cursor = if selection.is_empty() {
12192 movement::left(map, selection.start)
12193 } else {
12194 selection.start
12195 };
12196 selection.collapse_to(cursor, SelectionGoal::None);
12197 });
12198 })
12199 }
12200
12201 pub fn select_left(&mut self, _: &SelectLeft, window: &mut Window, cx: &mut Context<Self>) {
12202 self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
12203 self.change_selections(Default::default(), window, cx, |s| {
12204 s.move_heads_with(|map, head, _| (movement::left(map, head), SelectionGoal::None));
12205 })
12206 }
12207
12208 pub fn move_right(&mut self, _: &MoveRight, window: &mut Window, cx: &mut Context<Self>) {
12209 self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
12210 self.change_selections(Default::default(), window, cx, |s| {
12211 s.move_with(|map, selection| {
12212 let cursor = if selection.is_empty() {
12213 movement::right(map, selection.end)
12214 } else {
12215 selection.end
12216 };
12217 selection.collapse_to(cursor, SelectionGoal::None)
12218 });
12219 })
12220 }
12221
12222 pub fn select_right(&mut self, _: &SelectRight, window: &mut Window, cx: &mut Context<Self>) {
12223 self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
12224 self.change_selections(Default::default(), window, cx, |s| {
12225 s.move_heads_with(|map, head, _| (movement::right(map, head), SelectionGoal::None));
12226 })
12227 }
12228
12229 pub fn move_up(&mut self, _: &MoveUp, window: &mut Window, cx: &mut Context<Self>) {
12230 if self.take_rename(true, window, cx).is_some() {
12231 return;
12232 }
12233
12234 if self.mode.is_single_line() {
12235 cx.propagate();
12236 return;
12237 }
12238
12239 self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
12240
12241 let text_layout_details = &self.text_layout_details(window);
12242 let selection_count = self.selections.count();
12243 let first_selection = self.selections.first_anchor();
12244
12245 self.change_selections(Default::default(), window, cx, |s| {
12246 s.move_with(|map, selection| {
12247 if !selection.is_empty() {
12248 selection.goal = SelectionGoal::None;
12249 }
12250 let (cursor, goal) = movement::up(
12251 map,
12252 selection.start,
12253 selection.goal,
12254 false,
12255 text_layout_details,
12256 );
12257 selection.collapse_to(cursor, goal);
12258 });
12259 });
12260
12261 if selection_count == 1 && first_selection.range() == self.selections.first_anchor().range()
12262 {
12263 cx.propagate();
12264 }
12265 }
12266
12267 pub fn move_up_by_lines(
12268 &mut self,
12269 action: &MoveUpByLines,
12270 window: &mut Window,
12271 cx: &mut Context<Self>,
12272 ) {
12273 if self.take_rename(true, window, cx).is_some() {
12274 return;
12275 }
12276
12277 if self.mode.is_single_line() {
12278 cx.propagate();
12279 return;
12280 }
12281
12282 self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
12283
12284 let text_layout_details = &self.text_layout_details(window);
12285
12286 self.change_selections(Default::default(), window, cx, |s| {
12287 s.move_with(|map, selection| {
12288 if !selection.is_empty() {
12289 selection.goal = SelectionGoal::None;
12290 }
12291 let (cursor, goal) = movement::up_by_rows(
12292 map,
12293 selection.start,
12294 action.lines,
12295 selection.goal,
12296 false,
12297 text_layout_details,
12298 );
12299 selection.collapse_to(cursor, goal);
12300 });
12301 })
12302 }
12303
12304 pub fn move_down_by_lines(
12305 &mut self,
12306 action: &MoveDownByLines,
12307 window: &mut Window,
12308 cx: &mut Context<Self>,
12309 ) {
12310 if self.take_rename(true, window, cx).is_some() {
12311 return;
12312 }
12313
12314 if self.mode.is_single_line() {
12315 cx.propagate();
12316 return;
12317 }
12318
12319 self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
12320
12321 let text_layout_details = &self.text_layout_details(window);
12322
12323 self.change_selections(Default::default(), window, cx, |s| {
12324 s.move_with(|map, selection| {
12325 if !selection.is_empty() {
12326 selection.goal = SelectionGoal::None;
12327 }
12328 let (cursor, goal) = movement::down_by_rows(
12329 map,
12330 selection.start,
12331 action.lines,
12332 selection.goal,
12333 false,
12334 text_layout_details,
12335 );
12336 selection.collapse_to(cursor, goal);
12337 });
12338 })
12339 }
12340
12341 pub fn select_down_by_lines(
12342 &mut self,
12343 action: &SelectDownByLines,
12344 window: &mut Window,
12345 cx: &mut Context<Self>,
12346 ) {
12347 self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
12348 let text_layout_details = &self.text_layout_details(window);
12349 self.change_selections(Default::default(), window, cx, |s| {
12350 s.move_heads_with(|map, head, goal| {
12351 movement::down_by_rows(map, head, action.lines, goal, false, text_layout_details)
12352 })
12353 })
12354 }
12355
12356 pub fn select_up_by_lines(
12357 &mut self,
12358 action: &SelectUpByLines,
12359 window: &mut Window,
12360 cx: &mut Context<Self>,
12361 ) {
12362 self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
12363 let text_layout_details = &self.text_layout_details(window);
12364 self.change_selections(Default::default(), window, cx, |s| {
12365 s.move_heads_with(|map, head, goal| {
12366 movement::up_by_rows(map, head, action.lines, goal, false, text_layout_details)
12367 })
12368 })
12369 }
12370
12371 pub fn select_page_up(
12372 &mut self,
12373 _: &SelectPageUp,
12374 window: &mut Window,
12375 cx: &mut Context<Self>,
12376 ) {
12377 let Some(row_count) = self.visible_row_count() else {
12378 return;
12379 };
12380
12381 self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
12382
12383 let text_layout_details = &self.text_layout_details(window);
12384
12385 self.change_selections(Default::default(), window, cx, |s| {
12386 s.move_heads_with(|map, head, goal| {
12387 movement::up_by_rows(map, head, row_count, goal, false, text_layout_details)
12388 })
12389 })
12390 }
12391
12392 pub fn move_page_up(
12393 &mut self,
12394 action: &MovePageUp,
12395 window: &mut Window,
12396 cx: &mut Context<Self>,
12397 ) {
12398 if self.take_rename(true, window, cx).is_some() {
12399 return;
12400 }
12401
12402 if self
12403 .context_menu
12404 .borrow_mut()
12405 .as_mut()
12406 .map(|menu| menu.select_first(self.completion_provider.as_deref(), window, cx))
12407 .unwrap_or(false)
12408 {
12409 return;
12410 }
12411
12412 if matches!(self.mode, EditorMode::SingleLine { .. }) {
12413 cx.propagate();
12414 return;
12415 }
12416
12417 let Some(row_count) = self.visible_row_count() else {
12418 return;
12419 };
12420
12421 self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
12422
12423 let effects = if action.center_cursor {
12424 SelectionEffects::scroll(Autoscroll::center())
12425 } else {
12426 SelectionEffects::default()
12427 };
12428
12429 let text_layout_details = &self.text_layout_details(window);
12430
12431 self.change_selections(effects, window, cx, |s| {
12432 s.move_with(|map, selection| {
12433 if !selection.is_empty() {
12434 selection.goal = SelectionGoal::None;
12435 }
12436 let (cursor, goal) = movement::up_by_rows(
12437 map,
12438 selection.end,
12439 row_count,
12440 selection.goal,
12441 false,
12442 text_layout_details,
12443 );
12444 selection.collapse_to(cursor, goal);
12445 });
12446 });
12447 }
12448
12449 pub fn select_up(&mut self, _: &SelectUp, window: &mut Window, cx: &mut Context<Self>) {
12450 self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
12451 let text_layout_details = &self.text_layout_details(window);
12452 self.change_selections(Default::default(), window, cx, |s| {
12453 s.move_heads_with(|map, head, goal| {
12454 movement::up(map, head, goal, false, text_layout_details)
12455 })
12456 })
12457 }
12458
12459 pub fn move_down(&mut self, _: &MoveDown, window: &mut Window, cx: &mut Context<Self>) {
12460 self.take_rename(true, window, cx);
12461
12462 if self.mode.is_single_line() {
12463 cx.propagate();
12464 return;
12465 }
12466
12467 self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
12468
12469 let text_layout_details = &self.text_layout_details(window);
12470 let selection_count = self.selections.count();
12471 let first_selection = self.selections.first_anchor();
12472
12473 self.change_selections(Default::default(), window, cx, |s| {
12474 s.move_with(|map, selection| {
12475 if !selection.is_empty() {
12476 selection.goal = SelectionGoal::None;
12477 }
12478 let (cursor, goal) = movement::down(
12479 map,
12480 selection.end,
12481 selection.goal,
12482 false,
12483 text_layout_details,
12484 );
12485 selection.collapse_to(cursor, goal);
12486 });
12487 });
12488
12489 if selection_count == 1 && first_selection.range() == self.selections.first_anchor().range()
12490 {
12491 cx.propagate();
12492 }
12493 }
12494
12495 pub fn select_page_down(
12496 &mut self,
12497 _: &SelectPageDown,
12498 window: &mut Window,
12499 cx: &mut Context<Self>,
12500 ) {
12501 let Some(row_count) = self.visible_row_count() else {
12502 return;
12503 };
12504
12505 self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
12506
12507 let text_layout_details = &self.text_layout_details(window);
12508
12509 self.change_selections(Default::default(), window, cx, |s| {
12510 s.move_heads_with(|map, head, goal| {
12511 movement::down_by_rows(map, head, row_count, goal, false, text_layout_details)
12512 })
12513 })
12514 }
12515
12516 pub fn move_page_down(
12517 &mut self,
12518 action: &MovePageDown,
12519 window: &mut Window,
12520 cx: &mut Context<Self>,
12521 ) {
12522 if self.take_rename(true, window, cx).is_some() {
12523 return;
12524 }
12525
12526 if self
12527 .context_menu
12528 .borrow_mut()
12529 .as_mut()
12530 .map(|menu| menu.select_last(self.completion_provider.as_deref(), window, cx))
12531 .unwrap_or(false)
12532 {
12533 return;
12534 }
12535
12536 if matches!(self.mode, EditorMode::SingleLine { .. }) {
12537 cx.propagate();
12538 return;
12539 }
12540
12541 let Some(row_count) = self.visible_row_count() else {
12542 return;
12543 };
12544
12545 self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
12546
12547 let effects = if action.center_cursor {
12548 SelectionEffects::scroll(Autoscroll::center())
12549 } else {
12550 SelectionEffects::default()
12551 };
12552
12553 let text_layout_details = &self.text_layout_details(window);
12554 self.change_selections(effects, window, cx, |s| {
12555 s.move_with(|map, selection| {
12556 if !selection.is_empty() {
12557 selection.goal = SelectionGoal::None;
12558 }
12559 let (cursor, goal) = movement::down_by_rows(
12560 map,
12561 selection.end,
12562 row_count,
12563 selection.goal,
12564 false,
12565 text_layout_details,
12566 );
12567 selection.collapse_to(cursor, goal);
12568 });
12569 });
12570 }
12571
12572 pub fn select_down(&mut self, _: &SelectDown, window: &mut Window, cx: &mut Context<Self>) {
12573 self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
12574 let text_layout_details = &self.text_layout_details(window);
12575 self.change_selections(Default::default(), window, cx, |s| {
12576 s.move_heads_with(|map, head, goal| {
12577 movement::down(map, head, goal, false, text_layout_details)
12578 })
12579 });
12580 }
12581
12582 pub fn context_menu_first(
12583 &mut self,
12584 _: &ContextMenuFirst,
12585 window: &mut Window,
12586 cx: &mut Context<Self>,
12587 ) {
12588 if let Some(context_menu) = self.context_menu.borrow_mut().as_mut() {
12589 context_menu.select_first(self.completion_provider.as_deref(), window, cx);
12590 }
12591 }
12592
12593 pub fn context_menu_prev(
12594 &mut self,
12595 _: &ContextMenuPrevious,
12596 window: &mut Window,
12597 cx: &mut Context<Self>,
12598 ) {
12599 if let Some(context_menu) = self.context_menu.borrow_mut().as_mut() {
12600 context_menu.select_prev(self.completion_provider.as_deref(), window, cx);
12601 }
12602 }
12603
12604 pub fn context_menu_next(
12605 &mut self,
12606 _: &ContextMenuNext,
12607 window: &mut Window,
12608 cx: &mut Context<Self>,
12609 ) {
12610 if let Some(context_menu) = self.context_menu.borrow_mut().as_mut() {
12611 context_menu.select_next(self.completion_provider.as_deref(), window, cx);
12612 }
12613 }
12614
12615 pub fn context_menu_last(
12616 &mut self,
12617 _: &ContextMenuLast,
12618 window: &mut Window,
12619 cx: &mut Context<Self>,
12620 ) {
12621 if let Some(context_menu) = self.context_menu.borrow_mut().as_mut() {
12622 context_menu.select_last(self.completion_provider.as_deref(), window, cx);
12623 }
12624 }
12625
12626 pub fn signature_help_prev(
12627 &mut self,
12628 _: &SignatureHelpPrevious,
12629 _: &mut Window,
12630 cx: &mut Context<Self>,
12631 ) {
12632 if let Some(popover) = self.signature_help_state.popover_mut() {
12633 if popover.current_signature == 0 {
12634 popover.current_signature = popover.signatures.len() - 1;
12635 } else {
12636 popover.current_signature -= 1;
12637 }
12638 cx.notify();
12639 }
12640 }
12641
12642 pub fn signature_help_next(
12643 &mut self,
12644 _: &SignatureHelpNext,
12645 _: &mut Window,
12646 cx: &mut Context<Self>,
12647 ) {
12648 if let Some(popover) = self.signature_help_state.popover_mut() {
12649 if popover.current_signature + 1 == popover.signatures.len() {
12650 popover.current_signature = 0;
12651 } else {
12652 popover.current_signature += 1;
12653 }
12654 cx.notify();
12655 }
12656 }
12657
12658 pub fn move_to_previous_word_start(
12659 &mut self,
12660 _: &MoveToPreviousWordStart,
12661 window: &mut Window,
12662 cx: &mut Context<Self>,
12663 ) {
12664 self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
12665 self.change_selections(Default::default(), window, cx, |s| {
12666 s.move_cursors_with(|map, head, _| {
12667 (
12668 movement::previous_word_start(map, head),
12669 SelectionGoal::None,
12670 )
12671 });
12672 })
12673 }
12674
12675 pub fn move_to_previous_subword_start(
12676 &mut self,
12677 _: &MoveToPreviousSubwordStart,
12678 window: &mut Window,
12679 cx: &mut Context<Self>,
12680 ) {
12681 self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
12682 self.change_selections(Default::default(), window, cx, |s| {
12683 s.move_cursors_with(|map, head, _| {
12684 (
12685 movement::previous_subword_start(map, head),
12686 SelectionGoal::None,
12687 )
12688 });
12689 })
12690 }
12691
12692 pub fn select_to_previous_word_start(
12693 &mut self,
12694 _: &SelectToPreviousWordStart,
12695 window: &mut Window,
12696 cx: &mut Context<Self>,
12697 ) {
12698 self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
12699 self.change_selections(Default::default(), window, cx, |s| {
12700 s.move_heads_with(|map, head, _| {
12701 (
12702 movement::previous_word_start(map, head),
12703 SelectionGoal::None,
12704 )
12705 });
12706 })
12707 }
12708
12709 pub fn select_to_previous_subword_start(
12710 &mut self,
12711 _: &SelectToPreviousSubwordStart,
12712 window: &mut Window,
12713 cx: &mut Context<Self>,
12714 ) {
12715 self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
12716 self.change_selections(Default::default(), window, cx, |s| {
12717 s.move_heads_with(|map, head, _| {
12718 (
12719 movement::previous_subword_start(map, head),
12720 SelectionGoal::None,
12721 )
12722 });
12723 })
12724 }
12725
12726 pub fn delete_to_previous_word_start(
12727 &mut self,
12728 action: &DeleteToPreviousWordStart,
12729 window: &mut Window,
12730 cx: &mut Context<Self>,
12731 ) {
12732 self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
12733 self.transact(window, cx, |this, window, cx| {
12734 this.select_autoclose_pair(window, cx);
12735 this.change_selections(Default::default(), window, cx, |s| {
12736 s.move_with(|map, selection| {
12737 if selection.is_empty() {
12738 let cursor = if action.ignore_newlines {
12739 movement::previous_word_start(map, selection.head())
12740 } else {
12741 movement::previous_word_start_or_newline(map, selection.head())
12742 };
12743 selection.set_head(cursor, SelectionGoal::None);
12744 }
12745 });
12746 });
12747 this.insert("", window, cx);
12748 });
12749 }
12750
12751 pub fn delete_to_previous_subword_start(
12752 &mut self,
12753 _: &DeleteToPreviousSubwordStart,
12754 window: &mut Window,
12755 cx: &mut Context<Self>,
12756 ) {
12757 self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
12758 self.transact(window, cx, |this, window, cx| {
12759 this.select_autoclose_pair(window, cx);
12760 this.change_selections(Default::default(), window, cx, |s| {
12761 s.move_with(|map, selection| {
12762 if selection.is_empty() {
12763 let cursor = movement::previous_subword_start(map, selection.head());
12764 selection.set_head(cursor, SelectionGoal::None);
12765 }
12766 });
12767 });
12768 this.insert("", window, cx);
12769 });
12770 }
12771
12772 pub fn move_to_next_word_end(
12773 &mut self,
12774 _: &MoveToNextWordEnd,
12775 window: &mut Window,
12776 cx: &mut Context<Self>,
12777 ) {
12778 self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
12779 self.change_selections(Default::default(), window, cx, |s| {
12780 s.move_cursors_with(|map, head, _| {
12781 (movement::next_word_end(map, head), SelectionGoal::None)
12782 });
12783 })
12784 }
12785
12786 pub fn move_to_next_subword_end(
12787 &mut self,
12788 _: &MoveToNextSubwordEnd,
12789 window: &mut Window,
12790 cx: &mut Context<Self>,
12791 ) {
12792 self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
12793 self.change_selections(Default::default(), window, cx, |s| {
12794 s.move_cursors_with(|map, head, _| {
12795 (movement::next_subword_end(map, head), SelectionGoal::None)
12796 });
12797 })
12798 }
12799
12800 pub fn select_to_next_word_end(
12801 &mut self,
12802 _: &SelectToNextWordEnd,
12803 window: &mut Window,
12804 cx: &mut Context<Self>,
12805 ) {
12806 self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
12807 self.change_selections(Default::default(), window, cx, |s| {
12808 s.move_heads_with(|map, head, _| {
12809 (movement::next_word_end(map, head), SelectionGoal::None)
12810 });
12811 })
12812 }
12813
12814 pub fn select_to_next_subword_end(
12815 &mut self,
12816 _: &SelectToNextSubwordEnd,
12817 window: &mut Window,
12818 cx: &mut Context<Self>,
12819 ) {
12820 self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
12821 self.change_selections(Default::default(), window, cx, |s| {
12822 s.move_heads_with(|map, head, _| {
12823 (movement::next_subword_end(map, head), SelectionGoal::None)
12824 });
12825 })
12826 }
12827
12828 pub fn delete_to_next_word_end(
12829 &mut self,
12830 action: &DeleteToNextWordEnd,
12831 window: &mut Window,
12832 cx: &mut Context<Self>,
12833 ) {
12834 self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
12835 self.transact(window, cx, |this, window, cx| {
12836 this.change_selections(Default::default(), window, cx, |s| {
12837 s.move_with(|map, selection| {
12838 if selection.is_empty() {
12839 let cursor = if action.ignore_newlines {
12840 movement::next_word_end(map, selection.head())
12841 } else {
12842 movement::next_word_end_or_newline(map, selection.head())
12843 };
12844 selection.set_head(cursor, SelectionGoal::None);
12845 }
12846 });
12847 });
12848 this.insert("", window, cx);
12849 });
12850 }
12851
12852 pub fn delete_to_next_subword_end(
12853 &mut self,
12854 _: &DeleteToNextSubwordEnd,
12855 window: &mut Window,
12856 cx: &mut Context<Self>,
12857 ) {
12858 self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
12859 self.transact(window, cx, |this, window, cx| {
12860 this.change_selections(Default::default(), window, cx, |s| {
12861 s.move_with(|map, selection| {
12862 if selection.is_empty() {
12863 let cursor = movement::next_subword_end(map, selection.head());
12864 selection.set_head(cursor, SelectionGoal::None);
12865 }
12866 });
12867 });
12868 this.insert("", window, cx);
12869 });
12870 }
12871
12872 pub fn move_to_beginning_of_line(
12873 &mut self,
12874 action: &MoveToBeginningOfLine,
12875 window: &mut Window,
12876 cx: &mut Context<Self>,
12877 ) {
12878 self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
12879 self.change_selections(Default::default(), window, cx, |s| {
12880 s.move_cursors_with(|map, head, _| {
12881 (
12882 movement::indented_line_beginning(
12883 map,
12884 head,
12885 action.stop_at_soft_wraps,
12886 action.stop_at_indent,
12887 ),
12888 SelectionGoal::None,
12889 )
12890 });
12891 })
12892 }
12893
12894 pub fn select_to_beginning_of_line(
12895 &mut self,
12896 action: &SelectToBeginningOfLine,
12897 window: &mut Window,
12898 cx: &mut Context<Self>,
12899 ) {
12900 self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
12901 self.change_selections(Default::default(), window, cx, |s| {
12902 s.move_heads_with(|map, head, _| {
12903 (
12904 movement::indented_line_beginning(
12905 map,
12906 head,
12907 action.stop_at_soft_wraps,
12908 action.stop_at_indent,
12909 ),
12910 SelectionGoal::None,
12911 )
12912 });
12913 });
12914 }
12915
12916 pub fn delete_to_beginning_of_line(
12917 &mut self,
12918 action: &DeleteToBeginningOfLine,
12919 window: &mut Window,
12920 cx: &mut Context<Self>,
12921 ) {
12922 self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
12923 self.transact(window, cx, |this, window, cx| {
12924 this.change_selections(Default::default(), window, cx, |s| {
12925 s.move_with(|_, selection| {
12926 selection.reversed = true;
12927 });
12928 });
12929
12930 this.select_to_beginning_of_line(
12931 &SelectToBeginningOfLine {
12932 stop_at_soft_wraps: false,
12933 stop_at_indent: action.stop_at_indent,
12934 },
12935 window,
12936 cx,
12937 );
12938 this.backspace(&Backspace, window, cx);
12939 });
12940 }
12941
12942 pub fn move_to_end_of_line(
12943 &mut self,
12944 action: &MoveToEndOfLine,
12945 window: &mut Window,
12946 cx: &mut Context<Self>,
12947 ) {
12948 self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
12949 self.change_selections(Default::default(), window, cx, |s| {
12950 s.move_cursors_with(|map, head, _| {
12951 (
12952 movement::line_end(map, head, action.stop_at_soft_wraps),
12953 SelectionGoal::None,
12954 )
12955 });
12956 })
12957 }
12958
12959 pub fn select_to_end_of_line(
12960 &mut self,
12961 action: &SelectToEndOfLine,
12962 window: &mut Window,
12963 cx: &mut Context<Self>,
12964 ) {
12965 self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
12966 self.change_selections(Default::default(), window, cx, |s| {
12967 s.move_heads_with(|map, head, _| {
12968 (
12969 movement::line_end(map, head, action.stop_at_soft_wraps),
12970 SelectionGoal::None,
12971 )
12972 });
12973 })
12974 }
12975
12976 pub fn delete_to_end_of_line(
12977 &mut self,
12978 _: &DeleteToEndOfLine,
12979 window: &mut Window,
12980 cx: &mut Context<Self>,
12981 ) {
12982 self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
12983 self.transact(window, cx, |this, window, cx| {
12984 this.select_to_end_of_line(
12985 &SelectToEndOfLine {
12986 stop_at_soft_wraps: false,
12987 },
12988 window,
12989 cx,
12990 );
12991 this.delete(&Delete, window, cx);
12992 });
12993 }
12994
12995 pub fn cut_to_end_of_line(
12996 &mut self,
12997 _: &CutToEndOfLine,
12998 window: &mut Window,
12999 cx: &mut Context<Self>,
13000 ) {
13001 self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
13002 self.transact(window, cx, |this, window, cx| {
13003 this.select_to_end_of_line(
13004 &SelectToEndOfLine {
13005 stop_at_soft_wraps: false,
13006 },
13007 window,
13008 cx,
13009 );
13010 this.cut(&Cut, window, cx);
13011 });
13012 }
13013
13014 pub fn move_to_start_of_paragraph(
13015 &mut self,
13016 _: &MoveToStartOfParagraph,
13017 window: &mut Window,
13018 cx: &mut Context<Self>,
13019 ) {
13020 if matches!(self.mode, EditorMode::SingleLine { .. }) {
13021 cx.propagate();
13022 return;
13023 }
13024 self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
13025 self.change_selections(Default::default(), window, cx, |s| {
13026 s.move_with(|map, selection| {
13027 selection.collapse_to(
13028 movement::start_of_paragraph(map, selection.head(), 1),
13029 SelectionGoal::None,
13030 )
13031 });
13032 })
13033 }
13034
13035 pub fn move_to_end_of_paragraph(
13036 &mut self,
13037 _: &MoveToEndOfParagraph,
13038 window: &mut Window,
13039 cx: &mut Context<Self>,
13040 ) {
13041 if matches!(self.mode, EditorMode::SingleLine { .. }) {
13042 cx.propagate();
13043 return;
13044 }
13045 self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
13046 self.change_selections(Default::default(), window, cx, |s| {
13047 s.move_with(|map, selection| {
13048 selection.collapse_to(
13049 movement::end_of_paragraph(map, selection.head(), 1),
13050 SelectionGoal::None,
13051 )
13052 });
13053 })
13054 }
13055
13056 pub fn select_to_start_of_paragraph(
13057 &mut self,
13058 _: &SelectToStartOfParagraph,
13059 window: &mut Window,
13060 cx: &mut Context<Self>,
13061 ) {
13062 if matches!(self.mode, EditorMode::SingleLine { .. }) {
13063 cx.propagate();
13064 return;
13065 }
13066 self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
13067 self.change_selections(Default::default(), window, cx, |s| {
13068 s.move_heads_with(|map, head, _| {
13069 (
13070 movement::start_of_paragraph(map, head, 1),
13071 SelectionGoal::None,
13072 )
13073 });
13074 })
13075 }
13076
13077 pub fn select_to_end_of_paragraph(
13078 &mut self,
13079 _: &SelectToEndOfParagraph,
13080 window: &mut Window,
13081 cx: &mut Context<Self>,
13082 ) {
13083 if matches!(self.mode, EditorMode::SingleLine { .. }) {
13084 cx.propagate();
13085 return;
13086 }
13087 self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
13088 self.change_selections(Default::default(), window, cx, |s| {
13089 s.move_heads_with(|map, head, _| {
13090 (
13091 movement::end_of_paragraph(map, head, 1),
13092 SelectionGoal::None,
13093 )
13094 });
13095 })
13096 }
13097
13098 pub fn move_to_start_of_excerpt(
13099 &mut self,
13100 _: &MoveToStartOfExcerpt,
13101 window: &mut Window,
13102 cx: &mut Context<Self>,
13103 ) {
13104 if matches!(self.mode, EditorMode::SingleLine { .. }) {
13105 cx.propagate();
13106 return;
13107 }
13108 self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
13109 self.change_selections(Default::default(), window, cx, |s| {
13110 s.move_with(|map, selection| {
13111 selection.collapse_to(
13112 movement::start_of_excerpt(
13113 map,
13114 selection.head(),
13115 workspace::searchable::Direction::Prev,
13116 ),
13117 SelectionGoal::None,
13118 )
13119 });
13120 })
13121 }
13122
13123 pub fn move_to_start_of_next_excerpt(
13124 &mut self,
13125 _: &MoveToStartOfNextExcerpt,
13126 window: &mut Window,
13127 cx: &mut Context<Self>,
13128 ) {
13129 if matches!(self.mode, EditorMode::SingleLine { .. }) {
13130 cx.propagate();
13131 return;
13132 }
13133
13134 self.change_selections(Default::default(), window, cx, |s| {
13135 s.move_with(|map, selection| {
13136 selection.collapse_to(
13137 movement::start_of_excerpt(
13138 map,
13139 selection.head(),
13140 workspace::searchable::Direction::Next,
13141 ),
13142 SelectionGoal::None,
13143 )
13144 });
13145 })
13146 }
13147
13148 pub fn move_to_end_of_excerpt(
13149 &mut self,
13150 _: &MoveToEndOfExcerpt,
13151 window: &mut Window,
13152 cx: &mut Context<Self>,
13153 ) {
13154 if matches!(self.mode, EditorMode::SingleLine { .. }) {
13155 cx.propagate();
13156 return;
13157 }
13158 self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
13159 self.change_selections(Default::default(), window, cx, |s| {
13160 s.move_with(|map, selection| {
13161 selection.collapse_to(
13162 movement::end_of_excerpt(
13163 map,
13164 selection.head(),
13165 workspace::searchable::Direction::Next,
13166 ),
13167 SelectionGoal::None,
13168 )
13169 });
13170 })
13171 }
13172
13173 pub fn move_to_end_of_previous_excerpt(
13174 &mut self,
13175 _: &MoveToEndOfPreviousExcerpt,
13176 window: &mut Window,
13177 cx: &mut Context<Self>,
13178 ) {
13179 if matches!(self.mode, EditorMode::SingleLine { .. }) {
13180 cx.propagate();
13181 return;
13182 }
13183 self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
13184 self.change_selections(Default::default(), window, cx, |s| {
13185 s.move_with(|map, selection| {
13186 selection.collapse_to(
13187 movement::end_of_excerpt(
13188 map,
13189 selection.head(),
13190 workspace::searchable::Direction::Prev,
13191 ),
13192 SelectionGoal::None,
13193 )
13194 });
13195 })
13196 }
13197
13198 pub fn select_to_start_of_excerpt(
13199 &mut self,
13200 _: &SelectToStartOfExcerpt,
13201 window: &mut Window,
13202 cx: &mut Context<Self>,
13203 ) {
13204 if matches!(self.mode, EditorMode::SingleLine { .. }) {
13205 cx.propagate();
13206 return;
13207 }
13208 self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
13209 self.change_selections(Default::default(), window, cx, |s| {
13210 s.move_heads_with(|map, head, _| {
13211 (
13212 movement::start_of_excerpt(map, head, workspace::searchable::Direction::Prev),
13213 SelectionGoal::None,
13214 )
13215 });
13216 })
13217 }
13218
13219 pub fn select_to_start_of_next_excerpt(
13220 &mut self,
13221 _: &SelectToStartOfNextExcerpt,
13222 window: &mut Window,
13223 cx: &mut Context<Self>,
13224 ) {
13225 if matches!(self.mode, EditorMode::SingleLine { .. }) {
13226 cx.propagate();
13227 return;
13228 }
13229 self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
13230 self.change_selections(Default::default(), window, cx, |s| {
13231 s.move_heads_with(|map, head, _| {
13232 (
13233 movement::start_of_excerpt(map, head, workspace::searchable::Direction::Next),
13234 SelectionGoal::None,
13235 )
13236 });
13237 })
13238 }
13239
13240 pub fn select_to_end_of_excerpt(
13241 &mut self,
13242 _: &SelectToEndOfExcerpt,
13243 window: &mut Window,
13244 cx: &mut Context<Self>,
13245 ) {
13246 if matches!(self.mode, EditorMode::SingleLine { .. }) {
13247 cx.propagate();
13248 return;
13249 }
13250 self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
13251 self.change_selections(Default::default(), window, cx, |s| {
13252 s.move_heads_with(|map, head, _| {
13253 (
13254 movement::end_of_excerpt(map, head, workspace::searchable::Direction::Next),
13255 SelectionGoal::None,
13256 )
13257 });
13258 })
13259 }
13260
13261 pub fn select_to_end_of_previous_excerpt(
13262 &mut self,
13263 _: &SelectToEndOfPreviousExcerpt,
13264 window: &mut Window,
13265 cx: &mut Context<Self>,
13266 ) {
13267 if matches!(self.mode, EditorMode::SingleLine { .. }) {
13268 cx.propagate();
13269 return;
13270 }
13271 self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
13272 self.change_selections(Default::default(), window, cx, |s| {
13273 s.move_heads_with(|map, head, _| {
13274 (
13275 movement::end_of_excerpt(map, head, workspace::searchable::Direction::Prev),
13276 SelectionGoal::None,
13277 )
13278 });
13279 })
13280 }
13281
13282 pub fn move_to_beginning(
13283 &mut self,
13284 _: &MoveToBeginning,
13285 window: &mut Window,
13286 cx: &mut Context<Self>,
13287 ) {
13288 if matches!(self.mode, EditorMode::SingleLine { .. }) {
13289 cx.propagate();
13290 return;
13291 }
13292 self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
13293 self.change_selections(Default::default(), window, cx, |s| {
13294 s.select_ranges(vec![0..0]);
13295 });
13296 }
13297
13298 pub fn select_to_beginning(
13299 &mut self,
13300 _: &SelectToBeginning,
13301 window: &mut Window,
13302 cx: &mut Context<Self>,
13303 ) {
13304 let mut selection = self.selections.last::<Point>(cx);
13305 selection.set_head(Point::zero(), SelectionGoal::None);
13306 self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
13307 self.change_selections(Default::default(), window, cx, |s| {
13308 s.select(vec![selection]);
13309 });
13310 }
13311
13312 pub fn move_to_end(&mut self, _: &MoveToEnd, window: &mut Window, cx: &mut Context<Self>) {
13313 if matches!(self.mode, EditorMode::SingleLine { .. }) {
13314 cx.propagate();
13315 return;
13316 }
13317 self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
13318 let cursor = self.buffer.read(cx).read(cx).len();
13319 self.change_selections(Default::default(), window, cx, |s| {
13320 s.select_ranges(vec![cursor..cursor])
13321 });
13322 }
13323
13324 pub fn set_nav_history(&mut self, nav_history: Option<ItemNavHistory>) {
13325 self.nav_history = nav_history;
13326 }
13327
13328 pub fn nav_history(&self) -> Option<&ItemNavHistory> {
13329 self.nav_history.as_ref()
13330 }
13331
13332 pub fn create_nav_history_entry(&mut self, cx: &mut Context<Self>) {
13333 self.push_to_nav_history(
13334 self.selections.newest_anchor().head(),
13335 None,
13336 false,
13337 true,
13338 cx,
13339 );
13340 }
13341
13342 fn push_to_nav_history(
13343 &mut self,
13344 cursor_anchor: Anchor,
13345 new_position: Option<Point>,
13346 is_deactivate: bool,
13347 always: bool,
13348 cx: &mut Context<Self>,
13349 ) {
13350 if let Some(nav_history) = self.nav_history.as_mut() {
13351 let buffer = self.buffer.read(cx).read(cx);
13352 let cursor_position = cursor_anchor.to_point(&buffer);
13353 let scroll_state = self.scroll_manager.anchor();
13354 let scroll_top_row = scroll_state.top_row(&buffer);
13355 drop(buffer);
13356
13357 if let Some(new_position) = new_position {
13358 let row_delta = (new_position.row as i64 - cursor_position.row as i64).abs();
13359 if row_delta == 0 || (row_delta < MIN_NAVIGATION_HISTORY_ROW_DELTA && !always) {
13360 return;
13361 }
13362 }
13363
13364 nav_history.push(
13365 Some(NavigationData {
13366 cursor_anchor,
13367 cursor_position,
13368 scroll_anchor: scroll_state,
13369 scroll_top_row,
13370 }),
13371 cx,
13372 );
13373 cx.emit(EditorEvent::PushedToNavHistory {
13374 anchor: cursor_anchor,
13375 is_deactivate,
13376 })
13377 }
13378 }
13379
13380 pub fn select_to_end(&mut self, _: &SelectToEnd, window: &mut Window, cx: &mut Context<Self>) {
13381 self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
13382 let buffer = self.buffer.read(cx).snapshot(cx);
13383 let mut selection = self.selections.first::<usize>(cx);
13384 selection.set_head(buffer.len(), SelectionGoal::None);
13385 self.change_selections(Default::default(), window, cx, |s| {
13386 s.select(vec![selection]);
13387 });
13388 }
13389
13390 pub fn select_all(&mut self, _: &SelectAll, window: &mut Window, cx: &mut Context<Self>) {
13391 self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
13392 let end = self.buffer.read(cx).read(cx).len();
13393 self.change_selections(SelectionEffects::no_scroll(), window, cx, |s| {
13394 s.select_ranges(vec![0..end]);
13395 });
13396 }
13397
13398 pub fn select_line(&mut self, _: &SelectLine, window: &mut Window, cx: &mut Context<Self>) {
13399 self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
13400 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
13401 let mut selections = self.selections.all::<Point>(cx);
13402 let max_point = display_map.buffer_snapshot.max_point();
13403 for selection in &mut selections {
13404 let rows = selection.spanned_rows(true, &display_map);
13405 selection.start = Point::new(rows.start.0, 0);
13406 selection.end = cmp::min(max_point, Point::new(rows.end.0, 0));
13407 selection.reversed = false;
13408 }
13409 self.change_selections(Default::default(), window, cx, |s| {
13410 s.select(selections);
13411 });
13412 }
13413
13414 pub fn split_selection_into_lines(
13415 &mut self,
13416 _: &SplitSelectionIntoLines,
13417 window: &mut Window,
13418 cx: &mut Context<Self>,
13419 ) {
13420 let selections = self
13421 .selections
13422 .all::<Point>(cx)
13423 .into_iter()
13424 .map(|selection| selection.start..selection.end)
13425 .collect::<Vec<_>>();
13426 self.unfold_ranges(&selections, true, true, cx);
13427
13428 let mut new_selection_ranges = Vec::new();
13429 {
13430 let buffer = self.buffer.read(cx).read(cx);
13431 for selection in selections {
13432 for row in selection.start.row..selection.end.row {
13433 let cursor = Point::new(row, buffer.line_len(MultiBufferRow(row)));
13434 new_selection_ranges.push(cursor..cursor);
13435 }
13436
13437 let is_multiline_selection = selection.start.row != selection.end.row;
13438 // Don't insert last one if it's a multi-line selection ending at the start of a line,
13439 // so this action feels more ergonomic when paired with other selection operations
13440 let should_skip_last = is_multiline_selection && selection.end.column == 0;
13441 if !should_skip_last {
13442 new_selection_ranges.push(selection.end..selection.end);
13443 }
13444 }
13445 }
13446 self.change_selections(Default::default(), window, cx, |s| {
13447 s.select_ranges(new_selection_ranges);
13448 });
13449 }
13450
13451 pub fn add_selection_above(
13452 &mut self,
13453 _: &AddSelectionAbove,
13454 window: &mut Window,
13455 cx: &mut Context<Self>,
13456 ) {
13457 self.add_selection(true, window, cx);
13458 }
13459
13460 pub fn add_selection_below(
13461 &mut self,
13462 _: &AddSelectionBelow,
13463 window: &mut Window,
13464 cx: &mut Context<Self>,
13465 ) {
13466 self.add_selection(false, window, cx);
13467 }
13468
13469 fn add_selection(&mut self, above: bool, window: &mut Window, cx: &mut Context<Self>) {
13470 self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
13471
13472 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
13473 let all_selections = self.selections.all::<Point>(cx);
13474 let text_layout_details = self.text_layout_details(window);
13475
13476 let (mut columnar_selections, new_selections_to_columnarize) = {
13477 if let Some(state) = self.add_selections_state.as_ref() {
13478 let columnar_selection_ids: HashSet<_> = state
13479 .groups
13480 .iter()
13481 .flat_map(|group| group.stack.iter())
13482 .copied()
13483 .collect();
13484
13485 all_selections
13486 .into_iter()
13487 .partition(|s| columnar_selection_ids.contains(&s.id))
13488 } else {
13489 (Vec::new(), all_selections)
13490 }
13491 };
13492
13493 let mut state = self
13494 .add_selections_state
13495 .take()
13496 .unwrap_or_else(|| AddSelectionsState { groups: Vec::new() });
13497
13498 for selection in new_selections_to_columnarize {
13499 let range = selection.display_range(&display_map).sorted();
13500 let start_x = display_map.x_for_display_point(range.start, &text_layout_details);
13501 let end_x = display_map.x_for_display_point(range.end, &text_layout_details);
13502 let positions = start_x.min(end_x)..start_x.max(end_x);
13503 let mut stack = Vec::new();
13504 for row in range.start.row().0..=range.end.row().0 {
13505 if let Some(selection) = self.selections.build_columnar_selection(
13506 &display_map,
13507 DisplayRow(row),
13508 &positions,
13509 selection.reversed,
13510 &text_layout_details,
13511 ) {
13512 stack.push(selection.id);
13513 columnar_selections.push(selection);
13514 }
13515 }
13516 if !stack.is_empty() {
13517 if above {
13518 stack.reverse();
13519 }
13520 state.groups.push(AddSelectionsGroup { above, stack });
13521 }
13522 }
13523
13524 let mut final_selections = Vec::new();
13525 let end_row = if above {
13526 DisplayRow(0)
13527 } else {
13528 display_map.max_point().row()
13529 };
13530
13531 let mut last_added_item_per_group = HashMap::default();
13532 for group in state.groups.iter_mut() {
13533 if let Some(last_id) = group.stack.last() {
13534 last_added_item_per_group.insert(*last_id, group);
13535 }
13536 }
13537
13538 for selection in columnar_selections {
13539 if let Some(group) = last_added_item_per_group.get_mut(&selection.id) {
13540 if above == group.above {
13541 let range = selection.display_range(&display_map).sorted();
13542 debug_assert_eq!(range.start.row(), range.end.row());
13543 let mut row = range.start.row();
13544 let positions =
13545 if let SelectionGoal::HorizontalRange { start, end } = selection.goal {
13546 px(start)..px(end)
13547 } else {
13548 let start_x =
13549 display_map.x_for_display_point(range.start, &text_layout_details);
13550 let end_x =
13551 display_map.x_for_display_point(range.end, &text_layout_details);
13552 start_x.min(end_x)..start_x.max(end_x)
13553 };
13554
13555 let mut maybe_new_selection = None;
13556 while row != end_row {
13557 if above {
13558 row.0 -= 1;
13559 } else {
13560 row.0 += 1;
13561 }
13562 if let Some(new_selection) = self.selections.build_columnar_selection(
13563 &display_map,
13564 row,
13565 &positions,
13566 selection.reversed,
13567 &text_layout_details,
13568 ) {
13569 maybe_new_selection = Some(new_selection);
13570 break;
13571 }
13572 }
13573
13574 if let Some(new_selection) = maybe_new_selection {
13575 group.stack.push(new_selection.id);
13576 if above {
13577 final_selections.push(new_selection);
13578 final_selections.push(selection);
13579 } else {
13580 final_selections.push(selection);
13581 final_selections.push(new_selection);
13582 }
13583 } else {
13584 final_selections.push(selection);
13585 }
13586 } else {
13587 group.stack.pop();
13588 }
13589 } else {
13590 final_selections.push(selection);
13591 }
13592 }
13593
13594 self.change_selections(Default::default(), window, cx, |s| {
13595 s.select(final_selections);
13596 });
13597
13598 let final_selection_ids: HashSet<_> = self
13599 .selections
13600 .all::<Point>(cx)
13601 .iter()
13602 .map(|s| s.id)
13603 .collect();
13604 state.groups.retain_mut(|group| {
13605 // selections might get merged above so we remove invalid items from stacks
13606 group.stack.retain(|id| final_selection_ids.contains(id));
13607
13608 // single selection in stack can be treated as initial state
13609 group.stack.len() > 1
13610 });
13611
13612 if !state.groups.is_empty() {
13613 self.add_selections_state = Some(state);
13614 }
13615 }
13616
13617 fn select_match_ranges(
13618 &mut self,
13619 range: Range<usize>,
13620 reversed: bool,
13621 replace_newest: bool,
13622 auto_scroll: Option<Autoscroll>,
13623 window: &mut Window,
13624 cx: &mut Context<Editor>,
13625 ) {
13626 self.unfold_ranges(
13627 std::slice::from_ref(&range),
13628 false,
13629 auto_scroll.is_some(),
13630 cx,
13631 );
13632 let effects = if let Some(scroll) = auto_scroll {
13633 SelectionEffects::scroll(scroll)
13634 } else {
13635 SelectionEffects::no_scroll()
13636 };
13637 self.change_selections(effects, window, cx, |s| {
13638 if replace_newest {
13639 s.delete(s.newest_anchor().id);
13640 }
13641 if reversed {
13642 s.insert_range(range.end..range.start);
13643 } else {
13644 s.insert_range(range);
13645 }
13646 });
13647 }
13648
13649 pub fn select_next_match_internal(
13650 &mut self,
13651 display_map: &DisplaySnapshot,
13652 replace_newest: bool,
13653 autoscroll: Option<Autoscroll>,
13654 window: &mut Window,
13655 cx: &mut Context<Self>,
13656 ) -> Result<()> {
13657 let buffer = &display_map.buffer_snapshot;
13658 let mut selections = self.selections.all::<usize>(cx);
13659 if let Some(mut select_next_state) = self.select_next_state.take() {
13660 let query = &select_next_state.query;
13661 if !select_next_state.done {
13662 let first_selection = selections.iter().min_by_key(|s| s.id).unwrap();
13663 let last_selection = selections.iter().max_by_key(|s| s.id).unwrap();
13664 let mut next_selected_range = None;
13665
13666 let bytes_after_last_selection =
13667 buffer.bytes_in_range(last_selection.end..buffer.len());
13668 let bytes_before_first_selection = buffer.bytes_in_range(0..first_selection.start);
13669 let query_matches = query
13670 .stream_find_iter(bytes_after_last_selection)
13671 .map(|result| (last_selection.end, result))
13672 .chain(
13673 query
13674 .stream_find_iter(bytes_before_first_selection)
13675 .map(|result| (0, result)),
13676 );
13677
13678 for (start_offset, query_match) in query_matches {
13679 let query_match = query_match.unwrap(); // can only fail due to I/O
13680 let offset_range =
13681 start_offset + query_match.start()..start_offset + query_match.end();
13682
13683 if !select_next_state.wordwise
13684 || (!buffer.is_inside_word(offset_range.start, false)
13685 && !buffer.is_inside_word(offset_range.end, false))
13686 {
13687 // TODO: This is n^2, because we might check all the selections
13688 if !selections
13689 .iter()
13690 .any(|selection| selection.range().overlaps(&offset_range))
13691 {
13692 next_selected_range = Some(offset_range);
13693 break;
13694 }
13695 }
13696 }
13697
13698 if let Some(next_selected_range) = next_selected_range {
13699 self.select_match_ranges(
13700 next_selected_range,
13701 last_selection.reversed,
13702 replace_newest,
13703 autoscroll,
13704 window,
13705 cx,
13706 );
13707 } else {
13708 select_next_state.done = true;
13709 }
13710 }
13711
13712 self.select_next_state = Some(select_next_state);
13713 } else {
13714 let mut only_carets = true;
13715 let mut same_text_selected = true;
13716 let mut selected_text = None;
13717
13718 let mut selections_iter = selections.iter().peekable();
13719 while let Some(selection) = selections_iter.next() {
13720 if selection.start != selection.end {
13721 only_carets = false;
13722 }
13723
13724 if same_text_selected {
13725 if selected_text.is_none() {
13726 selected_text =
13727 Some(buffer.text_for_range(selection.range()).collect::<String>());
13728 }
13729
13730 if let Some(next_selection) = selections_iter.peek() {
13731 if next_selection.range().len() == selection.range().len() {
13732 let next_selected_text = buffer
13733 .text_for_range(next_selection.range())
13734 .collect::<String>();
13735 if Some(next_selected_text) != selected_text {
13736 same_text_selected = false;
13737 selected_text = None;
13738 }
13739 } else {
13740 same_text_selected = false;
13741 selected_text = None;
13742 }
13743 }
13744 }
13745 }
13746
13747 if only_carets {
13748 for selection in &mut selections {
13749 let (word_range, _) = buffer.surrounding_word(selection.start, false);
13750 selection.start = word_range.start;
13751 selection.end = word_range.end;
13752 selection.goal = SelectionGoal::None;
13753 selection.reversed = false;
13754 self.select_match_ranges(
13755 selection.start..selection.end,
13756 selection.reversed,
13757 replace_newest,
13758 autoscroll,
13759 window,
13760 cx,
13761 );
13762 }
13763
13764 if selections.len() == 1 {
13765 let selection = selections
13766 .last()
13767 .expect("ensured that there's only one selection");
13768 let query = buffer
13769 .text_for_range(selection.start..selection.end)
13770 .collect::<String>();
13771 let is_empty = query.is_empty();
13772 let select_state = SelectNextState {
13773 query: AhoCorasick::new(&[query])?,
13774 wordwise: true,
13775 done: is_empty,
13776 };
13777 self.select_next_state = Some(select_state);
13778 } else {
13779 self.select_next_state = None;
13780 }
13781 } else if let Some(selected_text) = selected_text {
13782 self.select_next_state = Some(SelectNextState {
13783 query: AhoCorasick::new(&[selected_text])?,
13784 wordwise: false,
13785 done: false,
13786 });
13787 self.select_next_match_internal(
13788 display_map,
13789 replace_newest,
13790 autoscroll,
13791 window,
13792 cx,
13793 )?;
13794 }
13795 }
13796 Ok(())
13797 }
13798
13799 pub fn select_all_matches(
13800 &mut self,
13801 _action: &SelectAllMatches,
13802 window: &mut Window,
13803 cx: &mut Context<Self>,
13804 ) -> Result<()> {
13805 self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
13806
13807 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
13808
13809 self.select_next_match_internal(&display_map, false, None, window, cx)?;
13810 let Some(select_next_state) = self.select_next_state.as_mut() else {
13811 return Ok(());
13812 };
13813 if select_next_state.done {
13814 return Ok(());
13815 }
13816
13817 let mut new_selections = Vec::new();
13818
13819 let reversed = self.selections.oldest::<usize>(cx).reversed;
13820 let buffer = &display_map.buffer_snapshot;
13821 let query_matches = select_next_state
13822 .query
13823 .stream_find_iter(buffer.bytes_in_range(0..buffer.len()));
13824
13825 for query_match in query_matches.into_iter() {
13826 let query_match = query_match.context("query match for select all action")?; // can only fail due to I/O
13827 let offset_range = if reversed {
13828 query_match.end()..query_match.start()
13829 } else {
13830 query_match.start()..query_match.end()
13831 };
13832
13833 if !select_next_state.wordwise
13834 || (!buffer.is_inside_word(offset_range.start, false)
13835 && !buffer.is_inside_word(offset_range.end, false))
13836 {
13837 new_selections.push(offset_range.start..offset_range.end);
13838 }
13839 }
13840
13841 select_next_state.done = true;
13842
13843 if new_selections.is_empty() {
13844 log::error!("bug: new_selections is empty in select_all_matches");
13845 return Ok(());
13846 }
13847
13848 self.unfold_ranges(&new_selections.clone(), false, false, cx);
13849 self.change_selections(SelectionEffects::no_scroll(), window, cx, |selections| {
13850 selections.select_ranges(new_selections)
13851 });
13852
13853 Ok(())
13854 }
13855
13856 pub fn select_next(
13857 &mut self,
13858 action: &SelectNext,
13859 window: &mut Window,
13860 cx: &mut Context<Self>,
13861 ) -> Result<()> {
13862 self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
13863 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
13864 self.select_next_match_internal(
13865 &display_map,
13866 action.replace_newest,
13867 Some(Autoscroll::newest()),
13868 window,
13869 cx,
13870 )?;
13871 Ok(())
13872 }
13873
13874 pub fn select_previous(
13875 &mut self,
13876 action: &SelectPrevious,
13877 window: &mut Window,
13878 cx: &mut Context<Self>,
13879 ) -> Result<()> {
13880 self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
13881 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
13882 let buffer = &display_map.buffer_snapshot;
13883 let mut selections = self.selections.all::<usize>(cx);
13884 if let Some(mut select_prev_state) = self.select_prev_state.take() {
13885 let query = &select_prev_state.query;
13886 if !select_prev_state.done {
13887 let first_selection = selections.iter().min_by_key(|s| s.id).unwrap();
13888 let last_selection = selections.iter().max_by_key(|s| s.id).unwrap();
13889 let mut next_selected_range = None;
13890 // When we're iterating matches backwards, the oldest match will actually be the furthest one in the buffer.
13891 let bytes_before_last_selection =
13892 buffer.reversed_bytes_in_range(0..last_selection.start);
13893 let bytes_after_first_selection =
13894 buffer.reversed_bytes_in_range(first_selection.end..buffer.len());
13895 let query_matches = query
13896 .stream_find_iter(bytes_before_last_selection)
13897 .map(|result| (last_selection.start, result))
13898 .chain(
13899 query
13900 .stream_find_iter(bytes_after_first_selection)
13901 .map(|result| (buffer.len(), result)),
13902 );
13903 for (end_offset, query_match) in query_matches {
13904 let query_match = query_match.unwrap(); // can only fail due to I/O
13905 let offset_range =
13906 end_offset - query_match.end()..end_offset - query_match.start();
13907
13908 if !select_prev_state.wordwise
13909 || (!buffer.is_inside_word(offset_range.start, false)
13910 && !buffer.is_inside_word(offset_range.end, false))
13911 {
13912 next_selected_range = Some(offset_range);
13913 break;
13914 }
13915 }
13916
13917 if let Some(next_selected_range) = next_selected_range {
13918 self.select_match_ranges(
13919 next_selected_range,
13920 last_selection.reversed,
13921 action.replace_newest,
13922 Some(Autoscroll::newest()),
13923 window,
13924 cx,
13925 );
13926 } else {
13927 select_prev_state.done = true;
13928 }
13929 }
13930
13931 self.select_prev_state = Some(select_prev_state);
13932 } else {
13933 let mut only_carets = true;
13934 let mut same_text_selected = true;
13935 let mut selected_text = None;
13936
13937 let mut selections_iter = selections.iter().peekable();
13938 while let Some(selection) = selections_iter.next() {
13939 if selection.start != selection.end {
13940 only_carets = false;
13941 }
13942
13943 if same_text_selected {
13944 if selected_text.is_none() {
13945 selected_text =
13946 Some(buffer.text_for_range(selection.range()).collect::<String>());
13947 }
13948
13949 if let Some(next_selection) = selections_iter.peek() {
13950 if next_selection.range().len() == selection.range().len() {
13951 let next_selected_text = buffer
13952 .text_for_range(next_selection.range())
13953 .collect::<String>();
13954 if Some(next_selected_text) != selected_text {
13955 same_text_selected = false;
13956 selected_text = None;
13957 }
13958 } else {
13959 same_text_selected = false;
13960 selected_text = None;
13961 }
13962 }
13963 }
13964 }
13965
13966 if only_carets {
13967 for selection in &mut selections {
13968 let (word_range, _) = buffer.surrounding_word(selection.start, false);
13969 selection.start = word_range.start;
13970 selection.end = word_range.end;
13971 selection.goal = SelectionGoal::None;
13972 selection.reversed = false;
13973 self.select_match_ranges(
13974 selection.start..selection.end,
13975 selection.reversed,
13976 action.replace_newest,
13977 Some(Autoscroll::newest()),
13978 window,
13979 cx,
13980 );
13981 }
13982 if selections.len() == 1 {
13983 let selection = selections
13984 .last()
13985 .expect("ensured that there's only one selection");
13986 let query = buffer
13987 .text_for_range(selection.start..selection.end)
13988 .collect::<String>();
13989 let is_empty = query.is_empty();
13990 let select_state = SelectNextState {
13991 query: AhoCorasick::new(&[query.chars().rev().collect::<String>()])?,
13992 wordwise: true,
13993 done: is_empty,
13994 };
13995 self.select_prev_state = Some(select_state);
13996 } else {
13997 self.select_prev_state = None;
13998 }
13999 } else if let Some(selected_text) = selected_text {
14000 self.select_prev_state = Some(SelectNextState {
14001 query: AhoCorasick::new(&[selected_text.chars().rev().collect::<String>()])?,
14002 wordwise: false,
14003 done: false,
14004 });
14005 self.select_previous(action, window, cx)?;
14006 }
14007 }
14008 Ok(())
14009 }
14010
14011 pub fn find_next_match(
14012 &mut self,
14013 _: &FindNextMatch,
14014 window: &mut Window,
14015 cx: &mut Context<Self>,
14016 ) -> Result<()> {
14017 let selections = self.selections.disjoint_anchors();
14018 match selections.first() {
14019 Some(first) if selections.len() >= 2 => {
14020 self.change_selections(Default::default(), window, cx, |s| {
14021 s.select_ranges([first.range()]);
14022 });
14023 }
14024 _ => self.select_next(
14025 &SelectNext {
14026 replace_newest: true,
14027 },
14028 window,
14029 cx,
14030 )?,
14031 }
14032 Ok(())
14033 }
14034
14035 pub fn find_previous_match(
14036 &mut self,
14037 _: &FindPreviousMatch,
14038 window: &mut Window,
14039 cx: &mut Context<Self>,
14040 ) -> Result<()> {
14041 let selections = self.selections.disjoint_anchors();
14042 match selections.last() {
14043 Some(last) if selections.len() >= 2 => {
14044 self.change_selections(Default::default(), window, cx, |s| {
14045 s.select_ranges([last.range()]);
14046 });
14047 }
14048 _ => self.select_previous(
14049 &SelectPrevious {
14050 replace_newest: true,
14051 },
14052 window,
14053 cx,
14054 )?,
14055 }
14056 Ok(())
14057 }
14058
14059 pub fn toggle_comments(
14060 &mut self,
14061 action: &ToggleComments,
14062 window: &mut Window,
14063 cx: &mut Context<Self>,
14064 ) {
14065 if self.read_only(cx) {
14066 return;
14067 }
14068 self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
14069 let text_layout_details = &self.text_layout_details(window);
14070 self.transact(window, cx, |this, window, cx| {
14071 let mut selections = this.selections.all::<MultiBufferPoint>(cx);
14072 let mut edits = Vec::new();
14073 let mut selection_edit_ranges = Vec::new();
14074 let mut last_toggled_row = None;
14075 let snapshot = this.buffer.read(cx).read(cx);
14076 let empty_str: Arc<str> = Arc::default();
14077 let mut suffixes_inserted = Vec::new();
14078 let ignore_indent = action.ignore_indent;
14079
14080 fn comment_prefix_range(
14081 snapshot: &MultiBufferSnapshot,
14082 row: MultiBufferRow,
14083 comment_prefix: &str,
14084 comment_prefix_whitespace: &str,
14085 ignore_indent: bool,
14086 ) -> Range<Point> {
14087 let indent_size = if ignore_indent {
14088 0
14089 } else {
14090 snapshot.indent_size_for_line(row).len
14091 };
14092
14093 let start = Point::new(row.0, indent_size);
14094
14095 let mut line_bytes = snapshot
14096 .bytes_in_range(start..snapshot.max_point())
14097 .flatten()
14098 .copied();
14099
14100 // If this line currently begins with the line comment prefix, then record
14101 // the range containing the prefix.
14102 if line_bytes
14103 .by_ref()
14104 .take(comment_prefix.len())
14105 .eq(comment_prefix.bytes())
14106 {
14107 // Include any whitespace that matches the comment prefix.
14108 let matching_whitespace_len = line_bytes
14109 .zip(comment_prefix_whitespace.bytes())
14110 .take_while(|(a, b)| a == b)
14111 .count() as u32;
14112 let end = Point::new(
14113 start.row,
14114 start.column + comment_prefix.len() as u32 + matching_whitespace_len,
14115 );
14116 start..end
14117 } else {
14118 start..start
14119 }
14120 }
14121
14122 fn comment_suffix_range(
14123 snapshot: &MultiBufferSnapshot,
14124 row: MultiBufferRow,
14125 comment_suffix: &str,
14126 comment_suffix_has_leading_space: bool,
14127 ) -> Range<Point> {
14128 let end = Point::new(row.0, snapshot.line_len(row));
14129 let suffix_start_column = end.column.saturating_sub(comment_suffix.len() as u32);
14130
14131 let mut line_end_bytes = snapshot
14132 .bytes_in_range(Point::new(end.row, suffix_start_column.saturating_sub(1))..end)
14133 .flatten()
14134 .copied();
14135
14136 let leading_space_len = if suffix_start_column > 0
14137 && line_end_bytes.next() == Some(b' ')
14138 && comment_suffix_has_leading_space
14139 {
14140 1
14141 } else {
14142 0
14143 };
14144
14145 // If this line currently begins with the line comment prefix, then record
14146 // the range containing the prefix.
14147 if line_end_bytes.by_ref().eq(comment_suffix.bytes()) {
14148 let start = Point::new(end.row, suffix_start_column - leading_space_len);
14149 start..end
14150 } else {
14151 end..end
14152 }
14153 }
14154
14155 // TODO: Handle selections that cross excerpts
14156 for selection in &mut selections {
14157 let start_column = snapshot
14158 .indent_size_for_line(MultiBufferRow(selection.start.row))
14159 .len;
14160 let language = if let Some(language) =
14161 snapshot.language_scope_at(Point::new(selection.start.row, start_column))
14162 {
14163 language
14164 } else {
14165 continue;
14166 };
14167
14168 selection_edit_ranges.clear();
14169
14170 // If multiple selections contain a given row, avoid processing that
14171 // row more than once.
14172 let mut start_row = MultiBufferRow(selection.start.row);
14173 if last_toggled_row == Some(start_row) {
14174 start_row = start_row.next_row();
14175 }
14176 let end_row =
14177 if selection.end.row > selection.start.row && selection.end.column == 0 {
14178 MultiBufferRow(selection.end.row - 1)
14179 } else {
14180 MultiBufferRow(selection.end.row)
14181 };
14182 last_toggled_row = Some(end_row);
14183
14184 if start_row > end_row {
14185 continue;
14186 }
14187
14188 // If the language has line comments, toggle those.
14189 let mut full_comment_prefixes = language.line_comment_prefixes().to_vec();
14190
14191 // If ignore_indent is set, trim spaces from the right side of all full_comment_prefixes
14192 if ignore_indent {
14193 full_comment_prefixes = full_comment_prefixes
14194 .into_iter()
14195 .map(|s| Arc::from(s.trim_end()))
14196 .collect();
14197 }
14198
14199 if !full_comment_prefixes.is_empty() {
14200 let first_prefix = full_comment_prefixes
14201 .first()
14202 .expect("prefixes is non-empty");
14203 let prefix_trimmed_lengths = full_comment_prefixes
14204 .iter()
14205 .map(|p| p.trim_end_matches(' ').len())
14206 .collect::<SmallVec<[usize; 4]>>();
14207
14208 let mut all_selection_lines_are_comments = true;
14209
14210 for row in start_row.0..=end_row.0 {
14211 let row = MultiBufferRow(row);
14212 if start_row < end_row && snapshot.is_line_blank(row) {
14213 continue;
14214 }
14215
14216 let prefix_range = full_comment_prefixes
14217 .iter()
14218 .zip(prefix_trimmed_lengths.iter().copied())
14219 .map(|(prefix, trimmed_prefix_len)| {
14220 comment_prefix_range(
14221 snapshot.deref(),
14222 row,
14223 &prefix[..trimmed_prefix_len],
14224 &prefix[trimmed_prefix_len..],
14225 ignore_indent,
14226 )
14227 })
14228 .max_by_key(|range| range.end.column - range.start.column)
14229 .expect("prefixes is non-empty");
14230
14231 if prefix_range.is_empty() {
14232 all_selection_lines_are_comments = false;
14233 }
14234
14235 selection_edit_ranges.push(prefix_range);
14236 }
14237
14238 if all_selection_lines_are_comments {
14239 edits.extend(
14240 selection_edit_ranges
14241 .iter()
14242 .cloned()
14243 .map(|range| (range, empty_str.clone())),
14244 );
14245 } else {
14246 let min_column = selection_edit_ranges
14247 .iter()
14248 .map(|range| range.start.column)
14249 .min()
14250 .unwrap_or(0);
14251 edits.extend(selection_edit_ranges.iter().map(|range| {
14252 let position = Point::new(range.start.row, min_column);
14253 (position..position, first_prefix.clone())
14254 }));
14255 }
14256 } else if let Some((full_comment_prefix, comment_suffix)) =
14257 language.block_comment_delimiters()
14258 {
14259 let comment_prefix = full_comment_prefix.trim_end_matches(' ');
14260 let comment_prefix_whitespace = &full_comment_prefix[comment_prefix.len()..];
14261 let prefix_range = comment_prefix_range(
14262 snapshot.deref(),
14263 start_row,
14264 comment_prefix,
14265 comment_prefix_whitespace,
14266 ignore_indent,
14267 );
14268 let suffix_range = comment_suffix_range(
14269 snapshot.deref(),
14270 end_row,
14271 comment_suffix.trim_start_matches(' '),
14272 comment_suffix.starts_with(' '),
14273 );
14274
14275 if prefix_range.is_empty() || suffix_range.is_empty() {
14276 edits.push((
14277 prefix_range.start..prefix_range.start,
14278 full_comment_prefix.clone(),
14279 ));
14280 edits.push((suffix_range.end..suffix_range.end, comment_suffix.clone()));
14281 suffixes_inserted.push((end_row, comment_suffix.len()));
14282 } else {
14283 edits.push((prefix_range, empty_str.clone()));
14284 edits.push((suffix_range, empty_str.clone()));
14285 }
14286 } else {
14287 continue;
14288 }
14289 }
14290
14291 drop(snapshot);
14292 this.buffer.update(cx, |buffer, cx| {
14293 buffer.edit(edits, None, cx);
14294 });
14295
14296 // Adjust selections so that they end before any comment suffixes that
14297 // were inserted.
14298 let mut suffixes_inserted = suffixes_inserted.into_iter().peekable();
14299 let mut selections = this.selections.all::<Point>(cx);
14300 let snapshot = this.buffer.read(cx).read(cx);
14301 for selection in &mut selections {
14302 while let Some((row, suffix_len)) = suffixes_inserted.peek().copied() {
14303 match row.cmp(&MultiBufferRow(selection.end.row)) {
14304 Ordering::Less => {
14305 suffixes_inserted.next();
14306 continue;
14307 }
14308 Ordering::Greater => break,
14309 Ordering::Equal => {
14310 if selection.end.column == snapshot.line_len(row) {
14311 if selection.is_empty() {
14312 selection.start.column -= suffix_len as u32;
14313 }
14314 selection.end.column -= suffix_len as u32;
14315 }
14316 break;
14317 }
14318 }
14319 }
14320 }
14321
14322 drop(snapshot);
14323 this.change_selections(Default::default(), window, cx, |s| s.select(selections));
14324
14325 let selections = this.selections.all::<Point>(cx);
14326 let selections_on_single_row = selections.windows(2).all(|selections| {
14327 selections[0].start.row == selections[1].start.row
14328 && selections[0].end.row == selections[1].end.row
14329 && selections[0].start.row == selections[0].end.row
14330 });
14331 let selections_selecting = selections
14332 .iter()
14333 .any(|selection| selection.start != selection.end);
14334 let advance_downwards = action.advance_downwards
14335 && selections_on_single_row
14336 && !selections_selecting
14337 && !matches!(this.mode, EditorMode::SingleLine { .. });
14338
14339 if advance_downwards {
14340 let snapshot = this.buffer.read(cx).snapshot(cx);
14341
14342 this.change_selections(Default::default(), window, cx, |s| {
14343 s.move_cursors_with(|display_snapshot, display_point, _| {
14344 let mut point = display_point.to_point(display_snapshot);
14345 point.row += 1;
14346 point = snapshot.clip_point(point, Bias::Left);
14347 let display_point = point.to_display_point(display_snapshot);
14348 let goal = SelectionGoal::HorizontalPosition(
14349 display_snapshot
14350 .x_for_display_point(display_point, text_layout_details)
14351 .into(),
14352 );
14353 (display_point, goal)
14354 })
14355 });
14356 }
14357 });
14358 }
14359
14360 pub fn select_enclosing_symbol(
14361 &mut self,
14362 _: &SelectEnclosingSymbol,
14363 window: &mut Window,
14364 cx: &mut Context<Self>,
14365 ) {
14366 self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
14367
14368 let buffer = self.buffer.read(cx).snapshot(cx);
14369 let old_selections = self.selections.all::<usize>(cx).into_boxed_slice();
14370
14371 fn update_selection(
14372 selection: &Selection<usize>,
14373 buffer_snap: &MultiBufferSnapshot,
14374 ) -> Option<Selection<usize>> {
14375 let cursor = selection.head();
14376 let (_buffer_id, symbols) = buffer_snap.symbols_containing(cursor, None)?;
14377 for symbol in symbols.iter().rev() {
14378 let start = symbol.range.start.to_offset(buffer_snap);
14379 let end = symbol.range.end.to_offset(buffer_snap);
14380 let new_range = start..end;
14381 if start < selection.start || end > selection.end {
14382 return Some(Selection {
14383 id: selection.id,
14384 start: new_range.start,
14385 end: new_range.end,
14386 goal: SelectionGoal::None,
14387 reversed: selection.reversed,
14388 });
14389 }
14390 }
14391 None
14392 }
14393
14394 let mut selected_larger_symbol = false;
14395 let new_selections = old_selections
14396 .iter()
14397 .map(|selection| match update_selection(selection, &buffer) {
14398 Some(new_selection) => {
14399 if new_selection.range() != selection.range() {
14400 selected_larger_symbol = true;
14401 }
14402 new_selection
14403 }
14404 None => selection.clone(),
14405 })
14406 .collect::<Vec<_>>();
14407
14408 if selected_larger_symbol {
14409 self.change_selections(Default::default(), window, cx, |s| {
14410 s.select(new_selections);
14411 });
14412 }
14413 }
14414
14415 pub fn select_larger_syntax_node(
14416 &mut self,
14417 _: &SelectLargerSyntaxNode,
14418 window: &mut Window,
14419 cx: &mut Context<Self>,
14420 ) {
14421 let Some(visible_row_count) = self.visible_row_count() else {
14422 return;
14423 };
14424 let old_selections: Box<[_]> = self.selections.all::<usize>(cx).into();
14425 if old_selections.is_empty() {
14426 return;
14427 }
14428
14429 self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
14430
14431 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
14432 let buffer = self.buffer.read(cx).snapshot(cx);
14433
14434 let mut selected_larger_node = false;
14435 let mut new_selections = old_selections
14436 .iter()
14437 .map(|selection| {
14438 let old_range = selection.start..selection.end;
14439
14440 if let Some((node, _)) = buffer.syntax_ancestor(old_range.clone()) {
14441 // manually select word at selection
14442 if ["string_content", "inline"].contains(&node.kind()) {
14443 let (word_range, _) = buffer.surrounding_word(old_range.start, false);
14444 // ignore if word is already selected
14445 if !word_range.is_empty() && old_range != word_range {
14446 let (last_word_range, _) =
14447 buffer.surrounding_word(old_range.end, false);
14448 // only select word if start and end point belongs to same word
14449 if word_range == last_word_range {
14450 selected_larger_node = true;
14451 return Selection {
14452 id: selection.id,
14453 start: word_range.start,
14454 end: word_range.end,
14455 goal: SelectionGoal::None,
14456 reversed: selection.reversed,
14457 };
14458 }
14459 }
14460 }
14461 }
14462
14463 let mut new_range = old_range.clone();
14464 while let Some((_node, containing_range)) =
14465 buffer.syntax_ancestor(new_range.clone())
14466 {
14467 new_range = match containing_range {
14468 MultiOrSingleBufferOffsetRange::Single(_) => break,
14469 MultiOrSingleBufferOffsetRange::Multi(range) => range,
14470 };
14471 if !display_map.intersects_fold(new_range.start)
14472 && !display_map.intersects_fold(new_range.end)
14473 {
14474 break;
14475 }
14476 }
14477
14478 selected_larger_node |= new_range != old_range;
14479 Selection {
14480 id: selection.id,
14481 start: new_range.start,
14482 end: new_range.end,
14483 goal: SelectionGoal::None,
14484 reversed: selection.reversed,
14485 }
14486 })
14487 .collect::<Vec<_>>();
14488
14489 if !selected_larger_node {
14490 return; // don't put this call in the history
14491 }
14492
14493 // scroll based on transformation done to the last selection created by the user
14494 let (last_old, last_new) = old_selections
14495 .last()
14496 .zip(new_selections.last().cloned())
14497 .expect("old_selections isn't empty");
14498
14499 // revert selection
14500 let is_selection_reversed = {
14501 let should_newest_selection_be_reversed = last_old.start != last_new.start;
14502 new_selections.last_mut().expect("checked above").reversed =
14503 should_newest_selection_be_reversed;
14504 should_newest_selection_be_reversed
14505 };
14506
14507 if selected_larger_node {
14508 self.select_syntax_node_history.disable_clearing = true;
14509 self.change_selections(SelectionEffects::no_scroll(), window, cx, |s| {
14510 s.select(new_selections.clone());
14511 });
14512 self.select_syntax_node_history.disable_clearing = false;
14513 }
14514
14515 let start_row = last_new.start.to_display_point(&display_map).row().0;
14516 let end_row = last_new.end.to_display_point(&display_map).row().0;
14517 let selection_height = end_row - start_row + 1;
14518 let scroll_margin_rows = self.vertical_scroll_margin() as u32;
14519
14520 let fits_on_the_screen = visible_row_count >= selection_height + scroll_margin_rows * 2;
14521 let scroll_behavior = if fits_on_the_screen {
14522 self.request_autoscroll(Autoscroll::fit(), cx);
14523 SelectSyntaxNodeScrollBehavior::FitSelection
14524 } else if is_selection_reversed {
14525 self.scroll_cursor_top(&ScrollCursorTop, window, cx);
14526 SelectSyntaxNodeScrollBehavior::CursorTop
14527 } else {
14528 self.scroll_cursor_bottom(&ScrollCursorBottom, window, cx);
14529 SelectSyntaxNodeScrollBehavior::CursorBottom
14530 };
14531
14532 self.select_syntax_node_history.push((
14533 old_selections,
14534 scroll_behavior,
14535 is_selection_reversed,
14536 ));
14537 }
14538
14539 pub fn select_smaller_syntax_node(
14540 &mut self,
14541 _: &SelectSmallerSyntaxNode,
14542 window: &mut Window,
14543 cx: &mut Context<Self>,
14544 ) {
14545 self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
14546
14547 if let Some((mut selections, scroll_behavior, is_selection_reversed)) =
14548 self.select_syntax_node_history.pop()
14549 {
14550 if let Some(selection) = selections.last_mut() {
14551 selection.reversed = is_selection_reversed;
14552 }
14553
14554 self.select_syntax_node_history.disable_clearing = true;
14555 self.change_selections(SelectionEffects::no_scroll(), window, cx, |s| {
14556 s.select(selections.to_vec());
14557 });
14558 self.select_syntax_node_history.disable_clearing = false;
14559
14560 match scroll_behavior {
14561 SelectSyntaxNodeScrollBehavior::CursorTop => {
14562 self.scroll_cursor_top(&ScrollCursorTop, window, cx);
14563 }
14564 SelectSyntaxNodeScrollBehavior::FitSelection => {
14565 self.request_autoscroll(Autoscroll::fit(), cx);
14566 }
14567 SelectSyntaxNodeScrollBehavior::CursorBottom => {
14568 self.scroll_cursor_bottom(&ScrollCursorBottom, window, cx);
14569 }
14570 }
14571 }
14572 }
14573
14574 fn refresh_runnables(&mut self, window: &mut Window, cx: &mut Context<Self>) -> Task<()> {
14575 if !EditorSettings::get_global(cx).gutter.runnables {
14576 self.clear_tasks();
14577 return Task::ready(());
14578 }
14579 let project = self.project.as_ref().map(Entity::downgrade);
14580 let task_sources = self.lsp_task_sources(cx);
14581 let multi_buffer = self.buffer.downgrade();
14582 cx.spawn_in(window, async move |editor, cx| {
14583 cx.background_executor().timer(UPDATE_DEBOUNCE).await;
14584 let Some(project) = project.and_then(|p| p.upgrade()) else {
14585 return;
14586 };
14587 let Ok(display_snapshot) = editor.update(cx, |this, cx| {
14588 this.display_map.update(cx, |map, cx| map.snapshot(cx))
14589 }) else {
14590 return;
14591 };
14592
14593 let hide_runnables = project
14594 .update(cx, |project, cx| {
14595 // Do not display any test indicators in non-dev server remote projects.
14596 project.is_via_collab() && project.ssh_connection_string(cx).is_none()
14597 })
14598 .unwrap_or(true);
14599 if hide_runnables {
14600 return;
14601 }
14602 let new_rows =
14603 cx.background_spawn({
14604 let snapshot = display_snapshot.clone();
14605 async move {
14606 Self::fetch_runnable_ranges(&snapshot, Anchor::min()..Anchor::max())
14607 }
14608 })
14609 .await;
14610 let Ok(lsp_tasks) =
14611 cx.update(|_, cx| crate::lsp_tasks(project.clone(), &task_sources, None, cx))
14612 else {
14613 return;
14614 };
14615 let lsp_tasks = lsp_tasks.await;
14616
14617 let Ok(mut lsp_tasks_by_rows) = cx.update(|_, cx| {
14618 lsp_tasks
14619 .into_iter()
14620 .flat_map(|(kind, tasks)| {
14621 tasks.into_iter().filter_map(move |(location, task)| {
14622 Some((kind.clone(), location?, task))
14623 })
14624 })
14625 .fold(HashMap::default(), |mut acc, (kind, location, task)| {
14626 let buffer = location.target.buffer;
14627 let buffer_snapshot = buffer.read(cx).snapshot();
14628 let offset = display_snapshot.buffer_snapshot.excerpts().find_map(
14629 |(excerpt_id, snapshot, _)| {
14630 if snapshot.remote_id() == buffer_snapshot.remote_id() {
14631 display_snapshot
14632 .buffer_snapshot
14633 .anchor_in_excerpt(excerpt_id, location.target.range.start)
14634 } else {
14635 None
14636 }
14637 },
14638 );
14639 if let Some(offset) = offset {
14640 let task_buffer_range =
14641 location.target.range.to_point(&buffer_snapshot);
14642 let context_buffer_range =
14643 task_buffer_range.to_offset(&buffer_snapshot);
14644 let context_range = BufferOffset(context_buffer_range.start)
14645 ..BufferOffset(context_buffer_range.end);
14646
14647 acc.entry((buffer_snapshot.remote_id(), task_buffer_range.start.row))
14648 .or_insert_with(|| RunnableTasks {
14649 templates: Vec::new(),
14650 offset,
14651 column: task_buffer_range.start.column,
14652 extra_variables: HashMap::default(),
14653 context_range,
14654 })
14655 .templates
14656 .push((kind, task.original_task().clone()));
14657 }
14658
14659 acc
14660 })
14661 }) else {
14662 return;
14663 };
14664
14665 let Ok(prefer_lsp) = multi_buffer.update(cx, |buffer, cx| {
14666 buffer.language_settings(cx).tasks.prefer_lsp
14667 }) else {
14668 return;
14669 };
14670
14671 let rows = Self::runnable_rows(
14672 project,
14673 display_snapshot,
14674 prefer_lsp && !lsp_tasks_by_rows.is_empty(),
14675 new_rows,
14676 cx.clone(),
14677 )
14678 .await;
14679 editor
14680 .update(cx, |editor, _| {
14681 editor.clear_tasks();
14682 for (key, mut value) in rows {
14683 if let Some(lsp_tasks) = lsp_tasks_by_rows.remove(&key) {
14684 value.templates.extend(lsp_tasks.templates);
14685 }
14686
14687 editor.insert_tasks(key, value);
14688 }
14689 for (key, value) in lsp_tasks_by_rows {
14690 editor.insert_tasks(key, value);
14691 }
14692 })
14693 .ok();
14694 })
14695 }
14696 fn fetch_runnable_ranges(
14697 snapshot: &DisplaySnapshot,
14698 range: Range<Anchor>,
14699 ) -> Vec<language::RunnableRange> {
14700 snapshot.buffer_snapshot.runnable_ranges(range).collect()
14701 }
14702
14703 fn runnable_rows(
14704 project: Entity<Project>,
14705 snapshot: DisplaySnapshot,
14706 prefer_lsp: bool,
14707 runnable_ranges: Vec<RunnableRange>,
14708 cx: AsyncWindowContext,
14709 ) -> Task<Vec<((BufferId, BufferRow), RunnableTasks)>> {
14710 cx.spawn(async move |cx| {
14711 let mut runnable_rows = Vec::with_capacity(runnable_ranges.len());
14712 for mut runnable in runnable_ranges {
14713 let Some(tasks) = cx
14714 .update(|_, cx| Self::templates_with_tags(&project, &mut runnable.runnable, cx))
14715 .ok()
14716 else {
14717 continue;
14718 };
14719 let mut tasks = tasks.await;
14720
14721 if prefer_lsp {
14722 tasks.retain(|(task_kind, _)| {
14723 !matches!(task_kind, TaskSourceKind::Language { .. })
14724 });
14725 }
14726 if tasks.is_empty() {
14727 continue;
14728 }
14729
14730 let point = runnable.run_range.start.to_point(&snapshot.buffer_snapshot);
14731 let Some(row) = snapshot
14732 .buffer_snapshot
14733 .buffer_line_for_row(MultiBufferRow(point.row))
14734 .map(|(_, range)| range.start.row)
14735 else {
14736 continue;
14737 };
14738
14739 let context_range =
14740 BufferOffset(runnable.full_range.start)..BufferOffset(runnable.full_range.end);
14741 runnable_rows.push((
14742 (runnable.buffer_id, row),
14743 RunnableTasks {
14744 templates: tasks,
14745 offset: snapshot
14746 .buffer_snapshot
14747 .anchor_before(runnable.run_range.start),
14748 context_range,
14749 column: point.column,
14750 extra_variables: runnable.extra_captures,
14751 },
14752 ));
14753 }
14754 runnable_rows
14755 })
14756 }
14757
14758 fn templates_with_tags(
14759 project: &Entity<Project>,
14760 runnable: &mut Runnable,
14761 cx: &mut App,
14762 ) -> Task<Vec<(TaskSourceKind, TaskTemplate)>> {
14763 let (inventory, worktree_id, file) = project.read_with(cx, |project, cx| {
14764 let (worktree_id, file) = project
14765 .buffer_for_id(runnable.buffer, cx)
14766 .and_then(|buffer| buffer.read(cx).file())
14767 .map(|file| (file.worktree_id(cx), file.clone()))
14768 .unzip();
14769
14770 (
14771 project.task_store().read(cx).task_inventory().cloned(),
14772 worktree_id,
14773 file,
14774 )
14775 });
14776
14777 let tags = mem::take(&mut runnable.tags);
14778 let language = runnable.language.clone();
14779 cx.spawn(async move |cx| {
14780 let mut templates_with_tags = Vec::new();
14781 if let Some(inventory) = inventory {
14782 for RunnableTag(tag) in tags {
14783 let Ok(new_tasks) = inventory.update(cx, |inventory, cx| {
14784 inventory.list_tasks(file.clone(), Some(language.clone()), worktree_id, cx)
14785 }) else {
14786 return templates_with_tags;
14787 };
14788 templates_with_tags.extend(new_tasks.await.into_iter().filter(
14789 move |(_, template)| {
14790 template.tags.iter().any(|source_tag| source_tag == &tag)
14791 },
14792 ));
14793 }
14794 }
14795 templates_with_tags.sort_by_key(|(kind, _)| kind.to_owned());
14796
14797 if let Some((leading_tag_source, _)) = templates_with_tags.first() {
14798 // Strongest source wins; if we have worktree tag binding, prefer that to
14799 // global and language bindings;
14800 // if we have a global binding, prefer that to language binding.
14801 let first_mismatch = templates_with_tags
14802 .iter()
14803 .position(|(tag_source, _)| tag_source != leading_tag_source);
14804 if let Some(index) = first_mismatch {
14805 templates_with_tags.truncate(index);
14806 }
14807 }
14808
14809 templates_with_tags
14810 })
14811 }
14812
14813 pub fn move_to_enclosing_bracket(
14814 &mut self,
14815 _: &MoveToEnclosingBracket,
14816 window: &mut Window,
14817 cx: &mut Context<Self>,
14818 ) {
14819 self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
14820 self.change_selections(Default::default(), window, cx, |s| {
14821 s.move_offsets_with(|snapshot, selection| {
14822 let Some(enclosing_bracket_ranges) =
14823 snapshot.enclosing_bracket_ranges(selection.start..selection.end)
14824 else {
14825 return;
14826 };
14827
14828 let mut best_length = usize::MAX;
14829 let mut best_inside = false;
14830 let mut best_in_bracket_range = false;
14831 let mut best_destination = None;
14832 for (open, close) in enclosing_bracket_ranges {
14833 let close = close.to_inclusive();
14834 let length = close.end() - open.start;
14835 let inside = selection.start >= open.end && selection.end <= *close.start();
14836 let in_bracket_range = open.to_inclusive().contains(&selection.head())
14837 || close.contains(&selection.head());
14838
14839 // If best is next to a bracket and current isn't, skip
14840 if !in_bracket_range && best_in_bracket_range {
14841 continue;
14842 }
14843
14844 // Prefer smaller lengths unless best is inside and current isn't
14845 if length > best_length && (best_inside || !inside) {
14846 continue;
14847 }
14848
14849 best_length = length;
14850 best_inside = inside;
14851 best_in_bracket_range = in_bracket_range;
14852 best_destination = Some(
14853 if close.contains(&selection.start) && close.contains(&selection.end) {
14854 if inside { open.end } else { open.start }
14855 } else if inside {
14856 *close.start()
14857 } else {
14858 *close.end()
14859 },
14860 );
14861 }
14862
14863 if let Some(destination) = best_destination {
14864 selection.collapse_to(destination, SelectionGoal::None);
14865 }
14866 })
14867 });
14868 }
14869
14870 pub fn undo_selection(
14871 &mut self,
14872 _: &UndoSelection,
14873 window: &mut Window,
14874 cx: &mut Context<Self>,
14875 ) {
14876 self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
14877 if let Some(entry) = self.selection_history.undo_stack.pop_back() {
14878 self.selection_history.mode = SelectionHistoryMode::Undoing;
14879 self.with_selection_effects_deferred(window, cx, |this, window, cx| {
14880 this.end_selection(window, cx);
14881 this.change_selections(
14882 SelectionEffects::scroll(Autoscroll::newest()),
14883 window,
14884 cx,
14885 |s| s.select_anchors(entry.selections.to_vec()),
14886 );
14887 });
14888 self.selection_history.mode = SelectionHistoryMode::Normal;
14889
14890 self.select_next_state = entry.select_next_state;
14891 self.select_prev_state = entry.select_prev_state;
14892 self.add_selections_state = entry.add_selections_state;
14893 }
14894 }
14895
14896 pub fn redo_selection(
14897 &mut self,
14898 _: &RedoSelection,
14899 window: &mut Window,
14900 cx: &mut Context<Self>,
14901 ) {
14902 self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
14903 if let Some(entry) = self.selection_history.redo_stack.pop_back() {
14904 self.selection_history.mode = SelectionHistoryMode::Redoing;
14905 self.with_selection_effects_deferred(window, cx, |this, window, cx| {
14906 this.end_selection(window, cx);
14907 this.change_selections(
14908 SelectionEffects::scroll(Autoscroll::newest()),
14909 window,
14910 cx,
14911 |s| s.select_anchors(entry.selections.to_vec()),
14912 );
14913 });
14914 self.selection_history.mode = SelectionHistoryMode::Normal;
14915
14916 self.select_next_state = entry.select_next_state;
14917 self.select_prev_state = entry.select_prev_state;
14918 self.add_selections_state = entry.add_selections_state;
14919 }
14920 }
14921
14922 pub fn expand_excerpts(
14923 &mut self,
14924 action: &ExpandExcerpts,
14925 _: &mut Window,
14926 cx: &mut Context<Self>,
14927 ) {
14928 self.expand_excerpts_for_direction(action.lines, ExpandExcerptDirection::UpAndDown, cx)
14929 }
14930
14931 pub fn expand_excerpts_down(
14932 &mut self,
14933 action: &ExpandExcerptsDown,
14934 _: &mut Window,
14935 cx: &mut Context<Self>,
14936 ) {
14937 self.expand_excerpts_for_direction(action.lines, ExpandExcerptDirection::Down, cx)
14938 }
14939
14940 pub fn expand_excerpts_up(
14941 &mut self,
14942 action: &ExpandExcerptsUp,
14943 _: &mut Window,
14944 cx: &mut Context<Self>,
14945 ) {
14946 self.expand_excerpts_for_direction(action.lines, ExpandExcerptDirection::Up, cx)
14947 }
14948
14949 pub fn expand_excerpts_for_direction(
14950 &mut self,
14951 lines: u32,
14952 direction: ExpandExcerptDirection,
14953
14954 cx: &mut Context<Self>,
14955 ) {
14956 let selections = self.selections.disjoint_anchors();
14957
14958 let lines = if lines == 0 {
14959 EditorSettings::get_global(cx).expand_excerpt_lines
14960 } else {
14961 lines
14962 };
14963
14964 self.buffer.update(cx, |buffer, cx| {
14965 let snapshot = buffer.snapshot(cx);
14966 let mut excerpt_ids = selections
14967 .iter()
14968 .flat_map(|selection| snapshot.excerpt_ids_for_range(selection.range()))
14969 .collect::<Vec<_>>();
14970 excerpt_ids.sort();
14971 excerpt_ids.dedup();
14972 buffer.expand_excerpts(excerpt_ids, lines, direction, cx)
14973 })
14974 }
14975
14976 pub fn expand_excerpt(
14977 &mut self,
14978 excerpt: ExcerptId,
14979 direction: ExpandExcerptDirection,
14980 window: &mut Window,
14981 cx: &mut Context<Self>,
14982 ) {
14983 let current_scroll_position = self.scroll_position(cx);
14984 let lines_to_expand = EditorSettings::get_global(cx).expand_excerpt_lines;
14985 let mut should_scroll_up = false;
14986
14987 if direction == ExpandExcerptDirection::Down {
14988 let multi_buffer = self.buffer.read(cx);
14989 let snapshot = multi_buffer.snapshot(cx);
14990 if let Some(buffer_id) = snapshot.buffer_id_for_excerpt(excerpt) {
14991 if let Some(buffer) = multi_buffer.buffer(buffer_id) {
14992 if let Some(excerpt_range) = snapshot.buffer_range_for_excerpt(excerpt) {
14993 let buffer_snapshot = buffer.read(cx).snapshot();
14994 let excerpt_end_row =
14995 Point::from_anchor(&excerpt_range.end, &buffer_snapshot).row;
14996 let last_row = buffer_snapshot.max_point().row;
14997 let lines_below = last_row.saturating_sub(excerpt_end_row);
14998 should_scroll_up = lines_below >= lines_to_expand;
14999 }
15000 }
15001 }
15002 }
15003
15004 self.buffer.update(cx, |buffer, cx| {
15005 buffer.expand_excerpts([excerpt], lines_to_expand, direction, cx)
15006 });
15007
15008 if should_scroll_up {
15009 let new_scroll_position =
15010 current_scroll_position + gpui::Point::new(0.0, lines_to_expand as f32);
15011 self.set_scroll_position(new_scroll_position, window, cx);
15012 }
15013 }
15014
15015 pub fn go_to_singleton_buffer_point(
15016 &mut self,
15017 point: Point,
15018 window: &mut Window,
15019 cx: &mut Context<Self>,
15020 ) {
15021 self.go_to_singleton_buffer_range(point..point, window, cx);
15022 }
15023
15024 pub fn go_to_singleton_buffer_range(
15025 &mut self,
15026 range: Range<Point>,
15027 window: &mut Window,
15028 cx: &mut Context<Self>,
15029 ) {
15030 let multibuffer = self.buffer().read(cx);
15031 let Some(buffer) = multibuffer.as_singleton() else {
15032 return;
15033 };
15034 let Some(start) = multibuffer.buffer_point_to_anchor(&buffer, range.start, cx) else {
15035 return;
15036 };
15037 let Some(end) = multibuffer.buffer_point_to_anchor(&buffer, range.end, cx) else {
15038 return;
15039 };
15040 self.change_selections(
15041 SelectionEffects::default().nav_history(true),
15042 window,
15043 cx,
15044 |s| s.select_anchor_ranges([start..end]),
15045 );
15046 }
15047
15048 pub fn go_to_diagnostic(
15049 &mut self,
15050 _: &GoToDiagnostic,
15051 window: &mut Window,
15052 cx: &mut Context<Self>,
15053 ) {
15054 if !self.diagnostics_enabled() {
15055 return;
15056 }
15057 self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
15058 self.go_to_diagnostic_impl(Direction::Next, window, cx)
15059 }
15060
15061 pub fn go_to_prev_diagnostic(
15062 &mut self,
15063 _: &GoToPreviousDiagnostic,
15064 window: &mut Window,
15065 cx: &mut Context<Self>,
15066 ) {
15067 if !self.diagnostics_enabled() {
15068 return;
15069 }
15070 self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
15071 self.go_to_diagnostic_impl(Direction::Prev, window, cx)
15072 }
15073
15074 pub fn go_to_diagnostic_impl(
15075 &mut self,
15076 direction: Direction,
15077 window: &mut Window,
15078 cx: &mut Context<Self>,
15079 ) {
15080 let buffer = self.buffer.read(cx).snapshot(cx);
15081 let selection = self.selections.newest::<usize>(cx);
15082
15083 let mut active_group_id = None;
15084 if let ActiveDiagnostic::Group(active_group) = &self.active_diagnostics {
15085 if active_group.active_range.start.to_offset(&buffer) == selection.start {
15086 active_group_id = Some(active_group.group_id);
15087 }
15088 }
15089
15090 fn filtered(
15091 snapshot: EditorSnapshot,
15092 diagnostics: impl Iterator<Item = DiagnosticEntry<usize>>,
15093 ) -> impl Iterator<Item = DiagnosticEntry<usize>> {
15094 diagnostics
15095 .filter(|entry| entry.range.start != entry.range.end)
15096 .filter(|entry| !entry.diagnostic.is_unnecessary)
15097 .filter(move |entry| !snapshot.intersects_fold(entry.range.start))
15098 }
15099
15100 let snapshot = self.snapshot(window, cx);
15101 let before = filtered(
15102 snapshot.clone(),
15103 buffer
15104 .diagnostics_in_range(0..selection.start)
15105 .filter(|entry| entry.range.start <= selection.start),
15106 );
15107 let after = filtered(
15108 snapshot,
15109 buffer
15110 .diagnostics_in_range(selection.start..buffer.len())
15111 .filter(|entry| entry.range.start >= selection.start),
15112 );
15113
15114 let mut found: Option<DiagnosticEntry<usize>> = None;
15115 if direction == Direction::Prev {
15116 'outer: for prev_diagnostics in [before.collect::<Vec<_>>(), after.collect::<Vec<_>>()]
15117 {
15118 for diagnostic in prev_diagnostics.into_iter().rev() {
15119 if diagnostic.range.start != selection.start
15120 || active_group_id
15121 .is_some_and(|active| diagnostic.diagnostic.group_id < active)
15122 {
15123 found = Some(diagnostic);
15124 break 'outer;
15125 }
15126 }
15127 }
15128 } else {
15129 for diagnostic in after.chain(before) {
15130 if diagnostic.range.start != selection.start
15131 || active_group_id.is_some_and(|active| diagnostic.diagnostic.group_id > active)
15132 {
15133 found = Some(diagnostic);
15134 break;
15135 }
15136 }
15137 }
15138 let Some(next_diagnostic) = found else {
15139 return;
15140 };
15141
15142 let Some(buffer_id) = buffer.anchor_after(next_diagnostic.range.start).buffer_id else {
15143 return;
15144 };
15145 self.change_selections(Default::default(), window, cx, |s| {
15146 s.select_ranges(vec![
15147 next_diagnostic.range.start..next_diagnostic.range.start,
15148 ])
15149 });
15150 self.activate_diagnostics(buffer_id, next_diagnostic, window, cx);
15151 self.refresh_inline_completion(false, true, window, cx);
15152 }
15153
15154 pub fn go_to_next_hunk(&mut self, _: &GoToHunk, window: &mut Window, cx: &mut Context<Self>) {
15155 self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
15156 let snapshot = self.snapshot(window, cx);
15157 let selection = self.selections.newest::<Point>(cx);
15158 self.go_to_hunk_before_or_after_position(
15159 &snapshot,
15160 selection.head(),
15161 Direction::Next,
15162 window,
15163 cx,
15164 );
15165 }
15166
15167 pub fn go_to_hunk_before_or_after_position(
15168 &mut self,
15169 snapshot: &EditorSnapshot,
15170 position: Point,
15171 direction: Direction,
15172 window: &mut Window,
15173 cx: &mut Context<Editor>,
15174 ) {
15175 let row = if direction == Direction::Next {
15176 self.hunk_after_position(snapshot, position)
15177 .map(|hunk| hunk.row_range.start)
15178 } else {
15179 self.hunk_before_position(snapshot, position)
15180 };
15181
15182 if let Some(row) = row {
15183 let destination = Point::new(row.0, 0);
15184 let autoscroll = Autoscroll::center();
15185
15186 self.unfold_ranges(&[destination..destination], false, false, cx);
15187 self.change_selections(SelectionEffects::scroll(autoscroll), window, cx, |s| {
15188 s.select_ranges([destination..destination]);
15189 });
15190 }
15191 }
15192
15193 fn hunk_after_position(
15194 &mut self,
15195 snapshot: &EditorSnapshot,
15196 position: Point,
15197 ) -> Option<MultiBufferDiffHunk> {
15198 snapshot
15199 .buffer_snapshot
15200 .diff_hunks_in_range(position..snapshot.buffer_snapshot.max_point())
15201 .find(|hunk| hunk.row_range.start.0 > position.row)
15202 .or_else(|| {
15203 snapshot
15204 .buffer_snapshot
15205 .diff_hunks_in_range(Point::zero()..position)
15206 .find(|hunk| hunk.row_range.end.0 < position.row)
15207 })
15208 }
15209
15210 fn go_to_prev_hunk(
15211 &mut self,
15212 _: &GoToPreviousHunk,
15213 window: &mut Window,
15214 cx: &mut Context<Self>,
15215 ) {
15216 self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
15217 let snapshot = self.snapshot(window, cx);
15218 let selection = self.selections.newest::<Point>(cx);
15219 self.go_to_hunk_before_or_after_position(
15220 &snapshot,
15221 selection.head(),
15222 Direction::Prev,
15223 window,
15224 cx,
15225 );
15226 }
15227
15228 fn hunk_before_position(
15229 &mut self,
15230 snapshot: &EditorSnapshot,
15231 position: Point,
15232 ) -> Option<MultiBufferRow> {
15233 snapshot
15234 .buffer_snapshot
15235 .diff_hunk_before(position)
15236 .or_else(|| snapshot.buffer_snapshot.diff_hunk_before(Point::MAX))
15237 }
15238
15239 fn go_to_next_change(
15240 &mut self,
15241 _: &GoToNextChange,
15242 window: &mut Window,
15243 cx: &mut Context<Self>,
15244 ) {
15245 if let Some(selections) = self
15246 .change_list
15247 .next_change(1, Direction::Next)
15248 .map(|s| s.to_vec())
15249 {
15250 self.change_selections(Default::default(), window, cx, |s| {
15251 let map = s.display_map();
15252 s.select_display_ranges(selections.iter().map(|a| {
15253 let point = a.to_display_point(&map);
15254 point..point
15255 }))
15256 })
15257 }
15258 }
15259
15260 fn go_to_previous_change(
15261 &mut self,
15262 _: &GoToPreviousChange,
15263 window: &mut Window,
15264 cx: &mut Context<Self>,
15265 ) {
15266 if let Some(selections) = self
15267 .change_list
15268 .next_change(1, Direction::Prev)
15269 .map(|s| s.to_vec())
15270 {
15271 self.change_selections(Default::default(), window, cx, |s| {
15272 let map = s.display_map();
15273 s.select_display_ranges(selections.iter().map(|a| {
15274 let point = a.to_display_point(&map);
15275 point..point
15276 }))
15277 })
15278 }
15279 }
15280
15281 fn go_to_line<T: 'static>(
15282 &mut self,
15283 position: Anchor,
15284 highlight_color: Option<Hsla>,
15285 window: &mut Window,
15286 cx: &mut Context<Self>,
15287 ) {
15288 let snapshot = self.snapshot(window, cx).display_snapshot;
15289 let position = position.to_point(&snapshot.buffer_snapshot);
15290 let start = snapshot
15291 .buffer_snapshot
15292 .clip_point(Point::new(position.row, 0), Bias::Left);
15293 let end = start + Point::new(1, 0);
15294 let start = snapshot.buffer_snapshot.anchor_before(start);
15295 let end = snapshot.buffer_snapshot.anchor_before(end);
15296
15297 self.highlight_rows::<T>(
15298 start..end,
15299 highlight_color
15300 .unwrap_or_else(|| cx.theme().colors().editor_highlighted_line_background),
15301 Default::default(),
15302 cx,
15303 );
15304
15305 if self.buffer.read(cx).is_singleton() {
15306 self.request_autoscroll(Autoscroll::center().for_anchor(start), cx);
15307 }
15308 }
15309
15310 pub fn go_to_definition(
15311 &mut self,
15312 _: &GoToDefinition,
15313 window: &mut Window,
15314 cx: &mut Context<Self>,
15315 ) -> Task<Result<Navigated>> {
15316 let definition =
15317 self.go_to_definition_of_kind(GotoDefinitionKind::Symbol, false, window, cx);
15318 let fallback_strategy = EditorSettings::get_global(cx).go_to_definition_fallback;
15319 cx.spawn_in(window, async move |editor, cx| {
15320 if definition.await? == Navigated::Yes {
15321 return Ok(Navigated::Yes);
15322 }
15323 match fallback_strategy {
15324 GoToDefinitionFallback::None => Ok(Navigated::No),
15325 GoToDefinitionFallback::FindAllReferences => {
15326 match editor.update_in(cx, |editor, window, cx| {
15327 editor.find_all_references(&FindAllReferences, window, cx)
15328 })? {
15329 Some(references) => references.await,
15330 None => Ok(Navigated::No),
15331 }
15332 }
15333 }
15334 })
15335 }
15336
15337 pub fn go_to_declaration(
15338 &mut self,
15339 _: &GoToDeclaration,
15340 window: &mut Window,
15341 cx: &mut Context<Self>,
15342 ) -> Task<Result<Navigated>> {
15343 self.go_to_definition_of_kind(GotoDefinitionKind::Declaration, false, window, cx)
15344 }
15345
15346 pub fn go_to_declaration_split(
15347 &mut self,
15348 _: &GoToDeclaration,
15349 window: &mut Window,
15350 cx: &mut Context<Self>,
15351 ) -> Task<Result<Navigated>> {
15352 self.go_to_definition_of_kind(GotoDefinitionKind::Declaration, true, window, cx)
15353 }
15354
15355 pub fn go_to_implementation(
15356 &mut self,
15357 _: &GoToImplementation,
15358 window: &mut Window,
15359 cx: &mut Context<Self>,
15360 ) -> Task<Result<Navigated>> {
15361 self.go_to_definition_of_kind(GotoDefinitionKind::Implementation, false, window, cx)
15362 }
15363
15364 pub fn go_to_implementation_split(
15365 &mut self,
15366 _: &GoToImplementationSplit,
15367 window: &mut Window,
15368 cx: &mut Context<Self>,
15369 ) -> Task<Result<Navigated>> {
15370 self.go_to_definition_of_kind(GotoDefinitionKind::Implementation, true, window, cx)
15371 }
15372
15373 pub fn go_to_type_definition(
15374 &mut self,
15375 _: &GoToTypeDefinition,
15376 window: &mut Window,
15377 cx: &mut Context<Self>,
15378 ) -> Task<Result<Navigated>> {
15379 self.go_to_definition_of_kind(GotoDefinitionKind::Type, false, window, cx)
15380 }
15381
15382 pub fn go_to_definition_split(
15383 &mut self,
15384 _: &GoToDefinitionSplit,
15385 window: &mut Window,
15386 cx: &mut Context<Self>,
15387 ) -> Task<Result<Navigated>> {
15388 self.go_to_definition_of_kind(GotoDefinitionKind::Symbol, true, window, cx)
15389 }
15390
15391 pub fn go_to_type_definition_split(
15392 &mut self,
15393 _: &GoToTypeDefinitionSplit,
15394 window: &mut Window,
15395 cx: &mut Context<Self>,
15396 ) -> Task<Result<Navigated>> {
15397 self.go_to_definition_of_kind(GotoDefinitionKind::Type, true, window, cx)
15398 }
15399
15400 fn go_to_definition_of_kind(
15401 &mut self,
15402 kind: GotoDefinitionKind,
15403 split: bool,
15404 window: &mut Window,
15405 cx: &mut Context<Self>,
15406 ) -> Task<Result<Navigated>> {
15407 let Some(provider) = self.semantics_provider.clone() else {
15408 return Task::ready(Ok(Navigated::No));
15409 };
15410 let head = self.selections.newest::<usize>(cx).head();
15411 let buffer = self.buffer.read(cx);
15412 let (buffer, head) = if let Some(text_anchor) = buffer.text_anchor_for_position(head, cx) {
15413 text_anchor
15414 } else {
15415 return Task::ready(Ok(Navigated::No));
15416 };
15417
15418 let Some(definitions) = provider.definitions(&buffer, head, kind, cx) else {
15419 return Task::ready(Ok(Navigated::No));
15420 };
15421
15422 cx.spawn_in(window, async move |editor, cx| {
15423 let definitions = definitions.await?;
15424 let navigated = editor
15425 .update_in(cx, |editor, window, cx| {
15426 editor.navigate_to_hover_links(
15427 Some(kind),
15428 definitions
15429 .into_iter()
15430 .filter(|location| {
15431 hover_links::exclude_link_to_position(&buffer, &head, location, cx)
15432 })
15433 .map(HoverLink::Text)
15434 .collect::<Vec<_>>(),
15435 split,
15436 window,
15437 cx,
15438 )
15439 })?
15440 .await?;
15441 anyhow::Ok(navigated)
15442 })
15443 }
15444
15445 pub fn open_url(&mut self, _: &OpenUrl, window: &mut Window, cx: &mut Context<Self>) {
15446 let selection = self.selections.newest_anchor();
15447 let head = selection.head();
15448 let tail = selection.tail();
15449
15450 let Some((buffer, start_position)) =
15451 self.buffer.read(cx).text_anchor_for_position(head, cx)
15452 else {
15453 return;
15454 };
15455
15456 let end_position = if head != tail {
15457 let Some((_, pos)) = self.buffer.read(cx).text_anchor_for_position(tail, cx) else {
15458 return;
15459 };
15460 Some(pos)
15461 } else {
15462 None
15463 };
15464
15465 let url_finder = cx.spawn_in(window, async move |editor, cx| {
15466 let url = if let Some(end_pos) = end_position {
15467 find_url_from_range(&buffer, start_position..end_pos, cx.clone())
15468 } else {
15469 find_url(&buffer, start_position, cx.clone()).map(|(_, url)| url)
15470 };
15471
15472 if let Some(url) = url {
15473 editor.update(cx, |_, cx| {
15474 cx.open_url(&url);
15475 })
15476 } else {
15477 Ok(())
15478 }
15479 });
15480
15481 url_finder.detach();
15482 }
15483
15484 pub fn open_selected_filename(
15485 &mut self,
15486 _: &OpenSelectedFilename,
15487 window: &mut Window,
15488 cx: &mut Context<Self>,
15489 ) {
15490 let Some(workspace) = self.workspace() else {
15491 return;
15492 };
15493
15494 let position = self.selections.newest_anchor().head();
15495
15496 let Some((buffer, buffer_position)) =
15497 self.buffer.read(cx).text_anchor_for_position(position, cx)
15498 else {
15499 return;
15500 };
15501
15502 let project = self.project.clone();
15503
15504 cx.spawn_in(window, async move |_, cx| {
15505 let result = find_file(&buffer, project, buffer_position, cx).await;
15506
15507 if let Some((_, path)) = result {
15508 workspace
15509 .update_in(cx, |workspace, window, cx| {
15510 workspace.open_resolved_path(path, window, cx)
15511 })?
15512 .await?;
15513 }
15514 anyhow::Ok(())
15515 })
15516 .detach();
15517 }
15518
15519 pub(crate) fn navigate_to_hover_links(
15520 &mut self,
15521 kind: Option<GotoDefinitionKind>,
15522 mut definitions: Vec<HoverLink>,
15523 split: bool,
15524 window: &mut Window,
15525 cx: &mut Context<Editor>,
15526 ) -> Task<Result<Navigated>> {
15527 // If there is one definition, just open it directly
15528 if definitions.len() == 1 {
15529 let definition = definitions.pop().unwrap();
15530
15531 enum TargetTaskResult {
15532 Location(Option<Location>),
15533 AlreadyNavigated,
15534 }
15535
15536 let target_task = match definition {
15537 HoverLink::Text(link) => {
15538 Task::ready(anyhow::Ok(TargetTaskResult::Location(Some(link.target))))
15539 }
15540 HoverLink::InlayHint(lsp_location, server_id) => {
15541 let computation =
15542 self.compute_target_location(lsp_location, server_id, window, cx);
15543 cx.background_spawn(async move {
15544 let location = computation.await?;
15545 Ok(TargetTaskResult::Location(location))
15546 })
15547 }
15548 HoverLink::Url(url) => {
15549 cx.open_url(&url);
15550 Task::ready(Ok(TargetTaskResult::AlreadyNavigated))
15551 }
15552 HoverLink::File(path) => {
15553 if let Some(workspace) = self.workspace() {
15554 cx.spawn_in(window, async move |_, cx| {
15555 workspace
15556 .update_in(cx, |workspace, window, cx| {
15557 workspace.open_resolved_path(path, window, cx)
15558 })?
15559 .await
15560 .map(|_| TargetTaskResult::AlreadyNavigated)
15561 })
15562 } else {
15563 Task::ready(Ok(TargetTaskResult::Location(None)))
15564 }
15565 }
15566 };
15567 cx.spawn_in(window, async move |editor, cx| {
15568 let target = match target_task.await.context("target resolution task")? {
15569 TargetTaskResult::AlreadyNavigated => return Ok(Navigated::Yes),
15570 TargetTaskResult::Location(None) => return Ok(Navigated::No),
15571 TargetTaskResult::Location(Some(target)) => target,
15572 };
15573
15574 editor.update_in(cx, |editor, window, cx| {
15575 let Some(workspace) = editor.workspace() else {
15576 return Navigated::No;
15577 };
15578 let pane = workspace.read(cx).active_pane().clone();
15579
15580 let range = target.range.to_point(target.buffer.read(cx));
15581 let range = editor.range_for_match(&range);
15582 let range = collapse_multiline_range(range);
15583
15584 if !split
15585 && Some(&target.buffer) == editor.buffer.read(cx).as_singleton().as_ref()
15586 {
15587 editor.go_to_singleton_buffer_range(range.clone(), window, cx);
15588 } else {
15589 window.defer(cx, move |window, cx| {
15590 let target_editor: Entity<Self> =
15591 workspace.update(cx, |workspace, cx| {
15592 let pane = if split {
15593 workspace.adjacent_pane(window, cx)
15594 } else {
15595 workspace.active_pane().clone()
15596 };
15597
15598 workspace.open_project_item(
15599 pane,
15600 target.buffer.clone(),
15601 true,
15602 true,
15603 window,
15604 cx,
15605 )
15606 });
15607 target_editor.update(cx, |target_editor, cx| {
15608 // When selecting a definition in a different buffer, disable the nav history
15609 // to avoid creating a history entry at the previous cursor location.
15610 pane.update(cx, |pane, _| pane.disable_history());
15611 target_editor.go_to_singleton_buffer_range(range, window, cx);
15612 pane.update(cx, |pane, _| pane.enable_history());
15613 });
15614 });
15615 }
15616 Navigated::Yes
15617 })
15618 })
15619 } else if !definitions.is_empty() {
15620 cx.spawn_in(window, async move |editor, cx| {
15621 let (title, location_tasks, workspace) = editor
15622 .update_in(cx, |editor, window, cx| {
15623 let tab_kind = match kind {
15624 Some(GotoDefinitionKind::Implementation) => "Implementations",
15625 _ => "Definitions",
15626 };
15627 let title = definitions
15628 .iter()
15629 .find_map(|definition| match definition {
15630 HoverLink::Text(link) => link.origin.as_ref().map(|origin| {
15631 let buffer = origin.buffer.read(cx);
15632 format!(
15633 "{} for {}",
15634 tab_kind,
15635 buffer
15636 .text_for_range(origin.range.clone())
15637 .collect::<String>()
15638 )
15639 }),
15640 HoverLink::InlayHint(_, _) => None,
15641 HoverLink::Url(_) => None,
15642 HoverLink::File(_) => None,
15643 })
15644 .unwrap_or(tab_kind.to_string());
15645 let location_tasks = definitions
15646 .into_iter()
15647 .map(|definition| match definition {
15648 HoverLink::Text(link) => Task::ready(Ok(Some(link.target))),
15649 HoverLink::InlayHint(lsp_location, server_id) => editor
15650 .compute_target_location(lsp_location, server_id, window, cx),
15651 HoverLink::Url(_) => Task::ready(Ok(None)),
15652 HoverLink::File(_) => Task::ready(Ok(None)),
15653 })
15654 .collect::<Vec<_>>();
15655 (title, location_tasks, editor.workspace().clone())
15656 })
15657 .context("location tasks preparation")?;
15658
15659 let locations: Vec<Location> = future::join_all(location_tasks)
15660 .await
15661 .into_iter()
15662 .filter_map(|location| location.transpose())
15663 .collect::<Result<_>>()
15664 .context("location tasks")?;
15665
15666 if locations.is_empty() {
15667 return Ok(Navigated::No);
15668 }
15669
15670 let Some(workspace) = workspace else {
15671 return Ok(Navigated::No);
15672 };
15673
15674 let opened = workspace
15675 .update_in(cx, |workspace, window, cx| {
15676 Self::open_locations_in_multibuffer(
15677 workspace,
15678 locations,
15679 title,
15680 split,
15681 MultibufferSelectionMode::First,
15682 window,
15683 cx,
15684 )
15685 })
15686 .ok();
15687
15688 anyhow::Ok(Navigated::from_bool(opened.is_some()))
15689 })
15690 } else {
15691 Task::ready(Ok(Navigated::No))
15692 }
15693 }
15694
15695 fn compute_target_location(
15696 &self,
15697 lsp_location: lsp::Location,
15698 server_id: LanguageServerId,
15699 window: &mut Window,
15700 cx: &mut Context<Self>,
15701 ) -> Task<anyhow::Result<Option<Location>>> {
15702 let Some(project) = self.project.clone() else {
15703 return Task::ready(Ok(None));
15704 };
15705
15706 cx.spawn_in(window, async move |editor, cx| {
15707 let location_task = editor.update(cx, |_, cx| {
15708 project.update(cx, |project, cx| {
15709 let language_server_name = project
15710 .language_server_statuses(cx)
15711 .find(|(id, _)| server_id == *id)
15712 .map(|(_, status)| LanguageServerName::from(status.name.as_str()));
15713 language_server_name.map(|language_server_name| {
15714 project.open_local_buffer_via_lsp(
15715 lsp_location.uri.clone(),
15716 server_id,
15717 language_server_name,
15718 cx,
15719 )
15720 })
15721 })
15722 })?;
15723 let location = match location_task {
15724 Some(task) => Some({
15725 let target_buffer_handle = task.await.context("open local buffer")?;
15726 let range = target_buffer_handle.read_with(cx, |target_buffer, _| {
15727 let target_start = target_buffer
15728 .clip_point_utf16(point_from_lsp(lsp_location.range.start), Bias::Left);
15729 let target_end = target_buffer
15730 .clip_point_utf16(point_from_lsp(lsp_location.range.end), Bias::Left);
15731 target_buffer.anchor_after(target_start)
15732 ..target_buffer.anchor_before(target_end)
15733 })?;
15734 Location {
15735 buffer: target_buffer_handle,
15736 range,
15737 }
15738 }),
15739 None => None,
15740 };
15741 Ok(location)
15742 })
15743 }
15744
15745 pub fn find_all_references(
15746 &mut self,
15747 _: &FindAllReferences,
15748 window: &mut Window,
15749 cx: &mut Context<Self>,
15750 ) -> Option<Task<Result<Navigated>>> {
15751 let selection = self.selections.newest::<usize>(cx);
15752 let multi_buffer = self.buffer.read(cx);
15753 let head = selection.head();
15754
15755 let multi_buffer_snapshot = multi_buffer.snapshot(cx);
15756 let head_anchor = multi_buffer_snapshot.anchor_at(
15757 head,
15758 if head < selection.tail() {
15759 Bias::Right
15760 } else {
15761 Bias::Left
15762 },
15763 );
15764
15765 match self
15766 .find_all_references_task_sources
15767 .binary_search_by(|anchor| anchor.cmp(&head_anchor, &multi_buffer_snapshot))
15768 {
15769 Ok(_) => {
15770 log::info!(
15771 "Ignoring repeated FindAllReferences invocation with the position of already running task"
15772 );
15773 return None;
15774 }
15775 Err(i) => {
15776 self.find_all_references_task_sources.insert(i, head_anchor);
15777 }
15778 }
15779
15780 let (buffer, head) = multi_buffer.text_anchor_for_position(head, cx)?;
15781 let workspace = self.workspace()?;
15782 let project = workspace.read(cx).project().clone();
15783 let references = project.update(cx, |project, cx| project.references(&buffer, head, cx));
15784 Some(cx.spawn_in(window, async move |editor, cx| {
15785 let _cleanup = cx.on_drop(&editor, move |editor, _| {
15786 if let Ok(i) = editor
15787 .find_all_references_task_sources
15788 .binary_search_by(|anchor| anchor.cmp(&head_anchor, &multi_buffer_snapshot))
15789 {
15790 editor.find_all_references_task_sources.remove(i);
15791 }
15792 });
15793
15794 let locations = references.await?;
15795 if locations.is_empty() {
15796 return anyhow::Ok(Navigated::No);
15797 }
15798
15799 workspace.update_in(cx, |workspace, window, cx| {
15800 let title = locations
15801 .first()
15802 .as_ref()
15803 .map(|location| {
15804 let buffer = location.buffer.read(cx);
15805 format!(
15806 "References to `{}`",
15807 buffer
15808 .text_for_range(location.range.clone())
15809 .collect::<String>()
15810 )
15811 })
15812 .unwrap();
15813 Self::open_locations_in_multibuffer(
15814 workspace,
15815 locations,
15816 title,
15817 false,
15818 MultibufferSelectionMode::First,
15819 window,
15820 cx,
15821 );
15822 Navigated::Yes
15823 })
15824 }))
15825 }
15826
15827 /// Opens a multibuffer with the given project locations in it
15828 pub fn open_locations_in_multibuffer(
15829 workspace: &mut Workspace,
15830 mut locations: Vec<Location>,
15831 title: String,
15832 split: bool,
15833 multibuffer_selection_mode: MultibufferSelectionMode,
15834 window: &mut Window,
15835 cx: &mut Context<Workspace>,
15836 ) {
15837 if locations.is_empty() {
15838 log::error!("bug: open_locations_in_multibuffer called with empty list of locations");
15839 return;
15840 }
15841
15842 // If there are multiple definitions, open them in a multibuffer
15843 locations.sort_by_key(|location| location.buffer.read(cx).remote_id());
15844 let mut locations = locations.into_iter().peekable();
15845 let mut ranges: Vec<Range<Anchor>> = Vec::new();
15846 let capability = workspace.project().read(cx).capability();
15847
15848 let excerpt_buffer = cx.new(|cx| {
15849 let mut multibuffer = MultiBuffer::new(capability);
15850 while let Some(location) = locations.next() {
15851 let buffer = location.buffer.read(cx);
15852 let mut ranges_for_buffer = Vec::new();
15853 let range = location.range.to_point(buffer);
15854 ranges_for_buffer.push(range.clone());
15855
15856 while let Some(next_location) = locations.peek() {
15857 if next_location.buffer == location.buffer {
15858 ranges_for_buffer.push(next_location.range.to_point(buffer));
15859 locations.next();
15860 } else {
15861 break;
15862 }
15863 }
15864
15865 ranges_for_buffer.sort_by_key(|range| (range.start, Reverse(range.end)));
15866 let (new_ranges, _) = multibuffer.set_excerpts_for_path(
15867 PathKey::for_buffer(&location.buffer, cx),
15868 location.buffer.clone(),
15869 ranges_for_buffer,
15870 DEFAULT_MULTIBUFFER_CONTEXT,
15871 cx,
15872 );
15873 ranges.extend(new_ranges)
15874 }
15875
15876 multibuffer.with_title(title)
15877 });
15878
15879 let editor = cx.new(|cx| {
15880 Editor::for_multibuffer(
15881 excerpt_buffer,
15882 Some(workspace.project().clone()),
15883 window,
15884 cx,
15885 )
15886 });
15887 editor.update(cx, |editor, cx| {
15888 match multibuffer_selection_mode {
15889 MultibufferSelectionMode::First => {
15890 if let Some(first_range) = ranges.first() {
15891 editor.change_selections(
15892 SelectionEffects::no_scroll(),
15893 window,
15894 cx,
15895 |selections| {
15896 selections.clear_disjoint();
15897 selections
15898 .select_anchor_ranges(std::iter::once(first_range.clone()));
15899 },
15900 );
15901 }
15902 editor.highlight_background::<Self>(
15903 &ranges,
15904 |theme| theme.colors().editor_highlighted_line_background,
15905 cx,
15906 );
15907 }
15908 MultibufferSelectionMode::All => {
15909 editor.change_selections(
15910 SelectionEffects::no_scroll(),
15911 window,
15912 cx,
15913 |selections| {
15914 selections.clear_disjoint();
15915 selections.select_anchor_ranges(ranges);
15916 },
15917 );
15918 }
15919 }
15920 editor.register_buffers_with_language_servers(cx);
15921 });
15922
15923 let item = Box::new(editor);
15924 let item_id = item.item_id();
15925
15926 if split {
15927 workspace.split_item(SplitDirection::Right, item.clone(), window, cx);
15928 } else {
15929 if PreviewTabsSettings::get_global(cx).enable_preview_from_code_navigation {
15930 let (preview_item_id, preview_item_idx) =
15931 workspace.active_pane().read_with(cx, |pane, _| {
15932 (pane.preview_item_id(), pane.preview_item_idx())
15933 });
15934
15935 workspace.add_item_to_active_pane(item.clone(), preview_item_idx, true, window, cx);
15936
15937 if let Some(preview_item_id) = preview_item_id {
15938 workspace.active_pane().update(cx, |pane, cx| {
15939 pane.remove_item(preview_item_id, false, false, window, cx);
15940 });
15941 }
15942 } else {
15943 workspace.add_item_to_active_pane(item.clone(), None, true, window, cx);
15944 }
15945 }
15946 workspace.active_pane().update(cx, |pane, cx| {
15947 pane.set_preview_item_id(Some(item_id), cx);
15948 });
15949 }
15950
15951 pub fn rename(
15952 &mut self,
15953 _: &Rename,
15954 window: &mut Window,
15955 cx: &mut Context<Self>,
15956 ) -> Option<Task<Result<()>>> {
15957 use language::ToOffset as _;
15958
15959 let provider = self.semantics_provider.clone()?;
15960 let selection = self.selections.newest_anchor().clone();
15961 let (cursor_buffer, cursor_buffer_position) = self
15962 .buffer
15963 .read(cx)
15964 .text_anchor_for_position(selection.head(), cx)?;
15965 let (tail_buffer, cursor_buffer_position_end) = self
15966 .buffer
15967 .read(cx)
15968 .text_anchor_for_position(selection.tail(), cx)?;
15969 if tail_buffer != cursor_buffer {
15970 return None;
15971 }
15972
15973 let snapshot = cursor_buffer.read(cx).snapshot();
15974 let cursor_buffer_offset = cursor_buffer_position.to_offset(&snapshot);
15975 let cursor_buffer_offset_end = cursor_buffer_position_end.to_offset(&snapshot);
15976 let prepare_rename = provider
15977 .range_for_rename(&cursor_buffer, cursor_buffer_position, cx)
15978 .unwrap_or_else(|| Task::ready(Ok(None)));
15979 drop(snapshot);
15980
15981 Some(cx.spawn_in(window, async move |this, cx| {
15982 let rename_range = if let Some(range) = prepare_rename.await? {
15983 Some(range)
15984 } else {
15985 this.update(cx, |this, cx| {
15986 let buffer = this.buffer.read(cx).snapshot(cx);
15987 let mut buffer_highlights = this
15988 .document_highlights_for_position(selection.head(), &buffer)
15989 .filter(|highlight| {
15990 highlight.start.excerpt_id == selection.head().excerpt_id
15991 && highlight.end.excerpt_id == selection.head().excerpt_id
15992 });
15993 buffer_highlights
15994 .next()
15995 .map(|highlight| highlight.start.text_anchor..highlight.end.text_anchor)
15996 })?
15997 };
15998 if let Some(rename_range) = rename_range {
15999 this.update_in(cx, |this, window, cx| {
16000 let snapshot = cursor_buffer.read(cx).snapshot();
16001 let rename_buffer_range = rename_range.to_offset(&snapshot);
16002 let cursor_offset_in_rename_range =
16003 cursor_buffer_offset.saturating_sub(rename_buffer_range.start);
16004 let cursor_offset_in_rename_range_end =
16005 cursor_buffer_offset_end.saturating_sub(rename_buffer_range.start);
16006
16007 this.take_rename(false, window, cx);
16008 let buffer = this.buffer.read(cx).read(cx);
16009 let cursor_offset = selection.head().to_offset(&buffer);
16010 let rename_start = cursor_offset.saturating_sub(cursor_offset_in_rename_range);
16011 let rename_end = rename_start + rename_buffer_range.len();
16012 let range = buffer.anchor_before(rename_start)..buffer.anchor_after(rename_end);
16013 let mut old_highlight_id = None;
16014 let old_name: Arc<str> = buffer
16015 .chunks(rename_start..rename_end, true)
16016 .map(|chunk| {
16017 if old_highlight_id.is_none() {
16018 old_highlight_id = chunk.syntax_highlight_id;
16019 }
16020 chunk.text
16021 })
16022 .collect::<String>()
16023 .into();
16024
16025 drop(buffer);
16026
16027 // Position the selection in the rename editor so that it matches the current selection.
16028 this.show_local_selections = false;
16029 let rename_editor = cx.new(|cx| {
16030 let mut editor = Editor::single_line(window, cx);
16031 editor.buffer.update(cx, |buffer, cx| {
16032 buffer.edit([(0..0, old_name.clone())], None, cx)
16033 });
16034 let rename_selection_range = match cursor_offset_in_rename_range
16035 .cmp(&cursor_offset_in_rename_range_end)
16036 {
16037 Ordering::Equal => {
16038 editor.select_all(&SelectAll, window, cx);
16039 return editor;
16040 }
16041 Ordering::Less => {
16042 cursor_offset_in_rename_range..cursor_offset_in_rename_range_end
16043 }
16044 Ordering::Greater => {
16045 cursor_offset_in_rename_range_end..cursor_offset_in_rename_range
16046 }
16047 };
16048 if rename_selection_range.end > old_name.len() {
16049 editor.select_all(&SelectAll, window, cx);
16050 } else {
16051 editor.change_selections(Default::default(), window, cx, |s| {
16052 s.select_ranges([rename_selection_range]);
16053 });
16054 }
16055 editor
16056 });
16057 cx.subscribe(&rename_editor, |_, _, e: &EditorEvent, cx| {
16058 if e == &EditorEvent::Focused {
16059 cx.emit(EditorEvent::FocusedIn)
16060 }
16061 })
16062 .detach();
16063
16064 let write_highlights =
16065 this.clear_background_highlights::<DocumentHighlightWrite>(cx);
16066 let read_highlights =
16067 this.clear_background_highlights::<DocumentHighlightRead>(cx);
16068 let ranges = write_highlights
16069 .iter()
16070 .flat_map(|(_, ranges)| ranges.iter())
16071 .chain(read_highlights.iter().flat_map(|(_, ranges)| ranges.iter()))
16072 .cloned()
16073 .collect();
16074
16075 this.highlight_text::<Rename>(
16076 ranges,
16077 HighlightStyle {
16078 fade_out: Some(0.6),
16079 ..Default::default()
16080 },
16081 cx,
16082 );
16083 let rename_focus_handle = rename_editor.focus_handle(cx);
16084 window.focus(&rename_focus_handle);
16085 let block_id = this.insert_blocks(
16086 [BlockProperties {
16087 style: BlockStyle::Flex,
16088 placement: BlockPlacement::Below(range.start),
16089 height: Some(1),
16090 render: Arc::new({
16091 let rename_editor = rename_editor.clone();
16092 move |cx: &mut BlockContext| {
16093 let mut text_style = cx.editor_style.text.clone();
16094 if let Some(highlight_style) = old_highlight_id
16095 .and_then(|h| h.style(&cx.editor_style.syntax))
16096 {
16097 text_style = text_style.highlight(highlight_style);
16098 }
16099 div()
16100 .block_mouse_except_scroll()
16101 .pl(cx.anchor_x)
16102 .child(EditorElement::new(
16103 &rename_editor,
16104 EditorStyle {
16105 background: cx.theme().system().transparent,
16106 local_player: cx.editor_style.local_player,
16107 text: text_style,
16108 scrollbar_width: cx.editor_style.scrollbar_width,
16109 syntax: cx.editor_style.syntax.clone(),
16110 status: cx.editor_style.status.clone(),
16111 inlay_hints_style: HighlightStyle {
16112 font_weight: Some(FontWeight::BOLD),
16113 ..make_inlay_hints_style(cx.app)
16114 },
16115 inline_completion_styles: make_suggestion_styles(
16116 cx.app,
16117 ),
16118 ..EditorStyle::default()
16119 },
16120 ))
16121 .into_any_element()
16122 }
16123 }),
16124 priority: 0,
16125 render_in_minimap: true,
16126 }],
16127 Some(Autoscroll::fit()),
16128 cx,
16129 )[0];
16130 this.pending_rename = Some(RenameState {
16131 range,
16132 old_name,
16133 editor: rename_editor,
16134 block_id,
16135 });
16136 })?;
16137 }
16138
16139 Ok(())
16140 }))
16141 }
16142
16143 pub fn confirm_rename(
16144 &mut self,
16145 _: &ConfirmRename,
16146 window: &mut Window,
16147 cx: &mut Context<Self>,
16148 ) -> Option<Task<Result<()>>> {
16149 let rename = self.take_rename(false, window, cx)?;
16150 let workspace = self.workspace()?.downgrade();
16151 let (buffer, start) = self
16152 .buffer
16153 .read(cx)
16154 .text_anchor_for_position(rename.range.start, cx)?;
16155 let (end_buffer, _) = self
16156 .buffer
16157 .read(cx)
16158 .text_anchor_for_position(rename.range.end, cx)?;
16159 if buffer != end_buffer {
16160 return None;
16161 }
16162
16163 let old_name = rename.old_name;
16164 let new_name = rename.editor.read(cx).text(cx);
16165
16166 let rename = self.semantics_provider.as_ref()?.perform_rename(
16167 &buffer,
16168 start,
16169 new_name.clone(),
16170 cx,
16171 )?;
16172
16173 Some(cx.spawn_in(window, async move |editor, cx| {
16174 let project_transaction = rename.await?;
16175 Self::open_project_transaction(
16176 &editor,
16177 workspace,
16178 project_transaction,
16179 format!("Rename: {} → {}", old_name, new_name),
16180 cx,
16181 )
16182 .await?;
16183
16184 editor.update(cx, |editor, cx| {
16185 editor.refresh_document_highlights(cx);
16186 })?;
16187 Ok(())
16188 }))
16189 }
16190
16191 fn take_rename(
16192 &mut self,
16193 moving_cursor: bool,
16194 window: &mut Window,
16195 cx: &mut Context<Self>,
16196 ) -> Option<RenameState> {
16197 let rename = self.pending_rename.take()?;
16198 if rename.editor.focus_handle(cx).is_focused(window) {
16199 window.focus(&self.focus_handle);
16200 }
16201
16202 self.remove_blocks(
16203 [rename.block_id].into_iter().collect(),
16204 Some(Autoscroll::fit()),
16205 cx,
16206 );
16207 self.clear_highlights::<Rename>(cx);
16208 self.show_local_selections = true;
16209
16210 if moving_cursor {
16211 let cursor_in_rename_editor = rename.editor.update(cx, |editor, cx| {
16212 editor.selections.newest::<usize>(cx).head()
16213 });
16214
16215 // Update the selection to match the position of the selection inside
16216 // the rename editor.
16217 let snapshot = self.buffer.read(cx).read(cx);
16218 let rename_range = rename.range.to_offset(&snapshot);
16219 let cursor_in_editor = snapshot
16220 .clip_offset(rename_range.start + cursor_in_rename_editor, Bias::Left)
16221 .min(rename_range.end);
16222 drop(snapshot);
16223
16224 self.change_selections(SelectionEffects::no_scroll(), window, cx, |s| {
16225 s.select_ranges(vec![cursor_in_editor..cursor_in_editor])
16226 });
16227 } else {
16228 self.refresh_document_highlights(cx);
16229 }
16230
16231 Some(rename)
16232 }
16233
16234 pub fn pending_rename(&self) -> Option<&RenameState> {
16235 self.pending_rename.as_ref()
16236 }
16237
16238 fn format(
16239 &mut self,
16240 _: &Format,
16241 window: &mut Window,
16242 cx: &mut Context<Self>,
16243 ) -> Option<Task<Result<()>>> {
16244 self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
16245
16246 let project = match &self.project {
16247 Some(project) => project.clone(),
16248 None => return None,
16249 };
16250
16251 Some(self.perform_format(
16252 project,
16253 FormatTrigger::Manual,
16254 FormatTarget::Buffers(self.buffer.read(cx).all_buffers()),
16255 window,
16256 cx,
16257 ))
16258 }
16259
16260 fn format_selections(
16261 &mut self,
16262 _: &FormatSelections,
16263 window: &mut Window,
16264 cx: &mut Context<Self>,
16265 ) -> Option<Task<Result<()>>> {
16266 self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
16267
16268 let project = match &self.project {
16269 Some(project) => project.clone(),
16270 None => return None,
16271 };
16272
16273 let ranges = self
16274 .selections
16275 .all_adjusted(cx)
16276 .into_iter()
16277 .map(|selection| selection.range())
16278 .collect_vec();
16279
16280 Some(self.perform_format(
16281 project,
16282 FormatTrigger::Manual,
16283 FormatTarget::Ranges(ranges),
16284 window,
16285 cx,
16286 ))
16287 }
16288
16289 fn perform_format(
16290 &mut self,
16291 project: Entity<Project>,
16292 trigger: FormatTrigger,
16293 target: FormatTarget,
16294 window: &mut Window,
16295 cx: &mut Context<Self>,
16296 ) -> Task<Result<()>> {
16297 let buffer = self.buffer.clone();
16298 let (buffers, target) = match target {
16299 FormatTarget::Buffers(buffers) => (buffers, LspFormatTarget::Buffers),
16300 FormatTarget::Ranges(selection_ranges) => {
16301 let multi_buffer = buffer.read(cx);
16302 let snapshot = multi_buffer.read(cx);
16303 let mut buffers = HashSet::default();
16304 let mut buffer_id_to_ranges: BTreeMap<BufferId, Vec<Range<text::Anchor>>> =
16305 BTreeMap::new();
16306 for selection_range in selection_ranges {
16307 for (buffer, buffer_range, _) in
16308 snapshot.range_to_buffer_ranges(selection_range)
16309 {
16310 let buffer_id = buffer.remote_id();
16311 let start = buffer.anchor_before(buffer_range.start);
16312 let end = buffer.anchor_after(buffer_range.end);
16313 buffers.insert(multi_buffer.buffer(buffer_id).unwrap());
16314 buffer_id_to_ranges
16315 .entry(buffer_id)
16316 .and_modify(|buffer_ranges| buffer_ranges.push(start..end))
16317 .or_insert_with(|| vec![start..end]);
16318 }
16319 }
16320 (buffers, LspFormatTarget::Ranges(buffer_id_to_ranges))
16321 }
16322 };
16323
16324 let transaction_id_prev = buffer.read(cx).last_transaction_id(cx);
16325 let selections_prev = transaction_id_prev
16326 .and_then(|transaction_id_prev| {
16327 // default to selections as they were after the last edit, if we have them,
16328 // instead of how they are now.
16329 // This will make it so that editing, moving somewhere else, formatting, then undoing the format
16330 // will take you back to where you made the last edit, instead of staying where you scrolled
16331 self.selection_history
16332 .transaction(transaction_id_prev)
16333 .map(|t| t.0.clone())
16334 })
16335 .unwrap_or_else(|| {
16336 log::info!("Failed to determine selections from before format. Falling back to selections when format was initiated");
16337 self.selections.disjoint_anchors()
16338 });
16339
16340 let mut timeout = cx.background_executor().timer(FORMAT_TIMEOUT).fuse();
16341 let format = project.update(cx, |project, cx| {
16342 project.format(buffers, target, true, trigger, cx)
16343 });
16344
16345 cx.spawn_in(window, async move |editor, cx| {
16346 let transaction = futures::select_biased! {
16347 transaction = format.log_err().fuse() => transaction,
16348 () = timeout => {
16349 log::warn!("timed out waiting for formatting");
16350 None
16351 }
16352 };
16353
16354 buffer
16355 .update(cx, |buffer, cx| {
16356 if let Some(transaction) = transaction {
16357 if !buffer.is_singleton() {
16358 buffer.push_transaction(&transaction.0, cx);
16359 }
16360 }
16361 cx.notify();
16362 })
16363 .ok();
16364
16365 if let Some(transaction_id_now) =
16366 buffer.read_with(cx, |b, cx| b.last_transaction_id(cx))?
16367 {
16368 let has_new_transaction = transaction_id_prev != Some(transaction_id_now);
16369 if has_new_transaction {
16370 _ = editor.update(cx, |editor, _| {
16371 editor
16372 .selection_history
16373 .insert_transaction(transaction_id_now, selections_prev);
16374 });
16375 }
16376 }
16377
16378 Ok(())
16379 })
16380 }
16381
16382 fn organize_imports(
16383 &mut self,
16384 _: &OrganizeImports,
16385 window: &mut Window,
16386 cx: &mut Context<Self>,
16387 ) -> Option<Task<Result<()>>> {
16388 self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
16389 let project = match &self.project {
16390 Some(project) => project.clone(),
16391 None => return None,
16392 };
16393 Some(self.perform_code_action_kind(
16394 project,
16395 CodeActionKind::SOURCE_ORGANIZE_IMPORTS,
16396 window,
16397 cx,
16398 ))
16399 }
16400
16401 fn perform_code_action_kind(
16402 &mut self,
16403 project: Entity<Project>,
16404 kind: CodeActionKind,
16405 window: &mut Window,
16406 cx: &mut Context<Self>,
16407 ) -> Task<Result<()>> {
16408 let buffer = self.buffer.clone();
16409 let buffers = buffer.read(cx).all_buffers();
16410 let mut timeout = cx.background_executor().timer(CODE_ACTION_TIMEOUT).fuse();
16411 let apply_action = project.update(cx, |project, cx| {
16412 project.apply_code_action_kind(buffers, kind, true, cx)
16413 });
16414 cx.spawn_in(window, async move |_, cx| {
16415 let transaction = futures::select_biased! {
16416 () = timeout => {
16417 log::warn!("timed out waiting for executing code action");
16418 None
16419 }
16420 transaction = apply_action.log_err().fuse() => transaction,
16421 };
16422 buffer
16423 .update(cx, |buffer, cx| {
16424 // check if we need this
16425 if let Some(transaction) = transaction {
16426 if !buffer.is_singleton() {
16427 buffer.push_transaction(&transaction.0, cx);
16428 }
16429 }
16430 cx.notify();
16431 })
16432 .ok();
16433 Ok(())
16434 })
16435 }
16436
16437 pub fn restart_language_server(
16438 &mut self,
16439 _: &RestartLanguageServer,
16440 _: &mut Window,
16441 cx: &mut Context<Self>,
16442 ) {
16443 if let Some(project) = self.project.clone() {
16444 self.buffer.update(cx, |multi_buffer, cx| {
16445 project.update(cx, |project, cx| {
16446 project.restart_language_servers_for_buffers(
16447 multi_buffer.all_buffers().into_iter().collect(),
16448 HashSet::default(),
16449 cx,
16450 );
16451 });
16452 })
16453 }
16454 }
16455
16456 pub fn stop_language_server(
16457 &mut self,
16458 _: &StopLanguageServer,
16459 _: &mut Window,
16460 cx: &mut Context<Self>,
16461 ) {
16462 if let Some(project) = self.project.clone() {
16463 self.buffer.update(cx, |multi_buffer, cx| {
16464 project.update(cx, |project, cx| {
16465 project.stop_language_servers_for_buffers(
16466 multi_buffer.all_buffers().into_iter().collect(),
16467 HashSet::default(),
16468 cx,
16469 );
16470 cx.emit(project::Event::RefreshInlayHints);
16471 });
16472 });
16473 }
16474 }
16475
16476 fn cancel_language_server_work(
16477 workspace: &mut Workspace,
16478 _: &actions::CancelLanguageServerWork,
16479 _: &mut Window,
16480 cx: &mut Context<Workspace>,
16481 ) {
16482 let project = workspace.project();
16483 let buffers = workspace
16484 .active_item(cx)
16485 .and_then(|item| item.act_as::<Editor>(cx))
16486 .map_or(HashSet::default(), |editor| {
16487 editor.read(cx).buffer.read(cx).all_buffers()
16488 });
16489 project.update(cx, |project, cx| {
16490 project.cancel_language_server_work_for_buffers(buffers, cx);
16491 });
16492 }
16493
16494 fn show_character_palette(
16495 &mut self,
16496 _: &ShowCharacterPalette,
16497 window: &mut Window,
16498 _: &mut Context<Self>,
16499 ) {
16500 window.show_character_palette();
16501 }
16502
16503 fn refresh_active_diagnostics(&mut self, cx: &mut Context<Editor>) {
16504 if !self.diagnostics_enabled() {
16505 return;
16506 }
16507
16508 if let ActiveDiagnostic::Group(active_diagnostics) = &mut self.active_diagnostics {
16509 let buffer = self.buffer.read(cx).snapshot(cx);
16510 let primary_range_start = active_diagnostics.active_range.start.to_offset(&buffer);
16511 let primary_range_end = active_diagnostics.active_range.end.to_offset(&buffer);
16512 let is_valid = buffer
16513 .diagnostics_in_range::<usize>(primary_range_start..primary_range_end)
16514 .any(|entry| {
16515 entry.diagnostic.is_primary
16516 && !entry.range.is_empty()
16517 && entry.range.start == primary_range_start
16518 && entry.diagnostic.message == active_diagnostics.active_message
16519 });
16520
16521 if !is_valid {
16522 self.dismiss_diagnostics(cx);
16523 }
16524 }
16525 }
16526
16527 pub fn active_diagnostic_group(&self) -> Option<&ActiveDiagnosticGroup> {
16528 match &self.active_diagnostics {
16529 ActiveDiagnostic::Group(group) => Some(group),
16530 _ => None,
16531 }
16532 }
16533
16534 pub fn set_all_diagnostics_active(&mut self, cx: &mut Context<Self>) {
16535 if !self.diagnostics_enabled() {
16536 return;
16537 }
16538 self.dismiss_diagnostics(cx);
16539 self.active_diagnostics = ActiveDiagnostic::All;
16540 }
16541
16542 fn activate_diagnostics(
16543 &mut self,
16544 buffer_id: BufferId,
16545 diagnostic: DiagnosticEntry<usize>,
16546 window: &mut Window,
16547 cx: &mut Context<Self>,
16548 ) {
16549 if !self.diagnostics_enabled() || matches!(self.active_diagnostics, ActiveDiagnostic::All) {
16550 return;
16551 }
16552 self.dismiss_diagnostics(cx);
16553 let snapshot = self.snapshot(window, cx);
16554 let buffer = self.buffer.read(cx).snapshot(cx);
16555 let Some(renderer) = GlobalDiagnosticRenderer::global(cx) else {
16556 return;
16557 };
16558
16559 let diagnostic_group = buffer
16560 .diagnostic_group(buffer_id, diagnostic.diagnostic.group_id)
16561 .collect::<Vec<_>>();
16562
16563 let blocks =
16564 renderer.render_group(diagnostic_group, buffer_id, snapshot, cx.weak_entity(), cx);
16565
16566 let blocks = self.display_map.update(cx, |display_map, cx| {
16567 display_map.insert_blocks(blocks, cx).into_iter().collect()
16568 });
16569 self.active_diagnostics = ActiveDiagnostic::Group(ActiveDiagnosticGroup {
16570 active_range: buffer.anchor_before(diagnostic.range.start)
16571 ..buffer.anchor_after(diagnostic.range.end),
16572 active_message: diagnostic.diagnostic.message.clone(),
16573 group_id: diagnostic.diagnostic.group_id,
16574 blocks,
16575 });
16576 cx.notify();
16577 }
16578
16579 fn dismiss_diagnostics(&mut self, cx: &mut Context<Self>) {
16580 if matches!(self.active_diagnostics, ActiveDiagnostic::All) {
16581 return;
16582 };
16583
16584 let prev = mem::replace(&mut self.active_diagnostics, ActiveDiagnostic::None);
16585 if let ActiveDiagnostic::Group(group) = prev {
16586 self.display_map.update(cx, |display_map, cx| {
16587 display_map.remove_blocks(group.blocks, cx);
16588 });
16589 cx.notify();
16590 }
16591 }
16592
16593 /// Disable inline diagnostics rendering for this editor.
16594 pub fn disable_inline_diagnostics(&mut self) {
16595 self.inline_diagnostics_enabled = false;
16596 self.inline_diagnostics_update = Task::ready(());
16597 self.inline_diagnostics.clear();
16598 }
16599
16600 pub fn disable_diagnostics(&mut self, cx: &mut Context<Self>) {
16601 self.diagnostics_enabled = false;
16602 self.dismiss_diagnostics(cx);
16603 self.inline_diagnostics_update = Task::ready(());
16604 self.inline_diagnostics.clear();
16605 }
16606
16607 pub fn diagnostics_enabled(&self) -> bool {
16608 self.diagnostics_enabled && self.mode.is_full()
16609 }
16610
16611 pub fn inline_diagnostics_enabled(&self) -> bool {
16612 self.inline_diagnostics_enabled && self.diagnostics_enabled()
16613 }
16614
16615 pub fn show_inline_diagnostics(&self) -> bool {
16616 self.show_inline_diagnostics
16617 }
16618
16619 pub fn toggle_inline_diagnostics(
16620 &mut self,
16621 _: &ToggleInlineDiagnostics,
16622 window: &mut Window,
16623 cx: &mut Context<Editor>,
16624 ) {
16625 self.show_inline_diagnostics = !self.show_inline_diagnostics;
16626 self.refresh_inline_diagnostics(false, window, cx);
16627 }
16628
16629 pub fn set_max_diagnostics_severity(&mut self, severity: DiagnosticSeverity, cx: &mut App) {
16630 self.diagnostics_max_severity = severity;
16631 self.display_map.update(cx, |display_map, _| {
16632 display_map.diagnostics_max_severity = self.diagnostics_max_severity;
16633 });
16634 }
16635
16636 pub fn toggle_diagnostics(
16637 &mut self,
16638 _: &ToggleDiagnostics,
16639 window: &mut Window,
16640 cx: &mut Context<Editor>,
16641 ) {
16642 if !self.diagnostics_enabled() {
16643 return;
16644 }
16645
16646 let new_severity = if self.diagnostics_max_severity == DiagnosticSeverity::Off {
16647 EditorSettings::get_global(cx)
16648 .diagnostics_max_severity
16649 .filter(|severity| severity != &DiagnosticSeverity::Off)
16650 .unwrap_or(DiagnosticSeverity::Hint)
16651 } else {
16652 DiagnosticSeverity::Off
16653 };
16654 self.set_max_diagnostics_severity(new_severity, cx);
16655 if self.diagnostics_max_severity == DiagnosticSeverity::Off {
16656 self.active_diagnostics = ActiveDiagnostic::None;
16657 self.inline_diagnostics_update = Task::ready(());
16658 self.inline_diagnostics.clear();
16659 } else {
16660 self.refresh_inline_diagnostics(false, window, cx);
16661 }
16662
16663 cx.notify();
16664 }
16665
16666 pub fn toggle_minimap(
16667 &mut self,
16668 _: &ToggleMinimap,
16669 window: &mut Window,
16670 cx: &mut Context<Editor>,
16671 ) {
16672 if self.supports_minimap(cx) {
16673 self.set_minimap_visibility(self.minimap_visibility.toggle_visibility(), window, cx);
16674 }
16675 }
16676
16677 fn refresh_inline_diagnostics(
16678 &mut self,
16679 debounce: bool,
16680 window: &mut Window,
16681 cx: &mut Context<Self>,
16682 ) {
16683 let max_severity = ProjectSettings::get_global(cx)
16684 .diagnostics
16685 .inline
16686 .max_severity
16687 .unwrap_or(self.diagnostics_max_severity);
16688
16689 if !self.inline_diagnostics_enabled()
16690 || !self.show_inline_diagnostics
16691 || max_severity == DiagnosticSeverity::Off
16692 {
16693 self.inline_diagnostics_update = Task::ready(());
16694 self.inline_diagnostics.clear();
16695 return;
16696 }
16697
16698 let debounce_ms = ProjectSettings::get_global(cx)
16699 .diagnostics
16700 .inline
16701 .update_debounce_ms;
16702 let debounce = if debounce && debounce_ms > 0 {
16703 Some(Duration::from_millis(debounce_ms))
16704 } else {
16705 None
16706 };
16707 self.inline_diagnostics_update = cx.spawn_in(window, async move |editor, cx| {
16708 if let Some(debounce) = debounce {
16709 cx.background_executor().timer(debounce).await;
16710 }
16711 let Some(snapshot) = editor.upgrade().and_then(|editor| {
16712 editor
16713 .update(cx, |editor, cx| editor.buffer().read(cx).snapshot(cx))
16714 .ok()
16715 }) else {
16716 return;
16717 };
16718
16719 let new_inline_diagnostics = cx
16720 .background_spawn(async move {
16721 let mut inline_diagnostics = Vec::<(Anchor, InlineDiagnostic)>::new();
16722 for diagnostic_entry in snapshot.diagnostics_in_range(0..snapshot.len()) {
16723 let message = diagnostic_entry
16724 .diagnostic
16725 .message
16726 .split_once('\n')
16727 .map(|(line, _)| line)
16728 .map(SharedString::new)
16729 .unwrap_or_else(|| {
16730 SharedString::from(diagnostic_entry.diagnostic.message)
16731 });
16732 let start_anchor = snapshot.anchor_before(diagnostic_entry.range.start);
16733 let (Ok(i) | Err(i)) = inline_diagnostics
16734 .binary_search_by(|(probe, _)| probe.cmp(&start_anchor, &snapshot));
16735 inline_diagnostics.insert(
16736 i,
16737 (
16738 start_anchor,
16739 InlineDiagnostic {
16740 message,
16741 group_id: diagnostic_entry.diagnostic.group_id,
16742 start: diagnostic_entry.range.start.to_point(&snapshot),
16743 is_primary: diagnostic_entry.diagnostic.is_primary,
16744 severity: diagnostic_entry.diagnostic.severity,
16745 },
16746 ),
16747 );
16748 }
16749 inline_diagnostics
16750 })
16751 .await;
16752
16753 editor
16754 .update(cx, |editor, cx| {
16755 editor.inline_diagnostics = new_inline_diagnostics;
16756 cx.notify();
16757 })
16758 .ok();
16759 });
16760 }
16761
16762 fn pull_diagnostics(
16763 &mut self,
16764 buffer_id: Option<BufferId>,
16765 window: &Window,
16766 cx: &mut Context<Self>,
16767 ) -> Option<()> {
16768 if !self.mode().is_full() {
16769 return None;
16770 }
16771 let pull_diagnostics_settings = ProjectSettings::get_global(cx)
16772 .diagnostics
16773 .lsp_pull_diagnostics;
16774 if !pull_diagnostics_settings.enabled {
16775 return None;
16776 }
16777 let project = self.project.as_ref()?.downgrade();
16778 let debounce = Duration::from_millis(pull_diagnostics_settings.debounce_ms);
16779 let mut buffers = self.buffer.read(cx).all_buffers();
16780 if let Some(buffer_id) = buffer_id {
16781 buffers.retain(|buffer| buffer.read(cx).remote_id() == buffer_id);
16782 }
16783
16784 self.pull_diagnostics_task = cx.spawn_in(window, async move |editor, cx| {
16785 cx.background_executor().timer(debounce).await;
16786
16787 let Ok(mut pull_diagnostics_tasks) = cx.update(|_, cx| {
16788 buffers
16789 .into_iter()
16790 .filter_map(|buffer| {
16791 project
16792 .update(cx, |project, cx| {
16793 project.lsp_store().update(cx, |lsp_store, cx| {
16794 lsp_store.pull_diagnostics_for_buffer(buffer, cx)
16795 })
16796 })
16797 .ok()
16798 })
16799 .collect::<FuturesUnordered<_>>()
16800 }) else {
16801 return;
16802 };
16803
16804 while let Some(pull_task) = pull_diagnostics_tasks.next().await {
16805 match pull_task {
16806 Ok(()) => {
16807 if editor
16808 .update_in(cx, |editor, window, cx| {
16809 editor.update_diagnostics_state(window, cx);
16810 })
16811 .is_err()
16812 {
16813 return;
16814 }
16815 }
16816 Err(e) => log::error!("Failed to update project diagnostics: {e:#}"),
16817 }
16818 }
16819 });
16820
16821 Some(())
16822 }
16823
16824 pub fn set_selections_from_remote(
16825 &mut self,
16826 selections: Vec<Selection<Anchor>>,
16827 pending_selection: Option<Selection<Anchor>>,
16828 window: &mut Window,
16829 cx: &mut Context<Self>,
16830 ) {
16831 let old_cursor_position = self.selections.newest_anchor().head();
16832 self.selections.change_with(cx, |s| {
16833 s.select_anchors(selections);
16834 if let Some(pending_selection) = pending_selection {
16835 s.set_pending(pending_selection, SelectMode::Character);
16836 } else {
16837 s.clear_pending();
16838 }
16839 });
16840 self.selections_did_change(
16841 false,
16842 &old_cursor_position,
16843 SelectionEffects::default(),
16844 window,
16845 cx,
16846 );
16847 }
16848
16849 pub fn transact(
16850 &mut self,
16851 window: &mut Window,
16852 cx: &mut Context<Self>,
16853 update: impl FnOnce(&mut Self, &mut Window, &mut Context<Self>),
16854 ) -> Option<TransactionId> {
16855 self.with_selection_effects_deferred(window, cx, |this, window, cx| {
16856 this.start_transaction_at(Instant::now(), window, cx);
16857 update(this, window, cx);
16858 this.end_transaction_at(Instant::now(), cx)
16859 })
16860 }
16861
16862 pub fn start_transaction_at(
16863 &mut self,
16864 now: Instant,
16865 window: &mut Window,
16866 cx: &mut Context<Self>,
16867 ) {
16868 self.end_selection(window, cx);
16869 if let Some(tx_id) = self
16870 .buffer
16871 .update(cx, |buffer, cx| buffer.start_transaction_at(now, cx))
16872 {
16873 self.selection_history
16874 .insert_transaction(tx_id, self.selections.disjoint_anchors());
16875 cx.emit(EditorEvent::TransactionBegun {
16876 transaction_id: tx_id,
16877 })
16878 }
16879 }
16880
16881 pub fn end_transaction_at(
16882 &mut self,
16883 now: Instant,
16884 cx: &mut Context<Self>,
16885 ) -> Option<TransactionId> {
16886 if let Some(transaction_id) = self
16887 .buffer
16888 .update(cx, |buffer, cx| buffer.end_transaction_at(now, cx))
16889 {
16890 if let Some((_, end_selections)) =
16891 self.selection_history.transaction_mut(transaction_id)
16892 {
16893 *end_selections = Some(self.selections.disjoint_anchors());
16894 } else {
16895 log::error!("unexpectedly ended a transaction that wasn't started by this editor");
16896 }
16897
16898 cx.emit(EditorEvent::Edited { transaction_id });
16899 Some(transaction_id)
16900 } else {
16901 None
16902 }
16903 }
16904
16905 pub fn set_mark(&mut self, _: &actions::SetMark, window: &mut Window, cx: &mut Context<Self>) {
16906 if self.selection_mark_mode {
16907 self.change_selections(SelectionEffects::no_scroll(), window, cx, |s| {
16908 s.move_with(|_, sel| {
16909 sel.collapse_to(sel.head(), SelectionGoal::None);
16910 });
16911 })
16912 }
16913 self.selection_mark_mode = true;
16914 cx.notify();
16915 }
16916
16917 pub fn swap_selection_ends(
16918 &mut self,
16919 _: &actions::SwapSelectionEnds,
16920 window: &mut Window,
16921 cx: &mut Context<Self>,
16922 ) {
16923 self.change_selections(SelectionEffects::no_scroll(), window, cx, |s| {
16924 s.move_with(|_, sel| {
16925 if sel.start != sel.end {
16926 sel.reversed = !sel.reversed
16927 }
16928 });
16929 });
16930 self.request_autoscroll(Autoscroll::newest(), cx);
16931 cx.notify();
16932 }
16933
16934 pub fn toggle_fold(
16935 &mut self,
16936 _: &actions::ToggleFold,
16937 window: &mut Window,
16938 cx: &mut Context<Self>,
16939 ) {
16940 if self.is_singleton(cx) {
16941 let selection = self.selections.newest::<Point>(cx);
16942
16943 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
16944 let range = if selection.is_empty() {
16945 let point = selection.head().to_display_point(&display_map);
16946 let start = DisplayPoint::new(point.row(), 0).to_point(&display_map);
16947 let end = DisplayPoint::new(point.row(), display_map.line_len(point.row()))
16948 .to_point(&display_map);
16949 start..end
16950 } else {
16951 selection.range()
16952 };
16953 if display_map.folds_in_range(range).next().is_some() {
16954 self.unfold_lines(&Default::default(), window, cx)
16955 } else {
16956 self.fold(&Default::default(), window, cx)
16957 }
16958 } else {
16959 let multi_buffer_snapshot = self.buffer.read(cx).snapshot(cx);
16960 let buffer_ids: HashSet<_> = self
16961 .selections
16962 .disjoint_anchor_ranges()
16963 .flat_map(|range| multi_buffer_snapshot.buffer_ids_for_range(range))
16964 .collect();
16965
16966 let should_unfold = buffer_ids
16967 .iter()
16968 .any(|buffer_id| self.is_buffer_folded(*buffer_id, cx));
16969
16970 for buffer_id in buffer_ids {
16971 if should_unfold {
16972 self.unfold_buffer(buffer_id, cx);
16973 } else {
16974 self.fold_buffer(buffer_id, cx);
16975 }
16976 }
16977 }
16978 }
16979
16980 pub fn toggle_fold_recursive(
16981 &mut self,
16982 _: &actions::ToggleFoldRecursive,
16983 window: &mut Window,
16984 cx: &mut Context<Self>,
16985 ) {
16986 let selection = self.selections.newest::<Point>(cx);
16987
16988 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
16989 let range = if selection.is_empty() {
16990 let point = selection.head().to_display_point(&display_map);
16991 let start = DisplayPoint::new(point.row(), 0).to_point(&display_map);
16992 let end = DisplayPoint::new(point.row(), display_map.line_len(point.row()))
16993 .to_point(&display_map);
16994 start..end
16995 } else {
16996 selection.range()
16997 };
16998 if display_map.folds_in_range(range).next().is_some() {
16999 self.unfold_recursive(&Default::default(), window, cx)
17000 } else {
17001 self.fold_recursive(&Default::default(), window, cx)
17002 }
17003 }
17004
17005 pub fn fold(&mut self, _: &actions::Fold, window: &mut Window, cx: &mut Context<Self>) {
17006 if self.is_singleton(cx) {
17007 let mut to_fold = Vec::new();
17008 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
17009 let selections = self.selections.all_adjusted(cx);
17010
17011 for selection in selections {
17012 let range = selection.range().sorted();
17013 let buffer_start_row = range.start.row;
17014
17015 if range.start.row != range.end.row {
17016 let mut found = false;
17017 let mut row = range.start.row;
17018 while row <= range.end.row {
17019 if let Some(crease) = display_map.crease_for_buffer_row(MultiBufferRow(row))
17020 {
17021 found = true;
17022 row = crease.range().end.row + 1;
17023 to_fold.push(crease);
17024 } else {
17025 row += 1
17026 }
17027 }
17028 if found {
17029 continue;
17030 }
17031 }
17032
17033 for row in (0..=range.start.row).rev() {
17034 if let Some(crease) = display_map.crease_for_buffer_row(MultiBufferRow(row)) {
17035 if crease.range().end.row >= buffer_start_row {
17036 to_fold.push(crease);
17037 if row <= range.start.row {
17038 break;
17039 }
17040 }
17041 }
17042 }
17043 }
17044
17045 self.fold_creases(to_fold, true, window, cx);
17046 } else {
17047 let multi_buffer_snapshot = self.buffer.read(cx).snapshot(cx);
17048 let buffer_ids = self
17049 .selections
17050 .disjoint_anchor_ranges()
17051 .flat_map(|range| multi_buffer_snapshot.buffer_ids_for_range(range))
17052 .collect::<HashSet<_>>();
17053 for buffer_id in buffer_ids {
17054 self.fold_buffer(buffer_id, cx);
17055 }
17056 }
17057 }
17058
17059 fn fold_at_level(
17060 &mut self,
17061 fold_at: &FoldAtLevel,
17062 window: &mut Window,
17063 cx: &mut Context<Self>,
17064 ) {
17065 if !self.buffer.read(cx).is_singleton() {
17066 return;
17067 }
17068
17069 let fold_at_level = fold_at.0;
17070 let snapshot = self.buffer.read(cx).snapshot(cx);
17071 let mut to_fold = Vec::new();
17072 let mut stack = vec![(0, snapshot.max_row().0, 1)];
17073
17074 while let Some((mut start_row, end_row, current_level)) = stack.pop() {
17075 while start_row < end_row {
17076 match self
17077 .snapshot(window, cx)
17078 .crease_for_buffer_row(MultiBufferRow(start_row))
17079 {
17080 Some(crease) => {
17081 let nested_start_row = crease.range().start.row + 1;
17082 let nested_end_row = crease.range().end.row;
17083
17084 if current_level < fold_at_level {
17085 stack.push((nested_start_row, nested_end_row, current_level + 1));
17086 } else if current_level == fold_at_level {
17087 to_fold.push(crease);
17088 }
17089
17090 start_row = nested_end_row + 1;
17091 }
17092 None => start_row += 1,
17093 }
17094 }
17095 }
17096
17097 self.fold_creases(to_fold, true, window, cx);
17098 }
17099
17100 pub fn fold_all(&mut self, _: &actions::FoldAll, window: &mut Window, cx: &mut Context<Self>) {
17101 if self.buffer.read(cx).is_singleton() {
17102 let mut fold_ranges = Vec::new();
17103 let snapshot = self.buffer.read(cx).snapshot(cx);
17104
17105 for row in 0..snapshot.max_row().0 {
17106 if let Some(foldable_range) = self
17107 .snapshot(window, cx)
17108 .crease_for_buffer_row(MultiBufferRow(row))
17109 {
17110 fold_ranges.push(foldable_range);
17111 }
17112 }
17113
17114 self.fold_creases(fold_ranges, true, window, cx);
17115 } else {
17116 self.toggle_fold_multiple_buffers = cx.spawn_in(window, async move |editor, cx| {
17117 editor
17118 .update_in(cx, |editor, _, cx| {
17119 for buffer_id in editor.buffer.read(cx).excerpt_buffer_ids() {
17120 editor.fold_buffer(buffer_id, cx);
17121 }
17122 })
17123 .ok();
17124 });
17125 }
17126 }
17127
17128 pub fn fold_function_bodies(
17129 &mut self,
17130 _: &actions::FoldFunctionBodies,
17131 window: &mut Window,
17132 cx: &mut Context<Self>,
17133 ) {
17134 let snapshot = self.buffer.read(cx).snapshot(cx);
17135
17136 let ranges = snapshot
17137 .text_object_ranges(0..snapshot.len(), TreeSitterOptions::default())
17138 .filter_map(|(range, obj)| (obj == TextObject::InsideFunction).then_some(range))
17139 .collect::<Vec<_>>();
17140
17141 let creases = ranges
17142 .into_iter()
17143 .map(|range| Crease::simple(range, self.display_map.read(cx).fold_placeholder.clone()))
17144 .collect();
17145
17146 self.fold_creases(creases, true, window, cx);
17147 }
17148
17149 pub fn fold_recursive(
17150 &mut self,
17151 _: &actions::FoldRecursive,
17152 window: &mut Window,
17153 cx: &mut Context<Self>,
17154 ) {
17155 let mut to_fold = Vec::new();
17156 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
17157 let selections = self.selections.all_adjusted(cx);
17158
17159 for selection in selections {
17160 let range = selection.range().sorted();
17161 let buffer_start_row = range.start.row;
17162
17163 if range.start.row != range.end.row {
17164 let mut found = false;
17165 for row in range.start.row..=range.end.row {
17166 if let Some(crease) = display_map.crease_for_buffer_row(MultiBufferRow(row)) {
17167 found = true;
17168 to_fold.push(crease);
17169 }
17170 }
17171 if found {
17172 continue;
17173 }
17174 }
17175
17176 for row in (0..=range.start.row).rev() {
17177 if let Some(crease) = display_map.crease_for_buffer_row(MultiBufferRow(row)) {
17178 if crease.range().end.row >= buffer_start_row {
17179 to_fold.push(crease);
17180 } else {
17181 break;
17182 }
17183 }
17184 }
17185 }
17186
17187 self.fold_creases(to_fold, true, window, cx);
17188 }
17189
17190 pub fn fold_at(
17191 &mut self,
17192 buffer_row: MultiBufferRow,
17193 window: &mut Window,
17194 cx: &mut Context<Self>,
17195 ) {
17196 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
17197
17198 if let Some(crease) = display_map.crease_for_buffer_row(buffer_row) {
17199 let autoscroll = self
17200 .selections
17201 .all::<Point>(cx)
17202 .iter()
17203 .any(|selection| crease.range().overlaps(&selection.range()));
17204
17205 self.fold_creases(vec![crease], autoscroll, window, cx);
17206 }
17207 }
17208
17209 pub fn unfold_lines(&mut self, _: &UnfoldLines, _window: &mut Window, cx: &mut Context<Self>) {
17210 if self.is_singleton(cx) {
17211 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
17212 let buffer = &display_map.buffer_snapshot;
17213 let selections = self.selections.all::<Point>(cx);
17214 let ranges = selections
17215 .iter()
17216 .map(|s| {
17217 let range = s.display_range(&display_map).sorted();
17218 let mut start = range.start.to_point(&display_map);
17219 let mut end = range.end.to_point(&display_map);
17220 start.column = 0;
17221 end.column = buffer.line_len(MultiBufferRow(end.row));
17222 start..end
17223 })
17224 .collect::<Vec<_>>();
17225
17226 self.unfold_ranges(&ranges, true, true, cx);
17227 } else {
17228 let multi_buffer_snapshot = self.buffer.read(cx).snapshot(cx);
17229 let buffer_ids = self
17230 .selections
17231 .disjoint_anchor_ranges()
17232 .flat_map(|range| multi_buffer_snapshot.buffer_ids_for_range(range))
17233 .collect::<HashSet<_>>();
17234 for buffer_id in buffer_ids {
17235 self.unfold_buffer(buffer_id, cx);
17236 }
17237 }
17238 }
17239
17240 pub fn unfold_recursive(
17241 &mut self,
17242 _: &UnfoldRecursive,
17243 _window: &mut Window,
17244 cx: &mut Context<Self>,
17245 ) {
17246 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
17247 let selections = self.selections.all::<Point>(cx);
17248 let ranges = selections
17249 .iter()
17250 .map(|s| {
17251 let mut range = s.display_range(&display_map).sorted();
17252 *range.start.column_mut() = 0;
17253 *range.end.column_mut() = display_map.line_len(range.end.row());
17254 let start = range.start.to_point(&display_map);
17255 let end = range.end.to_point(&display_map);
17256 start..end
17257 })
17258 .collect::<Vec<_>>();
17259
17260 self.unfold_ranges(&ranges, true, true, cx);
17261 }
17262
17263 pub fn unfold_at(
17264 &mut self,
17265 buffer_row: MultiBufferRow,
17266 _window: &mut Window,
17267 cx: &mut Context<Self>,
17268 ) {
17269 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
17270
17271 let intersection_range = Point::new(buffer_row.0, 0)
17272 ..Point::new(
17273 buffer_row.0,
17274 display_map.buffer_snapshot.line_len(buffer_row),
17275 );
17276
17277 let autoscroll = self
17278 .selections
17279 .all::<Point>(cx)
17280 .iter()
17281 .any(|selection| RangeExt::overlaps(&selection.range(), &intersection_range));
17282
17283 self.unfold_ranges(&[intersection_range], true, autoscroll, cx);
17284 }
17285
17286 pub fn unfold_all(
17287 &mut self,
17288 _: &actions::UnfoldAll,
17289 _window: &mut Window,
17290 cx: &mut Context<Self>,
17291 ) {
17292 if self.buffer.read(cx).is_singleton() {
17293 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
17294 self.unfold_ranges(&[0..display_map.buffer_snapshot.len()], true, true, cx);
17295 } else {
17296 self.toggle_fold_multiple_buffers = cx.spawn(async move |editor, cx| {
17297 editor
17298 .update(cx, |editor, cx| {
17299 for buffer_id in editor.buffer.read(cx).excerpt_buffer_ids() {
17300 editor.unfold_buffer(buffer_id, cx);
17301 }
17302 })
17303 .ok();
17304 });
17305 }
17306 }
17307
17308 pub fn fold_selected_ranges(
17309 &mut self,
17310 _: &FoldSelectedRanges,
17311 window: &mut Window,
17312 cx: &mut Context<Self>,
17313 ) {
17314 let selections = self.selections.all_adjusted(cx);
17315 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
17316 let ranges = selections
17317 .into_iter()
17318 .map(|s| Crease::simple(s.range(), display_map.fold_placeholder.clone()))
17319 .collect::<Vec<_>>();
17320 self.fold_creases(ranges, true, window, cx);
17321 }
17322
17323 pub fn fold_ranges<T: ToOffset + Clone>(
17324 &mut self,
17325 ranges: Vec<Range<T>>,
17326 auto_scroll: bool,
17327 window: &mut Window,
17328 cx: &mut Context<Self>,
17329 ) {
17330 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
17331 let ranges = ranges
17332 .into_iter()
17333 .map(|r| Crease::simple(r, display_map.fold_placeholder.clone()))
17334 .collect::<Vec<_>>();
17335 self.fold_creases(ranges, auto_scroll, window, cx);
17336 }
17337
17338 pub fn fold_creases<T: ToOffset + Clone>(
17339 &mut self,
17340 creases: Vec<Crease<T>>,
17341 auto_scroll: bool,
17342 _window: &mut Window,
17343 cx: &mut Context<Self>,
17344 ) {
17345 if creases.is_empty() {
17346 return;
17347 }
17348
17349 self.display_map.update(cx, |map, cx| map.fold(creases, cx));
17350
17351 if auto_scroll {
17352 self.request_autoscroll(Autoscroll::fit(), cx);
17353 }
17354
17355 cx.notify();
17356
17357 self.scrollbar_marker_state.dirty = true;
17358 self.folds_did_change(cx);
17359 }
17360
17361 /// Removes any folds whose ranges intersect any of the given ranges.
17362 pub fn unfold_ranges<T: ToOffset + Clone>(
17363 &mut self,
17364 ranges: &[Range<T>],
17365 inclusive: bool,
17366 auto_scroll: bool,
17367 cx: &mut Context<Self>,
17368 ) {
17369 self.remove_folds_with(ranges, auto_scroll, cx, |map, cx| {
17370 map.unfold_intersecting(ranges.iter().cloned(), inclusive, cx)
17371 });
17372 self.folds_did_change(cx);
17373 }
17374
17375 pub fn fold_buffer(&mut self, buffer_id: BufferId, cx: &mut Context<Self>) {
17376 if self.buffer().read(cx).is_singleton() || self.is_buffer_folded(buffer_id, cx) {
17377 return;
17378 }
17379 let folded_excerpts = self.buffer().read(cx).excerpts_for_buffer(buffer_id, cx);
17380 self.display_map.update(cx, |display_map, cx| {
17381 display_map.fold_buffers([buffer_id], cx)
17382 });
17383 cx.emit(EditorEvent::BufferFoldToggled {
17384 ids: folded_excerpts.iter().map(|&(id, _)| id).collect(),
17385 folded: true,
17386 });
17387 cx.notify();
17388 }
17389
17390 pub fn unfold_buffer(&mut self, buffer_id: BufferId, cx: &mut Context<Self>) {
17391 if self.buffer().read(cx).is_singleton() || !self.is_buffer_folded(buffer_id, cx) {
17392 return;
17393 }
17394 let unfolded_excerpts = self.buffer().read(cx).excerpts_for_buffer(buffer_id, cx);
17395 self.display_map.update(cx, |display_map, cx| {
17396 display_map.unfold_buffers([buffer_id], cx);
17397 });
17398 cx.emit(EditorEvent::BufferFoldToggled {
17399 ids: unfolded_excerpts.iter().map(|&(id, _)| id).collect(),
17400 folded: false,
17401 });
17402 cx.notify();
17403 }
17404
17405 pub fn is_buffer_folded(&self, buffer: BufferId, cx: &App) -> bool {
17406 self.display_map.read(cx).is_buffer_folded(buffer)
17407 }
17408
17409 pub fn folded_buffers<'a>(&self, cx: &'a App) -> &'a HashSet<BufferId> {
17410 self.display_map.read(cx).folded_buffers()
17411 }
17412
17413 pub fn disable_header_for_buffer(&mut self, buffer_id: BufferId, cx: &mut Context<Self>) {
17414 self.display_map.update(cx, |display_map, cx| {
17415 display_map.disable_header_for_buffer(buffer_id, cx);
17416 });
17417 cx.notify();
17418 }
17419
17420 /// Removes any folds with the given ranges.
17421 pub fn remove_folds_with_type<T: ToOffset + Clone>(
17422 &mut self,
17423 ranges: &[Range<T>],
17424 type_id: TypeId,
17425 auto_scroll: bool,
17426 cx: &mut Context<Self>,
17427 ) {
17428 self.remove_folds_with(ranges, auto_scroll, cx, |map, cx| {
17429 map.remove_folds_with_type(ranges.iter().cloned(), type_id, cx)
17430 });
17431 self.folds_did_change(cx);
17432 }
17433
17434 fn remove_folds_with<T: ToOffset + Clone>(
17435 &mut self,
17436 ranges: &[Range<T>],
17437 auto_scroll: bool,
17438 cx: &mut Context<Self>,
17439 update: impl FnOnce(&mut DisplayMap, &mut Context<DisplayMap>),
17440 ) {
17441 if ranges.is_empty() {
17442 return;
17443 }
17444
17445 let mut buffers_affected = HashSet::default();
17446 let multi_buffer = self.buffer().read(cx);
17447 for range in ranges {
17448 if let Some((_, buffer, _)) = multi_buffer.excerpt_containing(range.start.clone(), cx) {
17449 buffers_affected.insert(buffer.read(cx).remote_id());
17450 };
17451 }
17452
17453 self.display_map.update(cx, update);
17454
17455 if auto_scroll {
17456 self.request_autoscroll(Autoscroll::fit(), cx);
17457 }
17458
17459 cx.notify();
17460 self.scrollbar_marker_state.dirty = true;
17461 self.active_indent_guides_state.dirty = true;
17462 }
17463
17464 pub fn update_renderer_widths(
17465 &mut self,
17466 widths: impl IntoIterator<Item = (ChunkRendererId, Pixels)>,
17467 cx: &mut Context<Self>,
17468 ) -> bool {
17469 self.display_map
17470 .update(cx, |map, cx| map.update_fold_widths(widths, cx))
17471 }
17472
17473 pub fn default_fold_placeholder(&self, cx: &App) -> FoldPlaceholder {
17474 self.display_map.read(cx).fold_placeholder.clone()
17475 }
17476
17477 pub fn set_expand_all_diff_hunks(&mut self, cx: &mut App) {
17478 self.buffer.update(cx, |buffer, cx| {
17479 buffer.set_all_diff_hunks_expanded(cx);
17480 });
17481 }
17482
17483 pub fn expand_all_diff_hunks(
17484 &mut self,
17485 _: &ExpandAllDiffHunks,
17486 _window: &mut Window,
17487 cx: &mut Context<Self>,
17488 ) {
17489 self.buffer.update(cx, |buffer, cx| {
17490 buffer.expand_diff_hunks(vec![Anchor::min()..Anchor::max()], cx)
17491 });
17492 }
17493
17494 pub fn toggle_selected_diff_hunks(
17495 &mut self,
17496 _: &ToggleSelectedDiffHunks,
17497 _window: &mut Window,
17498 cx: &mut Context<Self>,
17499 ) {
17500 let ranges: Vec<_> = self.selections.disjoint.iter().map(|s| s.range()).collect();
17501 self.toggle_diff_hunks_in_ranges(ranges, cx);
17502 }
17503
17504 pub fn diff_hunks_in_ranges<'a>(
17505 &'a self,
17506 ranges: &'a [Range<Anchor>],
17507 buffer: &'a MultiBufferSnapshot,
17508 ) -> impl 'a + Iterator<Item = MultiBufferDiffHunk> {
17509 ranges.iter().flat_map(move |range| {
17510 let end_excerpt_id = range.end.excerpt_id;
17511 let range = range.to_point(buffer);
17512 let mut peek_end = range.end;
17513 if range.end.row < buffer.max_row().0 {
17514 peek_end = Point::new(range.end.row + 1, 0);
17515 }
17516 buffer
17517 .diff_hunks_in_range(range.start..peek_end)
17518 .filter(move |hunk| hunk.excerpt_id.cmp(&end_excerpt_id, buffer).is_le())
17519 })
17520 }
17521
17522 pub fn has_stageable_diff_hunks_in_ranges(
17523 &self,
17524 ranges: &[Range<Anchor>],
17525 snapshot: &MultiBufferSnapshot,
17526 ) -> bool {
17527 let mut hunks = self.diff_hunks_in_ranges(ranges, &snapshot);
17528 hunks.any(|hunk| hunk.status().has_secondary_hunk())
17529 }
17530
17531 pub fn toggle_staged_selected_diff_hunks(
17532 &mut self,
17533 _: &::git::ToggleStaged,
17534 _: &mut Window,
17535 cx: &mut Context<Self>,
17536 ) {
17537 let snapshot = self.buffer.read(cx).snapshot(cx);
17538 let ranges: Vec<_> = self.selections.disjoint.iter().map(|s| s.range()).collect();
17539 let stage = self.has_stageable_diff_hunks_in_ranges(&ranges, &snapshot);
17540 self.stage_or_unstage_diff_hunks(stage, ranges, cx);
17541 }
17542
17543 pub fn set_render_diff_hunk_controls(
17544 &mut self,
17545 render_diff_hunk_controls: RenderDiffHunkControlsFn,
17546 cx: &mut Context<Self>,
17547 ) {
17548 self.render_diff_hunk_controls = render_diff_hunk_controls;
17549 cx.notify();
17550 }
17551
17552 pub fn stage_and_next(
17553 &mut self,
17554 _: &::git::StageAndNext,
17555 window: &mut Window,
17556 cx: &mut Context<Self>,
17557 ) {
17558 self.do_stage_or_unstage_and_next(true, window, cx);
17559 }
17560
17561 pub fn unstage_and_next(
17562 &mut self,
17563 _: &::git::UnstageAndNext,
17564 window: &mut Window,
17565 cx: &mut Context<Self>,
17566 ) {
17567 self.do_stage_or_unstage_and_next(false, window, cx);
17568 }
17569
17570 pub fn stage_or_unstage_diff_hunks(
17571 &mut self,
17572 stage: bool,
17573 ranges: Vec<Range<Anchor>>,
17574 cx: &mut Context<Self>,
17575 ) {
17576 let task = self.save_buffers_for_ranges_if_needed(&ranges, cx);
17577 cx.spawn(async move |this, cx| {
17578 task.await?;
17579 this.update(cx, |this, cx| {
17580 let snapshot = this.buffer.read(cx).snapshot(cx);
17581 let chunk_by = this
17582 .diff_hunks_in_ranges(&ranges, &snapshot)
17583 .chunk_by(|hunk| hunk.buffer_id);
17584 for (buffer_id, hunks) in &chunk_by {
17585 this.do_stage_or_unstage(stage, buffer_id, hunks, cx);
17586 }
17587 })
17588 })
17589 .detach_and_log_err(cx);
17590 }
17591
17592 fn save_buffers_for_ranges_if_needed(
17593 &mut self,
17594 ranges: &[Range<Anchor>],
17595 cx: &mut Context<Editor>,
17596 ) -> Task<Result<()>> {
17597 let multibuffer = self.buffer.read(cx);
17598 let snapshot = multibuffer.read(cx);
17599 let buffer_ids: HashSet<_> = ranges
17600 .iter()
17601 .flat_map(|range| snapshot.buffer_ids_for_range(range.clone()))
17602 .collect();
17603 drop(snapshot);
17604
17605 let mut buffers = HashSet::default();
17606 for buffer_id in buffer_ids {
17607 if let Some(buffer_entity) = multibuffer.buffer(buffer_id) {
17608 let buffer = buffer_entity.read(cx);
17609 if buffer.file().is_some_and(|file| file.disk_state().exists()) && buffer.is_dirty()
17610 {
17611 buffers.insert(buffer_entity);
17612 }
17613 }
17614 }
17615
17616 if let Some(project) = &self.project {
17617 project.update(cx, |project, cx| project.save_buffers(buffers, cx))
17618 } else {
17619 Task::ready(Ok(()))
17620 }
17621 }
17622
17623 fn do_stage_or_unstage_and_next(
17624 &mut self,
17625 stage: bool,
17626 window: &mut Window,
17627 cx: &mut Context<Self>,
17628 ) {
17629 let ranges = self.selections.disjoint_anchor_ranges().collect::<Vec<_>>();
17630
17631 if ranges.iter().any(|range| range.start != range.end) {
17632 self.stage_or_unstage_diff_hunks(stage, ranges, cx);
17633 return;
17634 }
17635
17636 self.stage_or_unstage_diff_hunks(stage, ranges, cx);
17637 let snapshot = self.snapshot(window, cx);
17638 let position = self.selections.newest::<Point>(cx).head();
17639 let mut row = snapshot
17640 .buffer_snapshot
17641 .diff_hunks_in_range(position..snapshot.buffer_snapshot.max_point())
17642 .find(|hunk| hunk.row_range.start.0 > position.row)
17643 .map(|hunk| hunk.row_range.start);
17644
17645 let all_diff_hunks_expanded = self.buffer().read(cx).all_diff_hunks_expanded();
17646 // Outside of the project diff editor, wrap around to the beginning.
17647 if !all_diff_hunks_expanded {
17648 row = row.or_else(|| {
17649 snapshot
17650 .buffer_snapshot
17651 .diff_hunks_in_range(Point::zero()..position)
17652 .find(|hunk| hunk.row_range.end.0 < position.row)
17653 .map(|hunk| hunk.row_range.start)
17654 });
17655 }
17656
17657 if let Some(row) = row {
17658 let destination = Point::new(row.0, 0);
17659 let autoscroll = Autoscroll::center();
17660
17661 self.unfold_ranges(&[destination..destination], false, false, cx);
17662 self.change_selections(SelectionEffects::scroll(autoscroll), window, cx, |s| {
17663 s.select_ranges([destination..destination]);
17664 });
17665 }
17666 }
17667
17668 fn do_stage_or_unstage(
17669 &self,
17670 stage: bool,
17671 buffer_id: BufferId,
17672 hunks: impl Iterator<Item = MultiBufferDiffHunk>,
17673 cx: &mut App,
17674 ) -> Option<()> {
17675 let project = self.project.as_ref()?;
17676 let buffer = project.read(cx).buffer_for_id(buffer_id, cx)?;
17677 let diff = self.buffer.read(cx).diff_for(buffer_id)?;
17678 let buffer_snapshot = buffer.read(cx).snapshot();
17679 let file_exists = buffer_snapshot
17680 .file()
17681 .is_some_and(|file| file.disk_state().exists());
17682 diff.update(cx, |diff, cx| {
17683 diff.stage_or_unstage_hunks(
17684 stage,
17685 &hunks
17686 .map(|hunk| buffer_diff::DiffHunk {
17687 buffer_range: hunk.buffer_range,
17688 diff_base_byte_range: hunk.diff_base_byte_range,
17689 secondary_status: hunk.secondary_status,
17690 range: Point::zero()..Point::zero(), // unused
17691 })
17692 .collect::<Vec<_>>(),
17693 &buffer_snapshot,
17694 file_exists,
17695 cx,
17696 )
17697 });
17698 None
17699 }
17700
17701 pub fn expand_selected_diff_hunks(&mut self, cx: &mut Context<Self>) {
17702 let ranges: Vec<_> = self.selections.disjoint.iter().map(|s| s.range()).collect();
17703 self.buffer
17704 .update(cx, |buffer, cx| buffer.expand_diff_hunks(ranges, cx))
17705 }
17706
17707 pub fn clear_expanded_diff_hunks(&mut self, cx: &mut Context<Self>) -> bool {
17708 self.buffer.update(cx, |buffer, cx| {
17709 let ranges = vec![Anchor::min()..Anchor::max()];
17710 if !buffer.all_diff_hunks_expanded()
17711 && buffer.has_expanded_diff_hunks_in_ranges(&ranges, cx)
17712 {
17713 buffer.collapse_diff_hunks(ranges, cx);
17714 true
17715 } else {
17716 false
17717 }
17718 })
17719 }
17720
17721 fn toggle_diff_hunks_in_ranges(
17722 &mut self,
17723 ranges: Vec<Range<Anchor>>,
17724 cx: &mut Context<Editor>,
17725 ) {
17726 self.buffer.update(cx, |buffer, cx| {
17727 let expand = !buffer.has_expanded_diff_hunks_in_ranges(&ranges, cx);
17728 buffer.expand_or_collapse_diff_hunks(ranges, expand, cx);
17729 })
17730 }
17731
17732 fn toggle_single_diff_hunk(&mut self, range: Range<Anchor>, cx: &mut Context<Self>) {
17733 self.buffer.update(cx, |buffer, cx| {
17734 let snapshot = buffer.snapshot(cx);
17735 let excerpt_id = range.end.excerpt_id;
17736 let point_range = range.to_point(&snapshot);
17737 let expand = !buffer.single_hunk_is_expanded(range, cx);
17738 buffer.expand_or_collapse_diff_hunks_inner([(point_range, excerpt_id)], expand, cx);
17739 })
17740 }
17741
17742 pub(crate) fn apply_all_diff_hunks(
17743 &mut self,
17744 _: &ApplyAllDiffHunks,
17745 window: &mut Window,
17746 cx: &mut Context<Self>,
17747 ) {
17748 self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
17749
17750 let buffers = self.buffer.read(cx).all_buffers();
17751 for branch_buffer in buffers {
17752 branch_buffer.update(cx, |branch_buffer, cx| {
17753 branch_buffer.merge_into_base(Vec::new(), cx);
17754 });
17755 }
17756
17757 if let Some(project) = self.project.clone() {
17758 self.save(
17759 SaveOptions {
17760 format: true,
17761 autosave: false,
17762 },
17763 project,
17764 window,
17765 cx,
17766 )
17767 .detach_and_log_err(cx);
17768 }
17769 }
17770
17771 pub(crate) fn apply_selected_diff_hunks(
17772 &mut self,
17773 _: &ApplyDiffHunk,
17774 window: &mut Window,
17775 cx: &mut Context<Self>,
17776 ) {
17777 self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
17778 let snapshot = self.snapshot(window, cx);
17779 let hunks = snapshot.hunks_for_ranges(self.selections.ranges(cx));
17780 let mut ranges_by_buffer = HashMap::default();
17781 self.transact(window, cx, |editor, _window, cx| {
17782 for hunk in hunks {
17783 if let Some(buffer) = editor.buffer.read(cx).buffer(hunk.buffer_id) {
17784 ranges_by_buffer
17785 .entry(buffer.clone())
17786 .or_insert_with(Vec::new)
17787 .push(hunk.buffer_range.to_offset(buffer.read(cx)));
17788 }
17789 }
17790
17791 for (buffer, ranges) in ranges_by_buffer {
17792 buffer.update(cx, |buffer, cx| {
17793 buffer.merge_into_base(ranges, cx);
17794 });
17795 }
17796 });
17797
17798 if let Some(project) = self.project.clone() {
17799 self.save(
17800 SaveOptions {
17801 format: true,
17802 autosave: false,
17803 },
17804 project,
17805 window,
17806 cx,
17807 )
17808 .detach_and_log_err(cx);
17809 }
17810 }
17811
17812 pub fn set_gutter_hovered(&mut self, hovered: bool, cx: &mut Context<Self>) {
17813 if hovered != self.gutter_hovered {
17814 self.gutter_hovered = hovered;
17815 cx.notify();
17816 }
17817 }
17818
17819 pub fn insert_blocks(
17820 &mut self,
17821 blocks: impl IntoIterator<Item = BlockProperties<Anchor>>,
17822 autoscroll: Option<Autoscroll>,
17823 cx: &mut Context<Self>,
17824 ) -> Vec<CustomBlockId> {
17825 let blocks = self
17826 .display_map
17827 .update(cx, |display_map, cx| display_map.insert_blocks(blocks, cx));
17828 if let Some(autoscroll) = autoscroll {
17829 self.request_autoscroll(autoscroll, cx);
17830 }
17831 cx.notify();
17832 blocks
17833 }
17834
17835 pub fn resize_blocks(
17836 &mut self,
17837 heights: HashMap<CustomBlockId, u32>,
17838 autoscroll: Option<Autoscroll>,
17839 cx: &mut Context<Self>,
17840 ) {
17841 self.display_map
17842 .update(cx, |display_map, cx| display_map.resize_blocks(heights, cx));
17843 if let Some(autoscroll) = autoscroll {
17844 self.request_autoscroll(autoscroll, cx);
17845 }
17846 cx.notify();
17847 }
17848
17849 pub fn replace_blocks(
17850 &mut self,
17851 renderers: HashMap<CustomBlockId, RenderBlock>,
17852 autoscroll: Option<Autoscroll>,
17853 cx: &mut Context<Self>,
17854 ) {
17855 self.display_map
17856 .update(cx, |display_map, _cx| display_map.replace_blocks(renderers));
17857 if let Some(autoscroll) = autoscroll {
17858 self.request_autoscroll(autoscroll, cx);
17859 }
17860 cx.notify();
17861 }
17862
17863 pub fn remove_blocks(
17864 &mut self,
17865 block_ids: HashSet<CustomBlockId>,
17866 autoscroll: Option<Autoscroll>,
17867 cx: &mut Context<Self>,
17868 ) {
17869 self.display_map.update(cx, |display_map, cx| {
17870 display_map.remove_blocks(block_ids, cx)
17871 });
17872 if let Some(autoscroll) = autoscroll {
17873 self.request_autoscroll(autoscroll, cx);
17874 }
17875 cx.notify();
17876 }
17877
17878 pub fn row_for_block(
17879 &self,
17880 block_id: CustomBlockId,
17881 cx: &mut Context<Self>,
17882 ) -> Option<DisplayRow> {
17883 self.display_map
17884 .update(cx, |map, cx| map.row_for_block(block_id, cx))
17885 }
17886
17887 pub(crate) fn set_focused_block(&mut self, focused_block: FocusedBlock) {
17888 self.focused_block = Some(focused_block);
17889 }
17890
17891 pub(crate) fn take_focused_block(&mut self) -> Option<FocusedBlock> {
17892 self.focused_block.take()
17893 }
17894
17895 pub fn insert_creases(
17896 &mut self,
17897 creases: impl IntoIterator<Item = Crease<Anchor>>,
17898 cx: &mut Context<Self>,
17899 ) -> Vec<CreaseId> {
17900 self.display_map
17901 .update(cx, |map, cx| map.insert_creases(creases, cx))
17902 }
17903
17904 pub fn remove_creases(
17905 &mut self,
17906 ids: impl IntoIterator<Item = CreaseId>,
17907 cx: &mut Context<Self>,
17908 ) -> Vec<(CreaseId, Range<Anchor>)> {
17909 self.display_map
17910 .update(cx, |map, cx| map.remove_creases(ids, cx))
17911 }
17912
17913 pub fn longest_row(&self, cx: &mut App) -> DisplayRow {
17914 self.display_map
17915 .update(cx, |map, cx| map.snapshot(cx))
17916 .longest_row()
17917 }
17918
17919 pub fn max_point(&self, cx: &mut App) -> DisplayPoint {
17920 self.display_map
17921 .update(cx, |map, cx| map.snapshot(cx))
17922 .max_point()
17923 }
17924
17925 pub fn text(&self, cx: &App) -> String {
17926 self.buffer.read(cx).read(cx).text()
17927 }
17928
17929 pub fn is_empty(&self, cx: &App) -> bool {
17930 self.buffer.read(cx).read(cx).is_empty()
17931 }
17932
17933 pub fn text_option(&self, cx: &App) -> Option<String> {
17934 let text = self.text(cx);
17935 let text = text.trim();
17936
17937 if text.is_empty() {
17938 return None;
17939 }
17940
17941 Some(text.to_string())
17942 }
17943
17944 pub fn set_text(
17945 &mut self,
17946 text: impl Into<Arc<str>>,
17947 window: &mut Window,
17948 cx: &mut Context<Self>,
17949 ) {
17950 self.transact(window, cx, |this, _, cx| {
17951 this.buffer
17952 .read(cx)
17953 .as_singleton()
17954 .expect("you can only call set_text on editors for singleton buffers")
17955 .update(cx, |buffer, cx| buffer.set_text(text, cx));
17956 });
17957 }
17958
17959 pub fn display_text(&self, cx: &mut App) -> String {
17960 self.display_map
17961 .update(cx, |map, cx| map.snapshot(cx))
17962 .text()
17963 }
17964
17965 fn create_minimap(
17966 &self,
17967 minimap_settings: MinimapSettings,
17968 window: &mut Window,
17969 cx: &mut Context<Self>,
17970 ) -> Option<Entity<Self>> {
17971 (minimap_settings.minimap_enabled() && self.is_singleton(cx))
17972 .then(|| self.initialize_new_minimap(minimap_settings, window, cx))
17973 }
17974
17975 fn initialize_new_minimap(
17976 &self,
17977 minimap_settings: MinimapSettings,
17978 window: &mut Window,
17979 cx: &mut Context<Self>,
17980 ) -> Entity<Self> {
17981 const MINIMAP_FONT_WEIGHT: gpui::FontWeight = gpui::FontWeight::BLACK;
17982
17983 let mut minimap = Editor::new_internal(
17984 EditorMode::Minimap {
17985 parent: cx.weak_entity(),
17986 },
17987 self.buffer.clone(),
17988 self.project.clone(),
17989 Some(self.display_map.clone()),
17990 window,
17991 cx,
17992 );
17993 minimap.scroll_manager.clone_state(&self.scroll_manager);
17994 minimap.set_text_style_refinement(TextStyleRefinement {
17995 font_size: Some(MINIMAP_FONT_SIZE),
17996 font_weight: Some(MINIMAP_FONT_WEIGHT),
17997 ..Default::default()
17998 });
17999 minimap.update_minimap_configuration(minimap_settings, cx);
18000 cx.new(|_| minimap)
18001 }
18002
18003 fn update_minimap_configuration(&mut self, minimap_settings: MinimapSettings, cx: &App) {
18004 let current_line_highlight = minimap_settings
18005 .current_line_highlight
18006 .unwrap_or_else(|| EditorSettings::get_global(cx).current_line_highlight);
18007 self.set_current_line_highlight(Some(current_line_highlight));
18008 }
18009
18010 pub fn minimap(&self) -> Option<&Entity<Self>> {
18011 self.minimap
18012 .as_ref()
18013 .filter(|_| self.minimap_visibility.visible())
18014 }
18015
18016 pub fn wrap_guides(&self, cx: &App) -> SmallVec<[(usize, bool); 2]> {
18017 let mut wrap_guides = smallvec![];
18018
18019 if self.show_wrap_guides == Some(false) {
18020 return wrap_guides;
18021 }
18022
18023 let settings = self.buffer.read(cx).language_settings(cx);
18024 if settings.show_wrap_guides {
18025 match self.soft_wrap_mode(cx) {
18026 SoftWrap::Column(soft_wrap) => {
18027 wrap_guides.push((soft_wrap as usize, true));
18028 }
18029 SoftWrap::Bounded(soft_wrap) => {
18030 wrap_guides.push((soft_wrap as usize, true));
18031 }
18032 SoftWrap::GitDiff | SoftWrap::None | SoftWrap::EditorWidth => {}
18033 }
18034 wrap_guides.extend(settings.wrap_guides.iter().map(|guide| (*guide, false)))
18035 }
18036
18037 wrap_guides
18038 }
18039
18040 pub fn soft_wrap_mode(&self, cx: &App) -> SoftWrap {
18041 let settings = self.buffer.read(cx).language_settings(cx);
18042 let mode = self.soft_wrap_mode_override.unwrap_or(settings.soft_wrap);
18043 match mode {
18044 language_settings::SoftWrap::PreferLine | language_settings::SoftWrap::None => {
18045 SoftWrap::None
18046 }
18047 language_settings::SoftWrap::EditorWidth => SoftWrap::EditorWidth,
18048 language_settings::SoftWrap::PreferredLineLength => {
18049 SoftWrap::Column(settings.preferred_line_length)
18050 }
18051 language_settings::SoftWrap::Bounded => {
18052 SoftWrap::Bounded(settings.preferred_line_length)
18053 }
18054 }
18055 }
18056
18057 pub fn set_soft_wrap_mode(
18058 &mut self,
18059 mode: language_settings::SoftWrap,
18060
18061 cx: &mut Context<Self>,
18062 ) {
18063 self.soft_wrap_mode_override = Some(mode);
18064 cx.notify();
18065 }
18066
18067 pub fn set_hard_wrap(&mut self, hard_wrap: Option<usize>, cx: &mut Context<Self>) {
18068 self.hard_wrap = hard_wrap;
18069 cx.notify();
18070 }
18071
18072 pub fn set_text_style_refinement(&mut self, style: TextStyleRefinement) {
18073 self.text_style_refinement = Some(style);
18074 }
18075
18076 /// called by the Element so we know what style we were most recently rendered with.
18077 pub(crate) fn set_style(
18078 &mut self,
18079 style: EditorStyle,
18080 window: &mut Window,
18081 cx: &mut Context<Self>,
18082 ) {
18083 // We intentionally do not inform the display map about the minimap style
18084 // so that wrapping is not recalculated and stays consistent for the editor
18085 // and its linked minimap.
18086 if !self.mode.is_minimap() {
18087 let rem_size = window.rem_size();
18088 self.display_map.update(cx, |map, cx| {
18089 map.set_font(
18090 style.text.font(),
18091 style.text.font_size.to_pixels(rem_size),
18092 cx,
18093 )
18094 });
18095 }
18096 self.style = Some(style);
18097 }
18098
18099 pub fn style(&self) -> Option<&EditorStyle> {
18100 self.style.as_ref()
18101 }
18102
18103 // Called by the element. This method is not designed to be called outside of the editor
18104 // element's layout code because it does not notify when rewrapping is computed synchronously.
18105 pub(crate) fn set_wrap_width(&self, width: Option<Pixels>, cx: &mut App) -> bool {
18106 self.display_map
18107 .update(cx, |map, cx| map.set_wrap_width(width, cx))
18108 }
18109
18110 pub fn set_soft_wrap(&mut self) {
18111 self.soft_wrap_mode_override = Some(language_settings::SoftWrap::EditorWidth)
18112 }
18113
18114 pub fn toggle_soft_wrap(&mut self, _: &ToggleSoftWrap, _: &mut Window, cx: &mut Context<Self>) {
18115 if self.soft_wrap_mode_override.is_some() {
18116 self.soft_wrap_mode_override.take();
18117 } else {
18118 let soft_wrap = match self.soft_wrap_mode(cx) {
18119 SoftWrap::GitDiff => return,
18120 SoftWrap::None => language_settings::SoftWrap::EditorWidth,
18121 SoftWrap::EditorWidth | SoftWrap::Column(_) | SoftWrap::Bounded(_) => {
18122 language_settings::SoftWrap::None
18123 }
18124 };
18125 self.soft_wrap_mode_override = Some(soft_wrap);
18126 }
18127 cx.notify();
18128 }
18129
18130 pub fn toggle_tab_bar(&mut self, _: &ToggleTabBar, _: &mut Window, cx: &mut Context<Self>) {
18131 let Some(workspace) = self.workspace() else {
18132 return;
18133 };
18134 let fs = workspace.read(cx).app_state().fs.clone();
18135 let current_show = TabBarSettings::get_global(cx).show;
18136 update_settings_file::<TabBarSettings>(fs, cx, move |setting, _| {
18137 setting.show = Some(!current_show);
18138 });
18139 }
18140
18141 pub fn toggle_indent_guides(
18142 &mut self,
18143 _: &ToggleIndentGuides,
18144 _: &mut Window,
18145 cx: &mut Context<Self>,
18146 ) {
18147 let currently_enabled = self.should_show_indent_guides().unwrap_or_else(|| {
18148 self.buffer
18149 .read(cx)
18150 .language_settings(cx)
18151 .indent_guides
18152 .enabled
18153 });
18154 self.show_indent_guides = Some(!currently_enabled);
18155 cx.notify();
18156 }
18157
18158 fn should_show_indent_guides(&self) -> Option<bool> {
18159 self.show_indent_guides
18160 }
18161
18162 pub fn toggle_line_numbers(
18163 &mut self,
18164 _: &ToggleLineNumbers,
18165 _: &mut Window,
18166 cx: &mut Context<Self>,
18167 ) {
18168 let mut editor_settings = EditorSettings::get_global(cx).clone();
18169 editor_settings.gutter.line_numbers = !editor_settings.gutter.line_numbers;
18170 EditorSettings::override_global(editor_settings, cx);
18171 }
18172
18173 pub fn line_numbers_enabled(&self, cx: &App) -> bool {
18174 if let Some(show_line_numbers) = self.show_line_numbers {
18175 return show_line_numbers;
18176 }
18177 EditorSettings::get_global(cx).gutter.line_numbers
18178 }
18179
18180 pub fn should_use_relative_line_numbers(&self, cx: &mut App) -> bool {
18181 self.use_relative_line_numbers
18182 .unwrap_or(EditorSettings::get_global(cx).relative_line_numbers)
18183 }
18184
18185 pub fn toggle_relative_line_numbers(
18186 &mut self,
18187 _: &ToggleRelativeLineNumbers,
18188 _: &mut Window,
18189 cx: &mut Context<Self>,
18190 ) {
18191 let is_relative = self.should_use_relative_line_numbers(cx);
18192 self.set_relative_line_number(Some(!is_relative), cx)
18193 }
18194
18195 pub fn set_relative_line_number(&mut self, is_relative: Option<bool>, cx: &mut Context<Self>) {
18196 self.use_relative_line_numbers = is_relative;
18197 cx.notify();
18198 }
18199
18200 pub fn set_show_gutter(&mut self, show_gutter: bool, cx: &mut Context<Self>) {
18201 self.show_gutter = show_gutter;
18202 cx.notify();
18203 }
18204
18205 pub fn set_show_scrollbars(&mut self, show: bool, cx: &mut Context<Self>) {
18206 self.show_scrollbars = ScrollbarAxes {
18207 horizontal: show,
18208 vertical: show,
18209 };
18210 cx.notify();
18211 }
18212
18213 pub fn set_show_vertical_scrollbar(&mut self, show: bool, cx: &mut Context<Self>) {
18214 self.show_scrollbars.vertical = show;
18215 cx.notify();
18216 }
18217
18218 pub fn set_show_horizontal_scrollbar(&mut self, show: bool, cx: &mut Context<Self>) {
18219 self.show_scrollbars.horizontal = show;
18220 cx.notify();
18221 }
18222
18223 pub fn set_minimap_visibility(
18224 &mut self,
18225 minimap_visibility: MinimapVisibility,
18226 window: &mut Window,
18227 cx: &mut Context<Self>,
18228 ) {
18229 if self.minimap_visibility != minimap_visibility {
18230 if minimap_visibility.visible() && self.minimap.is_none() {
18231 let minimap_settings = EditorSettings::get_global(cx).minimap;
18232 self.minimap =
18233 self.create_minimap(minimap_settings.with_show_override(), window, cx);
18234 }
18235 self.minimap_visibility = minimap_visibility;
18236 cx.notify();
18237 }
18238 }
18239
18240 pub fn disable_scrollbars_and_minimap(&mut self, window: &mut Window, cx: &mut Context<Self>) {
18241 self.set_show_scrollbars(false, cx);
18242 self.set_minimap_visibility(MinimapVisibility::Disabled, window, cx);
18243 }
18244
18245 pub fn hide_minimap_by_default(&mut self, window: &mut Window, cx: &mut Context<Self>) {
18246 self.set_minimap_visibility(self.minimap_visibility.hidden(), window, cx);
18247 }
18248
18249 /// Normally the text in full mode and auto height editors is padded on the
18250 /// left side by roughly half a character width for improved hit testing.
18251 ///
18252 /// Use this method to disable this for cases where this is not wanted (e.g.
18253 /// if you want to align the editor text with some other text above or below)
18254 /// or if you want to add this padding to single-line editors.
18255 pub fn set_offset_content(&mut self, offset_content: bool, cx: &mut Context<Self>) {
18256 self.offset_content = offset_content;
18257 cx.notify();
18258 }
18259
18260 pub fn set_show_line_numbers(&mut self, show_line_numbers: bool, cx: &mut Context<Self>) {
18261 self.show_line_numbers = Some(show_line_numbers);
18262 cx.notify();
18263 }
18264
18265 pub fn disable_expand_excerpt_buttons(&mut self, cx: &mut Context<Self>) {
18266 self.disable_expand_excerpt_buttons = true;
18267 cx.notify();
18268 }
18269
18270 pub fn set_show_git_diff_gutter(&mut self, show_git_diff_gutter: bool, cx: &mut Context<Self>) {
18271 self.show_git_diff_gutter = Some(show_git_diff_gutter);
18272 cx.notify();
18273 }
18274
18275 pub fn set_show_code_actions(&mut self, show_code_actions: bool, cx: &mut Context<Self>) {
18276 self.show_code_actions = Some(show_code_actions);
18277 cx.notify();
18278 }
18279
18280 pub fn set_show_runnables(&mut self, show_runnables: bool, cx: &mut Context<Self>) {
18281 self.show_runnables = Some(show_runnables);
18282 cx.notify();
18283 }
18284
18285 pub fn set_show_breakpoints(&mut self, show_breakpoints: bool, cx: &mut Context<Self>) {
18286 self.show_breakpoints = Some(show_breakpoints);
18287 cx.notify();
18288 }
18289
18290 pub fn set_masked(&mut self, masked: bool, cx: &mut Context<Self>) {
18291 if self.display_map.read(cx).masked != masked {
18292 self.display_map.update(cx, |map, _| map.masked = masked);
18293 }
18294 cx.notify()
18295 }
18296
18297 pub fn set_show_wrap_guides(&mut self, show_wrap_guides: bool, cx: &mut Context<Self>) {
18298 self.show_wrap_guides = Some(show_wrap_guides);
18299 cx.notify();
18300 }
18301
18302 pub fn set_show_indent_guides(&mut self, show_indent_guides: bool, cx: &mut Context<Self>) {
18303 self.show_indent_guides = Some(show_indent_guides);
18304 cx.notify();
18305 }
18306
18307 pub fn working_directory(&self, cx: &App) -> Option<PathBuf> {
18308 if let Some(buffer) = self.buffer().read(cx).as_singleton() {
18309 if let Some(file) = buffer.read(cx).file().and_then(|f| f.as_local()) {
18310 if let Some(dir) = file.abs_path(cx).parent() {
18311 return Some(dir.to_owned());
18312 }
18313 }
18314
18315 if let Some(project_path) = buffer.read(cx).project_path(cx) {
18316 return Some(project_path.path.to_path_buf());
18317 }
18318 }
18319
18320 None
18321 }
18322
18323 fn target_file<'a>(&self, cx: &'a App) -> Option<&'a dyn language::LocalFile> {
18324 self.active_excerpt(cx)?
18325 .1
18326 .read(cx)
18327 .file()
18328 .and_then(|f| f.as_local())
18329 }
18330
18331 pub fn target_file_abs_path(&self, cx: &mut Context<Self>) -> Option<PathBuf> {
18332 self.active_excerpt(cx).and_then(|(_, buffer, _)| {
18333 let buffer = buffer.read(cx);
18334 if let Some(project_path) = buffer.project_path(cx) {
18335 let project = self.project.as_ref()?.read(cx);
18336 project.absolute_path(&project_path, cx)
18337 } else {
18338 buffer
18339 .file()
18340 .and_then(|file| file.as_local().map(|file| file.abs_path(cx)))
18341 }
18342 })
18343 }
18344
18345 fn target_file_path(&self, cx: &mut Context<Self>) -> Option<PathBuf> {
18346 self.active_excerpt(cx).and_then(|(_, buffer, _)| {
18347 let project_path = buffer.read(cx).project_path(cx)?;
18348 let project = self.project.as_ref()?.read(cx);
18349 let entry = project.entry_for_path(&project_path, cx)?;
18350 let path = entry.path.to_path_buf();
18351 Some(path)
18352 })
18353 }
18354
18355 pub fn reveal_in_finder(
18356 &mut self,
18357 _: &RevealInFileManager,
18358 _window: &mut Window,
18359 cx: &mut Context<Self>,
18360 ) {
18361 if let Some(target) = self.target_file(cx) {
18362 cx.reveal_path(&target.abs_path(cx));
18363 }
18364 }
18365
18366 pub fn copy_path(
18367 &mut self,
18368 _: &zed_actions::workspace::CopyPath,
18369 _window: &mut Window,
18370 cx: &mut Context<Self>,
18371 ) {
18372 if let Some(path) = self.target_file_abs_path(cx) {
18373 if let Some(path) = path.to_str() {
18374 cx.write_to_clipboard(ClipboardItem::new_string(path.to_string()));
18375 }
18376 }
18377 }
18378
18379 pub fn copy_relative_path(
18380 &mut self,
18381 _: &zed_actions::workspace::CopyRelativePath,
18382 _window: &mut Window,
18383 cx: &mut Context<Self>,
18384 ) {
18385 if let Some(path) = self.target_file_path(cx) {
18386 if let Some(path) = path.to_str() {
18387 cx.write_to_clipboard(ClipboardItem::new_string(path.to_string()));
18388 }
18389 }
18390 }
18391
18392 pub fn project_path(&self, cx: &App) -> Option<ProjectPath> {
18393 if let Some(buffer) = self.buffer.read(cx).as_singleton() {
18394 buffer.read(cx).project_path(cx)
18395 } else {
18396 None
18397 }
18398 }
18399
18400 // Returns true if the editor handled a go-to-line request
18401 pub fn go_to_active_debug_line(&mut self, window: &mut Window, cx: &mut Context<Self>) -> bool {
18402 maybe!({
18403 let breakpoint_store = self.breakpoint_store.as_ref()?;
18404
18405 let Some(active_stack_frame) = breakpoint_store.read(cx).active_position().cloned()
18406 else {
18407 self.clear_row_highlights::<ActiveDebugLine>();
18408 return None;
18409 };
18410
18411 let position = active_stack_frame.position;
18412 let buffer_id = position.buffer_id?;
18413 let snapshot = self
18414 .project
18415 .as_ref()?
18416 .read(cx)
18417 .buffer_for_id(buffer_id, cx)?
18418 .read(cx)
18419 .snapshot();
18420
18421 let mut handled = false;
18422 for (id, ExcerptRange { context, .. }) in
18423 self.buffer.read(cx).excerpts_for_buffer(buffer_id, cx)
18424 {
18425 if context.start.cmp(&position, &snapshot).is_ge()
18426 || context.end.cmp(&position, &snapshot).is_lt()
18427 {
18428 continue;
18429 }
18430 let snapshot = self.buffer.read(cx).snapshot(cx);
18431 let multibuffer_anchor = snapshot.anchor_in_excerpt(id, position)?;
18432
18433 handled = true;
18434 self.clear_row_highlights::<ActiveDebugLine>();
18435
18436 self.go_to_line::<ActiveDebugLine>(
18437 multibuffer_anchor,
18438 Some(cx.theme().colors().editor_debugger_active_line_background),
18439 window,
18440 cx,
18441 );
18442
18443 cx.notify();
18444 }
18445
18446 handled.then_some(())
18447 })
18448 .is_some()
18449 }
18450
18451 pub fn copy_file_name_without_extension(
18452 &mut self,
18453 _: &CopyFileNameWithoutExtension,
18454 _: &mut Window,
18455 cx: &mut Context<Self>,
18456 ) {
18457 if let Some(file) = self.target_file(cx) {
18458 if let Some(file_stem) = file.path().file_stem() {
18459 if let Some(name) = file_stem.to_str() {
18460 cx.write_to_clipboard(ClipboardItem::new_string(name.to_string()));
18461 }
18462 }
18463 }
18464 }
18465
18466 pub fn copy_file_name(&mut self, _: &CopyFileName, _: &mut Window, cx: &mut Context<Self>) {
18467 if let Some(file) = self.target_file(cx) {
18468 if let Some(file_name) = file.path().file_name() {
18469 if let Some(name) = file_name.to_str() {
18470 cx.write_to_clipboard(ClipboardItem::new_string(name.to_string()));
18471 }
18472 }
18473 }
18474 }
18475
18476 pub fn toggle_git_blame(
18477 &mut self,
18478 _: &::git::Blame,
18479 window: &mut Window,
18480 cx: &mut Context<Self>,
18481 ) {
18482 self.show_git_blame_gutter = !self.show_git_blame_gutter;
18483
18484 if self.show_git_blame_gutter && !self.has_blame_entries(cx) {
18485 self.start_git_blame(true, window, cx);
18486 }
18487
18488 cx.notify();
18489 }
18490
18491 pub fn toggle_git_blame_inline(
18492 &mut self,
18493 _: &ToggleGitBlameInline,
18494 window: &mut Window,
18495 cx: &mut Context<Self>,
18496 ) {
18497 self.toggle_git_blame_inline_internal(true, window, cx);
18498 cx.notify();
18499 }
18500
18501 pub fn open_git_blame_commit(
18502 &mut self,
18503 _: &OpenGitBlameCommit,
18504 window: &mut Window,
18505 cx: &mut Context<Self>,
18506 ) {
18507 self.open_git_blame_commit_internal(window, cx);
18508 }
18509
18510 fn open_git_blame_commit_internal(
18511 &mut self,
18512 window: &mut Window,
18513 cx: &mut Context<Self>,
18514 ) -> Option<()> {
18515 let blame = self.blame.as_ref()?;
18516 let snapshot = self.snapshot(window, cx);
18517 let cursor = self.selections.newest::<Point>(cx).head();
18518 let (buffer, point, _) = snapshot.buffer_snapshot.point_to_buffer_point(cursor)?;
18519 let blame_entry = blame
18520 .update(cx, |blame, cx| {
18521 blame
18522 .blame_for_rows(
18523 &[RowInfo {
18524 buffer_id: Some(buffer.remote_id()),
18525 buffer_row: Some(point.row),
18526 ..Default::default()
18527 }],
18528 cx,
18529 )
18530 .next()
18531 })
18532 .flatten()?;
18533 let renderer = cx.global::<GlobalBlameRenderer>().0.clone();
18534 let repo = blame.read(cx).repository(cx)?;
18535 let workspace = self.workspace()?.downgrade();
18536 renderer.open_blame_commit(blame_entry, repo, workspace, window, cx);
18537 None
18538 }
18539
18540 pub fn git_blame_inline_enabled(&self) -> bool {
18541 self.git_blame_inline_enabled
18542 }
18543
18544 pub fn toggle_selection_menu(
18545 &mut self,
18546 _: &ToggleSelectionMenu,
18547 _: &mut Window,
18548 cx: &mut Context<Self>,
18549 ) {
18550 self.show_selection_menu = self
18551 .show_selection_menu
18552 .map(|show_selections_menu| !show_selections_menu)
18553 .or_else(|| Some(!EditorSettings::get_global(cx).toolbar.selections_menu));
18554
18555 cx.notify();
18556 }
18557
18558 pub fn selection_menu_enabled(&self, cx: &App) -> bool {
18559 self.show_selection_menu
18560 .unwrap_or_else(|| EditorSettings::get_global(cx).toolbar.selections_menu)
18561 }
18562
18563 fn start_git_blame(
18564 &mut self,
18565 user_triggered: bool,
18566 window: &mut Window,
18567 cx: &mut Context<Self>,
18568 ) {
18569 if let Some(project) = self.project.as_ref() {
18570 let Some(buffer) = self.buffer().read(cx).as_singleton() else {
18571 return;
18572 };
18573
18574 if buffer.read(cx).file().is_none() {
18575 return;
18576 }
18577
18578 let focused = self.focus_handle(cx).contains_focused(window, cx);
18579
18580 let project = project.clone();
18581 let blame = cx.new(|cx| GitBlame::new(buffer, project, user_triggered, focused, cx));
18582 self.blame_subscription =
18583 Some(cx.observe_in(&blame, window, |_, _, _, cx| cx.notify()));
18584 self.blame = Some(blame);
18585 }
18586 }
18587
18588 fn toggle_git_blame_inline_internal(
18589 &mut self,
18590 user_triggered: bool,
18591 window: &mut Window,
18592 cx: &mut Context<Self>,
18593 ) {
18594 if self.git_blame_inline_enabled {
18595 self.git_blame_inline_enabled = false;
18596 self.show_git_blame_inline = false;
18597 self.show_git_blame_inline_delay_task.take();
18598 } else {
18599 self.git_blame_inline_enabled = true;
18600 self.start_git_blame_inline(user_triggered, window, cx);
18601 }
18602
18603 cx.notify();
18604 }
18605
18606 fn start_git_blame_inline(
18607 &mut self,
18608 user_triggered: bool,
18609 window: &mut Window,
18610 cx: &mut Context<Self>,
18611 ) {
18612 self.start_git_blame(user_triggered, window, cx);
18613
18614 if ProjectSettings::get_global(cx)
18615 .git
18616 .inline_blame_delay()
18617 .is_some()
18618 {
18619 self.start_inline_blame_timer(window, cx);
18620 } else {
18621 self.show_git_blame_inline = true
18622 }
18623 }
18624
18625 pub fn blame(&self) -> Option<&Entity<GitBlame>> {
18626 self.blame.as_ref()
18627 }
18628
18629 pub fn show_git_blame_gutter(&self) -> bool {
18630 self.show_git_blame_gutter
18631 }
18632
18633 pub fn render_git_blame_gutter(&self, cx: &App) -> bool {
18634 !self.mode().is_minimap() && self.show_git_blame_gutter && self.has_blame_entries(cx)
18635 }
18636
18637 pub fn render_git_blame_inline(&self, window: &Window, cx: &App) -> bool {
18638 self.show_git_blame_inline
18639 && (self.focus_handle.is_focused(window) || self.inline_blame_popover.is_some())
18640 && !self.newest_selection_head_on_empty_line(cx)
18641 && self.has_blame_entries(cx)
18642 }
18643
18644 fn has_blame_entries(&self, cx: &App) -> bool {
18645 self.blame()
18646 .map_or(false, |blame| blame.read(cx).has_generated_entries())
18647 }
18648
18649 fn newest_selection_head_on_empty_line(&self, cx: &App) -> bool {
18650 let cursor_anchor = self.selections.newest_anchor().head();
18651
18652 let snapshot = self.buffer.read(cx).snapshot(cx);
18653 let buffer_row = MultiBufferRow(cursor_anchor.to_point(&snapshot).row);
18654
18655 snapshot.line_len(buffer_row) == 0
18656 }
18657
18658 fn get_permalink_to_line(&self, cx: &mut Context<Self>) -> Task<Result<url::Url>> {
18659 let buffer_and_selection = maybe!({
18660 let selection = self.selections.newest::<Point>(cx);
18661 let selection_range = selection.range();
18662
18663 let multi_buffer = self.buffer().read(cx);
18664 let multi_buffer_snapshot = multi_buffer.snapshot(cx);
18665 let buffer_ranges = multi_buffer_snapshot.range_to_buffer_ranges(selection_range);
18666
18667 let (buffer, range, _) = if selection.reversed {
18668 buffer_ranges.first()
18669 } else {
18670 buffer_ranges.last()
18671 }?;
18672
18673 let selection = text::ToPoint::to_point(&range.start, &buffer).row
18674 ..text::ToPoint::to_point(&range.end, &buffer).row;
18675 Some((
18676 multi_buffer.buffer(buffer.remote_id()).unwrap().clone(),
18677 selection,
18678 ))
18679 });
18680
18681 let Some((buffer, selection)) = buffer_and_selection else {
18682 return Task::ready(Err(anyhow!("failed to determine buffer and selection")));
18683 };
18684
18685 let Some(project) = self.project.as_ref() else {
18686 return Task::ready(Err(anyhow!("editor does not have project")));
18687 };
18688
18689 project.update(cx, |project, cx| {
18690 project.get_permalink_to_line(&buffer, selection, cx)
18691 })
18692 }
18693
18694 pub fn copy_permalink_to_line(
18695 &mut self,
18696 _: &CopyPermalinkToLine,
18697 window: &mut Window,
18698 cx: &mut Context<Self>,
18699 ) {
18700 let permalink_task = self.get_permalink_to_line(cx);
18701 let workspace = self.workspace();
18702
18703 cx.spawn_in(window, async move |_, cx| match permalink_task.await {
18704 Ok(permalink) => {
18705 cx.update(|_, cx| {
18706 cx.write_to_clipboard(ClipboardItem::new_string(permalink.to_string()));
18707 })
18708 .ok();
18709 }
18710 Err(err) => {
18711 let message = format!("Failed to copy permalink: {err}");
18712
18713 anyhow::Result::<()>::Err(err).log_err();
18714
18715 if let Some(workspace) = workspace {
18716 workspace
18717 .update_in(cx, |workspace, _, cx| {
18718 struct CopyPermalinkToLine;
18719
18720 workspace.show_toast(
18721 Toast::new(
18722 NotificationId::unique::<CopyPermalinkToLine>(),
18723 message,
18724 ),
18725 cx,
18726 )
18727 })
18728 .ok();
18729 }
18730 }
18731 })
18732 .detach();
18733 }
18734
18735 pub fn copy_file_location(
18736 &mut self,
18737 _: &CopyFileLocation,
18738 _: &mut Window,
18739 cx: &mut Context<Self>,
18740 ) {
18741 let selection = self.selections.newest::<Point>(cx).start.row + 1;
18742 if let Some(file) = self.target_file(cx) {
18743 if let Some(path) = file.path().to_str() {
18744 cx.write_to_clipboard(ClipboardItem::new_string(format!("{path}:{selection}")));
18745 }
18746 }
18747 }
18748
18749 pub fn open_permalink_to_line(
18750 &mut self,
18751 _: &OpenPermalinkToLine,
18752 window: &mut Window,
18753 cx: &mut Context<Self>,
18754 ) {
18755 let permalink_task = self.get_permalink_to_line(cx);
18756 let workspace = self.workspace();
18757
18758 cx.spawn_in(window, async move |_, cx| match permalink_task.await {
18759 Ok(permalink) => {
18760 cx.update(|_, cx| {
18761 cx.open_url(permalink.as_ref());
18762 })
18763 .ok();
18764 }
18765 Err(err) => {
18766 let message = format!("Failed to open permalink: {err}");
18767
18768 anyhow::Result::<()>::Err(err).log_err();
18769
18770 if let Some(workspace) = workspace {
18771 workspace
18772 .update(cx, |workspace, cx| {
18773 struct OpenPermalinkToLine;
18774
18775 workspace.show_toast(
18776 Toast::new(
18777 NotificationId::unique::<OpenPermalinkToLine>(),
18778 message,
18779 ),
18780 cx,
18781 )
18782 })
18783 .ok();
18784 }
18785 }
18786 })
18787 .detach();
18788 }
18789
18790 pub fn insert_uuid_v4(
18791 &mut self,
18792 _: &InsertUuidV4,
18793 window: &mut Window,
18794 cx: &mut Context<Self>,
18795 ) {
18796 self.insert_uuid(UuidVersion::V4, window, cx);
18797 }
18798
18799 pub fn insert_uuid_v7(
18800 &mut self,
18801 _: &InsertUuidV7,
18802 window: &mut Window,
18803 cx: &mut Context<Self>,
18804 ) {
18805 self.insert_uuid(UuidVersion::V7, window, cx);
18806 }
18807
18808 fn insert_uuid(&mut self, version: UuidVersion, window: &mut Window, cx: &mut Context<Self>) {
18809 self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
18810 self.transact(window, cx, |this, window, cx| {
18811 let edits = this
18812 .selections
18813 .all::<Point>(cx)
18814 .into_iter()
18815 .map(|selection| {
18816 let uuid = match version {
18817 UuidVersion::V4 => uuid::Uuid::new_v4(),
18818 UuidVersion::V7 => uuid::Uuid::now_v7(),
18819 };
18820
18821 (selection.range(), uuid.to_string())
18822 });
18823 this.edit(edits, cx);
18824 this.refresh_inline_completion(true, false, window, cx);
18825 });
18826 }
18827
18828 pub fn open_selections_in_multibuffer(
18829 &mut self,
18830 _: &OpenSelectionsInMultibuffer,
18831 window: &mut Window,
18832 cx: &mut Context<Self>,
18833 ) {
18834 let multibuffer = self.buffer.read(cx);
18835
18836 let Some(buffer) = multibuffer.as_singleton() else {
18837 return;
18838 };
18839
18840 let Some(workspace) = self.workspace() else {
18841 return;
18842 };
18843
18844 let title = multibuffer.title(cx).to_string();
18845
18846 let locations = self
18847 .selections
18848 .all_anchors(cx)
18849 .into_iter()
18850 .map(|selection| Location {
18851 buffer: buffer.clone(),
18852 range: selection.start.text_anchor..selection.end.text_anchor,
18853 })
18854 .collect::<Vec<_>>();
18855
18856 cx.spawn_in(window, async move |_, cx| {
18857 workspace.update_in(cx, |workspace, window, cx| {
18858 Self::open_locations_in_multibuffer(
18859 workspace,
18860 locations,
18861 format!("Selections for '{title}'"),
18862 false,
18863 MultibufferSelectionMode::All,
18864 window,
18865 cx,
18866 );
18867 })
18868 })
18869 .detach();
18870 }
18871
18872 /// Adds a row highlight for the given range. If a row has multiple highlights, the
18873 /// last highlight added will be used.
18874 ///
18875 /// If the range ends at the beginning of a line, then that line will not be highlighted.
18876 pub fn highlight_rows<T: 'static>(
18877 &mut self,
18878 range: Range<Anchor>,
18879 color: Hsla,
18880 options: RowHighlightOptions,
18881 cx: &mut Context<Self>,
18882 ) {
18883 let snapshot = self.buffer().read(cx).snapshot(cx);
18884 let row_highlights = self.highlighted_rows.entry(TypeId::of::<T>()).or_default();
18885 let ix = row_highlights.binary_search_by(|highlight| {
18886 Ordering::Equal
18887 .then_with(|| highlight.range.start.cmp(&range.start, &snapshot))
18888 .then_with(|| highlight.range.end.cmp(&range.end, &snapshot))
18889 });
18890
18891 if let Err(mut ix) = ix {
18892 let index = post_inc(&mut self.highlight_order);
18893
18894 // If this range intersects with the preceding highlight, then merge it with
18895 // the preceding highlight. Otherwise insert a new highlight.
18896 let mut merged = false;
18897 if ix > 0 {
18898 let prev_highlight = &mut row_highlights[ix - 1];
18899 if prev_highlight
18900 .range
18901 .end
18902 .cmp(&range.start, &snapshot)
18903 .is_ge()
18904 {
18905 ix -= 1;
18906 if prev_highlight.range.end.cmp(&range.end, &snapshot).is_lt() {
18907 prev_highlight.range.end = range.end;
18908 }
18909 merged = true;
18910 prev_highlight.index = index;
18911 prev_highlight.color = color;
18912 prev_highlight.options = options;
18913 }
18914 }
18915
18916 if !merged {
18917 row_highlights.insert(
18918 ix,
18919 RowHighlight {
18920 range: range.clone(),
18921 index,
18922 color,
18923 options,
18924 type_id: TypeId::of::<T>(),
18925 },
18926 );
18927 }
18928
18929 // If any of the following highlights intersect with this one, merge them.
18930 while let Some(next_highlight) = row_highlights.get(ix + 1) {
18931 let highlight = &row_highlights[ix];
18932 if next_highlight
18933 .range
18934 .start
18935 .cmp(&highlight.range.end, &snapshot)
18936 .is_le()
18937 {
18938 if next_highlight
18939 .range
18940 .end
18941 .cmp(&highlight.range.end, &snapshot)
18942 .is_gt()
18943 {
18944 row_highlights[ix].range.end = next_highlight.range.end;
18945 }
18946 row_highlights.remove(ix + 1);
18947 } else {
18948 break;
18949 }
18950 }
18951 }
18952 }
18953
18954 /// Remove any highlighted row ranges of the given type that intersect the
18955 /// given ranges.
18956 pub fn remove_highlighted_rows<T: 'static>(
18957 &mut self,
18958 ranges_to_remove: Vec<Range<Anchor>>,
18959 cx: &mut Context<Self>,
18960 ) {
18961 let snapshot = self.buffer().read(cx).snapshot(cx);
18962 let row_highlights = self.highlighted_rows.entry(TypeId::of::<T>()).or_default();
18963 let mut ranges_to_remove = ranges_to_remove.iter().peekable();
18964 row_highlights.retain(|highlight| {
18965 while let Some(range_to_remove) = ranges_to_remove.peek() {
18966 match range_to_remove.end.cmp(&highlight.range.start, &snapshot) {
18967 Ordering::Less | Ordering::Equal => {
18968 ranges_to_remove.next();
18969 }
18970 Ordering::Greater => {
18971 match range_to_remove.start.cmp(&highlight.range.end, &snapshot) {
18972 Ordering::Less | Ordering::Equal => {
18973 return false;
18974 }
18975 Ordering::Greater => break,
18976 }
18977 }
18978 }
18979 }
18980
18981 true
18982 })
18983 }
18984
18985 /// Clear all anchor ranges for a certain highlight context type, so no corresponding rows will be highlighted.
18986 pub fn clear_row_highlights<T: 'static>(&mut self) {
18987 self.highlighted_rows.remove(&TypeId::of::<T>());
18988 }
18989
18990 /// For a highlight given context type, gets all anchor ranges that will be used for row highlighting.
18991 pub fn highlighted_rows<T: 'static>(&self) -> impl '_ + Iterator<Item = (Range<Anchor>, Hsla)> {
18992 self.highlighted_rows
18993 .get(&TypeId::of::<T>())
18994 .map_or(&[] as &[_], |vec| vec.as_slice())
18995 .iter()
18996 .map(|highlight| (highlight.range.clone(), highlight.color))
18997 }
18998
18999 /// Merges all anchor ranges for all context types ever set, picking the last highlight added in case of a row conflict.
19000 /// Returns a map of display rows that are highlighted and their corresponding highlight color.
19001 /// Allows to ignore certain kinds of highlights.
19002 pub fn highlighted_display_rows(
19003 &self,
19004 window: &mut Window,
19005 cx: &mut App,
19006 ) -> BTreeMap<DisplayRow, LineHighlight> {
19007 let snapshot = self.snapshot(window, cx);
19008 let mut used_highlight_orders = HashMap::default();
19009 self.highlighted_rows
19010 .iter()
19011 .flat_map(|(_, highlighted_rows)| highlighted_rows.iter())
19012 .fold(
19013 BTreeMap::<DisplayRow, LineHighlight>::new(),
19014 |mut unique_rows, highlight| {
19015 let start = highlight.range.start.to_display_point(&snapshot);
19016 let end = highlight.range.end.to_display_point(&snapshot);
19017 let start_row = start.row().0;
19018 let end_row = if highlight.range.end.text_anchor != text::Anchor::MAX
19019 && end.column() == 0
19020 {
19021 end.row().0.saturating_sub(1)
19022 } else {
19023 end.row().0
19024 };
19025 for row in start_row..=end_row {
19026 let used_index =
19027 used_highlight_orders.entry(row).or_insert(highlight.index);
19028 if highlight.index >= *used_index {
19029 *used_index = highlight.index;
19030 unique_rows.insert(
19031 DisplayRow(row),
19032 LineHighlight {
19033 include_gutter: highlight.options.include_gutter,
19034 border: None,
19035 background: highlight.color.into(),
19036 type_id: Some(highlight.type_id),
19037 },
19038 );
19039 }
19040 }
19041 unique_rows
19042 },
19043 )
19044 }
19045
19046 pub fn highlighted_display_row_for_autoscroll(
19047 &self,
19048 snapshot: &DisplaySnapshot,
19049 ) -> Option<DisplayRow> {
19050 self.highlighted_rows
19051 .values()
19052 .flat_map(|highlighted_rows| highlighted_rows.iter())
19053 .filter_map(|highlight| {
19054 if highlight.options.autoscroll {
19055 Some(highlight.range.start.to_display_point(snapshot).row())
19056 } else {
19057 None
19058 }
19059 })
19060 .min()
19061 }
19062
19063 pub fn set_search_within_ranges(&mut self, ranges: &[Range<Anchor>], cx: &mut Context<Self>) {
19064 self.highlight_background::<SearchWithinRange>(
19065 ranges,
19066 |colors| colors.colors().editor_document_highlight_read_background,
19067 cx,
19068 )
19069 }
19070
19071 pub fn set_breadcrumb_header(&mut self, new_header: String) {
19072 self.breadcrumb_header = Some(new_header);
19073 }
19074
19075 pub fn clear_search_within_ranges(&mut self, cx: &mut Context<Self>) {
19076 self.clear_background_highlights::<SearchWithinRange>(cx);
19077 }
19078
19079 pub fn highlight_background<T: 'static>(
19080 &mut self,
19081 ranges: &[Range<Anchor>],
19082 color_fetcher: fn(&Theme) -> Hsla,
19083 cx: &mut Context<Self>,
19084 ) {
19085 self.background_highlights.insert(
19086 HighlightKey::Type(TypeId::of::<T>()),
19087 (color_fetcher, Arc::from(ranges)),
19088 );
19089 self.scrollbar_marker_state.dirty = true;
19090 cx.notify();
19091 }
19092
19093 pub fn highlight_background_key<T: 'static>(
19094 &mut self,
19095 key: usize,
19096 ranges: &[Range<Anchor>],
19097 color_fetcher: fn(&Theme) -> Hsla,
19098 cx: &mut Context<Self>,
19099 ) {
19100 self.background_highlights.insert(
19101 HighlightKey::TypePlus(TypeId::of::<T>(), key),
19102 (color_fetcher, Arc::from(ranges)),
19103 );
19104 self.scrollbar_marker_state.dirty = true;
19105 cx.notify();
19106 }
19107
19108 pub fn clear_background_highlights<T: 'static>(
19109 &mut self,
19110 cx: &mut Context<Self>,
19111 ) -> Option<BackgroundHighlight> {
19112 let text_highlights = self
19113 .background_highlights
19114 .remove(&HighlightKey::Type(TypeId::of::<T>()))?;
19115 if !text_highlights.1.is_empty() {
19116 self.scrollbar_marker_state.dirty = true;
19117 cx.notify();
19118 }
19119 Some(text_highlights)
19120 }
19121
19122 pub fn highlight_gutter<T: 'static>(
19123 &mut self,
19124 ranges: impl Into<Vec<Range<Anchor>>>,
19125 color_fetcher: fn(&App) -> Hsla,
19126 cx: &mut Context<Self>,
19127 ) {
19128 self.gutter_highlights
19129 .insert(TypeId::of::<T>(), (color_fetcher, ranges.into()));
19130 cx.notify();
19131 }
19132
19133 pub fn clear_gutter_highlights<T: 'static>(
19134 &mut self,
19135 cx: &mut Context<Self>,
19136 ) -> Option<GutterHighlight> {
19137 cx.notify();
19138 self.gutter_highlights.remove(&TypeId::of::<T>())
19139 }
19140
19141 pub fn insert_gutter_highlight<T: 'static>(
19142 &mut self,
19143 range: Range<Anchor>,
19144 color_fetcher: fn(&App) -> Hsla,
19145 cx: &mut Context<Self>,
19146 ) {
19147 let snapshot = self.buffer().read(cx).snapshot(cx);
19148 let mut highlights = self
19149 .gutter_highlights
19150 .remove(&TypeId::of::<T>())
19151 .map(|(_, highlights)| highlights)
19152 .unwrap_or_default();
19153 let ix = highlights.binary_search_by(|highlight| {
19154 Ordering::Equal
19155 .then_with(|| highlight.start.cmp(&range.start, &snapshot))
19156 .then_with(|| highlight.end.cmp(&range.end, &snapshot))
19157 });
19158 if let Err(ix) = ix {
19159 highlights.insert(ix, range);
19160 }
19161 self.gutter_highlights
19162 .insert(TypeId::of::<T>(), (color_fetcher, highlights));
19163 }
19164
19165 pub fn remove_gutter_highlights<T: 'static>(
19166 &mut self,
19167 ranges_to_remove: Vec<Range<Anchor>>,
19168 cx: &mut Context<Self>,
19169 ) {
19170 let snapshot = self.buffer().read(cx).snapshot(cx);
19171 let Some((color_fetcher, mut gutter_highlights)) =
19172 self.gutter_highlights.remove(&TypeId::of::<T>())
19173 else {
19174 return;
19175 };
19176 let mut ranges_to_remove = ranges_to_remove.iter().peekable();
19177 gutter_highlights.retain(|highlight| {
19178 while let Some(range_to_remove) = ranges_to_remove.peek() {
19179 match range_to_remove.end.cmp(&highlight.start, &snapshot) {
19180 Ordering::Less | Ordering::Equal => {
19181 ranges_to_remove.next();
19182 }
19183 Ordering::Greater => {
19184 match range_to_remove.start.cmp(&highlight.end, &snapshot) {
19185 Ordering::Less | Ordering::Equal => {
19186 return false;
19187 }
19188 Ordering::Greater => break,
19189 }
19190 }
19191 }
19192 }
19193
19194 true
19195 });
19196 self.gutter_highlights
19197 .insert(TypeId::of::<T>(), (color_fetcher, gutter_highlights));
19198 }
19199
19200 #[cfg(feature = "test-support")]
19201 pub fn all_text_highlights(
19202 &self,
19203 window: &mut Window,
19204 cx: &mut Context<Self>,
19205 ) -> Vec<(HighlightStyle, Vec<Range<DisplayPoint>>)> {
19206 let snapshot = self.snapshot(window, cx);
19207 self.display_map.update(cx, |display_map, _| {
19208 display_map
19209 .all_text_highlights()
19210 .map(|highlight| {
19211 let (style, ranges) = highlight.as_ref();
19212 (
19213 *style,
19214 ranges
19215 .iter()
19216 .map(|range| range.clone().to_display_points(&snapshot))
19217 .collect(),
19218 )
19219 })
19220 .collect()
19221 })
19222 }
19223
19224 #[cfg(feature = "test-support")]
19225 pub fn all_text_background_highlights(
19226 &self,
19227 window: &mut Window,
19228 cx: &mut Context<Self>,
19229 ) -> Vec<(Range<DisplayPoint>, Hsla)> {
19230 let snapshot = self.snapshot(window, cx);
19231 let buffer = &snapshot.buffer_snapshot;
19232 let start = buffer.anchor_before(0);
19233 let end = buffer.anchor_after(buffer.len());
19234 self.background_highlights_in_range(start..end, &snapshot, cx.theme())
19235 }
19236
19237 #[cfg(feature = "test-support")]
19238 pub fn search_background_highlights(&mut self, cx: &mut Context<Self>) -> Vec<Range<Point>> {
19239 let snapshot = self.buffer().read(cx).snapshot(cx);
19240
19241 let highlights = self
19242 .background_highlights
19243 .get(&HighlightKey::Type(TypeId::of::<
19244 items::BufferSearchHighlights,
19245 >()));
19246
19247 if let Some((_color, ranges)) = highlights {
19248 ranges
19249 .iter()
19250 .map(|range| range.start.to_point(&snapshot)..range.end.to_point(&snapshot))
19251 .collect_vec()
19252 } else {
19253 vec![]
19254 }
19255 }
19256
19257 fn document_highlights_for_position<'a>(
19258 &'a self,
19259 position: Anchor,
19260 buffer: &'a MultiBufferSnapshot,
19261 ) -> impl 'a + Iterator<Item = &'a Range<Anchor>> {
19262 let read_highlights = self
19263 .background_highlights
19264 .get(&HighlightKey::Type(TypeId::of::<DocumentHighlightRead>()))
19265 .map(|h| &h.1);
19266 let write_highlights = self
19267 .background_highlights
19268 .get(&HighlightKey::Type(TypeId::of::<DocumentHighlightWrite>()))
19269 .map(|h| &h.1);
19270 let left_position = position.bias_left(buffer);
19271 let right_position = position.bias_right(buffer);
19272 read_highlights
19273 .into_iter()
19274 .chain(write_highlights)
19275 .flat_map(move |ranges| {
19276 let start_ix = match ranges.binary_search_by(|probe| {
19277 let cmp = probe.end.cmp(&left_position, buffer);
19278 if cmp.is_ge() {
19279 Ordering::Greater
19280 } else {
19281 Ordering::Less
19282 }
19283 }) {
19284 Ok(i) | Err(i) => i,
19285 };
19286
19287 ranges[start_ix..]
19288 .iter()
19289 .take_while(move |range| range.start.cmp(&right_position, buffer).is_le())
19290 })
19291 }
19292
19293 pub fn has_background_highlights<T: 'static>(&self) -> bool {
19294 self.background_highlights
19295 .get(&HighlightKey::Type(TypeId::of::<T>()))
19296 .map_or(false, |(_, highlights)| !highlights.is_empty())
19297 }
19298
19299 pub fn background_highlights_in_range(
19300 &self,
19301 search_range: Range<Anchor>,
19302 display_snapshot: &DisplaySnapshot,
19303 theme: &Theme,
19304 ) -> Vec<(Range<DisplayPoint>, Hsla)> {
19305 let mut results = Vec::new();
19306 for (color_fetcher, ranges) in self.background_highlights.values() {
19307 let color = color_fetcher(theme);
19308 let start_ix = match ranges.binary_search_by(|probe| {
19309 let cmp = probe
19310 .end
19311 .cmp(&search_range.start, &display_snapshot.buffer_snapshot);
19312 if cmp.is_gt() {
19313 Ordering::Greater
19314 } else {
19315 Ordering::Less
19316 }
19317 }) {
19318 Ok(i) | Err(i) => i,
19319 };
19320 for range in &ranges[start_ix..] {
19321 if range
19322 .start
19323 .cmp(&search_range.end, &display_snapshot.buffer_snapshot)
19324 .is_ge()
19325 {
19326 break;
19327 }
19328
19329 let start = range.start.to_display_point(display_snapshot);
19330 let end = range.end.to_display_point(display_snapshot);
19331 results.push((start..end, color))
19332 }
19333 }
19334 results
19335 }
19336
19337 pub fn background_highlight_row_ranges<T: 'static>(
19338 &self,
19339 search_range: Range<Anchor>,
19340 display_snapshot: &DisplaySnapshot,
19341 count: usize,
19342 ) -> Vec<RangeInclusive<DisplayPoint>> {
19343 let mut results = Vec::new();
19344 let Some((_, ranges)) = self
19345 .background_highlights
19346 .get(&HighlightKey::Type(TypeId::of::<T>()))
19347 else {
19348 return vec![];
19349 };
19350
19351 let start_ix = match ranges.binary_search_by(|probe| {
19352 let cmp = probe
19353 .end
19354 .cmp(&search_range.start, &display_snapshot.buffer_snapshot);
19355 if cmp.is_gt() {
19356 Ordering::Greater
19357 } else {
19358 Ordering::Less
19359 }
19360 }) {
19361 Ok(i) | Err(i) => i,
19362 };
19363 let mut push_region = |start: Option<Point>, end: Option<Point>| {
19364 if let (Some(start_display), Some(end_display)) = (start, end) {
19365 results.push(
19366 start_display.to_display_point(display_snapshot)
19367 ..=end_display.to_display_point(display_snapshot),
19368 );
19369 }
19370 };
19371 let mut start_row: Option<Point> = None;
19372 let mut end_row: Option<Point> = None;
19373 if ranges.len() > count {
19374 return Vec::new();
19375 }
19376 for range in &ranges[start_ix..] {
19377 if range
19378 .start
19379 .cmp(&search_range.end, &display_snapshot.buffer_snapshot)
19380 .is_ge()
19381 {
19382 break;
19383 }
19384 let end = range.end.to_point(&display_snapshot.buffer_snapshot);
19385 if let Some(current_row) = &end_row {
19386 if end.row == current_row.row {
19387 continue;
19388 }
19389 }
19390 let start = range.start.to_point(&display_snapshot.buffer_snapshot);
19391 if start_row.is_none() {
19392 assert_eq!(end_row, None);
19393 start_row = Some(start);
19394 end_row = Some(end);
19395 continue;
19396 }
19397 if let Some(current_end) = end_row.as_mut() {
19398 if start.row > current_end.row + 1 {
19399 push_region(start_row, end_row);
19400 start_row = Some(start);
19401 end_row = Some(end);
19402 } else {
19403 // Merge two hunks.
19404 *current_end = end;
19405 }
19406 } else {
19407 unreachable!();
19408 }
19409 }
19410 // We might still have a hunk that was not rendered (if there was a search hit on the last line)
19411 push_region(start_row, end_row);
19412 results
19413 }
19414
19415 pub fn gutter_highlights_in_range(
19416 &self,
19417 search_range: Range<Anchor>,
19418 display_snapshot: &DisplaySnapshot,
19419 cx: &App,
19420 ) -> Vec<(Range<DisplayPoint>, Hsla)> {
19421 let mut results = Vec::new();
19422 for (color_fetcher, ranges) in self.gutter_highlights.values() {
19423 let color = color_fetcher(cx);
19424 let start_ix = match ranges.binary_search_by(|probe| {
19425 let cmp = probe
19426 .end
19427 .cmp(&search_range.start, &display_snapshot.buffer_snapshot);
19428 if cmp.is_gt() {
19429 Ordering::Greater
19430 } else {
19431 Ordering::Less
19432 }
19433 }) {
19434 Ok(i) | Err(i) => i,
19435 };
19436 for range in &ranges[start_ix..] {
19437 if range
19438 .start
19439 .cmp(&search_range.end, &display_snapshot.buffer_snapshot)
19440 .is_ge()
19441 {
19442 break;
19443 }
19444
19445 let start = range.start.to_display_point(display_snapshot);
19446 let end = range.end.to_display_point(display_snapshot);
19447 results.push((start..end, color))
19448 }
19449 }
19450 results
19451 }
19452
19453 /// Get the text ranges corresponding to the redaction query
19454 pub fn redacted_ranges(
19455 &self,
19456 search_range: Range<Anchor>,
19457 display_snapshot: &DisplaySnapshot,
19458 cx: &App,
19459 ) -> Vec<Range<DisplayPoint>> {
19460 display_snapshot
19461 .buffer_snapshot
19462 .redacted_ranges(search_range, |file| {
19463 if let Some(file) = file {
19464 file.is_private()
19465 && EditorSettings::get(
19466 Some(SettingsLocation {
19467 worktree_id: file.worktree_id(cx),
19468 path: file.path().as_ref(),
19469 }),
19470 cx,
19471 )
19472 .redact_private_values
19473 } else {
19474 false
19475 }
19476 })
19477 .map(|range| {
19478 range.start.to_display_point(display_snapshot)
19479 ..range.end.to_display_point(display_snapshot)
19480 })
19481 .collect()
19482 }
19483
19484 pub fn highlight_text_key<T: 'static>(
19485 &mut self,
19486 key: usize,
19487 ranges: Vec<Range<Anchor>>,
19488 style: HighlightStyle,
19489 cx: &mut Context<Self>,
19490 ) {
19491 self.display_map.update(cx, |map, _| {
19492 map.highlight_text(
19493 HighlightKey::TypePlus(TypeId::of::<T>(), key),
19494 ranges,
19495 style,
19496 );
19497 });
19498 cx.notify();
19499 }
19500
19501 pub fn highlight_text<T: 'static>(
19502 &mut self,
19503 ranges: Vec<Range<Anchor>>,
19504 style: HighlightStyle,
19505 cx: &mut Context<Self>,
19506 ) {
19507 self.display_map.update(cx, |map, _| {
19508 map.highlight_text(HighlightKey::Type(TypeId::of::<T>()), ranges, style)
19509 });
19510 cx.notify();
19511 }
19512
19513 pub(crate) fn highlight_inlays<T: 'static>(
19514 &mut self,
19515 highlights: Vec<InlayHighlight>,
19516 style: HighlightStyle,
19517 cx: &mut Context<Self>,
19518 ) {
19519 self.display_map.update(cx, |map, _| {
19520 map.highlight_inlays(TypeId::of::<T>(), highlights, style)
19521 });
19522 cx.notify();
19523 }
19524
19525 pub fn text_highlights<'a, T: 'static>(
19526 &'a self,
19527 cx: &'a App,
19528 ) -> Option<(HighlightStyle, &'a [Range<Anchor>])> {
19529 self.display_map.read(cx).text_highlights(TypeId::of::<T>())
19530 }
19531
19532 pub fn clear_highlights<T: 'static>(&mut self, cx: &mut Context<Self>) {
19533 let cleared = self
19534 .display_map
19535 .update(cx, |map, _| map.clear_highlights(TypeId::of::<T>()));
19536 if cleared {
19537 cx.notify();
19538 }
19539 }
19540
19541 pub fn show_local_cursors(&self, window: &mut Window, cx: &mut App) -> bool {
19542 (self.read_only(cx) || self.blink_manager.read(cx).visible())
19543 && self.focus_handle.is_focused(window)
19544 }
19545
19546 pub fn set_show_cursor_when_unfocused(&mut self, is_enabled: bool, cx: &mut Context<Self>) {
19547 self.show_cursor_when_unfocused = is_enabled;
19548 cx.notify();
19549 }
19550
19551 fn on_buffer_changed(&mut self, _: Entity<MultiBuffer>, cx: &mut Context<Self>) {
19552 cx.notify();
19553 }
19554
19555 fn on_debug_session_event(
19556 &mut self,
19557 _session: Entity<Session>,
19558 event: &SessionEvent,
19559 cx: &mut Context<Self>,
19560 ) {
19561 match event {
19562 SessionEvent::InvalidateInlineValue => {
19563 self.refresh_inline_values(cx);
19564 }
19565 _ => {}
19566 }
19567 }
19568
19569 pub fn refresh_inline_values(&mut self, cx: &mut Context<Self>) {
19570 let Some(project) = self.project.clone() else {
19571 return;
19572 };
19573
19574 if !self.inline_value_cache.enabled {
19575 let inlays = std::mem::take(&mut self.inline_value_cache.inlays);
19576 self.splice_inlays(&inlays, Vec::new(), cx);
19577 return;
19578 }
19579
19580 let current_execution_position = self
19581 .highlighted_rows
19582 .get(&TypeId::of::<ActiveDebugLine>())
19583 .and_then(|lines| lines.last().map(|line| line.range.end));
19584
19585 self.inline_value_cache.refresh_task = cx.spawn(async move |editor, cx| {
19586 let inline_values = editor
19587 .update(cx, |editor, cx| {
19588 let Some(current_execution_position) = current_execution_position else {
19589 return Some(Task::ready(Ok(Vec::new())));
19590 };
19591
19592 let buffer = editor.buffer.read_with(cx, |buffer, cx| {
19593 let snapshot = buffer.snapshot(cx);
19594
19595 let excerpt = snapshot.excerpt_containing(
19596 current_execution_position..current_execution_position,
19597 )?;
19598
19599 editor.buffer.read(cx).buffer(excerpt.buffer_id())
19600 })?;
19601
19602 let range =
19603 buffer.read(cx).anchor_before(0)..current_execution_position.text_anchor;
19604
19605 project.inline_values(buffer, range, cx)
19606 })
19607 .ok()
19608 .flatten()?
19609 .await
19610 .context("refreshing debugger inlays")
19611 .log_err()?;
19612
19613 let mut buffer_inline_values: HashMap<BufferId, Vec<InlayHint>> = HashMap::default();
19614
19615 for (buffer_id, inline_value) in inline_values
19616 .into_iter()
19617 .filter_map(|hint| Some((hint.position.buffer_id?, hint)))
19618 {
19619 buffer_inline_values
19620 .entry(buffer_id)
19621 .or_default()
19622 .push(inline_value);
19623 }
19624
19625 editor
19626 .update(cx, |editor, cx| {
19627 let snapshot = editor.buffer.read(cx).snapshot(cx);
19628 let mut new_inlays = Vec::default();
19629
19630 for (excerpt_id, buffer_snapshot, _) in snapshot.excerpts() {
19631 let buffer_id = buffer_snapshot.remote_id();
19632 buffer_inline_values
19633 .get(&buffer_id)
19634 .into_iter()
19635 .flatten()
19636 .for_each(|hint| {
19637 let inlay = Inlay::debugger(
19638 post_inc(&mut editor.next_inlay_id),
19639 Anchor::in_buffer(excerpt_id, buffer_id, hint.position),
19640 hint.text(),
19641 );
19642
19643 new_inlays.push(inlay);
19644 });
19645 }
19646
19647 let mut inlay_ids = new_inlays.iter().map(|inlay| inlay.id).collect();
19648 std::mem::swap(&mut editor.inline_value_cache.inlays, &mut inlay_ids);
19649
19650 editor.splice_inlays(&inlay_ids, new_inlays, cx);
19651 })
19652 .ok()?;
19653 Some(())
19654 });
19655 }
19656
19657 fn on_buffer_event(
19658 &mut self,
19659 multibuffer: &Entity<MultiBuffer>,
19660 event: &multi_buffer::Event,
19661 window: &mut Window,
19662 cx: &mut Context<Self>,
19663 ) {
19664 match event {
19665 multi_buffer::Event::Edited {
19666 singleton_buffer_edited,
19667 edited_buffer,
19668 } => {
19669 self.scrollbar_marker_state.dirty = true;
19670 self.active_indent_guides_state.dirty = true;
19671 self.refresh_active_diagnostics(cx);
19672 self.refresh_code_actions(window, cx);
19673 self.refresh_selected_text_highlights(true, window, cx);
19674 self.refresh_single_line_folds(window, cx);
19675 refresh_matching_bracket_highlights(self, window, cx);
19676 if self.has_active_inline_completion() {
19677 self.update_visible_inline_completion(window, cx);
19678 }
19679 if let Some(project) = self.project.as_ref() {
19680 if let Some(edited_buffer) = edited_buffer {
19681 project.update(cx, |project, cx| {
19682 self.registered_buffers
19683 .entry(edited_buffer.read(cx).remote_id())
19684 .or_insert_with(|| {
19685 project
19686 .register_buffer_with_language_servers(&edited_buffer, cx)
19687 });
19688 });
19689 }
19690 }
19691 cx.emit(EditorEvent::BufferEdited);
19692 cx.emit(SearchEvent::MatchesInvalidated);
19693
19694 if let Some(buffer) = edited_buffer {
19695 self.update_lsp_data(false, Some(buffer.read(cx).remote_id()), window, cx);
19696 }
19697
19698 if *singleton_buffer_edited {
19699 if let Some(buffer) = edited_buffer {
19700 if buffer.read(cx).file().is_none() {
19701 cx.emit(EditorEvent::TitleChanged);
19702 }
19703 }
19704 if let Some(project) = &self.project {
19705 #[allow(clippy::mutable_key_type)]
19706 let languages_affected = multibuffer.update(cx, |multibuffer, cx| {
19707 multibuffer
19708 .all_buffers()
19709 .into_iter()
19710 .filter_map(|buffer| {
19711 buffer.update(cx, |buffer, cx| {
19712 let language = buffer.language()?;
19713 let should_discard = project.update(cx, |project, cx| {
19714 project.is_local()
19715 && !project.has_language_servers_for(buffer, cx)
19716 });
19717 should_discard.not().then_some(language.clone())
19718 })
19719 })
19720 .collect::<HashSet<_>>()
19721 });
19722 if !languages_affected.is_empty() {
19723 self.refresh_inlay_hints(
19724 InlayHintRefreshReason::BufferEdited(languages_affected),
19725 cx,
19726 );
19727 }
19728 }
19729 }
19730
19731 let Some(project) = &self.project else { return };
19732 let (telemetry, is_via_ssh) = {
19733 let project = project.read(cx);
19734 let telemetry = project.client().telemetry().clone();
19735 let is_via_ssh = project.is_via_ssh();
19736 (telemetry, is_via_ssh)
19737 };
19738 refresh_linked_ranges(self, window, cx);
19739 telemetry.log_edit_event("editor", is_via_ssh);
19740 }
19741 multi_buffer::Event::ExcerptsAdded {
19742 buffer,
19743 predecessor,
19744 excerpts,
19745 } => {
19746 self.tasks_update_task = Some(self.refresh_runnables(window, cx));
19747 let buffer_id = buffer.read(cx).remote_id();
19748 if self.buffer.read(cx).diff_for(buffer_id).is_none() {
19749 if let Some(project) = &self.project {
19750 update_uncommitted_diff_for_buffer(
19751 cx.entity(),
19752 project,
19753 [buffer.clone()],
19754 self.buffer.clone(),
19755 cx,
19756 )
19757 .detach();
19758 }
19759 }
19760 self.update_lsp_data(false, Some(buffer_id), window, cx);
19761 cx.emit(EditorEvent::ExcerptsAdded {
19762 buffer: buffer.clone(),
19763 predecessor: *predecessor,
19764 excerpts: excerpts.clone(),
19765 });
19766 self.refresh_inlay_hints(InlayHintRefreshReason::NewLinesShown, cx);
19767 }
19768 multi_buffer::Event::ExcerptsRemoved {
19769 ids,
19770 removed_buffer_ids,
19771 } => {
19772 self.refresh_inlay_hints(InlayHintRefreshReason::ExcerptsRemoved(ids.clone()), cx);
19773 let buffer = self.buffer.read(cx);
19774 self.registered_buffers
19775 .retain(|buffer_id, _| buffer.buffer(*buffer_id).is_some());
19776 jsx_tag_auto_close::refresh_enabled_in_any_buffer(self, multibuffer, cx);
19777 cx.emit(EditorEvent::ExcerptsRemoved {
19778 ids: ids.clone(),
19779 removed_buffer_ids: removed_buffer_ids.clone(),
19780 });
19781 }
19782 multi_buffer::Event::ExcerptsEdited {
19783 excerpt_ids,
19784 buffer_ids,
19785 } => {
19786 self.display_map.update(cx, |map, cx| {
19787 map.unfold_buffers(buffer_ids.iter().copied(), cx)
19788 });
19789 cx.emit(EditorEvent::ExcerptsEdited {
19790 ids: excerpt_ids.clone(),
19791 });
19792 }
19793 multi_buffer::Event::ExcerptsExpanded { ids } => {
19794 self.refresh_inlay_hints(InlayHintRefreshReason::NewLinesShown, cx);
19795 cx.emit(EditorEvent::ExcerptsExpanded { ids: ids.clone() })
19796 }
19797 multi_buffer::Event::Reparsed(buffer_id) => {
19798 self.tasks_update_task = Some(self.refresh_runnables(window, cx));
19799 jsx_tag_auto_close::refresh_enabled_in_any_buffer(self, multibuffer, cx);
19800
19801 cx.emit(EditorEvent::Reparsed(*buffer_id));
19802 }
19803 multi_buffer::Event::DiffHunksToggled => {
19804 self.tasks_update_task = Some(self.refresh_runnables(window, cx));
19805 }
19806 multi_buffer::Event::LanguageChanged(buffer_id) => {
19807 linked_editing_ranges::refresh_linked_ranges(self, window, cx);
19808 jsx_tag_auto_close::refresh_enabled_in_any_buffer(self, multibuffer, cx);
19809 cx.emit(EditorEvent::Reparsed(*buffer_id));
19810 cx.notify();
19811 }
19812 multi_buffer::Event::DirtyChanged => cx.emit(EditorEvent::DirtyChanged),
19813 multi_buffer::Event::Saved => cx.emit(EditorEvent::Saved),
19814 multi_buffer::Event::FileHandleChanged
19815 | multi_buffer::Event::Reloaded
19816 | multi_buffer::Event::BufferDiffChanged => cx.emit(EditorEvent::TitleChanged),
19817 multi_buffer::Event::Closed => cx.emit(EditorEvent::Closed),
19818 multi_buffer::Event::DiagnosticsUpdated => {
19819 self.update_diagnostics_state(window, cx);
19820 }
19821 _ => {}
19822 };
19823 }
19824
19825 fn update_diagnostics_state(&mut self, window: &mut Window, cx: &mut Context<'_, Editor>) {
19826 if !self.diagnostics_enabled() {
19827 return;
19828 }
19829 self.refresh_active_diagnostics(cx);
19830 self.refresh_inline_diagnostics(true, window, cx);
19831 self.scrollbar_marker_state.dirty = true;
19832 cx.notify();
19833 }
19834
19835 pub fn start_temporary_diff_override(&mut self) {
19836 self.load_diff_task.take();
19837 self.temporary_diff_override = true;
19838 }
19839
19840 pub fn end_temporary_diff_override(&mut self, cx: &mut Context<Self>) {
19841 self.temporary_diff_override = false;
19842 self.set_render_diff_hunk_controls(Arc::new(render_diff_hunk_controls), cx);
19843 self.buffer.update(cx, |buffer, cx| {
19844 buffer.set_all_diff_hunks_collapsed(cx);
19845 });
19846
19847 if let Some(project) = self.project.clone() {
19848 self.load_diff_task = Some(
19849 update_uncommitted_diff_for_buffer(
19850 cx.entity(),
19851 &project,
19852 self.buffer.read(cx).all_buffers(),
19853 self.buffer.clone(),
19854 cx,
19855 )
19856 .shared(),
19857 );
19858 }
19859 }
19860
19861 fn on_display_map_changed(
19862 &mut self,
19863 _: Entity<DisplayMap>,
19864 _: &mut Window,
19865 cx: &mut Context<Self>,
19866 ) {
19867 cx.notify();
19868 }
19869
19870 fn settings_changed(&mut self, window: &mut Window, cx: &mut Context<Self>) {
19871 let new_severity = if self.diagnostics_enabled() {
19872 EditorSettings::get_global(cx)
19873 .diagnostics_max_severity
19874 .unwrap_or(DiagnosticSeverity::Hint)
19875 } else {
19876 DiagnosticSeverity::Off
19877 };
19878 self.set_max_diagnostics_severity(new_severity, cx);
19879 self.tasks_update_task = Some(self.refresh_runnables(window, cx));
19880 self.update_edit_prediction_settings(cx);
19881 self.refresh_inline_completion(true, false, window, cx);
19882 self.refresh_inline_values(cx);
19883 self.refresh_inlay_hints(
19884 InlayHintRefreshReason::SettingsChange(inlay_hint_settings(
19885 self.selections.newest_anchor().head(),
19886 &self.buffer.read(cx).snapshot(cx),
19887 cx,
19888 )),
19889 cx,
19890 );
19891
19892 let old_cursor_shape = self.cursor_shape;
19893
19894 {
19895 let editor_settings = EditorSettings::get_global(cx);
19896 self.scroll_manager.vertical_scroll_margin = editor_settings.vertical_scroll_margin;
19897 self.show_breadcrumbs = editor_settings.toolbar.breadcrumbs;
19898 self.cursor_shape = editor_settings.cursor_shape.unwrap_or_default();
19899 self.hide_mouse_mode = editor_settings.hide_mouse.unwrap_or_default();
19900 }
19901
19902 if old_cursor_shape != self.cursor_shape {
19903 cx.emit(EditorEvent::CursorShapeChanged);
19904 }
19905
19906 let project_settings = ProjectSettings::get_global(cx);
19907 self.serialize_dirty_buffers =
19908 !self.mode.is_minimap() && project_settings.session.restore_unsaved_buffers;
19909
19910 if self.mode.is_full() {
19911 let show_inline_diagnostics = project_settings.diagnostics.inline.enabled;
19912 let inline_blame_enabled = project_settings.git.inline_blame_enabled();
19913 if self.show_inline_diagnostics != show_inline_diagnostics {
19914 self.show_inline_diagnostics = show_inline_diagnostics;
19915 self.refresh_inline_diagnostics(false, window, cx);
19916 }
19917
19918 if self.git_blame_inline_enabled != inline_blame_enabled {
19919 self.toggle_git_blame_inline_internal(false, window, cx);
19920 }
19921
19922 let minimap_settings = EditorSettings::get_global(cx).minimap;
19923 if self.minimap_visibility != MinimapVisibility::Disabled {
19924 if self.minimap_visibility.settings_visibility()
19925 != minimap_settings.minimap_enabled()
19926 {
19927 self.set_minimap_visibility(
19928 MinimapVisibility::for_mode(self.mode(), cx),
19929 window,
19930 cx,
19931 );
19932 } else if let Some(minimap_entity) = self.minimap.as_ref() {
19933 minimap_entity.update(cx, |minimap_editor, cx| {
19934 minimap_editor.update_minimap_configuration(minimap_settings, cx)
19935 })
19936 }
19937 }
19938 }
19939
19940 if let Some(inlay_splice) = self.colors.as_mut().and_then(|colors| {
19941 colors.render_mode_updated(EditorSettings::get_global(cx).lsp_document_colors)
19942 }) {
19943 if !inlay_splice.to_insert.is_empty() || !inlay_splice.to_remove.is_empty() {
19944 self.splice_inlays(&inlay_splice.to_remove, inlay_splice.to_insert, cx);
19945 }
19946 self.refresh_colors(false, None, window, cx);
19947 }
19948
19949 cx.notify();
19950 }
19951
19952 pub fn set_searchable(&mut self, searchable: bool) {
19953 self.searchable = searchable;
19954 }
19955
19956 pub fn searchable(&self) -> bool {
19957 self.searchable
19958 }
19959
19960 fn open_proposed_changes_editor(
19961 &mut self,
19962 _: &OpenProposedChangesEditor,
19963 window: &mut Window,
19964 cx: &mut Context<Self>,
19965 ) {
19966 let Some(workspace) = self.workspace() else {
19967 cx.propagate();
19968 return;
19969 };
19970
19971 let selections = self.selections.all::<usize>(cx);
19972 let multi_buffer = self.buffer.read(cx);
19973 let multi_buffer_snapshot = multi_buffer.snapshot(cx);
19974 let mut new_selections_by_buffer = HashMap::default();
19975 for selection in selections {
19976 for (buffer, range, _) in
19977 multi_buffer_snapshot.range_to_buffer_ranges(selection.start..selection.end)
19978 {
19979 let mut range = range.to_point(buffer);
19980 range.start.column = 0;
19981 range.end.column = buffer.line_len(range.end.row);
19982 new_selections_by_buffer
19983 .entry(multi_buffer.buffer(buffer.remote_id()).unwrap())
19984 .or_insert(Vec::new())
19985 .push(range)
19986 }
19987 }
19988
19989 let proposed_changes_buffers = new_selections_by_buffer
19990 .into_iter()
19991 .map(|(buffer, ranges)| ProposedChangeLocation { buffer, ranges })
19992 .collect::<Vec<_>>();
19993 let proposed_changes_editor = cx.new(|cx| {
19994 ProposedChangesEditor::new(
19995 "Proposed changes",
19996 proposed_changes_buffers,
19997 self.project.clone(),
19998 window,
19999 cx,
20000 )
20001 });
20002
20003 window.defer(cx, move |window, cx| {
20004 workspace.update(cx, |workspace, cx| {
20005 workspace.active_pane().update(cx, |pane, cx| {
20006 pane.add_item(
20007 Box::new(proposed_changes_editor),
20008 true,
20009 true,
20010 None,
20011 window,
20012 cx,
20013 );
20014 });
20015 });
20016 });
20017 }
20018
20019 pub fn open_excerpts_in_split(
20020 &mut self,
20021 _: &OpenExcerptsSplit,
20022 window: &mut Window,
20023 cx: &mut Context<Self>,
20024 ) {
20025 self.open_excerpts_common(None, true, window, cx)
20026 }
20027
20028 pub fn open_excerpts(&mut self, _: &OpenExcerpts, window: &mut Window, cx: &mut Context<Self>) {
20029 self.open_excerpts_common(None, false, window, cx)
20030 }
20031
20032 fn open_excerpts_common(
20033 &mut self,
20034 jump_data: Option<JumpData>,
20035 split: bool,
20036 window: &mut Window,
20037 cx: &mut Context<Self>,
20038 ) {
20039 let Some(workspace) = self.workspace() else {
20040 cx.propagate();
20041 return;
20042 };
20043
20044 if self.buffer.read(cx).is_singleton() {
20045 cx.propagate();
20046 return;
20047 }
20048
20049 let mut new_selections_by_buffer = HashMap::default();
20050 match &jump_data {
20051 Some(JumpData::MultiBufferPoint {
20052 excerpt_id,
20053 position,
20054 anchor,
20055 line_offset_from_top,
20056 }) => {
20057 let multi_buffer_snapshot = self.buffer.read(cx).snapshot(cx);
20058 if let Some(buffer) = multi_buffer_snapshot
20059 .buffer_id_for_excerpt(*excerpt_id)
20060 .and_then(|buffer_id| self.buffer.read(cx).buffer(buffer_id))
20061 {
20062 let buffer_snapshot = buffer.read(cx).snapshot();
20063 let jump_to_point = if buffer_snapshot.can_resolve(anchor) {
20064 language::ToPoint::to_point(anchor, &buffer_snapshot)
20065 } else {
20066 buffer_snapshot.clip_point(*position, Bias::Left)
20067 };
20068 let jump_to_offset = buffer_snapshot.point_to_offset(jump_to_point);
20069 new_selections_by_buffer.insert(
20070 buffer,
20071 (
20072 vec![jump_to_offset..jump_to_offset],
20073 Some(*line_offset_from_top),
20074 ),
20075 );
20076 }
20077 }
20078 Some(JumpData::MultiBufferRow {
20079 row,
20080 line_offset_from_top,
20081 }) => {
20082 let point = MultiBufferPoint::new(row.0, 0);
20083 if let Some((buffer, buffer_point, _)) =
20084 self.buffer.read(cx).point_to_buffer_point(point, cx)
20085 {
20086 let buffer_offset = buffer.read(cx).point_to_offset(buffer_point);
20087 new_selections_by_buffer
20088 .entry(buffer)
20089 .or_insert((Vec::new(), Some(*line_offset_from_top)))
20090 .0
20091 .push(buffer_offset..buffer_offset)
20092 }
20093 }
20094 None => {
20095 let selections = self.selections.all::<usize>(cx);
20096 let multi_buffer = self.buffer.read(cx);
20097 for selection in selections {
20098 for (snapshot, range, _, anchor) in multi_buffer
20099 .snapshot(cx)
20100 .range_to_buffer_ranges_with_deleted_hunks(selection.range())
20101 {
20102 if let Some(anchor) = anchor {
20103 // selection is in a deleted hunk
20104 let Some(buffer_id) = anchor.buffer_id else {
20105 continue;
20106 };
20107 let Some(buffer_handle) = multi_buffer.buffer(buffer_id) else {
20108 continue;
20109 };
20110 let offset = text::ToOffset::to_offset(
20111 &anchor.text_anchor,
20112 &buffer_handle.read(cx).snapshot(),
20113 );
20114 let range = offset..offset;
20115 new_selections_by_buffer
20116 .entry(buffer_handle)
20117 .or_insert((Vec::new(), None))
20118 .0
20119 .push(range)
20120 } else {
20121 let Some(buffer_handle) = multi_buffer.buffer(snapshot.remote_id())
20122 else {
20123 continue;
20124 };
20125 new_selections_by_buffer
20126 .entry(buffer_handle)
20127 .or_insert((Vec::new(), None))
20128 .0
20129 .push(range)
20130 }
20131 }
20132 }
20133 }
20134 }
20135
20136 new_selections_by_buffer
20137 .retain(|buffer, _| Self::can_open_excerpts_in_file(buffer.read(cx).file()));
20138
20139 if new_selections_by_buffer.is_empty() {
20140 return;
20141 }
20142
20143 // We defer the pane interaction because we ourselves are a workspace item
20144 // and activating a new item causes the pane to call a method on us reentrantly,
20145 // which panics if we're on the stack.
20146 window.defer(cx, move |window, cx| {
20147 workspace.update(cx, |workspace, cx| {
20148 let pane = if split {
20149 workspace.adjacent_pane(window, cx)
20150 } else {
20151 workspace.active_pane().clone()
20152 };
20153
20154 for (buffer, (ranges, scroll_offset)) in new_selections_by_buffer {
20155 let editor = buffer
20156 .read(cx)
20157 .file()
20158 .is_none()
20159 .then(|| {
20160 // Handle file-less buffers separately: those are not really the project items, so won't have a project path or entity id,
20161 // so `workspace.open_project_item` will never find them, always opening a new editor.
20162 // Instead, we try to activate the existing editor in the pane first.
20163 let (editor, pane_item_index) =
20164 pane.read(cx).items().enumerate().find_map(|(i, item)| {
20165 let editor = item.downcast::<Editor>()?;
20166 let singleton_buffer =
20167 editor.read(cx).buffer().read(cx).as_singleton()?;
20168 if singleton_buffer == buffer {
20169 Some((editor, i))
20170 } else {
20171 None
20172 }
20173 })?;
20174 pane.update(cx, |pane, cx| {
20175 pane.activate_item(pane_item_index, true, true, window, cx)
20176 });
20177 Some(editor)
20178 })
20179 .flatten()
20180 .unwrap_or_else(|| {
20181 workspace.open_project_item::<Self>(
20182 pane.clone(),
20183 buffer,
20184 true,
20185 true,
20186 window,
20187 cx,
20188 )
20189 });
20190
20191 editor.update(cx, |editor, cx| {
20192 let autoscroll = match scroll_offset {
20193 Some(scroll_offset) => Autoscroll::top_relative(scroll_offset as usize),
20194 None => Autoscroll::newest(),
20195 };
20196 let nav_history = editor.nav_history.take();
20197 editor.change_selections(
20198 SelectionEffects::scroll(autoscroll),
20199 window,
20200 cx,
20201 |s| {
20202 s.select_ranges(ranges);
20203 },
20204 );
20205 editor.nav_history = nav_history;
20206 });
20207 }
20208 })
20209 });
20210 }
20211
20212 // For now, don't allow opening excerpts in buffers that aren't backed by
20213 // regular project files.
20214 fn can_open_excerpts_in_file(file: Option<&Arc<dyn language::File>>) -> bool {
20215 file.map_or(true, |file| project::File::from_dyn(Some(file)).is_some())
20216 }
20217
20218 fn marked_text_ranges(&self, cx: &App) -> Option<Vec<Range<OffsetUtf16>>> {
20219 let snapshot = self.buffer.read(cx).read(cx);
20220 let (_, ranges) = self.text_highlights::<InputComposition>(cx)?;
20221 Some(
20222 ranges
20223 .iter()
20224 .map(move |range| {
20225 range.start.to_offset_utf16(&snapshot)..range.end.to_offset_utf16(&snapshot)
20226 })
20227 .collect(),
20228 )
20229 }
20230
20231 fn selection_replacement_ranges(
20232 &self,
20233 range: Range<OffsetUtf16>,
20234 cx: &mut App,
20235 ) -> Vec<Range<OffsetUtf16>> {
20236 let selections = self.selections.all::<OffsetUtf16>(cx);
20237 let newest_selection = selections
20238 .iter()
20239 .max_by_key(|selection| selection.id)
20240 .unwrap();
20241 let start_delta = range.start.0 as isize - newest_selection.start.0 as isize;
20242 let end_delta = range.end.0 as isize - newest_selection.end.0 as isize;
20243 let snapshot = self.buffer.read(cx).read(cx);
20244 selections
20245 .into_iter()
20246 .map(|mut selection| {
20247 selection.start.0 =
20248 (selection.start.0 as isize).saturating_add(start_delta) as usize;
20249 selection.end.0 = (selection.end.0 as isize).saturating_add(end_delta) as usize;
20250 snapshot.clip_offset_utf16(selection.start, Bias::Left)
20251 ..snapshot.clip_offset_utf16(selection.end, Bias::Right)
20252 })
20253 .collect()
20254 }
20255
20256 fn report_editor_event(
20257 &self,
20258 event_type: &'static str,
20259 file_extension: Option<String>,
20260 cx: &App,
20261 ) {
20262 if cfg!(any(test, feature = "test-support")) {
20263 return;
20264 }
20265
20266 let Some(project) = &self.project else { return };
20267
20268 // If None, we are in a file without an extension
20269 let file = self
20270 .buffer
20271 .read(cx)
20272 .as_singleton()
20273 .and_then(|b| b.read(cx).file());
20274 let file_extension = file_extension.or(file
20275 .as_ref()
20276 .and_then(|file| Path::new(file.file_name(cx)).extension())
20277 .and_then(|e| e.to_str())
20278 .map(|a| a.to_string()));
20279
20280 let vim_mode = vim_enabled(cx);
20281
20282 let edit_predictions_provider = all_language_settings(file, cx).edit_predictions.provider;
20283 let copilot_enabled = edit_predictions_provider
20284 == language::language_settings::EditPredictionProvider::Copilot;
20285 let copilot_enabled_for_language = self
20286 .buffer
20287 .read(cx)
20288 .language_settings(cx)
20289 .show_edit_predictions;
20290
20291 let project = project.read(cx);
20292 telemetry::event!(
20293 event_type,
20294 file_extension,
20295 vim_mode,
20296 copilot_enabled,
20297 copilot_enabled_for_language,
20298 edit_predictions_provider,
20299 is_via_ssh = project.is_via_ssh(),
20300 );
20301 }
20302
20303 /// Copy the highlighted chunks to the clipboard as JSON. The format is an array of lines,
20304 /// with each line being an array of {text, highlight} objects.
20305 fn copy_highlight_json(
20306 &mut self,
20307 _: &CopyHighlightJson,
20308 window: &mut Window,
20309 cx: &mut Context<Self>,
20310 ) {
20311 #[derive(Serialize)]
20312 struct Chunk<'a> {
20313 text: String,
20314 highlight: Option<&'a str>,
20315 }
20316
20317 let snapshot = self.buffer.read(cx).snapshot(cx);
20318 let range = self
20319 .selected_text_range(false, window, cx)
20320 .and_then(|selection| {
20321 if selection.range.is_empty() {
20322 None
20323 } else {
20324 Some(selection.range)
20325 }
20326 })
20327 .unwrap_or_else(|| 0..snapshot.len());
20328
20329 let chunks = snapshot.chunks(range, true);
20330 let mut lines = Vec::new();
20331 let mut line: VecDeque<Chunk> = VecDeque::new();
20332
20333 let Some(style) = self.style.as_ref() else {
20334 return;
20335 };
20336
20337 for chunk in chunks {
20338 let highlight = chunk
20339 .syntax_highlight_id
20340 .and_then(|id| id.name(&style.syntax));
20341 let mut chunk_lines = chunk.text.split('\n').peekable();
20342 while let Some(text) = chunk_lines.next() {
20343 let mut merged_with_last_token = false;
20344 if let Some(last_token) = line.back_mut() {
20345 if last_token.highlight == highlight {
20346 last_token.text.push_str(text);
20347 merged_with_last_token = true;
20348 }
20349 }
20350
20351 if !merged_with_last_token {
20352 line.push_back(Chunk {
20353 text: text.into(),
20354 highlight,
20355 });
20356 }
20357
20358 if chunk_lines.peek().is_some() {
20359 if line.len() > 1 && line.front().unwrap().text.is_empty() {
20360 line.pop_front();
20361 }
20362 if line.len() > 1 && line.back().unwrap().text.is_empty() {
20363 line.pop_back();
20364 }
20365
20366 lines.push(mem::take(&mut line));
20367 }
20368 }
20369 }
20370
20371 let Some(lines) = serde_json::to_string_pretty(&lines).log_err() else {
20372 return;
20373 };
20374 cx.write_to_clipboard(ClipboardItem::new_string(lines));
20375 }
20376
20377 pub fn open_context_menu(
20378 &mut self,
20379 _: &OpenContextMenu,
20380 window: &mut Window,
20381 cx: &mut Context<Self>,
20382 ) {
20383 self.request_autoscroll(Autoscroll::newest(), cx);
20384 let position = self.selections.newest_display(cx).start;
20385 mouse_context_menu::deploy_context_menu(self, None, position, window, cx);
20386 }
20387
20388 pub fn inlay_hint_cache(&self) -> &InlayHintCache {
20389 &self.inlay_hint_cache
20390 }
20391
20392 pub fn replay_insert_event(
20393 &mut self,
20394 text: &str,
20395 relative_utf16_range: Option<Range<isize>>,
20396 window: &mut Window,
20397 cx: &mut Context<Self>,
20398 ) {
20399 if !self.input_enabled {
20400 cx.emit(EditorEvent::InputIgnored { text: text.into() });
20401 return;
20402 }
20403 if let Some(relative_utf16_range) = relative_utf16_range {
20404 let selections = self.selections.all::<OffsetUtf16>(cx);
20405 self.change_selections(SelectionEffects::no_scroll(), window, cx, |s| {
20406 let new_ranges = selections.into_iter().map(|range| {
20407 let start = OffsetUtf16(
20408 range
20409 .head()
20410 .0
20411 .saturating_add_signed(relative_utf16_range.start),
20412 );
20413 let end = OffsetUtf16(
20414 range
20415 .head()
20416 .0
20417 .saturating_add_signed(relative_utf16_range.end),
20418 );
20419 start..end
20420 });
20421 s.select_ranges(new_ranges);
20422 });
20423 }
20424
20425 self.handle_input(text, window, cx);
20426 }
20427
20428 pub fn supports_inlay_hints(&self, cx: &mut App) -> bool {
20429 let Some(provider) = self.semantics_provider.as_ref() else {
20430 return false;
20431 };
20432
20433 let mut supports = false;
20434 self.buffer().update(cx, |this, cx| {
20435 this.for_each_buffer(|buffer| {
20436 supports |= provider.supports_inlay_hints(buffer, cx);
20437 });
20438 });
20439
20440 supports
20441 }
20442
20443 pub fn is_focused(&self, window: &Window) -> bool {
20444 self.focus_handle.is_focused(window)
20445 }
20446
20447 fn handle_focus(&mut self, window: &mut Window, cx: &mut Context<Self>) {
20448 cx.emit(EditorEvent::Focused);
20449
20450 if let Some(descendant) = self
20451 .last_focused_descendant
20452 .take()
20453 .and_then(|descendant| descendant.upgrade())
20454 {
20455 window.focus(&descendant);
20456 } else {
20457 if let Some(blame) = self.blame.as_ref() {
20458 blame.update(cx, GitBlame::focus)
20459 }
20460
20461 self.blink_manager.update(cx, BlinkManager::enable);
20462 self.show_cursor_names(window, cx);
20463 self.buffer.update(cx, |buffer, cx| {
20464 buffer.finalize_last_transaction(cx);
20465 if self.leader_id.is_none() {
20466 buffer.set_active_selections(
20467 &self.selections.disjoint_anchors(),
20468 self.selections.line_mode,
20469 self.cursor_shape,
20470 cx,
20471 );
20472 }
20473 });
20474 }
20475 }
20476
20477 fn handle_focus_in(&mut self, _: &mut Window, cx: &mut Context<Self>) {
20478 cx.emit(EditorEvent::FocusedIn)
20479 }
20480
20481 fn handle_focus_out(
20482 &mut self,
20483 event: FocusOutEvent,
20484 _window: &mut Window,
20485 cx: &mut Context<Self>,
20486 ) {
20487 if event.blurred != self.focus_handle {
20488 self.last_focused_descendant = Some(event.blurred);
20489 }
20490 self.refresh_inlay_hints(InlayHintRefreshReason::ModifiersChanged(false), cx);
20491 }
20492
20493 pub fn handle_blur(&mut self, window: &mut Window, cx: &mut Context<Self>) {
20494 self.blink_manager.update(cx, BlinkManager::disable);
20495 self.buffer
20496 .update(cx, |buffer, cx| buffer.remove_active_selections(cx));
20497
20498 if let Some(blame) = self.blame.as_ref() {
20499 blame.update(cx, GitBlame::blur)
20500 }
20501 if !self.hover_state.focused(window, cx) {
20502 hide_hover(self, cx);
20503 }
20504 if !self
20505 .context_menu
20506 .borrow()
20507 .as_ref()
20508 .is_some_and(|context_menu| context_menu.focused(window, cx))
20509 {
20510 self.hide_context_menu(window, cx);
20511 }
20512 self.discard_inline_completion(false, cx);
20513 cx.emit(EditorEvent::Blurred);
20514 cx.notify();
20515 }
20516
20517 pub fn observe_pending_input(&mut self, window: &mut Window, cx: &mut Context<Self>) {
20518 let mut pending: String = window
20519 .pending_input_keystrokes()
20520 .into_iter()
20521 .flatten()
20522 .filter_map(|keystroke| {
20523 if keystroke.modifiers.is_subset_of(&Modifiers::shift()) {
20524 keystroke.key_char.clone()
20525 } else {
20526 None
20527 }
20528 })
20529 .collect();
20530
20531 if !self.input_enabled || self.read_only || !self.focus_handle.is_focused(window) {
20532 pending = "".to_string();
20533 }
20534
20535 let existing_pending = self
20536 .text_highlights::<PendingInput>(cx)
20537 .map(|(_, ranges)| ranges.iter().cloned().collect::<Vec<_>>());
20538 if existing_pending.is_none() && pending.is_empty() {
20539 return;
20540 }
20541 let transaction =
20542 self.transact(window, cx, |this, window, cx| {
20543 let selections = this.selections.all::<usize>(cx);
20544 let edits = selections
20545 .iter()
20546 .map(|selection| (selection.end..selection.end, pending.clone()));
20547 this.edit(edits, cx);
20548 this.change_selections(SelectionEffects::no_scroll(), window, cx, |s| {
20549 s.select_ranges(selections.into_iter().enumerate().map(|(ix, sel)| {
20550 sel.start + ix * pending.len()..sel.end + ix * pending.len()
20551 }));
20552 });
20553 if let Some(existing_ranges) = existing_pending {
20554 let edits = existing_ranges.iter().map(|range| (range.clone(), ""));
20555 this.edit(edits, cx);
20556 }
20557 });
20558
20559 let snapshot = self.snapshot(window, cx);
20560 let ranges = self
20561 .selections
20562 .all::<usize>(cx)
20563 .into_iter()
20564 .map(|selection| {
20565 snapshot.buffer_snapshot.anchor_after(selection.end)
20566 ..snapshot
20567 .buffer_snapshot
20568 .anchor_before(selection.end + pending.len())
20569 })
20570 .collect();
20571
20572 if pending.is_empty() {
20573 self.clear_highlights::<PendingInput>(cx);
20574 } else {
20575 self.highlight_text::<PendingInput>(
20576 ranges,
20577 HighlightStyle {
20578 underline: Some(UnderlineStyle {
20579 thickness: px(1.),
20580 color: None,
20581 wavy: false,
20582 }),
20583 ..Default::default()
20584 },
20585 cx,
20586 );
20587 }
20588
20589 self.ime_transaction = self.ime_transaction.or(transaction);
20590 if let Some(transaction) = self.ime_transaction {
20591 self.buffer.update(cx, |buffer, cx| {
20592 buffer.group_until_transaction(transaction, cx);
20593 });
20594 }
20595
20596 if self.text_highlights::<PendingInput>(cx).is_none() {
20597 self.ime_transaction.take();
20598 }
20599 }
20600
20601 pub fn register_action_renderer(
20602 &mut self,
20603 listener: impl Fn(&Editor, &mut Window, &mut Context<Editor>) + 'static,
20604 ) -> Subscription {
20605 let id = self.next_editor_action_id.post_inc();
20606 self.editor_actions
20607 .borrow_mut()
20608 .insert(id, Box::new(listener));
20609
20610 let editor_actions = self.editor_actions.clone();
20611 Subscription::new(move || {
20612 editor_actions.borrow_mut().remove(&id);
20613 })
20614 }
20615
20616 pub fn register_action<A: Action>(
20617 &mut self,
20618 listener: impl Fn(&A, &mut Window, &mut App) + 'static,
20619 ) -> Subscription {
20620 let id = self.next_editor_action_id.post_inc();
20621 let listener = Arc::new(listener);
20622 self.editor_actions.borrow_mut().insert(
20623 id,
20624 Box::new(move |_, window, _| {
20625 let listener = listener.clone();
20626 window.on_action(TypeId::of::<A>(), move |action, phase, window, cx| {
20627 let action = action.downcast_ref().unwrap();
20628 if phase == DispatchPhase::Bubble {
20629 listener(action, window, cx)
20630 }
20631 })
20632 }),
20633 );
20634
20635 let editor_actions = self.editor_actions.clone();
20636 Subscription::new(move || {
20637 editor_actions.borrow_mut().remove(&id);
20638 })
20639 }
20640
20641 pub fn file_header_size(&self) -> u32 {
20642 FILE_HEADER_HEIGHT
20643 }
20644
20645 pub fn restore(
20646 &mut self,
20647 revert_changes: HashMap<BufferId, Vec<(Range<text::Anchor>, Rope)>>,
20648 window: &mut Window,
20649 cx: &mut Context<Self>,
20650 ) {
20651 let workspace = self.workspace();
20652 let project = self.project.as_ref();
20653 let save_tasks = self.buffer().update(cx, |multi_buffer, cx| {
20654 let mut tasks = Vec::new();
20655 for (buffer_id, changes) in revert_changes {
20656 if let Some(buffer) = multi_buffer.buffer(buffer_id) {
20657 buffer.update(cx, |buffer, cx| {
20658 buffer.edit(
20659 changes
20660 .into_iter()
20661 .map(|(range, text)| (range, text.to_string())),
20662 None,
20663 cx,
20664 );
20665 });
20666
20667 if let Some(project) =
20668 project.filter(|_| multi_buffer.all_diff_hunks_expanded())
20669 {
20670 project.update(cx, |project, cx| {
20671 tasks.push((buffer.clone(), project.save_buffer(buffer, cx)));
20672 })
20673 }
20674 }
20675 }
20676 tasks
20677 });
20678 cx.spawn_in(window, async move |_, cx| {
20679 for (buffer, task) in save_tasks {
20680 let result = task.await;
20681 if result.is_err() {
20682 let Some(path) = buffer
20683 .read_with(cx, |buffer, cx| buffer.project_path(cx))
20684 .ok()
20685 else {
20686 continue;
20687 };
20688 if let Some((workspace, path)) = workspace.as_ref().zip(path) {
20689 let Some(task) = cx
20690 .update_window_entity(&workspace, |workspace, window, cx| {
20691 workspace
20692 .open_path_preview(path, None, false, false, false, window, cx)
20693 })
20694 .ok()
20695 else {
20696 continue;
20697 };
20698 task.await.log_err();
20699 }
20700 }
20701 }
20702 })
20703 .detach();
20704 self.change_selections(SelectionEffects::no_scroll(), window, cx, |selections| {
20705 selections.refresh()
20706 });
20707 }
20708
20709 pub fn to_pixel_point(
20710 &self,
20711 source: multi_buffer::Anchor,
20712 editor_snapshot: &EditorSnapshot,
20713 window: &mut Window,
20714 ) -> Option<gpui::Point<Pixels>> {
20715 let source_point = source.to_display_point(editor_snapshot);
20716 self.display_to_pixel_point(source_point, editor_snapshot, window)
20717 }
20718
20719 pub fn display_to_pixel_point(
20720 &self,
20721 source: DisplayPoint,
20722 editor_snapshot: &EditorSnapshot,
20723 window: &mut Window,
20724 ) -> Option<gpui::Point<Pixels>> {
20725 let line_height = self.style()?.text.line_height_in_pixels(window.rem_size());
20726 let text_layout_details = self.text_layout_details(window);
20727 let scroll_top = text_layout_details
20728 .scroll_anchor
20729 .scroll_position(editor_snapshot)
20730 .y;
20731
20732 if source.row().as_f32() < scroll_top.floor() {
20733 return None;
20734 }
20735 let source_x = editor_snapshot.x_for_display_point(source, &text_layout_details);
20736 let source_y = line_height * (source.row().as_f32() - scroll_top);
20737 Some(gpui::Point::new(source_x, source_y))
20738 }
20739
20740 pub fn has_visible_completions_menu(&self) -> bool {
20741 !self.edit_prediction_preview_is_active()
20742 && self.context_menu.borrow().as_ref().map_or(false, |menu| {
20743 menu.visible() && matches!(menu, CodeContextMenu::Completions(_))
20744 })
20745 }
20746
20747 pub fn register_addon<T: Addon>(&mut self, instance: T) {
20748 if self.mode.is_minimap() {
20749 return;
20750 }
20751 self.addons
20752 .insert(std::any::TypeId::of::<T>(), Box::new(instance));
20753 }
20754
20755 pub fn unregister_addon<T: Addon>(&mut self) {
20756 self.addons.remove(&std::any::TypeId::of::<T>());
20757 }
20758
20759 pub fn addon<T: Addon>(&self) -> Option<&T> {
20760 let type_id = std::any::TypeId::of::<T>();
20761 self.addons
20762 .get(&type_id)
20763 .and_then(|item| item.to_any().downcast_ref::<T>())
20764 }
20765
20766 pub fn addon_mut<T: Addon>(&mut self) -> Option<&mut T> {
20767 let type_id = std::any::TypeId::of::<T>();
20768 self.addons
20769 .get_mut(&type_id)
20770 .and_then(|item| item.to_any_mut()?.downcast_mut::<T>())
20771 }
20772
20773 fn character_dimensions(&self, window: &mut Window) -> CharacterDimensions {
20774 let text_layout_details = self.text_layout_details(window);
20775 let style = &text_layout_details.editor_style;
20776 let font_id = window.text_system().resolve_font(&style.text.font());
20777 let font_size = style.text.font_size.to_pixels(window.rem_size());
20778 let line_height = style.text.line_height_in_pixels(window.rem_size());
20779 let em_width = window.text_system().em_width(font_id, font_size).unwrap();
20780 let em_advance = window.text_system().em_advance(font_id, font_size).unwrap();
20781
20782 CharacterDimensions {
20783 em_width,
20784 em_advance,
20785 line_height,
20786 }
20787 }
20788
20789 pub fn wait_for_diff_to_load(&self) -> Option<Shared<Task<()>>> {
20790 self.load_diff_task.clone()
20791 }
20792
20793 fn read_metadata_from_db(
20794 &mut self,
20795 item_id: u64,
20796 workspace_id: WorkspaceId,
20797 window: &mut Window,
20798 cx: &mut Context<Editor>,
20799 ) {
20800 if self.is_singleton(cx)
20801 && !self.mode.is_minimap()
20802 && WorkspaceSettings::get(None, cx).restore_on_startup != RestoreOnStartupBehavior::None
20803 {
20804 let buffer_snapshot = OnceCell::new();
20805
20806 if let Some(folds) = DB.get_editor_folds(item_id, workspace_id).log_err() {
20807 if !folds.is_empty() {
20808 let snapshot =
20809 buffer_snapshot.get_or_init(|| self.buffer.read(cx).snapshot(cx));
20810 self.fold_ranges(
20811 folds
20812 .into_iter()
20813 .map(|(start, end)| {
20814 snapshot.clip_offset(start, Bias::Left)
20815 ..snapshot.clip_offset(end, Bias::Right)
20816 })
20817 .collect(),
20818 false,
20819 window,
20820 cx,
20821 );
20822 }
20823 }
20824
20825 if let Some(selections) = DB.get_editor_selections(item_id, workspace_id).log_err() {
20826 if !selections.is_empty() {
20827 let snapshot =
20828 buffer_snapshot.get_or_init(|| self.buffer.read(cx).snapshot(cx));
20829 // skip adding the initial selection to selection history
20830 self.selection_history.mode = SelectionHistoryMode::Skipping;
20831 self.change_selections(SelectionEffects::no_scroll(), window, cx, |s| {
20832 s.select_ranges(selections.into_iter().map(|(start, end)| {
20833 snapshot.clip_offset(start, Bias::Left)
20834 ..snapshot.clip_offset(end, Bias::Right)
20835 }));
20836 });
20837 self.selection_history.mode = SelectionHistoryMode::Normal;
20838 }
20839 };
20840 }
20841
20842 self.read_scroll_position_from_db(item_id, workspace_id, window, cx);
20843 }
20844
20845 fn update_lsp_data(
20846 &mut self,
20847 ignore_cache: bool,
20848 for_buffer: Option<BufferId>,
20849 window: &mut Window,
20850 cx: &mut Context<'_, Self>,
20851 ) {
20852 self.pull_diagnostics(for_buffer, window, cx);
20853 self.refresh_colors(ignore_cache, for_buffer, window, cx);
20854 }
20855}
20856
20857fn vim_enabled(cx: &App) -> bool {
20858 cx.global::<SettingsStore>()
20859 .raw_user_settings()
20860 .get("vim_mode")
20861 == Some(&serde_json::Value::Bool(true))
20862}
20863
20864fn process_completion_for_edit(
20865 completion: &Completion,
20866 intent: CompletionIntent,
20867 buffer: &Entity<Buffer>,
20868 cursor_position: &text::Anchor,
20869 cx: &mut Context<Editor>,
20870) -> CompletionEdit {
20871 let buffer = buffer.read(cx);
20872 let buffer_snapshot = buffer.snapshot();
20873 let (snippet, new_text) = if completion.is_snippet() {
20874 // Workaround for typescript language server issues so that methods don't expand within
20875 // strings and functions with type expressions. The previous point is used because the query
20876 // for function identifier doesn't match when the cursor is immediately after. See PR #30312
20877 let mut snippet_source = completion.new_text.clone();
20878 let mut previous_point = text::ToPoint::to_point(cursor_position, buffer);
20879 previous_point.column = previous_point.column.saturating_sub(1);
20880 if let Some(scope) = buffer_snapshot.language_scope_at(previous_point) {
20881 if scope.prefers_label_for_snippet_in_completion() {
20882 if let Some(label) = completion.label() {
20883 if matches!(
20884 completion.kind(),
20885 Some(CompletionItemKind::FUNCTION) | Some(CompletionItemKind::METHOD)
20886 ) {
20887 snippet_source = label;
20888 }
20889 }
20890 }
20891 }
20892 match Snippet::parse(&snippet_source).log_err() {
20893 Some(parsed_snippet) => (Some(parsed_snippet.clone()), parsed_snippet.text),
20894 None => (None, completion.new_text.clone()),
20895 }
20896 } else {
20897 (None, completion.new_text.clone())
20898 };
20899
20900 let mut range_to_replace = {
20901 let replace_range = &completion.replace_range;
20902 if let CompletionSource::Lsp {
20903 insert_range: Some(insert_range),
20904 ..
20905 } = &completion.source
20906 {
20907 debug_assert_eq!(
20908 insert_range.start, replace_range.start,
20909 "insert_range and replace_range should start at the same position"
20910 );
20911 debug_assert!(
20912 insert_range
20913 .start
20914 .cmp(&cursor_position, &buffer_snapshot)
20915 .is_le(),
20916 "insert_range should start before or at cursor position"
20917 );
20918 debug_assert!(
20919 replace_range
20920 .start
20921 .cmp(&cursor_position, &buffer_snapshot)
20922 .is_le(),
20923 "replace_range should start before or at cursor position"
20924 );
20925 debug_assert!(
20926 insert_range
20927 .end
20928 .cmp(&cursor_position, &buffer_snapshot)
20929 .is_le(),
20930 "insert_range should end before or at cursor position"
20931 );
20932
20933 let should_replace = match intent {
20934 CompletionIntent::CompleteWithInsert => false,
20935 CompletionIntent::CompleteWithReplace => true,
20936 CompletionIntent::Complete | CompletionIntent::Compose => {
20937 let insert_mode =
20938 language_settings(buffer.language().map(|l| l.name()), buffer.file(), cx)
20939 .completions
20940 .lsp_insert_mode;
20941 match insert_mode {
20942 LspInsertMode::Insert => false,
20943 LspInsertMode::Replace => true,
20944 LspInsertMode::ReplaceSubsequence => {
20945 let mut text_to_replace = buffer.chars_for_range(
20946 buffer.anchor_before(replace_range.start)
20947 ..buffer.anchor_after(replace_range.end),
20948 );
20949 let mut current_needle = text_to_replace.next();
20950 for haystack_ch in completion.label.text.chars() {
20951 if let Some(needle_ch) = current_needle {
20952 if haystack_ch.eq_ignore_ascii_case(&needle_ch) {
20953 current_needle = text_to_replace.next();
20954 }
20955 }
20956 }
20957 current_needle.is_none()
20958 }
20959 LspInsertMode::ReplaceSuffix => {
20960 if replace_range
20961 .end
20962 .cmp(&cursor_position, &buffer_snapshot)
20963 .is_gt()
20964 {
20965 let range_after_cursor = *cursor_position..replace_range.end;
20966 let text_after_cursor = buffer
20967 .text_for_range(
20968 buffer.anchor_before(range_after_cursor.start)
20969 ..buffer.anchor_after(range_after_cursor.end),
20970 )
20971 .collect::<String>()
20972 .to_ascii_lowercase();
20973 completion
20974 .label
20975 .text
20976 .to_ascii_lowercase()
20977 .ends_with(&text_after_cursor)
20978 } else {
20979 true
20980 }
20981 }
20982 }
20983 }
20984 };
20985
20986 if should_replace {
20987 replace_range.clone()
20988 } else {
20989 insert_range.clone()
20990 }
20991 } else {
20992 replace_range.clone()
20993 }
20994 };
20995
20996 if range_to_replace
20997 .end
20998 .cmp(&cursor_position, &buffer_snapshot)
20999 .is_lt()
21000 {
21001 range_to_replace.end = *cursor_position;
21002 }
21003
21004 CompletionEdit {
21005 new_text,
21006 replace_range: range_to_replace.to_offset(&buffer),
21007 snippet,
21008 }
21009}
21010
21011struct CompletionEdit {
21012 new_text: String,
21013 replace_range: Range<usize>,
21014 snippet: Option<Snippet>,
21015}
21016
21017fn insert_extra_newline_brackets(
21018 buffer: &MultiBufferSnapshot,
21019 range: Range<usize>,
21020 language: &language::LanguageScope,
21021) -> bool {
21022 let leading_whitespace_len = buffer
21023 .reversed_chars_at(range.start)
21024 .take_while(|c| c.is_whitespace() && *c != '\n')
21025 .map(|c| c.len_utf8())
21026 .sum::<usize>();
21027 let trailing_whitespace_len = buffer
21028 .chars_at(range.end)
21029 .take_while(|c| c.is_whitespace() && *c != '\n')
21030 .map(|c| c.len_utf8())
21031 .sum::<usize>();
21032 let range = range.start - leading_whitespace_len..range.end + trailing_whitespace_len;
21033
21034 language.brackets().any(|(pair, enabled)| {
21035 let pair_start = pair.start.trim_end();
21036 let pair_end = pair.end.trim_start();
21037
21038 enabled
21039 && pair.newline
21040 && buffer.contains_str_at(range.end, pair_end)
21041 && buffer.contains_str_at(range.start.saturating_sub(pair_start.len()), pair_start)
21042 })
21043}
21044
21045fn insert_extra_newline_tree_sitter(buffer: &MultiBufferSnapshot, range: Range<usize>) -> bool {
21046 let (buffer, range) = match buffer.range_to_buffer_ranges(range).as_slice() {
21047 [(buffer, range, _)] => (*buffer, range.clone()),
21048 _ => return false,
21049 };
21050 let pair = {
21051 let mut result: Option<BracketMatch> = None;
21052
21053 for pair in buffer
21054 .all_bracket_ranges(range.clone())
21055 .filter(move |pair| {
21056 pair.open_range.start <= range.start && pair.close_range.end >= range.end
21057 })
21058 {
21059 let len = pair.close_range.end - pair.open_range.start;
21060
21061 if let Some(existing) = &result {
21062 let existing_len = existing.close_range.end - existing.open_range.start;
21063 if len > existing_len {
21064 continue;
21065 }
21066 }
21067
21068 result = Some(pair);
21069 }
21070
21071 result
21072 };
21073 let Some(pair) = pair else {
21074 return false;
21075 };
21076 pair.newline_only
21077 && buffer
21078 .chars_for_range(pair.open_range.end..range.start)
21079 .chain(buffer.chars_for_range(range.end..pair.close_range.start))
21080 .all(|c| c.is_whitespace() && c != '\n')
21081}
21082
21083fn update_uncommitted_diff_for_buffer(
21084 editor: Entity<Editor>,
21085 project: &Entity<Project>,
21086 buffers: impl IntoIterator<Item = Entity<Buffer>>,
21087 buffer: Entity<MultiBuffer>,
21088 cx: &mut App,
21089) -> Task<()> {
21090 let mut tasks = Vec::new();
21091 project.update(cx, |project, cx| {
21092 for buffer in buffers {
21093 if project::File::from_dyn(buffer.read(cx).file()).is_some() {
21094 tasks.push(project.open_uncommitted_diff(buffer.clone(), cx))
21095 }
21096 }
21097 });
21098 cx.spawn(async move |cx| {
21099 let diffs = future::join_all(tasks).await;
21100 if editor
21101 .read_with(cx, |editor, _cx| editor.temporary_diff_override)
21102 .unwrap_or(false)
21103 {
21104 return;
21105 }
21106
21107 buffer
21108 .update(cx, |buffer, cx| {
21109 for diff in diffs.into_iter().flatten() {
21110 buffer.add_diff(diff, cx);
21111 }
21112 })
21113 .ok();
21114 })
21115}
21116
21117fn char_len_with_expanded_tabs(offset: usize, text: &str, tab_size: NonZeroU32) -> usize {
21118 let tab_size = tab_size.get() as usize;
21119 let mut width = offset;
21120
21121 for ch in text.chars() {
21122 width += if ch == '\t' {
21123 tab_size - (width % tab_size)
21124 } else {
21125 1
21126 };
21127 }
21128
21129 width - offset
21130}
21131
21132#[cfg(test)]
21133mod tests {
21134 use super::*;
21135
21136 #[test]
21137 fn test_string_size_with_expanded_tabs() {
21138 let nz = |val| NonZeroU32::new(val).unwrap();
21139 assert_eq!(char_len_with_expanded_tabs(0, "", nz(4)), 0);
21140 assert_eq!(char_len_with_expanded_tabs(0, "hello", nz(4)), 5);
21141 assert_eq!(char_len_with_expanded_tabs(0, "\thello", nz(4)), 9);
21142 assert_eq!(char_len_with_expanded_tabs(0, "abc\tab", nz(4)), 6);
21143 assert_eq!(char_len_with_expanded_tabs(0, "hello\t", nz(4)), 8);
21144 assert_eq!(char_len_with_expanded_tabs(0, "\t\t", nz(8)), 16);
21145 assert_eq!(char_len_with_expanded_tabs(0, "x\t", nz(8)), 8);
21146 assert_eq!(char_len_with_expanded_tabs(7, "x\t", nz(8)), 9);
21147 }
21148}
21149
21150/// Tokenizes a string into runs of text that should stick together, or that is whitespace.
21151struct WordBreakingTokenizer<'a> {
21152 input: &'a str,
21153}
21154
21155impl<'a> WordBreakingTokenizer<'a> {
21156 fn new(input: &'a str) -> Self {
21157 Self { input }
21158 }
21159}
21160
21161fn is_char_ideographic(ch: char) -> bool {
21162 use unicode_script::Script::*;
21163 use unicode_script::UnicodeScript;
21164 matches!(ch.script(), Han | Tangut | Yi)
21165}
21166
21167fn is_grapheme_ideographic(text: &str) -> bool {
21168 text.chars().any(is_char_ideographic)
21169}
21170
21171fn is_grapheme_whitespace(text: &str) -> bool {
21172 text.chars().any(|x| x.is_whitespace())
21173}
21174
21175fn should_stay_with_preceding_ideograph(text: &str) -> bool {
21176 text.chars().next().map_or(false, |ch| {
21177 matches!(ch, '。' | '、' | ',' | '?' | '!' | ':' | ';' | '…')
21178 })
21179}
21180
21181#[derive(PartialEq, Eq, Debug, Clone, Copy)]
21182enum WordBreakToken<'a> {
21183 Word { token: &'a str, grapheme_len: usize },
21184 InlineWhitespace { token: &'a str, grapheme_len: usize },
21185 Newline,
21186}
21187
21188impl<'a> Iterator for WordBreakingTokenizer<'a> {
21189 /// Yields a span, the count of graphemes in the token, and whether it was
21190 /// whitespace. Note that it also breaks at word boundaries.
21191 type Item = WordBreakToken<'a>;
21192
21193 fn next(&mut self) -> Option<Self::Item> {
21194 use unicode_segmentation::UnicodeSegmentation;
21195 if self.input.is_empty() {
21196 return None;
21197 }
21198
21199 let mut iter = self.input.graphemes(true).peekable();
21200 let mut offset = 0;
21201 let mut grapheme_len = 0;
21202 if let Some(first_grapheme) = iter.next() {
21203 let is_newline = first_grapheme == "\n";
21204 let is_whitespace = is_grapheme_whitespace(first_grapheme);
21205 offset += first_grapheme.len();
21206 grapheme_len += 1;
21207 if is_grapheme_ideographic(first_grapheme) && !is_whitespace {
21208 if let Some(grapheme) = iter.peek().copied() {
21209 if should_stay_with_preceding_ideograph(grapheme) {
21210 offset += grapheme.len();
21211 grapheme_len += 1;
21212 }
21213 }
21214 } else {
21215 let mut words = self.input[offset..].split_word_bound_indices().peekable();
21216 let mut next_word_bound = words.peek().copied();
21217 if next_word_bound.map_or(false, |(i, _)| i == 0) {
21218 next_word_bound = words.next();
21219 }
21220 while let Some(grapheme) = iter.peek().copied() {
21221 if next_word_bound.map_or(false, |(i, _)| i == offset) {
21222 break;
21223 };
21224 if is_grapheme_whitespace(grapheme) != is_whitespace
21225 || (grapheme == "\n") != is_newline
21226 {
21227 break;
21228 };
21229 offset += grapheme.len();
21230 grapheme_len += 1;
21231 iter.next();
21232 }
21233 }
21234 let token = &self.input[..offset];
21235 self.input = &self.input[offset..];
21236 if token == "\n" {
21237 Some(WordBreakToken::Newline)
21238 } else if is_whitespace {
21239 Some(WordBreakToken::InlineWhitespace {
21240 token,
21241 grapheme_len,
21242 })
21243 } else {
21244 Some(WordBreakToken::Word {
21245 token,
21246 grapheme_len,
21247 })
21248 }
21249 } else {
21250 None
21251 }
21252 }
21253}
21254
21255#[test]
21256fn test_word_breaking_tokenizer() {
21257 let tests: &[(&str, &[WordBreakToken<'static>])] = &[
21258 ("", &[]),
21259 (" ", &[whitespace(" ", 2)]),
21260 ("Ʒ", &[word("Ʒ", 1)]),
21261 ("Ǽ", &[word("Ǽ", 1)]),
21262 ("⋑", &[word("⋑", 1)]),
21263 ("⋑⋑", &[word("⋑⋑", 2)]),
21264 (
21265 "原理,进而",
21266 &[word("原", 1), word("理,", 2), word("进", 1), word("而", 1)],
21267 ),
21268 (
21269 "hello world",
21270 &[word("hello", 5), whitespace(" ", 1), word("world", 5)],
21271 ),
21272 (
21273 "hello, world",
21274 &[word("hello,", 6), whitespace(" ", 1), word("world", 5)],
21275 ),
21276 (
21277 " hello world",
21278 &[
21279 whitespace(" ", 2),
21280 word("hello", 5),
21281 whitespace(" ", 1),
21282 word("world", 5),
21283 ],
21284 ),
21285 (
21286 "这是什么 \n 钢笔",
21287 &[
21288 word("这", 1),
21289 word("是", 1),
21290 word("什", 1),
21291 word("么", 1),
21292 whitespace(" ", 1),
21293 newline(),
21294 whitespace(" ", 1),
21295 word("钢", 1),
21296 word("笔", 1),
21297 ],
21298 ),
21299 (" mutton", &[whitespace(" ", 1), word("mutton", 6)]),
21300 ];
21301
21302 fn word(token: &'static str, grapheme_len: usize) -> WordBreakToken<'static> {
21303 WordBreakToken::Word {
21304 token,
21305 grapheme_len,
21306 }
21307 }
21308
21309 fn whitespace(token: &'static str, grapheme_len: usize) -> WordBreakToken<'static> {
21310 WordBreakToken::InlineWhitespace {
21311 token,
21312 grapheme_len,
21313 }
21314 }
21315
21316 fn newline() -> WordBreakToken<'static> {
21317 WordBreakToken::Newline
21318 }
21319
21320 for (input, result) in tests {
21321 assert_eq!(
21322 WordBreakingTokenizer::new(input)
21323 .collect::<Vec<_>>()
21324 .as_slice(),
21325 *result,
21326 );
21327 }
21328}
21329
21330fn wrap_with_prefix(
21331 first_line_prefix: String,
21332 subsequent_lines_prefix: String,
21333 unwrapped_text: String,
21334 wrap_column: usize,
21335 tab_size: NonZeroU32,
21336 preserve_existing_whitespace: bool,
21337) -> String {
21338 let first_line_prefix_len = char_len_with_expanded_tabs(0, &first_line_prefix, tab_size);
21339 let subsequent_lines_prefix_len =
21340 char_len_with_expanded_tabs(0, &subsequent_lines_prefix, tab_size);
21341 let mut wrapped_text = String::new();
21342 let mut current_line = first_line_prefix.clone();
21343 let mut is_first_line = true;
21344
21345 let tokenizer = WordBreakingTokenizer::new(&unwrapped_text);
21346 let mut current_line_len = first_line_prefix_len;
21347 let mut in_whitespace = false;
21348 for token in tokenizer {
21349 let have_preceding_whitespace = in_whitespace;
21350 match token {
21351 WordBreakToken::Word {
21352 token,
21353 grapheme_len,
21354 } => {
21355 in_whitespace = false;
21356 let current_prefix_len = if is_first_line {
21357 first_line_prefix_len
21358 } else {
21359 subsequent_lines_prefix_len
21360 };
21361 if current_line_len + grapheme_len > wrap_column
21362 && current_line_len != current_prefix_len
21363 {
21364 wrapped_text.push_str(current_line.trim_end());
21365 wrapped_text.push('\n');
21366 is_first_line = false;
21367 current_line = subsequent_lines_prefix.clone();
21368 current_line_len = subsequent_lines_prefix_len;
21369 }
21370 current_line.push_str(token);
21371 current_line_len += grapheme_len;
21372 }
21373 WordBreakToken::InlineWhitespace {
21374 mut token,
21375 mut grapheme_len,
21376 } => {
21377 in_whitespace = true;
21378 if have_preceding_whitespace && !preserve_existing_whitespace {
21379 continue;
21380 }
21381 if !preserve_existing_whitespace {
21382 token = " ";
21383 grapheme_len = 1;
21384 }
21385 let current_prefix_len = if is_first_line {
21386 first_line_prefix_len
21387 } else {
21388 subsequent_lines_prefix_len
21389 };
21390 if current_line_len + grapheme_len > wrap_column {
21391 wrapped_text.push_str(current_line.trim_end());
21392 wrapped_text.push('\n');
21393 is_first_line = false;
21394 current_line = subsequent_lines_prefix.clone();
21395 current_line_len = subsequent_lines_prefix_len;
21396 } else if current_line_len != current_prefix_len || preserve_existing_whitespace {
21397 current_line.push_str(token);
21398 current_line_len += grapheme_len;
21399 }
21400 }
21401 WordBreakToken::Newline => {
21402 in_whitespace = true;
21403 let current_prefix_len = if is_first_line {
21404 first_line_prefix_len
21405 } else {
21406 subsequent_lines_prefix_len
21407 };
21408 if preserve_existing_whitespace {
21409 wrapped_text.push_str(current_line.trim_end());
21410 wrapped_text.push('\n');
21411 is_first_line = false;
21412 current_line = subsequent_lines_prefix.clone();
21413 current_line_len = subsequent_lines_prefix_len;
21414 } else if have_preceding_whitespace {
21415 continue;
21416 } else if current_line_len + 1 > wrap_column
21417 && current_line_len != current_prefix_len
21418 {
21419 wrapped_text.push_str(current_line.trim_end());
21420 wrapped_text.push('\n');
21421 is_first_line = false;
21422 current_line = subsequent_lines_prefix.clone();
21423 current_line_len = subsequent_lines_prefix_len;
21424 } else if current_line_len != current_prefix_len {
21425 current_line.push(' ');
21426 current_line_len += 1;
21427 }
21428 }
21429 }
21430 }
21431
21432 if !current_line.is_empty() {
21433 wrapped_text.push_str(¤t_line);
21434 }
21435 wrapped_text
21436}
21437
21438#[test]
21439fn test_wrap_with_prefix() {
21440 assert_eq!(
21441 wrap_with_prefix(
21442 "# ".to_string(),
21443 "# ".to_string(),
21444 "abcdefg".to_string(),
21445 4,
21446 NonZeroU32::new(4).unwrap(),
21447 false,
21448 ),
21449 "# abcdefg"
21450 );
21451 assert_eq!(
21452 wrap_with_prefix(
21453 "".to_string(),
21454 "".to_string(),
21455 "\thello world".to_string(),
21456 8,
21457 NonZeroU32::new(4).unwrap(),
21458 false,
21459 ),
21460 "hello\nworld"
21461 );
21462 assert_eq!(
21463 wrap_with_prefix(
21464 "// ".to_string(),
21465 "// ".to_string(),
21466 "xx \nyy zz aa bb cc".to_string(),
21467 12,
21468 NonZeroU32::new(4).unwrap(),
21469 false,
21470 ),
21471 "// xx yy zz\n// aa bb cc"
21472 );
21473 assert_eq!(
21474 wrap_with_prefix(
21475 String::new(),
21476 String::new(),
21477 "这是什么 \n 钢笔".to_string(),
21478 3,
21479 NonZeroU32::new(4).unwrap(),
21480 false,
21481 ),
21482 "这是什\n么 钢\n笔"
21483 );
21484}
21485
21486pub trait CollaborationHub {
21487 fn collaborators<'a>(&self, cx: &'a App) -> &'a HashMap<PeerId, Collaborator>;
21488 fn user_participant_indices<'a>(&self, cx: &'a App) -> &'a HashMap<u64, ParticipantIndex>;
21489 fn user_names(&self, cx: &App) -> HashMap<u64, SharedString>;
21490}
21491
21492impl CollaborationHub for Entity<Project> {
21493 fn collaborators<'a>(&self, cx: &'a App) -> &'a HashMap<PeerId, Collaborator> {
21494 self.read(cx).collaborators()
21495 }
21496
21497 fn user_participant_indices<'a>(&self, cx: &'a App) -> &'a HashMap<u64, ParticipantIndex> {
21498 self.read(cx).user_store().read(cx).participant_indices()
21499 }
21500
21501 fn user_names(&self, cx: &App) -> HashMap<u64, SharedString> {
21502 let this = self.read(cx);
21503 let user_ids = this.collaborators().values().map(|c| c.user_id);
21504 this.user_store().read(cx).participant_names(user_ids, cx)
21505 }
21506}
21507
21508pub trait SemanticsProvider {
21509 fn hover(
21510 &self,
21511 buffer: &Entity<Buffer>,
21512 position: text::Anchor,
21513 cx: &mut App,
21514 ) -> Option<Task<Vec<project::Hover>>>;
21515
21516 fn inline_values(
21517 &self,
21518 buffer_handle: Entity<Buffer>,
21519 range: Range<text::Anchor>,
21520 cx: &mut App,
21521 ) -> Option<Task<anyhow::Result<Vec<InlayHint>>>>;
21522
21523 fn inlay_hints(
21524 &self,
21525 buffer_handle: Entity<Buffer>,
21526 range: Range<text::Anchor>,
21527 cx: &mut App,
21528 ) -> Option<Task<anyhow::Result<Vec<InlayHint>>>>;
21529
21530 fn resolve_inlay_hint(
21531 &self,
21532 hint: InlayHint,
21533 buffer_handle: Entity<Buffer>,
21534 server_id: LanguageServerId,
21535 cx: &mut App,
21536 ) -> Option<Task<anyhow::Result<InlayHint>>>;
21537
21538 fn supports_inlay_hints(&self, buffer: &Entity<Buffer>, cx: &mut App) -> bool;
21539
21540 fn document_highlights(
21541 &self,
21542 buffer: &Entity<Buffer>,
21543 position: text::Anchor,
21544 cx: &mut App,
21545 ) -> Option<Task<Result<Vec<DocumentHighlight>>>>;
21546
21547 fn definitions(
21548 &self,
21549 buffer: &Entity<Buffer>,
21550 position: text::Anchor,
21551 kind: GotoDefinitionKind,
21552 cx: &mut App,
21553 ) -> Option<Task<Result<Vec<LocationLink>>>>;
21554
21555 fn range_for_rename(
21556 &self,
21557 buffer: &Entity<Buffer>,
21558 position: text::Anchor,
21559 cx: &mut App,
21560 ) -> Option<Task<Result<Option<Range<text::Anchor>>>>>;
21561
21562 fn perform_rename(
21563 &self,
21564 buffer: &Entity<Buffer>,
21565 position: text::Anchor,
21566 new_name: String,
21567 cx: &mut App,
21568 ) -> Option<Task<Result<ProjectTransaction>>>;
21569}
21570
21571pub trait CompletionProvider {
21572 fn completions(
21573 &self,
21574 excerpt_id: ExcerptId,
21575 buffer: &Entity<Buffer>,
21576 buffer_position: text::Anchor,
21577 trigger: CompletionContext,
21578 window: &mut Window,
21579 cx: &mut Context<Editor>,
21580 ) -> Task<Result<Vec<CompletionResponse>>>;
21581
21582 fn resolve_completions(
21583 &self,
21584 _buffer: Entity<Buffer>,
21585 _completion_indices: Vec<usize>,
21586 _completions: Rc<RefCell<Box<[Completion]>>>,
21587 _cx: &mut Context<Editor>,
21588 ) -> Task<Result<bool>> {
21589 Task::ready(Ok(false))
21590 }
21591
21592 fn apply_additional_edits_for_completion(
21593 &self,
21594 _buffer: Entity<Buffer>,
21595 _completions: Rc<RefCell<Box<[Completion]>>>,
21596 _completion_index: usize,
21597 _push_to_history: bool,
21598 _cx: &mut Context<Editor>,
21599 ) -> Task<Result<Option<language::Transaction>>> {
21600 Task::ready(Ok(None))
21601 }
21602
21603 fn is_completion_trigger(
21604 &self,
21605 buffer: &Entity<Buffer>,
21606 position: language::Anchor,
21607 text: &str,
21608 trigger_in_words: bool,
21609 menu_is_open: bool,
21610 cx: &mut Context<Editor>,
21611 ) -> bool;
21612
21613 fn selection_changed(&self, _mat: Option<&StringMatch>, _window: &mut Window, _cx: &mut App) {}
21614
21615 fn sort_completions(&self) -> bool {
21616 true
21617 }
21618
21619 fn filter_completions(&self) -> bool {
21620 true
21621 }
21622}
21623
21624pub trait CodeActionProvider {
21625 fn id(&self) -> Arc<str>;
21626
21627 fn code_actions(
21628 &self,
21629 buffer: &Entity<Buffer>,
21630 range: Range<text::Anchor>,
21631 window: &mut Window,
21632 cx: &mut App,
21633 ) -> Task<Result<Vec<CodeAction>>>;
21634
21635 fn apply_code_action(
21636 &self,
21637 buffer_handle: Entity<Buffer>,
21638 action: CodeAction,
21639 excerpt_id: ExcerptId,
21640 push_to_history: bool,
21641 window: &mut Window,
21642 cx: &mut App,
21643 ) -> Task<Result<ProjectTransaction>>;
21644}
21645
21646impl CodeActionProvider for Entity<Project> {
21647 fn id(&self) -> Arc<str> {
21648 "project".into()
21649 }
21650
21651 fn code_actions(
21652 &self,
21653 buffer: &Entity<Buffer>,
21654 range: Range<text::Anchor>,
21655 _window: &mut Window,
21656 cx: &mut App,
21657 ) -> Task<Result<Vec<CodeAction>>> {
21658 self.update(cx, |project, cx| {
21659 let code_lens = project.code_lens(buffer, range.clone(), cx);
21660 let code_actions = project.code_actions(buffer, range, None, cx);
21661 cx.background_spawn(async move {
21662 let (code_lens, code_actions) = join(code_lens, code_actions).await;
21663 Ok(code_lens
21664 .context("code lens fetch")?
21665 .into_iter()
21666 .chain(code_actions.context("code action fetch")?)
21667 .collect())
21668 })
21669 })
21670 }
21671
21672 fn apply_code_action(
21673 &self,
21674 buffer_handle: Entity<Buffer>,
21675 action: CodeAction,
21676 _excerpt_id: ExcerptId,
21677 push_to_history: bool,
21678 _window: &mut Window,
21679 cx: &mut App,
21680 ) -> Task<Result<ProjectTransaction>> {
21681 self.update(cx, |project, cx| {
21682 project.apply_code_action(buffer_handle, action, push_to_history, cx)
21683 })
21684 }
21685}
21686
21687fn snippet_completions(
21688 project: &Project,
21689 buffer: &Entity<Buffer>,
21690 buffer_position: text::Anchor,
21691 cx: &mut App,
21692) -> Task<Result<CompletionResponse>> {
21693 let languages = buffer.read(cx).languages_at(buffer_position);
21694 let snippet_store = project.snippets().read(cx);
21695
21696 let scopes: Vec<_> = languages
21697 .iter()
21698 .filter_map(|language| {
21699 let language_name = language.lsp_id();
21700 let snippets = snippet_store.snippets_for(Some(language_name), cx);
21701
21702 if snippets.is_empty() {
21703 None
21704 } else {
21705 Some((language.default_scope(), snippets))
21706 }
21707 })
21708 .collect();
21709
21710 if scopes.is_empty() {
21711 return Task::ready(Ok(CompletionResponse {
21712 completions: vec![],
21713 is_incomplete: false,
21714 }));
21715 }
21716
21717 let snapshot = buffer.read(cx).text_snapshot();
21718 let chars: String = snapshot
21719 .reversed_chars_for_range(text::Anchor::MIN..buffer_position)
21720 .collect();
21721 let executor = cx.background_executor().clone();
21722
21723 cx.background_spawn(async move {
21724 let mut is_incomplete = false;
21725 let mut completions: Vec<Completion> = Vec::new();
21726 for (scope, snippets) in scopes.into_iter() {
21727 let classifier = CharClassifier::new(Some(scope)).for_completion(true);
21728 let mut last_word = chars
21729 .chars()
21730 .take_while(|c| classifier.is_word(*c))
21731 .collect::<String>();
21732 last_word = last_word.chars().rev().collect();
21733
21734 if last_word.is_empty() {
21735 return Ok(CompletionResponse {
21736 completions: vec![],
21737 is_incomplete: true,
21738 });
21739 }
21740
21741 let as_offset = text::ToOffset::to_offset(&buffer_position, &snapshot);
21742 let to_lsp = |point: &text::Anchor| {
21743 let end = text::ToPointUtf16::to_point_utf16(point, &snapshot);
21744 point_to_lsp(end)
21745 };
21746 let lsp_end = to_lsp(&buffer_position);
21747
21748 let candidates = snippets
21749 .iter()
21750 .enumerate()
21751 .flat_map(|(ix, snippet)| {
21752 snippet
21753 .prefix
21754 .iter()
21755 .map(move |prefix| StringMatchCandidate::new(ix, &prefix))
21756 })
21757 .collect::<Vec<StringMatchCandidate>>();
21758
21759 const MAX_RESULTS: usize = 100;
21760 let mut matches = fuzzy::match_strings(
21761 &candidates,
21762 &last_word,
21763 last_word.chars().any(|c| c.is_uppercase()),
21764 true,
21765 MAX_RESULTS,
21766 &Default::default(),
21767 executor.clone(),
21768 )
21769 .await;
21770
21771 if matches.len() >= MAX_RESULTS {
21772 is_incomplete = true;
21773 }
21774
21775 // Remove all candidates where the query's start does not match the start of any word in the candidate
21776 if let Some(query_start) = last_word.chars().next() {
21777 matches.retain(|string_match| {
21778 split_words(&string_match.string).any(|word| {
21779 // Check that the first codepoint of the word as lowercase matches the first
21780 // codepoint of the query as lowercase
21781 word.chars()
21782 .flat_map(|codepoint| codepoint.to_lowercase())
21783 .zip(query_start.to_lowercase())
21784 .all(|(word_cp, query_cp)| word_cp == query_cp)
21785 })
21786 });
21787 }
21788
21789 let matched_strings = matches
21790 .into_iter()
21791 .map(|m| m.string)
21792 .collect::<HashSet<_>>();
21793
21794 completions.extend(snippets.iter().filter_map(|snippet| {
21795 let matching_prefix = snippet
21796 .prefix
21797 .iter()
21798 .find(|prefix| matched_strings.contains(*prefix))?;
21799 let start = as_offset - last_word.len();
21800 let start = snapshot.anchor_before(start);
21801 let range = start..buffer_position;
21802 let lsp_start = to_lsp(&start);
21803 let lsp_range = lsp::Range {
21804 start: lsp_start,
21805 end: lsp_end,
21806 };
21807 Some(Completion {
21808 replace_range: range,
21809 new_text: snippet.body.clone(),
21810 source: CompletionSource::Lsp {
21811 insert_range: None,
21812 server_id: LanguageServerId(usize::MAX),
21813 resolved: true,
21814 lsp_completion: Box::new(lsp::CompletionItem {
21815 label: snippet.prefix.first().unwrap().clone(),
21816 kind: Some(CompletionItemKind::SNIPPET),
21817 label_details: snippet.description.as_ref().map(|description| {
21818 lsp::CompletionItemLabelDetails {
21819 detail: Some(description.clone()),
21820 description: None,
21821 }
21822 }),
21823 insert_text_format: Some(InsertTextFormat::SNIPPET),
21824 text_edit: Some(lsp::CompletionTextEdit::InsertAndReplace(
21825 lsp::InsertReplaceEdit {
21826 new_text: snippet.body.clone(),
21827 insert: lsp_range,
21828 replace: lsp_range,
21829 },
21830 )),
21831 filter_text: Some(snippet.body.clone()),
21832 sort_text: Some(char::MAX.to_string()),
21833 ..lsp::CompletionItem::default()
21834 }),
21835 lsp_defaults: None,
21836 },
21837 label: CodeLabel {
21838 text: matching_prefix.clone(),
21839 runs: Vec::new(),
21840 filter_range: 0..matching_prefix.len(),
21841 },
21842 icon_path: None,
21843 documentation: Some(CompletionDocumentation::SingleLineAndMultiLinePlainText {
21844 single_line: snippet.name.clone().into(),
21845 plain_text: snippet
21846 .description
21847 .clone()
21848 .map(|description| description.into()),
21849 }),
21850 insert_text_mode: None,
21851 confirm: None,
21852 })
21853 }))
21854 }
21855
21856 Ok(CompletionResponse {
21857 completions,
21858 is_incomplete,
21859 })
21860 })
21861}
21862
21863impl CompletionProvider for Entity<Project> {
21864 fn completions(
21865 &self,
21866 _excerpt_id: ExcerptId,
21867 buffer: &Entity<Buffer>,
21868 buffer_position: text::Anchor,
21869 options: CompletionContext,
21870 _window: &mut Window,
21871 cx: &mut Context<Editor>,
21872 ) -> Task<Result<Vec<CompletionResponse>>> {
21873 self.update(cx, |project, cx| {
21874 let snippets = snippet_completions(project, buffer, buffer_position, cx);
21875 let project_completions = project.completions(buffer, buffer_position, options, cx);
21876 cx.background_spawn(async move {
21877 let mut responses = project_completions.await?;
21878 let snippets = snippets.await?;
21879 if !snippets.completions.is_empty() {
21880 responses.push(snippets);
21881 }
21882 Ok(responses)
21883 })
21884 })
21885 }
21886
21887 fn resolve_completions(
21888 &self,
21889 buffer: Entity<Buffer>,
21890 completion_indices: Vec<usize>,
21891 completions: Rc<RefCell<Box<[Completion]>>>,
21892 cx: &mut Context<Editor>,
21893 ) -> Task<Result<bool>> {
21894 self.update(cx, |project, cx| {
21895 project.lsp_store().update(cx, |lsp_store, cx| {
21896 lsp_store.resolve_completions(buffer, completion_indices, completions, cx)
21897 })
21898 })
21899 }
21900
21901 fn apply_additional_edits_for_completion(
21902 &self,
21903 buffer: Entity<Buffer>,
21904 completions: Rc<RefCell<Box<[Completion]>>>,
21905 completion_index: usize,
21906 push_to_history: bool,
21907 cx: &mut Context<Editor>,
21908 ) -> Task<Result<Option<language::Transaction>>> {
21909 self.update(cx, |project, cx| {
21910 project.lsp_store().update(cx, |lsp_store, cx| {
21911 lsp_store.apply_additional_edits_for_completion(
21912 buffer,
21913 completions,
21914 completion_index,
21915 push_to_history,
21916 cx,
21917 )
21918 })
21919 })
21920 }
21921
21922 fn is_completion_trigger(
21923 &self,
21924 buffer: &Entity<Buffer>,
21925 position: language::Anchor,
21926 text: &str,
21927 trigger_in_words: bool,
21928 menu_is_open: bool,
21929 cx: &mut Context<Editor>,
21930 ) -> bool {
21931 let mut chars = text.chars();
21932 let char = if let Some(char) = chars.next() {
21933 char
21934 } else {
21935 return false;
21936 };
21937 if chars.next().is_some() {
21938 return false;
21939 }
21940
21941 let buffer = buffer.read(cx);
21942 let snapshot = buffer.snapshot();
21943 if !menu_is_open && !snapshot.settings_at(position, cx).show_completions_on_input {
21944 return false;
21945 }
21946 let classifier = snapshot.char_classifier_at(position).for_completion(true);
21947 if trigger_in_words && classifier.is_word(char) {
21948 return true;
21949 }
21950
21951 buffer.completion_triggers().contains(text)
21952 }
21953}
21954
21955impl SemanticsProvider for Entity<Project> {
21956 fn hover(
21957 &self,
21958 buffer: &Entity<Buffer>,
21959 position: text::Anchor,
21960 cx: &mut App,
21961 ) -> Option<Task<Vec<project::Hover>>> {
21962 Some(self.update(cx, |project, cx| project.hover(buffer, position, cx)))
21963 }
21964
21965 fn document_highlights(
21966 &self,
21967 buffer: &Entity<Buffer>,
21968 position: text::Anchor,
21969 cx: &mut App,
21970 ) -> Option<Task<Result<Vec<DocumentHighlight>>>> {
21971 Some(self.update(cx, |project, cx| {
21972 project.document_highlights(buffer, position, cx)
21973 }))
21974 }
21975
21976 fn definitions(
21977 &self,
21978 buffer: &Entity<Buffer>,
21979 position: text::Anchor,
21980 kind: GotoDefinitionKind,
21981 cx: &mut App,
21982 ) -> Option<Task<Result<Vec<LocationLink>>>> {
21983 Some(self.update(cx, |project, cx| match kind {
21984 GotoDefinitionKind::Symbol => project.definitions(&buffer, position, cx),
21985 GotoDefinitionKind::Declaration => project.declarations(&buffer, position, cx),
21986 GotoDefinitionKind::Type => project.type_definitions(&buffer, position, cx),
21987 GotoDefinitionKind::Implementation => project.implementations(&buffer, position, cx),
21988 }))
21989 }
21990
21991 fn supports_inlay_hints(&self, buffer: &Entity<Buffer>, cx: &mut App) -> bool {
21992 // TODO: make this work for remote projects
21993 self.update(cx, |project, cx| {
21994 if project
21995 .active_debug_session(cx)
21996 .is_some_and(|(session, _)| session.read(cx).any_stopped_thread())
21997 {
21998 return true;
21999 }
22000
22001 buffer.update(cx, |buffer, cx| {
22002 project.any_language_server_supports_inlay_hints(buffer, cx)
22003 })
22004 })
22005 }
22006
22007 fn inline_values(
22008 &self,
22009 buffer_handle: Entity<Buffer>,
22010 range: Range<text::Anchor>,
22011 cx: &mut App,
22012 ) -> Option<Task<anyhow::Result<Vec<InlayHint>>>> {
22013 self.update(cx, |project, cx| {
22014 let (session, active_stack_frame) = project.active_debug_session(cx)?;
22015
22016 Some(project.inline_values(session, active_stack_frame, buffer_handle, range, cx))
22017 })
22018 }
22019
22020 fn inlay_hints(
22021 &self,
22022 buffer_handle: Entity<Buffer>,
22023 range: Range<text::Anchor>,
22024 cx: &mut App,
22025 ) -> Option<Task<anyhow::Result<Vec<InlayHint>>>> {
22026 Some(self.update(cx, |project, cx| {
22027 project.inlay_hints(buffer_handle, range, cx)
22028 }))
22029 }
22030
22031 fn resolve_inlay_hint(
22032 &self,
22033 hint: InlayHint,
22034 buffer_handle: Entity<Buffer>,
22035 server_id: LanguageServerId,
22036 cx: &mut App,
22037 ) -> Option<Task<anyhow::Result<InlayHint>>> {
22038 Some(self.update(cx, |project, cx| {
22039 project.resolve_inlay_hint(hint, buffer_handle, server_id, cx)
22040 }))
22041 }
22042
22043 fn range_for_rename(
22044 &self,
22045 buffer: &Entity<Buffer>,
22046 position: text::Anchor,
22047 cx: &mut App,
22048 ) -> Option<Task<Result<Option<Range<text::Anchor>>>>> {
22049 Some(self.update(cx, |project, cx| {
22050 let buffer = buffer.clone();
22051 let task = project.prepare_rename(buffer.clone(), position, cx);
22052 cx.spawn(async move |_, cx| {
22053 Ok(match task.await? {
22054 PrepareRenameResponse::Success(range) => Some(range),
22055 PrepareRenameResponse::InvalidPosition => None,
22056 PrepareRenameResponse::OnlyUnpreparedRenameSupported => {
22057 // Fallback on using TreeSitter info to determine identifier range
22058 buffer.read_with(cx, |buffer, _| {
22059 let snapshot = buffer.snapshot();
22060 let (range, kind) = snapshot.surrounding_word(position);
22061 if kind != Some(CharKind::Word) {
22062 return None;
22063 }
22064 Some(
22065 snapshot.anchor_before(range.start)
22066 ..snapshot.anchor_after(range.end),
22067 )
22068 })?
22069 }
22070 })
22071 })
22072 }))
22073 }
22074
22075 fn perform_rename(
22076 &self,
22077 buffer: &Entity<Buffer>,
22078 position: text::Anchor,
22079 new_name: String,
22080 cx: &mut App,
22081 ) -> Option<Task<Result<ProjectTransaction>>> {
22082 Some(self.update(cx, |project, cx| {
22083 project.perform_rename(buffer.clone(), position, new_name, cx)
22084 }))
22085 }
22086}
22087
22088fn inlay_hint_settings(
22089 location: Anchor,
22090 snapshot: &MultiBufferSnapshot,
22091 cx: &mut Context<Editor>,
22092) -> InlayHintSettings {
22093 let file = snapshot.file_at(location);
22094 let language = snapshot.language_at(location).map(|l| l.name());
22095 language_settings(language, file, cx).inlay_hints
22096}
22097
22098fn consume_contiguous_rows(
22099 contiguous_row_selections: &mut Vec<Selection<Point>>,
22100 selection: &Selection<Point>,
22101 display_map: &DisplaySnapshot,
22102 selections: &mut Peekable<std::slice::Iter<Selection<Point>>>,
22103) -> (MultiBufferRow, MultiBufferRow) {
22104 contiguous_row_selections.push(selection.clone());
22105 let start_row = MultiBufferRow(selection.start.row);
22106 let mut end_row = ending_row(selection, display_map);
22107
22108 while let Some(next_selection) = selections.peek() {
22109 if next_selection.start.row <= end_row.0 {
22110 end_row = ending_row(next_selection, display_map);
22111 contiguous_row_selections.push(selections.next().unwrap().clone());
22112 } else {
22113 break;
22114 }
22115 }
22116 (start_row, end_row)
22117}
22118
22119fn ending_row(next_selection: &Selection<Point>, display_map: &DisplaySnapshot) -> MultiBufferRow {
22120 if next_selection.end.column > 0 || next_selection.is_empty() {
22121 MultiBufferRow(display_map.next_line_boundary(next_selection.end).0.row + 1)
22122 } else {
22123 MultiBufferRow(next_selection.end.row)
22124 }
22125}
22126
22127impl EditorSnapshot {
22128 pub fn remote_selections_in_range<'a>(
22129 &'a self,
22130 range: &'a Range<Anchor>,
22131 collaboration_hub: &dyn CollaborationHub,
22132 cx: &'a App,
22133 ) -> impl 'a + Iterator<Item = RemoteSelection> {
22134 let participant_names = collaboration_hub.user_names(cx);
22135 let participant_indices = collaboration_hub.user_participant_indices(cx);
22136 let collaborators_by_peer_id = collaboration_hub.collaborators(cx);
22137 let collaborators_by_replica_id = collaborators_by_peer_id
22138 .values()
22139 .map(|collaborator| (collaborator.replica_id, collaborator))
22140 .collect::<HashMap<_, _>>();
22141 self.buffer_snapshot
22142 .selections_in_range(range, false)
22143 .filter_map(move |(replica_id, line_mode, cursor_shape, selection)| {
22144 if replica_id == AGENT_REPLICA_ID {
22145 Some(RemoteSelection {
22146 replica_id,
22147 selection,
22148 cursor_shape,
22149 line_mode,
22150 collaborator_id: CollaboratorId::Agent,
22151 user_name: Some("Agent".into()),
22152 color: cx.theme().players().agent(),
22153 })
22154 } else {
22155 let collaborator = collaborators_by_replica_id.get(&replica_id)?;
22156 let participant_index = participant_indices.get(&collaborator.user_id).copied();
22157 let user_name = participant_names.get(&collaborator.user_id).cloned();
22158 Some(RemoteSelection {
22159 replica_id,
22160 selection,
22161 cursor_shape,
22162 line_mode,
22163 collaborator_id: CollaboratorId::PeerId(collaborator.peer_id),
22164 user_name,
22165 color: if let Some(index) = participant_index {
22166 cx.theme().players().color_for_participant(index.0)
22167 } else {
22168 cx.theme().players().absent()
22169 },
22170 })
22171 }
22172 })
22173 }
22174
22175 pub fn hunks_for_ranges(
22176 &self,
22177 ranges: impl IntoIterator<Item = Range<Point>>,
22178 ) -> Vec<MultiBufferDiffHunk> {
22179 let mut hunks = Vec::new();
22180 let mut processed_buffer_rows: HashMap<BufferId, HashSet<Range<text::Anchor>>> =
22181 HashMap::default();
22182 for query_range in ranges {
22183 let query_rows =
22184 MultiBufferRow(query_range.start.row)..MultiBufferRow(query_range.end.row + 1);
22185 for hunk in self.buffer_snapshot.diff_hunks_in_range(
22186 Point::new(query_rows.start.0, 0)..Point::new(query_rows.end.0, 0),
22187 ) {
22188 // Include deleted hunks that are adjacent to the query range, because
22189 // otherwise they would be missed.
22190 let mut intersects_range = hunk.row_range.overlaps(&query_rows);
22191 if hunk.status().is_deleted() {
22192 intersects_range |= hunk.row_range.start == query_rows.end;
22193 intersects_range |= hunk.row_range.end == query_rows.start;
22194 }
22195 if intersects_range {
22196 if !processed_buffer_rows
22197 .entry(hunk.buffer_id)
22198 .or_default()
22199 .insert(hunk.buffer_range.start..hunk.buffer_range.end)
22200 {
22201 continue;
22202 }
22203 hunks.push(hunk);
22204 }
22205 }
22206 }
22207
22208 hunks
22209 }
22210
22211 fn display_diff_hunks_for_rows<'a>(
22212 &'a self,
22213 display_rows: Range<DisplayRow>,
22214 folded_buffers: &'a HashSet<BufferId>,
22215 ) -> impl 'a + Iterator<Item = DisplayDiffHunk> {
22216 let buffer_start = DisplayPoint::new(display_rows.start, 0).to_point(self);
22217 let buffer_end = DisplayPoint::new(display_rows.end, 0).to_point(self);
22218
22219 self.buffer_snapshot
22220 .diff_hunks_in_range(buffer_start..buffer_end)
22221 .filter_map(|hunk| {
22222 if folded_buffers.contains(&hunk.buffer_id) {
22223 return None;
22224 }
22225
22226 let hunk_start_point = Point::new(hunk.row_range.start.0, 0);
22227 let hunk_end_point = Point::new(hunk.row_range.end.0, 0);
22228
22229 let hunk_display_start = self.point_to_display_point(hunk_start_point, Bias::Left);
22230 let hunk_display_end = self.point_to_display_point(hunk_end_point, Bias::Right);
22231
22232 let display_hunk = if hunk_display_start.column() != 0 {
22233 DisplayDiffHunk::Folded {
22234 display_row: hunk_display_start.row(),
22235 }
22236 } else {
22237 let mut end_row = hunk_display_end.row();
22238 if hunk_display_end.column() > 0 {
22239 end_row.0 += 1;
22240 }
22241 let is_created_file = hunk.is_created_file();
22242 DisplayDiffHunk::Unfolded {
22243 status: hunk.status(),
22244 diff_base_byte_range: hunk.diff_base_byte_range,
22245 display_row_range: hunk_display_start.row()..end_row,
22246 multi_buffer_range: Anchor::range_in_buffer(
22247 hunk.excerpt_id,
22248 hunk.buffer_id,
22249 hunk.buffer_range,
22250 ),
22251 is_created_file,
22252 }
22253 };
22254
22255 Some(display_hunk)
22256 })
22257 }
22258
22259 pub fn language_at<T: ToOffset>(&self, position: T) -> Option<&Arc<Language>> {
22260 self.display_snapshot.buffer_snapshot.language_at(position)
22261 }
22262
22263 pub fn is_focused(&self) -> bool {
22264 self.is_focused
22265 }
22266
22267 pub fn placeholder_text(&self) -> Option<&Arc<str>> {
22268 self.placeholder_text.as_ref()
22269 }
22270
22271 pub fn scroll_position(&self) -> gpui::Point<f32> {
22272 self.scroll_anchor.scroll_position(&self.display_snapshot)
22273 }
22274
22275 fn gutter_dimensions(
22276 &self,
22277 font_id: FontId,
22278 font_size: Pixels,
22279 max_line_number_width: Pixels,
22280 cx: &App,
22281 ) -> Option<GutterDimensions> {
22282 if !self.show_gutter {
22283 return None;
22284 }
22285
22286 let ch_width = cx.text_system().ch_width(font_id, font_size).log_err()?;
22287 let ch_advance = cx.text_system().ch_advance(font_id, font_size).log_err()?;
22288
22289 let show_git_gutter = self.show_git_diff_gutter.unwrap_or_else(|| {
22290 matches!(
22291 ProjectSettings::get_global(cx).git.git_gutter,
22292 Some(GitGutterSetting::TrackedFiles)
22293 )
22294 });
22295 let gutter_settings = EditorSettings::get_global(cx).gutter;
22296 let show_line_numbers = self
22297 .show_line_numbers
22298 .unwrap_or(gutter_settings.line_numbers);
22299 let line_gutter_width = if show_line_numbers {
22300 // Avoid flicker-like gutter resizes when the line number gains another digit by
22301 // only resizing the gutter on files with > 10**min_line_number_digits lines.
22302 let min_width_for_number_on_gutter =
22303 ch_advance * gutter_settings.min_line_number_digits as f32;
22304 max_line_number_width.max(min_width_for_number_on_gutter)
22305 } else {
22306 0.0.into()
22307 };
22308
22309 let show_runnables = self.show_runnables.unwrap_or(gutter_settings.runnables);
22310 let show_breakpoints = self.show_breakpoints.unwrap_or(gutter_settings.breakpoints);
22311
22312 let git_blame_entries_width =
22313 self.git_blame_gutter_max_author_length
22314 .map(|max_author_length| {
22315 let renderer = cx.global::<GlobalBlameRenderer>().0.clone();
22316 const MAX_RELATIVE_TIMESTAMP: &str = "60 minutes ago";
22317
22318 /// The number of characters to dedicate to gaps and margins.
22319 const SPACING_WIDTH: usize = 4;
22320
22321 let max_char_count = max_author_length.min(renderer.max_author_length())
22322 + ::git::SHORT_SHA_LENGTH
22323 + MAX_RELATIVE_TIMESTAMP.len()
22324 + SPACING_WIDTH;
22325
22326 ch_advance * max_char_count
22327 });
22328
22329 let is_singleton = self.buffer_snapshot.is_singleton();
22330
22331 let mut left_padding = git_blame_entries_width.unwrap_or(Pixels::ZERO);
22332 left_padding += if !is_singleton {
22333 ch_width * 4.0
22334 } else if show_runnables || show_breakpoints {
22335 ch_width * 3.0
22336 } else if show_git_gutter && show_line_numbers {
22337 ch_width * 2.0
22338 } else if show_git_gutter || show_line_numbers {
22339 ch_width
22340 } else {
22341 px(0.)
22342 };
22343
22344 let shows_folds = is_singleton && gutter_settings.folds;
22345
22346 let right_padding = if shows_folds && show_line_numbers {
22347 ch_width * 4.0
22348 } else if shows_folds || (!is_singleton && show_line_numbers) {
22349 ch_width * 3.0
22350 } else if show_line_numbers {
22351 ch_width
22352 } else {
22353 px(0.)
22354 };
22355
22356 Some(GutterDimensions {
22357 left_padding,
22358 right_padding,
22359 width: line_gutter_width + left_padding + right_padding,
22360 margin: GutterDimensions::default_gutter_margin(font_id, font_size, cx),
22361 git_blame_entries_width,
22362 })
22363 }
22364
22365 pub fn render_crease_toggle(
22366 &self,
22367 buffer_row: MultiBufferRow,
22368 row_contains_cursor: bool,
22369 editor: Entity<Editor>,
22370 window: &mut Window,
22371 cx: &mut App,
22372 ) -> Option<AnyElement> {
22373 let folded = self.is_line_folded(buffer_row);
22374 let mut is_foldable = false;
22375
22376 if let Some(crease) = self
22377 .crease_snapshot
22378 .query_row(buffer_row, &self.buffer_snapshot)
22379 {
22380 is_foldable = true;
22381 match crease {
22382 Crease::Inline { render_toggle, .. } | Crease::Block { render_toggle, .. } => {
22383 if let Some(render_toggle) = render_toggle {
22384 let toggle_callback =
22385 Arc::new(move |folded, window: &mut Window, cx: &mut App| {
22386 if folded {
22387 editor.update(cx, |editor, cx| {
22388 editor.fold_at(buffer_row, window, cx)
22389 });
22390 } else {
22391 editor.update(cx, |editor, cx| {
22392 editor.unfold_at(buffer_row, window, cx)
22393 });
22394 }
22395 });
22396 return Some((render_toggle)(
22397 buffer_row,
22398 folded,
22399 toggle_callback,
22400 window,
22401 cx,
22402 ));
22403 }
22404 }
22405 }
22406 }
22407
22408 is_foldable |= self.starts_indent(buffer_row);
22409
22410 if folded || (is_foldable && (row_contains_cursor || self.gutter_hovered)) {
22411 Some(
22412 Disclosure::new(("gutter_crease", buffer_row.0), !folded)
22413 .toggle_state(folded)
22414 .on_click(window.listener_for(&editor, move |this, _e, window, cx| {
22415 if folded {
22416 this.unfold_at(buffer_row, window, cx);
22417 } else {
22418 this.fold_at(buffer_row, window, cx);
22419 }
22420 }))
22421 .into_any_element(),
22422 )
22423 } else {
22424 None
22425 }
22426 }
22427
22428 pub fn render_crease_trailer(
22429 &self,
22430 buffer_row: MultiBufferRow,
22431 window: &mut Window,
22432 cx: &mut App,
22433 ) -> Option<AnyElement> {
22434 let folded = self.is_line_folded(buffer_row);
22435 if let Crease::Inline { render_trailer, .. } = self
22436 .crease_snapshot
22437 .query_row(buffer_row, &self.buffer_snapshot)?
22438 {
22439 let render_trailer = render_trailer.as_ref()?;
22440 Some(render_trailer(buffer_row, folded, window, cx))
22441 } else {
22442 None
22443 }
22444 }
22445}
22446
22447impl Deref for EditorSnapshot {
22448 type Target = DisplaySnapshot;
22449
22450 fn deref(&self) -> &Self::Target {
22451 &self.display_snapshot
22452 }
22453}
22454
22455#[derive(Clone, Debug, PartialEq, Eq)]
22456pub enum EditorEvent {
22457 InputIgnored {
22458 text: Arc<str>,
22459 },
22460 InputHandled {
22461 utf16_range_to_replace: Option<Range<isize>>,
22462 text: Arc<str>,
22463 },
22464 ExcerptsAdded {
22465 buffer: Entity<Buffer>,
22466 predecessor: ExcerptId,
22467 excerpts: Vec<(ExcerptId, ExcerptRange<language::Anchor>)>,
22468 },
22469 ExcerptsRemoved {
22470 ids: Vec<ExcerptId>,
22471 removed_buffer_ids: Vec<BufferId>,
22472 },
22473 BufferFoldToggled {
22474 ids: Vec<ExcerptId>,
22475 folded: bool,
22476 },
22477 ExcerptsEdited {
22478 ids: Vec<ExcerptId>,
22479 },
22480 ExcerptsExpanded {
22481 ids: Vec<ExcerptId>,
22482 },
22483 BufferEdited,
22484 Edited {
22485 transaction_id: clock::Lamport,
22486 },
22487 Reparsed(BufferId),
22488 Focused,
22489 FocusedIn,
22490 Blurred,
22491 DirtyChanged,
22492 Saved,
22493 TitleChanged,
22494 DiffBaseChanged,
22495 SelectionsChanged {
22496 local: bool,
22497 },
22498 ScrollPositionChanged {
22499 local: bool,
22500 autoscroll: bool,
22501 },
22502 Closed,
22503 TransactionUndone {
22504 transaction_id: clock::Lamport,
22505 },
22506 TransactionBegun {
22507 transaction_id: clock::Lamport,
22508 },
22509 Reloaded,
22510 CursorShapeChanged,
22511 PushedToNavHistory {
22512 anchor: Anchor,
22513 is_deactivate: bool,
22514 },
22515}
22516
22517impl EventEmitter<EditorEvent> for Editor {}
22518
22519impl Focusable for Editor {
22520 fn focus_handle(&self, _cx: &App) -> FocusHandle {
22521 self.focus_handle.clone()
22522 }
22523}
22524
22525impl Render for Editor {
22526 fn render(&mut self, _: &mut Window, cx: &mut Context<Self>) -> impl IntoElement {
22527 let settings = ThemeSettings::get_global(cx);
22528
22529 let mut text_style = match self.mode {
22530 EditorMode::SingleLine { .. } | EditorMode::AutoHeight { .. } => TextStyle {
22531 color: cx.theme().colors().editor_foreground,
22532 font_family: settings.ui_font.family.clone(),
22533 font_features: settings.ui_font.features.clone(),
22534 font_fallbacks: settings.ui_font.fallbacks.clone(),
22535 font_size: rems(0.875).into(),
22536 font_weight: settings.ui_font.weight,
22537 line_height: relative(settings.buffer_line_height.value()),
22538 ..Default::default()
22539 },
22540 EditorMode::Full { .. } | EditorMode::Minimap { .. } => TextStyle {
22541 color: cx.theme().colors().editor_foreground,
22542 font_family: settings.buffer_font.family.clone(),
22543 font_features: settings.buffer_font.features.clone(),
22544 font_fallbacks: settings.buffer_font.fallbacks.clone(),
22545 font_size: settings.buffer_font_size(cx).into(),
22546 font_weight: settings.buffer_font.weight,
22547 line_height: relative(settings.buffer_line_height.value()),
22548 ..Default::default()
22549 },
22550 };
22551 if let Some(text_style_refinement) = &self.text_style_refinement {
22552 text_style.refine(text_style_refinement)
22553 }
22554
22555 let background = match self.mode {
22556 EditorMode::SingleLine { .. } => cx.theme().system().transparent,
22557 EditorMode::AutoHeight { .. } => cx.theme().system().transparent,
22558 EditorMode::Full { .. } => cx.theme().colors().editor_background,
22559 EditorMode::Minimap { .. } => cx.theme().colors().editor_background.opacity(0.7),
22560 };
22561
22562 EditorElement::new(
22563 &cx.entity(),
22564 EditorStyle {
22565 background,
22566 border: cx.theme().colors().border,
22567 local_player: cx.theme().players().local(),
22568 text: text_style,
22569 scrollbar_width: EditorElement::SCROLLBAR_WIDTH,
22570 syntax: cx.theme().syntax().clone(),
22571 status: cx.theme().status().clone(),
22572 inlay_hints_style: make_inlay_hints_style(cx),
22573 inline_completion_styles: make_suggestion_styles(cx),
22574 unnecessary_code_fade: ThemeSettings::get_global(cx).unnecessary_code_fade,
22575 show_underlines: self.diagnostics_enabled(),
22576 },
22577 )
22578 }
22579}
22580
22581impl EntityInputHandler for Editor {
22582 fn text_for_range(
22583 &mut self,
22584 range_utf16: Range<usize>,
22585 adjusted_range: &mut Option<Range<usize>>,
22586 _: &mut Window,
22587 cx: &mut Context<Self>,
22588 ) -> Option<String> {
22589 let snapshot = self.buffer.read(cx).read(cx);
22590 let start = snapshot.clip_offset_utf16(OffsetUtf16(range_utf16.start), Bias::Left);
22591 let end = snapshot.clip_offset_utf16(OffsetUtf16(range_utf16.end), Bias::Right);
22592 if (start.0..end.0) != range_utf16 {
22593 adjusted_range.replace(start.0..end.0);
22594 }
22595 Some(snapshot.text_for_range(start..end).collect())
22596 }
22597
22598 fn selected_text_range(
22599 &mut self,
22600 ignore_disabled_input: bool,
22601 _: &mut Window,
22602 cx: &mut Context<Self>,
22603 ) -> Option<UTF16Selection> {
22604 // Prevent the IME menu from appearing when holding down an alphabetic key
22605 // while input is disabled.
22606 if !ignore_disabled_input && !self.input_enabled {
22607 return None;
22608 }
22609
22610 let selection = self.selections.newest::<OffsetUtf16>(cx);
22611 let range = selection.range();
22612
22613 Some(UTF16Selection {
22614 range: range.start.0..range.end.0,
22615 reversed: selection.reversed,
22616 })
22617 }
22618
22619 fn marked_text_range(&self, _: &mut Window, cx: &mut Context<Self>) -> Option<Range<usize>> {
22620 let snapshot = self.buffer.read(cx).read(cx);
22621 let range = self.text_highlights::<InputComposition>(cx)?.1.first()?;
22622 Some(range.start.to_offset_utf16(&snapshot).0..range.end.to_offset_utf16(&snapshot).0)
22623 }
22624
22625 fn unmark_text(&mut self, _: &mut Window, cx: &mut Context<Self>) {
22626 self.clear_highlights::<InputComposition>(cx);
22627 self.ime_transaction.take();
22628 }
22629
22630 fn replace_text_in_range(
22631 &mut self,
22632 range_utf16: Option<Range<usize>>,
22633 text: &str,
22634 window: &mut Window,
22635 cx: &mut Context<Self>,
22636 ) {
22637 if !self.input_enabled {
22638 cx.emit(EditorEvent::InputIgnored { text: text.into() });
22639 return;
22640 }
22641
22642 self.transact(window, cx, |this, window, cx| {
22643 let new_selected_ranges = if let Some(range_utf16) = range_utf16 {
22644 let range_utf16 = OffsetUtf16(range_utf16.start)..OffsetUtf16(range_utf16.end);
22645 Some(this.selection_replacement_ranges(range_utf16, cx))
22646 } else {
22647 this.marked_text_ranges(cx)
22648 };
22649
22650 let range_to_replace = new_selected_ranges.as_ref().and_then(|ranges_to_replace| {
22651 let newest_selection_id = this.selections.newest_anchor().id;
22652 this.selections
22653 .all::<OffsetUtf16>(cx)
22654 .iter()
22655 .zip(ranges_to_replace.iter())
22656 .find_map(|(selection, range)| {
22657 if selection.id == newest_selection_id {
22658 Some(
22659 (range.start.0 as isize - selection.head().0 as isize)
22660 ..(range.end.0 as isize - selection.head().0 as isize),
22661 )
22662 } else {
22663 None
22664 }
22665 })
22666 });
22667
22668 cx.emit(EditorEvent::InputHandled {
22669 utf16_range_to_replace: range_to_replace,
22670 text: text.into(),
22671 });
22672
22673 if let Some(new_selected_ranges) = new_selected_ranges {
22674 this.change_selections(SelectionEffects::no_scroll(), window, cx, |selections| {
22675 selections.select_ranges(new_selected_ranges)
22676 });
22677 this.backspace(&Default::default(), window, cx);
22678 }
22679
22680 this.handle_input(text, window, cx);
22681 });
22682
22683 if let Some(transaction) = self.ime_transaction {
22684 self.buffer.update(cx, |buffer, cx| {
22685 buffer.group_until_transaction(transaction, cx);
22686 });
22687 }
22688
22689 self.unmark_text(window, cx);
22690 }
22691
22692 fn replace_and_mark_text_in_range(
22693 &mut self,
22694 range_utf16: Option<Range<usize>>,
22695 text: &str,
22696 new_selected_range_utf16: Option<Range<usize>>,
22697 window: &mut Window,
22698 cx: &mut Context<Self>,
22699 ) {
22700 if !self.input_enabled {
22701 return;
22702 }
22703
22704 let transaction = self.transact(window, cx, |this, window, cx| {
22705 let ranges_to_replace = if let Some(mut marked_ranges) = this.marked_text_ranges(cx) {
22706 let snapshot = this.buffer.read(cx).read(cx);
22707 if let Some(relative_range_utf16) = range_utf16.as_ref() {
22708 for marked_range in &mut marked_ranges {
22709 marked_range.end.0 = marked_range.start.0 + relative_range_utf16.end;
22710 marked_range.start.0 += relative_range_utf16.start;
22711 marked_range.start =
22712 snapshot.clip_offset_utf16(marked_range.start, Bias::Left);
22713 marked_range.end =
22714 snapshot.clip_offset_utf16(marked_range.end, Bias::Right);
22715 }
22716 }
22717 Some(marked_ranges)
22718 } else if let Some(range_utf16) = range_utf16 {
22719 let range_utf16 = OffsetUtf16(range_utf16.start)..OffsetUtf16(range_utf16.end);
22720 Some(this.selection_replacement_ranges(range_utf16, cx))
22721 } else {
22722 None
22723 };
22724
22725 let range_to_replace = ranges_to_replace.as_ref().and_then(|ranges_to_replace| {
22726 let newest_selection_id = this.selections.newest_anchor().id;
22727 this.selections
22728 .all::<OffsetUtf16>(cx)
22729 .iter()
22730 .zip(ranges_to_replace.iter())
22731 .find_map(|(selection, range)| {
22732 if selection.id == newest_selection_id {
22733 Some(
22734 (range.start.0 as isize - selection.head().0 as isize)
22735 ..(range.end.0 as isize - selection.head().0 as isize),
22736 )
22737 } else {
22738 None
22739 }
22740 })
22741 });
22742
22743 cx.emit(EditorEvent::InputHandled {
22744 utf16_range_to_replace: range_to_replace,
22745 text: text.into(),
22746 });
22747
22748 if let Some(ranges) = ranges_to_replace {
22749 this.change_selections(SelectionEffects::no_scroll(), window, cx, |s| {
22750 s.select_ranges(ranges)
22751 });
22752 }
22753
22754 let marked_ranges = {
22755 let snapshot = this.buffer.read(cx).read(cx);
22756 this.selections
22757 .disjoint_anchors()
22758 .iter()
22759 .map(|selection| {
22760 selection.start.bias_left(&snapshot)..selection.end.bias_right(&snapshot)
22761 })
22762 .collect::<Vec<_>>()
22763 };
22764
22765 if text.is_empty() {
22766 this.unmark_text(window, cx);
22767 } else {
22768 this.highlight_text::<InputComposition>(
22769 marked_ranges.clone(),
22770 HighlightStyle {
22771 underline: Some(UnderlineStyle {
22772 thickness: px(1.),
22773 color: None,
22774 wavy: false,
22775 }),
22776 ..Default::default()
22777 },
22778 cx,
22779 );
22780 }
22781
22782 // Disable auto-closing when composing text (i.e. typing a `"` on a Brazilian keyboard)
22783 let use_autoclose = this.use_autoclose;
22784 let use_auto_surround = this.use_auto_surround;
22785 this.set_use_autoclose(false);
22786 this.set_use_auto_surround(false);
22787 this.handle_input(text, window, cx);
22788 this.set_use_autoclose(use_autoclose);
22789 this.set_use_auto_surround(use_auto_surround);
22790
22791 if let Some(new_selected_range) = new_selected_range_utf16 {
22792 let snapshot = this.buffer.read(cx).read(cx);
22793 let new_selected_ranges = marked_ranges
22794 .into_iter()
22795 .map(|marked_range| {
22796 let insertion_start = marked_range.start.to_offset_utf16(&snapshot).0;
22797 let new_start = OffsetUtf16(new_selected_range.start + insertion_start);
22798 let new_end = OffsetUtf16(new_selected_range.end + insertion_start);
22799 snapshot.clip_offset_utf16(new_start, Bias::Left)
22800 ..snapshot.clip_offset_utf16(new_end, Bias::Right)
22801 })
22802 .collect::<Vec<_>>();
22803
22804 drop(snapshot);
22805 this.change_selections(SelectionEffects::no_scroll(), window, cx, |selections| {
22806 selections.select_ranges(new_selected_ranges)
22807 });
22808 }
22809 });
22810
22811 self.ime_transaction = self.ime_transaction.or(transaction);
22812 if let Some(transaction) = self.ime_transaction {
22813 self.buffer.update(cx, |buffer, cx| {
22814 buffer.group_until_transaction(transaction, cx);
22815 });
22816 }
22817
22818 if self.text_highlights::<InputComposition>(cx).is_none() {
22819 self.ime_transaction.take();
22820 }
22821 }
22822
22823 fn bounds_for_range(
22824 &mut self,
22825 range_utf16: Range<usize>,
22826 element_bounds: gpui::Bounds<Pixels>,
22827 window: &mut Window,
22828 cx: &mut Context<Self>,
22829 ) -> Option<gpui::Bounds<Pixels>> {
22830 let text_layout_details = self.text_layout_details(window);
22831 let CharacterDimensions {
22832 em_width,
22833 em_advance,
22834 line_height,
22835 } = self.character_dimensions(window);
22836
22837 let snapshot = self.snapshot(window, cx);
22838 let scroll_position = snapshot.scroll_position();
22839 let scroll_left = scroll_position.x * em_advance;
22840
22841 let start = OffsetUtf16(range_utf16.start).to_display_point(&snapshot);
22842 let x = snapshot.x_for_display_point(start, &text_layout_details) - scroll_left
22843 + self.gutter_dimensions.full_width();
22844 let y = line_height * (start.row().as_f32() - scroll_position.y);
22845
22846 Some(Bounds {
22847 origin: element_bounds.origin + point(x, y),
22848 size: size(em_width, line_height),
22849 })
22850 }
22851
22852 fn character_index_for_point(
22853 &mut self,
22854 point: gpui::Point<Pixels>,
22855 _window: &mut Window,
22856 _cx: &mut Context<Self>,
22857 ) -> Option<usize> {
22858 let position_map = self.last_position_map.as_ref()?;
22859 if !position_map.text_hitbox.contains(&point) {
22860 return None;
22861 }
22862 let display_point = position_map.point_for_position(point).previous_valid;
22863 let anchor = position_map
22864 .snapshot
22865 .display_point_to_anchor(display_point, Bias::Left);
22866 let utf16_offset = anchor.to_offset_utf16(&position_map.snapshot.buffer_snapshot);
22867 Some(utf16_offset.0)
22868 }
22869}
22870
22871trait SelectionExt {
22872 fn display_range(&self, map: &DisplaySnapshot) -> Range<DisplayPoint>;
22873 fn spanned_rows(
22874 &self,
22875 include_end_if_at_line_start: bool,
22876 map: &DisplaySnapshot,
22877 ) -> Range<MultiBufferRow>;
22878}
22879
22880impl<T: ToPoint + ToOffset> SelectionExt for Selection<T> {
22881 fn display_range(&self, map: &DisplaySnapshot) -> Range<DisplayPoint> {
22882 let start = self
22883 .start
22884 .to_point(&map.buffer_snapshot)
22885 .to_display_point(map);
22886 let end = self
22887 .end
22888 .to_point(&map.buffer_snapshot)
22889 .to_display_point(map);
22890 if self.reversed {
22891 end..start
22892 } else {
22893 start..end
22894 }
22895 }
22896
22897 fn spanned_rows(
22898 &self,
22899 include_end_if_at_line_start: bool,
22900 map: &DisplaySnapshot,
22901 ) -> Range<MultiBufferRow> {
22902 let start = self.start.to_point(&map.buffer_snapshot);
22903 let mut end = self.end.to_point(&map.buffer_snapshot);
22904 if !include_end_if_at_line_start && start.row != end.row && end.column == 0 {
22905 end.row -= 1;
22906 }
22907
22908 let buffer_start = map.prev_line_boundary(start).0;
22909 let buffer_end = map.next_line_boundary(end).0;
22910 MultiBufferRow(buffer_start.row)..MultiBufferRow(buffer_end.row + 1)
22911 }
22912}
22913
22914impl<T: InvalidationRegion> InvalidationStack<T> {
22915 fn invalidate<S>(&mut self, selections: &[Selection<S>], buffer: &MultiBufferSnapshot)
22916 where
22917 S: Clone + ToOffset,
22918 {
22919 while let Some(region) = self.last() {
22920 let all_selections_inside_invalidation_ranges =
22921 if selections.len() == region.ranges().len() {
22922 selections
22923 .iter()
22924 .zip(region.ranges().iter().map(|r| r.to_offset(buffer)))
22925 .all(|(selection, invalidation_range)| {
22926 let head = selection.head().to_offset(buffer);
22927 invalidation_range.start <= head && invalidation_range.end >= head
22928 })
22929 } else {
22930 false
22931 };
22932
22933 if all_selections_inside_invalidation_ranges {
22934 break;
22935 } else {
22936 self.pop();
22937 }
22938 }
22939 }
22940}
22941
22942impl<T> Default for InvalidationStack<T> {
22943 fn default() -> Self {
22944 Self(Default::default())
22945 }
22946}
22947
22948impl<T> Deref for InvalidationStack<T> {
22949 type Target = Vec<T>;
22950
22951 fn deref(&self) -> &Self::Target {
22952 &self.0
22953 }
22954}
22955
22956impl<T> DerefMut for InvalidationStack<T> {
22957 fn deref_mut(&mut self) -> &mut Self::Target {
22958 &mut self.0
22959 }
22960}
22961
22962impl InvalidationRegion for SnippetState {
22963 fn ranges(&self) -> &[Range<Anchor>] {
22964 &self.ranges[self.active_index]
22965 }
22966}
22967
22968fn inline_completion_edit_text(
22969 current_snapshot: &BufferSnapshot,
22970 edits: &[(Range<Anchor>, String)],
22971 edit_preview: &EditPreview,
22972 include_deletions: bool,
22973 cx: &App,
22974) -> HighlightedText {
22975 let edits = edits
22976 .iter()
22977 .map(|(anchor, text)| {
22978 (
22979 anchor.start.text_anchor..anchor.end.text_anchor,
22980 text.clone(),
22981 )
22982 })
22983 .collect::<Vec<_>>();
22984
22985 edit_preview.highlight_edits(current_snapshot, &edits, include_deletions, cx)
22986}
22987
22988pub fn diagnostic_style(severity: lsp::DiagnosticSeverity, colors: &StatusColors) -> Hsla {
22989 match severity {
22990 lsp::DiagnosticSeverity::ERROR => colors.error,
22991 lsp::DiagnosticSeverity::WARNING => colors.warning,
22992 lsp::DiagnosticSeverity::INFORMATION => colors.info,
22993 lsp::DiagnosticSeverity::HINT => colors.info,
22994 _ => colors.ignored,
22995 }
22996}
22997
22998pub fn styled_runs_for_code_label<'a>(
22999 label: &'a CodeLabel,
23000 syntax_theme: &'a theme::SyntaxTheme,
23001) -> impl 'a + Iterator<Item = (Range<usize>, HighlightStyle)> {
23002 let fade_out = HighlightStyle {
23003 fade_out: Some(0.35),
23004 ..Default::default()
23005 };
23006
23007 let mut prev_end = label.filter_range.end;
23008 label
23009 .runs
23010 .iter()
23011 .enumerate()
23012 .flat_map(move |(ix, (range, highlight_id))| {
23013 let style = if let Some(style) = highlight_id.style(syntax_theme) {
23014 style
23015 } else {
23016 return Default::default();
23017 };
23018 let mut muted_style = style;
23019 muted_style.highlight(fade_out);
23020
23021 let mut runs = SmallVec::<[(Range<usize>, HighlightStyle); 3]>::new();
23022 if range.start >= label.filter_range.end {
23023 if range.start > prev_end {
23024 runs.push((prev_end..range.start, fade_out));
23025 }
23026 runs.push((range.clone(), muted_style));
23027 } else if range.end <= label.filter_range.end {
23028 runs.push((range.clone(), style));
23029 } else {
23030 runs.push((range.start..label.filter_range.end, style));
23031 runs.push((label.filter_range.end..range.end, muted_style));
23032 }
23033 prev_end = cmp::max(prev_end, range.end);
23034
23035 if ix + 1 == label.runs.len() && label.text.len() > prev_end {
23036 runs.push((prev_end..label.text.len(), fade_out));
23037 }
23038
23039 runs
23040 })
23041}
23042
23043pub(crate) fn split_words(text: &str) -> impl std::iter::Iterator<Item = &str> + '_ {
23044 let mut prev_index = 0;
23045 let mut prev_codepoint: Option<char> = None;
23046 text.char_indices()
23047 .chain([(text.len(), '\0')])
23048 .filter_map(move |(index, codepoint)| {
23049 let prev_codepoint = prev_codepoint.replace(codepoint)?;
23050 let is_boundary = index == text.len()
23051 || !prev_codepoint.is_uppercase() && codepoint.is_uppercase()
23052 || !prev_codepoint.is_alphanumeric() && codepoint.is_alphanumeric();
23053 if is_boundary {
23054 let chunk = &text[prev_index..index];
23055 prev_index = index;
23056 Some(chunk)
23057 } else {
23058 None
23059 }
23060 })
23061}
23062
23063pub trait RangeToAnchorExt: Sized {
23064 fn to_anchors(self, snapshot: &MultiBufferSnapshot) -> Range<Anchor>;
23065
23066 fn to_display_points(self, snapshot: &EditorSnapshot) -> Range<DisplayPoint> {
23067 let anchor_range = self.to_anchors(&snapshot.buffer_snapshot);
23068 anchor_range.start.to_display_point(snapshot)..anchor_range.end.to_display_point(snapshot)
23069 }
23070}
23071
23072impl<T: ToOffset> RangeToAnchorExt for Range<T> {
23073 fn to_anchors(self, snapshot: &MultiBufferSnapshot) -> Range<Anchor> {
23074 let start_offset = self.start.to_offset(snapshot);
23075 let end_offset = self.end.to_offset(snapshot);
23076 if start_offset == end_offset {
23077 snapshot.anchor_before(start_offset)..snapshot.anchor_before(end_offset)
23078 } else {
23079 snapshot.anchor_after(self.start)..snapshot.anchor_before(self.end)
23080 }
23081 }
23082}
23083
23084pub trait RowExt {
23085 fn as_f32(&self) -> f32;
23086
23087 fn next_row(&self) -> Self;
23088
23089 fn previous_row(&self) -> Self;
23090
23091 fn minus(&self, other: Self) -> u32;
23092}
23093
23094impl RowExt for DisplayRow {
23095 fn as_f32(&self) -> f32 {
23096 self.0 as f32
23097 }
23098
23099 fn next_row(&self) -> Self {
23100 Self(self.0 + 1)
23101 }
23102
23103 fn previous_row(&self) -> Self {
23104 Self(self.0.saturating_sub(1))
23105 }
23106
23107 fn minus(&self, other: Self) -> u32 {
23108 self.0 - other.0
23109 }
23110}
23111
23112impl RowExt for MultiBufferRow {
23113 fn as_f32(&self) -> f32 {
23114 self.0 as f32
23115 }
23116
23117 fn next_row(&self) -> Self {
23118 Self(self.0 + 1)
23119 }
23120
23121 fn previous_row(&self) -> Self {
23122 Self(self.0.saturating_sub(1))
23123 }
23124
23125 fn minus(&self, other: Self) -> u32 {
23126 self.0 - other.0
23127 }
23128}
23129
23130trait RowRangeExt {
23131 type Row;
23132
23133 fn len(&self) -> usize;
23134
23135 fn iter_rows(&self) -> impl DoubleEndedIterator<Item = Self::Row>;
23136}
23137
23138impl RowRangeExt for Range<MultiBufferRow> {
23139 type Row = MultiBufferRow;
23140
23141 fn len(&self) -> usize {
23142 (self.end.0 - self.start.0) as usize
23143 }
23144
23145 fn iter_rows(&self) -> impl DoubleEndedIterator<Item = MultiBufferRow> {
23146 (self.start.0..self.end.0).map(MultiBufferRow)
23147 }
23148}
23149
23150impl RowRangeExt for Range<DisplayRow> {
23151 type Row = DisplayRow;
23152
23153 fn len(&self) -> usize {
23154 (self.end.0 - self.start.0) as usize
23155 }
23156
23157 fn iter_rows(&self) -> impl DoubleEndedIterator<Item = DisplayRow> {
23158 (self.start.0..self.end.0).map(DisplayRow)
23159 }
23160}
23161
23162/// If select range has more than one line, we
23163/// just point the cursor to range.start.
23164fn collapse_multiline_range(range: Range<Point>) -> Range<Point> {
23165 if range.start.row == range.end.row {
23166 range
23167 } else {
23168 range.start..range.start
23169 }
23170}
23171pub struct KillRing(ClipboardItem);
23172impl Global for KillRing {}
23173
23174const UPDATE_DEBOUNCE: Duration = Duration::from_millis(50);
23175
23176enum BreakpointPromptEditAction {
23177 Log,
23178 Condition,
23179 HitCondition,
23180}
23181
23182struct BreakpointPromptEditor {
23183 pub(crate) prompt: Entity<Editor>,
23184 editor: WeakEntity<Editor>,
23185 breakpoint_anchor: Anchor,
23186 breakpoint: Breakpoint,
23187 edit_action: BreakpointPromptEditAction,
23188 block_ids: HashSet<CustomBlockId>,
23189 editor_margins: Arc<Mutex<EditorMargins>>,
23190 _subscriptions: Vec<Subscription>,
23191}
23192
23193impl BreakpointPromptEditor {
23194 const MAX_LINES: u8 = 4;
23195
23196 fn new(
23197 editor: WeakEntity<Editor>,
23198 breakpoint_anchor: Anchor,
23199 breakpoint: Breakpoint,
23200 edit_action: BreakpointPromptEditAction,
23201 window: &mut Window,
23202 cx: &mut Context<Self>,
23203 ) -> Self {
23204 let base_text = match edit_action {
23205 BreakpointPromptEditAction::Log => breakpoint.message.as_ref(),
23206 BreakpointPromptEditAction::Condition => breakpoint.condition.as_ref(),
23207 BreakpointPromptEditAction::HitCondition => breakpoint.hit_condition.as_ref(),
23208 }
23209 .map(|msg| msg.to_string())
23210 .unwrap_or_default();
23211
23212 let buffer = cx.new(|cx| Buffer::local(base_text, cx));
23213 let buffer = cx.new(|cx| MultiBuffer::singleton(buffer, cx));
23214
23215 let prompt = cx.new(|cx| {
23216 let mut prompt = Editor::new(
23217 EditorMode::AutoHeight {
23218 min_lines: 1,
23219 max_lines: Some(Self::MAX_LINES as usize),
23220 },
23221 buffer,
23222 None,
23223 window,
23224 cx,
23225 );
23226 prompt.set_soft_wrap_mode(language::language_settings::SoftWrap::EditorWidth, cx);
23227 prompt.set_show_cursor_when_unfocused(false, cx);
23228 prompt.set_placeholder_text(
23229 match edit_action {
23230 BreakpointPromptEditAction::Log => "Message to log when a breakpoint is hit. Expressions within {} are interpolated.",
23231 BreakpointPromptEditAction::Condition => "Condition when a breakpoint is hit. Expressions within {} are interpolated.",
23232 BreakpointPromptEditAction::HitCondition => "How many breakpoint hits to ignore",
23233 },
23234 cx,
23235 );
23236
23237 prompt
23238 });
23239
23240 Self {
23241 prompt,
23242 editor,
23243 breakpoint_anchor,
23244 breakpoint,
23245 edit_action,
23246 editor_margins: Arc::new(Mutex::new(EditorMargins::default())),
23247 block_ids: Default::default(),
23248 _subscriptions: vec![],
23249 }
23250 }
23251
23252 pub(crate) fn add_block_ids(&mut self, block_ids: Vec<CustomBlockId>) {
23253 self.block_ids.extend(block_ids)
23254 }
23255
23256 fn confirm(&mut self, _: &menu::Confirm, window: &mut Window, cx: &mut Context<Self>) {
23257 if let Some(editor) = self.editor.upgrade() {
23258 let message = self
23259 .prompt
23260 .read(cx)
23261 .buffer
23262 .read(cx)
23263 .as_singleton()
23264 .expect("A multi buffer in breakpoint prompt isn't possible")
23265 .read(cx)
23266 .as_rope()
23267 .to_string();
23268
23269 editor.update(cx, |editor, cx| {
23270 editor.edit_breakpoint_at_anchor(
23271 self.breakpoint_anchor,
23272 self.breakpoint.clone(),
23273 match self.edit_action {
23274 BreakpointPromptEditAction::Log => {
23275 BreakpointEditAction::EditLogMessage(message.into())
23276 }
23277 BreakpointPromptEditAction::Condition => {
23278 BreakpointEditAction::EditCondition(message.into())
23279 }
23280 BreakpointPromptEditAction::HitCondition => {
23281 BreakpointEditAction::EditHitCondition(message.into())
23282 }
23283 },
23284 cx,
23285 );
23286
23287 editor.remove_blocks(self.block_ids.clone(), None, cx);
23288 cx.focus_self(window);
23289 });
23290 }
23291 }
23292
23293 fn cancel(&mut self, _: &menu::Cancel, window: &mut Window, cx: &mut Context<Self>) {
23294 self.editor
23295 .update(cx, |editor, cx| {
23296 editor.remove_blocks(self.block_ids.clone(), None, cx);
23297 window.focus(&editor.focus_handle);
23298 })
23299 .log_err();
23300 }
23301
23302 fn render_prompt_editor(&self, cx: &mut Context<Self>) -> impl IntoElement {
23303 let settings = ThemeSettings::get_global(cx);
23304 let text_style = TextStyle {
23305 color: if self.prompt.read(cx).read_only(cx) {
23306 cx.theme().colors().text_disabled
23307 } else {
23308 cx.theme().colors().text
23309 },
23310 font_family: settings.buffer_font.family.clone(),
23311 font_fallbacks: settings.buffer_font.fallbacks.clone(),
23312 font_size: settings.buffer_font_size(cx).into(),
23313 font_weight: settings.buffer_font.weight,
23314 line_height: relative(settings.buffer_line_height.value()),
23315 ..Default::default()
23316 };
23317 EditorElement::new(
23318 &self.prompt,
23319 EditorStyle {
23320 background: cx.theme().colors().editor_background,
23321 local_player: cx.theme().players().local(),
23322 text: text_style,
23323 ..Default::default()
23324 },
23325 )
23326 }
23327}
23328
23329impl Render for BreakpointPromptEditor {
23330 fn render(&mut self, window: &mut Window, cx: &mut Context<Self>) -> impl IntoElement {
23331 let editor_margins = *self.editor_margins.lock();
23332 let gutter_dimensions = editor_margins.gutter;
23333 h_flex()
23334 .key_context("Editor")
23335 .bg(cx.theme().colors().editor_background)
23336 .border_y_1()
23337 .border_color(cx.theme().status().info_border)
23338 .size_full()
23339 .py(window.line_height() / 2.5)
23340 .on_action(cx.listener(Self::confirm))
23341 .on_action(cx.listener(Self::cancel))
23342 .child(h_flex().w(gutter_dimensions.full_width() + (gutter_dimensions.margin / 2.0)))
23343 .child(div().flex_1().child(self.render_prompt_editor(cx)))
23344 }
23345}
23346
23347impl Focusable for BreakpointPromptEditor {
23348 fn focus_handle(&self, cx: &App) -> FocusHandle {
23349 self.prompt.focus_handle(cx)
23350 }
23351}
23352
23353fn all_edits_insertions_or_deletions(
23354 edits: &Vec<(Range<Anchor>, String)>,
23355 snapshot: &MultiBufferSnapshot,
23356) -> bool {
23357 let mut all_insertions = true;
23358 let mut all_deletions = true;
23359
23360 for (range, new_text) in edits.iter() {
23361 let range_is_empty = range.to_offset(&snapshot).is_empty();
23362 let text_is_empty = new_text.is_empty();
23363
23364 if range_is_empty != text_is_empty {
23365 if range_is_empty {
23366 all_deletions = false;
23367 } else {
23368 all_insertions = false;
23369 }
23370 } else {
23371 return false;
23372 }
23373
23374 if !all_insertions && !all_deletions {
23375 return false;
23376 }
23377 }
23378 all_insertions || all_deletions
23379}
23380
23381struct MissingEditPredictionKeybindingTooltip;
23382
23383impl Render for MissingEditPredictionKeybindingTooltip {
23384 fn render(&mut self, window: &mut Window, cx: &mut Context<Self>) -> impl IntoElement {
23385 ui::tooltip_container(window, cx, |container, _, cx| {
23386 container
23387 .flex_shrink_0()
23388 .max_w_80()
23389 .min_h(rems_from_px(124.))
23390 .justify_between()
23391 .child(
23392 v_flex()
23393 .flex_1()
23394 .text_ui_sm(cx)
23395 .child(Label::new("Conflict with Accept Keybinding"))
23396 .child("Your keymap currently overrides the default accept keybinding. To continue, assign one keybinding for the `editor::AcceptEditPrediction` action.")
23397 )
23398 .child(
23399 h_flex()
23400 .pb_1()
23401 .gap_1()
23402 .items_end()
23403 .w_full()
23404 .child(Button::new("open-keymap", "Assign Keybinding").size(ButtonSize::Compact).on_click(|_ev, window, cx| {
23405 window.dispatch_action(zed_actions::OpenKeymap.boxed_clone(), cx)
23406 }))
23407 .child(Button::new("see-docs", "See Docs").size(ButtonSize::Compact).on_click(|_ev, _window, cx| {
23408 cx.open_url("https://zed.dev/docs/completions#edit-predictions-missing-keybinding");
23409 })),
23410 )
23411 })
23412 }
23413}
23414
23415#[derive(Debug, Clone, Copy, PartialEq)]
23416pub struct LineHighlight {
23417 pub background: Background,
23418 pub border: Option<gpui::Hsla>,
23419 pub include_gutter: bool,
23420 pub type_id: Option<TypeId>,
23421}
23422
23423struct LineManipulationResult {
23424 pub new_text: String,
23425 pub line_count_before: usize,
23426 pub line_count_after: usize,
23427}
23428
23429fn render_diff_hunk_controls(
23430 row: u32,
23431 status: &DiffHunkStatus,
23432 hunk_range: Range<Anchor>,
23433 is_created_file: bool,
23434 line_height: Pixels,
23435 editor: &Entity<Editor>,
23436 _window: &mut Window,
23437 cx: &mut App,
23438) -> AnyElement {
23439 h_flex()
23440 .h(line_height)
23441 .mr_1()
23442 .gap_1()
23443 .px_0p5()
23444 .pb_1()
23445 .border_x_1()
23446 .border_b_1()
23447 .border_color(cx.theme().colors().border_variant)
23448 .rounded_b_lg()
23449 .bg(cx.theme().colors().editor_background)
23450 .gap_1()
23451 .block_mouse_except_scroll()
23452 .shadow_md()
23453 .child(if status.has_secondary_hunk() {
23454 Button::new(("stage", row as u64), "Stage")
23455 .alpha(if status.is_pending() { 0.66 } else { 1.0 })
23456 .tooltip({
23457 let focus_handle = editor.focus_handle(cx);
23458 move |window, cx| {
23459 Tooltip::for_action_in(
23460 "Stage Hunk",
23461 &::git::ToggleStaged,
23462 &focus_handle,
23463 window,
23464 cx,
23465 )
23466 }
23467 })
23468 .on_click({
23469 let editor = editor.clone();
23470 move |_event, _window, cx| {
23471 editor.update(cx, |editor, cx| {
23472 editor.stage_or_unstage_diff_hunks(
23473 true,
23474 vec![hunk_range.start..hunk_range.start],
23475 cx,
23476 );
23477 });
23478 }
23479 })
23480 } else {
23481 Button::new(("unstage", row as u64), "Unstage")
23482 .alpha(if status.is_pending() { 0.66 } else { 1.0 })
23483 .tooltip({
23484 let focus_handle = editor.focus_handle(cx);
23485 move |window, cx| {
23486 Tooltip::for_action_in(
23487 "Unstage Hunk",
23488 &::git::ToggleStaged,
23489 &focus_handle,
23490 window,
23491 cx,
23492 )
23493 }
23494 })
23495 .on_click({
23496 let editor = editor.clone();
23497 move |_event, _window, cx| {
23498 editor.update(cx, |editor, cx| {
23499 editor.stage_or_unstage_diff_hunks(
23500 false,
23501 vec![hunk_range.start..hunk_range.start],
23502 cx,
23503 );
23504 });
23505 }
23506 })
23507 })
23508 .child(
23509 Button::new(("restore", row as u64), "Restore")
23510 .tooltip({
23511 let focus_handle = editor.focus_handle(cx);
23512 move |window, cx| {
23513 Tooltip::for_action_in(
23514 "Restore Hunk",
23515 &::git::Restore,
23516 &focus_handle,
23517 window,
23518 cx,
23519 )
23520 }
23521 })
23522 .on_click({
23523 let editor = editor.clone();
23524 move |_event, window, cx| {
23525 editor.update(cx, |editor, cx| {
23526 let snapshot = editor.snapshot(window, cx);
23527 let point = hunk_range.start.to_point(&snapshot.buffer_snapshot);
23528 editor.restore_hunks_in_ranges(vec![point..point], window, cx);
23529 });
23530 }
23531 })
23532 .disabled(is_created_file),
23533 )
23534 .when(
23535 !editor.read(cx).buffer().read(cx).all_diff_hunks_expanded(),
23536 |el| {
23537 el.child(
23538 IconButton::new(("next-hunk", row as u64), IconName::ArrowDown)
23539 .shape(IconButtonShape::Square)
23540 .icon_size(IconSize::Small)
23541 // .disabled(!has_multiple_hunks)
23542 .tooltip({
23543 let focus_handle = editor.focus_handle(cx);
23544 move |window, cx| {
23545 Tooltip::for_action_in(
23546 "Next Hunk",
23547 &GoToHunk,
23548 &focus_handle,
23549 window,
23550 cx,
23551 )
23552 }
23553 })
23554 .on_click({
23555 let editor = editor.clone();
23556 move |_event, window, cx| {
23557 editor.update(cx, |editor, cx| {
23558 let snapshot = editor.snapshot(window, cx);
23559 let position =
23560 hunk_range.end.to_point(&snapshot.buffer_snapshot);
23561 editor.go_to_hunk_before_or_after_position(
23562 &snapshot,
23563 position,
23564 Direction::Next,
23565 window,
23566 cx,
23567 );
23568 editor.expand_selected_diff_hunks(cx);
23569 });
23570 }
23571 }),
23572 )
23573 .child(
23574 IconButton::new(("prev-hunk", row as u64), IconName::ArrowUp)
23575 .shape(IconButtonShape::Square)
23576 .icon_size(IconSize::Small)
23577 // .disabled(!has_multiple_hunks)
23578 .tooltip({
23579 let focus_handle = editor.focus_handle(cx);
23580 move |window, cx| {
23581 Tooltip::for_action_in(
23582 "Previous Hunk",
23583 &GoToPreviousHunk,
23584 &focus_handle,
23585 window,
23586 cx,
23587 )
23588 }
23589 })
23590 .on_click({
23591 let editor = editor.clone();
23592 move |_event, window, cx| {
23593 editor.update(cx, |editor, cx| {
23594 let snapshot = editor.snapshot(window, cx);
23595 let point =
23596 hunk_range.start.to_point(&snapshot.buffer_snapshot);
23597 editor.go_to_hunk_before_or_after_position(
23598 &snapshot,
23599 point,
23600 Direction::Prev,
23601 window,
23602 cx,
23603 );
23604 editor.expand_selected_diff_hunks(cx);
23605 });
23606 }
23607 }),
23608 )
23609 },
23610 )
23611 .into_any_element()
23612}