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, GoToDiagnosticSeverityFilter},
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 workspace.register_action(Editor::toggle_focus);
360 },
361 )
362 .detach();
363
364 cx.on_action(move |_: &workspace::NewFile, cx| {
365 let app_state = workspace::AppState::global(cx);
366 if let Some(app_state) = app_state.upgrade() {
367 workspace::open_new(
368 Default::default(),
369 app_state,
370 cx,
371 |workspace, window, cx| {
372 Editor::new_file(workspace, &Default::default(), window, cx)
373 },
374 )
375 .detach();
376 }
377 });
378 cx.on_action(move |_: &workspace::NewWindow, cx| {
379 let app_state = workspace::AppState::global(cx);
380 if let Some(app_state) = app_state.upgrade() {
381 workspace::open_new(
382 Default::default(),
383 app_state,
384 cx,
385 |workspace, window, cx| {
386 cx.activate(true);
387 Editor::new_file(workspace, &Default::default(), window, cx)
388 },
389 )
390 .detach();
391 }
392 });
393}
394
395pub fn set_blame_renderer(renderer: impl BlameRenderer + 'static, cx: &mut App) {
396 cx.set_global(GlobalBlameRenderer(Arc::new(renderer)));
397}
398
399pub trait DiagnosticRenderer {
400 fn render_group(
401 &self,
402 diagnostic_group: Vec<DiagnosticEntry<Point>>,
403 buffer_id: BufferId,
404 snapshot: EditorSnapshot,
405 editor: WeakEntity<Editor>,
406 cx: &mut App,
407 ) -> Vec<BlockProperties<Anchor>>;
408
409 fn render_hover(
410 &self,
411 diagnostic_group: Vec<DiagnosticEntry<Point>>,
412 range: Range<Point>,
413 buffer_id: BufferId,
414 cx: &mut App,
415 ) -> Option<Entity<markdown::Markdown>>;
416
417 fn open_link(
418 &self,
419 editor: &mut Editor,
420 link: SharedString,
421 window: &mut Window,
422 cx: &mut Context<Editor>,
423 );
424}
425
426pub(crate) struct GlobalDiagnosticRenderer(pub Arc<dyn DiagnosticRenderer>);
427
428impl GlobalDiagnosticRenderer {
429 fn global(cx: &App) -> Option<Arc<dyn DiagnosticRenderer>> {
430 cx.try_global::<Self>().map(|g| g.0.clone())
431 }
432}
433
434impl gpui::Global for GlobalDiagnosticRenderer {}
435pub fn set_diagnostic_renderer(renderer: impl DiagnosticRenderer + 'static, cx: &mut App) {
436 cx.set_global(GlobalDiagnosticRenderer(Arc::new(renderer)));
437}
438
439pub struct SearchWithinRange;
440
441trait InvalidationRegion {
442 fn ranges(&self) -> &[Range<Anchor>];
443}
444
445#[derive(Clone, Debug, PartialEq)]
446pub enum SelectPhase {
447 Begin {
448 position: DisplayPoint,
449 add: bool,
450 click_count: usize,
451 },
452 BeginColumnar {
453 position: DisplayPoint,
454 reset: bool,
455 mode: ColumnarMode,
456 goal_column: u32,
457 },
458 Extend {
459 position: DisplayPoint,
460 click_count: usize,
461 },
462 Update {
463 position: DisplayPoint,
464 goal_column: u32,
465 scroll_delta: gpui::Point<f32>,
466 },
467 End,
468}
469
470#[derive(Clone, Debug, PartialEq)]
471pub enum ColumnarMode {
472 FromMouse,
473 FromSelection,
474}
475
476#[derive(Clone, Debug)]
477pub enum SelectMode {
478 Character,
479 Word(Range<Anchor>),
480 Line(Range<Anchor>),
481 All,
482}
483
484#[derive(Clone, PartialEq, Eq, Debug)]
485pub enum EditorMode {
486 SingleLine,
487 AutoHeight {
488 min_lines: usize,
489 max_lines: Option<usize>,
490 },
491 Full {
492 /// When set to `true`, the editor will scale its UI elements with the buffer font size.
493 scale_ui_elements_with_buffer_font_size: bool,
494 /// When set to `true`, the editor will render a background for the active line.
495 show_active_line_background: bool,
496 /// When set to `true`, the editor's height will be determined by its content.
497 sized_by_content: bool,
498 },
499 Minimap {
500 parent: WeakEntity<Editor>,
501 },
502}
503
504impl EditorMode {
505 pub fn full() -> Self {
506 Self::Full {
507 scale_ui_elements_with_buffer_font_size: true,
508 show_active_line_background: true,
509 sized_by_content: false,
510 }
511 }
512
513 #[inline]
514 pub fn is_full(&self) -> bool {
515 matches!(self, Self::Full { .. })
516 }
517
518 #[inline]
519 pub fn is_single_line(&self) -> bool {
520 matches!(self, Self::SingleLine { .. })
521 }
522
523 #[inline]
524 fn is_minimap(&self) -> bool {
525 matches!(self, Self::Minimap { .. })
526 }
527}
528
529#[derive(Copy, Clone, Debug)]
530pub enum SoftWrap {
531 /// Prefer not to wrap at all.
532 ///
533 /// Note: this is currently internal, as actually limited by [`crate::MAX_LINE_LEN`] until it wraps.
534 /// The mode is used inside git diff hunks, where it's seems currently more useful to not wrap as much as possible.
535 GitDiff,
536 /// Prefer a single line generally, unless an overly long line is encountered.
537 None,
538 /// Soft wrap lines that exceed the editor width.
539 EditorWidth,
540 /// Soft wrap lines at the preferred line length.
541 Column(u32),
542 /// Soft wrap line at the preferred line length or the editor width (whichever is smaller).
543 Bounded(u32),
544}
545
546#[derive(Clone)]
547pub struct EditorStyle {
548 pub background: Hsla,
549 pub border: Hsla,
550 pub local_player: PlayerColor,
551 pub text: TextStyle,
552 pub scrollbar_width: Pixels,
553 pub syntax: Arc<SyntaxTheme>,
554 pub status: StatusColors,
555 pub inlay_hints_style: HighlightStyle,
556 pub inline_completion_styles: InlineCompletionStyles,
557 pub unnecessary_code_fade: f32,
558 pub show_underlines: bool,
559}
560
561impl Default for EditorStyle {
562 fn default() -> Self {
563 Self {
564 background: Hsla::default(),
565 border: Hsla::default(),
566 local_player: PlayerColor::default(),
567 text: TextStyle::default(),
568 scrollbar_width: Pixels::default(),
569 syntax: Default::default(),
570 // HACK: Status colors don't have a real default.
571 // We should look into removing the status colors from the editor
572 // style and retrieve them directly from the theme.
573 status: StatusColors::dark(),
574 inlay_hints_style: HighlightStyle::default(),
575 inline_completion_styles: InlineCompletionStyles {
576 insertion: HighlightStyle::default(),
577 whitespace: HighlightStyle::default(),
578 },
579 unnecessary_code_fade: Default::default(),
580 show_underlines: true,
581 }
582 }
583}
584
585pub fn make_inlay_hints_style(cx: &mut App) -> HighlightStyle {
586 let show_background = language_settings::language_settings(None, None, cx)
587 .inlay_hints
588 .show_background;
589
590 HighlightStyle {
591 color: Some(cx.theme().status().hint),
592 background_color: show_background.then(|| cx.theme().status().hint_background),
593 ..HighlightStyle::default()
594 }
595}
596
597pub fn make_suggestion_styles(cx: &mut App) -> InlineCompletionStyles {
598 InlineCompletionStyles {
599 insertion: HighlightStyle {
600 color: Some(cx.theme().status().predictive),
601 ..HighlightStyle::default()
602 },
603 whitespace: HighlightStyle {
604 background_color: Some(cx.theme().status().created_background),
605 ..HighlightStyle::default()
606 },
607 }
608}
609
610type CompletionId = usize;
611
612pub(crate) enum EditDisplayMode {
613 TabAccept,
614 DiffPopover,
615 Inline,
616}
617
618enum InlineCompletion {
619 Edit {
620 edits: Vec<(Range<Anchor>, String)>,
621 edit_preview: Option<EditPreview>,
622 display_mode: EditDisplayMode,
623 snapshot: BufferSnapshot,
624 },
625 Move {
626 target: Anchor,
627 snapshot: BufferSnapshot,
628 },
629}
630
631struct InlineCompletionState {
632 inlay_ids: Vec<InlayId>,
633 completion: InlineCompletion,
634 completion_id: Option<SharedString>,
635 invalidation_range: Range<Anchor>,
636}
637
638enum EditPredictionSettings {
639 Disabled,
640 Enabled {
641 show_in_menu: bool,
642 preview_requires_modifier: bool,
643 },
644}
645
646enum InlineCompletionHighlight {}
647
648#[derive(Debug, Clone)]
649struct InlineDiagnostic {
650 message: SharedString,
651 group_id: usize,
652 is_primary: bool,
653 start: Point,
654 severity: lsp::DiagnosticSeverity,
655}
656
657pub enum MenuInlineCompletionsPolicy {
658 Never,
659 ByProvider,
660}
661
662pub enum EditPredictionPreview {
663 /// Modifier is not pressed
664 Inactive { released_too_fast: bool },
665 /// Modifier pressed
666 Active {
667 since: Instant,
668 previous_scroll_position: Option<ScrollAnchor>,
669 },
670}
671
672impl EditPredictionPreview {
673 pub fn released_too_fast(&self) -> bool {
674 match self {
675 EditPredictionPreview::Inactive { released_too_fast } => *released_too_fast,
676 EditPredictionPreview::Active { .. } => false,
677 }
678 }
679
680 pub fn set_previous_scroll_position(&mut self, scroll_position: Option<ScrollAnchor>) {
681 if let EditPredictionPreview::Active {
682 previous_scroll_position,
683 ..
684 } = self
685 {
686 *previous_scroll_position = scroll_position;
687 }
688 }
689}
690
691pub struct ContextMenuOptions {
692 pub min_entries_visible: usize,
693 pub max_entries_visible: usize,
694 pub placement: Option<ContextMenuPlacement>,
695}
696
697#[derive(Debug, Clone, PartialEq, Eq)]
698pub enum ContextMenuPlacement {
699 Above,
700 Below,
701}
702
703#[derive(Copy, Clone, Eq, PartialEq, PartialOrd, Ord, Debug, Default)]
704struct EditorActionId(usize);
705
706impl EditorActionId {
707 pub fn post_inc(&mut self) -> Self {
708 let answer = self.0;
709
710 *self = Self(answer + 1);
711
712 Self(answer)
713 }
714}
715
716// type GetFieldEditorTheme = dyn Fn(&theme::Theme) -> theme::FieldEditor;
717// type OverrideTextStyle = dyn Fn(&EditorStyle) -> Option<HighlightStyle>;
718
719type BackgroundHighlight = (fn(&Theme) -> Hsla, Arc<[Range<Anchor>]>);
720type GutterHighlight = (fn(&App) -> Hsla, Vec<Range<Anchor>>);
721
722#[derive(Default)]
723struct ScrollbarMarkerState {
724 scrollbar_size: Size<Pixels>,
725 dirty: bool,
726 markers: Arc<[PaintQuad]>,
727 pending_refresh: Option<Task<Result<()>>>,
728}
729
730impl ScrollbarMarkerState {
731 fn should_refresh(&self, scrollbar_size: Size<Pixels>) -> bool {
732 self.pending_refresh.is_none() && (self.scrollbar_size != scrollbar_size || self.dirty)
733 }
734}
735
736#[derive(Clone, Copy, PartialEq, Eq)]
737pub enum MinimapVisibility {
738 Disabled,
739 Enabled {
740 /// The configuration currently present in the users settings.
741 setting_configuration: bool,
742 /// Whether to override the currently set visibility from the users setting.
743 toggle_override: bool,
744 },
745}
746
747impl MinimapVisibility {
748 fn for_mode(mode: &EditorMode, cx: &App) -> Self {
749 if mode.is_full() {
750 Self::Enabled {
751 setting_configuration: EditorSettings::get_global(cx).minimap.minimap_enabled(),
752 toggle_override: false,
753 }
754 } else {
755 Self::Disabled
756 }
757 }
758
759 fn hidden(&self) -> Self {
760 match *self {
761 Self::Enabled {
762 setting_configuration,
763 ..
764 } => Self::Enabled {
765 setting_configuration,
766 toggle_override: setting_configuration,
767 },
768 Self::Disabled => Self::Disabled,
769 }
770 }
771
772 fn disabled(&self) -> bool {
773 match *self {
774 Self::Disabled => true,
775 _ => false,
776 }
777 }
778
779 fn settings_visibility(&self) -> bool {
780 match *self {
781 Self::Enabled {
782 setting_configuration,
783 ..
784 } => setting_configuration,
785 _ => false,
786 }
787 }
788
789 fn visible(&self) -> bool {
790 match *self {
791 Self::Enabled {
792 setting_configuration,
793 toggle_override,
794 } => setting_configuration ^ toggle_override,
795 _ => false,
796 }
797 }
798
799 fn toggle_visibility(&self) -> Self {
800 match *self {
801 Self::Enabled {
802 toggle_override,
803 setting_configuration,
804 } => Self::Enabled {
805 setting_configuration,
806 toggle_override: !toggle_override,
807 },
808 Self::Disabled => Self::Disabled,
809 }
810 }
811}
812
813#[derive(Clone, Debug)]
814struct RunnableTasks {
815 templates: Vec<(TaskSourceKind, TaskTemplate)>,
816 offset: multi_buffer::Anchor,
817 // We need the column at which the task context evaluation should take place (when we're spawning it via gutter).
818 column: u32,
819 // Values of all named captures, including those starting with '_'
820 extra_variables: HashMap<String, String>,
821 // Full range of the tagged region. We use it to determine which `extra_variables` to grab for context resolution in e.g. a modal.
822 context_range: Range<BufferOffset>,
823}
824
825impl RunnableTasks {
826 fn resolve<'a>(
827 &'a self,
828 cx: &'a task::TaskContext,
829 ) -> impl Iterator<Item = (TaskSourceKind, ResolvedTask)> + 'a {
830 self.templates.iter().filter_map(|(kind, template)| {
831 template
832 .resolve_task(&kind.to_id_base(), cx)
833 .map(|task| (kind.clone(), task))
834 })
835 }
836}
837
838#[derive(Clone)]
839pub struct ResolvedTasks {
840 templates: SmallVec<[(TaskSourceKind, ResolvedTask); 1]>,
841 position: Anchor,
842}
843
844#[derive(Copy, Clone, Debug, PartialEq, PartialOrd)]
845struct BufferOffset(usize);
846
847// Addons allow storing per-editor state in other crates (e.g. Vim)
848pub trait Addon: 'static {
849 fn extend_key_context(&self, _: &mut KeyContext, _: &App) {}
850
851 fn render_buffer_header_controls(
852 &self,
853 _: &ExcerptInfo,
854 _: &Window,
855 _: &App,
856 ) -> Option<AnyElement> {
857 None
858 }
859
860 fn to_any(&self) -> &dyn std::any::Any;
861
862 fn to_any_mut(&mut self) -> Option<&mut dyn std::any::Any> {
863 None
864 }
865}
866
867struct ChangeLocation {
868 current: Option<Vec<Anchor>>,
869 original: Vec<Anchor>,
870}
871impl ChangeLocation {
872 fn locations(&self) -> &[Anchor] {
873 self.current.as_ref().unwrap_or(&self.original)
874 }
875}
876
877/// A set of caret positions, registered when the editor was edited.
878pub struct ChangeList {
879 changes: Vec<ChangeLocation>,
880 /// Currently "selected" change.
881 position: Option<usize>,
882}
883
884impl ChangeList {
885 pub fn new() -> Self {
886 Self {
887 changes: Vec::new(),
888 position: None,
889 }
890 }
891
892 /// Moves to the next change in the list (based on the direction given) and returns the caret positions for the next change.
893 /// If reaches the end of the list in the direction, returns the corresponding change until called for a different direction.
894 pub fn next_change(&mut self, count: usize, direction: Direction) -> Option<&[Anchor]> {
895 if self.changes.is_empty() {
896 return None;
897 }
898
899 let prev = self.position.unwrap_or(self.changes.len());
900 let next = if direction == Direction::Prev {
901 prev.saturating_sub(count)
902 } else {
903 (prev + count).min(self.changes.len() - 1)
904 };
905 self.position = Some(next);
906 self.changes.get(next).map(|change| change.locations())
907 }
908
909 /// Adds a new change to the list, resetting the change list position.
910 pub fn push_to_change_list(&mut self, group: bool, new_positions: Vec<Anchor>) {
911 self.position.take();
912 if let Some(last) = self.changes.last_mut()
913 && group
914 {
915 last.current = Some(new_positions)
916 } else {
917 self.changes.push(ChangeLocation {
918 original: new_positions,
919 current: None,
920 });
921 }
922 }
923
924 pub fn last(&self) -> Option<&[Anchor]> {
925 self.changes.last().map(|change| change.locations())
926 }
927
928 pub fn last_before_grouping(&self) -> Option<&[Anchor]> {
929 self.changes.last().map(|change| change.original.as_slice())
930 }
931
932 pub fn invert_last_group(&mut self) {
933 if let Some(last) = self.changes.last_mut() {
934 if let Some(current) = last.current.as_mut() {
935 mem::swap(&mut last.original, current);
936 }
937 }
938 }
939}
940
941#[derive(Clone)]
942struct InlineBlamePopoverState {
943 scroll_handle: ScrollHandle,
944 commit_message: Option<ParsedCommitMessage>,
945 markdown: Entity<Markdown>,
946}
947
948struct InlineBlamePopover {
949 position: gpui::Point<Pixels>,
950 hide_task: Option<Task<()>>,
951 popover_bounds: Option<Bounds<Pixels>>,
952 popover_state: InlineBlamePopoverState,
953 keyboard_grace: bool,
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(EditorMode::SingleLine, buffer, None, window, cx)
1666 }
1667
1668 pub fn multi_line(window: &mut Window, cx: &mut Context<Self>) -> Self {
1669 let buffer = cx.new(|cx| Buffer::local("", cx));
1670 let buffer = cx.new(|cx| MultiBuffer::singleton(buffer, cx));
1671 Self::new(EditorMode::full(), buffer, None, window, cx)
1672 }
1673
1674 pub fn auto_height(
1675 min_lines: usize,
1676 max_lines: usize,
1677 window: &mut Window,
1678 cx: &mut Context<Self>,
1679 ) -> Self {
1680 let buffer = cx.new(|cx| Buffer::local("", cx));
1681 let buffer = cx.new(|cx| MultiBuffer::singleton(buffer, cx));
1682 Self::new(
1683 EditorMode::AutoHeight {
1684 min_lines,
1685 max_lines: Some(max_lines),
1686 },
1687 buffer,
1688 None,
1689 window,
1690 cx,
1691 )
1692 }
1693
1694 /// Creates a new auto-height editor with a minimum number of lines but no maximum.
1695 /// The editor grows as tall as needed to fit its content.
1696 pub fn auto_height_unbounded(
1697 min_lines: usize,
1698 window: &mut Window,
1699 cx: &mut Context<Self>,
1700 ) -> Self {
1701 let buffer = cx.new(|cx| Buffer::local("", cx));
1702 let buffer = cx.new(|cx| MultiBuffer::singleton(buffer, cx));
1703 Self::new(
1704 EditorMode::AutoHeight {
1705 min_lines,
1706 max_lines: None,
1707 },
1708 buffer,
1709 None,
1710 window,
1711 cx,
1712 )
1713 }
1714
1715 pub fn for_buffer(
1716 buffer: Entity<Buffer>,
1717 project: Option<Entity<Project>>,
1718 window: &mut Window,
1719 cx: &mut Context<Self>,
1720 ) -> Self {
1721 let buffer = cx.new(|cx| MultiBuffer::singleton(buffer, cx));
1722 Self::new(EditorMode::full(), buffer, project, window, cx)
1723 }
1724
1725 pub fn for_multibuffer(
1726 buffer: Entity<MultiBuffer>,
1727 project: Option<Entity<Project>>,
1728 window: &mut Window,
1729 cx: &mut Context<Self>,
1730 ) -> Self {
1731 Self::new(EditorMode::full(), buffer, project, window, cx)
1732 }
1733
1734 pub fn clone(&self, window: &mut Window, cx: &mut Context<Self>) -> Self {
1735 let mut clone = Self::new(
1736 self.mode.clone(),
1737 self.buffer.clone(),
1738 self.project.clone(),
1739 window,
1740 cx,
1741 );
1742 self.display_map.update(cx, |display_map, cx| {
1743 let snapshot = display_map.snapshot(cx);
1744 clone.display_map.update(cx, |display_map, cx| {
1745 display_map.set_state(&snapshot, cx);
1746 });
1747 });
1748 clone.folds_did_change(cx);
1749 clone.selections.clone_state(&self.selections);
1750 clone.scroll_manager.clone_state(&self.scroll_manager);
1751 clone.searchable = self.searchable;
1752 clone.read_only = self.read_only;
1753 clone
1754 }
1755
1756 pub fn new(
1757 mode: EditorMode,
1758 buffer: Entity<MultiBuffer>,
1759 project: Option<Entity<Project>>,
1760 window: &mut Window,
1761 cx: &mut Context<Self>,
1762 ) -> Self {
1763 Editor::new_internal(mode, buffer, project, None, window, cx)
1764 }
1765
1766 fn new_internal(
1767 mode: EditorMode,
1768 buffer: Entity<MultiBuffer>,
1769 project: Option<Entity<Project>>,
1770 display_map: Option<Entity<DisplayMap>>,
1771 window: &mut Window,
1772 cx: &mut Context<Self>,
1773 ) -> Self {
1774 debug_assert!(
1775 display_map.is_none() || mode.is_minimap(),
1776 "Providing a display map for a new editor is only intended for the minimap and might have unindended side effects otherwise!"
1777 );
1778
1779 let full_mode = mode.is_full();
1780 let is_minimap = mode.is_minimap();
1781 let diagnostics_max_severity = if full_mode {
1782 EditorSettings::get_global(cx)
1783 .diagnostics_max_severity
1784 .unwrap_or(DiagnosticSeverity::Hint)
1785 } else {
1786 DiagnosticSeverity::Off
1787 };
1788 let style = window.text_style();
1789 let font_size = style.font_size.to_pixels(window.rem_size());
1790 let editor = cx.entity().downgrade();
1791 let fold_placeholder = FoldPlaceholder {
1792 constrain_width: true,
1793 render: Arc::new(move |fold_id, fold_range, cx| {
1794 let editor = editor.clone();
1795 div()
1796 .id(fold_id)
1797 .bg(cx.theme().colors().ghost_element_background)
1798 .hover(|style| style.bg(cx.theme().colors().ghost_element_hover))
1799 .active(|style| style.bg(cx.theme().colors().ghost_element_active))
1800 .rounded_xs()
1801 .size_full()
1802 .cursor_pointer()
1803 .child("⋯")
1804 .on_mouse_down(MouseButton::Left, |_, _, cx| cx.stop_propagation())
1805 .on_click(move |_, _window, cx| {
1806 editor
1807 .update(cx, |editor, cx| {
1808 editor.unfold_ranges(
1809 &[fold_range.start..fold_range.end],
1810 true,
1811 false,
1812 cx,
1813 );
1814 cx.stop_propagation();
1815 })
1816 .ok();
1817 })
1818 .into_any()
1819 }),
1820 merge_adjacent: true,
1821 ..FoldPlaceholder::default()
1822 };
1823 let display_map = display_map.unwrap_or_else(|| {
1824 cx.new(|cx| {
1825 DisplayMap::new(
1826 buffer.clone(),
1827 style.font(),
1828 font_size,
1829 None,
1830 FILE_HEADER_HEIGHT,
1831 MULTI_BUFFER_EXCERPT_HEADER_HEIGHT,
1832 fold_placeholder,
1833 diagnostics_max_severity,
1834 cx,
1835 )
1836 })
1837 });
1838
1839 let selections = SelectionsCollection::new(display_map.clone(), buffer.clone());
1840
1841 let blink_manager = cx.new(|cx| {
1842 let mut blink_manager = BlinkManager::new(CURSOR_BLINK_INTERVAL, cx);
1843 if is_minimap {
1844 blink_manager.disable(cx);
1845 }
1846 blink_manager
1847 });
1848
1849 let soft_wrap_mode_override = matches!(mode, EditorMode::SingleLine { .. })
1850 .then(|| language_settings::SoftWrap::None);
1851
1852 let mut project_subscriptions = Vec::new();
1853 if full_mode {
1854 if let Some(project) = project.as_ref() {
1855 project_subscriptions.push(cx.subscribe_in(
1856 project,
1857 window,
1858 |editor, _, event, window, cx| match event {
1859 project::Event::RefreshCodeLens => {
1860 // we always query lens with actions, without storing them, always refreshing them
1861 }
1862 project::Event::RefreshInlayHints => {
1863 editor
1864 .refresh_inlay_hints(InlayHintRefreshReason::RefreshRequested, cx);
1865 }
1866 project::Event::LanguageServerAdded(..)
1867 | project::Event::LanguageServerRemoved(..) => {
1868 if editor.tasks_update_task.is_none() {
1869 editor.tasks_update_task =
1870 Some(editor.refresh_runnables(window, cx));
1871 }
1872 editor.update_lsp_data(true, None, window, cx);
1873 }
1874 project::Event::SnippetEdit(id, snippet_edits) => {
1875 if let Some(buffer) = editor.buffer.read(cx).buffer(*id) {
1876 let focus_handle = editor.focus_handle(cx);
1877 if focus_handle.is_focused(window) {
1878 let snapshot = buffer.read(cx).snapshot();
1879 for (range, snippet) in snippet_edits {
1880 let editor_range =
1881 language::range_from_lsp(*range).to_offset(&snapshot);
1882 editor
1883 .insert_snippet(
1884 &[editor_range],
1885 snippet.clone(),
1886 window,
1887 cx,
1888 )
1889 .ok();
1890 }
1891 }
1892 }
1893 }
1894 _ => {}
1895 },
1896 ));
1897 if let Some(task_inventory) = project
1898 .read(cx)
1899 .task_store()
1900 .read(cx)
1901 .task_inventory()
1902 .cloned()
1903 {
1904 project_subscriptions.push(cx.observe_in(
1905 &task_inventory,
1906 window,
1907 |editor, _, window, cx| {
1908 editor.tasks_update_task = Some(editor.refresh_runnables(window, cx));
1909 },
1910 ));
1911 };
1912
1913 project_subscriptions.push(cx.subscribe_in(
1914 &project.read(cx).breakpoint_store(),
1915 window,
1916 |editor, _, event, window, cx| match event {
1917 BreakpointStoreEvent::ClearDebugLines => {
1918 editor.clear_row_highlights::<ActiveDebugLine>();
1919 editor.refresh_inline_values(cx);
1920 }
1921 BreakpointStoreEvent::SetDebugLine => {
1922 if editor.go_to_active_debug_line(window, cx) {
1923 cx.stop_propagation();
1924 }
1925
1926 editor.refresh_inline_values(cx);
1927 }
1928 _ => {}
1929 },
1930 ));
1931 let git_store = project.read(cx).git_store().clone();
1932 let project = project.clone();
1933 project_subscriptions.push(cx.subscribe(&git_store, move |this, _, event, cx| {
1934 match event {
1935 GitStoreEvent::RepositoryUpdated(
1936 _,
1937 RepositoryEvent::Updated {
1938 new_instance: true, ..
1939 },
1940 _,
1941 ) => {
1942 this.load_diff_task = Some(
1943 update_uncommitted_diff_for_buffer(
1944 cx.entity(),
1945 &project,
1946 this.buffer.read(cx).all_buffers(),
1947 this.buffer.clone(),
1948 cx,
1949 )
1950 .shared(),
1951 );
1952 }
1953 _ => {}
1954 }
1955 }));
1956 }
1957 }
1958
1959 let buffer_snapshot = buffer.read(cx).snapshot(cx);
1960
1961 let inlay_hint_settings =
1962 inlay_hint_settings(selections.newest_anchor().head(), &buffer_snapshot, cx);
1963 let focus_handle = cx.focus_handle();
1964 if !is_minimap {
1965 cx.on_focus(&focus_handle, window, Self::handle_focus)
1966 .detach();
1967 cx.on_focus_in(&focus_handle, window, Self::handle_focus_in)
1968 .detach();
1969 cx.on_focus_out(&focus_handle, window, Self::handle_focus_out)
1970 .detach();
1971 cx.on_blur(&focus_handle, window, Self::handle_blur)
1972 .detach();
1973 cx.observe_pending_input(window, Self::observe_pending_input)
1974 .detach();
1975 }
1976
1977 let show_indent_guides = if matches!(
1978 mode,
1979 EditorMode::SingleLine { .. } | EditorMode::Minimap { .. }
1980 ) {
1981 Some(false)
1982 } else {
1983 None
1984 };
1985
1986 let breakpoint_store = match (&mode, project.as_ref()) {
1987 (EditorMode::Full { .. }, Some(project)) => Some(project.read(cx).breakpoint_store()),
1988 _ => None,
1989 };
1990
1991 let mut code_action_providers = Vec::new();
1992 let mut load_uncommitted_diff = None;
1993 if let Some(project) = project.clone() {
1994 load_uncommitted_diff = Some(
1995 update_uncommitted_diff_for_buffer(
1996 cx.entity(),
1997 &project,
1998 buffer.read(cx).all_buffers(),
1999 buffer.clone(),
2000 cx,
2001 )
2002 .shared(),
2003 );
2004 code_action_providers.push(Rc::new(project) as Rc<_>);
2005 }
2006
2007 let mut editor = Self {
2008 focus_handle,
2009 show_cursor_when_unfocused: false,
2010 last_focused_descendant: None,
2011 buffer: buffer.clone(),
2012 display_map: display_map.clone(),
2013 selections,
2014 scroll_manager: ScrollManager::new(cx),
2015 columnar_selection_state: None,
2016 add_selections_state: None,
2017 select_next_state: None,
2018 select_prev_state: None,
2019 selection_history: SelectionHistory::default(),
2020 defer_selection_effects: false,
2021 deferred_selection_effects_state: None,
2022 autoclose_regions: Vec::new(),
2023 snippet_stack: InvalidationStack::default(),
2024 select_syntax_node_history: SelectSyntaxNodeHistory::default(),
2025 ime_transaction: None,
2026 active_diagnostics: ActiveDiagnostic::None,
2027 show_inline_diagnostics: ProjectSettings::get_global(cx).diagnostics.inline.enabled,
2028 inline_diagnostics_update: Task::ready(()),
2029 inline_diagnostics: Vec::new(),
2030 soft_wrap_mode_override,
2031 diagnostics_max_severity,
2032 hard_wrap: None,
2033 completion_provider: project.clone().map(|project| Rc::new(project) as _),
2034 semantics_provider: project.clone().map(|project| Rc::new(project) as _),
2035 collaboration_hub: project.clone().map(|project| Box::new(project) as _),
2036 project,
2037 blink_manager: blink_manager.clone(),
2038 show_local_selections: true,
2039 show_scrollbars: ScrollbarAxes {
2040 horizontal: full_mode,
2041 vertical: full_mode,
2042 },
2043 minimap_visibility: MinimapVisibility::for_mode(&mode, cx),
2044 offset_content: !matches!(mode, EditorMode::SingleLine { .. }),
2045 show_breadcrumbs: EditorSettings::get_global(cx).toolbar.breadcrumbs,
2046 show_gutter: full_mode,
2047 show_line_numbers: (!full_mode).then_some(false),
2048 use_relative_line_numbers: None,
2049 disable_expand_excerpt_buttons: !full_mode,
2050 show_git_diff_gutter: None,
2051 show_code_actions: None,
2052 show_runnables: None,
2053 show_breakpoints: None,
2054 show_wrap_guides: None,
2055 show_indent_guides,
2056 placeholder_text: None,
2057 highlight_order: 0,
2058 highlighted_rows: HashMap::default(),
2059 background_highlights: TreeMap::default(),
2060 gutter_highlights: TreeMap::default(),
2061 scrollbar_marker_state: ScrollbarMarkerState::default(),
2062 active_indent_guides_state: ActiveIndentGuidesState::default(),
2063 nav_history: None,
2064 context_menu: RefCell::new(None),
2065 context_menu_options: None,
2066 mouse_context_menu: None,
2067 completion_tasks: Vec::new(),
2068 inline_blame_popover: None,
2069 inline_blame_popover_show_task: None,
2070 signature_help_state: SignatureHelpState::default(),
2071 auto_signature_help: None,
2072 find_all_references_task_sources: Vec::new(),
2073 next_completion_id: 0,
2074 next_inlay_id: 0,
2075 code_action_providers,
2076 available_code_actions: None,
2077 code_actions_task: None,
2078 quick_selection_highlight_task: None,
2079 debounced_selection_highlight_task: None,
2080 document_highlights_task: None,
2081 linked_editing_range_task: None,
2082 pending_rename: None,
2083 searchable: !is_minimap,
2084 cursor_shape: EditorSettings::get_global(cx)
2085 .cursor_shape
2086 .unwrap_or_default(),
2087 current_line_highlight: None,
2088 autoindent_mode: Some(AutoindentMode::EachLine),
2089 collapse_matches: false,
2090 workspace: None,
2091 input_enabled: !is_minimap,
2092 use_modal_editing: full_mode,
2093 read_only: is_minimap,
2094 use_autoclose: true,
2095 use_auto_surround: true,
2096 auto_replace_emoji_shortcode: false,
2097 jsx_tag_auto_close_enabled_in_any_buffer: false,
2098 leader_id: None,
2099 remote_id: None,
2100 hover_state: HoverState::default(),
2101 pending_mouse_down: None,
2102 hovered_link_state: None,
2103 edit_prediction_provider: None,
2104 active_inline_completion: None,
2105 stale_inline_completion_in_menu: None,
2106 edit_prediction_preview: EditPredictionPreview::Inactive {
2107 released_too_fast: false,
2108 },
2109 inline_diagnostics_enabled: full_mode,
2110 diagnostics_enabled: full_mode,
2111 inline_value_cache: InlineValueCache::new(inlay_hint_settings.show_value_hints),
2112 inlay_hint_cache: InlayHintCache::new(inlay_hint_settings),
2113 gutter_hovered: false,
2114 pixel_position_of_newest_cursor: None,
2115 last_bounds: None,
2116 last_position_map: None,
2117 expect_bounds_change: None,
2118 gutter_dimensions: GutterDimensions::default(),
2119 style: None,
2120 show_cursor_names: false,
2121 hovered_cursors: HashMap::default(),
2122 next_editor_action_id: EditorActionId::default(),
2123 editor_actions: Rc::default(),
2124 inline_completions_hidden_for_vim_mode: false,
2125 show_inline_completions_override: None,
2126 menu_inline_completions_policy: MenuInlineCompletionsPolicy::ByProvider,
2127 edit_prediction_settings: EditPredictionSettings::Disabled,
2128 edit_prediction_indent_conflict: false,
2129 edit_prediction_requires_modifier_in_indent_conflict: true,
2130 custom_context_menu: None,
2131 show_git_blame_gutter: false,
2132 show_git_blame_inline: false,
2133 show_selection_menu: None,
2134 show_git_blame_inline_delay_task: None,
2135 git_blame_inline_enabled: full_mode
2136 && ProjectSettings::get_global(cx).git.inline_blame_enabled(),
2137 render_diff_hunk_controls: Arc::new(render_diff_hunk_controls),
2138 serialize_dirty_buffers: !is_minimap
2139 && ProjectSettings::get_global(cx)
2140 .session
2141 .restore_unsaved_buffers,
2142 blame: None,
2143 blame_subscription: None,
2144 tasks: BTreeMap::default(),
2145
2146 breakpoint_store,
2147 gutter_breakpoint_indicator: (None, None),
2148 hovered_diff_hunk_row: None,
2149 _subscriptions: (!is_minimap)
2150 .then(|| {
2151 vec![
2152 cx.observe(&buffer, Self::on_buffer_changed),
2153 cx.subscribe_in(&buffer, window, Self::on_buffer_event),
2154 cx.observe_in(&display_map, window, Self::on_display_map_changed),
2155 cx.observe(&blink_manager, |_, _, cx| cx.notify()),
2156 cx.observe_global_in::<SettingsStore>(window, Self::settings_changed),
2157 observe_buffer_font_size_adjustment(cx, |_, cx| cx.notify()),
2158 cx.observe_window_activation(window, |editor, window, cx| {
2159 let active = window.is_window_active();
2160 editor.blink_manager.update(cx, |blink_manager, cx| {
2161 if active {
2162 blink_manager.enable(cx);
2163 } else {
2164 blink_manager.disable(cx);
2165 }
2166 });
2167 if active {
2168 editor.show_mouse_cursor(cx);
2169 }
2170 }),
2171 ]
2172 })
2173 .unwrap_or_default(),
2174 tasks_update_task: None,
2175 pull_diagnostics_task: Task::ready(()),
2176 colors: None,
2177 next_color_inlay_id: 0,
2178 linked_edit_ranges: Default::default(),
2179 in_project_search: false,
2180 previous_search_ranges: None,
2181 breadcrumb_header: None,
2182 focused_block: None,
2183 next_scroll_position: NextScrollCursorCenterTopBottom::default(),
2184 addons: HashMap::default(),
2185 registered_buffers: HashMap::default(),
2186 _scroll_cursor_center_top_bottom_task: Task::ready(()),
2187 selection_mark_mode: false,
2188 toggle_fold_multiple_buffers: Task::ready(()),
2189 serialize_selections: Task::ready(()),
2190 serialize_folds: Task::ready(()),
2191 text_style_refinement: None,
2192 load_diff_task: load_uncommitted_diff,
2193 temporary_diff_override: false,
2194 mouse_cursor_hidden: false,
2195 minimap: None,
2196 hide_mouse_mode: EditorSettings::get_global(cx)
2197 .hide_mouse
2198 .unwrap_or_default(),
2199 change_list: ChangeList::new(),
2200 mode,
2201 selection_drag_state: SelectionDragState::None,
2202 folding_newlines: Task::ready(()),
2203 };
2204
2205 if is_minimap {
2206 return editor;
2207 }
2208
2209 if let Some(breakpoints) = editor.breakpoint_store.as_ref() {
2210 editor
2211 ._subscriptions
2212 .push(cx.observe(breakpoints, |_, _, cx| {
2213 cx.notify();
2214 }));
2215 }
2216 editor.tasks_update_task = Some(editor.refresh_runnables(window, cx));
2217 editor._subscriptions.extend(project_subscriptions);
2218
2219 editor._subscriptions.push(cx.subscribe_in(
2220 &cx.entity(),
2221 window,
2222 |editor, _, e: &EditorEvent, window, cx| match e {
2223 EditorEvent::ScrollPositionChanged { local, .. } => {
2224 if *local {
2225 let new_anchor = editor.scroll_manager.anchor();
2226 let snapshot = editor.snapshot(window, cx);
2227 editor.update_restoration_data(cx, move |data| {
2228 data.scroll_position = (
2229 new_anchor.top_row(&snapshot.buffer_snapshot),
2230 new_anchor.offset,
2231 );
2232 });
2233 editor.hide_signature_help(cx, SignatureHelpHiddenBy::Escape);
2234 editor.inline_blame_popover.take();
2235 }
2236 }
2237 EditorEvent::Edited { .. } => {
2238 if !vim_enabled(cx) {
2239 let (map, selections) = editor.selections.all_adjusted_display(cx);
2240 let pop_state = editor
2241 .change_list
2242 .last()
2243 .map(|previous| {
2244 previous.len() == selections.len()
2245 && previous.iter().enumerate().all(|(ix, p)| {
2246 p.to_display_point(&map).row()
2247 == selections[ix].head().row()
2248 })
2249 })
2250 .unwrap_or(false);
2251 let new_positions = selections
2252 .into_iter()
2253 .map(|s| map.display_point_to_anchor(s.head(), Bias::Left))
2254 .collect();
2255 editor
2256 .change_list
2257 .push_to_change_list(pop_state, new_positions);
2258 }
2259 }
2260 _ => (),
2261 },
2262 ));
2263
2264 if let Some(dap_store) = editor
2265 .project
2266 .as_ref()
2267 .map(|project| project.read(cx).dap_store())
2268 {
2269 let weak_editor = cx.weak_entity();
2270
2271 editor
2272 ._subscriptions
2273 .push(
2274 cx.observe_new::<project::debugger::session::Session>(move |_, _, cx| {
2275 let session_entity = cx.entity();
2276 weak_editor
2277 .update(cx, |editor, cx| {
2278 editor._subscriptions.push(
2279 cx.subscribe(&session_entity, Self::on_debug_session_event),
2280 );
2281 })
2282 .ok();
2283 }),
2284 );
2285
2286 for session in dap_store.read(cx).sessions().cloned().collect::<Vec<_>>() {
2287 editor
2288 ._subscriptions
2289 .push(cx.subscribe(&session, Self::on_debug_session_event));
2290 }
2291 }
2292
2293 // skip adding the initial selection to selection history
2294 editor.selection_history.mode = SelectionHistoryMode::Skipping;
2295 editor.end_selection(window, cx);
2296 editor.selection_history.mode = SelectionHistoryMode::Normal;
2297
2298 editor.scroll_manager.show_scrollbars(window, cx);
2299 jsx_tag_auto_close::refresh_enabled_in_any_buffer(&mut editor, &buffer, cx);
2300
2301 if full_mode {
2302 let should_auto_hide_scrollbars = cx.should_auto_hide_scrollbars();
2303 cx.set_global(ScrollbarAutoHide(should_auto_hide_scrollbars));
2304
2305 if editor.git_blame_inline_enabled {
2306 editor.start_git_blame_inline(false, window, cx);
2307 }
2308
2309 editor.go_to_active_debug_line(window, cx);
2310
2311 if let Some(buffer) = buffer.read(cx).as_singleton() {
2312 if let Some(project) = editor.project.as_ref() {
2313 let handle = project.update(cx, |project, cx| {
2314 project.register_buffer_with_language_servers(&buffer, cx)
2315 });
2316 editor
2317 .registered_buffers
2318 .insert(buffer.read(cx).remote_id(), handle);
2319 }
2320 }
2321
2322 editor.minimap =
2323 editor.create_minimap(EditorSettings::get_global(cx).minimap, window, cx);
2324 editor.colors = Some(LspColorData::new(cx));
2325 editor.update_lsp_data(false, None, window, cx);
2326 }
2327
2328 if editor.mode.is_full() {
2329 editor.report_editor_event("Editor Opened", None, cx);
2330 }
2331
2332 editor
2333 }
2334
2335 pub fn deploy_mouse_context_menu(
2336 &mut self,
2337 position: gpui::Point<Pixels>,
2338 context_menu: Entity<ContextMenu>,
2339 window: &mut Window,
2340 cx: &mut Context<Self>,
2341 ) {
2342 self.mouse_context_menu = Some(MouseContextMenu::new(
2343 self,
2344 crate::mouse_context_menu::MenuPosition::PinnedToScreen(position),
2345 context_menu,
2346 window,
2347 cx,
2348 ));
2349 }
2350
2351 pub fn mouse_menu_is_focused(&self, window: &Window, cx: &App) -> bool {
2352 self.mouse_context_menu
2353 .as_ref()
2354 .is_some_and(|menu| menu.context_menu.focus_handle(cx).is_focused(window))
2355 }
2356
2357 pub fn key_context(&self, window: &Window, cx: &App) -> KeyContext {
2358 self.key_context_internal(self.has_active_inline_completion(), window, cx)
2359 }
2360
2361 fn key_context_internal(
2362 &self,
2363 has_active_edit_prediction: bool,
2364 window: &Window,
2365 cx: &App,
2366 ) -> KeyContext {
2367 let mut key_context = KeyContext::new_with_defaults();
2368 key_context.add("Editor");
2369 let mode = match self.mode {
2370 EditorMode::SingleLine { .. } => "single_line",
2371 EditorMode::AutoHeight { .. } => "auto_height",
2372 EditorMode::Minimap { .. } => "minimap",
2373 EditorMode::Full { .. } => "full",
2374 };
2375
2376 if EditorSettings::jupyter_enabled(cx) {
2377 key_context.add("jupyter");
2378 }
2379
2380 key_context.set("mode", mode);
2381 if self.pending_rename.is_some() {
2382 key_context.add("renaming");
2383 }
2384
2385 match self.context_menu.borrow().as_ref() {
2386 Some(CodeContextMenu::Completions(menu)) => {
2387 if menu.visible() {
2388 key_context.add("menu");
2389 key_context.add("showing_completions");
2390 }
2391 }
2392 Some(CodeContextMenu::CodeActions(menu)) => {
2393 if menu.visible() {
2394 key_context.add("menu");
2395 key_context.add("showing_code_actions")
2396 }
2397 }
2398 None => {}
2399 }
2400
2401 if self.signature_help_state.has_multiple_signatures() {
2402 key_context.add("showing_signature_help");
2403 }
2404
2405 // Disable vim contexts when a sub-editor (e.g. rename/inline assistant) is focused.
2406 if !self.focus_handle(cx).contains_focused(window, cx)
2407 || (self.is_focused(window) || self.mouse_menu_is_focused(window, cx))
2408 {
2409 for addon in self.addons.values() {
2410 addon.extend_key_context(&mut key_context, cx)
2411 }
2412 }
2413
2414 if let Some(singleton_buffer) = self.buffer.read(cx).as_singleton() {
2415 if let Some(extension) = singleton_buffer
2416 .read(cx)
2417 .file()
2418 .and_then(|file| file.path().extension()?.to_str())
2419 {
2420 key_context.set("extension", extension.to_string());
2421 }
2422 } else {
2423 key_context.add("multibuffer");
2424 }
2425
2426 if has_active_edit_prediction {
2427 if self.edit_prediction_in_conflict() {
2428 key_context.add(EDIT_PREDICTION_CONFLICT_KEY_CONTEXT);
2429 } else {
2430 key_context.add(EDIT_PREDICTION_KEY_CONTEXT);
2431 key_context.add("copilot_suggestion");
2432 }
2433 }
2434
2435 if self.selection_mark_mode {
2436 key_context.add("selection_mode");
2437 }
2438
2439 key_context
2440 }
2441
2442 fn show_mouse_cursor(&mut self, cx: &mut Context<Self>) {
2443 if self.mouse_cursor_hidden {
2444 self.mouse_cursor_hidden = false;
2445 cx.notify();
2446 }
2447 }
2448
2449 pub fn hide_mouse_cursor(&mut self, origin: HideMouseCursorOrigin, cx: &mut Context<Self>) {
2450 let hide_mouse_cursor = match origin {
2451 HideMouseCursorOrigin::TypingAction => {
2452 matches!(
2453 self.hide_mouse_mode,
2454 HideMouseMode::OnTyping | HideMouseMode::OnTypingAndMovement
2455 )
2456 }
2457 HideMouseCursorOrigin::MovementAction => {
2458 matches!(self.hide_mouse_mode, HideMouseMode::OnTypingAndMovement)
2459 }
2460 };
2461 if self.mouse_cursor_hidden != hide_mouse_cursor {
2462 self.mouse_cursor_hidden = hide_mouse_cursor;
2463 cx.notify();
2464 }
2465 }
2466
2467 pub fn edit_prediction_in_conflict(&self) -> bool {
2468 if !self.show_edit_predictions_in_menu() {
2469 return false;
2470 }
2471
2472 let showing_completions = self
2473 .context_menu
2474 .borrow()
2475 .as_ref()
2476 .map_or(false, |context| {
2477 matches!(context, CodeContextMenu::Completions(_))
2478 });
2479
2480 showing_completions
2481 || self.edit_prediction_requires_modifier()
2482 // Require modifier key when the cursor is on leading whitespace, to allow `tab`
2483 // bindings to insert tab characters.
2484 || (self.edit_prediction_requires_modifier_in_indent_conflict && self.edit_prediction_indent_conflict)
2485 }
2486
2487 pub fn accept_edit_prediction_keybind(
2488 &self,
2489 accept_partial: bool,
2490 window: &Window,
2491 cx: &App,
2492 ) -> AcceptEditPredictionBinding {
2493 let key_context = self.key_context_internal(true, window, cx);
2494 let in_conflict = self.edit_prediction_in_conflict();
2495
2496 let bindings = if accept_partial {
2497 window.bindings_for_action_in_context(&AcceptPartialEditPrediction, key_context)
2498 } else {
2499 window.bindings_for_action_in_context(&AcceptEditPrediction, key_context)
2500 };
2501
2502 // TODO: if the binding contains multiple keystrokes, display all of them, not
2503 // just the first one.
2504 AcceptEditPredictionBinding(bindings.into_iter().rev().find(|binding| {
2505 !in_conflict
2506 || binding
2507 .keystrokes()
2508 .first()
2509 .map_or(false, |keystroke| keystroke.modifiers.modified())
2510 }))
2511 }
2512
2513 pub fn new_file(
2514 workspace: &mut Workspace,
2515 _: &workspace::NewFile,
2516 window: &mut Window,
2517 cx: &mut Context<Workspace>,
2518 ) {
2519 Self::new_in_workspace(workspace, window, cx).detach_and_prompt_err(
2520 "Failed to create buffer",
2521 window,
2522 cx,
2523 |e, _, _| match e.error_code() {
2524 ErrorCode::RemoteUpgradeRequired => Some(format!(
2525 "The remote instance of Zed does not support this yet. It must be upgraded to {}",
2526 e.error_tag("required").unwrap_or("the latest version")
2527 )),
2528 _ => None,
2529 },
2530 );
2531 }
2532
2533 pub fn new_in_workspace(
2534 workspace: &mut Workspace,
2535 window: &mut Window,
2536 cx: &mut Context<Workspace>,
2537 ) -> Task<Result<Entity<Editor>>> {
2538 let project = workspace.project().clone();
2539 let create = project.update(cx, |project, cx| project.create_buffer(cx));
2540
2541 cx.spawn_in(window, async move |workspace, cx| {
2542 let buffer = create.await?;
2543 workspace.update_in(cx, |workspace, window, cx| {
2544 let editor =
2545 cx.new(|cx| Editor::for_buffer(buffer, Some(project.clone()), window, cx));
2546 workspace.add_item_to_active_pane(Box::new(editor.clone()), None, true, window, cx);
2547 editor
2548 })
2549 })
2550 }
2551
2552 fn new_file_vertical(
2553 workspace: &mut Workspace,
2554 _: &workspace::NewFileSplitVertical,
2555 window: &mut Window,
2556 cx: &mut Context<Workspace>,
2557 ) {
2558 Self::new_file_in_direction(workspace, SplitDirection::vertical(cx), window, cx)
2559 }
2560
2561 fn new_file_horizontal(
2562 workspace: &mut Workspace,
2563 _: &workspace::NewFileSplitHorizontal,
2564 window: &mut Window,
2565 cx: &mut Context<Workspace>,
2566 ) {
2567 Self::new_file_in_direction(workspace, SplitDirection::horizontal(cx), window, cx)
2568 }
2569
2570 fn new_file_in_direction(
2571 workspace: &mut Workspace,
2572 direction: SplitDirection,
2573 window: &mut Window,
2574 cx: &mut Context<Workspace>,
2575 ) {
2576 let project = workspace.project().clone();
2577 let create = project.update(cx, |project, cx| project.create_buffer(cx));
2578
2579 cx.spawn_in(window, async move |workspace, cx| {
2580 let buffer = create.await?;
2581 workspace.update_in(cx, move |workspace, window, cx| {
2582 workspace.split_item(
2583 direction,
2584 Box::new(
2585 cx.new(|cx| Editor::for_buffer(buffer, Some(project.clone()), window, cx)),
2586 ),
2587 window,
2588 cx,
2589 )
2590 })?;
2591 anyhow::Ok(())
2592 })
2593 .detach_and_prompt_err("Failed to create buffer", window, cx, |e, _, _| {
2594 match e.error_code() {
2595 ErrorCode::RemoteUpgradeRequired => Some(format!(
2596 "The remote instance of Zed does not support this yet. It must be upgraded to {}",
2597 e.error_tag("required").unwrap_or("the latest version")
2598 )),
2599 _ => None,
2600 }
2601 });
2602 }
2603
2604 pub fn leader_id(&self) -> Option<CollaboratorId> {
2605 self.leader_id
2606 }
2607
2608 pub fn buffer(&self) -> &Entity<MultiBuffer> {
2609 &self.buffer
2610 }
2611
2612 pub fn workspace(&self) -> Option<Entity<Workspace>> {
2613 self.workspace.as_ref()?.0.upgrade()
2614 }
2615
2616 pub fn title<'a>(&self, cx: &'a App) -> Cow<'a, str> {
2617 self.buffer().read(cx).title(cx)
2618 }
2619
2620 pub fn snapshot(&self, window: &mut Window, cx: &mut App) -> EditorSnapshot {
2621 let git_blame_gutter_max_author_length = self
2622 .render_git_blame_gutter(cx)
2623 .then(|| {
2624 if let Some(blame) = self.blame.as_ref() {
2625 let max_author_length =
2626 blame.update(cx, |blame, cx| blame.max_author_length(cx));
2627 Some(max_author_length)
2628 } else {
2629 None
2630 }
2631 })
2632 .flatten();
2633
2634 EditorSnapshot {
2635 mode: self.mode.clone(),
2636 show_gutter: self.show_gutter,
2637 show_line_numbers: self.show_line_numbers,
2638 show_git_diff_gutter: self.show_git_diff_gutter,
2639 show_code_actions: self.show_code_actions,
2640 show_runnables: self.show_runnables,
2641 show_breakpoints: self.show_breakpoints,
2642 git_blame_gutter_max_author_length,
2643 display_snapshot: self.display_map.update(cx, |map, cx| map.snapshot(cx)),
2644 scroll_anchor: self.scroll_manager.anchor(),
2645 ongoing_scroll: self.scroll_manager.ongoing_scroll(),
2646 placeholder_text: self.placeholder_text.clone(),
2647 is_focused: self.focus_handle.is_focused(window),
2648 current_line_highlight: self
2649 .current_line_highlight
2650 .unwrap_or_else(|| EditorSettings::get_global(cx).current_line_highlight),
2651 gutter_hovered: self.gutter_hovered,
2652 }
2653 }
2654
2655 pub fn language_at<T: ToOffset>(&self, point: T, cx: &App) -> Option<Arc<Language>> {
2656 self.buffer.read(cx).language_at(point, cx)
2657 }
2658
2659 pub fn file_at<T: ToOffset>(&self, point: T, cx: &App) -> Option<Arc<dyn language::File>> {
2660 self.buffer.read(cx).read(cx).file_at(point).cloned()
2661 }
2662
2663 pub fn active_excerpt(
2664 &self,
2665 cx: &App,
2666 ) -> Option<(ExcerptId, Entity<Buffer>, Range<text::Anchor>)> {
2667 self.buffer
2668 .read(cx)
2669 .excerpt_containing(self.selections.newest_anchor().head(), cx)
2670 }
2671
2672 pub fn mode(&self) -> &EditorMode {
2673 &self.mode
2674 }
2675
2676 pub fn set_mode(&mut self, mode: EditorMode) {
2677 self.mode = mode;
2678 }
2679
2680 pub fn collaboration_hub(&self) -> Option<&dyn CollaborationHub> {
2681 self.collaboration_hub.as_deref()
2682 }
2683
2684 pub fn set_collaboration_hub(&mut self, hub: Box<dyn CollaborationHub>) {
2685 self.collaboration_hub = Some(hub);
2686 }
2687
2688 pub fn set_in_project_search(&mut self, in_project_search: bool) {
2689 self.in_project_search = in_project_search;
2690 }
2691
2692 pub fn set_custom_context_menu(
2693 &mut self,
2694 f: impl 'static
2695 + Fn(
2696 &mut Self,
2697 DisplayPoint,
2698 &mut Window,
2699 &mut Context<Self>,
2700 ) -> Option<Entity<ui::ContextMenu>>,
2701 ) {
2702 self.custom_context_menu = Some(Box::new(f))
2703 }
2704
2705 pub fn set_completion_provider(&mut self, provider: Option<Rc<dyn CompletionProvider>>) {
2706 self.completion_provider = provider;
2707 }
2708
2709 pub fn semantics_provider(&self) -> Option<Rc<dyn SemanticsProvider>> {
2710 self.semantics_provider.clone()
2711 }
2712
2713 pub fn set_semantics_provider(&mut self, provider: Option<Rc<dyn SemanticsProvider>>) {
2714 self.semantics_provider = provider;
2715 }
2716
2717 pub fn set_edit_prediction_provider<T>(
2718 &mut self,
2719 provider: Option<Entity<T>>,
2720 window: &mut Window,
2721 cx: &mut Context<Self>,
2722 ) where
2723 T: EditPredictionProvider,
2724 {
2725 self.edit_prediction_provider =
2726 provider.map(|provider| RegisteredInlineCompletionProvider {
2727 _subscription: cx.observe_in(&provider, window, |this, _, window, cx| {
2728 if this.focus_handle.is_focused(window) {
2729 this.update_visible_inline_completion(window, cx);
2730 }
2731 }),
2732 provider: Arc::new(provider),
2733 });
2734 self.update_edit_prediction_settings(cx);
2735 self.refresh_inline_completion(false, false, window, cx);
2736 }
2737
2738 pub fn placeholder_text(&self) -> Option<&str> {
2739 self.placeholder_text.as_deref()
2740 }
2741
2742 pub fn set_placeholder_text(
2743 &mut self,
2744 placeholder_text: impl Into<Arc<str>>,
2745 cx: &mut Context<Self>,
2746 ) {
2747 let placeholder_text = Some(placeholder_text.into());
2748 if self.placeholder_text != placeholder_text {
2749 self.placeholder_text = placeholder_text;
2750 cx.notify();
2751 }
2752 }
2753
2754 pub fn set_cursor_shape(&mut self, cursor_shape: CursorShape, cx: &mut Context<Self>) {
2755 self.cursor_shape = cursor_shape;
2756
2757 // Disrupt blink for immediate user feedback that the cursor shape has changed
2758 self.blink_manager.update(cx, BlinkManager::show_cursor);
2759
2760 cx.notify();
2761 }
2762
2763 pub fn set_current_line_highlight(
2764 &mut self,
2765 current_line_highlight: Option<CurrentLineHighlight>,
2766 ) {
2767 self.current_line_highlight = current_line_highlight;
2768 }
2769
2770 pub fn set_collapse_matches(&mut self, collapse_matches: bool) {
2771 self.collapse_matches = collapse_matches;
2772 }
2773
2774 fn register_buffers_with_language_servers(&mut self, cx: &mut Context<Self>) {
2775 let buffers = self.buffer.read(cx).all_buffers();
2776 let Some(project) = self.project.as_ref() else {
2777 return;
2778 };
2779 project.update(cx, |project, cx| {
2780 for buffer in buffers {
2781 self.registered_buffers
2782 .entry(buffer.read(cx).remote_id())
2783 .or_insert_with(|| project.register_buffer_with_language_servers(&buffer, cx));
2784 }
2785 })
2786 }
2787
2788 pub fn range_for_match<T: std::marker::Copy>(&self, range: &Range<T>) -> Range<T> {
2789 if self.collapse_matches {
2790 return range.start..range.start;
2791 }
2792 range.clone()
2793 }
2794
2795 pub fn set_clip_at_line_ends(&mut self, clip: bool, cx: &mut Context<Self>) {
2796 if self.display_map.read(cx).clip_at_line_ends != clip {
2797 self.display_map
2798 .update(cx, |map, _| map.clip_at_line_ends = clip);
2799 }
2800 }
2801
2802 pub fn set_input_enabled(&mut self, input_enabled: bool) {
2803 self.input_enabled = input_enabled;
2804 }
2805
2806 pub fn set_inline_completions_hidden_for_vim_mode(
2807 &mut self,
2808 hidden: bool,
2809 window: &mut Window,
2810 cx: &mut Context<Self>,
2811 ) {
2812 if hidden != self.inline_completions_hidden_for_vim_mode {
2813 self.inline_completions_hidden_for_vim_mode = hidden;
2814 if hidden {
2815 self.update_visible_inline_completion(window, cx);
2816 } else {
2817 self.refresh_inline_completion(true, false, window, cx);
2818 }
2819 }
2820 }
2821
2822 pub fn set_menu_inline_completions_policy(&mut self, value: MenuInlineCompletionsPolicy) {
2823 self.menu_inline_completions_policy = value;
2824 }
2825
2826 pub fn set_autoindent(&mut self, autoindent: bool) {
2827 if autoindent {
2828 self.autoindent_mode = Some(AutoindentMode::EachLine);
2829 } else {
2830 self.autoindent_mode = None;
2831 }
2832 }
2833
2834 pub fn read_only(&self, cx: &App) -> bool {
2835 self.read_only || self.buffer.read(cx).read_only()
2836 }
2837
2838 pub fn set_read_only(&mut self, read_only: bool) {
2839 self.read_only = read_only;
2840 }
2841
2842 pub fn set_use_autoclose(&mut self, autoclose: bool) {
2843 self.use_autoclose = autoclose;
2844 }
2845
2846 pub fn set_use_auto_surround(&mut self, auto_surround: bool) {
2847 self.use_auto_surround = auto_surround;
2848 }
2849
2850 pub fn set_auto_replace_emoji_shortcode(&mut self, auto_replace: bool) {
2851 self.auto_replace_emoji_shortcode = auto_replace;
2852 }
2853
2854 pub fn toggle_edit_predictions(
2855 &mut self,
2856 _: &ToggleEditPrediction,
2857 window: &mut Window,
2858 cx: &mut Context<Self>,
2859 ) {
2860 if self.show_inline_completions_override.is_some() {
2861 self.set_show_edit_predictions(None, window, cx);
2862 } else {
2863 let show_edit_predictions = !self.edit_predictions_enabled();
2864 self.set_show_edit_predictions(Some(show_edit_predictions), window, cx);
2865 }
2866 }
2867
2868 pub fn set_show_edit_predictions(
2869 &mut self,
2870 show_edit_predictions: Option<bool>,
2871 window: &mut Window,
2872 cx: &mut Context<Self>,
2873 ) {
2874 self.show_inline_completions_override = show_edit_predictions;
2875 self.update_edit_prediction_settings(cx);
2876
2877 if let Some(false) = show_edit_predictions {
2878 self.discard_inline_completion(false, cx);
2879 } else {
2880 self.refresh_inline_completion(false, true, window, cx);
2881 }
2882 }
2883
2884 fn inline_completions_disabled_in_scope(
2885 &self,
2886 buffer: &Entity<Buffer>,
2887 buffer_position: language::Anchor,
2888 cx: &App,
2889 ) -> bool {
2890 let snapshot = buffer.read(cx).snapshot();
2891 let settings = snapshot.settings_at(buffer_position, cx);
2892
2893 let Some(scope) = snapshot.language_scope_at(buffer_position) else {
2894 return false;
2895 };
2896
2897 scope.override_name().map_or(false, |scope_name| {
2898 settings
2899 .edit_predictions_disabled_in
2900 .iter()
2901 .any(|s| s == scope_name)
2902 })
2903 }
2904
2905 pub fn set_use_modal_editing(&mut self, to: bool) {
2906 self.use_modal_editing = to;
2907 }
2908
2909 pub fn use_modal_editing(&self) -> bool {
2910 self.use_modal_editing
2911 }
2912
2913 fn selections_did_change(
2914 &mut self,
2915 local: bool,
2916 old_cursor_position: &Anchor,
2917 effects: SelectionEffects,
2918 window: &mut Window,
2919 cx: &mut Context<Self>,
2920 ) {
2921 window.invalidate_character_coordinates();
2922
2923 // Copy selections to primary selection buffer
2924 #[cfg(any(target_os = "linux", target_os = "freebsd"))]
2925 if local {
2926 let selections = self.selections.all::<usize>(cx);
2927 let buffer_handle = self.buffer.read(cx).read(cx);
2928
2929 let mut text = String::new();
2930 for (index, selection) in selections.iter().enumerate() {
2931 let text_for_selection = buffer_handle
2932 .text_for_range(selection.start..selection.end)
2933 .collect::<String>();
2934
2935 text.push_str(&text_for_selection);
2936 if index != selections.len() - 1 {
2937 text.push('\n');
2938 }
2939 }
2940
2941 if !text.is_empty() {
2942 cx.write_to_primary(ClipboardItem::new_string(text));
2943 }
2944 }
2945
2946 if self.focus_handle.is_focused(window) && self.leader_id.is_none() {
2947 self.buffer.update(cx, |buffer, cx| {
2948 buffer.set_active_selections(
2949 &self.selections.disjoint_anchors(),
2950 self.selections.line_mode,
2951 self.cursor_shape,
2952 cx,
2953 )
2954 });
2955 }
2956 let display_map = self
2957 .display_map
2958 .update(cx, |display_map, cx| display_map.snapshot(cx));
2959 let buffer = &display_map.buffer_snapshot;
2960 if self.selections.count() == 1 {
2961 self.add_selections_state = None;
2962 }
2963 self.select_next_state = None;
2964 self.select_prev_state = None;
2965 self.select_syntax_node_history.try_clear();
2966 self.invalidate_autoclose_regions(&self.selections.disjoint_anchors(), buffer);
2967 self.snippet_stack
2968 .invalidate(&self.selections.disjoint_anchors(), buffer);
2969 self.take_rename(false, window, cx);
2970
2971 let newest_selection = self.selections.newest_anchor();
2972 let new_cursor_position = newest_selection.head();
2973 let selection_start = newest_selection.start;
2974
2975 if effects.nav_history.is_none() || effects.nav_history == Some(true) {
2976 self.push_to_nav_history(
2977 *old_cursor_position,
2978 Some(new_cursor_position.to_point(buffer)),
2979 false,
2980 effects.nav_history == Some(true),
2981 cx,
2982 );
2983 }
2984
2985 if local {
2986 if let Some(buffer_id) = new_cursor_position.buffer_id {
2987 if !self.registered_buffers.contains_key(&buffer_id) {
2988 if let Some(project) = self.project.as_ref() {
2989 project.update(cx, |project, cx| {
2990 let Some(buffer) = self.buffer.read(cx).buffer(buffer_id) else {
2991 return;
2992 };
2993 self.registered_buffers.insert(
2994 buffer_id,
2995 project.register_buffer_with_language_servers(&buffer, cx),
2996 );
2997 })
2998 }
2999 }
3000 }
3001
3002 let mut context_menu = self.context_menu.borrow_mut();
3003 let completion_menu = match context_menu.as_ref() {
3004 Some(CodeContextMenu::Completions(menu)) => Some(menu),
3005 Some(CodeContextMenu::CodeActions(_)) => {
3006 *context_menu = None;
3007 None
3008 }
3009 None => None,
3010 };
3011 let completion_position = completion_menu.map(|menu| menu.initial_position);
3012 drop(context_menu);
3013
3014 if effects.completions {
3015 if let Some(completion_position) = completion_position {
3016 let start_offset = selection_start.to_offset(buffer);
3017 let position_matches = start_offset == completion_position.to_offset(buffer);
3018 let continue_showing = if position_matches {
3019 if self.snippet_stack.is_empty() {
3020 buffer.char_kind_before(start_offset, true) == Some(CharKind::Word)
3021 } else {
3022 // Snippet choices can be shown even when the cursor is in whitespace.
3023 // Dismissing the menu with actions like backspace is handled by
3024 // invalidation regions.
3025 true
3026 }
3027 } else {
3028 false
3029 };
3030
3031 if continue_showing {
3032 self.show_completions(&ShowCompletions { trigger: None }, window, cx);
3033 } else {
3034 self.hide_context_menu(window, cx);
3035 }
3036 }
3037 }
3038
3039 hide_hover(self, cx);
3040
3041 if old_cursor_position.to_display_point(&display_map).row()
3042 != new_cursor_position.to_display_point(&display_map).row()
3043 {
3044 self.available_code_actions.take();
3045 }
3046 self.refresh_code_actions(window, cx);
3047 self.refresh_document_highlights(cx);
3048 self.refresh_selected_text_highlights(false, window, cx);
3049 refresh_matching_bracket_highlights(self, window, cx);
3050 self.update_visible_inline_completion(window, cx);
3051 self.edit_prediction_requires_modifier_in_indent_conflict = true;
3052 linked_editing_ranges::refresh_linked_ranges(self, window, cx);
3053 self.inline_blame_popover.take();
3054 if self.git_blame_inline_enabled {
3055 self.start_inline_blame_timer(window, cx);
3056 }
3057 }
3058
3059 self.blink_manager.update(cx, BlinkManager::pause_blinking);
3060 cx.emit(EditorEvent::SelectionsChanged { local });
3061
3062 let selections = &self.selections.disjoint;
3063 if selections.len() == 1 {
3064 cx.emit(SearchEvent::ActiveMatchChanged)
3065 }
3066 if local {
3067 if let Some((_, _, buffer_snapshot)) = buffer.as_singleton() {
3068 let inmemory_selections = selections
3069 .iter()
3070 .map(|s| {
3071 text::ToPoint::to_point(&s.range().start.text_anchor, buffer_snapshot)
3072 ..text::ToPoint::to_point(&s.range().end.text_anchor, buffer_snapshot)
3073 })
3074 .collect();
3075 self.update_restoration_data(cx, |data| {
3076 data.selections = inmemory_selections;
3077 });
3078
3079 if WorkspaceSettings::get(None, cx).restore_on_startup
3080 != RestoreOnStartupBehavior::None
3081 {
3082 if let Some(workspace_id) =
3083 self.workspace.as_ref().and_then(|workspace| workspace.1)
3084 {
3085 let snapshot = self.buffer().read(cx).snapshot(cx);
3086 let selections = selections.clone();
3087 let background_executor = cx.background_executor().clone();
3088 let editor_id = cx.entity().entity_id().as_u64() as ItemId;
3089 self.serialize_selections = cx.background_spawn(async move {
3090 background_executor.timer(SERIALIZATION_THROTTLE_TIME).await;
3091 let db_selections = selections
3092 .iter()
3093 .map(|selection| {
3094 (
3095 selection.start.to_offset(&snapshot),
3096 selection.end.to_offset(&snapshot),
3097 )
3098 })
3099 .collect();
3100
3101 DB.save_editor_selections(editor_id, workspace_id, db_selections)
3102 .await
3103 .with_context(|| format!("persisting editor selections for editor {editor_id}, workspace {workspace_id:?}"))
3104 .log_err();
3105 });
3106 }
3107 }
3108 }
3109 }
3110
3111 cx.notify();
3112 }
3113
3114 fn folds_did_change(&mut self, cx: &mut Context<Self>) {
3115 use text::ToOffset as _;
3116 use text::ToPoint as _;
3117
3118 if self.mode.is_minimap()
3119 || WorkspaceSettings::get(None, cx).restore_on_startup == RestoreOnStartupBehavior::None
3120 {
3121 return;
3122 }
3123
3124 let Some(singleton) = self.buffer().read(cx).as_singleton() else {
3125 return;
3126 };
3127
3128 let snapshot = singleton.read(cx).snapshot();
3129 let inmemory_folds = self.display_map.update(cx, |display_map, cx| {
3130 let display_snapshot = display_map.snapshot(cx);
3131
3132 display_snapshot
3133 .folds_in_range(0..display_snapshot.buffer_snapshot.len())
3134 .map(|fold| {
3135 fold.range.start.text_anchor.to_point(&snapshot)
3136 ..fold.range.end.text_anchor.to_point(&snapshot)
3137 })
3138 .collect()
3139 });
3140 self.update_restoration_data(cx, |data| {
3141 data.folds = inmemory_folds;
3142 });
3143
3144 let Some(workspace_id) = self.workspace.as_ref().and_then(|workspace| workspace.1) else {
3145 return;
3146 };
3147 let background_executor = cx.background_executor().clone();
3148 let editor_id = cx.entity().entity_id().as_u64() as ItemId;
3149 let db_folds = self.display_map.update(cx, |display_map, cx| {
3150 display_map
3151 .snapshot(cx)
3152 .folds_in_range(0..snapshot.len())
3153 .map(|fold| {
3154 (
3155 fold.range.start.text_anchor.to_offset(&snapshot),
3156 fold.range.end.text_anchor.to_offset(&snapshot),
3157 )
3158 })
3159 .collect()
3160 });
3161 self.serialize_folds = cx.background_spawn(async move {
3162 background_executor.timer(SERIALIZATION_THROTTLE_TIME).await;
3163 DB.save_editor_folds(editor_id, workspace_id, db_folds)
3164 .await
3165 .with_context(|| {
3166 format!(
3167 "persisting editor folds for editor {editor_id}, workspace {workspace_id:?}"
3168 )
3169 })
3170 .log_err();
3171 });
3172 }
3173
3174 pub fn sync_selections(
3175 &mut self,
3176 other: Entity<Editor>,
3177 cx: &mut Context<Self>,
3178 ) -> gpui::Subscription {
3179 let other_selections = other.read(cx).selections.disjoint.to_vec();
3180 self.selections.change_with(cx, |selections| {
3181 selections.select_anchors(other_selections);
3182 });
3183
3184 let other_subscription =
3185 cx.subscribe(&other, |this, other, other_evt, cx| match other_evt {
3186 EditorEvent::SelectionsChanged { local: true } => {
3187 let other_selections = other.read(cx).selections.disjoint.to_vec();
3188 if other_selections.is_empty() {
3189 return;
3190 }
3191 this.selections.change_with(cx, |selections| {
3192 selections.select_anchors(other_selections);
3193 });
3194 }
3195 _ => {}
3196 });
3197
3198 let this_subscription =
3199 cx.subscribe_self::<EditorEvent>(move |this, this_evt, cx| match this_evt {
3200 EditorEvent::SelectionsChanged { local: true } => {
3201 let these_selections = this.selections.disjoint.to_vec();
3202 if these_selections.is_empty() {
3203 return;
3204 }
3205 other.update(cx, |other_editor, cx| {
3206 other_editor.selections.change_with(cx, |selections| {
3207 selections.select_anchors(these_selections);
3208 })
3209 });
3210 }
3211 _ => {}
3212 });
3213
3214 Subscription::join(other_subscription, this_subscription)
3215 }
3216
3217 /// Changes selections using the provided mutation function. Changes to `self.selections` occur
3218 /// immediately, but when run within `transact` or `with_selection_effects_deferred` other
3219 /// effects of selection change occur at the end of the transaction.
3220 pub fn change_selections<R>(
3221 &mut self,
3222 effects: SelectionEffects,
3223 window: &mut Window,
3224 cx: &mut Context<Self>,
3225 change: impl FnOnce(&mut MutableSelectionsCollection<'_>) -> R,
3226 ) -> R {
3227 if let Some(state) = &mut self.deferred_selection_effects_state {
3228 state.effects.scroll = effects.scroll.or(state.effects.scroll);
3229 state.effects.completions = effects.completions;
3230 state.effects.nav_history = effects.nav_history.or(state.effects.nav_history);
3231 let (changed, result) = self.selections.change_with(cx, change);
3232 state.changed |= changed;
3233 return result;
3234 }
3235 let mut state = DeferredSelectionEffectsState {
3236 changed: false,
3237 effects,
3238 old_cursor_position: self.selections.newest_anchor().head(),
3239 history_entry: SelectionHistoryEntry {
3240 selections: self.selections.disjoint_anchors(),
3241 select_next_state: self.select_next_state.clone(),
3242 select_prev_state: self.select_prev_state.clone(),
3243 add_selections_state: self.add_selections_state.clone(),
3244 },
3245 };
3246 let (changed, result) = self.selections.change_with(cx, change);
3247 state.changed = state.changed || changed;
3248 if self.defer_selection_effects {
3249 self.deferred_selection_effects_state = Some(state);
3250 } else {
3251 self.apply_selection_effects(state, window, cx);
3252 }
3253 result
3254 }
3255
3256 /// Defers the effects of selection change, so that the effects of multiple calls to
3257 /// `change_selections` are applied at the end. This way these intermediate states aren't added
3258 /// to selection history and the state of popovers based on selection position aren't
3259 /// erroneously updated.
3260 pub fn with_selection_effects_deferred<R>(
3261 &mut self,
3262 window: &mut Window,
3263 cx: &mut Context<Self>,
3264 update: impl FnOnce(&mut Self, &mut Window, &mut Context<Self>) -> R,
3265 ) -> R {
3266 let already_deferred = self.defer_selection_effects;
3267 self.defer_selection_effects = true;
3268 let result = update(self, window, cx);
3269 if !already_deferred {
3270 self.defer_selection_effects = false;
3271 if let Some(state) = self.deferred_selection_effects_state.take() {
3272 self.apply_selection_effects(state, window, cx);
3273 }
3274 }
3275 result
3276 }
3277
3278 fn apply_selection_effects(
3279 &mut self,
3280 state: DeferredSelectionEffectsState,
3281 window: &mut Window,
3282 cx: &mut Context<Self>,
3283 ) {
3284 if state.changed {
3285 self.selection_history.push(state.history_entry);
3286
3287 if let Some(autoscroll) = state.effects.scroll {
3288 self.request_autoscroll(autoscroll, cx);
3289 }
3290
3291 let old_cursor_position = &state.old_cursor_position;
3292
3293 self.selections_did_change(true, &old_cursor_position, state.effects, window, cx);
3294
3295 if self.should_open_signature_help_automatically(&old_cursor_position, cx) {
3296 self.show_signature_help(&ShowSignatureHelp, window, cx);
3297 }
3298 }
3299 }
3300
3301 pub fn edit<I, S, T>(&mut self, edits: I, cx: &mut Context<Self>)
3302 where
3303 I: IntoIterator<Item = (Range<S>, T)>,
3304 S: ToOffset,
3305 T: Into<Arc<str>>,
3306 {
3307 if self.read_only(cx) {
3308 return;
3309 }
3310
3311 self.buffer
3312 .update(cx, |buffer, cx| buffer.edit(edits, None, cx));
3313 }
3314
3315 pub fn edit_with_autoindent<I, S, T>(&mut self, edits: I, cx: &mut Context<Self>)
3316 where
3317 I: IntoIterator<Item = (Range<S>, T)>,
3318 S: ToOffset,
3319 T: Into<Arc<str>>,
3320 {
3321 if self.read_only(cx) {
3322 return;
3323 }
3324
3325 self.buffer.update(cx, |buffer, cx| {
3326 buffer.edit(edits, self.autoindent_mode.clone(), cx)
3327 });
3328 }
3329
3330 pub fn edit_with_block_indent<I, S, T>(
3331 &mut self,
3332 edits: I,
3333 original_indent_columns: Vec<Option<u32>>,
3334 cx: &mut Context<Self>,
3335 ) where
3336 I: IntoIterator<Item = (Range<S>, T)>,
3337 S: ToOffset,
3338 T: Into<Arc<str>>,
3339 {
3340 if self.read_only(cx) {
3341 return;
3342 }
3343
3344 self.buffer.update(cx, |buffer, cx| {
3345 buffer.edit(
3346 edits,
3347 Some(AutoindentMode::Block {
3348 original_indent_columns,
3349 }),
3350 cx,
3351 )
3352 });
3353 }
3354
3355 fn select(&mut self, phase: SelectPhase, window: &mut Window, cx: &mut Context<Self>) {
3356 self.hide_context_menu(window, cx);
3357
3358 match phase {
3359 SelectPhase::Begin {
3360 position,
3361 add,
3362 click_count,
3363 } => self.begin_selection(position, add, click_count, window, cx),
3364 SelectPhase::BeginColumnar {
3365 position,
3366 goal_column,
3367 reset,
3368 mode,
3369 } => self.begin_columnar_selection(position, goal_column, reset, mode, window, cx),
3370 SelectPhase::Extend {
3371 position,
3372 click_count,
3373 } => self.extend_selection(position, click_count, window, cx),
3374 SelectPhase::Update {
3375 position,
3376 goal_column,
3377 scroll_delta,
3378 } => self.update_selection(position, goal_column, scroll_delta, window, cx),
3379 SelectPhase::End => self.end_selection(window, cx),
3380 }
3381 }
3382
3383 fn extend_selection(
3384 &mut self,
3385 position: DisplayPoint,
3386 click_count: usize,
3387 window: &mut Window,
3388 cx: &mut Context<Self>,
3389 ) {
3390 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
3391 let tail = self.selections.newest::<usize>(cx).tail();
3392 self.begin_selection(position, false, click_count, window, cx);
3393
3394 let position = position.to_offset(&display_map, Bias::Left);
3395 let tail_anchor = display_map.buffer_snapshot.anchor_before(tail);
3396
3397 let mut pending_selection = self
3398 .selections
3399 .pending_anchor()
3400 .expect("extend_selection not called with pending selection");
3401 if position >= tail {
3402 pending_selection.start = tail_anchor;
3403 } else {
3404 pending_selection.end = tail_anchor;
3405 pending_selection.reversed = true;
3406 }
3407
3408 let mut pending_mode = self.selections.pending_mode().unwrap();
3409 match &mut pending_mode {
3410 SelectMode::Word(range) | SelectMode::Line(range) => *range = tail_anchor..tail_anchor,
3411 _ => {}
3412 }
3413
3414 let effects = if EditorSettings::get_global(cx).autoscroll_on_clicks {
3415 SelectionEffects::scroll(Autoscroll::fit())
3416 } else {
3417 SelectionEffects::no_scroll()
3418 };
3419
3420 self.change_selections(effects, window, cx, |s| {
3421 s.set_pending(pending_selection, pending_mode)
3422 });
3423 }
3424
3425 fn begin_selection(
3426 &mut self,
3427 position: DisplayPoint,
3428 add: bool,
3429 click_count: usize,
3430 window: &mut Window,
3431 cx: &mut Context<Self>,
3432 ) {
3433 if !self.focus_handle.is_focused(window) {
3434 self.last_focused_descendant = None;
3435 window.focus(&self.focus_handle);
3436 }
3437
3438 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
3439 let buffer = &display_map.buffer_snapshot;
3440 let position = display_map.clip_point(position, Bias::Left);
3441
3442 let start;
3443 let end;
3444 let mode;
3445 let mut auto_scroll;
3446 match click_count {
3447 1 => {
3448 start = buffer.anchor_before(position.to_point(&display_map));
3449 end = start;
3450 mode = SelectMode::Character;
3451 auto_scroll = true;
3452 }
3453 2 => {
3454 let position = display_map
3455 .clip_point(position, Bias::Left)
3456 .to_offset(&display_map, Bias::Left);
3457 let (range, _) = buffer.surrounding_word(position, false);
3458 start = buffer.anchor_before(range.start);
3459 end = buffer.anchor_before(range.end);
3460 mode = SelectMode::Word(start..end);
3461 auto_scroll = true;
3462 }
3463 3 => {
3464 let position = display_map
3465 .clip_point(position, Bias::Left)
3466 .to_point(&display_map);
3467 let line_start = display_map.prev_line_boundary(position).0;
3468 let next_line_start = buffer.clip_point(
3469 display_map.next_line_boundary(position).0 + Point::new(1, 0),
3470 Bias::Left,
3471 );
3472 start = buffer.anchor_before(line_start);
3473 end = buffer.anchor_before(next_line_start);
3474 mode = SelectMode::Line(start..end);
3475 auto_scroll = true;
3476 }
3477 _ => {
3478 start = buffer.anchor_before(0);
3479 end = buffer.anchor_before(buffer.len());
3480 mode = SelectMode::All;
3481 auto_scroll = false;
3482 }
3483 }
3484 auto_scroll &= EditorSettings::get_global(cx).autoscroll_on_clicks;
3485
3486 let point_to_delete: Option<usize> = {
3487 let selected_points: Vec<Selection<Point>> =
3488 self.selections.disjoint_in_range(start..end, cx);
3489
3490 if !add || click_count > 1 {
3491 None
3492 } else if !selected_points.is_empty() {
3493 Some(selected_points[0].id)
3494 } else {
3495 let clicked_point_already_selected =
3496 self.selections.disjoint.iter().find(|selection| {
3497 selection.start.to_point(buffer) == start.to_point(buffer)
3498 || selection.end.to_point(buffer) == end.to_point(buffer)
3499 });
3500
3501 clicked_point_already_selected.map(|selection| selection.id)
3502 }
3503 };
3504
3505 let selections_count = self.selections.count();
3506 let effects = if auto_scroll {
3507 SelectionEffects::default()
3508 } else {
3509 SelectionEffects::no_scroll()
3510 };
3511
3512 self.change_selections(effects, window, cx, |s| {
3513 if let Some(point_to_delete) = point_to_delete {
3514 s.delete(point_to_delete);
3515
3516 if selections_count == 1 {
3517 s.set_pending_anchor_range(start..end, mode);
3518 }
3519 } else {
3520 if !add {
3521 s.clear_disjoint();
3522 }
3523
3524 s.set_pending_anchor_range(start..end, mode);
3525 }
3526 });
3527 }
3528
3529 fn begin_columnar_selection(
3530 &mut self,
3531 position: DisplayPoint,
3532 goal_column: u32,
3533 reset: bool,
3534 mode: ColumnarMode,
3535 window: &mut Window,
3536 cx: &mut Context<Self>,
3537 ) {
3538 if !self.focus_handle.is_focused(window) {
3539 self.last_focused_descendant = None;
3540 window.focus(&self.focus_handle);
3541 }
3542
3543 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
3544
3545 if reset {
3546 let pointer_position = display_map
3547 .buffer_snapshot
3548 .anchor_before(position.to_point(&display_map));
3549
3550 self.change_selections(
3551 SelectionEffects::scroll(Autoscroll::newest()),
3552 window,
3553 cx,
3554 |s| {
3555 s.clear_disjoint();
3556 s.set_pending_anchor_range(
3557 pointer_position..pointer_position,
3558 SelectMode::Character,
3559 );
3560 },
3561 );
3562 };
3563
3564 let tail = self.selections.newest::<Point>(cx).tail();
3565 let selection_anchor = display_map.buffer_snapshot.anchor_before(tail);
3566 self.columnar_selection_state = match mode {
3567 ColumnarMode::FromMouse => Some(ColumnarSelectionState::FromMouse {
3568 selection_tail: selection_anchor,
3569 display_point: if reset {
3570 if position.column() != goal_column {
3571 Some(DisplayPoint::new(position.row(), goal_column))
3572 } else {
3573 None
3574 }
3575 } else {
3576 None
3577 },
3578 }),
3579 ColumnarMode::FromSelection => Some(ColumnarSelectionState::FromSelection {
3580 selection_tail: selection_anchor,
3581 }),
3582 };
3583
3584 if !reset {
3585 self.select_columns(position, goal_column, &display_map, window, cx);
3586 }
3587 }
3588
3589 fn update_selection(
3590 &mut self,
3591 position: DisplayPoint,
3592 goal_column: u32,
3593 scroll_delta: gpui::Point<f32>,
3594 window: &mut Window,
3595 cx: &mut Context<Self>,
3596 ) {
3597 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
3598
3599 if self.columnar_selection_state.is_some() {
3600 self.select_columns(position, goal_column, &display_map, window, cx);
3601 } else if let Some(mut pending) = self.selections.pending_anchor() {
3602 let buffer = &display_map.buffer_snapshot;
3603 let head;
3604 let tail;
3605 let mode = self.selections.pending_mode().unwrap();
3606 match &mode {
3607 SelectMode::Character => {
3608 head = position.to_point(&display_map);
3609 tail = pending.tail().to_point(buffer);
3610 }
3611 SelectMode::Word(original_range) => {
3612 let offset = display_map
3613 .clip_point(position, Bias::Left)
3614 .to_offset(&display_map, Bias::Left);
3615 let original_range = original_range.to_offset(buffer);
3616
3617 let head_offset = if buffer.is_inside_word(offset, false)
3618 || original_range.contains(&offset)
3619 {
3620 let (word_range, _) = buffer.surrounding_word(offset, false);
3621 if word_range.start < original_range.start {
3622 word_range.start
3623 } else {
3624 word_range.end
3625 }
3626 } else {
3627 offset
3628 };
3629
3630 head = head_offset.to_point(buffer);
3631 if head_offset <= original_range.start {
3632 tail = original_range.end.to_point(buffer);
3633 } else {
3634 tail = original_range.start.to_point(buffer);
3635 }
3636 }
3637 SelectMode::Line(original_range) => {
3638 let original_range = original_range.to_point(&display_map.buffer_snapshot);
3639
3640 let position = display_map
3641 .clip_point(position, Bias::Left)
3642 .to_point(&display_map);
3643 let line_start = display_map.prev_line_boundary(position).0;
3644 let next_line_start = buffer.clip_point(
3645 display_map.next_line_boundary(position).0 + Point::new(1, 0),
3646 Bias::Left,
3647 );
3648
3649 if line_start < original_range.start {
3650 head = line_start
3651 } else {
3652 head = next_line_start
3653 }
3654
3655 if head <= original_range.start {
3656 tail = original_range.end;
3657 } else {
3658 tail = original_range.start;
3659 }
3660 }
3661 SelectMode::All => {
3662 return;
3663 }
3664 };
3665
3666 if head < tail {
3667 pending.start = buffer.anchor_before(head);
3668 pending.end = buffer.anchor_before(tail);
3669 pending.reversed = true;
3670 } else {
3671 pending.start = buffer.anchor_before(tail);
3672 pending.end = buffer.anchor_before(head);
3673 pending.reversed = false;
3674 }
3675
3676 self.change_selections(SelectionEffects::no_scroll(), window, cx, |s| {
3677 s.set_pending(pending, mode);
3678 });
3679 } else {
3680 log::error!("update_selection dispatched with no pending selection");
3681 return;
3682 }
3683
3684 self.apply_scroll_delta(scroll_delta, window, cx);
3685 cx.notify();
3686 }
3687
3688 fn end_selection(&mut self, window: &mut Window, cx: &mut Context<Self>) {
3689 self.columnar_selection_state.take();
3690 if self.selections.pending_anchor().is_some() {
3691 let selections = self.selections.all::<usize>(cx);
3692 self.change_selections(SelectionEffects::no_scroll(), window, cx, |s| {
3693 s.select(selections);
3694 s.clear_pending();
3695 });
3696 }
3697 }
3698
3699 fn select_columns(
3700 &mut self,
3701 head: DisplayPoint,
3702 goal_column: u32,
3703 display_map: &DisplaySnapshot,
3704 window: &mut Window,
3705 cx: &mut Context<Self>,
3706 ) {
3707 let Some(columnar_state) = self.columnar_selection_state.as_ref() else {
3708 return;
3709 };
3710
3711 let tail = match columnar_state {
3712 ColumnarSelectionState::FromMouse {
3713 selection_tail,
3714 display_point,
3715 } => display_point.unwrap_or_else(|| selection_tail.to_display_point(&display_map)),
3716 ColumnarSelectionState::FromSelection { selection_tail } => {
3717 selection_tail.to_display_point(&display_map)
3718 }
3719 };
3720
3721 let start_row = cmp::min(tail.row(), head.row());
3722 let end_row = cmp::max(tail.row(), head.row());
3723 let start_column = cmp::min(tail.column(), goal_column);
3724 let end_column = cmp::max(tail.column(), goal_column);
3725 let reversed = start_column < tail.column();
3726
3727 let selection_ranges = (start_row.0..=end_row.0)
3728 .map(DisplayRow)
3729 .filter_map(|row| {
3730 if (matches!(columnar_state, ColumnarSelectionState::FromMouse { .. })
3731 || start_column <= display_map.line_len(row))
3732 && !display_map.is_block_line(row)
3733 {
3734 let start = display_map
3735 .clip_point(DisplayPoint::new(row, start_column), Bias::Left)
3736 .to_point(display_map);
3737 let end = display_map
3738 .clip_point(DisplayPoint::new(row, end_column), Bias::Right)
3739 .to_point(display_map);
3740 if reversed {
3741 Some(end..start)
3742 } else {
3743 Some(start..end)
3744 }
3745 } else {
3746 None
3747 }
3748 })
3749 .collect::<Vec<_>>();
3750
3751 let ranges = match columnar_state {
3752 ColumnarSelectionState::FromMouse { .. } => {
3753 let mut non_empty_ranges = selection_ranges
3754 .iter()
3755 .filter(|selection_range| selection_range.start != selection_range.end)
3756 .peekable();
3757 if non_empty_ranges.peek().is_some() {
3758 non_empty_ranges.cloned().collect()
3759 } else {
3760 selection_ranges
3761 }
3762 }
3763 _ => selection_ranges,
3764 };
3765
3766 self.change_selections(SelectionEffects::no_scroll(), window, cx, |s| {
3767 s.select_ranges(ranges);
3768 });
3769 cx.notify();
3770 }
3771
3772 pub fn has_non_empty_selection(&self, cx: &mut App) -> bool {
3773 self.selections
3774 .all_adjusted(cx)
3775 .iter()
3776 .any(|selection| !selection.is_empty())
3777 }
3778
3779 pub fn has_pending_nonempty_selection(&self) -> bool {
3780 let pending_nonempty_selection = match self.selections.pending_anchor() {
3781 Some(Selection { start, end, .. }) => start != end,
3782 None => false,
3783 };
3784
3785 pending_nonempty_selection
3786 || (self.columnar_selection_state.is_some() && self.selections.disjoint.len() > 1)
3787 }
3788
3789 pub fn has_pending_selection(&self) -> bool {
3790 self.selections.pending_anchor().is_some() || self.columnar_selection_state.is_some()
3791 }
3792
3793 pub fn cancel(&mut self, _: &Cancel, window: &mut Window, cx: &mut Context<Self>) {
3794 self.selection_mark_mode = false;
3795 self.selection_drag_state = SelectionDragState::None;
3796
3797 if self.clear_expanded_diff_hunks(cx) {
3798 cx.notify();
3799 return;
3800 }
3801 if self.dismiss_menus_and_popups(true, window, cx) {
3802 return;
3803 }
3804
3805 if self.mode.is_full()
3806 && self.change_selections(Default::default(), window, cx, |s| s.try_cancel())
3807 {
3808 return;
3809 }
3810
3811 cx.propagate();
3812 }
3813
3814 pub fn dismiss_menus_and_popups(
3815 &mut self,
3816 is_user_requested: bool,
3817 window: &mut Window,
3818 cx: &mut Context<Self>,
3819 ) -> bool {
3820 if self.take_rename(false, window, cx).is_some() {
3821 return true;
3822 }
3823
3824 if hide_hover(self, cx) {
3825 return true;
3826 }
3827
3828 if self.hide_signature_help(cx, SignatureHelpHiddenBy::Escape) {
3829 return true;
3830 }
3831
3832 if self.hide_context_menu(window, cx).is_some() {
3833 return true;
3834 }
3835
3836 if self.mouse_context_menu.take().is_some() {
3837 return true;
3838 }
3839
3840 if is_user_requested && self.discard_inline_completion(true, cx) {
3841 return true;
3842 }
3843
3844 if self.snippet_stack.pop().is_some() {
3845 return true;
3846 }
3847
3848 if self.mode.is_full() && matches!(self.active_diagnostics, ActiveDiagnostic::Group(_)) {
3849 self.dismiss_diagnostics(cx);
3850 return true;
3851 }
3852
3853 false
3854 }
3855
3856 fn linked_editing_ranges_for(
3857 &self,
3858 selection: Range<text::Anchor>,
3859 cx: &App,
3860 ) -> Option<HashMap<Entity<Buffer>, Vec<Range<text::Anchor>>>> {
3861 if self.linked_edit_ranges.is_empty() {
3862 return None;
3863 }
3864 let ((base_range, linked_ranges), buffer_snapshot, buffer) =
3865 selection.end.buffer_id.and_then(|end_buffer_id| {
3866 if selection.start.buffer_id != Some(end_buffer_id) {
3867 return None;
3868 }
3869 let buffer = self.buffer.read(cx).buffer(end_buffer_id)?;
3870 let snapshot = buffer.read(cx).snapshot();
3871 self.linked_edit_ranges
3872 .get(end_buffer_id, selection.start..selection.end, &snapshot)
3873 .map(|ranges| (ranges, snapshot, buffer))
3874 })?;
3875 use text::ToOffset as TO;
3876 // find offset from the start of current range to current cursor position
3877 let start_byte_offset = TO::to_offset(&base_range.start, &buffer_snapshot);
3878
3879 let start_offset = TO::to_offset(&selection.start, &buffer_snapshot);
3880 let start_difference = start_offset - start_byte_offset;
3881 let end_offset = TO::to_offset(&selection.end, &buffer_snapshot);
3882 let end_difference = end_offset - start_byte_offset;
3883 // Current range has associated linked ranges.
3884 let mut linked_edits = HashMap::<_, Vec<_>>::default();
3885 for range in linked_ranges.iter() {
3886 let start_offset = TO::to_offset(&range.start, &buffer_snapshot);
3887 let end_offset = start_offset + end_difference;
3888 let start_offset = start_offset + start_difference;
3889 if start_offset > buffer_snapshot.len() || end_offset > buffer_snapshot.len() {
3890 continue;
3891 }
3892 if self.selections.disjoint_anchor_ranges().any(|s| {
3893 if s.start.buffer_id != selection.start.buffer_id
3894 || s.end.buffer_id != selection.end.buffer_id
3895 {
3896 return false;
3897 }
3898 TO::to_offset(&s.start.text_anchor, &buffer_snapshot) <= end_offset
3899 && TO::to_offset(&s.end.text_anchor, &buffer_snapshot) >= start_offset
3900 }) {
3901 continue;
3902 }
3903 let start = buffer_snapshot.anchor_after(start_offset);
3904 let end = buffer_snapshot.anchor_after(end_offset);
3905 linked_edits
3906 .entry(buffer.clone())
3907 .or_default()
3908 .push(start..end);
3909 }
3910 Some(linked_edits)
3911 }
3912
3913 pub fn handle_input(&mut self, text: &str, window: &mut Window, cx: &mut Context<Self>) {
3914 let text: Arc<str> = text.into();
3915
3916 if self.read_only(cx) {
3917 return;
3918 }
3919
3920 self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
3921
3922 let selections = self.selections.all_adjusted(cx);
3923 let mut bracket_inserted = false;
3924 let mut edits = Vec::new();
3925 let mut linked_edits = HashMap::<_, Vec<_>>::default();
3926 let mut new_selections = Vec::with_capacity(selections.len());
3927 let mut new_autoclose_regions = Vec::new();
3928 let snapshot = self.buffer.read(cx).read(cx);
3929 let mut clear_linked_edit_ranges = false;
3930
3931 for (selection, autoclose_region) in
3932 self.selections_with_autoclose_regions(selections, &snapshot)
3933 {
3934 if let Some(scope) = snapshot.language_scope_at(selection.head()) {
3935 // Determine if the inserted text matches the opening or closing
3936 // bracket of any of this language's bracket pairs.
3937 let mut bracket_pair = None;
3938 let mut is_bracket_pair_start = false;
3939 let mut is_bracket_pair_end = false;
3940 if !text.is_empty() {
3941 let mut bracket_pair_matching_end = None;
3942 // `text` can be empty when a user is using IME (e.g. Chinese Wubi Simplified)
3943 // and they are removing the character that triggered IME popup.
3944 for (pair, enabled) in scope.brackets() {
3945 if !pair.close && !pair.surround {
3946 continue;
3947 }
3948
3949 if enabled && pair.start.ends_with(text.as_ref()) {
3950 let prefix_len = pair.start.len() - text.len();
3951 let preceding_text_matches_prefix = prefix_len == 0
3952 || (selection.start.column >= (prefix_len as u32)
3953 && snapshot.contains_str_at(
3954 Point::new(
3955 selection.start.row,
3956 selection.start.column - (prefix_len as u32),
3957 ),
3958 &pair.start[..prefix_len],
3959 ));
3960 if preceding_text_matches_prefix {
3961 bracket_pair = Some(pair.clone());
3962 is_bracket_pair_start = true;
3963 break;
3964 }
3965 }
3966 if pair.end.as_str() == text.as_ref() && bracket_pair_matching_end.is_none()
3967 {
3968 // take first bracket pair matching end, but don't break in case a later bracket
3969 // pair matches start
3970 bracket_pair_matching_end = Some(pair.clone());
3971 }
3972 }
3973 if let Some(end) = bracket_pair_matching_end
3974 && bracket_pair.is_none()
3975 {
3976 bracket_pair = Some(end);
3977 is_bracket_pair_end = true;
3978 }
3979 }
3980
3981 if let Some(bracket_pair) = bracket_pair {
3982 let snapshot_settings = snapshot.language_settings_at(selection.start, cx);
3983 let autoclose = self.use_autoclose && snapshot_settings.use_autoclose;
3984 let auto_surround =
3985 self.use_auto_surround && snapshot_settings.use_auto_surround;
3986 if selection.is_empty() {
3987 if is_bracket_pair_start {
3988 // If the inserted text is a suffix of an opening bracket and the
3989 // selection is preceded by the rest of the opening bracket, then
3990 // insert the closing bracket.
3991 let following_text_allows_autoclose = snapshot
3992 .chars_at(selection.start)
3993 .next()
3994 .map_or(true, |c| scope.should_autoclose_before(c));
3995
3996 let preceding_text_allows_autoclose = selection.start.column == 0
3997 || snapshot.reversed_chars_at(selection.start).next().map_or(
3998 true,
3999 |c| {
4000 bracket_pair.start != bracket_pair.end
4001 || !snapshot
4002 .char_classifier_at(selection.start)
4003 .is_word(c)
4004 },
4005 );
4006
4007 let is_closing_quote = if bracket_pair.end == bracket_pair.start
4008 && bracket_pair.start.len() == 1
4009 {
4010 let target = bracket_pair.start.chars().next().unwrap();
4011 let current_line_count = snapshot
4012 .reversed_chars_at(selection.start)
4013 .take_while(|&c| c != '\n')
4014 .filter(|&c| c == target)
4015 .count();
4016 current_line_count % 2 == 1
4017 } else {
4018 false
4019 };
4020
4021 if autoclose
4022 && bracket_pair.close
4023 && following_text_allows_autoclose
4024 && preceding_text_allows_autoclose
4025 && !is_closing_quote
4026 {
4027 let anchor = snapshot.anchor_before(selection.end);
4028 new_selections.push((selection.map(|_| anchor), text.len()));
4029 new_autoclose_regions.push((
4030 anchor,
4031 text.len(),
4032 selection.id,
4033 bracket_pair.clone(),
4034 ));
4035 edits.push((
4036 selection.range(),
4037 format!("{}{}", text, bracket_pair.end).into(),
4038 ));
4039 bracket_inserted = true;
4040 continue;
4041 }
4042 }
4043
4044 if let Some(region) = autoclose_region {
4045 // If the selection is followed by an auto-inserted closing bracket,
4046 // then don't insert that closing bracket again; just move the selection
4047 // past the closing bracket.
4048 let should_skip = selection.end == region.range.end.to_point(&snapshot)
4049 && text.as_ref() == region.pair.end.as_str();
4050 if should_skip {
4051 let anchor = snapshot.anchor_after(selection.end);
4052 new_selections
4053 .push((selection.map(|_| anchor), region.pair.end.len()));
4054 continue;
4055 }
4056 }
4057
4058 let always_treat_brackets_as_autoclosed = snapshot
4059 .language_settings_at(selection.start, cx)
4060 .always_treat_brackets_as_autoclosed;
4061 if always_treat_brackets_as_autoclosed
4062 && is_bracket_pair_end
4063 && snapshot.contains_str_at(selection.end, text.as_ref())
4064 {
4065 // Otherwise, when `always_treat_brackets_as_autoclosed` is set to `true
4066 // and the inserted text is a closing bracket and the selection is followed
4067 // by the closing bracket then move the selection past the closing bracket.
4068 let anchor = snapshot.anchor_after(selection.end);
4069 new_selections.push((selection.map(|_| anchor), text.len()));
4070 continue;
4071 }
4072 }
4073 // If an opening bracket is 1 character long and is typed while
4074 // text is selected, then surround that text with the bracket pair.
4075 else if auto_surround
4076 && bracket_pair.surround
4077 && is_bracket_pair_start
4078 && bracket_pair.start.chars().count() == 1
4079 {
4080 edits.push((selection.start..selection.start, text.clone()));
4081 edits.push((
4082 selection.end..selection.end,
4083 bracket_pair.end.as_str().into(),
4084 ));
4085 bracket_inserted = true;
4086 new_selections.push((
4087 Selection {
4088 id: selection.id,
4089 start: snapshot.anchor_after(selection.start),
4090 end: snapshot.anchor_before(selection.end),
4091 reversed: selection.reversed,
4092 goal: selection.goal,
4093 },
4094 0,
4095 ));
4096 continue;
4097 }
4098 }
4099 }
4100
4101 if self.auto_replace_emoji_shortcode
4102 && selection.is_empty()
4103 && text.as_ref().ends_with(':')
4104 {
4105 if let Some(possible_emoji_short_code) =
4106 Self::find_possible_emoji_shortcode_at_position(&snapshot, selection.start)
4107 {
4108 if !possible_emoji_short_code.is_empty() {
4109 if let Some(emoji) = emojis::get_by_shortcode(&possible_emoji_short_code) {
4110 let emoji_shortcode_start = Point::new(
4111 selection.start.row,
4112 selection.start.column - possible_emoji_short_code.len() as u32 - 1,
4113 );
4114
4115 // Remove shortcode from buffer
4116 edits.push((
4117 emoji_shortcode_start..selection.start,
4118 "".to_string().into(),
4119 ));
4120 new_selections.push((
4121 Selection {
4122 id: selection.id,
4123 start: snapshot.anchor_after(emoji_shortcode_start),
4124 end: snapshot.anchor_before(selection.start),
4125 reversed: selection.reversed,
4126 goal: selection.goal,
4127 },
4128 0,
4129 ));
4130
4131 // Insert emoji
4132 let selection_start_anchor = snapshot.anchor_after(selection.start);
4133 new_selections.push((selection.map(|_| selection_start_anchor), 0));
4134 edits.push((selection.start..selection.end, emoji.to_string().into()));
4135
4136 continue;
4137 }
4138 }
4139 }
4140 }
4141
4142 // If not handling any auto-close operation, then just replace the selected
4143 // text with the given input and move the selection to the end of the
4144 // newly inserted text.
4145 let anchor = snapshot.anchor_after(selection.end);
4146 if !self.linked_edit_ranges.is_empty() {
4147 let start_anchor = snapshot.anchor_before(selection.start);
4148
4149 let is_word_char = text.chars().next().map_or(true, |char| {
4150 let classifier = snapshot
4151 .char_classifier_at(start_anchor.to_offset(&snapshot))
4152 .ignore_punctuation(true);
4153 classifier.is_word(char)
4154 });
4155
4156 if is_word_char {
4157 if let Some(ranges) = self
4158 .linked_editing_ranges_for(start_anchor.text_anchor..anchor.text_anchor, cx)
4159 {
4160 for (buffer, edits) in ranges {
4161 linked_edits
4162 .entry(buffer.clone())
4163 .or_default()
4164 .extend(edits.into_iter().map(|range| (range, text.clone())));
4165 }
4166 }
4167 } else {
4168 clear_linked_edit_ranges = true;
4169 }
4170 }
4171
4172 new_selections.push((selection.map(|_| anchor), 0));
4173 edits.push((selection.start..selection.end, text.clone()));
4174 }
4175
4176 drop(snapshot);
4177
4178 self.transact(window, cx, |this, window, cx| {
4179 if clear_linked_edit_ranges {
4180 this.linked_edit_ranges.clear();
4181 }
4182 let initial_buffer_versions =
4183 jsx_tag_auto_close::construct_initial_buffer_versions_map(this, &edits, cx);
4184
4185 this.buffer.update(cx, |buffer, cx| {
4186 buffer.edit(edits, this.autoindent_mode.clone(), cx);
4187 });
4188 for (buffer, edits) in linked_edits {
4189 buffer.update(cx, |buffer, cx| {
4190 let snapshot = buffer.snapshot();
4191 let edits = edits
4192 .into_iter()
4193 .map(|(range, text)| {
4194 use text::ToPoint as TP;
4195 let end_point = TP::to_point(&range.end, &snapshot);
4196 let start_point = TP::to_point(&range.start, &snapshot);
4197 (start_point..end_point, text)
4198 })
4199 .sorted_by_key(|(range, _)| range.start);
4200 buffer.edit(edits, None, cx);
4201 })
4202 }
4203 let new_anchor_selections = new_selections.iter().map(|e| &e.0);
4204 let new_selection_deltas = new_selections.iter().map(|e| e.1);
4205 let map = this.display_map.update(cx, |map, cx| map.snapshot(cx));
4206 let new_selections = resolve_selections::<usize, _>(new_anchor_selections, &map)
4207 .zip(new_selection_deltas)
4208 .map(|(selection, delta)| Selection {
4209 id: selection.id,
4210 start: selection.start + delta,
4211 end: selection.end + delta,
4212 reversed: selection.reversed,
4213 goal: SelectionGoal::None,
4214 })
4215 .collect::<Vec<_>>();
4216
4217 let mut i = 0;
4218 for (position, delta, selection_id, pair) in new_autoclose_regions {
4219 let position = position.to_offset(&map.buffer_snapshot) + delta;
4220 let start = map.buffer_snapshot.anchor_before(position);
4221 let end = map.buffer_snapshot.anchor_after(position);
4222 while let Some(existing_state) = this.autoclose_regions.get(i) {
4223 match existing_state.range.start.cmp(&start, &map.buffer_snapshot) {
4224 Ordering::Less => i += 1,
4225 Ordering::Greater => break,
4226 Ordering::Equal => {
4227 match end.cmp(&existing_state.range.end, &map.buffer_snapshot) {
4228 Ordering::Less => i += 1,
4229 Ordering::Equal => break,
4230 Ordering::Greater => break,
4231 }
4232 }
4233 }
4234 }
4235 this.autoclose_regions.insert(
4236 i,
4237 AutocloseRegion {
4238 selection_id,
4239 range: start..end,
4240 pair,
4241 },
4242 );
4243 }
4244
4245 let had_active_inline_completion = this.has_active_inline_completion();
4246 this.change_selections(
4247 SelectionEffects::scroll(Autoscroll::fit()).completions(false),
4248 window,
4249 cx,
4250 |s| s.select(new_selections),
4251 );
4252
4253 if !bracket_inserted {
4254 if let Some(on_type_format_task) =
4255 this.trigger_on_type_formatting(text.to_string(), window, cx)
4256 {
4257 on_type_format_task.detach_and_log_err(cx);
4258 }
4259 }
4260
4261 let editor_settings = EditorSettings::get_global(cx);
4262 if bracket_inserted
4263 && (editor_settings.auto_signature_help
4264 || editor_settings.show_signature_help_after_edits)
4265 {
4266 this.show_signature_help(&ShowSignatureHelp, window, cx);
4267 }
4268
4269 let trigger_in_words =
4270 this.show_edit_predictions_in_menu() || !had_active_inline_completion;
4271 if this.hard_wrap.is_some() {
4272 let latest: Range<Point> = this.selections.newest(cx).range();
4273 if latest.is_empty()
4274 && this
4275 .buffer()
4276 .read(cx)
4277 .snapshot(cx)
4278 .line_len(MultiBufferRow(latest.start.row))
4279 == latest.start.column
4280 {
4281 this.rewrap_impl(
4282 RewrapOptions {
4283 override_language_settings: true,
4284 preserve_existing_whitespace: true,
4285 },
4286 cx,
4287 )
4288 }
4289 }
4290 this.trigger_completion_on_input(&text, trigger_in_words, window, cx);
4291 linked_editing_ranges::refresh_linked_ranges(this, window, cx);
4292 this.refresh_inline_completion(true, false, window, cx);
4293 jsx_tag_auto_close::handle_from(this, initial_buffer_versions, window, cx);
4294 });
4295 }
4296
4297 fn find_possible_emoji_shortcode_at_position(
4298 snapshot: &MultiBufferSnapshot,
4299 position: Point,
4300 ) -> Option<String> {
4301 let mut chars = Vec::new();
4302 let mut found_colon = false;
4303 for char in snapshot.reversed_chars_at(position).take(100) {
4304 // Found a possible emoji shortcode in the middle of the buffer
4305 if found_colon {
4306 if char.is_whitespace() {
4307 chars.reverse();
4308 return Some(chars.iter().collect());
4309 }
4310 // If the previous character is not a whitespace, we are in the middle of a word
4311 // and we only want to complete the shortcode if the word is made up of other emojis
4312 let mut containing_word = String::new();
4313 for ch in snapshot
4314 .reversed_chars_at(position)
4315 .skip(chars.len() + 1)
4316 .take(100)
4317 {
4318 if ch.is_whitespace() {
4319 break;
4320 }
4321 containing_word.push(ch);
4322 }
4323 let containing_word = containing_word.chars().rev().collect::<String>();
4324 if util::word_consists_of_emojis(containing_word.as_str()) {
4325 chars.reverse();
4326 return Some(chars.iter().collect());
4327 }
4328 }
4329
4330 if char.is_whitespace() || !char.is_ascii() {
4331 return None;
4332 }
4333 if char == ':' {
4334 found_colon = true;
4335 } else {
4336 chars.push(char);
4337 }
4338 }
4339 // Found a possible emoji shortcode at the beginning of the buffer
4340 chars.reverse();
4341 Some(chars.iter().collect())
4342 }
4343
4344 pub fn newline(&mut self, _: &Newline, window: &mut Window, cx: &mut Context<Self>) {
4345 self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
4346 self.transact(window, cx, |this, window, cx| {
4347 let (edits_with_flags, selection_info): (Vec<_>, Vec<_>) = {
4348 let selections = this.selections.all::<usize>(cx);
4349 let multi_buffer = this.buffer.read(cx);
4350 let buffer = multi_buffer.snapshot(cx);
4351 selections
4352 .iter()
4353 .map(|selection| {
4354 let start_point = selection.start.to_point(&buffer);
4355 let mut existing_indent =
4356 buffer.indent_size_for_line(MultiBufferRow(start_point.row));
4357 existing_indent.len = cmp::min(existing_indent.len, start_point.column);
4358 let start = selection.start;
4359 let end = selection.end;
4360 let selection_is_empty = start == end;
4361 let language_scope = buffer.language_scope_at(start);
4362 let (
4363 comment_delimiter,
4364 doc_delimiter,
4365 insert_extra_newline,
4366 indent_on_newline,
4367 indent_on_extra_newline,
4368 ) = if let Some(language) = &language_scope {
4369 let mut insert_extra_newline =
4370 insert_extra_newline_brackets(&buffer, start..end, language)
4371 || insert_extra_newline_tree_sitter(&buffer, start..end);
4372
4373 // Comment extension on newline is allowed only for cursor selections
4374 let comment_delimiter = maybe!({
4375 if !selection_is_empty {
4376 return None;
4377 }
4378
4379 if !multi_buffer.language_settings(cx).extend_comment_on_newline {
4380 return None;
4381 }
4382
4383 let delimiters = language.line_comment_prefixes();
4384 let max_len_of_delimiter =
4385 delimiters.iter().map(|delimiter| delimiter.len()).max()?;
4386 let (snapshot, range) =
4387 buffer.buffer_line_for_row(MultiBufferRow(start_point.row))?;
4388
4389 let num_of_whitespaces = snapshot
4390 .chars_for_range(range.clone())
4391 .take_while(|c| c.is_whitespace())
4392 .count();
4393 let comment_candidate = snapshot
4394 .chars_for_range(range.clone())
4395 .skip(num_of_whitespaces)
4396 .take(max_len_of_delimiter)
4397 .collect::<String>();
4398 let (delimiter, trimmed_len) = delimiters
4399 .iter()
4400 .filter_map(|delimiter| {
4401 let prefix = delimiter.trim_end();
4402 if comment_candidate.starts_with(prefix) {
4403 Some((delimiter, prefix.len()))
4404 } else {
4405 None
4406 }
4407 })
4408 .max_by_key(|(_, len)| *len)?;
4409
4410 if let Some((block_start, _)) = language.block_comment_delimiters()
4411 {
4412 let block_start_trimmed = block_start.trim_end();
4413 if block_start_trimmed.starts_with(delimiter.trim_end()) {
4414 let line_content = snapshot
4415 .chars_for_range(range)
4416 .skip(num_of_whitespaces)
4417 .take(block_start_trimmed.len())
4418 .collect::<String>();
4419
4420 if line_content.starts_with(block_start_trimmed) {
4421 return None;
4422 }
4423 }
4424 }
4425
4426 let cursor_is_placed_after_comment_marker =
4427 num_of_whitespaces + trimmed_len <= start_point.column as usize;
4428 if cursor_is_placed_after_comment_marker {
4429 Some(delimiter.clone())
4430 } else {
4431 None
4432 }
4433 });
4434
4435 let mut indent_on_newline = IndentSize::spaces(0);
4436 let mut indent_on_extra_newline = IndentSize::spaces(0);
4437
4438 let doc_delimiter = maybe!({
4439 if !selection_is_empty {
4440 return None;
4441 }
4442
4443 if !multi_buffer.language_settings(cx).extend_comment_on_newline {
4444 return None;
4445 }
4446
4447 let DocumentationConfig {
4448 start: start_tag,
4449 end: end_tag,
4450 prefix: delimiter,
4451 tab_size: len,
4452 } = language.documentation()?;
4453
4454 let is_within_block_comment = buffer
4455 .language_scope_at(start_point)
4456 .is_some_and(|scope| scope.override_name() == Some("comment"));
4457 if !is_within_block_comment {
4458 return None;
4459 }
4460
4461 let (snapshot, range) =
4462 buffer.buffer_line_for_row(MultiBufferRow(start_point.row))?;
4463
4464 let num_of_whitespaces = snapshot
4465 .chars_for_range(range.clone())
4466 .take_while(|c| c.is_whitespace())
4467 .count();
4468
4469 // 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.
4470 let column = start_point.column;
4471 let cursor_is_after_start_tag = {
4472 let start_tag_len = start_tag.len();
4473 let start_tag_line = snapshot
4474 .chars_for_range(range.clone())
4475 .skip(num_of_whitespaces)
4476 .take(start_tag_len)
4477 .collect::<String>();
4478 if start_tag_line.starts_with(start_tag.as_ref()) {
4479 num_of_whitespaces + start_tag_len <= column as usize
4480 } else {
4481 false
4482 }
4483 };
4484
4485 let cursor_is_after_delimiter = {
4486 let delimiter_trim = delimiter.trim_end();
4487 let delimiter_line = snapshot
4488 .chars_for_range(range.clone())
4489 .skip(num_of_whitespaces)
4490 .take(delimiter_trim.len())
4491 .collect::<String>();
4492 if delimiter_line.starts_with(delimiter_trim) {
4493 num_of_whitespaces + delimiter_trim.len() <= column as usize
4494 } else {
4495 false
4496 }
4497 };
4498
4499 let cursor_is_before_end_tag_if_exists = {
4500 let mut char_position = 0u32;
4501 let mut end_tag_offset = None;
4502
4503 'outer: for chunk in snapshot.text_for_range(range.clone()) {
4504 if let Some(byte_pos) = chunk.find(&**end_tag) {
4505 let chars_before_match =
4506 chunk[..byte_pos].chars().count() as u32;
4507 end_tag_offset =
4508 Some(char_position + chars_before_match);
4509 break 'outer;
4510 }
4511 char_position += chunk.chars().count() as u32;
4512 }
4513
4514 if let Some(end_tag_offset) = end_tag_offset {
4515 let cursor_is_before_end_tag = column <= end_tag_offset;
4516 if cursor_is_after_start_tag {
4517 if cursor_is_before_end_tag {
4518 insert_extra_newline = true;
4519 }
4520 let cursor_is_at_start_of_end_tag =
4521 column == end_tag_offset;
4522 if cursor_is_at_start_of_end_tag {
4523 indent_on_extra_newline.len = (*len).into();
4524 }
4525 }
4526 cursor_is_before_end_tag
4527 } else {
4528 true
4529 }
4530 };
4531
4532 if (cursor_is_after_start_tag || cursor_is_after_delimiter)
4533 && cursor_is_before_end_tag_if_exists
4534 {
4535 if cursor_is_after_start_tag {
4536 indent_on_newline.len = (*len).into();
4537 }
4538 Some(delimiter.clone())
4539 } else {
4540 None
4541 }
4542 });
4543
4544 (
4545 comment_delimiter,
4546 doc_delimiter,
4547 insert_extra_newline,
4548 indent_on_newline,
4549 indent_on_extra_newline,
4550 )
4551 } else {
4552 (
4553 None,
4554 None,
4555 false,
4556 IndentSize::default(),
4557 IndentSize::default(),
4558 )
4559 };
4560
4561 let prevent_auto_indent = doc_delimiter.is_some();
4562 let delimiter = comment_delimiter.or(doc_delimiter);
4563
4564 let capacity_for_delimiter =
4565 delimiter.as_deref().map(str::len).unwrap_or_default();
4566 let mut new_text = String::with_capacity(
4567 1 + capacity_for_delimiter
4568 + existing_indent.len as usize
4569 + indent_on_newline.len as usize
4570 + indent_on_extra_newline.len as usize,
4571 );
4572 new_text.push('\n');
4573 new_text.extend(existing_indent.chars());
4574 new_text.extend(indent_on_newline.chars());
4575
4576 if let Some(delimiter) = &delimiter {
4577 new_text.push_str(delimiter);
4578 }
4579
4580 if insert_extra_newline {
4581 new_text.push('\n');
4582 new_text.extend(existing_indent.chars());
4583 new_text.extend(indent_on_extra_newline.chars());
4584 }
4585
4586 let anchor = buffer.anchor_after(end);
4587 let new_selection = selection.map(|_| anchor);
4588 (
4589 ((start..end, new_text), prevent_auto_indent),
4590 (insert_extra_newline, new_selection),
4591 )
4592 })
4593 .unzip()
4594 };
4595
4596 let mut auto_indent_edits = Vec::new();
4597 let mut edits = Vec::new();
4598 for (edit, prevent_auto_indent) in edits_with_flags {
4599 if prevent_auto_indent {
4600 edits.push(edit);
4601 } else {
4602 auto_indent_edits.push(edit);
4603 }
4604 }
4605 if !edits.is_empty() {
4606 this.edit(edits, cx);
4607 }
4608 if !auto_indent_edits.is_empty() {
4609 this.edit_with_autoindent(auto_indent_edits, cx);
4610 }
4611
4612 let buffer = this.buffer.read(cx).snapshot(cx);
4613 let new_selections = selection_info
4614 .into_iter()
4615 .map(|(extra_newline_inserted, new_selection)| {
4616 let mut cursor = new_selection.end.to_point(&buffer);
4617 if extra_newline_inserted {
4618 cursor.row -= 1;
4619 cursor.column = buffer.line_len(MultiBufferRow(cursor.row));
4620 }
4621 new_selection.map(|_| cursor)
4622 })
4623 .collect();
4624
4625 this.change_selections(Default::default(), window, cx, |s| s.select(new_selections));
4626 this.refresh_inline_completion(true, false, window, cx);
4627 });
4628 }
4629
4630 pub fn newline_above(&mut self, _: &NewlineAbove, window: &mut Window, cx: &mut Context<Self>) {
4631 self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
4632
4633 let buffer = self.buffer.read(cx);
4634 let snapshot = buffer.snapshot(cx);
4635
4636 let mut edits = Vec::new();
4637 let mut rows = Vec::new();
4638
4639 for (rows_inserted, selection) in self.selections.all_adjusted(cx).into_iter().enumerate() {
4640 let cursor = selection.head();
4641 let row = cursor.row;
4642
4643 let start_of_line = snapshot.clip_point(Point::new(row, 0), Bias::Left);
4644
4645 let newline = "\n".to_string();
4646 edits.push((start_of_line..start_of_line, newline));
4647
4648 rows.push(row + rows_inserted as u32);
4649 }
4650
4651 self.transact(window, cx, |editor, window, cx| {
4652 editor.edit(edits, cx);
4653
4654 editor.change_selections(Default::default(), window, cx, |s| {
4655 let mut index = 0;
4656 s.move_cursors_with(|map, _, _| {
4657 let row = rows[index];
4658 index += 1;
4659
4660 let point = Point::new(row, 0);
4661 let boundary = map.next_line_boundary(point).1;
4662 let clipped = map.clip_point(boundary, Bias::Left);
4663
4664 (clipped, SelectionGoal::None)
4665 });
4666 });
4667
4668 let mut indent_edits = Vec::new();
4669 let multibuffer_snapshot = editor.buffer.read(cx).snapshot(cx);
4670 for row in rows {
4671 let indents = multibuffer_snapshot.suggested_indents(row..row + 1, cx);
4672 for (row, indent) in indents {
4673 if indent.len == 0 {
4674 continue;
4675 }
4676
4677 let text = match indent.kind {
4678 IndentKind::Space => " ".repeat(indent.len as usize),
4679 IndentKind::Tab => "\t".repeat(indent.len as usize),
4680 };
4681 let point = Point::new(row.0, 0);
4682 indent_edits.push((point..point, text));
4683 }
4684 }
4685 editor.edit(indent_edits, cx);
4686 });
4687 }
4688
4689 pub fn newline_below(&mut self, _: &NewlineBelow, window: &mut Window, cx: &mut Context<Self>) {
4690 self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
4691
4692 let buffer = self.buffer.read(cx);
4693 let snapshot = buffer.snapshot(cx);
4694
4695 let mut edits = Vec::new();
4696 let mut rows = Vec::new();
4697 let mut rows_inserted = 0;
4698
4699 for selection in self.selections.all_adjusted(cx) {
4700 let cursor = selection.head();
4701 let row = cursor.row;
4702
4703 let point = Point::new(row + 1, 0);
4704 let start_of_line = snapshot.clip_point(point, Bias::Left);
4705
4706 let newline = "\n".to_string();
4707 edits.push((start_of_line..start_of_line, newline));
4708
4709 rows_inserted += 1;
4710 rows.push(row + rows_inserted);
4711 }
4712
4713 self.transact(window, cx, |editor, window, cx| {
4714 editor.edit(edits, cx);
4715
4716 editor.change_selections(Default::default(), window, cx, |s| {
4717 let mut index = 0;
4718 s.move_cursors_with(|map, _, _| {
4719 let row = rows[index];
4720 index += 1;
4721
4722 let point = Point::new(row, 0);
4723 let boundary = map.next_line_boundary(point).1;
4724 let clipped = map.clip_point(boundary, Bias::Left);
4725
4726 (clipped, SelectionGoal::None)
4727 });
4728 });
4729
4730 let mut indent_edits = Vec::new();
4731 let multibuffer_snapshot = editor.buffer.read(cx).snapshot(cx);
4732 for row in rows {
4733 let indents = multibuffer_snapshot.suggested_indents(row..row + 1, cx);
4734 for (row, indent) in indents {
4735 if indent.len == 0 {
4736 continue;
4737 }
4738
4739 let text = match indent.kind {
4740 IndentKind::Space => " ".repeat(indent.len as usize),
4741 IndentKind::Tab => "\t".repeat(indent.len as usize),
4742 };
4743 let point = Point::new(row.0, 0);
4744 indent_edits.push((point..point, text));
4745 }
4746 }
4747 editor.edit(indent_edits, cx);
4748 });
4749 }
4750
4751 pub fn insert(&mut self, text: &str, window: &mut Window, cx: &mut Context<Self>) {
4752 let autoindent = text.is_empty().not().then(|| AutoindentMode::Block {
4753 original_indent_columns: Vec::new(),
4754 });
4755 self.insert_with_autoindent_mode(text, autoindent, window, cx);
4756 }
4757
4758 fn insert_with_autoindent_mode(
4759 &mut self,
4760 text: &str,
4761 autoindent_mode: Option<AutoindentMode>,
4762 window: &mut Window,
4763 cx: &mut Context<Self>,
4764 ) {
4765 if self.read_only(cx) {
4766 return;
4767 }
4768
4769 let text: Arc<str> = text.into();
4770 self.transact(window, cx, |this, window, cx| {
4771 let old_selections = this.selections.all_adjusted(cx);
4772 let selection_anchors = this.buffer.update(cx, |buffer, cx| {
4773 let anchors = {
4774 let snapshot = buffer.read(cx);
4775 old_selections
4776 .iter()
4777 .map(|s| {
4778 let anchor = snapshot.anchor_after(s.head());
4779 s.map(|_| anchor)
4780 })
4781 .collect::<Vec<_>>()
4782 };
4783 buffer.edit(
4784 old_selections
4785 .iter()
4786 .map(|s| (s.start..s.end, text.clone())),
4787 autoindent_mode,
4788 cx,
4789 );
4790 anchors
4791 });
4792
4793 this.change_selections(Default::default(), window, cx, |s| {
4794 s.select_anchors(selection_anchors);
4795 });
4796
4797 cx.notify();
4798 });
4799 }
4800
4801 fn trigger_completion_on_input(
4802 &mut self,
4803 text: &str,
4804 trigger_in_words: bool,
4805 window: &mut Window,
4806 cx: &mut Context<Self>,
4807 ) {
4808 let completions_source = self
4809 .context_menu
4810 .borrow()
4811 .as_ref()
4812 .and_then(|menu| match menu {
4813 CodeContextMenu::Completions(completions_menu) => Some(completions_menu.source),
4814 CodeContextMenu::CodeActions(_) => None,
4815 });
4816
4817 match completions_source {
4818 Some(CompletionsMenuSource::Words) => {
4819 self.show_word_completions(&ShowWordCompletions, window, cx)
4820 }
4821 Some(CompletionsMenuSource::Normal)
4822 | Some(CompletionsMenuSource::SnippetChoices)
4823 | None
4824 if self.is_completion_trigger(
4825 text,
4826 trigger_in_words,
4827 completions_source.is_some(),
4828 cx,
4829 ) =>
4830 {
4831 self.show_completions(
4832 &ShowCompletions {
4833 trigger: Some(text.to_owned()).filter(|x| !x.is_empty()),
4834 },
4835 window,
4836 cx,
4837 )
4838 }
4839 _ => {
4840 self.hide_context_menu(window, cx);
4841 }
4842 }
4843 }
4844
4845 fn is_completion_trigger(
4846 &self,
4847 text: &str,
4848 trigger_in_words: bool,
4849 menu_is_open: bool,
4850 cx: &mut Context<Self>,
4851 ) -> bool {
4852 let position = self.selections.newest_anchor().head();
4853 let multibuffer = self.buffer.read(cx);
4854 let Some(buffer) = position
4855 .buffer_id
4856 .and_then(|buffer_id| multibuffer.buffer(buffer_id).clone())
4857 else {
4858 return false;
4859 };
4860
4861 if let Some(completion_provider) = &self.completion_provider {
4862 completion_provider.is_completion_trigger(
4863 &buffer,
4864 position.text_anchor,
4865 text,
4866 trigger_in_words,
4867 menu_is_open,
4868 cx,
4869 )
4870 } else {
4871 false
4872 }
4873 }
4874
4875 /// If any empty selections is touching the start of its innermost containing autoclose
4876 /// region, expand it to select the brackets.
4877 fn select_autoclose_pair(&mut self, window: &mut Window, cx: &mut Context<Self>) {
4878 let selections = self.selections.all::<usize>(cx);
4879 let buffer = self.buffer.read(cx).read(cx);
4880 let new_selections = self
4881 .selections_with_autoclose_regions(selections, &buffer)
4882 .map(|(mut selection, region)| {
4883 if !selection.is_empty() {
4884 return selection;
4885 }
4886
4887 if let Some(region) = region {
4888 let mut range = region.range.to_offset(&buffer);
4889 if selection.start == range.start && range.start >= region.pair.start.len() {
4890 range.start -= region.pair.start.len();
4891 if buffer.contains_str_at(range.start, ®ion.pair.start)
4892 && buffer.contains_str_at(range.end, ®ion.pair.end)
4893 {
4894 range.end += region.pair.end.len();
4895 selection.start = range.start;
4896 selection.end = range.end;
4897
4898 return selection;
4899 }
4900 }
4901 }
4902
4903 let always_treat_brackets_as_autoclosed = buffer
4904 .language_settings_at(selection.start, cx)
4905 .always_treat_brackets_as_autoclosed;
4906
4907 if !always_treat_brackets_as_autoclosed {
4908 return selection;
4909 }
4910
4911 if let Some(scope) = buffer.language_scope_at(selection.start) {
4912 for (pair, enabled) in scope.brackets() {
4913 if !enabled || !pair.close {
4914 continue;
4915 }
4916
4917 if buffer.contains_str_at(selection.start, &pair.end) {
4918 let pair_start_len = pair.start.len();
4919 if buffer.contains_str_at(
4920 selection.start.saturating_sub(pair_start_len),
4921 &pair.start,
4922 ) {
4923 selection.start -= pair_start_len;
4924 selection.end += pair.end.len();
4925
4926 return selection;
4927 }
4928 }
4929 }
4930 }
4931
4932 selection
4933 })
4934 .collect();
4935
4936 drop(buffer);
4937 self.change_selections(SelectionEffects::no_scroll(), window, cx, |selections| {
4938 selections.select(new_selections)
4939 });
4940 }
4941
4942 /// Iterate the given selections, and for each one, find the smallest surrounding
4943 /// autoclose region. This uses the ordering of the selections and the autoclose
4944 /// regions to avoid repeated comparisons.
4945 fn selections_with_autoclose_regions<'a, D: ToOffset + Clone>(
4946 &'a self,
4947 selections: impl IntoIterator<Item = Selection<D>>,
4948 buffer: &'a MultiBufferSnapshot,
4949 ) -> impl Iterator<Item = (Selection<D>, Option<&'a AutocloseRegion>)> {
4950 let mut i = 0;
4951 let mut regions = self.autoclose_regions.as_slice();
4952 selections.into_iter().map(move |selection| {
4953 let range = selection.start.to_offset(buffer)..selection.end.to_offset(buffer);
4954
4955 let mut enclosing = None;
4956 while let Some(pair_state) = regions.get(i) {
4957 if pair_state.range.end.to_offset(buffer) < range.start {
4958 regions = ®ions[i + 1..];
4959 i = 0;
4960 } else if pair_state.range.start.to_offset(buffer) > range.end {
4961 break;
4962 } else {
4963 if pair_state.selection_id == selection.id {
4964 enclosing = Some(pair_state);
4965 }
4966 i += 1;
4967 }
4968 }
4969
4970 (selection, enclosing)
4971 })
4972 }
4973
4974 /// Remove any autoclose regions that no longer contain their selection.
4975 fn invalidate_autoclose_regions(
4976 &mut self,
4977 mut selections: &[Selection<Anchor>],
4978 buffer: &MultiBufferSnapshot,
4979 ) {
4980 self.autoclose_regions.retain(|state| {
4981 let mut i = 0;
4982 while let Some(selection) = selections.get(i) {
4983 if selection.end.cmp(&state.range.start, buffer).is_lt() {
4984 selections = &selections[1..];
4985 continue;
4986 }
4987 if selection.start.cmp(&state.range.end, buffer).is_gt() {
4988 break;
4989 }
4990 if selection.id == state.selection_id {
4991 return true;
4992 } else {
4993 i += 1;
4994 }
4995 }
4996 false
4997 });
4998 }
4999
5000 fn completion_query(buffer: &MultiBufferSnapshot, position: impl ToOffset) -> Option<String> {
5001 let offset = position.to_offset(buffer);
5002 let (word_range, kind) = buffer.surrounding_word(offset, true);
5003 if offset > word_range.start && kind == Some(CharKind::Word) {
5004 Some(
5005 buffer
5006 .text_for_range(word_range.start..offset)
5007 .collect::<String>(),
5008 )
5009 } else {
5010 None
5011 }
5012 }
5013
5014 pub fn toggle_inline_values(
5015 &mut self,
5016 _: &ToggleInlineValues,
5017 _: &mut Window,
5018 cx: &mut Context<Self>,
5019 ) {
5020 self.inline_value_cache.enabled = !self.inline_value_cache.enabled;
5021
5022 self.refresh_inline_values(cx);
5023 }
5024
5025 pub fn toggle_inlay_hints(
5026 &mut self,
5027 _: &ToggleInlayHints,
5028 _: &mut Window,
5029 cx: &mut Context<Self>,
5030 ) {
5031 self.refresh_inlay_hints(
5032 InlayHintRefreshReason::Toggle(!self.inlay_hints_enabled()),
5033 cx,
5034 );
5035 }
5036
5037 pub fn inlay_hints_enabled(&self) -> bool {
5038 self.inlay_hint_cache.enabled
5039 }
5040
5041 pub fn inline_values_enabled(&self) -> bool {
5042 self.inline_value_cache.enabled
5043 }
5044
5045 #[cfg(any(test, feature = "test-support"))]
5046 pub fn inline_value_inlays(&self, cx: &App) -> Vec<Inlay> {
5047 self.display_map
5048 .read(cx)
5049 .current_inlays()
5050 .filter(|inlay| matches!(inlay.id, InlayId::DebuggerValue(_)))
5051 .cloned()
5052 .collect()
5053 }
5054
5055 #[cfg(any(test, feature = "test-support"))]
5056 pub fn all_inlays(&self, cx: &App) -> Vec<Inlay> {
5057 self.display_map
5058 .read(cx)
5059 .current_inlays()
5060 .cloned()
5061 .collect()
5062 }
5063
5064 fn refresh_inlay_hints(&mut self, reason: InlayHintRefreshReason, cx: &mut Context<Self>) {
5065 if self.semantics_provider.is_none() || !self.mode.is_full() {
5066 return;
5067 }
5068
5069 let reason_description = reason.description();
5070 let ignore_debounce = matches!(
5071 reason,
5072 InlayHintRefreshReason::SettingsChange(_)
5073 | InlayHintRefreshReason::Toggle(_)
5074 | InlayHintRefreshReason::ExcerptsRemoved(_)
5075 | InlayHintRefreshReason::ModifiersChanged(_)
5076 );
5077 let (invalidate_cache, required_languages) = match reason {
5078 InlayHintRefreshReason::ModifiersChanged(enabled) => {
5079 match self.inlay_hint_cache.modifiers_override(enabled) {
5080 Some(enabled) => {
5081 if enabled {
5082 (InvalidationStrategy::RefreshRequested, None)
5083 } else {
5084 self.splice_inlays(
5085 &self
5086 .visible_inlay_hints(cx)
5087 .iter()
5088 .map(|inlay| inlay.id)
5089 .collect::<Vec<InlayId>>(),
5090 Vec::new(),
5091 cx,
5092 );
5093 return;
5094 }
5095 }
5096 None => return,
5097 }
5098 }
5099 InlayHintRefreshReason::Toggle(enabled) => {
5100 if self.inlay_hint_cache.toggle(enabled) {
5101 if enabled {
5102 (InvalidationStrategy::RefreshRequested, None)
5103 } else {
5104 self.splice_inlays(
5105 &self
5106 .visible_inlay_hints(cx)
5107 .iter()
5108 .map(|inlay| inlay.id)
5109 .collect::<Vec<InlayId>>(),
5110 Vec::new(),
5111 cx,
5112 );
5113 return;
5114 }
5115 } else {
5116 return;
5117 }
5118 }
5119 InlayHintRefreshReason::SettingsChange(new_settings) => {
5120 match self.inlay_hint_cache.update_settings(
5121 &self.buffer,
5122 new_settings,
5123 self.visible_inlay_hints(cx),
5124 cx,
5125 ) {
5126 ControlFlow::Break(Some(InlaySplice {
5127 to_remove,
5128 to_insert,
5129 })) => {
5130 self.splice_inlays(&to_remove, to_insert, cx);
5131 return;
5132 }
5133 ControlFlow::Break(None) => return,
5134 ControlFlow::Continue(()) => (InvalidationStrategy::RefreshRequested, None),
5135 }
5136 }
5137 InlayHintRefreshReason::ExcerptsRemoved(excerpts_removed) => {
5138 if let Some(InlaySplice {
5139 to_remove,
5140 to_insert,
5141 }) = self.inlay_hint_cache.remove_excerpts(&excerpts_removed)
5142 {
5143 self.splice_inlays(&to_remove, to_insert, cx);
5144 }
5145 self.display_map.update(cx, |display_map, _| {
5146 display_map.remove_inlays_for_excerpts(&excerpts_removed)
5147 });
5148 return;
5149 }
5150 InlayHintRefreshReason::NewLinesShown => (InvalidationStrategy::None, None),
5151 InlayHintRefreshReason::BufferEdited(buffer_languages) => {
5152 (InvalidationStrategy::BufferEdited, Some(buffer_languages))
5153 }
5154 InlayHintRefreshReason::RefreshRequested => {
5155 (InvalidationStrategy::RefreshRequested, None)
5156 }
5157 };
5158
5159 if let Some(InlaySplice {
5160 to_remove,
5161 to_insert,
5162 }) = self.inlay_hint_cache.spawn_hint_refresh(
5163 reason_description,
5164 self.visible_excerpts(required_languages.as_ref(), cx),
5165 invalidate_cache,
5166 ignore_debounce,
5167 cx,
5168 ) {
5169 self.splice_inlays(&to_remove, to_insert, cx);
5170 }
5171 }
5172
5173 fn visible_inlay_hints(&self, cx: &Context<Editor>) -> Vec<Inlay> {
5174 self.display_map
5175 .read(cx)
5176 .current_inlays()
5177 .filter(move |inlay| matches!(inlay.id, InlayId::Hint(_)))
5178 .cloned()
5179 .collect()
5180 }
5181
5182 pub fn visible_excerpts(
5183 &self,
5184 restrict_to_languages: Option<&HashSet<Arc<Language>>>,
5185 cx: &mut Context<Editor>,
5186 ) -> HashMap<ExcerptId, (Entity<Buffer>, clock::Global, Range<usize>)> {
5187 let Some(project) = self.project.as_ref() else {
5188 return HashMap::default();
5189 };
5190 let project = project.read(cx);
5191 let multi_buffer = self.buffer().read(cx);
5192 let multi_buffer_snapshot = multi_buffer.snapshot(cx);
5193 let multi_buffer_visible_start = self
5194 .scroll_manager
5195 .anchor()
5196 .anchor
5197 .to_point(&multi_buffer_snapshot);
5198 let multi_buffer_visible_end = multi_buffer_snapshot.clip_point(
5199 multi_buffer_visible_start
5200 + Point::new(self.visible_line_count().unwrap_or(0.).ceil() as u32, 0),
5201 Bias::Left,
5202 );
5203 let multi_buffer_visible_range = multi_buffer_visible_start..multi_buffer_visible_end;
5204 multi_buffer_snapshot
5205 .range_to_buffer_ranges(multi_buffer_visible_range)
5206 .into_iter()
5207 .filter(|(_, excerpt_visible_range, _)| !excerpt_visible_range.is_empty())
5208 .filter_map(|(buffer, excerpt_visible_range, excerpt_id)| {
5209 let buffer_file = project::File::from_dyn(buffer.file())?;
5210 let buffer_worktree = project.worktree_for_id(buffer_file.worktree_id(cx), cx)?;
5211 let worktree_entry = buffer_worktree
5212 .read(cx)
5213 .entry_for_id(buffer_file.project_entry_id(cx)?)?;
5214 if worktree_entry.is_ignored {
5215 return None;
5216 }
5217
5218 let language = buffer.language()?;
5219 if let Some(restrict_to_languages) = restrict_to_languages {
5220 if !restrict_to_languages.contains(language) {
5221 return None;
5222 }
5223 }
5224 Some((
5225 excerpt_id,
5226 (
5227 multi_buffer.buffer(buffer.remote_id()).unwrap(),
5228 buffer.version().clone(),
5229 excerpt_visible_range,
5230 ),
5231 ))
5232 })
5233 .collect()
5234 }
5235
5236 pub fn text_layout_details(&self, window: &mut Window) -> TextLayoutDetails {
5237 TextLayoutDetails {
5238 text_system: window.text_system().clone(),
5239 editor_style: self.style.clone().unwrap(),
5240 rem_size: window.rem_size(),
5241 scroll_anchor: self.scroll_manager.anchor(),
5242 visible_rows: self.visible_line_count(),
5243 vertical_scroll_margin: self.scroll_manager.vertical_scroll_margin,
5244 }
5245 }
5246
5247 pub fn splice_inlays(
5248 &self,
5249 to_remove: &[InlayId],
5250 to_insert: Vec<Inlay>,
5251 cx: &mut Context<Self>,
5252 ) {
5253 self.display_map.update(cx, |display_map, cx| {
5254 display_map.splice_inlays(to_remove, to_insert, cx)
5255 });
5256 cx.notify();
5257 }
5258
5259 fn trigger_on_type_formatting(
5260 &self,
5261 input: String,
5262 window: &mut Window,
5263 cx: &mut Context<Self>,
5264 ) -> Option<Task<Result<()>>> {
5265 if input.len() != 1 {
5266 return None;
5267 }
5268
5269 let project = self.project.as_ref()?;
5270 let position = self.selections.newest_anchor().head();
5271 let (buffer, buffer_position) = self
5272 .buffer
5273 .read(cx)
5274 .text_anchor_for_position(position, cx)?;
5275
5276 let settings = language_settings::language_settings(
5277 buffer
5278 .read(cx)
5279 .language_at(buffer_position)
5280 .map(|l| l.name()),
5281 buffer.read(cx).file(),
5282 cx,
5283 );
5284 if !settings.use_on_type_format {
5285 return None;
5286 }
5287
5288 // OnTypeFormatting returns a list of edits, no need to pass them between Zed instances,
5289 // hence we do LSP request & edit on host side only — add formats to host's history.
5290 let push_to_lsp_host_history = true;
5291 // If this is not the host, append its history with new edits.
5292 let push_to_client_history = project.read(cx).is_via_collab();
5293
5294 let on_type_formatting = project.update(cx, |project, cx| {
5295 project.on_type_format(
5296 buffer.clone(),
5297 buffer_position,
5298 input,
5299 push_to_lsp_host_history,
5300 cx,
5301 )
5302 });
5303 Some(cx.spawn_in(window, async move |editor, cx| {
5304 if let Some(transaction) = on_type_formatting.await? {
5305 if push_to_client_history {
5306 buffer
5307 .update(cx, |buffer, _| {
5308 buffer.push_transaction(transaction, Instant::now());
5309 buffer.finalize_last_transaction();
5310 })
5311 .ok();
5312 }
5313 editor.update(cx, |editor, cx| {
5314 editor.refresh_document_highlights(cx);
5315 })?;
5316 }
5317 Ok(())
5318 }))
5319 }
5320
5321 pub fn show_word_completions(
5322 &mut self,
5323 _: &ShowWordCompletions,
5324 window: &mut Window,
5325 cx: &mut Context<Self>,
5326 ) {
5327 self.open_or_update_completions_menu(Some(CompletionsMenuSource::Words), None, window, cx);
5328 }
5329
5330 pub fn show_completions(
5331 &mut self,
5332 options: &ShowCompletions,
5333 window: &mut Window,
5334 cx: &mut Context<Self>,
5335 ) {
5336 self.open_or_update_completions_menu(None, options.trigger.as_deref(), window, cx);
5337 }
5338
5339 fn open_or_update_completions_menu(
5340 &mut self,
5341 requested_source: Option<CompletionsMenuSource>,
5342 trigger: Option<&str>,
5343 window: &mut Window,
5344 cx: &mut Context<Self>,
5345 ) {
5346 if self.pending_rename.is_some() {
5347 return;
5348 }
5349
5350 let multibuffer_snapshot = self.buffer.read(cx).read(cx);
5351
5352 // Typically `start` == `end`, but with snippet tabstop choices the default choice is
5353 // inserted and selected. To handle that case, the start of the selection is used so that
5354 // the menu starts with all choices.
5355 let position = self
5356 .selections
5357 .newest_anchor()
5358 .start
5359 .bias_right(&multibuffer_snapshot);
5360 if position.diff_base_anchor.is_some() {
5361 return;
5362 }
5363 let (buffer, buffer_position) =
5364 if let Some(output) = self.buffer.read(cx).text_anchor_for_position(position, cx) {
5365 output
5366 } else {
5367 return;
5368 };
5369 let buffer_snapshot = buffer.read(cx).snapshot();
5370
5371 let query: Option<Arc<String>> =
5372 Self::completion_query(&multibuffer_snapshot, position).map(|query| query.into());
5373
5374 drop(multibuffer_snapshot);
5375
5376 let provider = match requested_source {
5377 Some(CompletionsMenuSource::Normal) | None => self.completion_provider.clone(),
5378 Some(CompletionsMenuSource::Words) => None,
5379 Some(CompletionsMenuSource::SnippetChoices) => {
5380 log::error!("bug: SnippetChoices requested_source is not handled");
5381 None
5382 }
5383 };
5384
5385 let sort_completions = provider
5386 .as_ref()
5387 .map_or(false, |provider| provider.sort_completions());
5388
5389 let filter_completions = provider
5390 .as_ref()
5391 .map_or(true, |provider| provider.filter_completions());
5392
5393 let trigger_kind = match trigger {
5394 Some(trigger) if buffer.read(cx).completion_triggers().contains(trigger) => {
5395 CompletionTriggerKind::TRIGGER_CHARACTER
5396 }
5397 _ => CompletionTriggerKind::INVOKED,
5398 };
5399 let completion_context = CompletionContext {
5400 trigger_character: trigger.and_then(|trigger| {
5401 if trigger_kind == CompletionTriggerKind::TRIGGER_CHARACTER {
5402 Some(String::from(trigger))
5403 } else {
5404 None
5405 }
5406 }),
5407 trigger_kind,
5408 };
5409
5410 // Hide the current completions menu when a trigger char is typed. Without this, cached
5411 // completions from before the trigger char may be reused (#32774). Snippet choices could
5412 // involve trigger chars, so this is skipped in that case.
5413 if trigger_kind == CompletionTriggerKind::TRIGGER_CHARACTER && self.snippet_stack.is_empty()
5414 {
5415 let menu_is_open = matches!(
5416 self.context_menu.borrow().as_ref(),
5417 Some(CodeContextMenu::Completions(_))
5418 );
5419 if menu_is_open {
5420 self.hide_context_menu(window, cx);
5421 }
5422 }
5423
5424 if let Some(CodeContextMenu::Completions(menu)) = self.context_menu.borrow_mut().as_mut() {
5425 if filter_completions {
5426 menu.filter(query.clone(), provider.clone(), window, cx);
5427 }
5428 // When `is_incomplete` is false, no need to re-query completions when the current query
5429 // is a suffix of the initial query.
5430 if !menu.is_incomplete {
5431 // If the new query is a suffix of the old query (typing more characters) and
5432 // the previous result was complete, the existing completions can be filtered.
5433 //
5434 // Note that this is always true for snippet completions.
5435 let query_matches = match (&menu.initial_query, &query) {
5436 (Some(initial_query), Some(query)) => query.starts_with(initial_query.as_ref()),
5437 (None, _) => true,
5438 _ => false,
5439 };
5440 if query_matches {
5441 let position_matches = if menu.initial_position == position {
5442 true
5443 } else {
5444 let snapshot = self.buffer.read(cx).read(cx);
5445 menu.initial_position.to_offset(&snapshot) == position.to_offset(&snapshot)
5446 };
5447 if position_matches {
5448 return;
5449 }
5450 }
5451 }
5452 };
5453
5454 let (word_replace_range, word_to_exclude) = if let (word_range, Some(CharKind::Word)) =
5455 buffer_snapshot.surrounding_word(buffer_position, false)
5456 {
5457 let word_to_exclude = buffer_snapshot
5458 .text_for_range(word_range.clone())
5459 .collect::<String>();
5460 (
5461 buffer_snapshot.anchor_before(word_range.start)
5462 ..buffer_snapshot.anchor_after(buffer_position),
5463 Some(word_to_exclude),
5464 )
5465 } else {
5466 (buffer_position..buffer_position, None)
5467 };
5468
5469 let language = buffer_snapshot
5470 .language_at(buffer_position)
5471 .map(|language| language.name());
5472
5473 let completion_settings =
5474 language_settings(language.clone(), buffer_snapshot.file(), cx).completions;
5475
5476 let show_completion_documentation = buffer_snapshot
5477 .settings_at(buffer_position, cx)
5478 .show_completion_documentation;
5479
5480 // The document can be large, so stay in reasonable bounds when searching for words,
5481 // otherwise completion pop-up might be slow to appear.
5482 const WORD_LOOKUP_ROWS: u32 = 5_000;
5483 let buffer_row = text::ToPoint::to_point(&buffer_position, &buffer_snapshot).row;
5484 let min_word_search = buffer_snapshot.clip_point(
5485 Point::new(buffer_row.saturating_sub(WORD_LOOKUP_ROWS), 0),
5486 Bias::Left,
5487 );
5488 let max_word_search = buffer_snapshot.clip_point(
5489 Point::new(buffer_row + WORD_LOOKUP_ROWS, 0).min(buffer_snapshot.max_point()),
5490 Bias::Right,
5491 );
5492 let word_search_range = buffer_snapshot.point_to_offset(min_word_search)
5493 ..buffer_snapshot.point_to_offset(max_word_search);
5494
5495 let skip_digits = query
5496 .as_ref()
5497 .map_or(true, |query| !query.chars().any(|c| c.is_digit(10)));
5498
5499 let (mut words, provider_responses) = match &provider {
5500 Some(provider) => {
5501 let provider_responses = provider.completions(
5502 position.excerpt_id,
5503 &buffer,
5504 buffer_position,
5505 completion_context,
5506 window,
5507 cx,
5508 );
5509
5510 let words = match completion_settings.words {
5511 WordsCompletionMode::Disabled => Task::ready(BTreeMap::default()),
5512 WordsCompletionMode::Enabled | WordsCompletionMode::Fallback => cx
5513 .background_spawn(async move {
5514 buffer_snapshot.words_in_range(WordsQuery {
5515 fuzzy_contents: None,
5516 range: word_search_range,
5517 skip_digits,
5518 })
5519 }),
5520 };
5521
5522 (words, provider_responses)
5523 }
5524 None => (
5525 cx.background_spawn(async move {
5526 buffer_snapshot.words_in_range(WordsQuery {
5527 fuzzy_contents: None,
5528 range: word_search_range,
5529 skip_digits,
5530 })
5531 }),
5532 Task::ready(Ok(Vec::new())),
5533 ),
5534 };
5535
5536 let snippet_sort_order = EditorSettings::get_global(cx).snippet_sort_order;
5537
5538 let id = post_inc(&mut self.next_completion_id);
5539 let task = cx.spawn_in(window, async move |editor, cx| {
5540 let Ok(()) = editor.update(cx, |this, _| {
5541 this.completion_tasks.retain(|(task_id, _)| *task_id >= id);
5542 }) else {
5543 return;
5544 };
5545
5546 // TODO: Ideally completions from different sources would be selectively re-queried, so
5547 // that having one source with `is_incomplete: true` doesn't cause all to be re-queried.
5548 let mut completions = Vec::new();
5549 let mut is_incomplete = false;
5550 if let Some(provider_responses) = provider_responses.await.log_err() {
5551 if !provider_responses.is_empty() {
5552 for response in provider_responses {
5553 completions.extend(response.completions);
5554 is_incomplete = is_incomplete || response.is_incomplete;
5555 }
5556 if completion_settings.words == WordsCompletionMode::Fallback {
5557 words = Task::ready(BTreeMap::default());
5558 }
5559 }
5560 }
5561
5562 let mut words = words.await;
5563 if let Some(word_to_exclude) = &word_to_exclude {
5564 words.remove(word_to_exclude);
5565 }
5566 for lsp_completion in &completions {
5567 words.remove(&lsp_completion.new_text);
5568 }
5569 completions.extend(words.into_iter().map(|(word, word_range)| Completion {
5570 replace_range: word_replace_range.clone(),
5571 new_text: word.clone(),
5572 label: CodeLabel::plain(word, None),
5573 icon_path: None,
5574 documentation: None,
5575 source: CompletionSource::BufferWord {
5576 word_range,
5577 resolved: false,
5578 },
5579 insert_text_mode: Some(InsertTextMode::AS_IS),
5580 confirm: None,
5581 }));
5582
5583 let menu = if completions.is_empty() {
5584 None
5585 } else {
5586 let Ok((mut menu, matches_task)) = editor.update(cx, |editor, cx| {
5587 let languages = editor
5588 .workspace
5589 .as_ref()
5590 .and_then(|(workspace, _)| workspace.upgrade())
5591 .map(|workspace| workspace.read(cx).app_state().languages.clone());
5592 let menu = CompletionsMenu::new(
5593 id,
5594 requested_source.unwrap_or(CompletionsMenuSource::Normal),
5595 sort_completions,
5596 show_completion_documentation,
5597 position,
5598 query.clone(),
5599 is_incomplete,
5600 buffer.clone(),
5601 completions.into(),
5602 snippet_sort_order,
5603 languages,
5604 language,
5605 cx,
5606 );
5607
5608 let query = if filter_completions { query } else { None };
5609 let matches_task = if let Some(query) = query {
5610 menu.do_async_filtering(query, cx)
5611 } else {
5612 Task::ready(menu.unfiltered_matches())
5613 };
5614 (menu, matches_task)
5615 }) else {
5616 return;
5617 };
5618
5619 let matches = matches_task.await;
5620
5621 let Ok(()) = editor.update_in(cx, |editor, window, cx| {
5622 // Newer menu already set, so exit.
5623 match editor.context_menu.borrow().as_ref() {
5624 Some(CodeContextMenu::Completions(prev_menu)) => {
5625 if prev_menu.id > id {
5626 return;
5627 }
5628 }
5629 _ => {}
5630 };
5631
5632 // Only valid to take prev_menu because it the new menu is immediately set
5633 // below, or the menu is hidden.
5634 match editor.context_menu.borrow_mut().take() {
5635 Some(CodeContextMenu::Completions(prev_menu)) => {
5636 let position_matches =
5637 if prev_menu.initial_position == menu.initial_position {
5638 true
5639 } else {
5640 let snapshot = editor.buffer.read(cx).read(cx);
5641 prev_menu.initial_position.to_offset(&snapshot)
5642 == menu.initial_position.to_offset(&snapshot)
5643 };
5644 if position_matches {
5645 // Preserve markdown cache before `set_filter_results` because it will
5646 // try to populate the documentation cache.
5647 menu.preserve_markdown_cache(prev_menu);
5648 }
5649 }
5650 _ => {}
5651 };
5652
5653 menu.set_filter_results(matches, provider, window, cx);
5654 }) else {
5655 return;
5656 };
5657
5658 menu.visible().then_some(menu)
5659 };
5660
5661 editor
5662 .update_in(cx, |editor, window, cx| {
5663 if editor.focus_handle.is_focused(window) {
5664 if let Some(menu) = menu {
5665 *editor.context_menu.borrow_mut() =
5666 Some(CodeContextMenu::Completions(menu));
5667
5668 crate::hover_popover::hide_hover(editor, cx);
5669 if editor.show_edit_predictions_in_menu() {
5670 editor.update_visible_inline_completion(window, cx);
5671 } else {
5672 editor.discard_inline_completion(false, cx);
5673 }
5674
5675 cx.notify();
5676 return;
5677 }
5678 }
5679
5680 if editor.completion_tasks.len() <= 1 {
5681 // If there are no more completion tasks and the last menu was empty, we should hide it.
5682 let was_hidden = editor.hide_context_menu(window, cx).is_none();
5683 // If it was already hidden and we don't show inline completions in the menu, we should
5684 // also show the inline-completion when available.
5685 if was_hidden && editor.show_edit_predictions_in_menu() {
5686 editor.update_visible_inline_completion(window, cx);
5687 }
5688 }
5689 })
5690 .ok();
5691 });
5692
5693 self.completion_tasks.push((id, task));
5694 }
5695
5696 #[cfg(feature = "test-support")]
5697 pub fn current_completions(&self) -> Option<Vec<project::Completion>> {
5698 let menu = self.context_menu.borrow();
5699 if let CodeContextMenu::Completions(menu) = menu.as_ref()? {
5700 let completions = menu.completions.borrow();
5701 Some(completions.to_vec())
5702 } else {
5703 None
5704 }
5705 }
5706
5707 pub fn with_completions_menu_matching_id<R>(
5708 &self,
5709 id: CompletionId,
5710 f: impl FnOnce(Option<&mut CompletionsMenu>) -> R,
5711 ) -> R {
5712 let mut context_menu = self.context_menu.borrow_mut();
5713 let Some(CodeContextMenu::Completions(completions_menu)) = &mut *context_menu else {
5714 return f(None);
5715 };
5716 if completions_menu.id != id {
5717 return f(None);
5718 }
5719 f(Some(completions_menu))
5720 }
5721
5722 pub fn confirm_completion(
5723 &mut self,
5724 action: &ConfirmCompletion,
5725 window: &mut Window,
5726 cx: &mut Context<Self>,
5727 ) -> Option<Task<Result<()>>> {
5728 self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
5729 self.do_completion(action.item_ix, CompletionIntent::Complete, window, cx)
5730 }
5731
5732 pub fn confirm_completion_insert(
5733 &mut self,
5734 _: &ConfirmCompletionInsert,
5735 window: &mut Window,
5736 cx: &mut Context<Self>,
5737 ) -> Option<Task<Result<()>>> {
5738 self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
5739 self.do_completion(None, CompletionIntent::CompleteWithInsert, window, cx)
5740 }
5741
5742 pub fn confirm_completion_replace(
5743 &mut self,
5744 _: &ConfirmCompletionReplace,
5745 window: &mut Window,
5746 cx: &mut Context<Self>,
5747 ) -> Option<Task<Result<()>>> {
5748 self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
5749 self.do_completion(None, CompletionIntent::CompleteWithReplace, window, cx)
5750 }
5751
5752 pub fn compose_completion(
5753 &mut self,
5754 action: &ComposeCompletion,
5755 window: &mut Window,
5756 cx: &mut Context<Self>,
5757 ) -> Option<Task<Result<()>>> {
5758 self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
5759 self.do_completion(action.item_ix, CompletionIntent::Compose, window, cx)
5760 }
5761
5762 fn do_completion(
5763 &mut self,
5764 item_ix: Option<usize>,
5765 intent: CompletionIntent,
5766 window: &mut Window,
5767 cx: &mut Context<Editor>,
5768 ) -> Option<Task<Result<()>>> {
5769 use language::ToOffset as _;
5770
5771 let CodeContextMenu::Completions(completions_menu) = self.hide_context_menu(window, cx)?
5772 else {
5773 return None;
5774 };
5775
5776 let candidate_id = {
5777 let entries = completions_menu.entries.borrow();
5778 let mat = entries.get(item_ix.unwrap_or(completions_menu.selected_item))?;
5779 if self.show_edit_predictions_in_menu() {
5780 self.discard_inline_completion(true, cx);
5781 }
5782 mat.candidate_id
5783 };
5784
5785 let completion = completions_menu
5786 .completions
5787 .borrow()
5788 .get(candidate_id)?
5789 .clone();
5790 cx.stop_propagation();
5791
5792 let buffer_handle = completions_menu.buffer.clone();
5793
5794 let CompletionEdit {
5795 new_text,
5796 snippet,
5797 replace_range,
5798 } = process_completion_for_edit(
5799 &completion,
5800 intent,
5801 &buffer_handle,
5802 &completions_menu.initial_position.text_anchor,
5803 cx,
5804 );
5805
5806 let buffer = buffer_handle.read(cx);
5807 let snapshot = self.buffer.read(cx).snapshot(cx);
5808 let newest_anchor = self.selections.newest_anchor();
5809 let replace_range_multibuffer = {
5810 let excerpt = snapshot.excerpt_containing(newest_anchor.range()).unwrap();
5811 let multibuffer_anchor = snapshot
5812 .anchor_in_excerpt(excerpt.id(), buffer.anchor_before(replace_range.start))
5813 .unwrap()
5814 ..snapshot
5815 .anchor_in_excerpt(excerpt.id(), buffer.anchor_before(replace_range.end))
5816 .unwrap();
5817 multibuffer_anchor.start.to_offset(&snapshot)
5818 ..multibuffer_anchor.end.to_offset(&snapshot)
5819 };
5820 if newest_anchor.head().buffer_id != Some(buffer.remote_id()) {
5821 return None;
5822 }
5823
5824 let old_text = buffer
5825 .text_for_range(replace_range.clone())
5826 .collect::<String>();
5827 let lookbehind = newest_anchor
5828 .start
5829 .text_anchor
5830 .to_offset(buffer)
5831 .saturating_sub(replace_range.start);
5832 let lookahead = replace_range
5833 .end
5834 .saturating_sub(newest_anchor.end.text_anchor.to_offset(buffer));
5835 let prefix = &old_text[..old_text.len().saturating_sub(lookahead)];
5836 let suffix = &old_text[lookbehind.min(old_text.len())..];
5837
5838 let selections = self.selections.all::<usize>(cx);
5839 let mut ranges = Vec::new();
5840 let mut linked_edits = HashMap::<_, Vec<_>>::default();
5841
5842 for selection in &selections {
5843 let range = if selection.id == newest_anchor.id {
5844 replace_range_multibuffer.clone()
5845 } else {
5846 let mut range = selection.range();
5847
5848 // if prefix is present, don't duplicate it
5849 if snapshot.contains_str_at(range.start.saturating_sub(lookbehind), prefix) {
5850 range.start = range.start.saturating_sub(lookbehind);
5851
5852 // if suffix is also present, mimic the newest cursor and replace it
5853 if selection.id != newest_anchor.id
5854 && snapshot.contains_str_at(range.end, suffix)
5855 {
5856 range.end += lookahead;
5857 }
5858 }
5859 range
5860 };
5861
5862 ranges.push(range.clone());
5863
5864 if !self.linked_edit_ranges.is_empty() {
5865 let start_anchor = snapshot.anchor_before(range.start);
5866 let end_anchor = snapshot.anchor_after(range.end);
5867 if let Some(ranges) = self
5868 .linked_editing_ranges_for(start_anchor.text_anchor..end_anchor.text_anchor, cx)
5869 {
5870 for (buffer, edits) in ranges {
5871 linked_edits
5872 .entry(buffer.clone())
5873 .or_default()
5874 .extend(edits.into_iter().map(|range| (range, new_text.to_owned())));
5875 }
5876 }
5877 }
5878 }
5879
5880 let common_prefix_len = old_text
5881 .chars()
5882 .zip(new_text.chars())
5883 .take_while(|(a, b)| a == b)
5884 .map(|(a, _)| a.len_utf8())
5885 .sum::<usize>();
5886
5887 cx.emit(EditorEvent::InputHandled {
5888 utf16_range_to_replace: None,
5889 text: new_text[common_prefix_len..].into(),
5890 });
5891
5892 self.transact(window, cx, |this, window, cx| {
5893 if let Some(mut snippet) = snippet {
5894 snippet.text = new_text.to_string();
5895 this.insert_snippet(&ranges, snippet, window, cx).log_err();
5896 } else {
5897 this.buffer.update(cx, |buffer, cx| {
5898 let auto_indent = match completion.insert_text_mode {
5899 Some(InsertTextMode::AS_IS) => None,
5900 _ => this.autoindent_mode.clone(),
5901 };
5902 let edits = ranges.into_iter().map(|range| (range, new_text.as_str()));
5903 buffer.edit(edits, auto_indent, cx);
5904 });
5905 }
5906 for (buffer, edits) in linked_edits {
5907 buffer.update(cx, |buffer, cx| {
5908 let snapshot = buffer.snapshot();
5909 let edits = edits
5910 .into_iter()
5911 .map(|(range, text)| {
5912 use text::ToPoint as TP;
5913 let end_point = TP::to_point(&range.end, &snapshot);
5914 let start_point = TP::to_point(&range.start, &snapshot);
5915 (start_point..end_point, text)
5916 })
5917 .sorted_by_key(|(range, _)| range.start);
5918 buffer.edit(edits, None, cx);
5919 })
5920 }
5921
5922 this.refresh_inline_completion(true, false, window, cx);
5923 });
5924
5925 let show_new_completions_on_confirm = completion
5926 .confirm
5927 .as_ref()
5928 .map_or(false, |confirm| confirm(intent, window, cx));
5929 if show_new_completions_on_confirm {
5930 self.show_completions(&ShowCompletions { trigger: None }, window, cx);
5931 }
5932
5933 let provider = self.completion_provider.as_ref()?;
5934 drop(completion);
5935 let apply_edits = provider.apply_additional_edits_for_completion(
5936 buffer_handle,
5937 completions_menu.completions.clone(),
5938 candidate_id,
5939 true,
5940 cx,
5941 );
5942
5943 let editor_settings = EditorSettings::get_global(cx);
5944 if editor_settings.show_signature_help_after_edits || editor_settings.auto_signature_help {
5945 // After the code completion is finished, users often want to know what signatures are needed.
5946 // so we should automatically call signature_help
5947 self.show_signature_help(&ShowSignatureHelp, window, cx);
5948 }
5949
5950 Some(cx.foreground_executor().spawn(async move {
5951 apply_edits.await?;
5952 Ok(())
5953 }))
5954 }
5955
5956 pub fn toggle_code_actions(
5957 &mut self,
5958 action: &ToggleCodeActions,
5959 window: &mut Window,
5960 cx: &mut Context<Self>,
5961 ) {
5962 let quick_launch = action.quick_launch;
5963 let mut context_menu = self.context_menu.borrow_mut();
5964 if let Some(CodeContextMenu::CodeActions(code_actions)) = context_menu.as_ref() {
5965 if code_actions.deployed_from == action.deployed_from {
5966 // Toggle if we're selecting the same one
5967 *context_menu = None;
5968 cx.notify();
5969 return;
5970 } else {
5971 // Otherwise, clear it and start a new one
5972 *context_menu = None;
5973 cx.notify();
5974 }
5975 }
5976 drop(context_menu);
5977 let snapshot = self.snapshot(window, cx);
5978 let deployed_from = action.deployed_from.clone();
5979 let action = action.clone();
5980 self.completion_tasks.clear();
5981 self.discard_inline_completion(false, cx);
5982
5983 let multibuffer_point = match &action.deployed_from {
5984 Some(CodeActionSource::Indicator(row)) | Some(CodeActionSource::RunMenu(row)) => {
5985 DisplayPoint::new(*row, 0).to_point(&snapshot)
5986 }
5987 _ => self.selections.newest::<Point>(cx).head(),
5988 };
5989 let Some((buffer, buffer_row)) = snapshot
5990 .buffer_snapshot
5991 .buffer_line_for_row(MultiBufferRow(multibuffer_point.row))
5992 .and_then(|(buffer_snapshot, range)| {
5993 self.buffer()
5994 .read(cx)
5995 .buffer(buffer_snapshot.remote_id())
5996 .map(|buffer| (buffer, range.start.row))
5997 })
5998 else {
5999 return;
6000 };
6001 let buffer_id = buffer.read(cx).remote_id();
6002 let tasks = self
6003 .tasks
6004 .get(&(buffer_id, buffer_row))
6005 .map(|t| Arc::new(t.to_owned()));
6006
6007 if !self.focus_handle.is_focused(window) {
6008 return;
6009 }
6010 let project = self.project.clone();
6011
6012 let code_actions_task = match deployed_from {
6013 Some(CodeActionSource::RunMenu(_)) => Task::ready(None),
6014 _ => self.code_actions(buffer_row, window, cx),
6015 };
6016
6017 let runnable_task = match deployed_from {
6018 Some(CodeActionSource::Indicator(_)) => Task::ready(Ok(Default::default())),
6019 _ => {
6020 let mut task_context_task = Task::ready(None);
6021 if let Some(tasks) = &tasks {
6022 if let Some(project) = project {
6023 task_context_task =
6024 Self::build_tasks_context(&project, &buffer, buffer_row, &tasks, cx);
6025 }
6026 }
6027
6028 cx.spawn_in(window, {
6029 let buffer = buffer.clone();
6030 async move |editor, cx| {
6031 let task_context = task_context_task.await;
6032
6033 let resolved_tasks =
6034 tasks
6035 .zip(task_context.clone())
6036 .map(|(tasks, task_context)| ResolvedTasks {
6037 templates: tasks.resolve(&task_context).collect(),
6038 position: snapshot.buffer_snapshot.anchor_before(Point::new(
6039 multibuffer_point.row,
6040 tasks.column,
6041 )),
6042 });
6043 let debug_scenarios = editor
6044 .update(cx, |editor, cx| {
6045 editor.debug_scenarios(&resolved_tasks, &buffer, cx)
6046 })?
6047 .await;
6048 anyhow::Ok((resolved_tasks, debug_scenarios, task_context))
6049 }
6050 })
6051 }
6052 };
6053
6054 cx.spawn_in(window, async move |editor, cx| {
6055 let (resolved_tasks, debug_scenarios, task_context) = runnable_task.await?;
6056 let code_actions = code_actions_task.await;
6057 let spawn_straight_away = quick_launch
6058 && resolved_tasks
6059 .as_ref()
6060 .map_or(false, |tasks| tasks.templates.len() == 1)
6061 && code_actions
6062 .as_ref()
6063 .map_or(true, |actions| actions.is_empty())
6064 && debug_scenarios.is_empty();
6065
6066 editor.update_in(cx, |editor, window, cx| {
6067 crate::hover_popover::hide_hover(editor, cx);
6068 let actions = CodeActionContents::new(
6069 resolved_tasks,
6070 code_actions,
6071 debug_scenarios,
6072 task_context.unwrap_or_default(),
6073 );
6074
6075 // Don't show the menu if there are no actions available
6076 if actions.is_empty() {
6077 cx.notify();
6078 return Task::ready(Ok(()));
6079 }
6080
6081 *editor.context_menu.borrow_mut() =
6082 Some(CodeContextMenu::CodeActions(CodeActionsMenu {
6083 buffer,
6084 actions,
6085 selected_item: Default::default(),
6086 scroll_handle: UniformListScrollHandle::default(),
6087 deployed_from,
6088 }));
6089 cx.notify();
6090 if spawn_straight_away {
6091 if let Some(task) = editor.confirm_code_action(
6092 &ConfirmCodeAction { item_ix: Some(0) },
6093 window,
6094 cx,
6095 ) {
6096 return task;
6097 }
6098 }
6099
6100 Task::ready(Ok(()))
6101 })
6102 })
6103 .detach_and_log_err(cx);
6104 }
6105
6106 fn debug_scenarios(
6107 &mut self,
6108 resolved_tasks: &Option<ResolvedTasks>,
6109 buffer: &Entity<Buffer>,
6110 cx: &mut App,
6111 ) -> Task<Vec<task::DebugScenario>> {
6112 maybe!({
6113 let project = self.project.as_ref()?;
6114 let dap_store = project.read(cx).dap_store();
6115 let mut scenarios = vec![];
6116 let resolved_tasks = resolved_tasks.as_ref()?;
6117 let buffer = buffer.read(cx);
6118 let language = buffer.language()?;
6119 let file = buffer.file();
6120 let debug_adapter = language_settings(language.name().into(), file, cx)
6121 .debuggers
6122 .first()
6123 .map(SharedString::from)
6124 .or_else(|| language.config().debuggers.first().map(SharedString::from))?;
6125
6126 dap_store.update(cx, |dap_store, cx| {
6127 for (_, task) in &resolved_tasks.templates {
6128 let maybe_scenario = dap_store.debug_scenario_for_build_task(
6129 task.original_task().clone(),
6130 debug_adapter.clone().into(),
6131 task.display_label().to_owned().into(),
6132 cx,
6133 );
6134 scenarios.push(maybe_scenario);
6135 }
6136 });
6137 Some(cx.background_spawn(async move {
6138 let scenarios = futures::future::join_all(scenarios)
6139 .await
6140 .into_iter()
6141 .flatten()
6142 .collect::<Vec<_>>();
6143 scenarios
6144 }))
6145 })
6146 .unwrap_or_else(|| Task::ready(vec![]))
6147 }
6148
6149 fn code_actions(
6150 &mut self,
6151 buffer_row: u32,
6152 window: &mut Window,
6153 cx: &mut Context<Self>,
6154 ) -> Task<Option<Rc<[AvailableCodeAction]>>> {
6155 let mut task = self.code_actions_task.take();
6156 cx.spawn_in(window, async move |editor, cx| {
6157 while let Some(prev_task) = task {
6158 prev_task.await.log_err();
6159 task = editor
6160 .update(cx, |this, _| this.code_actions_task.take())
6161 .ok()?;
6162 }
6163
6164 editor
6165 .update(cx, |editor, cx| {
6166 editor
6167 .available_code_actions
6168 .clone()
6169 .and_then(|(location, code_actions)| {
6170 let snapshot = location.buffer.read(cx).snapshot();
6171 let point_range = location.range.to_point(&snapshot);
6172 let point_range = point_range.start.row..=point_range.end.row;
6173 if point_range.contains(&buffer_row) {
6174 Some(code_actions)
6175 } else {
6176 None
6177 }
6178 })
6179 })
6180 .ok()
6181 .flatten()
6182 })
6183 }
6184
6185 pub fn confirm_code_action(
6186 &mut self,
6187 action: &ConfirmCodeAction,
6188 window: &mut Window,
6189 cx: &mut Context<Self>,
6190 ) -> Option<Task<Result<()>>> {
6191 self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
6192
6193 let actions_menu =
6194 if let CodeContextMenu::CodeActions(menu) = self.hide_context_menu(window, cx)? {
6195 menu
6196 } else {
6197 return None;
6198 };
6199
6200 let action_ix = action.item_ix.unwrap_or(actions_menu.selected_item);
6201 let action = actions_menu.actions.get(action_ix)?;
6202 let title = action.label();
6203 let buffer = actions_menu.buffer;
6204 let workspace = self.workspace()?;
6205
6206 match action {
6207 CodeActionsItem::Task(task_source_kind, resolved_task) => {
6208 workspace.update(cx, |workspace, cx| {
6209 workspace.schedule_resolved_task(
6210 task_source_kind,
6211 resolved_task,
6212 false,
6213 window,
6214 cx,
6215 );
6216
6217 Some(Task::ready(Ok(())))
6218 })
6219 }
6220 CodeActionsItem::CodeAction {
6221 excerpt_id,
6222 action,
6223 provider,
6224 } => {
6225 let apply_code_action =
6226 provider.apply_code_action(buffer, action, excerpt_id, true, window, cx);
6227 let workspace = workspace.downgrade();
6228 Some(cx.spawn_in(window, async move |editor, cx| {
6229 let project_transaction = apply_code_action.await?;
6230 Self::open_project_transaction(
6231 &editor,
6232 workspace,
6233 project_transaction,
6234 title,
6235 cx,
6236 )
6237 .await
6238 }))
6239 }
6240 CodeActionsItem::DebugScenario(scenario) => {
6241 let context = actions_menu.actions.context.clone();
6242
6243 workspace.update(cx, |workspace, cx| {
6244 dap::send_telemetry(&scenario, TelemetrySpawnLocation::Gutter, cx);
6245 workspace.start_debug_session(
6246 scenario,
6247 context,
6248 Some(buffer),
6249 None,
6250 window,
6251 cx,
6252 );
6253 });
6254 Some(Task::ready(Ok(())))
6255 }
6256 }
6257 }
6258
6259 pub async fn open_project_transaction(
6260 this: &WeakEntity<Editor>,
6261 workspace: WeakEntity<Workspace>,
6262 transaction: ProjectTransaction,
6263 title: String,
6264 cx: &mut AsyncWindowContext,
6265 ) -> Result<()> {
6266 let mut entries = transaction.0.into_iter().collect::<Vec<_>>();
6267 cx.update(|_, cx| {
6268 entries.sort_unstable_by_key(|(buffer, _)| {
6269 buffer.read(cx).file().map(|f| f.path().clone())
6270 });
6271 })?;
6272
6273 // If the project transaction's edits are all contained within this editor, then
6274 // avoid opening a new editor to display them.
6275
6276 if let Some((buffer, transaction)) = entries.first() {
6277 if entries.len() == 1 {
6278 let excerpt = this.update(cx, |editor, cx| {
6279 editor
6280 .buffer()
6281 .read(cx)
6282 .excerpt_containing(editor.selections.newest_anchor().head(), cx)
6283 })?;
6284 if let Some((_, excerpted_buffer, excerpt_range)) = excerpt {
6285 if excerpted_buffer == *buffer {
6286 let all_edits_within_excerpt = buffer.read_with(cx, |buffer, _| {
6287 let excerpt_range = excerpt_range.to_offset(buffer);
6288 buffer
6289 .edited_ranges_for_transaction::<usize>(transaction)
6290 .all(|range| {
6291 excerpt_range.start <= range.start
6292 && excerpt_range.end >= range.end
6293 })
6294 })?;
6295
6296 if all_edits_within_excerpt {
6297 return Ok(());
6298 }
6299 }
6300 }
6301 }
6302 } else {
6303 return Ok(());
6304 }
6305
6306 let mut ranges_to_highlight = Vec::new();
6307 let excerpt_buffer = cx.new(|cx| {
6308 let mut multibuffer = MultiBuffer::new(Capability::ReadWrite).with_title(title);
6309 for (buffer_handle, transaction) in &entries {
6310 let edited_ranges = buffer_handle
6311 .read(cx)
6312 .edited_ranges_for_transaction::<Point>(transaction)
6313 .collect::<Vec<_>>();
6314 let (ranges, _) = multibuffer.set_excerpts_for_path(
6315 PathKey::for_buffer(buffer_handle, cx),
6316 buffer_handle.clone(),
6317 edited_ranges,
6318 DEFAULT_MULTIBUFFER_CONTEXT,
6319 cx,
6320 );
6321
6322 ranges_to_highlight.extend(ranges);
6323 }
6324 multibuffer.push_transaction(entries.iter().map(|(b, t)| (b, t)), cx);
6325 multibuffer
6326 })?;
6327
6328 workspace.update_in(cx, |workspace, window, cx| {
6329 let project = workspace.project().clone();
6330 let editor =
6331 cx.new(|cx| Editor::for_multibuffer(excerpt_buffer, Some(project), window, cx));
6332 workspace.add_item_to_active_pane(Box::new(editor.clone()), None, true, window, cx);
6333 editor.update(cx, |editor, cx| {
6334 editor.highlight_background::<Self>(
6335 &ranges_to_highlight,
6336 |theme| theme.colors().editor_highlighted_line_background,
6337 cx,
6338 );
6339 });
6340 })?;
6341
6342 Ok(())
6343 }
6344
6345 pub fn clear_code_action_providers(&mut self) {
6346 self.code_action_providers.clear();
6347 self.available_code_actions.take();
6348 }
6349
6350 pub fn add_code_action_provider(
6351 &mut self,
6352 provider: Rc<dyn CodeActionProvider>,
6353 window: &mut Window,
6354 cx: &mut Context<Self>,
6355 ) {
6356 if self
6357 .code_action_providers
6358 .iter()
6359 .any(|existing_provider| existing_provider.id() == provider.id())
6360 {
6361 return;
6362 }
6363
6364 self.code_action_providers.push(provider);
6365 self.refresh_code_actions(window, cx);
6366 }
6367
6368 pub fn remove_code_action_provider(
6369 &mut self,
6370 id: Arc<str>,
6371 window: &mut Window,
6372 cx: &mut Context<Self>,
6373 ) {
6374 self.code_action_providers
6375 .retain(|provider| provider.id() != id);
6376 self.refresh_code_actions(window, cx);
6377 }
6378
6379 pub fn code_actions_enabled_for_toolbar(&self, cx: &App) -> bool {
6380 !self.code_action_providers.is_empty()
6381 && EditorSettings::get_global(cx).toolbar.code_actions
6382 }
6383
6384 pub fn has_available_code_actions(&self) -> bool {
6385 self.available_code_actions
6386 .as_ref()
6387 .is_some_and(|(_, actions)| !actions.is_empty())
6388 }
6389
6390 fn render_inline_code_actions(
6391 &self,
6392 icon_size: ui::IconSize,
6393 display_row: DisplayRow,
6394 is_active: bool,
6395 cx: &mut Context<Self>,
6396 ) -> AnyElement {
6397 let show_tooltip = !self.context_menu_visible();
6398 IconButton::new("inline_code_actions", ui::IconName::BoltFilled)
6399 .icon_size(icon_size)
6400 .shape(ui::IconButtonShape::Square)
6401 .style(ButtonStyle::Transparent)
6402 .icon_color(ui::Color::Hidden)
6403 .toggle_state(is_active)
6404 .when(show_tooltip, |this| {
6405 this.tooltip({
6406 let focus_handle = self.focus_handle.clone();
6407 move |window, cx| {
6408 Tooltip::for_action_in(
6409 "Toggle Code Actions",
6410 &ToggleCodeActions {
6411 deployed_from: None,
6412 quick_launch: false,
6413 },
6414 &focus_handle,
6415 window,
6416 cx,
6417 )
6418 }
6419 })
6420 })
6421 .on_click(cx.listener(move |editor, _: &ClickEvent, window, cx| {
6422 window.focus(&editor.focus_handle(cx));
6423 editor.toggle_code_actions(
6424 &crate::actions::ToggleCodeActions {
6425 deployed_from: Some(crate::actions::CodeActionSource::Indicator(
6426 display_row,
6427 )),
6428 quick_launch: false,
6429 },
6430 window,
6431 cx,
6432 );
6433 }))
6434 .into_any_element()
6435 }
6436
6437 pub fn context_menu(&self) -> &RefCell<Option<CodeContextMenu>> {
6438 &self.context_menu
6439 }
6440
6441 fn refresh_code_actions(&mut self, window: &mut Window, cx: &mut Context<Self>) -> Option<()> {
6442 let newest_selection = self.selections.newest_anchor().clone();
6443 let newest_selection_adjusted = self.selections.newest_adjusted(cx).clone();
6444 let buffer = self.buffer.read(cx);
6445 if newest_selection.head().diff_base_anchor.is_some() {
6446 return None;
6447 }
6448 let (start_buffer, start) =
6449 buffer.text_anchor_for_position(newest_selection_adjusted.start, cx)?;
6450 let (end_buffer, end) =
6451 buffer.text_anchor_for_position(newest_selection_adjusted.end, cx)?;
6452 if start_buffer != end_buffer {
6453 return None;
6454 }
6455
6456 self.code_actions_task = Some(cx.spawn_in(window, async move |this, cx| {
6457 cx.background_executor()
6458 .timer(CODE_ACTIONS_DEBOUNCE_TIMEOUT)
6459 .await;
6460
6461 let (providers, tasks) = this.update_in(cx, |this, window, cx| {
6462 let providers = this.code_action_providers.clone();
6463 let tasks = this
6464 .code_action_providers
6465 .iter()
6466 .map(|provider| provider.code_actions(&start_buffer, start..end, window, cx))
6467 .collect::<Vec<_>>();
6468 (providers, tasks)
6469 })?;
6470
6471 let mut actions = Vec::new();
6472 for (provider, provider_actions) in
6473 providers.into_iter().zip(future::join_all(tasks).await)
6474 {
6475 if let Some(provider_actions) = provider_actions.log_err() {
6476 actions.extend(provider_actions.into_iter().map(|action| {
6477 AvailableCodeAction {
6478 excerpt_id: newest_selection.start.excerpt_id,
6479 action,
6480 provider: provider.clone(),
6481 }
6482 }));
6483 }
6484 }
6485
6486 this.update(cx, |this, cx| {
6487 this.available_code_actions = if actions.is_empty() {
6488 None
6489 } else {
6490 Some((
6491 Location {
6492 buffer: start_buffer,
6493 range: start..end,
6494 },
6495 actions.into(),
6496 ))
6497 };
6498 cx.notify();
6499 })
6500 }));
6501 None
6502 }
6503
6504 fn start_inline_blame_timer(&mut self, window: &mut Window, cx: &mut Context<Self>) {
6505 if let Some(delay) = ProjectSettings::get_global(cx).git.inline_blame_delay() {
6506 self.show_git_blame_inline = false;
6507
6508 self.show_git_blame_inline_delay_task =
6509 Some(cx.spawn_in(window, async move |this, cx| {
6510 cx.background_executor().timer(delay).await;
6511
6512 this.update(cx, |this, cx| {
6513 this.show_git_blame_inline = true;
6514 cx.notify();
6515 })
6516 .log_err();
6517 }));
6518 }
6519 }
6520
6521 pub fn blame_hover(&mut self, _: &BlameHover, window: &mut Window, cx: &mut Context<Self>) {
6522 let snapshot = self.snapshot(window, cx);
6523 let cursor = self.selections.newest::<Point>(cx).head();
6524 let Some((buffer, point, _)) = snapshot.buffer_snapshot.point_to_buffer_point(cursor)
6525 else {
6526 return;
6527 };
6528
6529 let Some(blame) = self.blame.as_ref() else {
6530 return;
6531 };
6532
6533 let row_info = RowInfo {
6534 buffer_id: Some(buffer.remote_id()),
6535 buffer_row: Some(point.row),
6536 ..Default::default()
6537 };
6538 let Some(blame_entry) = blame
6539 .update(cx, |blame, cx| blame.blame_for_rows(&[row_info], cx).next())
6540 .flatten()
6541 else {
6542 return;
6543 };
6544
6545 let anchor = self.selections.newest_anchor().head();
6546 let position = self.to_pixel_point(anchor, &snapshot, window);
6547 if let (Some(position), Some(last_bounds)) = (position, self.last_bounds) {
6548 self.show_blame_popover(&blame_entry, position + last_bounds.origin, true, cx);
6549 };
6550 }
6551
6552 fn show_blame_popover(
6553 &mut self,
6554 blame_entry: &BlameEntry,
6555 position: gpui::Point<Pixels>,
6556 ignore_timeout: bool,
6557 cx: &mut Context<Self>,
6558 ) {
6559 if let Some(state) = &mut self.inline_blame_popover {
6560 state.hide_task.take();
6561 } else {
6562 let blame_popover_delay = EditorSettings::get_global(cx).hover_popover_delay;
6563 let blame_entry = blame_entry.clone();
6564 let show_task = cx.spawn(async move |editor, cx| {
6565 if !ignore_timeout {
6566 cx.background_executor()
6567 .timer(std::time::Duration::from_millis(blame_popover_delay))
6568 .await;
6569 }
6570 editor
6571 .update(cx, |editor, cx| {
6572 editor.inline_blame_popover_show_task.take();
6573 let Some(blame) = editor.blame.as_ref() else {
6574 return;
6575 };
6576 let blame = blame.read(cx);
6577 let details = blame.details_for_entry(&blame_entry);
6578 let markdown = cx.new(|cx| {
6579 Markdown::new(
6580 details
6581 .as_ref()
6582 .map(|message| message.message.clone())
6583 .unwrap_or_default(),
6584 None,
6585 None,
6586 cx,
6587 )
6588 });
6589 editor.inline_blame_popover = Some(InlineBlamePopover {
6590 position,
6591 hide_task: None,
6592 popover_bounds: None,
6593 popover_state: InlineBlamePopoverState {
6594 scroll_handle: ScrollHandle::new(),
6595 commit_message: details,
6596 markdown,
6597 },
6598 keyboard_grace: ignore_timeout,
6599 });
6600 cx.notify();
6601 })
6602 .ok();
6603 });
6604 self.inline_blame_popover_show_task = Some(show_task);
6605 }
6606 }
6607
6608 fn hide_blame_popover(&mut self, cx: &mut Context<Self>) {
6609 self.inline_blame_popover_show_task.take();
6610 if let Some(state) = &mut self.inline_blame_popover {
6611 let hide_task = cx.spawn(async move |editor, cx| {
6612 cx.background_executor()
6613 .timer(std::time::Duration::from_millis(100))
6614 .await;
6615 editor
6616 .update(cx, |editor, cx| {
6617 editor.inline_blame_popover.take();
6618 cx.notify();
6619 })
6620 .ok();
6621 });
6622 state.hide_task = Some(hide_task);
6623 }
6624 }
6625
6626 fn refresh_document_highlights(&mut self, cx: &mut Context<Self>) -> Option<()> {
6627 if self.pending_rename.is_some() {
6628 return None;
6629 }
6630
6631 let provider = self.semantics_provider.clone()?;
6632 let buffer = self.buffer.read(cx);
6633 let newest_selection = self.selections.newest_anchor().clone();
6634 let cursor_position = newest_selection.head();
6635 let (cursor_buffer, cursor_buffer_position) =
6636 buffer.text_anchor_for_position(cursor_position, cx)?;
6637 let (tail_buffer, tail_buffer_position) =
6638 buffer.text_anchor_for_position(newest_selection.tail(), cx)?;
6639 if cursor_buffer != tail_buffer {
6640 return None;
6641 }
6642
6643 let snapshot = cursor_buffer.read(cx).snapshot();
6644 let (start_word_range, _) = snapshot.surrounding_word(cursor_buffer_position, false);
6645 let (end_word_range, _) = snapshot.surrounding_word(tail_buffer_position, false);
6646 if start_word_range != end_word_range {
6647 self.document_highlights_task.take();
6648 self.clear_background_highlights::<DocumentHighlightRead>(cx);
6649 self.clear_background_highlights::<DocumentHighlightWrite>(cx);
6650 return None;
6651 }
6652
6653 let debounce = EditorSettings::get_global(cx).lsp_highlight_debounce;
6654 self.document_highlights_task = Some(cx.spawn(async move |this, cx| {
6655 cx.background_executor()
6656 .timer(Duration::from_millis(debounce))
6657 .await;
6658
6659 let highlights = if let Some(highlights) = cx
6660 .update(|cx| {
6661 provider.document_highlights(&cursor_buffer, cursor_buffer_position, cx)
6662 })
6663 .ok()
6664 .flatten()
6665 {
6666 highlights.await.log_err()
6667 } else {
6668 None
6669 };
6670
6671 if let Some(highlights) = highlights {
6672 this.update(cx, |this, cx| {
6673 if this.pending_rename.is_some() {
6674 return;
6675 }
6676
6677 let buffer_id = cursor_position.buffer_id;
6678 let buffer = this.buffer.read(cx);
6679 if !buffer
6680 .text_anchor_for_position(cursor_position, cx)
6681 .map_or(false, |(buffer, _)| buffer == cursor_buffer)
6682 {
6683 return;
6684 }
6685
6686 let cursor_buffer_snapshot = cursor_buffer.read(cx);
6687 let mut write_ranges = Vec::new();
6688 let mut read_ranges = Vec::new();
6689 for highlight in highlights {
6690 for (excerpt_id, excerpt_range) in
6691 buffer.excerpts_for_buffer(cursor_buffer.read(cx).remote_id(), cx)
6692 {
6693 let start = highlight
6694 .range
6695 .start
6696 .max(&excerpt_range.context.start, cursor_buffer_snapshot);
6697 let end = highlight
6698 .range
6699 .end
6700 .min(&excerpt_range.context.end, cursor_buffer_snapshot);
6701 if start.cmp(&end, cursor_buffer_snapshot).is_ge() {
6702 continue;
6703 }
6704
6705 let range = Anchor {
6706 buffer_id,
6707 excerpt_id,
6708 text_anchor: start,
6709 diff_base_anchor: None,
6710 }..Anchor {
6711 buffer_id,
6712 excerpt_id,
6713 text_anchor: end,
6714 diff_base_anchor: None,
6715 };
6716 if highlight.kind == lsp::DocumentHighlightKind::WRITE {
6717 write_ranges.push(range);
6718 } else {
6719 read_ranges.push(range);
6720 }
6721 }
6722 }
6723
6724 this.highlight_background::<DocumentHighlightRead>(
6725 &read_ranges,
6726 |theme| theme.colors().editor_document_highlight_read_background,
6727 cx,
6728 );
6729 this.highlight_background::<DocumentHighlightWrite>(
6730 &write_ranges,
6731 |theme| theme.colors().editor_document_highlight_write_background,
6732 cx,
6733 );
6734 cx.notify();
6735 })
6736 .log_err();
6737 }
6738 }));
6739 None
6740 }
6741
6742 fn prepare_highlight_query_from_selection(
6743 &mut self,
6744 cx: &mut Context<Editor>,
6745 ) -> Option<(String, Range<Anchor>)> {
6746 if matches!(self.mode, EditorMode::SingleLine { .. }) {
6747 return None;
6748 }
6749 if !EditorSettings::get_global(cx).selection_highlight {
6750 return None;
6751 }
6752 if self.selections.count() != 1 || self.selections.line_mode {
6753 return None;
6754 }
6755 let selection = self.selections.newest::<Point>(cx);
6756 if selection.is_empty() || selection.start.row != selection.end.row {
6757 return None;
6758 }
6759 let multi_buffer_snapshot = self.buffer().read(cx).snapshot(cx);
6760 let selection_anchor_range = selection.range().to_anchors(&multi_buffer_snapshot);
6761 let query = multi_buffer_snapshot
6762 .text_for_range(selection_anchor_range.clone())
6763 .collect::<String>();
6764 if query.trim().is_empty() {
6765 return None;
6766 }
6767 Some((query, selection_anchor_range))
6768 }
6769
6770 fn update_selection_occurrence_highlights(
6771 &mut self,
6772 query_text: String,
6773 query_range: Range<Anchor>,
6774 multi_buffer_range_to_query: Range<Point>,
6775 use_debounce: bool,
6776 window: &mut Window,
6777 cx: &mut Context<Editor>,
6778 ) -> Task<()> {
6779 let multi_buffer_snapshot = self.buffer().read(cx).snapshot(cx);
6780 cx.spawn_in(window, async move |editor, cx| {
6781 if use_debounce {
6782 cx.background_executor()
6783 .timer(SELECTION_HIGHLIGHT_DEBOUNCE_TIMEOUT)
6784 .await;
6785 }
6786 let match_task = cx.background_spawn(async move {
6787 let buffer_ranges = multi_buffer_snapshot
6788 .range_to_buffer_ranges(multi_buffer_range_to_query)
6789 .into_iter()
6790 .filter(|(_, excerpt_visible_range, _)| !excerpt_visible_range.is_empty());
6791 let mut match_ranges = Vec::new();
6792 let Ok(regex) = project::search::SearchQuery::text(
6793 query_text.clone(),
6794 false,
6795 false,
6796 false,
6797 Default::default(),
6798 Default::default(),
6799 false,
6800 None,
6801 ) else {
6802 return Vec::default();
6803 };
6804 for (buffer_snapshot, search_range, excerpt_id) in buffer_ranges {
6805 match_ranges.extend(
6806 regex
6807 .search(&buffer_snapshot, Some(search_range.clone()))
6808 .await
6809 .into_iter()
6810 .filter_map(|match_range| {
6811 let match_start = buffer_snapshot
6812 .anchor_after(search_range.start + match_range.start);
6813 let match_end = buffer_snapshot
6814 .anchor_before(search_range.start + match_range.end);
6815 let match_anchor_range = Anchor::range_in_buffer(
6816 excerpt_id,
6817 buffer_snapshot.remote_id(),
6818 match_start..match_end,
6819 );
6820 (match_anchor_range != query_range).then_some(match_anchor_range)
6821 }),
6822 );
6823 }
6824 match_ranges
6825 });
6826 let match_ranges = match_task.await;
6827 editor
6828 .update_in(cx, |editor, _, cx| {
6829 editor.clear_background_highlights::<SelectedTextHighlight>(cx);
6830 if !match_ranges.is_empty() {
6831 editor.highlight_background::<SelectedTextHighlight>(
6832 &match_ranges,
6833 |theme| theme.colors().editor_document_highlight_bracket_background,
6834 cx,
6835 )
6836 }
6837 })
6838 .log_err();
6839 })
6840 }
6841
6842 fn refresh_single_line_folds(&mut self, window: &mut Window, cx: &mut Context<Editor>) {
6843 struct NewlineFold;
6844 let type_id = std::any::TypeId::of::<NewlineFold>();
6845 if !self.mode.is_single_line() {
6846 return;
6847 }
6848 let snapshot = self.snapshot(window, cx);
6849 if snapshot.buffer_snapshot.max_point().row == 0 {
6850 return;
6851 }
6852 let task = cx.background_spawn(async move {
6853 let new_newlines = snapshot
6854 .buffer_chars_at(0)
6855 .filter_map(|(c, i)| {
6856 if c == '\n' {
6857 Some(
6858 snapshot.buffer_snapshot.anchor_after(i)
6859 ..snapshot.buffer_snapshot.anchor_before(i + 1),
6860 )
6861 } else {
6862 None
6863 }
6864 })
6865 .collect::<Vec<_>>();
6866 let existing_newlines = snapshot
6867 .folds_in_range(0..snapshot.buffer_snapshot.len())
6868 .filter_map(|fold| {
6869 if fold.placeholder.type_tag == Some(type_id) {
6870 Some(fold.range.start..fold.range.end)
6871 } else {
6872 None
6873 }
6874 })
6875 .collect::<Vec<_>>();
6876
6877 (new_newlines, existing_newlines)
6878 });
6879 self.folding_newlines = cx.spawn(async move |this, cx| {
6880 let (new_newlines, existing_newlines) = task.await;
6881 if new_newlines == existing_newlines {
6882 return;
6883 }
6884 let placeholder = FoldPlaceholder {
6885 render: Arc::new(move |_, _, cx| {
6886 div()
6887 .bg(cx.theme().status().hint_background)
6888 .border_b_1()
6889 .size_full()
6890 .font(ThemeSettings::get_global(cx).buffer_font.clone())
6891 .border_color(cx.theme().status().hint)
6892 .child("\\n")
6893 .into_any()
6894 }),
6895 constrain_width: false,
6896 merge_adjacent: false,
6897 type_tag: Some(type_id),
6898 };
6899 let creases = new_newlines
6900 .into_iter()
6901 .map(|range| Crease::simple(range, placeholder.clone()))
6902 .collect();
6903 this.update(cx, |this, cx| {
6904 this.display_map.update(cx, |display_map, cx| {
6905 display_map.remove_folds_with_type(existing_newlines, type_id, cx);
6906 display_map.fold(creases, cx);
6907 });
6908 })
6909 .ok();
6910 });
6911 }
6912
6913 fn refresh_selected_text_highlights(
6914 &mut self,
6915 on_buffer_edit: bool,
6916 window: &mut Window,
6917 cx: &mut Context<Editor>,
6918 ) {
6919 let Some((query_text, query_range)) = self.prepare_highlight_query_from_selection(cx)
6920 else {
6921 self.clear_background_highlights::<SelectedTextHighlight>(cx);
6922 self.quick_selection_highlight_task.take();
6923 self.debounced_selection_highlight_task.take();
6924 return;
6925 };
6926 let multi_buffer_snapshot = self.buffer().read(cx).snapshot(cx);
6927 if on_buffer_edit
6928 || self
6929 .quick_selection_highlight_task
6930 .as_ref()
6931 .map_or(true, |(prev_anchor_range, _)| {
6932 prev_anchor_range != &query_range
6933 })
6934 {
6935 let multi_buffer_visible_start = self
6936 .scroll_manager
6937 .anchor()
6938 .anchor
6939 .to_point(&multi_buffer_snapshot);
6940 let multi_buffer_visible_end = multi_buffer_snapshot.clip_point(
6941 multi_buffer_visible_start
6942 + Point::new(self.visible_line_count().unwrap_or(0.).ceil() as u32, 0),
6943 Bias::Left,
6944 );
6945 let multi_buffer_visible_range = multi_buffer_visible_start..multi_buffer_visible_end;
6946 self.quick_selection_highlight_task = Some((
6947 query_range.clone(),
6948 self.update_selection_occurrence_highlights(
6949 query_text.clone(),
6950 query_range.clone(),
6951 multi_buffer_visible_range,
6952 false,
6953 window,
6954 cx,
6955 ),
6956 ));
6957 }
6958 if on_buffer_edit
6959 || self
6960 .debounced_selection_highlight_task
6961 .as_ref()
6962 .map_or(true, |(prev_anchor_range, _)| {
6963 prev_anchor_range != &query_range
6964 })
6965 {
6966 let multi_buffer_start = multi_buffer_snapshot
6967 .anchor_before(0)
6968 .to_point(&multi_buffer_snapshot);
6969 let multi_buffer_end = multi_buffer_snapshot
6970 .anchor_after(multi_buffer_snapshot.len())
6971 .to_point(&multi_buffer_snapshot);
6972 let multi_buffer_full_range = multi_buffer_start..multi_buffer_end;
6973 self.debounced_selection_highlight_task = Some((
6974 query_range.clone(),
6975 self.update_selection_occurrence_highlights(
6976 query_text,
6977 query_range,
6978 multi_buffer_full_range,
6979 true,
6980 window,
6981 cx,
6982 ),
6983 ));
6984 }
6985 }
6986
6987 pub fn refresh_inline_completion(
6988 &mut self,
6989 debounce: bool,
6990 user_requested: bool,
6991 window: &mut Window,
6992 cx: &mut Context<Self>,
6993 ) -> Option<()> {
6994 let provider = self.edit_prediction_provider()?;
6995 let cursor = self.selections.newest_anchor().head();
6996 let (buffer, cursor_buffer_position) =
6997 self.buffer.read(cx).text_anchor_for_position(cursor, cx)?;
6998
6999 if !self.edit_predictions_enabled_in_buffer(&buffer, cursor_buffer_position, cx) {
7000 self.discard_inline_completion(false, cx);
7001 return None;
7002 }
7003
7004 if !user_requested
7005 && (!self.should_show_edit_predictions()
7006 || !self.is_focused(window)
7007 || buffer.read(cx).is_empty())
7008 {
7009 self.discard_inline_completion(false, cx);
7010 return None;
7011 }
7012
7013 self.update_visible_inline_completion(window, cx);
7014 provider.refresh(
7015 self.project.clone(),
7016 buffer,
7017 cursor_buffer_position,
7018 debounce,
7019 cx,
7020 );
7021 Some(())
7022 }
7023
7024 fn show_edit_predictions_in_menu(&self) -> bool {
7025 match self.edit_prediction_settings {
7026 EditPredictionSettings::Disabled => false,
7027 EditPredictionSettings::Enabled { show_in_menu, .. } => show_in_menu,
7028 }
7029 }
7030
7031 pub fn edit_predictions_enabled(&self) -> bool {
7032 match self.edit_prediction_settings {
7033 EditPredictionSettings::Disabled => false,
7034 EditPredictionSettings::Enabled { .. } => true,
7035 }
7036 }
7037
7038 fn edit_prediction_requires_modifier(&self) -> bool {
7039 match self.edit_prediction_settings {
7040 EditPredictionSettings::Disabled => false,
7041 EditPredictionSettings::Enabled {
7042 preview_requires_modifier,
7043 ..
7044 } => preview_requires_modifier,
7045 }
7046 }
7047
7048 pub fn update_edit_prediction_settings(&mut self, cx: &mut Context<Self>) {
7049 if self.edit_prediction_provider.is_none() {
7050 self.edit_prediction_settings = EditPredictionSettings::Disabled;
7051 } else {
7052 let selection = self.selections.newest_anchor();
7053 let cursor = selection.head();
7054
7055 if let Some((buffer, cursor_buffer_position)) =
7056 self.buffer.read(cx).text_anchor_for_position(cursor, cx)
7057 {
7058 self.edit_prediction_settings =
7059 self.edit_prediction_settings_at_position(&buffer, cursor_buffer_position, cx);
7060 }
7061 }
7062 }
7063
7064 fn edit_prediction_settings_at_position(
7065 &self,
7066 buffer: &Entity<Buffer>,
7067 buffer_position: language::Anchor,
7068 cx: &App,
7069 ) -> EditPredictionSettings {
7070 if !self.mode.is_full()
7071 || !self.show_inline_completions_override.unwrap_or(true)
7072 || self.inline_completions_disabled_in_scope(buffer, buffer_position, cx)
7073 {
7074 return EditPredictionSettings::Disabled;
7075 }
7076
7077 let buffer = buffer.read(cx);
7078
7079 let file = buffer.file();
7080
7081 if !language_settings(buffer.language().map(|l| l.name()), file, cx).show_edit_predictions {
7082 return EditPredictionSettings::Disabled;
7083 };
7084
7085 let by_provider = matches!(
7086 self.menu_inline_completions_policy,
7087 MenuInlineCompletionsPolicy::ByProvider
7088 );
7089
7090 let show_in_menu = by_provider
7091 && self
7092 .edit_prediction_provider
7093 .as_ref()
7094 .map_or(false, |provider| {
7095 provider.provider.show_completions_in_menu()
7096 });
7097
7098 let preview_requires_modifier =
7099 all_language_settings(file, cx).edit_predictions_mode() == EditPredictionsMode::Subtle;
7100
7101 EditPredictionSettings::Enabled {
7102 show_in_menu,
7103 preview_requires_modifier,
7104 }
7105 }
7106
7107 fn should_show_edit_predictions(&self) -> bool {
7108 self.snippet_stack.is_empty() && self.edit_predictions_enabled()
7109 }
7110
7111 pub fn edit_prediction_preview_is_active(&self) -> bool {
7112 matches!(
7113 self.edit_prediction_preview,
7114 EditPredictionPreview::Active { .. }
7115 )
7116 }
7117
7118 pub fn edit_predictions_enabled_at_cursor(&self, cx: &App) -> bool {
7119 let cursor = self.selections.newest_anchor().head();
7120 if let Some((buffer, cursor_position)) =
7121 self.buffer.read(cx).text_anchor_for_position(cursor, cx)
7122 {
7123 self.edit_predictions_enabled_in_buffer(&buffer, cursor_position, cx)
7124 } else {
7125 false
7126 }
7127 }
7128
7129 pub fn supports_minimap(&self, cx: &App) -> bool {
7130 !self.minimap_visibility.disabled() && self.is_singleton(cx)
7131 }
7132
7133 fn edit_predictions_enabled_in_buffer(
7134 &self,
7135 buffer: &Entity<Buffer>,
7136 buffer_position: language::Anchor,
7137 cx: &App,
7138 ) -> bool {
7139 maybe!({
7140 if self.read_only(cx) {
7141 return Some(false);
7142 }
7143 let provider = self.edit_prediction_provider()?;
7144 if !provider.is_enabled(&buffer, buffer_position, cx) {
7145 return Some(false);
7146 }
7147 let buffer = buffer.read(cx);
7148 let Some(file) = buffer.file() else {
7149 return Some(true);
7150 };
7151 let settings = all_language_settings(Some(file), cx);
7152 Some(settings.edit_predictions_enabled_for_file(file, cx))
7153 })
7154 .unwrap_or(false)
7155 }
7156
7157 fn cycle_inline_completion(
7158 &mut self,
7159 direction: Direction,
7160 window: &mut Window,
7161 cx: &mut Context<Self>,
7162 ) -> Option<()> {
7163 let provider = self.edit_prediction_provider()?;
7164 let cursor = self.selections.newest_anchor().head();
7165 let (buffer, cursor_buffer_position) =
7166 self.buffer.read(cx).text_anchor_for_position(cursor, cx)?;
7167 if self.inline_completions_hidden_for_vim_mode || !self.should_show_edit_predictions() {
7168 return None;
7169 }
7170
7171 provider.cycle(buffer, cursor_buffer_position, direction, cx);
7172 self.update_visible_inline_completion(window, cx);
7173
7174 Some(())
7175 }
7176
7177 pub fn show_inline_completion(
7178 &mut self,
7179 _: &ShowEditPrediction,
7180 window: &mut Window,
7181 cx: &mut Context<Self>,
7182 ) {
7183 if !self.has_active_inline_completion() {
7184 self.refresh_inline_completion(false, true, window, cx);
7185 return;
7186 }
7187
7188 self.update_visible_inline_completion(window, cx);
7189 }
7190
7191 pub fn display_cursor_names(
7192 &mut self,
7193 _: &DisplayCursorNames,
7194 window: &mut Window,
7195 cx: &mut Context<Self>,
7196 ) {
7197 self.show_cursor_names(window, cx);
7198 }
7199
7200 fn show_cursor_names(&mut self, window: &mut Window, cx: &mut Context<Self>) {
7201 self.show_cursor_names = true;
7202 cx.notify();
7203 cx.spawn_in(window, async move |this, cx| {
7204 cx.background_executor().timer(CURSORS_VISIBLE_FOR).await;
7205 this.update(cx, |this, cx| {
7206 this.show_cursor_names = false;
7207 cx.notify()
7208 })
7209 .ok()
7210 })
7211 .detach();
7212 }
7213
7214 pub fn next_edit_prediction(
7215 &mut self,
7216 _: &NextEditPrediction,
7217 window: &mut Window,
7218 cx: &mut Context<Self>,
7219 ) {
7220 if self.has_active_inline_completion() {
7221 self.cycle_inline_completion(Direction::Next, window, cx);
7222 } else {
7223 let is_copilot_disabled = self
7224 .refresh_inline_completion(false, true, window, cx)
7225 .is_none();
7226 if is_copilot_disabled {
7227 cx.propagate();
7228 }
7229 }
7230 }
7231
7232 pub fn previous_edit_prediction(
7233 &mut self,
7234 _: &PreviousEditPrediction,
7235 window: &mut Window,
7236 cx: &mut Context<Self>,
7237 ) {
7238 if self.has_active_inline_completion() {
7239 self.cycle_inline_completion(Direction::Prev, window, cx);
7240 } else {
7241 let is_copilot_disabled = self
7242 .refresh_inline_completion(false, true, window, cx)
7243 .is_none();
7244 if is_copilot_disabled {
7245 cx.propagate();
7246 }
7247 }
7248 }
7249
7250 pub fn accept_edit_prediction(
7251 &mut self,
7252 _: &AcceptEditPrediction,
7253 window: &mut Window,
7254 cx: &mut Context<Self>,
7255 ) {
7256 if self.show_edit_predictions_in_menu() {
7257 self.hide_context_menu(window, cx);
7258 }
7259
7260 let Some(active_inline_completion) = self.active_inline_completion.as_ref() else {
7261 return;
7262 };
7263
7264 self.report_inline_completion_event(
7265 active_inline_completion.completion_id.clone(),
7266 true,
7267 cx,
7268 );
7269
7270 match &active_inline_completion.completion {
7271 InlineCompletion::Move { target, .. } => {
7272 let target = *target;
7273
7274 if let Some(position_map) = &self.last_position_map {
7275 if position_map
7276 .visible_row_range
7277 .contains(&target.to_display_point(&position_map.snapshot).row())
7278 || !self.edit_prediction_requires_modifier()
7279 {
7280 self.unfold_ranges(&[target..target], true, false, cx);
7281 // Note that this is also done in vim's handler of the Tab action.
7282 self.change_selections(
7283 SelectionEffects::scroll(Autoscroll::newest()),
7284 window,
7285 cx,
7286 |selections| {
7287 selections.select_anchor_ranges([target..target]);
7288 },
7289 );
7290 self.clear_row_highlights::<EditPredictionPreview>();
7291
7292 self.edit_prediction_preview
7293 .set_previous_scroll_position(None);
7294 } else {
7295 self.edit_prediction_preview
7296 .set_previous_scroll_position(Some(
7297 position_map.snapshot.scroll_anchor,
7298 ));
7299
7300 self.highlight_rows::<EditPredictionPreview>(
7301 target..target,
7302 cx.theme().colors().editor_highlighted_line_background,
7303 RowHighlightOptions {
7304 autoscroll: true,
7305 ..Default::default()
7306 },
7307 cx,
7308 );
7309 self.request_autoscroll(Autoscroll::fit(), cx);
7310 }
7311 }
7312 }
7313 InlineCompletion::Edit { edits, .. } => {
7314 if let Some(provider) = self.edit_prediction_provider() {
7315 provider.accept(cx);
7316 }
7317
7318 // Store the transaction ID and selections before applying the edit
7319 let transaction_id_prev = self.buffer.read(cx).last_transaction_id(cx);
7320
7321 let snapshot = self.buffer.read(cx).snapshot(cx);
7322 let last_edit_end = edits.last().unwrap().0.end.bias_right(&snapshot);
7323
7324 self.buffer.update(cx, |buffer, cx| {
7325 buffer.edit(edits.iter().cloned(), None, cx)
7326 });
7327
7328 self.change_selections(SelectionEffects::no_scroll(), window, cx, |s| {
7329 s.select_anchor_ranges([last_edit_end..last_edit_end]);
7330 });
7331
7332 let selections = self.selections.disjoint_anchors();
7333 if let Some(transaction_id_now) = self.buffer.read(cx).last_transaction_id(cx) {
7334 let has_new_transaction = transaction_id_prev != Some(transaction_id_now);
7335 if has_new_transaction {
7336 self.selection_history
7337 .insert_transaction(transaction_id_now, selections);
7338 }
7339 }
7340
7341 self.update_visible_inline_completion(window, cx);
7342 if self.active_inline_completion.is_none() {
7343 self.refresh_inline_completion(true, true, window, cx);
7344 }
7345
7346 cx.notify();
7347 }
7348 }
7349
7350 self.edit_prediction_requires_modifier_in_indent_conflict = false;
7351 }
7352
7353 pub fn accept_partial_inline_completion(
7354 &mut self,
7355 _: &AcceptPartialEditPrediction,
7356 window: &mut Window,
7357 cx: &mut Context<Self>,
7358 ) {
7359 let Some(active_inline_completion) = self.active_inline_completion.as_ref() else {
7360 return;
7361 };
7362 if self.selections.count() != 1 {
7363 return;
7364 }
7365
7366 self.report_inline_completion_event(
7367 active_inline_completion.completion_id.clone(),
7368 true,
7369 cx,
7370 );
7371
7372 match &active_inline_completion.completion {
7373 InlineCompletion::Move { target, .. } => {
7374 let target = *target;
7375 self.change_selections(
7376 SelectionEffects::scroll(Autoscroll::newest()),
7377 window,
7378 cx,
7379 |selections| {
7380 selections.select_anchor_ranges([target..target]);
7381 },
7382 );
7383 }
7384 InlineCompletion::Edit { edits, .. } => {
7385 // Find an insertion that starts at the cursor position.
7386 let snapshot = self.buffer.read(cx).snapshot(cx);
7387 let cursor_offset = self.selections.newest::<usize>(cx).head();
7388 let insertion = edits.iter().find_map(|(range, text)| {
7389 let range = range.to_offset(&snapshot);
7390 if range.is_empty() && range.start == cursor_offset {
7391 Some(text)
7392 } else {
7393 None
7394 }
7395 });
7396
7397 if let Some(text) = insertion {
7398 let mut partial_completion = text
7399 .chars()
7400 .by_ref()
7401 .take_while(|c| c.is_alphabetic())
7402 .collect::<String>();
7403 if partial_completion.is_empty() {
7404 partial_completion = text
7405 .chars()
7406 .by_ref()
7407 .take_while(|c| c.is_whitespace() || !c.is_alphabetic())
7408 .collect::<String>();
7409 }
7410
7411 cx.emit(EditorEvent::InputHandled {
7412 utf16_range_to_replace: None,
7413 text: partial_completion.clone().into(),
7414 });
7415
7416 self.insert_with_autoindent_mode(&partial_completion, None, window, cx);
7417
7418 self.refresh_inline_completion(true, true, window, cx);
7419 cx.notify();
7420 } else {
7421 self.accept_edit_prediction(&Default::default(), window, cx);
7422 }
7423 }
7424 }
7425 }
7426
7427 fn discard_inline_completion(
7428 &mut self,
7429 should_report_inline_completion_event: bool,
7430 cx: &mut Context<Self>,
7431 ) -> bool {
7432 if should_report_inline_completion_event {
7433 let completion_id = self
7434 .active_inline_completion
7435 .as_ref()
7436 .and_then(|active_completion| active_completion.completion_id.clone());
7437
7438 self.report_inline_completion_event(completion_id, false, cx);
7439 }
7440
7441 if let Some(provider) = self.edit_prediction_provider() {
7442 provider.discard(cx);
7443 }
7444
7445 self.take_active_inline_completion(cx)
7446 }
7447
7448 fn report_inline_completion_event(&self, id: Option<SharedString>, accepted: bool, cx: &App) {
7449 let Some(provider) = self.edit_prediction_provider() else {
7450 return;
7451 };
7452
7453 let Some((_, buffer, _)) = self
7454 .buffer
7455 .read(cx)
7456 .excerpt_containing(self.selections.newest_anchor().head(), cx)
7457 else {
7458 return;
7459 };
7460
7461 let extension = buffer
7462 .read(cx)
7463 .file()
7464 .and_then(|file| Some(file.path().extension()?.to_string_lossy().to_string()));
7465
7466 let event_type = match accepted {
7467 true => "Edit Prediction Accepted",
7468 false => "Edit Prediction Discarded",
7469 };
7470 telemetry::event!(
7471 event_type,
7472 provider = provider.name(),
7473 prediction_id = id,
7474 suggestion_accepted = accepted,
7475 file_extension = extension,
7476 );
7477 }
7478
7479 pub fn has_active_inline_completion(&self) -> bool {
7480 self.active_inline_completion.is_some()
7481 }
7482
7483 fn take_active_inline_completion(&mut self, cx: &mut Context<Self>) -> bool {
7484 let Some(active_inline_completion) = self.active_inline_completion.take() else {
7485 return false;
7486 };
7487
7488 self.splice_inlays(&active_inline_completion.inlay_ids, Default::default(), cx);
7489 self.clear_highlights::<InlineCompletionHighlight>(cx);
7490 self.stale_inline_completion_in_menu = Some(active_inline_completion);
7491 true
7492 }
7493
7494 /// Returns true when we're displaying the edit prediction popover below the cursor
7495 /// like we are not previewing and the LSP autocomplete menu is visible
7496 /// or we are in `when_holding_modifier` mode.
7497 pub fn edit_prediction_visible_in_cursor_popover(&self, has_completion: bool) -> bool {
7498 if self.edit_prediction_preview_is_active()
7499 || !self.show_edit_predictions_in_menu()
7500 || !self.edit_predictions_enabled()
7501 {
7502 return false;
7503 }
7504
7505 if self.has_visible_completions_menu() {
7506 return true;
7507 }
7508
7509 has_completion && self.edit_prediction_requires_modifier()
7510 }
7511
7512 fn handle_modifiers_changed(
7513 &mut self,
7514 modifiers: Modifiers,
7515 position_map: &PositionMap,
7516 window: &mut Window,
7517 cx: &mut Context<Self>,
7518 ) {
7519 if self.show_edit_predictions_in_menu() {
7520 self.update_edit_prediction_preview(&modifiers, window, cx);
7521 }
7522
7523 self.update_selection_mode(&modifiers, position_map, window, cx);
7524
7525 let mouse_position = window.mouse_position();
7526 if !position_map.text_hitbox.is_hovered(window) {
7527 return;
7528 }
7529
7530 self.update_hovered_link(
7531 position_map.point_for_position(mouse_position),
7532 &position_map.snapshot,
7533 modifiers,
7534 window,
7535 cx,
7536 )
7537 }
7538
7539 fn multi_cursor_modifier(invert: bool, modifiers: &Modifiers, cx: &mut Context<Self>) -> bool {
7540 let multi_cursor_setting = EditorSettings::get_global(cx).multi_cursor_modifier;
7541 if invert {
7542 match multi_cursor_setting {
7543 MultiCursorModifier::Alt => modifiers.alt,
7544 MultiCursorModifier::CmdOrCtrl => modifiers.secondary(),
7545 }
7546 } else {
7547 match multi_cursor_setting {
7548 MultiCursorModifier::Alt => modifiers.secondary(),
7549 MultiCursorModifier::CmdOrCtrl => modifiers.alt,
7550 }
7551 }
7552 }
7553
7554 fn columnar_selection_mode(
7555 modifiers: &Modifiers,
7556 cx: &mut Context<Self>,
7557 ) -> Option<ColumnarMode> {
7558 if modifiers.shift && modifiers.number_of_modifiers() == 2 {
7559 if Self::multi_cursor_modifier(false, modifiers, cx) {
7560 Some(ColumnarMode::FromMouse)
7561 } else if Self::multi_cursor_modifier(true, modifiers, cx) {
7562 Some(ColumnarMode::FromSelection)
7563 } else {
7564 None
7565 }
7566 } else {
7567 None
7568 }
7569 }
7570
7571 fn update_selection_mode(
7572 &mut self,
7573 modifiers: &Modifiers,
7574 position_map: &PositionMap,
7575 window: &mut Window,
7576 cx: &mut Context<Self>,
7577 ) {
7578 let Some(mode) = Self::columnar_selection_mode(modifiers, cx) else {
7579 return;
7580 };
7581 if self.selections.pending.is_none() {
7582 return;
7583 }
7584
7585 let mouse_position = window.mouse_position();
7586 let point_for_position = position_map.point_for_position(mouse_position);
7587 let position = point_for_position.previous_valid;
7588
7589 self.select(
7590 SelectPhase::BeginColumnar {
7591 position,
7592 reset: false,
7593 mode,
7594 goal_column: point_for_position.exact_unclipped.column(),
7595 },
7596 window,
7597 cx,
7598 );
7599 }
7600
7601 fn update_edit_prediction_preview(
7602 &mut self,
7603 modifiers: &Modifiers,
7604 window: &mut Window,
7605 cx: &mut Context<Self>,
7606 ) {
7607 let mut modifiers_held = false;
7608 if let Some(accept_keystroke) = self
7609 .accept_edit_prediction_keybind(false, window, cx)
7610 .keystroke()
7611 {
7612 modifiers_held = modifiers_held
7613 || (&accept_keystroke.modifiers == modifiers
7614 && accept_keystroke.modifiers.modified());
7615 };
7616 if let Some(accept_partial_keystroke) = self
7617 .accept_edit_prediction_keybind(true, window, cx)
7618 .keystroke()
7619 {
7620 modifiers_held = modifiers_held
7621 || (&accept_partial_keystroke.modifiers == modifiers
7622 && accept_partial_keystroke.modifiers.modified());
7623 }
7624
7625 if modifiers_held {
7626 if matches!(
7627 self.edit_prediction_preview,
7628 EditPredictionPreview::Inactive { .. }
7629 ) {
7630 self.edit_prediction_preview = EditPredictionPreview::Active {
7631 previous_scroll_position: None,
7632 since: Instant::now(),
7633 };
7634
7635 self.update_visible_inline_completion(window, cx);
7636 cx.notify();
7637 }
7638 } else if let EditPredictionPreview::Active {
7639 previous_scroll_position,
7640 since,
7641 } = self.edit_prediction_preview
7642 {
7643 if let (Some(previous_scroll_position), Some(position_map)) =
7644 (previous_scroll_position, self.last_position_map.as_ref())
7645 {
7646 self.set_scroll_position(
7647 previous_scroll_position
7648 .scroll_position(&position_map.snapshot.display_snapshot),
7649 window,
7650 cx,
7651 );
7652 }
7653
7654 self.edit_prediction_preview = EditPredictionPreview::Inactive {
7655 released_too_fast: since.elapsed() < Duration::from_millis(200),
7656 };
7657 self.clear_row_highlights::<EditPredictionPreview>();
7658 self.update_visible_inline_completion(window, cx);
7659 cx.notify();
7660 }
7661 }
7662
7663 fn update_visible_inline_completion(
7664 &mut self,
7665 _window: &mut Window,
7666 cx: &mut Context<Self>,
7667 ) -> Option<()> {
7668 let selection = self.selections.newest_anchor();
7669 let cursor = selection.head();
7670 let multibuffer = self.buffer.read(cx).snapshot(cx);
7671 let offset_selection = selection.map(|endpoint| endpoint.to_offset(&multibuffer));
7672 let excerpt_id = cursor.excerpt_id;
7673
7674 let show_in_menu = self.show_edit_predictions_in_menu();
7675 let completions_menu_has_precedence = !show_in_menu
7676 && (self.context_menu.borrow().is_some()
7677 || (!self.completion_tasks.is_empty() && !self.has_active_inline_completion()));
7678
7679 if completions_menu_has_precedence
7680 || !offset_selection.is_empty()
7681 || self
7682 .active_inline_completion
7683 .as_ref()
7684 .map_or(false, |completion| {
7685 let invalidation_range = completion.invalidation_range.to_offset(&multibuffer);
7686 let invalidation_range = invalidation_range.start..=invalidation_range.end;
7687 !invalidation_range.contains(&offset_selection.head())
7688 })
7689 {
7690 self.discard_inline_completion(false, cx);
7691 return None;
7692 }
7693
7694 self.take_active_inline_completion(cx);
7695 let Some(provider) = self.edit_prediction_provider() else {
7696 self.edit_prediction_settings = EditPredictionSettings::Disabled;
7697 return None;
7698 };
7699
7700 let (buffer, cursor_buffer_position) =
7701 self.buffer.read(cx).text_anchor_for_position(cursor, cx)?;
7702
7703 self.edit_prediction_settings =
7704 self.edit_prediction_settings_at_position(&buffer, cursor_buffer_position, cx);
7705
7706 self.edit_prediction_indent_conflict = multibuffer.is_line_whitespace_upto(cursor);
7707
7708 if self.edit_prediction_indent_conflict {
7709 let cursor_point = cursor.to_point(&multibuffer);
7710
7711 let indents = multibuffer.suggested_indents(cursor_point.row..cursor_point.row + 1, cx);
7712
7713 if let Some((_, indent)) = indents.iter().next() {
7714 if indent.len == cursor_point.column {
7715 self.edit_prediction_indent_conflict = false;
7716 }
7717 }
7718 }
7719
7720 let inline_completion = provider.suggest(&buffer, cursor_buffer_position, cx)?;
7721 let edits = inline_completion
7722 .edits
7723 .into_iter()
7724 .flat_map(|(range, new_text)| {
7725 let start = multibuffer.anchor_in_excerpt(excerpt_id, range.start)?;
7726 let end = multibuffer.anchor_in_excerpt(excerpt_id, range.end)?;
7727 Some((start..end, new_text))
7728 })
7729 .collect::<Vec<_>>();
7730 if edits.is_empty() {
7731 return None;
7732 }
7733
7734 let first_edit_start = edits.first().unwrap().0.start;
7735 let first_edit_start_point = first_edit_start.to_point(&multibuffer);
7736 let edit_start_row = first_edit_start_point.row.saturating_sub(2);
7737
7738 let last_edit_end = edits.last().unwrap().0.end;
7739 let last_edit_end_point = last_edit_end.to_point(&multibuffer);
7740 let edit_end_row = cmp::min(multibuffer.max_point().row, last_edit_end_point.row + 2);
7741
7742 let cursor_row = cursor.to_point(&multibuffer).row;
7743
7744 let snapshot = multibuffer.buffer_for_excerpt(excerpt_id).cloned()?;
7745
7746 let mut inlay_ids = Vec::new();
7747 let invalidation_row_range;
7748 let move_invalidation_row_range = if cursor_row < edit_start_row {
7749 Some(cursor_row..edit_end_row)
7750 } else if cursor_row > edit_end_row {
7751 Some(edit_start_row..cursor_row)
7752 } else {
7753 None
7754 };
7755 let is_move =
7756 move_invalidation_row_range.is_some() || self.inline_completions_hidden_for_vim_mode;
7757 let completion = if is_move {
7758 invalidation_row_range =
7759 move_invalidation_row_range.unwrap_or(edit_start_row..edit_end_row);
7760 let target = first_edit_start;
7761 InlineCompletion::Move { target, snapshot }
7762 } else {
7763 let show_completions_in_buffer = !self.edit_prediction_visible_in_cursor_popover(true)
7764 && !self.inline_completions_hidden_for_vim_mode;
7765
7766 if show_completions_in_buffer {
7767 if edits
7768 .iter()
7769 .all(|(range, _)| range.to_offset(&multibuffer).is_empty())
7770 {
7771 let mut inlays = Vec::new();
7772 for (range, new_text) in &edits {
7773 let inlay = Inlay::inline_completion(
7774 post_inc(&mut self.next_inlay_id),
7775 range.start,
7776 new_text.as_str(),
7777 );
7778 inlay_ids.push(inlay.id);
7779 inlays.push(inlay);
7780 }
7781
7782 self.splice_inlays(&[], inlays, cx);
7783 } else {
7784 let background_color = cx.theme().status().deleted_background;
7785 self.highlight_text::<InlineCompletionHighlight>(
7786 edits.iter().map(|(range, _)| range.clone()).collect(),
7787 HighlightStyle {
7788 background_color: Some(background_color),
7789 ..Default::default()
7790 },
7791 cx,
7792 );
7793 }
7794 }
7795
7796 invalidation_row_range = edit_start_row..edit_end_row;
7797
7798 let display_mode = if all_edits_insertions_or_deletions(&edits, &multibuffer) {
7799 if provider.show_tab_accept_marker() {
7800 EditDisplayMode::TabAccept
7801 } else {
7802 EditDisplayMode::Inline
7803 }
7804 } else {
7805 EditDisplayMode::DiffPopover
7806 };
7807
7808 InlineCompletion::Edit {
7809 edits,
7810 edit_preview: inline_completion.edit_preview,
7811 display_mode,
7812 snapshot,
7813 }
7814 };
7815
7816 let invalidation_range = multibuffer
7817 .anchor_before(Point::new(invalidation_row_range.start, 0))
7818 ..multibuffer.anchor_after(Point::new(
7819 invalidation_row_range.end,
7820 multibuffer.line_len(MultiBufferRow(invalidation_row_range.end)),
7821 ));
7822
7823 self.stale_inline_completion_in_menu = None;
7824 self.active_inline_completion = Some(InlineCompletionState {
7825 inlay_ids,
7826 completion,
7827 completion_id: inline_completion.id,
7828 invalidation_range,
7829 });
7830
7831 cx.notify();
7832
7833 Some(())
7834 }
7835
7836 pub fn edit_prediction_provider(&self) -> Option<Arc<dyn InlineCompletionProviderHandle>> {
7837 Some(self.edit_prediction_provider.as_ref()?.provider.clone())
7838 }
7839
7840 fn clear_tasks(&mut self) {
7841 self.tasks.clear()
7842 }
7843
7844 fn insert_tasks(&mut self, key: (BufferId, BufferRow), value: RunnableTasks) {
7845 if self.tasks.insert(key, value).is_some() {
7846 // This case should hopefully be rare, but just in case...
7847 log::error!(
7848 "multiple different run targets found on a single line, only the last target will be rendered"
7849 )
7850 }
7851 }
7852
7853 /// Get all display points of breakpoints that will be rendered within editor
7854 ///
7855 /// This function is used to handle overlaps between breakpoints and Code action/runner symbol.
7856 /// It's also used to set the color of line numbers with breakpoints to the breakpoint color.
7857 /// TODO debugger: Use this function to color toggle symbols that house nested breakpoints
7858 fn active_breakpoints(
7859 &self,
7860 range: Range<DisplayRow>,
7861 window: &mut Window,
7862 cx: &mut Context<Self>,
7863 ) -> HashMap<DisplayRow, (Anchor, Breakpoint, Option<BreakpointSessionState>)> {
7864 let mut breakpoint_display_points = HashMap::default();
7865
7866 let Some(breakpoint_store) = self.breakpoint_store.clone() else {
7867 return breakpoint_display_points;
7868 };
7869
7870 let snapshot = self.snapshot(window, cx);
7871
7872 let multi_buffer_snapshot = &snapshot.display_snapshot.buffer_snapshot;
7873 let Some(project) = self.project.as_ref() else {
7874 return breakpoint_display_points;
7875 };
7876
7877 let range = snapshot.display_point_to_point(DisplayPoint::new(range.start, 0), Bias::Left)
7878 ..snapshot.display_point_to_point(DisplayPoint::new(range.end, 0), Bias::Right);
7879
7880 for (buffer_snapshot, range, excerpt_id) in
7881 multi_buffer_snapshot.range_to_buffer_ranges(range)
7882 {
7883 let Some(buffer) = project
7884 .read(cx)
7885 .buffer_for_id(buffer_snapshot.remote_id(), cx)
7886 else {
7887 continue;
7888 };
7889 let breakpoints = breakpoint_store.read(cx).breakpoints(
7890 &buffer,
7891 Some(
7892 buffer_snapshot.anchor_before(range.start)
7893 ..buffer_snapshot.anchor_after(range.end),
7894 ),
7895 buffer_snapshot,
7896 cx,
7897 );
7898 for (breakpoint, state) in breakpoints {
7899 let multi_buffer_anchor =
7900 Anchor::in_buffer(excerpt_id, buffer_snapshot.remote_id(), breakpoint.position);
7901 let position = multi_buffer_anchor
7902 .to_point(&multi_buffer_snapshot)
7903 .to_display_point(&snapshot);
7904
7905 breakpoint_display_points.insert(
7906 position.row(),
7907 (multi_buffer_anchor, breakpoint.bp.clone(), state),
7908 );
7909 }
7910 }
7911
7912 breakpoint_display_points
7913 }
7914
7915 fn breakpoint_context_menu(
7916 &self,
7917 anchor: Anchor,
7918 window: &mut Window,
7919 cx: &mut Context<Self>,
7920 ) -> Entity<ui::ContextMenu> {
7921 let weak_editor = cx.weak_entity();
7922 let focus_handle = self.focus_handle(cx);
7923
7924 let row = self
7925 .buffer
7926 .read(cx)
7927 .snapshot(cx)
7928 .summary_for_anchor::<Point>(&anchor)
7929 .row;
7930
7931 let breakpoint = self
7932 .breakpoint_at_row(row, window, cx)
7933 .map(|(anchor, bp)| (anchor, Arc::from(bp)));
7934
7935 let log_breakpoint_msg = if breakpoint.as_ref().is_some_and(|bp| bp.1.message.is_some()) {
7936 "Edit Log Breakpoint"
7937 } else {
7938 "Set Log Breakpoint"
7939 };
7940
7941 let condition_breakpoint_msg = if breakpoint
7942 .as_ref()
7943 .is_some_and(|bp| bp.1.condition.is_some())
7944 {
7945 "Edit Condition Breakpoint"
7946 } else {
7947 "Set Condition Breakpoint"
7948 };
7949
7950 let hit_condition_breakpoint_msg = if breakpoint
7951 .as_ref()
7952 .is_some_and(|bp| bp.1.hit_condition.is_some())
7953 {
7954 "Edit Hit Condition Breakpoint"
7955 } else {
7956 "Set Hit Condition Breakpoint"
7957 };
7958
7959 let set_breakpoint_msg = if breakpoint.as_ref().is_some() {
7960 "Unset Breakpoint"
7961 } else {
7962 "Set Breakpoint"
7963 };
7964
7965 let run_to_cursor = window.is_action_available(&RunToCursor, cx);
7966
7967 let toggle_state_msg = breakpoint.as_ref().map_or(None, |bp| match bp.1.state {
7968 BreakpointState::Enabled => Some("Disable"),
7969 BreakpointState::Disabled => Some("Enable"),
7970 });
7971
7972 let (anchor, breakpoint) =
7973 breakpoint.unwrap_or_else(|| (anchor, Arc::new(Breakpoint::new_standard())));
7974
7975 ui::ContextMenu::build(window, cx, |menu, _, _cx| {
7976 menu.on_blur_subscription(Subscription::new(|| {}))
7977 .context(focus_handle)
7978 .when(run_to_cursor, |this| {
7979 let weak_editor = weak_editor.clone();
7980 this.entry("Run to cursor", None, move |window, cx| {
7981 weak_editor
7982 .update(cx, |editor, cx| {
7983 editor.change_selections(
7984 SelectionEffects::no_scroll(),
7985 window,
7986 cx,
7987 |s| s.select_ranges([Point::new(row, 0)..Point::new(row, 0)]),
7988 );
7989 })
7990 .ok();
7991
7992 window.dispatch_action(Box::new(RunToCursor), cx);
7993 })
7994 .separator()
7995 })
7996 .when_some(toggle_state_msg, |this, msg| {
7997 this.entry(msg, None, {
7998 let weak_editor = weak_editor.clone();
7999 let breakpoint = breakpoint.clone();
8000 move |_window, cx| {
8001 weak_editor
8002 .update(cx, |this, cx| {
8003 this.edit_breakpoint_at_anchor(
8004 anchor,
8005 breakpoint.as_ref().clone(),
8006 BreakpointEditAction::InvertState,
8007 cx,
8008 );
8009 })
8010 .log_err();
8011 }
8012 })
8013 })
8014 .entry(set_breakpoint_msg, None, {
8015 let weak_editor = weak_editor.clone();
8016 let breakpoint = breakpoint.clone();
8017 move |_window, cx| {
8018 weak_editor
8019 .update(cx, |this, cx| {
8020 this.edit_breakpoint_at_anchor(
8021 anchor,
8022 breakpoint.as_ref().clone(),
8023 BreakpointEditAction::Toggle,
8024 cx,
8025 );
8026 })
8027 .log_err();
8028 }
8029 })
8030 .entry(log_breakpoint_msg, None, {
8031 let breakpoint = breakpoint.clone();
8032 let weak_editor = weak_editor.clone();
8033 move |window, cx| {
8034 weak_editor
8035 .update(cx, |this, cx| {
8036 this.add_edit_breakpoint_block(
8037 anchor,
8038 breakpoint.as_ref(),
8039 BreakpointPromptEditAction::Log,
8040 window,
8041 cx,
8042 );
8043 })
8044 .log_err();
8045 }
8046 })
8047 .entry(condition_breakpoint_msg, None, {
8048 let breakpoint = breakpoint.clone();
8049 let weak_editor = weak_editor.clone();
8050 move |window, cx| {
8051 weak_editor
8052 .update(cx, |this, cx| {
8053 this.add_edit_breakpoint_block(
8054 anchor,
8055 breakpoint.as_ref(),
8056 BreakpointPromptEditAction::Condition,
8057 window,
8058 cx,
8059 );
8060 })
8061 .log_err();
8062 }
8063 })
8064 .entry(hit_condition_breakpoint_msg, None, move |window, cx| {
8065 weak_editor
8066 .update(cx, |this, cx| {
8067 this.add_edit_breakpoint_block(
8068 anchor,
8069 breakpoint.as_ref(),
8070 BreakpointPromptEditAction::HitCondition,
8071 window,
8072 cx,
8073 );
8074 })
8075 .log_err();
8076 })
8077 })
8078 }
8079
8080 fn render_breakpoint(
8081 &self,
8082 position: Anchor,
8083 row: DisplayRow,
8084 breakpoint: &Breakpoint,
8085 state: Option<BreakpointSessionState>,
8086 cx: &mut Context<Self>,
8087 ) -> IconButton {
8088 let is_rejected = state.is_some_and(|s| !s.verified);
8089 // Is it a breakpoint that shows up when hovering over gutter?
8090 let (is_phantom, collides_with_existing) = self.gutter_breakpoint_indicator.0.map_or(
8091 (false, false),
8092 |PhantomBreakpointIndicator {
8093 is_active,
8094 display_row,
8095 collides_with_existing_breakpoint,
8096 }| {
8097 (
8098 is_active && display_row == row,
8099 collides_with_existing_breakpoint,
8100 )
8101 },
8102 );
8103
8104 let (color, icon) = {
8105 let icon = match (&breakpoint.message.is_some(), breakpoint.is_disabled()) {
8106 (false, false) => ui::IconName::DebugBreakpoint,
8107 (true, false) => ui::IconName::DebugLogBreakpoint,
8108 (false, true) => ui::IconName::DebugDisabledBreakpoint,
8109 (true, true) => ui::IconName::DebugDisabledLogBreakpoint,
8110 };
8111
8112 let color = if is_phantom {
8113 Color::Hint
8114 } else if is_rejected {
8115 Color::Disabled
8116 } else {
8117 Color::Debugger
8118 };
8119
8120 (color, icon)
8121 };
8122
8123 let breakpoint = Arc::from(breakpoint.clone());
8124
8125 let alt_as_text = gpui::Keystroke {
8126 modifiers: Modifiers::secondary_key(),
8127 ..Default::default()
8128 };
8129 let primary_action_text = if breakpoint.is_disabled() {
8130 "Enable breakpoint"
8131 } else if is_phantom && !collides_with_existing {
8132 "Set breakpoint"
8133 } else {
8134 "Unset breakpoint"
8135 };
8136 let focus_handle = self.focus_handle.clone();
8137
8138 let meta = if is_rejected {
8139 SharedString::from("No executable code is associated with this line.")
8140 } else if collides_with_existing && !breakpoint.is_disabled() {
8141 SharedString::from(format!(
8142 "{alt_as_text}-click to disable,\nright-click for more options."
8143 ))
8144 } else {
8145 SharedString::from("Right-click for more options.")
8146 };
8147 IconButton::new(("breakpoint_indicator", row.0 as usize), icon)
8148 .icon_size(IconSize::XSmall)
8149 .size(ui::ButtonSize::None)
8150 .when(is_rejected, |this| {
8151 this.indicator(Indicator::icon(Icon::new(IconName::Warning)).color(Color::Warning))
8152 })
8153 .icon_color(color)
8154 .style(ButtonStyle::Transparent)
8155 .on_click(cx.listener({
8156 let breakpoint = breakpoint.clone();
8157
8158 move |editor, event: &ClickEvent, window, cx| {
8159 let edit_action = if event.modifiers().platform || breakpoint.is_disabled() {
8160 BreakpointEditAction::InvertState
8161 } else {
8162 BreakpointEditAction::Toggle
8163 };
8164
8165 window.focus(&editor.focus_handle(cx));
8166 editor.edit_breakpoint_at_anchor(
8167 position,
8168 breakpoint.as_ref().clone(),
8169 edit_action,
8170 cx,
8171 );
8172 }
8173 }))
8174 .on_right_click(cx.listener(move |editor, event: &ClickEvent, window, cx| {
8175 editor.set_breakpoint_context_menu(
8176 row,
8177 Some(position),
8178 event.down.position,
8179 window,
8180 cx,
8181 );
8182 }))
8183 .tooltip(move |window, cx| {
8184 Tooltip::with_meta_in(
8185 primary_action_text,
8186 Some(&ToggleBreakpoint),
8187 meta.clone(),
8188 &focus_handle,
8189 window,
8190 cx,
8191 )
8192 })
8193 }
8194
8195 fn build_tasks_context(
8196 project: &Entity<Project>,
8197 buffer: &Entity<Buffer>,
8198 buffer_row: u32,
8199 tasks: &Arc<RunnableTasks>,
8200 cx: &mut Context<Self>,
8201 ) -> Task<Option<task::TaskContext>> {
8202 let position = Point::new(buffer_row, tasks.column);
8203 let range_start = buffer.read(cx).anchor_at(position, Bias::Right);
8204 let location = Location {
8205 buffer: buffer.clone(),
8206 range: range_start..range_start,
8207 };
8208 // Fill in the environmental variables from the tree-sitter captures
8209 let mut captured_task_variables = TaskVariables::default();
8210 for (capture_name, value) in tasks.extra_variables.clone() {
8211 captured_task_variables.insert(
8212 task::VariableName::Custom(capture_name.into()),
8213 value.clone(),
8214 );
8215 }
8216 project.update(cx, |project, cx| {
8217 project.task_store().update(cx, |task_store, cx| {
8218 task_store.task_context_for_location(captured_task_variables, location, cx)
8219 })
8220 })
8221 }
8222
8223 pub fn spawn_nearest_task(
8224 &mut self,
8225 action: &SpawnNearestTask,
8226 window: &mut Window,
8227 cx: &mut Context<Self>,
8228 ) {
8229 let Some((workspace, _)) = self.workspace.clone() else {
8230 return;
8231 };
8232 let Some(project) = self.project.clone() else {
8233 return;
8234 };
8235
8236 // Try to find a closest, enclosing node using tree-sitter that has a
8237 // task
8238 let Some((buffer, buffer_row, tasks)) = self
8239 .find_enclosing_node_task(cx)
8240 // Or find the task that's closest in row-distance.
8241 .or_else(|| self.find_closest_task(cx))
8242 else {
8243 return;
8244 };
8245
8246 let reveal_strategy = action.reveal;
8247 let task_context = Self::build_tasks_context(&project, &buffer, buffer_row, &tasks, cx);
8248 cx.spawn_in(window, async move |_, cx| {
8249 let context = task_context.await?;
8250 let (task_source_kind, mut resolved_task) = tasks.resolve(&context).next()?;
8251
8252 let resolved = &mut resolved_task.resolved;
8253 resolved.reveal = reveal_strategy;
8254
8255 workspace
8256 .update_in(cx, |workspace, window, cx| {
8257 workspace.schedule_resolved_task(
8258 task_source_kind,
8259 resolved_task,
8260 false,
8261 window,
8262 cx,
8263 );
8264 })
8265 .ok()
8266 })
8267 .detach();
8268 }
8269
8270 fn find_closest_task(
8271 &mut self,
8272 cx: &mut Context<Self>,
8273 ) -> Option<(Entity<Buffer>, u32, Arc<RunnableTasks>)> {
8274 let cursor_row = self.selections.newest_adjusted(cx).head().row;
8275
8276 let ((buffer_id, row), tasks) = self
8277 .tasks
8278 .iter()
8279 .min_by_key(|((_, row), _)| cursor_row.abs_diff(*row))?;
8280
8281 let buffer = self.buffer.read(cx).buffer(*buffer_id)?;
8282 let tasks = Arc::new(tasks.to_owned());
8283 Some((buffer, *row, tasks))
8284 }
8285
8286 fn find_enclosing_node_task(
8287 &mut self,
8288 cx: &mut Context<Self>,
8289 ) -> Option<(Entity<Buffer>, u32, Arc<RunnableTasks>)> {
8290 let snapshot = self.buffer.read(cx).snapshot(cx);
8291 let offset = self.selections.newest::<usize>(cx).head();
8292 let excerpt = snapshot.excerpt_containing(offset..offset)?;
8293 let buffer_id = excerpt.buffer().remote_id();
8294
8295 let layer = excerpt.buffer().syntax_layer_at(offset)?;
8296 let mut cursor = layer.node().walk();
8297
8298 while cursor.goto_first_child_for_byte(offset).is_some() {
8299 if cursor.node().end_byte() == offset {
8300 cursor.goto_next_sibling();
8301 }
8302 }
8303
8304 // Ascend to the smallest ancestor that contains the range and has a task.
8305 loop {
8306 let node = cursor.node();
8307 let node_range = node.byte_range();
8308 let symbol_start_row = excerpt.buffer().offset_to_point(node.start_byte()).row;
8309
8310 // Check if this node contains our offset
8311 if node_range.start <= offset && node_range.end >= offset {
8312 // If it contains offset, check for task
8313 if let Some(tasks) = self.tasks.get(&(buffer_id, symbol_start_row)) {
8314 let buffer = self.buffer.read(cx).buffer(buffer_id)?;
8315 return Some((buffer, symbol_start_row, Arc::new(tasks.to_owned())));
8316 }
8317 }
8318
8319 if !cursor.goto_parent() {
8320 break;
8321 }
8322 }
8323 None
8324 }
8325
8326 fn render_run_indicator(
8327 &self,
8328 _style: &EditorStyle,
8329 is_active: bool,
8330 row: DisplayRow,
8331 breakpoint: Option<(Anchor, Breakpoint, Option<BreakpointSessionState>)>,
8332 cx: &mut Context<Self>,
8333 ) -> IconButton {
8334 let color = Color::Muted;
8335 let position = breakpoint.as_ref().map(|(anchor, _, _)| *anchor);
8336
8337 IconButton::new(("run_indicator", row.0 as usize), ui::IconName::Play)
8338 .shape(ui::IconButtonShape::Square)
8339 .icon_size(IconSize::XSmall)
8340 .icon_color(color)
8341 .toggle_state(is_active)
8342 .on_click(cx.listener(move |editor, e: &ClickEvent, window, cx| {
8343 let quick_launch = e.down.button == MouseButton::Left;
8344 window.focus(&editor.focus_handle(cx));
8345 editor.toggle_code_actions(
8346 &ToggleCodeActions {
8347 deployed_from: Some(CodeActionSource::RunMenu(row)),
8348 quick_launch,
8349 },
8350 window,
8351 cx,
8352 );
8353 }))
8354 .on_right_click(cx.listener(move |editor, event: &ClickEvent, window, cx| {
8355 editor.set_breakpoint_context_menu(row, position, event.down.position, window, cx);
8356 }))
8357 }
8358
8359 pub fn context_menu_visible(&self) -> bool {
8360 !self.edit_prediction_preview_is_active()
8361 && self
8362 .context_menu
8363 .borrow()
8364 .as_ref()
8365 .map_or(false, |menu| menu.visible())
8366 }
8367
8368 pub fn context_menu_origin(&self) -> Option<ContextMenuOrigin> {
8369 self.context_menu
8370 .borrow()
8371 .as_ref()
8372 .map(|menu| menu.origin())
8373 }
8374
8375 pub fn set_context_menu_options(&mut self, options: ContextMenuOptions) {
8376 self.context_menu_options = Some(options);
8377 }
8378
8379 const EDIT_PREDICTION_POPOVER_PADDING_X: Pixels = Pixels(24.);
8380 const EDIT_PREDICTION_POPOVER_PADDING_Y: Pixels = Pixels(2.);
8381
8382 fn render_edit_prediction_popover(
8383 &mut self,
8384 text_bounds: &Bounds<Pixels>,
8385 content_origin: gpui::Point<Pixels>,
8386 right_margin: Pixels,
8387 editor_snapshot: &EditorSnapshot,
8388 visible_row_range: Range<DisplayRow>,
8389 scroll_top: f32,
8390 scroll_bottom: f32,
8391 line_layouts: &[LineWithInvisibles],
8392 line_height: Pixels,
8393 scroll_pixel_position: gpui::Point<Pixels>,
8394 newest_selection_head: Option<DisplayPoint>,
8395 editor_width: Pixels,
8396 style: &EditorStyle,
8397 window: &mut Window,
8398 cx: &mut App,
8399 ) -> Option<(AnyElement, gpui::Point<Pixels>)> {
8400 if self.mode().is_minimap() {
8401 return None;
8402 }
8403 let active_inline_completion = self.active_inline_completion.as_ref()?;
8404
8405 if self.edit_prediction_visible_in_cursor_popover(true) {
8406 return None;
8407 }
8408
8409 match &active_inline_completion.completion {
8410 InlineCompletion::Move { target, .. } => {
8411 let target_display_point = target.to_display_point(editor_snapshot);
8412
8413 if self.edit_prediction_requires_modifier() {
8414 if !self.edit_prediction_preview_is_active() {
8415 return None;
8416 }
8417
8418 self.render_edit_prediction_modifier_jump_popover(
8419 text_bounds,
8420 content_origin,
8421 visible_row_range,
8422 line_layouts,
8423 line_height,
8424 scroll_pixel_position,
8425 newest_selection_head,
8426 target_display_point,
8427 window,
8428 cx,
8429 )
8430 } else {
8431 self.render_edit_prediction_eager_jump_popover(
8432 text_bounds,
8433 content_origin,
8434 editor_snapshot,
8435 visible_row_range,
8436 scroll_top,
8437 scroll_bottom,
8438 line_height,
8439 scroll_pixel_position,
8440 target_display_point,
8441 editor_width,
8442 window,
8443 cx,
8444 )
8445 }
8446 }
8447 InlineCompletion::Edit {
8448 display_mode: EditDisplayMode::Inline,
8449 ..
8450 } => None,
8451 InlineCompletion::Edit {
8452 display_mode: EditDisplayMode::TabAccept,
8453 edits,
8454 ..
8455 } => {
8456 let range = &edits.first()?.0;
8457 let target_display_point = range.end.to_display_point(editor_snapshot);
8458
8459 self.render_edit_prediction_end_of_line_popover(
8460 "Accept",
8461 editor_snapshot,
8462 visible_row_range,
8463 target_display_point,
8464 line_height,
8465 scroll_pixel_position,
8466 content_origin,
8467 editor_width,
8468 window,
8469 cx,
8470 )
8471 }
8472 InlineCompletion::Edit {
8473 edits,
8474 edit_preview,
8475 display_mode: EditDisplayMode::DiffPopover,
8476 snapshot,
8477 } => self.render_edit_prediction_diff_popover(
8478 text_bounds,
8479 content_origin,
8480 right_margin,
8481 editor_snapshot,
8482 visible_row_range,
8483 line_layouts,
8484 line_height,
8485 scroll_pixel_position,
8486 newest_selection_head,
8487 editor_width,
8488 style,
8489 edits,
8490 edit_preview,
8491 snapshot,
8492 window,
8493 cx,
8494 ),
8495 }
8496 }
8497
8498 fn render_edit_prediction_modifier_jump_popover(
8499 &mut self,
8500 text_bounds: &Bounds<Pixels>,
8501 content_origin: gpui::Point<Pixels>,
8502 visible_row_range: Range<DisplayRow>,
8503 line_layouts: &[LineWithInvisibles],
8504 line_height: Pixels,
8505 scroll_pixel_position: gpui::Point<Pixels>,
8506 newest_selection_head: Option<DisplayPoint>,
8507 target_display_point: DisplayPoint,
8508 window: &mut Window,
8509 cx: &mut App,
8510 ) -> Option<(AnyElement, gpui::Point<Pixels>)> {
8511 let scrolled_content_origin =
8512 content_origin - gpui::Point::new(scroll_pixel_position.x, Pixels(0.0));
8513
8514 const SCROLL_PADDING_Y: Pixels = px(12.);
8515
8516 if target_display_point.row() < visible_row_range.start {
8517 return self.render_edit_prediction_scroll_popover(
8518 |_| SCROLL_PADDING_Y,
8519 IconName::ArrowUp,
8520 visible_row_range,
8521 line_layouts,
8522 newest_selection_head,
8523 scrolled_content_origin,
8524 window,
8525 cx,
8526 );
8527 } else if target_display_point.row() >= visible_row_range.end {
8528 return self.render_edit_prediction_scroll_popover(
8529 |size| text_bounds.size.height - size.height - SCROLL_PADDING_Y,
8530 IconName::ArrowDown,
8531 visible_row_range,
8532 line_layouts,
8533 newest_selection_head,
8534 scrolled_content_origin,
8535 window,
8536 cx,
8537 );
8538 }
8539
8540 const POLE_WIDTH: Pixels = px(2.);
8541
8542 let line_layout =
8543 line_layouts.get(target_display_point.row().minus(visible_row_range.start) as usize)?;
8544 let target_column = target_display_point.column() as usize;
8545
8546 let target_x = line_layout.x_for_index(target_column);
8547 let target_y =
8548 (target_display_point.row().as_f32() * line_height) - scroll_pixel_position.y;
8549
8550 let flag_on_right = target_x < text_bounds.size.width / 2.;
8551
8552 let mut border_color = Self::edit_prediction_callout_popover_border_color(cx);
8553 border_color.l += 0.001;
8554
8555 let mut element = v_flex()
8556 .items_end()
8557 .when(flag_on_right, |el| el.items_start())
8558 .child(if flag_on_right {
8559 self.render_edit_prediction_line_popover("Jump", None, window, cx)?
8560 .rounded_bl(px(0.))
8561 .rounded_tl(px(0.))
8562 .border_l_2()
8563 .border_color(border_color)
8564 } else {
8565 self.render_edit_prediction_line_popover("Jump", None, window, cx)?
8566 .rounded_br(px(0.))
8567 .rounded_tr(px(0.))
8568 .border_r_2()
8569 .border_color(border_color)
8570 })
8571 .child(div().w(POLE_WIDTH).bg(border_color).h(line_height))
8572 .into_any();
8573
8574 let size = element.layout_as_root(AvailableSpace::min_size(), window, cx);
8575
8576 let mut origin = scrolled_content_origin + point(target_x, target_y)
8577 - point(
8578 if flag_on_right {
8579 POLE_WIDTH
8580 } else {
8581 size.width - POLE_WIDTH
8582 },
8583 size.height - line_height,
8584 );
8585
8586 origin.x = origin.x.max(content_origin.x);
8587
8588 element.prepaint_at(origin, window, cx);
8589
8590 Some((element, origin))
8591 }
8592
8593 fn render_edit_prediction_scroll_popover(
8594 &mut self,
8595 to_y: impl Fn(Size<Pixels>) -> Pixels,
8596 scroll_icon: IconName,
8597 visible_row_range: Range<DisplayRow>,
8598 line_layouts: &[LineWithInvisibles],
8599 newest_selection_head: Option<DisplayPoint>,
8600 scrolled_content_origin: gpui::Point<Pixels>,
8601 window: &mut Window,
8602 cx: &mut App,
8603 ) -> Option<(AnyElement, gpui::Point<Pixels>)> {
8604 let mut element = self
8605 .render_edit_prediction_line_popover("Scroll", Some(scroll_icon), window, cx)?
8606 .into_any();
8607
8608 let size = element.layout_as_root(AvailableSpace::min_size(), window, cx);
8609
8610 let cursor = newest_selection_head?;
8611 let cursor_row_layout =
8612 line_layouts.get(cursor.row().minus(visible_row_range.start) as usize)?;
8613 let cursor_column = cursor.column() as usize;
8614
8615 let cursor_character_x = cursor_row_layout.x_for_index(cursor_column);
8616
8617 let origin = scrolled_content_origin + point(cursor_character_x, to_y(size));
8618
8619 element.prepaint_at(origin, window, cx);
8620 Some((element, origin))
8621 }
8622
8623 fn render_edit_prediction_eager_jump_popover(
8624 &mut self,
8625 text_bounds: &Bounds<Pixels>,
8626 content_origin: gpui::Point<Pixels>,
8627 editor_snapshot: &EditorSnapshot,
8628 visible_row_range: Range<DisplayRow>,
8629 scroll_top: f32,
8630 scroll_bottom: f32,
8631 line_height: Pixels,
8632 scroll_pixel_position: gpui::Point<Pixels>,
8633 target_display_point: DisplayPoint,
8634 editor_width: Pixels,
8635 window: &mut Window,
8636 cx: &mut App,
8637 ) -> Option<(AnyElement, gpui::Point<Pixels>)> {
8638 if target_display_point.row().as_f32() < scroll_top {
8639 let mut element = self
8640 .render_edit_prediction_line_popover(
8641 "Jump to Edit",
8642 Some(IconName::ArrowUp),
8643 window,
8644 cx,
8645 )?
8646 .into_any();
8647
8648 let size = element.layout_as_root(AvailableSpace::min_size(), window, cx);
8649 let offset = point(
8650 (text_bounds.size.width - size.width) / 2.,
8651 Self::EDIT_PREDICTION_POPOVER_PADDING_Y,
8652 );
8653
8654 let origin = text_bounds.origin + offset;
8655 element.prepaint_at(origin, window, cx);
8656 Some((element, origin))
8657 } else if (target_display_point.row().as_f32() + 1.) > scroll_bottom {
8658 let mut element = self
8659 .render_edit_prediction_line_popover(
8660 "Jump to Edit",
8661 Some(IconName::ArrowDown),
8662 window,
8663 cx,
8664 )?
8665 .into_any();
8666
8667 let size = element.layout_as_root(AvailableSpace::min_size(), window, cx);
8668 let offset = point(
8669 (text_bounds.size.width - size.width) / 2.,
8670 text_bounds.size.height - size.height - Self::EDIT_PREDICTION_POPOVER_PADDING_Y,
8671 );
8672
8673 let origin = text_bounds.origin + offset;
8674 element.prepaint_at(origin, window, cx);
8675 Some((element, origin))
8676 } else {
8677 self.render_edit_prediction_end_of_line_popover(
8678 "Jump to Edit",
8679 editor_snapshot,
8680 visible_row_range,
8681 target_display_point,
8682 line_height,
8683 scroll_pixel_position,
8684 content_origin,
8685 editor_width,
8686 window,
8687 cx,
8688 )
8689 }
8690 }
8691
8692 fn render_edit_prediction_end_of_line_popover(
8693 self: &mut Editor,
8694 label: &'static str,
8695 editor_snapshot: &EditorSnapshot,
8696 visible_row_range: Range<DisplayRow>,
8697 target_display_point: DisplayPoint,
8698 line_height: Pixels,
8699 scroll_pixel_position: gpui::Point<Pixels>,
8700 content_origin: gpui::Point<Pixels>,
8701 editor_width: Pixels,
8702 window: &mut Window,
8703 cx: &mut App,
8704 ) -> Option<(AnyElement, gpui::Point<Pixels>)> {
8705 let target_line_end = DisplayPoint::new(
8706 target_display_point.row(),
8707 editor_snapshot.line_len(target_display_point.row()),
8708 );
8709
8710 let mut element = self
8711 .render_edit_prediction_line_popover(label, None, window, cx)?
8712 .into_any();
8713
8714 let size = element.layout_as_root(AvailableSpace::min_size(), window, cx);
8715
8716 let line_origin = self.display_to_pixel_point(target_line_end, editor_snapshot, window)?;
8717
8718 let start_point = content_origin - point(scroll_pixel_position.x, Pixels::ZERO);
8719 let mut origin = start_point
8720 + line_origin
8721 + point(Self::EDIT_PREDICTION_POPOVER_PADDING_X, Pixels::ZERO);
8722 origin.x = origin.x.max(content_origin.x);
8723
8724 let max_x = content_origin.x + editor_width - size.width;
8725
8726 if origin.x > max_x {
8727 let offset = line_height + Self::EDIT_PREDICTION_POPOVER_PADDING_Y;
8728
8729 let icon = if visible_row_range.contains(&(target_display_point.row() + 2)) {
8730 origin.y += offset;
8731 IconName::ArrowUp
8732 } else {
8733 origin.y -= offset;
8734 IconName::ArrowDown
8735 };
8736
8737 element = self
8738 .render_edit_prediction_line_popover(label, Some(icon), window, cx)?
8739 .into_any();
8740
8741 let size = element.layout_as_root(AvailableSpace::min_size(), window, cx);
8742
8743 origin.x = content_origin.x + editor_width - size.width - px(2.);
8744 }
8745
8746 element.prepaint_at(origin, window, cx);
8747 Some((element, origin))
8748 }
8749
8750 fn render_edit_prediction_diff_popover(
8751 self: &Editor,
8752 text_bounds: &Bounds<Pixels>,
8753 content_origin: gpui::Point<Pixels>,
8754 right_margin: Pixels,
8755 editor_snapshot: &EditorSnapshot,
8756 visible_row_range: Range<DisplayRow>,
8757 line_layouts: &[LineWithInvisibles],
8758 line_height: Pixels,
8759 scroll_pixel_position: gpui::Point<Pixels>,
8760 newest_selection_head: Option<DisplayPoint>,
8761 editor_width: Pixels,
8762 style: &EditorStyle,
8763 edits: &Vec<(Range<Anchor>, String)>,
8764 edit_preview: &Option<language::EditPreview>,
8765 snapshot: &language::BufferSnapshot,
8766 window: &mut Window,
8767 cx: &mut App,
8768 ) -> Option<(AnyElement, gpui::Point<Pixels>)> {
8769 let edit_start = edits
8770 .first()
8771 .unwrap()
8772 .0
8773 .start
8774 .to_display_point(editor_snapshot);
8775 let edit_end = edits
8776 .last()
8777 .unwrap()
8778 .0
8779 .end
8780 .to_display_point(editor_snapshot);
8781
8782 let is_visible = visible_row_range.contains(&edit_start.row())
8783 || visible_row_range.contains(&edit_end.row());
8784 if !is_visible {
8785 return None;
8786 }
8787
8788 let highlighted_edits =
8789 crate::inline_completion_edit_text(&snapshot, edits, edit_preview.as_ref()?, false, cx);
8790
8791 let styled_text = highlighted_edits.to_styled_text(&style.text);
8792 let line_count = highlighted_edits.text.lines().count();
8793
8794 const BORDER_WIDTH: Pixels = px(1.);
8795
8796 let keybind = self.render_edit_prediction_accept_keybind(window, cx);
8797 let has_keybind = keybind.is_some();
8798
8799 let mut element = h_flex()
8800 .items_start()
8801 .child(
8802 h_flex()
8803 .bg(cx.theme().colors().editor_background)
8804 .border(BORDER_WIDTH)
8805 .shadow_xs()
8806 .border_color(cx.theme().colors().border)
8807 .rounded_l_lg()
8808 .when(line_count > 1, |el| el.rounded_br_lg())
8809 .pr_1()
8810 .child(styled_text),
8811 )
8812 .child(
8813 h_flex()
8814 .h(line_height + BORDER_WIDTH * 2.)
8815 .px_1p5()
8816 .gap_1()
8817 // Workaround: For some reason, there's a gap if we don't do this
8818 .ml(-BORDER_WIDTH)
8819 .shadow(vec![gpui::BoxShadow {
8820 color: gpui::black().opacity(0.05),
8821 offset: point(px(1.), px(1.)),
8822 blur_radius: px(2.),
8823 spread_radius: px(0.),
8824 }])
8825 .bg(Editor::edit_prediction_line_popover_bg_color(cx))
8826 .border(BORDER_WIDTH)
8827 .border_color(cx.theme().colors().border)
8828 .rounded_r_lg()
8829 .id("edit_prediction_diff_popover_keybind")
8830 .when(!has_keybind, |el| {
8831 let status_colors = cx.theme().status();
8832
8833 el.bg(status_colors.error_background)
8834 .border_color(status_colors.error.opacity(0.6))
8835 .child(Icon::new(IconName::Info).color(Color::Error))
8836 .cursor_default()
8837 .hoverable_tooltip(move |_window, cx| {
8838 cx.new(|_| MissingEditPredictionKeybindingTooltip).into()
8839 })
8840 })
8841 .children(keybind),
8842 )
8843 .into_any();
8844
8845 let longest_row =
8846 editor_snapshot.longest_row_in_range(edit_start.row()..edit_end.row() + 1);
8847 let longest_line_width = if visible_row_range.contains(&longest_row) {
8848 line_layouts[(longest_row.0 - visible_row_range.start.0) as usize].width
8849 } else {
8850 layout_line(
8851 longest_row,
8852 editor_snapshot,
8853 style,
8854 editor_width,
8855 |_| false,
8856 window,
8857 cx,
8858 )
8859 .width
8860 };
8861
8862 let viewport_bounds =
8863 Bounds::new(Default::default(), window.viewport_size()).extend(Edges {
8864 right: -right_margin,
8865 ..Default::default()
8866 });
8867
8868 let x_after_longest =
8869 text_bounds.origin.x + longest_line_width + Self::EDIT_PREDICTION_POPOVER_PADDING_X
8870 - scroll_pixel_position.x;
8871
8872 let element_bounds = element.layout_as_root(AvailableSpace::min_size(), window, cx);
8873
8874 // Fully visible if it can be displayed within the window (allow overlapping other
8875 // panes). However, this is only allowed if the popover starts within text_bounds.
8876 let can_position_to_the_right = x_after_longest < text_bounds.right()
8877 && x_after_longest + element_bounds.width < viewport_bounds.right();
8878
8879 let mut origin = if can_position_to_the_right {
8880 point(
8881 x_after_longest,
8882 text_bounds.origin.y + edit_start.row().as_f32() * line_height
8883 - scroll_pixel_position.y,
8884 )
8885 } else {
8886 let cursor_row = newest_selection_head.map(|head| head.row());
8887 let above_edit = edit_start
8888 .row()
8889 .0
8890 .checked_sub(line_count as u32)
8891 .map(DisplayRow);
8892 let below_edit = Some(edit_end.row() + 1);
8893 let above_cursor =
8894 cursor_row.and_then(|row| row.0.checked_sub(line_count as u32).map(DisplayRow));
8895 let below_cursor = cursor_row.map(|cursor_row| cursor_row + 1);
8896
8897 // Place the edit popover adjacent to the edit if there is a location
8898 // available that is onscreen and does not obscure the cursor. Otherwise,
8899 // place it adjacent to the cursor.
8900 let row_target = [above_edit, below_edit, above_cursor, below_cursor]
8901 .into_iter()
8902 .flatten()
8903 .find(|&start_row| {
8904 let end_row = start_row + line_count as u32;
8905 visible_row_range.contains(&start_row)
8906 && visible_row_range.contains(&end_row)
8907 && cursor_row.map_or(true, |cursor_row| {
8908 !((start_row..end_row).contains(&cursor_row))
8909 })
8910 })?;
8911
8912 content_origin
8913 + point(
8914 -scroll_pixel_position.x,
8915 row_target.as_f32() * line_height - scroll_pixel_position.y,
8916 )
8917 };
8918
8919 origin.x -= BORDER_WIDTH;
8920
8921 window.defer_draw(element, origin, 1);
8922
8923 // Do not return an element, since it will already be drawn due to defer_draw.
8924 None
8925 }
8926
8927 fn edit_prediction_cursor_popover_height(&self) -> Pixels {
8928 px(30.)
8929 }
8930
8931 fn current_user_player_color(&self, cx: &mut App) -> PlayerColor {
8932 if self.read_only(cx) {
8933 cx.theme().players().read_only()
8934 } else {
8935 self.style.as_ref().unwrap().local_player
8936 }
8937 }
8938
8939 fn render_edit_prediction_accept_keybind(
8940 &self,
8941 window: &mut Window,
8942 cx: &App,
8943 ) -> Option<AnyElement> {
8944 let accept_binding = self.accept_edit_prediction_keybind(false, window, cx);
8945 let accept_keystroke = accept_binding.keystroke()?;
8946
8947 let is_platform_style_mac = PlatformStyle::platform() == PlatformStyle::Mac;
8948
8949 let modifiers_color = if accept_keystroke.modifiers == window.modifiers() {
8950 Color::Accent
8951 } else {
8952 Color::Muted
8953 };
8954
8955 h_flex()
8956 .px_0p5()
8957 .when(is_platform_style_mac, |parent| parent.gap_0p5())
8958 .font(theme::ThemeSettings::get_global(cx).buffer_font.clone())
8959 .text_size(TextSize::XSmall.rems(cx))
8960 .child(h_flex().children(ui::render_modifiers(
8961 &accept_keystroke.modifiers,
8962 PlatformStyle::platform(),
8963 Some(modifiers_color),
8964 Some(IconSize::XSmall.rems().into()),
8965 true,
8966 )))
8967 .when(is_platform_style_mac, |parent| {
8968 parent.child(accept_keystroke.key.clone())
8969 })
8970 .when(!is_platform_style_mac, |parent| {
8971 parent.child(
8972 Key::new(
8973 util::capitalize(&accept_keystroke.key),
8974 Some(Color::Default),
8975 )
8976 .size(Some(IconSize::XSmall.rems().into())),
8977 )
8978 })
8979 .into_any()
8980 .into()
8981 }
8982
8983 fn render_edit_prediction_line_popover(
8984 &self,
8985 label: impl Into<SharedString>,
8986 icon: Option<IconName>,
8987 window: &mut Window,
8988 cx: &App,
8989 ) -> Option<Stateful<Div>> {
8990 let padding_right = if icon.is_some() { px(4.) } else { px(8.) };
8991
8992 let keybind = self.render_edit_prediction_accept_keybind(window, cx);
8993 let has_keybind = keybind.is_some();
8994
8995 let result = h_flex()
8996 .id("ep-line-popover")
8997 .py_0p5()
8998 .pl_1()
8999 .pr(padding_right)
9000 .gap_1()
9001 .rounded_md()
9002 .border_1()
9003 .bg(Self::edit_prediction_line_popover_bg_color(cx))
9004 .border_color(Self::edit_prediction_callout_popover_border_color(cx))
9005 .shadow_xs()
9006 .when(!has_keybind, |el| {
9007 let status_colors = cx.theme().status();
9008
9009 el.bg(status_colors.error_background)
9010 .border_color(status_colors.error.opacity(0.6))
9011 .pl_2()
9012 .child(Icon::new(IconName::ZedPredictError).color(Color::Error))
9013 .cursor_default()
9014 .hoverable_tooltip(move |_window, cx| {
9015 cx.new(|_| MissingEditPredictionKeybindingTooltip).into()
9016 })
9017 })
9018 .children(keybind)
9019 .child(
9020 Label::new(label)
9021 .size(LabelSize::Small)
9022 .when(!has_keybind, |el| {
9023 el.color(cx.theme().status().error.into()).strikethrough()
9024 }),
9025 )
9026 .when(!has_keybind, |el| {
9027 el.child(
9028 h_flex().ml_1().child(
9029 Icon::new(IconName::Info)
9030 .size(IconSize::Small)
9031 .color(cx.theme().status().error.into()),
9032 ),
9033 )
9034 })
9035 .when_some(icon, |element, icon| {
9036 element.child(
9037 div()
9038 .mt(px(1.5))
9039 .child(Icon::new(icon).size(IconSize::Small)),
9040 )
9041 });
9042
9043 Some(result)
9044 }
9045
9046 fn edit_prediction_line_popover_bg_color(cx: &App) -> Hsla {
9047 let accent_color = cx.theme().colors().text_accent;
9048 let editor_bg_color = cx.theme().colors().editor_background;
9049 editor_bg_color.blend(accent_color.opacity(0.1))
9050 }
9051
9052 fn edit_prediction_callout_popover_border_color(cx: &App) -> Hsla {
9053 let accent_color = cx.theme().colors().text_accent;
9054 let editor_bg_color = cx.theme().colors().editor_background;
9055 editor_bg_color.blend(accent_color.opacity(0.6))
9056 }
9057
9058 fn render_edit_prediction_cursor_popover(
9059 &self,
9060 min_width: Pixels,
9061 max_width: Pixels,
9062 cursor_point: Point,
9063 style: &EditorStyle,
9064 accept_keystroke: Option<&gpui::Keystroke>,
9065 _window: &Window,
9066 cx: &mut Context<Editor>,
9067 ) -> Option<AnyElement> {
9068 let provider = self.edit_prediction_provider.as_ref()?;
9069
9070 if provider.provider.needs_terms_acceptance(cx) {
9071 return Some(
9072 h_flex()
9073 .min_w(min_width)
9074 .flex_1()
9075 .px_2()
9076 .py_1()
9077 .gap_3()
9078 .elevation_2(cx)
9079 .hover(|style| style.bg(cx.theme().colors().element_hover))
9080 .id("accept-terms")
9081 .cursor_pointer()
9082 .on_mouse_down(MouseButton::Left, |_, window, _| window.prevent_default())
9083 .on_click(cx.listener(|this, _event, window, cx| {
9084 cx.stop_propagation();
9085 this.report_editor_event("Edit Prediction Provider ToS Clicked", None, cx);
9086 window.dispatch_action(
9087 zed_actions::OpenZedPredictOnboarding.boxed_clone(),
9088 cx,
9089 );
9090 }))
9091 .child(
9092 h_flex()
9093 .flex_1()
9094 .gap_2()
9095 .child(Icon::new(IconName::ZedPredict))
9096 .child(Label::new("Accept Terms of Service"))
9097 .child(div().w_full())
9098 .child(
9099 Icon::new(IconName::ArrowUpRight)
9100 .color(Color::Muted)
9101 .size(IconSize::Small),
9102 )
9103 .into_any_element(),
9104 )
9105 .into_any(),
9106 );
9107 }
9108
9109 let is_refreshing = provider.provider.is_refreshing(cx);
9110
9111 fn pending_completion_container() -> Div {
9112 h_flex()
9113 .h_full()
9114 .flex_1()
9115 .gap_2()
9116 .child(Icon::new(IconName::ZedPredict))
9117 }
9118
9119 let completion = match &self.active_inline_completion {
9120 Some(prediction) => {
9121 if !self.has_visible_completions_menu() {
9122 const RADIUS: Pixels = px(6.);
9123 const BORDER_WIDTH: Pixels = px(1.);
9124
9125 return Some(
9126 h_flex()
9127 .elevation_2(cx)
9128 .border(BORDER_WIDTH)
9129 .border_color(cx.theme().colors().border)
9130 .when(accept_keystroke.is_none(), |el| {
9131 el.border_color(cx.theme().status().error)
9132 })
9133 .rounded(RADIUS)
9134 .rounded_tl(px(0.))
9135 .overflow_hidden()
9136 .child(div().px_1p5().child(match &prediction.completion {
9137 InlineCompletion::Move { target, snapshot } => {
9138 use text::ToPoint as _;
9139 if target.text_anchor.to_point(&snapshot).row > cursor_point.row
9140 {
9141 Icon::new(IconName::ZedPredictDown)
9142 } else {
9143 Icon::new(IconName::ZedPredictUp)
9144 }
9145 }
9146 InlineCompletion::Edit { .. } => Icon::new(IconName::ZedPredict),
9147 }))
9148 .child(
9149 h_flex()
9150 .gap_1()
9151 .py_1()
9152 .px_2()
9153 .rounded_r(RADIUS - BORDER_WIDTH)
9154 .border_l_1()
9155 .border_color(cx.theme().colors().border)
9156 .bg(Self::edit_prediction_line_popover_bg_color(cx))
9157 .when(self.edit_prediction_preview.released_too_fast(), |el| {
9158 el.child(
9159 Label::new("Hold")
9160 .size(LabelSize::Small)
9161 .when(accept_keystroke.is_none(), |el| {
9162 el.strikethrough()
9163 })
9164 .line_height_style(LineHeightStyle::UiLabel),
9165 )
9166 })
9167 .id("edit_prediction_cursor_popover_keybind")
9168 .when(accept_keystroke.is_none(), |el| {
9169 let status_colors = cx.theme().status();
9170
9171 el.bg(status_colors.error_background)
9172 .border_color(status_colors.error.opacity(0.6))
9173 .child(Icon::new(IconName::Info).color(Color::Error))
9174 .cursor_default()
9175 .hoverable_tooltip(move |_window, cx| {
9176 cx.new(|_| MissingEditPredictionKeybindingTooltip)
9177 .into()
9178 })
9179 })
9180 .when_some(
9181 accept_keystroke.as_ref(),
9182 |el, accept_keystroke| {
9183 el.child(h_flex().children(ui::render_modifiers(
9184 &accept_keystroke.modifiers,
9185 PlatformStyle::platform(),
9186 Some(Color::Default),
9187 Some(IconSize::XSmall.rems().into()),
9188 false,
9189 )))
9190 },
9191 ),
9192 )
9193 .into_any(),
9194 );
9195 }
9196
9197 self.render_edit_prediction_cursor_popover_preview(
9198 prediction,
9199 cursor_point,
9200 style,
9201 cx,
9202 )?
9203 }
9204
9205 None if is_refreshing => match &self.stale_inline_completion_in_menu {
9206 Some(stale_completion) => self.render_edit_prediction_cursor_popover_preview(
9207 stale_completion,
9208 cursor_point,
9209 style,
9210 cx,
9211 )?,
9212
9213 None => {
9214 pending_completion_container().child(Label::new("...").size(LabelSize::Small))
9215 }
9216 },
9217
9218 None => pending_completion_container().child(Label::new("No Prediction")),
9219 };
9220
9221 let completion = if is_refreshing {
9222 completion
9223 .with_animation(
9224 "loading-completion",
9225 Animation::new(Duration::from_secs(2))
9226 .repeat()
9227 .with_easing(pulsating_between(0.4, 0.8)),
9228 |label, delta| label.opacity(delta),
9229 )
9230 .into_any_element()
9231 } else {
9232 completion.into_any_element()
9233 };
9234
9235 let has_completion = self.active_inline_completion.is_some();
9236
9237 let is_platform_style_mac = PlatformStyle::platform() == PlatformStyle::Mac;
9238 Some(
9239 h_flex()
9240 .min_w(min_width)
9241 .max_w(max_width)
9242 .flex_1()
9243 .elevation_2(cx)
9244 .border_color(cx.theme().colors().border)
9245 .child(
9246 div()
9247 .flex_1()
9248 .py_1()
9249 .px_2()
9250 .overflow_hidden()
9251 .child(completion),
9252 )
9253 .when_some(accept_keystroke, |el, accept_keystroke| {
9254 if !accept_keystroke.modifiers.modified() {
9255 return el;
9256 }
9257
9258 el.child(
9259 h_flex()
9260 .h_full()
9261 .border_l_1()
9262 .rounded_r_lg()
9263 .border_color(cx.theme().colors().border)
9264 .bg(Self::edit_prediction_line_popover_bg_color(cx))
9265 .gap_1()
9266 .py_1()
9267 .px_2()
9268 .child(
9269 h_flex()
9270 .font(theme::ThemeSettings::get_global(cx).buffer_font.clone())
9271 .when(is_platform_style_mac, |parent| parent.gap_1())
9272 .child(h_flex().children(ui::render_modifiers(
9273 &accept_keystroke.modifiers,
9274 PlatformStyle::platform(),
9275 Some(if !has_completion {
9276 Color::Muted
9277 } else {
9278 Color::Default
9279 }),
9280 None,
9281 false,
9282 ))),
9283 )
9284 .child(Label::new("Preview").into_any_element())
9285 .opacity(if has_completion { 1.0 } else { 0.4 }),
9286 )
9287 })
9288 .into_any(),
9289 )
9290 }
9291
9292 fn render_edit_prediction_cursor_popover_preview(
9293 &self,
9294 completion: &InlineCompletionState,
9295 cursor_point: Point,
9296 style: &EditorStyle,
9297 cx: &mut Context<Editor>,
9298 ) -> Option<Div> {
9299 use text::ToPoint as _;
9300
9301 fn render_relative_row_jump(
9302 prefix: impl Into<String>,
9303 current_row: u32,
9304 target_row: u32,
9305 ) -> Div {
9306 let (row_diff, arrow) = if target_row < current_row {
9307 (current_row - target_row, IconName::ArrowUp)
9308 } else {
9309 (target_row - current_row, IconName::ArrowDown)
9310 };
9311
9312 h_flex()
9313 .child(
9314 Label::new(format!("{}{}", prefix.into(), row_diff))
9315 .color(Color::Muted)
9316 .size(LabelSize::Small),
9317 )
9318 .child(Icon::new(arrow).color(Color::Muted).size(IconSize::Small))
9319 }
9320
9321 match &completion.completion {
9322 InlineCompletion::Move {
9323 target, snapshot, ..
9324 } => Some(
9325 h_flex()
9326 .px_2()
9327 .gap_2()
9328 .flex_1()
9329 .child(
9330 if target.text_anchor.to_point(&snapshot).row > cursor_point.row {
9331 Icon::new(IconName::ZedPredictDown)
9332 } else {
9333 Icon::new(IconName::ZedPredictUp)
9334 },
9335 )
9336 .child(Label::new("Jump to Edit")),
9337 ),
9338
9339 InlineCompletion::Edit {
9340 edits,
9341 edit_preview,
9342 snapshot,
9343 display_mode: _,
9344 } => {
9345 let first_edit_row = edits.first()?.0.start.text_anchor.to_point(&snapshot).row;
9346
9347 let (highlighted_edits, has_more_lines) = crate::inline_completion_edit_text(
9348 &snapshot,
9349 &edits,
9350 edit_preview.as_ref()?,
9351 true,
9352 cx,
9353 )
9354 .first_line_preview();
9355
9356 let styled_text = gpui::StyledText::new(highlighted_edits.text)
9357 .with_default_highlights(&style.text, highlighted_edits.highlights);
9358
9359 let preview = h_flex()
9360 .gap_1()
9361 .min_w_16()
9362 .child(styled_text)
9363 .when(has_more_lines, |parent| parent.child("…"));
9364
9365 let left = if first_edit_row != cursor_point.row {
9366 render_relative_row_jump("", cursor_point.row, first_edit_row)
9367 .into_any_element()
9368 } else {
9369 Icon::new(IconName::ZedPredict).into_any_element()
9370 };
9371
9372 Some(
9373 h_flex()
9374 .h_full()
9375 .flex_1()
9376 .gap_2()
9377 .pr_1()
9378 .overflow_x_hidden()
9379 .font(theme::ThemeSettings::get_global(cx).buffer_font.clone())
9380 .child(left)
9381 .child(preview),
9382 )
9383 }
9384 }
9385 }
9386
9387 pub fn render_context_menu(
9388 &self,
9389 style: &EditorStyle,
9390 max_height_in_lines: u32,
9391 window: &mut Window,
9392 cx: &mut Context<Editor>,
9393 ) -> Option<AnyElement> {
9394 let menu = self.context_menu.borrow();
9395 let menu = menu.as_ref()?;
9396 if !menu.visible() {
9397 return None;
9398 };
9399 Some(menu.render(style, max_height_in_lines, window, cx))
9400 }
9401
9402 fn render_context_menu_aside(
9403 &mut self,
9404 max_size: Size<Pixels>,
9405 window: &mut Window,
9406 cx: &mut Context<Editor>,
9407 ) -> Option<AnyElement> {
9408 self.context_menu.borrow_mut().as_mut().and_then(|menu| {
9409 if menu.visible() {
9410 menu.render_aside(max_size, window, cx)
9411 } else {
9412 None
9413 }
9414 })
9415 }
9416
9417 fn hide_context_menu(
9418 &mut self,
9419 window: &mut Window,
9420 cx: &mut Context<Self>,
9421 ) -> Option<CodeContextMenu> {
9422 cx.notify();
9423 self.completion_tasks.clear();
9424 let context_menu = self.context_menu.borrow_mut().take();
9425 self.stale_inline_completion_in_menu.take();
9426 self.update_visible_inline_completion(window, cx);
9427 if let Some(CodeContextMenu::Completions(_)) = &context_menu {
9428 if let Some(completion_provider) = &self.completion_provider {
9429 completion_provider.selection_changed(None, window, cx);
9430 }
9431 }
9432 context_menu
9433 }
9434
9435 fn show_snippet_choices(
9436 &mut self,
9437 choices: &Vec<String>,
9438 selection: Range<Anchor>,
9439 cx: &mut Context<Self>,
9440 ) {
9441 let buffer_id = match (&selection.start.buffer_id, &selection.end.buffer_id) {
9442 (Some(a), Some(b)) if a == b => a,
9443 _ => {
9444 log::error!("expected anchor range to have matching buffer IDs");
9445 return;
9446 }
9447 };
9448 let multi_buffer = self.buffer().read(cx);
9449 let Some(buffer) = multi_buffer.buffer(*buffer_id) else {
9450 return;
9451 };
9452
9453 let id = post_inc(&mut self.next_completion_id);
9454 let snippet_sort_order = EditorSettings::get_global(cx).snippet_sort_order;
9455 *self.context_menu.borrow_mut() = Some(CodeContextMenu::Completions(
9456 CompletionsMenu::new_snippet_choices(
9457 id,
9458 true,
9459 choices,
9460 selection,
9461 buffer,
9462 snippet_sort_order,
9463 ),
9464 ));
9465 }
9466
9467 pub fn insert_snippet(
9468 &mut self,
9469 insertion_ranges: &[Range<usize>],
9470 snippet: Snippet,
9471 window: &mut Window,
9472 cx: &mut Context<Self>,
9473 ) -> Result<()> {
9474 struct Tabstop<T> {
9475 is_end_tabstop: bool,
9476 ranges: Vec<Range<T>>,
9477 choices: Option<Vec<String>>,
9478 }
9479
9480 let tabstops = self.buffer.update(cx, |buffer, cx| {
9481 let snippet_text: Arc<str> = snippet.text.clone().into();
9482 let edits = insertion_ranges
9483 .iter()
9484 .cloned()
9485 .map(|range| (range, snippet_text.clone()));
9486 let autoindent_mode = AutoindentMode::Block {
9487 original_indent_columns: Vec::new(),
9488 };
9489 buffer.edit(edits, Some(autoindent_mode), cx);
9490
9491 let snapshot = &*buffer.read(cx);
9492 let snippet = &snippet;
9493 snippet
9494 .tabstops
9495 .iter()
9496 .map(|tabstop| {
9497 let is_end_tabstop = tabstop.ranges.first().map_or(false, |tabstop| {
9498 tabstop.is_empty() && tabstop.start == snippet.text.len() as isize
9499 });
9500 let mut tabstop_ranges = tabstop
9501 .ranges
9502 .iter()
9503 .flat_map(|tabstop_range| {
9504 let mut delta = 0_isize;
9505 insertion_ranges.iter().map(move |insertion_range| {
9506 let insertion_start = insertion_range.start as isize + delta;
9507 delta +=
9508 snippet.text.len() as isize - insertion_range.len() as isize;
9509
9510 let start = ((insertion_start + tabstop_range.start) as usize)
9511 .min(snapshot.len());
9512 let end = ((insertion_start + tabstop_range.end) as usize)
9513 .min(snapshot.len());
9514 snapshot.anchor_before(start)..snapshot.anchor_after(end)
9515 })
9516 })
9517 .collect::<Vec<_>>();
9518 tabstop_ranges.sort_unstable_by(|a, b| a.start.cmp(&b.start, snapshot));
9519
9520 Tabstop {
9521 is_end_tabstop,
9522 ranges: tabstop_ranges,
9523 choices: tabstop.choices.clone(),
9524 }
9525 })
9526 .collect::<Vec<_>>()
9527 });
9528 if let Some(tabstop) = tabstops.first() {
9529 self.change_selections(Default::default(), window, cx, |s| {
9530 // Reverse order so that the first range is the newest created selection.
9531 // Completions will use it and autoscroll will prioritize it.
9532 s.select_ranges(tabstop.ranges.iter().rev().cloned());
9533 });
9534
9535 if let Some(choices) = &tabstop.choices {
9536 if let Some(selection) = tabstop.ranges.first() {
9537 self.show_snippet_choices(choices, selection.clone(), cx)
9538 }
9539 }
9540
9541 // If we're already at the last tabstop and it's at the end of the snippet,
9542 // we're done, we don't need to keep the state around.
9543 if !tabstop.is_end_tabstop {
9544 let choices = tabstops
9545 .iter()
9546 .map(|tabstop| tabstop.choices.clone())
9547 .collect();
9548
9549 let ranges = tabstops
9550 .into_iter()
9551 .map(|tabstop| tabstop.ranges)
9552 .collect::<Vec<_>>();
9553
9554 self.snippet_stack.push(SnippetState {
9555 active_index: 0,
9556 ranges,
9557 choices,
9558 });
9559 }
9560
9561 // Check whether the just-entered snippet ends with an auto-closable bracket.
9562 if self.autoclose_regions.is_empty() {
9563 let snapshot = self.buffer.read(cx).snapshot(cx);
9564 for selection in &mut self.selections.all::<Point>(cx) {
9565 let selection_head = selection.head();
9566 let Some(scope) = snapshot.language_scope_at(selection_head) else {
9567 continue;
9568 };
9569
9570 let mut bracket_pair = None;
9571 let next_chars = snapshot.chars_at(selection_head).collect::<String>();
9572 let prev_chars = snapshot
9573 .reversed_chars_at(selection_head)
9574 .collect::<String>();
9575 for (pair, enabled) in scope.brackets() {
9576 if enabled
9577 && pair.close
9578 && prev_chars.starts_with(pair.start.as_str())
9579 && next_chars.starts_with(pair.end.as_str())
9580 {
9581 bracket_pair = Some(pair.clone());
9582 break;
9583 }
9584 }
9585 if let Some(pair) = bracket_pair {
9586 let snapshot_settings = snapshot.language_settings_at(selection_head, cx);
9587 let autoclose_enabled =
9588 self.use_autoclose && snapshot_settings.use_autoclose;
9589 if autoclose_enabled {
9590 let start = snapshot.anchor_after(selection_head);
9591 let end = snapshot.anchor_after(selection_head);
9592 self.autoclose_regions.push(AutocloseRegion {
9593 selection_id: selection.id,
9594 range: start..end,
9595 pair,
9596 });
9597 }
9598 }
9599 }
9600 }
9601 }
9602 Ok(())
9603 }
9604
9605 pub fn move_to_next_snippet_tabstop(
9606 &mut self,
9607 window: &mut Window,
9608 cx: &mut Context<Self>,
9609 ) -> bool {
9610 self.move_to_snippet_tabstop(Bias::Right, window, cx)
9611 }
9612
9613 pub fn move_to_prev_snippet_tabstop(
9614 &mut self,
9615 window: &mut Window,
9616 cx: &mut Context<Self>,
9617 ) -> bool {
9618 self.move_to_snippet_tabstop(Bias::Left, window, cx)
9619 }
9620
9621 pub fn move_to_snippet_tabstop(
9622 &mut self,
9623 bias: Bias,
9624 window: &mut Window,
9625 cx: &mut Context<Self>,
9626 ) -> bool {
9627 if let Some(mut snippet) = self.snippet_stack.pop() {
9628 match bias {
9629 Bias::Left => {
9630 if snippet.active_index > 0 {
9631 snippet.active_index -= 1;
9632 } else {
9633 self.snippet_stack.push(snippet);
9634 return false;
9635 }
9636 }
9637 Bias::Right => {
9638 if snippet.active_index + 1 < snippet.ranges.len() {
9639 snippet.active_index += 1;
9640 } else {
9641 self.snippet_stack.push(snippet);
9642 return false;
9643 }
9644 }
9645 }
9646 if let Some(current_ranges) = snippet.ranges.get(snippet.active_index) {
9647 self.change_selections(Default::default(), window, cx, |s| {
9648 // Reverse order so that the first range is the newest created selection.
9649 // Completions will use it and autoscroll will prioritize it.
9650 s.select_ranges(current_ranges.iter().rev().cloned())
9651 });
9652
9653 if let Some(choices) = &snippet.choices[snippet.active_index] {
9654 if let Some(selection) = current_ranges.first() {
9655 self.show_snippet_choices(&choices, selection.clone(), cx);
9656 }
9657 }
9658
9659 // If snippet state is not at the last tabstop, push it back on the stack
9660 if snippet.active_index + 1 < snippet.ranges.len() {
9661 self.snippet_stack.push(snippet);
9662 }
9663 return true;
9664 }
9665 }
9666
9667 false
9668 }
9669
9670 pub fn clear(&mut self, window: &mut Window, cx: &mut Context<Self>) {
9671 self.transact(window, cx, |this, window, cx| {
9672 this.select_all(&SelectAll, window, cx);
9673 this.insert("", window, cx);
9674 });
9675 }
9676
9677 pub fn backspace(&mut self, _: &Backspace, window: &mut Window, cx: &mut Context<Self>) {
9678 self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
9679 self.transact(window, cx, |this, window, cx| {
9680 this.select_autoclose_pair(window, cx);
9681 let mut linked_ranges = HashMap::<_, Vec<_>>::default();
9682 if !this.linked_edit_ranges.is_empty() {
9683 let selections = this.selections.all::<MultiBufferPoint>(cx);
9684 let snapshot = this.buffer.read(cx).snapshot(cx);
9685
9686 for selection in selections.iter() {
9687 let selection_start = snapshot.anchor_before(selection.start).text_anchor;
9688 let selection_end = snapshot.anchor_after(selection.end).text_anchor;
9689 if selection_start.buffer_id != selection_end.buffer_id {
9690 continue;
9691 }
9692 if let Some(ranges) =
9693 this.linked_editing_ranges_for(selection_start..selection_end, cx)
9694 {
9695 for (buffer, entries) in ranges {
9696 linked_ranges.entry(buffer).or_default().extend(entries);
9697 }
9698 }
9699 }
9700 }
9701
9702 let mut selections = this.selections.all::<MultiBufferPoint>(cx);
9703 let display_map = this.display_map.update(cx, |map, cx| map.snapshot(cx));
9704 for selection in &mut selections {
9705 if selection.is_empty() {
9706 let old_head = selection.head();
9707 let mut new_head =
9708 movement::left(&display_map, old_head.to_display_point(&display_map))
9709 .to_point(&display_map);
9710 if let Some((buffer, line_buffer_range)) = display_map
9711 .buffer_snapshot
9712 .buffer_line_for_row(MultiBufferRow(old_head.row))
9713 {
9714 let indent_size = buffer.indent_size_for_line(line_buffer_range.start.row);
9715 let indent_len = match indent_size.kind {
9716 IndentKind::Space => {
9717 buffer.settings_at(line_buffer_range.start, cx).tab_size
9718 }
9719 IndentKind::Tab => NonZeroU32::new(1).unwrap(),
9720 };
9721 if old_head.column <= indent_size.len && old_head.column > 0 {
9722 let indent_len = indent_len.get();
9723 new_head = cmp::min(
9724 new_head,
9725 MultiBufferPoint::new(
9726 old_head.row,
9727 ((old_head.column - 1) / indent_len) * indent_len,
9728 ),
9729 );
9730 }
9731 }
9732
9733 selection.set_head(new_head, SelectionGoal::None);
9734 }
9735 }
9736
9737 this.change_selections(Default::default(), window, cx, |s| s.select(selections));
9738 this.insert("", window, cx);
9739 let empty_str: Arc<str> = Arc::from("");
9740 for (buffer, edits) in linked_ranges {
9741 let snapshot = buffer.read(cx).snapshot();
9742 use text::ToPoint as TP;
9743
9744 let edits = edits
9745 .into_iter()
9746 .map(|range| {
9747 let end_point = TP::to_point(&range.end, &snapshot);
9748 let mut start_point = TP::to_point(&range.start, &snapshot);
9749
9750 if end_point == start_point {
9751 let offset = text::ToOffset::to_offset(&range.start, &snapshot)
9752 .saturating_sub(1);
9753 start_point =
9754 snapshot.clip_point(TP::to_point(&offset, &snapshot), Bias::Left);
9755 };
9756
9757 (start_point..end_point, empty_str.clone())
9758 })
9759 .sorted_by_key(|(range, _)| range.start)
9760 .collect::<Vec<_>>();
9761 buffer.update(cx, |this, cx| {
9762 this.edit(edits, None, cx);
9763 })
9764 }
9765 this.refresh_inline_completion(true, false, window, cx);
9766 linked_editing_ranges::refresh_linked_ranges(this, window, cx);
9767 });
9768 }
9769
9770 pub fn delete(&mut self, _: &Delete, window: &mut Window, cx: &mut Context<Self>) {
9771 self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
9772 self.transact(window, cx, |this, window, cx| {
9773 this.change_selections(Default::default(), window, cx, |s| {
9774 s.move_with(|map, selection| {
9775 if selection.is_empty() {
9776 let cursor = movement::right(map, selection.head());
9777 selection.end = cursor;
9778 selection.reversed = true;
9779 selection.goal = SelectionGoal::None;
9780 }
9781 })
9782 });
9783 this.insert("", window, cx);
9784 this.refresh_inline_completion(true, false, window, cx);
9785 });
9786 }
9787
9788 pub fn backtab(&mut self, _: &Backtab, window: &mut Window, cx: &mut Context<Self>) {
9789 if self.mode.is_single_line() {
9790 cx.propagate();
9791 return;
9792 }
9793
9794 self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
9795 if self.move_to_prev_snippet_tabstop(window, cx) {
9796 return;
9797 }
9798 self.outdent(&Outdent, window, cx);
9799 }
9800
9801 pub fn tab(&mut self, _: &Tab, window: &mut Window, cx: &mut Context<Self>) {
9802 if self.mode.is_single_line() {
9803 cx.propagate();
9804 return;
9805 }
9806
9807 if self.move_to_next_snippet_tabstop(window, cx) {
9808 self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
9809 return;
9810 }
9811 if self.read_only(cx) {
9812 return;
9813 }
9814 self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
9815 let mut selections = self.selections.all_adjusted(cx);
9816 let buffer = self.buffer.read(cx);
9817 let snapshot = buffer.snapshot(cx);
9818 let rows_iter = selections.iter().map(|s| s.head().row);
9819 let suggested_indents = snapshot.suggested_indents(rows_iter, cx);
9820
9821 let has_some_cursor_in_whitespace = selections
9822 .iter()
9823 .filter(|selection| selection.is_empty())
9824 .any(|selection| {
9825 let cursor = selection.head();
9826 let current_indent = snapshot.indent_size_for_line(MultiBufferRow(cursor.row));
9827 cursor.column < current_indent.len
9828 });
9829
9830 let mut edits = Vec::new();
9831 let mut prev_edited_row = 0;
9832 let mut row_delta = 0;
9833 for selection in &mut selections {
9834 if selection.start.row != prev_edited_row {
9835 row_delta = 0;
9836 }
9837 prev_edited_row = selection.end.row;
9838
9839 // If the selection is non-empty, then increase the indentation of the selected lines.
9840 if !selection.is_empty() {
9841 row_delta =
9842 Self::indent_selection(buffer, &snapshot, selection, &mut edits, row_delta, cx);
9843 continue;
9844 }
9845
9846 let cursor = selection.head();
9847 let current_indent = snapshot.indent_size_for_line(MultiBufferRow(cursor.row));
9848 if let Some(suggested_indent) =
9849 suggested_indents.get(&MultiBufferRow(cursor.row)).copied()
9850 {
9851 // Don't do anything if already at suggested indent
9852 // and there is any other cursor which is not
9853 if has_some_cursor_in_whitespace
9854 && cursor.column == current_indent.len
9855 && current_indent.len == suggested_indent.len
9856 {
9857 continue;
9858 }
9859
9860 // Adjust line and move cursor to suggested indent
9861 // if cursor is not at suggested indent
9862 if cursor.column < suggested_indent.len
9863 && cursor.column <= current_indent.len
9864 && current_indent.len <= suggested_indent.len
9865 {
9866 selection.start = Point::new(cursor.row, suggested_indent.len);
9867 selection.end = selection.start;
9868 if row_delta == 0 {
9869 edits.extend(Buffer::edit_for_indent_size_adjustment(
9870 cursor.row,
9871 current_indent,
9872 suggested_indent,
9873 ));
9874 row_delta = suggested_indent.len - current_indent.len;
9875 }
9876 continue;
9877 }
9878
9879 // If current indent is more than suggested indent
9880 // only move cursor to current indent and skip indent
9881 if cursor.column < current_indent.len && current_indent.len > suggested_indent.len {
9882 selection.start = Point::new(cursor.row, current_indent.len);
9883 selection.end = selection.start;
9884 continue;
9885 }
9886 }
9887
9888 // Otherwise, insert a hard or soft tab.
9889 let settings = buffer.language_settings_at(cursor, cx);
9890 let tab_size = if settings.hard_tabs {
9891 IndentSize::tab()
9892 } else {
9893 let tab_size = settings.tab_size.get();
9894 let indent_remainder = snapshot
9895 .text_for_range(Point::new(cursor.row, 0)..cursor)
9896 .flat_map(str::chars)
9897 .fold(row_delta % tab_size, |counter: u32, c| {
9898 if c == '\t' {
9899 0
9900 } else {
9901 (counter + 1) % tab_size
9902 }
9903 });
9904
9905 let chars_to_next_tab_stop = tab_size - indent_remainder;
9906 IndentSize::spaces(chars_to_next_tab_stop)
9907 };
9908 selection.start = Point::new(cursor.row, cursor.column + row_delta + tab_size.len);
9909 selection.end = selection.start;
9910 edits.push((cursor..cursor, tab_size.chars().collect::<String>()));
9911 row_delta += tab_size.len;
9912 }
9913
9914 self.transact(window, cx, |this, window, cx| {
9915 this.buffer.update(cx, |b, cx| b.edit(edits, None, cx));
9916 this.change_selections(Default::default(), window, cx, |s| s.select(selections));
9917 this.refresh_inline_completion(true, false, window, cx);
9918 });
9919 }
9920
9921 pub fn indent(&mut self, _: &Indent, window: &mut Window, cx: &mut Context<Self>) {
9922 if self.read_only(cx) {
9923 return;
9924 }
9925 if self.mode.is_single_line() {
9926 cx.propagate();
9927 return;
9928 }
9929
9930 self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
9931 let mut selections = self.selections.all::<Point>(cx);
9932 let mut prev_edited_row = 0;
9933 let mut row_delta = 0;
9934 let mut edits = Vec::new();
9935 let buffer = self.buffer.read(cx);
9936 let snapshot = buffer.snapshot(cx);
9937 for selection in &mut selections {
9938 if selection.start.row != prev_edited_row {
9939 row_delta = 0;
9940 }
9941 prev_edited_row = selection.end.row;
9942
9943 row_delta =
9944 Self::indent_selection(buffer, &snapshot, selection, &mut edits, row_delta, cx);
9945 }
9946
9947 self.transact(window, cx, |this, window, cx| {
9948 this.buffer.update(cx, |b, cx| b.edit(edits, None, cx));
9949 this.change_selections(Default::default(), window, cx, |s| s.select(selections));
9950 });
9951 }
9952
9953 fn indent_selection(
9954 buffer: &MultiBuffer,
9955 snapshot: &MultiBufferSnapshot,
9956 selection: &mut Selection<Point>,
9957 edits: &mut Vec<(Range<Point>, String)>,
9958 delta_for_start_row: u32,
9959 cx: &App,
9960 ) -> u32 {
9961 let settings = buffer.language_settings_at(selection.start, cx);
9962 let tab_size = settings.tab_size.get();
9963 let indent_kind = if settings.hard_tabs {
9964 IndentKind::Tab
9965 } else {
9966 IndentKind::Space
9967 };
9968 let mut start_row = selection.start.row;
9969 let mut end_row = selection.end.row + 1;
9970
9971 // If a selection ends at the beginning of a line, don't indent
9972 // that last line.
9973 if selection.end.column == 0 && selection.end.row > selection.start.row {
9974 end_row -= 1;
9975 }
9976
9977 // Avoid re-indenting a row that has already been indented by a
9978 // previous selection, but still update this selection's column
9979 // to reflect that indentation.
9980 if delta_for_start_row > 0 {
9981 start_row += 1;
9982 selection.start.column += delta_for_start_row;
9983 if selection.end.row == selection.start.row {
9984 selection.end.column += delta_for_start_row;
9985 }
9986 }
9987
9988 let mut delta_for_end_row = 0;
9989 let has_multiple_rows = start_row + 1 != end_row;
9990 for row in start_row..end_row {
9991 let current_indent = snapshot.indent_size_for_line(MultiBufferRow(row));
9992 let indent_delta = match (current_indent.kind, indent_kind) {
9993 (IndentKind::Space, IndentKind::Space) => {
9994 let columns_to_next_tab_stop = tab_size - (current_indent.len % tab_size);
9995 IndentSize::spaces(columns_to_next_tab_stop)
9996 }
9997 (IndentKind::Tab, IndentKind::Space) => IndentSize::spaces(tab_size),
9998 (_, IndentKind::Tab) => IndentSize::tab(),
9999 };
10000
10001 let start = if has_multiple_rows || current_indent.len < selection.start.column {
10002 0
10003 } else {
10004 selection.start.column
10005 };
10006 let row_start = Point::new(row, start);
10007 edits.push((
10008 row_start..row_start,
10009 indent_delta.chars().collect::<String>(),
10010 ));
10011
10012 // Update this selection's endpoints to reflect the indentation.
10013 if row == selection.start.row {
10014 selection.start.column += indent_delta.len;
10015 }
10016 if row == selection.end.row {
10017 selection.end.column += indent_delta.len;
10018 delta_for_end_row = indent_delta.len;
10019 }
10020 }
10021
10022 if selection.start.row == selection.end.row {
10023 delta_for_start_row + delta_for_end_row
10024 } else {
10025 delta_for_end_row
10026 }
10027 }
10028
10029 pub fn outdent(&mut self, _: &Outdent, window: &mut Window, cx: &mut Context<Self>) {
10030 if self.read_only(cx) {
10031 return;
10032 }
10033 if self.mode.is_single_line() {
10034 cx.propagate();
10035 return;
10036 }
10037
10038 self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
10039 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
10040 let selections = self.selections.all::<Point>(cx);
10041 let mut deletion_ranges = Vec::new();
10042 let mut last_outdent = None;
10043 {
10044 let buffer = self.buffer.read(cx);
10045 let snapshot = buffer.snapshot(cx);
10046 for selection in &selections {
10047 let settings = buffer.language_settings_at(selection.start, cx);
10048 let tab_size = settings.tab_size.get();
10049 let mut rows = selection.spanned_rows(false, &display_map);
10050
10051 // Avoid re-outdenting a row that has already been outdented by a
10052 // previous selection.
10053 if let Some(last_row) = last_outdent {
10054 if last_row == rows.start {
10055 rows.start = rows.start.next_row();
10056 }
10057 }
10058 let has_multiple_rows = rows.len() > 1;
10059 for row in rows.iter_rows() {
10060 let indent_size = snapshot.indent_size_for_line(row);
10061 if indent_size.len > 0 {
10062 let deletion_len = match indent_size.kind {
10063 IndentKind::Space => {
10064 let columns_to_prev_tab_stop = indent_size.len % tab_size;
10065 if columns_to_prev_tab_stop == 0 {
10066 tab_size
10067 } else {
10068 columns_to_prev_tab_stop
10069 }
10070 }
10071 IndentKind::Tab => 1,
10072 };
10073 let start = if has_multiple_rows
10074 || deletion_len > selection.start.column
10075 || indent_size.len < selection.start.column
10076 {
10077 0
10078 } else {
10079 selection.start.column - deletion_len
10080 };
10081 deletion_ranges.push(
10082 Point::new(row.0, start)..Point::new(row.0, start + deletion_len),
10083 );
10084 last_outdent = Some(row);
10085 }
10086 }
10087 }
10088 }
10089
10090 self.transact(window, cx, |this, window, cx| {
10091 this.buffer.update(cx, |buffer, cx| {
10092 let empty_str: Arc<str> = Arc::default();
10093 buffer.edit(
10094 deletion_ranges
10095 .into_iter()
10096 .map(|range| (range, empty_str.clone())),
10097 None,
10098 cx,
10099 );
10100 });
10101 let selections = this.selections.all::<usize>(cx);
10102 this.change_selections(Default::default(), window, cx, |s| s.select(selections));
10103 });
10104 }
10105
10106 pub fn autoindent(&mut self, _: &AutoIndent, window: &mut Window, cx: &mut Context<Self>) {
10107 if self.read_only(cx) {
10108 return;
10109 }
10110 if self.mode.is_single_line() {
10111 cx.propagate();
10112 return;
10113 }
10114
10115 self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
10116 let selections = self
10117 .selections
10118 .all::<usize>(cx)
10119 .into_iter()
10120 .map(|s| s.range());
10121
10122 self.transact(window, cx, |this, window, cx| {
10123 this.buffer.update(cx, |buffer, cx| {
10124 buffer.autoindent_ranges(selections, cx);
10125 });
10126 let selections = this.selections.all::<usize>(cx);
10127 this.change_selections(Default::default(), window, cx, |s| s.select(selections));
10128 });
10129 }
10130
10131 pub fn delete_line(&mut self, _: &DeleteLine, window: &mut Window, cx: &mut Context<Self>) {
10132 self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
10133 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
10134 let selections = self.selections.all::<Point>(cx);
10135
10136 let mut new_cursors = Vec::new();
10137 let mut edit_ranges = Vec::new();
10138 let mut selections = selections.iter().peekable();
10139 while let Some(selection) = selections.next() {
10140 let mut rows = selection.spanned_rows(false, &display_map);
10141 let goal_display_column = selection.head().to_display_point(&display_map).column();
10142
10143 // Accumulate contiguous regions of rows that we want to delete.
10144 while let Some(next_selection) = selections.peek() {
10145 let next_rows = next_selection.spanned_rows(false, &display_map);
10146 if next_rows.start <= rows.end {
10147 rows.end = next_rows.end;
10148 selections.next().unwrap();
10149 } else {
10150 break;
10151 }
10152 }
10153
10154 let buffer = &display_map.buffer_snapshot;
10155 let mut edit_start = Point::new(rows.start.0, 0).to_offset(buffer);
10156 let edit_end;
10157 let cursor_buffer_row;
10158 if buffer.max_point().row >= rows.end.0 {
10159 // If there's a line after the range, delete the \n from the end of the row range
10160 // and position the cursor on the next line.
10161 edit_end = Point::new(rows.end.0, 0).to_offset(buffer);
10162 cursor_buffer_row = rows.end;
10163 } else {
10164 // If there isn't a line after the range, delete the \n from the line before the
10165 // start of the row range and position the cursor there.
10166 edit_start = edit_start.saturating_sub(1);
10167 edit_end = buffer.len();
10168 cursor_buffer_row = rows.start.previous_row();
10169 }
10170
10171 let mut cursor = Point::new(cursor_buffer_row.0, 0).to_display_point(&display_map);
10172 *cursor.column_mut() =
10173 cmp::min(goal_display_column, display_map.line_len(cursor.row()));
10174
10175 new_cursors.push((
10176 selection.id,
10177 buffer.anchor_after(cursor.to_point(&display_map)),
10178 ));
10179 edit_ranges.push(edit_start..edit_end);
10180 }
10181
10182 self.transact(window, cx, |this, window, cx| {
10183 let buffer = this.buffer.update(cx, |buffer, cx| {
10184 let empty_str: Arc<str> = Arc::default();
10185 buffer.edit(
10186 edit_ranges
10187 .into_iter()
10188 .map(|range| (range, empty_str.clone())),
10189 None,
10190 cx,
10191 );
10192 buffer.snapshot(cx)
10193 });
10194 let new_selections = new_cursors
10195 .into_iter()
10196 .map(|(id, cursor)| {
10197 let cursor = cursor.to_point(&buffer);
10198 Selection {
10199 id,
10200 start: cursor,
10201 end: cursor,
10202 reversed: false,
10203 goal: SelectionGoal::None,
10204 }
10205 })
10206 .collect();
10207
10208 this.change_selections(Default::default(), window, cx, |s| {
10209 s.select(new_selections);
10210 });
10211 });
10212 }
10213
10214 pub fn join_lines_impl(
10215 &mut self,
10216 insert_whitespace: bool,
10217 window: &mut Window,
10218 cx: &mut Context<Self>,
10219 ) {
10220 if self.read_only(cx) {
10221 return;
10222 }
10223 let mut row_ranges = Vec::<Range<MultiBufferRow>>::new();
10224 for selection in self.selections.all::<Point>(cx) {
10225 let start = MultiBufferRow(selection.start.row);
10226 // Treat single line selections as if they include the next line. Otherwise this action
10227 // would do nothing for single line selections individual cursors.
10228 let end = if selection.start.row == selection.end.row {
10229 MultiBufferRow(selection.start.row + 1)
10230 } else {
10231 MultiBufferRow(selection.end.row)
10232 };
10233
10234 if let Some(last_row_range) = row_ranges.last_mut() {
10235 if start <= last_row_range.end {
10236 last_row_range.end = end;
10237 continue;
10238 }
10239 }
10240 row_ranges.push(start..end);
10241 }
10242
10243 let snapshot = self.buffer.read(cx).snapshot(cx);
10244 let mut cursor_positions = Vec::new();
10245 for row_range in &row_ranges {
10246 let anchor = snapshot.anchor_before(Point::new(
10247 row_range.end.previous_row().0,
10248 snapshot.line_len(row_range.end.previous_row()),
10249 ));
10250 cursor_positions.push(anchor..anchor);
10251 }
10252
10253 self.transact(window, cx, |this, window, cx| {
10254 for row_range in row_ranges.into_iter().rev() {
10255 for row in row_range.iter_rows().rev() {
10256 let end_of_line = Point::new(row.0, snapshot.line_len(row));
10257 let next_line_row = row.next_row();
10258 let indent = snapshot.indent_size_for_line(next_line_row);
10259 let start_of_next_line = Point::new(next_line_row.0, indent.len);
10260
10261 let replace =
10262 if snapshot.line_len(next_line_row) > indent.len && insert_whitespace {
10263 " "
10264 } else {
10265 ""
10266 };
10267
10268 this.buffer.update(cx, |buffer, cx| {
10269 buffer.edit([(end_of_line..start_of_next_line, replace)], None, cx)
10270 });
10271 }
10272 }
10273
10274 this.change_selections(Default::default(), window, cx, |s| {
10275 s.select_anchor_ranges(cursor_positions)
10276 });
10277 });
10278 }
10279
10280 pub fn join_lines(&mut self, _: &JoinLines, window: &mut Window, cx: &mut Context<Self>) {
10281 self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
10282 self.join_lines_impl(true, window, cx);
10283 }
10284
10285 pub fn sort_lines_case_sensitive(
10286 &mut self,
10287 _: &SortLinesCaseSensitive,
10288 window: &mut Window,
10289 cx: &mut Context<Self>,
10290 ) {
10291 self.manipulate_immutable_lines(window, cx, |lines| lines.sort())
10292 }
10293
10294 pub fn sort_lines_by_length(
10295 &mut self,
10296 _: &SortLinesByLength,
10297 window: &mut Window,
10298 cx: &mut Context<Self>,
10299 ) {
10300 self.manipulate_immutable_lines(window, cx, |lines| {
10301 lines.sort_by_key(|&line| line.chars().count())
10302 })
10303 }
10304
10305 pub fn sort_lines_case_insensitive(
10306 &mut self,
10307 _: &SortLinesCaseInsensitive,
10308 window: &mut Window,
10309 cx: &mut Context<Self>,
10310 ) {
10311 self.manipulate_immutable_lines(window, cx, |lines| {
10312 lines.sort_by_key(|line| line.to_lowercase())
10313 })
10314 }
10315
10316 pub fn unique_lines_case_insensitive(
10317 &mut self,
10318 _: &UniqueLinesCaseInsensitive,
10319 window: &mut Window,
10320 cx: &mut Context<Self>,
10321 ) {
10322 self.manipulate_immutable_lines(window, cx, |lines| {
10323 let mut seen = HashSet::default();
10324 lines.retain(|line| seen.insert(line.to_lowercase()));
10325 })
10326 }
10327
10328 pub fn unique_lines_case_sensitive(
10329 &mut self,
10330 _: &UniqueLinesCaseSensitive,
10331 window: &mut Window,
10332 cx: &mut Context<Self>,
10333 ) {
10334 self.manipulate_immutable_lines(window, cx, |lines| {
10335 let mut seen = HashSet::default();
10336 lines.retain(|line| seen.insert(*line));
10337 })
10338 }
10339
10340 pub fn reload_file(&mut self, _: &ReloadFile, window: &mut Window, cx: &mut Context<Self>) {
10341 let Some(project) = self.project.clone() else {
10342 return;
10343 };
10344 self.reload(project, window, cx)
10345 .detach_and_notify_err(window, cx);
10346 }
10347
10348 pub fn restore_file(
10349 &mut self,
10350 _: &::git::RestoreFile,
10351 window: &mut Window,
10352 cx: &mut Context<Self>,
10353 ) {
10354 self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
10355 let mut buffer_ids = HashSet::default();
10356 let snapshot = self.buffer().read(cx).snapshot(cx);
10357 for selection in self.selections.all::<usize>(cx) {
10358 buffer_ids.extend(snapshot.buffer_ids_for_range(selection.range()))
10359 }
10360
10361 let buffer = self.buffer().read(cx);
10362 let ranges = buffer_ids
10363 .into_iter()
10364 .flat_map(|buffer_id| buffer.excerpt_ranges_for_buffer(buffer_id, cx))
10365 .collect::<Vec<_>>();
10366
10367 self.restore_hunks_in_ranges(ranges, window, cx);
10368 }
10369
10370 pub fn git_restore(&mut self, _: &Restore, window: &mut Window, cx: &mut Context<Self>) {
10371 self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
10372 let selections = self
10373 .selections
10374 .all(cx)
10375 .into_iter()
10376 .map(|s| s.range())
10377 .collect();
10378 self.restore_hunks_in_ranges(selections, window, cx);
10379 }
10380
10381 pub fn restore_hunks_in_ranges(
10382 &mut self,
10383 ranges: Vec<Range<Point>>,
10384 window: &mut Window,
10385 cx: &mut Context<Editor>,
10386 ) {
10387 let mut revert_changes = HashMap::default();
10388 let chunk_by = self
10389 .snapshot(window, cx)
10390 .hunks_for_ranges(ranges)
10391 .into_iter()
10392 .chunk_by(|hunk| hunk.buffer_id);
10393 for (buffer_id, hunks) in &chunk_by {
10394 let hunks = hunks.collect::<Vec<_>>();
10395 for hunk in &hunks {
10396 self.prepare_restore_change(&mut revert_changes, hunk, cx);
10397 }
10398 self.do_stage_or_unstage(false, buffer_id, hunks.into_iter(), cx);
10399 }
10400 drop(chunk_by);
10401 if !revert_changes.is_empty() {
10402 self.transact(window, cx, |editor, window, cx| {
10403 editor.restore(revert_changes, window, cx);
10404 });
10405 }
10406 }
10407
10408 pub fn open_active_item_in_terminal(
10409 &mut self,
10410 _: &OpenInTerminal,
10411 window: &mut Window,
10412 cx: &mut Context<Self>,
10413 ) {
10414 if let Some(working_directory) = self.active_excerpt(cx).and_then(|(_, buffer, _)| {
10415 let project_path = buffer.read(cx).project_path(cx)?;
10416 let project = self.project.as_ref()?.read(cx);
10417 let entry = project.entry_for_path(&project_path, cx)?;
10418 let parent = match &entry.canonical_path {
10419 Some(canonical_path) => canonical_path.to_path_buf(),
10420 None => project.absolute_path(&project_path, cx)?,
10421 }
10422 .parent()?
10423 .to_path_buf();
10424 Some(parent)
10425 }) {
10426 window.dispatch_action(OpenTerminal { working_directory }.boxed_clone(), cx);
10427 }
10428 }
10429
10430 fn set_breakpoint_context_menu(
10431 &mut self,
10432 display_row: DisplayRow,
10433 position: Option<Anchor>,
10434 clicked_point: gpui::Point<Pixels>,
10435 window: &mut Window,
10436 cx: &mut Context<Self>,
10437 ) {
10438 let source = self
10439 .buffer
10440 .read(cx)
10441 .snapshot(cx)
10442 .anchor_before(Point::new(display_row.0, 0u32));
10443
10444 let context_menu = self.breakpoint_context_menu(position.unwrap_or(source), window, cx);
10445
10446 self.mouse_context_menu = MouseContextMenu::pinned_to_editor(
10447 self,
10448 source,
10449 clicked_point,
10450 context_menu,
10451 window,
10452 cx,
10453 );
10454 }
10455
10456 fn add_edit_breakpoint_block(
10457 &mut self,
10458 anchor: Anchor,
10459 breakpoint: &Breakpoint,
10460 edit_action: BreakpointPromptEditAction,
10461 window: &mut Window,
10462 cx: &mut Context<Self>,
10463 ) {
10464 let weak_editor = cx.weak_entity();
10465 let bp_prompt = cx.new(|cx| {
10466 BreakpointPromptEditor::new(
10467 weak_editor,
10468 anchor,
10469 breakpoint.clone(),
10470 edit_action,
10471 window,
10472 cx,
10473 )
10474 });
10475
10476 let height = bp_prompt.update(cx, |this, cx| {
10477 this.prompt
10478 .update(cx, |prompt, cx| prompt.max_point(cx).row().0 + 1 + 2)
10479 });
10480 let cloned_prompt = bp_prompt.clone();
10481 let blocks = vec![BlockProperties {
10482 style: BlockStyle::Sticky,
10483 placement: BlockPlacement::Above(anchor),
10484 height: Some(height),
10485 render: Arc::new(move |cx| {
10486 *cloned_prompt.read(cx).editor_margins.lock() = *cx.margins;
10487 cloned_prompt.clone().into_any_element()
10488 }),
10489 priority: 0,
10490 }];
10491
10492 let focus_handle = bp_prompt.focus_handle(cx);
10493 window.focus(&focus_handle);
10494
10495 let block_ids = self.insert_blocks(blocks, None, cx);
10496 bp_prompt.update(cx, |prompt, _| {
10497 prompt.add_block_ids(block_ids);
10498 });
10499 }
10500
10501 pub(crate) fn breakpoint_at_row(
10502 &self,
10503 row: u32,
10504 window: &mut Window,
10505 cx: &mut Context<Self>,
10506 ) -> Option<(Anchor, Breakpoint)> {
10507 let snapshot = self.snapshot(window, cx);
10508 let breakpoint_position = snapshot.buffer_snapshot.anchor_before(Point::new(row, 0));
10509
10510 self.breakpoint_at_anchor(breakpoint_position, &snapshot, cx)
10511 }
10512
10513 pub(crate) fn breakpoint_at_anchor(
10514 &self,
10515 breakpoint_position: Anchor,
10516 snapshot: &EditorSnapshot,
10517 cx: &mut Context<Self>,
10518 ) -> Option<(Anchor, Breakpoint)> {
10519 let project = self.project.clone()?;
10520
10521 let buffer_id = breakpoint_position.buffer_id.or_else(|| {
10522 snapshot
10523 .buffer_snapshot
10524 .buffer_id_for_excerpt(breakpoint_position.excerpt_id)
10525 })?;
10526
10527 let enclosing_excerpt = breakpoint_position.excerpt_id;
10528 let buffer = project.read(cx).buffer_for_id(buffer_id, cx)?;
10529 let buffer_snapshot = buffer.read(cx).snapshot();
10530
10531 let row = buffer_snapshot
10532 .summary_for_anchor::<text::PointUtf16>(&breakpoint_position.text_anchor)
10533 .row;
10534
10535 let line_len = snapshot.buffer_snapshot.line_len(MultiBufferRow(row));
10536 let anchor_end = snapshot
10537 .buffer_snapshot
10538 .anchor_after(Point::new(row, line_len));
10539
10540 let bp = self
10541 .breakpoint_store
10542 .as_ref()?
10543 .read_with(cx, |breakpoint_store, cx| {
10544 breakpoint_store
10545 .breakpoints(
10546 &buffer,
10547 Some(breakpoint_position.text_anchor..anchor_end.text_anchor),
10548 &buffer_snapshot,
10549 cx,
10550 )
10551 .next()
10552 .and_then(|(bp, _)| {
10553 let breakpoint_row = buffer_snapshot
10554 .summary_for_anchor::<text::PointUtf16>(&bp.position)
10555 .row;
10556
10557 if breakpoint_row == row {
10558 snapshot
10559 .buffer_snapshot
10560 .anchor_in_excerpt(enclosing_excerpt, bp.position)
10561 .map(|position| (position, bp.bp.clone()))
10562 } else {
10563 None
10564 }
10565 })
10566 });
10567 bp
10568 }
10569
10570 pub fn edit_log_breakpoint(
10571 &mut self,
10572 _: &EditLogBreakpoint,
10573 window: &mut Window,
10574 cx: &mut Context<Self>,
10575 ) {
10576 for (anchor, breakpoint) in self.breakpoints_at_cursors(window, cx) {
10577 let breakpoint = breakpoint.unwrap_or_else(|| Breakpoint {
10578 message: None,
10579 state: BreakpointState::Enabled,
10580 condition: None,
10581 hit_condition: None,
10582 });
10583
10584 self.add_edit_breakpoint_block(
10585 anchor,
10586 &breakpoint,
10587 BreakpointPromptEditAction::Log,
10588 window,
10589 cx,
10590 );
10591 }
10592 }
10593
10594 fn breakpoints_at_cursors(
10595 &self,
10596 window: &mut Window,
10597 cx: &mut Context<Self>,
10598 ) -> Vec<(Anchor, Option<Breakpoint>)> {
10599 let snapshot = self.snapshot(window, cx);
10600 let cursors = self
10601 .selections
10602 .disjoint_anchors()
10603 .into_iter()
10604 .map(|selection| {
10605 let cursor_position: Point = selection.head().to_point(&snapshot.buffer_snapshot);
10606
10607 let breakpoint_position = self
10608 .breakpoint_at_row(cursor_position.row, window, cx)
10609 .map(|bp| bp.0)
10610 .unwrap_or_else(|| {
10611 snapshot
10612 .display_snapshot
10613 .buffer_snapshot
10614 .anchor_after(Point::new(cursor_position.row, 0))
10615 });
10616
10617 let breakpoint = self
10618 .breakpoint_at_anchor(breakpoint_position, &snapshot, cx)
10619 .map(|(anchor, breakpoint)| (anchor, Some(breakpoint)));
10620
10621 breakpoint.unwrap_or_else(|| (breakpoint_position, None))
10622 })
10623 // 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.
10624 .collect::<HashMap<Anchor, _>>();
10625
10626 cursors.into_iter().collect()
10627 }
10628
10629 pub fn enable_breakpoint(
10630 &mut self,
10631 _: &crate::actions::EnableBreakpoint,
10632 window: &mut Window,
10633 cx: &mut Context<Self>,
10634 ) {
10635 for (anchor, breakpoint) in self.breakpoints_at_cursors(window, cx) {
10636 let Some(breakpoint) = breakpoint.filter(|breakpoint| breakpoint.is_disabled()) else {
10637 continue;
10638 };
10639 self.edit_breakpoint_at_anchor(
10640 anchor,
10641 breakpoint,
10642 BreakpointEditAction::InvertState,
10643 cx,
10644 );
10645 }
10646 }
10647
10648 pub fn disable_breakpoint(
10649 &mut self,
10650 _: &crate::actions::DisableBreakpoint,
10651 window: &mut Window,
10652 cx: &mut Context<Self>,
10653 ) {
10654 for (anchor, breakpoint) in self.breakpoints_at_cursors(window, cx) {
10655 let Some(breakpoint) = breakpoint.filter(|breakpoint| breakpoint.is_enabled()) else {
10656 continue;
10657 };
10658 self.edit_breakpoint_at_anchor(
10659 anchor,
10660 breakpoint,
10661 BreakpointEditAction::InvertState,
10662 cx,
10663 );
10664 }
10665 }
10666
10667 pub fn toggle_breakpoint(
10668 &mut self,
10669 _: &crate::actions::ToggleBreakpoint,
10670 window: &mut Window,
10671 cx: &mut Context<Self>,
10672 ) {
10673 for (anchor, breakpoint) in self.breakpoints_at_cursors(window, cx) {
10674 if let Some(breakpoint) = breakpoint {
10675 self.edit_breakpoint_at_anchor(
10676 anchor,
10677 breakpoint,
10678 BreakpointEditAction::Toggle,
10679 cx,
10680 );
10681 } else {
10682 self.edit_breakpoint_at_anchor(
10683 anchor,
10684 Breakpoint::new_standard(),
10685 BreakpointEditAction::Toggle,
10686 cx,
10687 );
10688 }
10689 }
10690 }
10691
10692 pub fn edit_breakpoint_at_anchor(
10693 &mut self,
10694 breakpoint_position: Anchor,
10695 breakpoint: Breakpoint,
10696 edit_action: BreakpointEditAction,
10697 cx: &mut Context<Self>,
10698 ) {
10699 let Some(breakpoint_store) = &self.breakpoint_store else {
10700 return;
10701 };
10702
10703 let Some(buffer_id) = breakpoint_position.buffer_id.or_else(|| {
10704 if breakpoint_position == Anchor::min() {
10705 self.buffer()
10706 .read(cx)
10707 .excerpt_buffer_ids()
10708 .into_iter()
10709 .next()
10710 } else {
10711 None
10712 }
10713 }) else {
10714 return;
10715 };
10716
10717 let Some(buffer) = self.buffer().read(cx).buffer(buffer_id) else {
10718 return;
10719 };
10720
10721 breakpoint_store.update(cx, |breakpoint_store, cx| {
10722 breakpoint_store.toggle_breakpoint(
10723 buffer,
10724 BreakpointWithPosition {
10725 position: breakpoint_position.text_anchor,
10726 bp: breakpoint,
10727 },
10728 edit_action,
10729 cx,
10730 );
10731 });
10732
10733 cx.notify();
10734 }
10735
10736 #[cfg(any(test, feature = "test-support"))]
10737 pub fn breakpoint_store(&self) -> Option<Entity<BreakpointStore>> {
10738 self.breakpoint_store.clone()
10739 }
10740
10741 pub fn prepare_restore_change(
10742 &self,
10743 revert_changes: &mut HashMap<BufferId, Vec<(Range<text::Anchor>, Rope)>>,
10744 hunk: &MultiBufferDiffHunk,
10745 cx: &mut App,
10746 ) -> Option<()> {
10747 if hunk.is_created_file() {
10748 return None;
10749 }
10750 let buffer = self.buffer.read(cx);
10751 let diff = buffer.diff_for(hunk.buffer_id)?;
10752 let buffer = buffer.buffer(hunk.buffer_id)?;
10753 let buffer = buffer.read(cx);
10754 let original_text = diff
10755 .read(cx)
10756 .base_text()
10757 .as_rope()
10758 .slice(hunk.diff_base_byte_range.clone());
10759 let buffer_snapshot = buffer.snapshot();
10760 let buffer_revert_changes = revert_changes.entry(buffer.remote_id()).or_default();
10761 if let Err(i) = buffer_revert_changes.binary_search_by(|probe| {
10762 probe
10763 .0
10764 .start
10765 .cmp(&hunk.buffer_range.start, &buffer_snapshot)
10766 .then(probe.0.end.cmp(&hunk.buffer_range.end, &buffer_snapshot))
10767 }) {
10768 buffer_revert_changes.insert(i, (hunk.buffer_range.clone(), original_text));
10769 Some(())
10770 } else {
10771 None
10772 }
10773 }
10774
10775 pub fn reverse_lines(&mut self, _: &ReverseLines, window: &mut Window, cx: &mut Context<Self>) {
10776 self.manipulate_immutable_lines(window, cx, |lines| lines.reverse())
10777 }
10778
10779 pub fn shuffle_lines(&mut self, _: &ShuffleLines, window: &mut Window, cx: &mut Context<Self>) {
10780 self.manipulate_immutable_lines(window, cx, |lines| lines.shuffle(&mut thread_rng()))
10781 }
10782
10783 fn manipulate_lines<M>(
10784 &mut self,
10785 window: &mut Window,
10786 cx: &mut Context<Self>,
10787 mut manipulate: M,
10788 ) where
10789 M: FnMut(&str) -> LineManipulationResult,
10790 {
10791 self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
10792
10793 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
10794 let buffer = self.buffer.read(cx).snapshot(cx);
10795
10796 let mut edits = Vec::new();
10797
10798 let selections = self.selections.all::<Point>(cx);
10799 let mut selections = selections.iter().peekable();
10800 let mut contiguous_row_selections = Vec::new();
10801 let mut new_selections = Vec::new();
10802 let mut added_lines = 0;
10803 let mut removed_lines = 0;
10804
10805 while let Some(selection) = selections.next() {
10806 let (start_row, end_row) = consume_contiguous_rows(
10807 &mut contiguous_row_selections,
10808 selection,
10809 &display_map,
10810 &mut selections,
10811 );
10812
10813 let start_point = Point::new(start_row.0, 0);
10814 let end_point = Point::new(
10815 end_row.previous_row().0,
10816 buffer.line_len(end_row.previous_row()),
10817 );
10818 let text = buffer
10819 .text_for_range(start_point..end_point)
10820 .collect::<String>();
10821
10822 let LineManipulationResult {
10823 new_text,
10824 line_count_before,
10825 line_count_after,
10826 } = manipulate(&text);
10827
10828 edits.push((start_point..end_point, new_text));
10829
10830 // Selections must change based on added and removed line count
10831 let start_row =
10832 MultiBufferRow(start_point.row + added_lines as u32 - removed_lines as u32);
10833 let end_row = MultiBufferRow(start_row.0 + line_count_after.saturating_sub(1) as u32);
10834 new_selections.push(Selection {
10835 id: selection.id,
10836 start: start_row,
10837 end: end_row,
10838 goal: SelectionGoal::None,
10839 reversed: selection.reversed,
10840 });
10841
10842 if line_count_after > line_count_before {
10843 added_lines += line_count_after - line_count_before;
10844 } else if line_count_before > line_count_after {
10845 removed_lines += line_count_before - line_count_after;
10846 }
10847 }
10848
10849 self.transact(window, cx, |this, window, cx| {
10850 let buffer = this.buffer.update(cx, |buffer, cx| {
10851 buffer.edit(edits, None, cx);
10852 buffer.snapshot(cx)
10853 });
10854
10855 // Recalculate offsets on newly edited buffer
10856 let new_selections = new_selections
10857 .iter()
10858 .map(|s| {
10859 let start_point = Point::new(s.start.0, 0);
10860 let end_point = Point::new(s.end.0, buffer.line_len(s.end));
10861 Selection {
10862 id: s.id,
10863 start: buffer.point_to_offset(start_point),
10864 end: buffer.point_to_offset(end_point),
10865 goal: s.goal,
10866 reversed: s.reversed,
10867 }
10868 })
10869 .collect();
10870
10871 this.change_selections(Default::default(), window, cx, |s| {
10872 s.select(new_selections);
10873 });
10874
10875 this.request_autoscroll(Autoscroll::fit(), cx);
10876 });
10877 }
10878
10879 pub fn toggle_case(&mut self, _: &ToggleCase, window: &mut Window, cx: &mut Context<Self>) {
10880 self.manipulate_text(window, cx, |text| {
10881 let has_upper_case_characters = text.chars().any(|c| c.is_uppercase());
10882 if has_upper_case_characters {
10883 text.to_lowercase()
10884 } else {
10885 text.to_uppercase()
10886 }
10887 })
10888 }
10889
10890 fn manipulate_immutable_lines<Fn>(
10891 &mut self,
10892 window: &mut Window,
10893 cx: &mut Context<Self>,
10894 mut callback: Fn,
10895 ) where
10896 Fn: FnMut(&mut Vec<&str>),
10897 {
10898 self.manipulate_lines(window, cx, |text| {
10899 let mut lines: Vec<&str> = text.split('\n').collect();
10900 let line_count_before = lines.len();
10901
10902 callback(&mut lines);
10903
10904 LineManipulationResult {
10905 new_text: lines.join("\n"),
10906 line_count_before,
10907 line_count_after: lines.len(),
10908 }
10909 });
10910 }
10911
10912 fn manipulate_mutable_lines<Fn>(
10913 &mut self,
10914 window: &mut Window,
10915 cx: &mut Context<Self>,
10916 mut callback: Fn,
10917 ) where
10918 Fn: FnMut(&mut Vec<Cow<'_, str>>),
10919 {
10920 self.manipulate_lines(window, cx, |text| {
10921 let mut lines: Vec<Cow<str>> = text.split('\n').map(Cow::from).collect();
10922 let line_count_before = lines.len();
10923
10924 callback(&mut lines);
10925
10926 LineManipulationResult {
10927 new_text: lines.join("\n"),
10928 line_count_before,
10929 line_count_after: lines.len(),
10930 }
10931 });
10932 }
10933
10934 pub fn convert_indentation_to_spaces(
10935 &mut self,
10936 _: &ConvertIndentationToSpaces,
10937 window: &mut Window,
10938 cx: &mut Context<Self>,
10939 ) {
10940 let settings = self.buffer.read(cx).language_settings(cx);
10941 let tab_size = settings.tab_size.get() as usize;
10942
10943 self.manipulate_mutable_lines(window, cx, |lines| {
10944 // Allocates a reasonably sized scratch buffer once for the whole loop
10945 let mut reindented_line = String::with_capacity(MAX_LINE_LEN);
10946 // Avoids recomputing spaces that could be inserted many times
10947 let space_cache: Vec<Vec<char>> = (1..=tab_size)
10948 .map(|n| IndentSize::spaces(n as u32).chars().collect())
10949 .collect();
10950
10951 for line in lines.iter_mut().filter(|line| !line.is_empty()) {
10952 let mut chars = line.as_ref().chars();
10953 let mut col = 0;
10954 let mut changed = false;
10955
10956 while let Some(ch) = chars.next() {
10957 match ch {
10958 ' ' => {
10959 reindented_line.push(' ');
10960 col += 1;
10961 }
10962 '\t' => {
10963 // \t are converted to spaces depending on the current column
10964 let spaces_len = tab_size - (col % tab_size);
10965 reindented_line.extend(&space_cache[spaces_len - 1]);
10966 col += spaces_len;
10967 changed = true;
10968 }
10969 _ => {
10970 // If we dont append before break, the character is consumed
10971 reindented_line.push(ch);
10972 break;
10973 }
10974 }
10975 }
10976
10977 if !changed {
10978 reindented_line.clear();
10979 continue;
10980 }
10981 // Append the rest of the line and replace old reference with new one
10982 reindented_line.extend(chars);
10983 *line = Cow::Owned(reindented_line.clone());
10984 reindented_line.clear();
10985 }
10986 });
10987 }
10988
10989 pub fn convert_indentation_to_tabs(
10990 &mut self,
10991 _: &ConvertIndentationToTabs,
10992 window: &mut Window,
10993 cx: &mut Context<Self>,
10994 ) {
10995 let settings = self.buffer.read(cx).language_settings(cx);
10996 let tab_size = settings.tab_size.get() as usize;
10997
10998 self.manipulate_mutable_lines(window, cx, |lines| {
10999 // Allocates a reasonably sized buffer once for the whole loop
11000 let mut reindented_line = String::with_capacity(MAX_LINE_LEN);
11001 // Avoids recomputing spaces that could be inserted many times
11002 let space_cache: Vec<Vec<char>> = (1..=tab_size)
11003 .map(|n| IndentSize::spaces(n as u32).chars().collect())
11004 .collect();
11005
11006 for line in lines.iter_mut().filter(|line| !line.is_empty()) {
11007 let mut chars = line.chars();
11008 let mut spaces_count = 0;
11009 let mut first_non_indent_char = None;
11010 let mut changed = false;
11011
11012 while let Some(ch) = chars.next() {
11013 match ch {
11014 ' ' => {
11015 // Keep track of spaces. Append \t when we reach tab_size
11016 spaces_count += 1;
11017 changed = true;
11018 if spaces_count == tab_size {
11019 reindented_line.push('\t');
11020 spaces_count = 0;
11021 }
11022 }
11023 '\t' => {
11024 reindented_line.push('\t');
11025 spaces_count = 0;
11026 }
11027 _ => {
11028 // Dont append it yet, we might have remaining spaces
11029 first_non_indent_char = Some(ch);
11030 break;
11031 }
11032 }
11033 }
11034
11035 if !changed {
11036 reindented_line.clear();
11037 continue;
11038 }
11039 // Remaining spaces that didn't make a full tab stop
11040 if spaces_count > 0 {
11041 reindented_line.extend(&space_cache[spaces_count - 1]);
11042 }
11043 // If we consume an extra character that was not indentation, add it back
11044 if let Some(extra_char) = first_non_indent_char {
11045 reindented_line.push(extra_char);
11046 }
11047 // Append the rest of the line and replace old reference with new one
11048 reindented_line.extend(chars);
11049 *line = Cow::Owned(reindented_line.clone());
11050 reindented_line.clear();
11051 }
11052 });
11053 }
11054
11055 pub fn convert_to_upper_case(
11056 &mut self,
11057 _: &ConvertToUpperCase,
11058 window: &mut Window,
11059 cx: &mut Context<Self>,
11060 ) {
11061 self.manipulate_text(window, cx, |text| text.to_uppercase())
11062 }
11063
11064 pub fn convert_to_lower_case(
11065 &mut self,
11066 _: &ConvertToLowerCase,
11067 window: &mut Window,
11068 cx: &mut Context<Self>,
11069 ) {
11070 self.manipulate_text(window, cx, |text| text.to_lowercase())
11071 }
11072
11073 pub fn convert_to_title_case(
11074 &mut self,
11075 _: &ConvertToTitleCase,
11076 window: &mut Window,
11077 cx: &mut Context<Self>,
11078 ) {
11079 self.manipulate_text(window, cx, |text| {
11080 text.split('\n')
11081 .map(|line| line.to_case(Case::Title))
11082 .join("\n")
11083 })
11084 }
11085
11086 pub fn convert_to_snake_case(
11087 &mut self,
11088 _: &ConvertToSnakeCase,
11089 window: &mut Window,
11090 cx: &mut Context<Self>,
11091 ) {
11092 self.manipulate_text(window, cx, |text| text.to_case(Case::Snake))
11093 }
11094
11095 pub fn convert_to_kebab_case(
11096 &mut self,
11097 _: &ConvertToKebabCase,
11098 window: &mut Window,
11099 cx: &mut Context<Self>,
11100 ) {
11101 self.manipulate_text(window, cx, |text| text.to_case(Case::Kebab))
11102 }
11103
11104 pub fn convert_to_upper_camel_case(
11105 &mut self,
11106 _: &ConvertToUpperCamelCase,
11107 window: &mut Window,
11108 cx: &mut Context<Self>,
11109 ) {
11110 self.manipulate_text(window, cx, |text| {
11111 text.split('\n')
11112 .map(|line| line.to_case(Case::UpperCamel))
11113 .join("\n")
11114 })
11115 }
11116
11117 pub fn convert_to_lower_camel_case(
11118 &mut self,
11119 _: &ConvertToLowerCamelCase,
11120 window: &mut Window,
11121 cx: &mut Context<Self>,
11122 ) {
11123 self.manipulate_text(window, cx, |text| text.to_case(Case::Camel))
11124 }
11125
11126 pub fn convert_to_opposite_case(
11127 &mut self,
11128 _: &ConvertToOppositeCase,
11129 window: &mut Window,
11130 cx: &mut Context<Self>,
11131 ) {
11132 self.manipulate_text(window, cx, |text| {
11133 text.chars()
11134 .fold(String::with_capacity(text.len()), |mut t, c| {
11135 if c.is_uppercase() {
11136 t.extend(c.to_lowercase());
11137 } else {
11138 t.extend(c.to_uppercase());
11139 }
11140 t
11141 })
11142 })
11143 }
11144
11145 pub fn convert_to_rot13(
11146 &mut self,
11147 _: &ConvertToRot13,
11148 window: &mut Window,
11149 cx: &mut Context<Self>,
11150 ) {
11151 self.manipulate_text(window, cx, |text| {
11152 text.chars()
11153 .map(|c| match c {
11154 'A'..='M' | 'a'..='m' => ((c as u8) + 13) as char,
11155 'N'..='Z' | 'n'..='z' => ((c as u8) - 13) as char,
11156 _ => c,
11157 })
11158 .collect()
11159 })
11160 }
11161
11162 pub fn convert_to_rot47(
11163 &mut self,
11164 _: &ConvertToRot47,
11165 window: &mut Window,
11166 cx: &mut Context<Self>,
11167 ) {
11168 self.manipulate_text(window, cx, |text| {
11169 text.chars()
11170 .map(|c| {
11171 let code_point = c as u32;
11172 if code_point >= 33 && code_point <= 126 {
11173 return char::from_u32(33 + ((code_point + 14) % 94)).unwrap();
11174 }
11175 c
11176 })
11177 .collect()
11178 })
11179 }
11180
11181 fn manipulate_text<Fn>(&mut self, window: &mut Window, cx: &mut Context<Self>, mut callback: Fn)
11182 where
11183 Fn: FnMut(&str) -> String,
11184 {
11185 let buffer = self.buffer.read(cx).snapshot(cx);
11186
11187 let mut new_selections = Vec::new();
11188 let mut edits = Vec::new();
11189 let mut selection_adjustment = 0i32;
11190
11191 for selection in self.selections.all::<usize>(cx) {
11192 let selection_is_empty = selection.is_empty();
11193
11194 let (start, end) = if selection_is_empty {
11195 let (word_range, _) = buffer.surrounding_word(selection.start, false);
11196 (word_range.start, word_range.end)
11197 } else {
11198 (selection.start, selection.end)
11199 };
11200
11201 let text = buffer.text_for_range(start..end).collect::<String>();
11202 let old_length = text.len() as i32;
11203 let text = callback(&text);
11204
11205 new_selections.push(Selection {
11206 start: (start as i32 - selection_adjustment) as usize,
11207 end: ((start + text.len()) as i32 - selection_adjustment) as usize,
11208 goal: SelectionGoal::None,
11209 ..selection
11210 });
11211
11212 selection_adjustment += old_length - text.len() as i32;
11213
11214 edits.push((start..end, text));
11215 }
11216
11217 self.transact(window, cx, |this, window, cx| {
11218 this.buffer.update(cx, |buffer, cx| {
11219 buffer.edit(edits, None, cx);
11220 });
11221
11222 this.change_selections(Default::default(), window, cx, |s| {
11223 s.select(new_selections);
11224 });
11225
11226 this.request_autoscroll(Autoscroll::fit(), cx);
11227 });
11228 }
11229
11230 pub fn move_selection_on_drop(
11231 &mut self,
11232 selection: &Selection<Anchor>,
11233 target: DisplayPoint,
11234 is_cut: bool,
11235 window: &mut Window,
11236 cx: &mut Context<Self>,
11237 ) {
11238 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
11239 let buffer = &display_map.buffer_snapshot;
11240 let mut edits = Vec::new();
11241 let insert_point = display_map
11242 .clip_point(target, Bias::Left)
11243 .to_point(&display_map);
11244 let text = buffer
11245 .text_for_range(selection.start..selection.end)
11246 .collect::<String>();
11247 if is_cut {
11248 edits.push(((selection.start..selection.end), String::new()));
11249 }
11250 let insert_anchor = buffer.anchor_before(insert_point);
11251 edits.push(((insert_anchor..insert_anchor), text));
11252 let last_edit_start = insert_anchor.bias_left(buffer);
11253 let last_edit_end = insert_anchor.bias_right(buffer);
11254 self.transact(window, cx, |this, window, cx| {
11255 this.buffer.update(cx, |buffer, cx| {
11256 buffer.edit(edits, None, cx);
11257 });
11258 this.change_selections(Default::default(), window, cx, |s| {
11259 s.select_anchor_ranges([last_edit_start..last_edit_end]);
11260 });
11261 });
11262 }
11263
11264 pub fn clear_selection_drag_state(&mut self) {
11265 self.selection_drag_state = SelectionDragState::None;
11266 }
11267
11268 pub fn duplicate(
11269 &mut self,
11270 upwards: bool,
11271 whole_lines: bool,
11272 window: &mut Window,
11273 cx: &mut Context<Self>,
11274 ) {
11275 self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
11276
11277 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
11278 let buffer = &display_map.buffer_snapshot;
11279 let selections = self.selections.all::<Point>(cx);
11280
11281 let mut edits = Vec::new();
11282 let mut selections_iter = selections.iter().peekable();
11283 while let Some(selection) = selections_iter.next() {
11284 let mut rows = selection.spanned_rows(false, &display_map);
11285 // duplicate line-wise
11286 if whole_lines || selection.start == selection.end {
11287 // Avoid duplicating the same lines twice.
11288 while let Some(next_selection) = selections_iter.peek() {
11289 let next_rows = next_selection.spanned_rows(false, &display_map);
11290 if next_rows.start < rows.end {
11291 rows.end = next_rows.end;
11292 selections_iter.next().unwrap();
11293 } else {
11294 break;
11295 }
11296 }
11297
11298 // Copy the text from the selected row region and splice it either at the start
11299 // or end of the region.
11300 let start = Point::new(rows.start.0, 0);
11301 let end = Point::new(
11302 rows.end.previous_row().0,
11303 buffer.line_len(rows.end.previous_row()),
11304 );
11305 let text = buffer
11306 .text_for_range(start..end)
11307 .chain(Some("\n"))
11308 .collect::<String>();
11309 let insert_location = if upwards {
11310 Point::new(rows.end.0, 0)
11311 } else {
11312 start
11313 };
11314 edits.push((insert_location..insert_location, text));
11315 } else {
11316 // duplicate character-wise
11317 let start = selection.start;
11318 let end = selection.end;
11319 let text = buffer.text_for_range(start..end).collect::<String>();
11320 edits.push((selection.end..selection.end, text));
11321 }
11322 }
11323
11324 self.transact(window, cx, |this, _, cx| {
11325 this.buffer.update(cx, |buffer, cx| {
11326 buffer.edit(edits, None, cx);
11327 });
11328
11329 this.request_autoscroll(Autoscroll::fit(), cx);
11330 });
11331 }
11332
11333 pub fn duplicate_line_up(
11334 &mut self,
11335 _: &DuplicateLineUp,
11336 window: &mut Window,
11337 cx: &mut Context<Self>,
11338 ) {
11339 self.duplicate(true, true, window, cx);
11340 }
11341
11342 pub fn duplicate_line_down(
11343 &mut self,
11344 _: &DuplicateLineDown,
11345 window: &mut Window,
11346 cx: &mut Context<Self>,
11347 ) {
11348 self.duplicate(false, true, window, cx);
11349 }
11350
11351 pub fn duplicate_selection(
11352 &mut self,
11353 _: &DuplicateSelection,
11354 window: &mut Window,
11355 cx: &mut Context<Self>,
11356 ) {
11357 self.duplicate(false, false, window, cx);
11358 }
11359
11360 pub fn move_line_up(&mut self, _: &MoveLineUp, window: &mut Window, cx: &mut Context<Self>) {
11361 self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
11362 if self.mode.is_single_line() {
11363 cx.propagate();
11364 return;
11365 }
11366
11367 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
11368 let buffer = self.buffer.read(cx).snapshot(cx);
11369
11370 let mut edits = Vec::new();
11371 let mut unfold_ranges = Vec::new();
11372 let mut refold_creases = Vec::new();
11373
11374 let selections = self.selections.all::<Point>(cx);
11375 let mut selections = selections.iter().peekable();
11376 let mut contiguous_row_selections = Vec::new();
11377 let mut new_selections = Vec::new();
11378
11379 while let Some(selection) = selections.next() {
11380 // Find all the selections that span a contiguous row range
11381 let (start_row, end_row) = consume_contiguous_rows(
11382 &mut contiguous_row_selections,
11383 selection,
11384 &display_map,
11385 &mut selections,
11386 );
11387
11388 // Move the text spanned by the row range to be before the line preceding the row range
11389 if start_row.0 > 0 {
11390 let range_to_move = Point::new(
11391 start_row.previous_row().0,
11392 buffer.line_len(start_row.previous_row()),
11393 )
11394 ..Point::new(
11395 end_row.previous_row().0,
11396 buffer.line_len(end_row.previous_row()),
11397 );
11398 let insertion_point = display_map
11399 .prev_line_boundary(Point::new(start_row.previous_row().0, 0))
11400 .0;
11401
11402 // Don't move lines across excerpts
11403 if buffer
11404 .excerpt_containing(insertion_point..range_to_move.end)
11405 .is_some()
11406 {
11407 let text = buffer
11408 .text_for_range(range_to_move.clone())
11409 .flat_map(|s| s.chars())
11410 .skip(1)
11411 .chain(['\n'])
11412 .collect::<String>();
11413
11414 edits.push((
11415 buffer.anchor_after(range_to_move.start)
11416 ..buffer.anchor_before(range_to_move.end),
11417 String::new(),
11418 ));
11419 let insertion_anchor = buffer.anchor_after(insertion_point);
11420 edits.push((insertion_anchor..insertion_anchor, text));
11421
11422 let row_delta = range_to_move.start.row - insertion_point.row + 1;
11423
11424 // Move selections up
11425 new_selections.extend(contiguous_row_selections.drain(..).map(
11426 |mut selection| {
11427 selection.start.row -= row_delta;
11428 selection.end.row -= row_delta;
11429 selection
11430 },
11431 ));
11432
11433 // Move folds up
11434 unfold_ranges.push(range_to_move.clone());
11435 for fold in display_map.folds_in_range(
11436 buffer.anchor_before(range_to_move.start)
11437 ..buffer.anchor_after(range_to_move.end),
11438 ) {
11439 let mut start = fold.range.start.to_point(&buffer);
11440 let mut end = fold.range.end.to_point(&buffer);
11441 start.row -= row_delta;
11442 end.row -= row_delta;
11443 refold_creases.push(Crease::simple(start..end, fold.placeholder.clone()));
11444 }
11445 }
11446 }
11447
11448 // If we didn't move line(s), preserve the existing selections
11449 new_selections.append(&mut contiguous_row_selections);
11450 }
11451
11452 self.transact(window, cx, |this, window, cx| {
11453 this.unfold_ranges(&unfold_ranges, true, true, cx);
11454 this.buffer.update(cx, |buffer, cx| {
11455 for (range, text) in edits {
11456 buffer.edit([(range, text)], None, cx);
11457 }
11458 });
11459 this.fold_creases(refold_creases, true, window, cx);
11460 this.change_selections(Default::default(), window, cx, |s| {
11461 s.select(new_selections);
11462 })
11463 });
11464 }
11465
11466 pub fn move_line_down(
11467 &mut self,
11468 _: &MoveLineDown,
11469 window: &mut Window,
11470 cx: &mut Context<Self>,
11471 ) {
11472 self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
11473 if self.mode.is_single_line() {
11474 cx.propagate();
11475 return;
11476 }
11477
11478 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
11479 let buffer = self.buffer.read(cx).snapshot(cx);
11480
11481 let mut edits = Vec::new();
11482 let mut unfold_ranges = Vec::new();
11483 let mut refold_creases = Vec::new();
11484
11485 let selections = self.selections.all::<Point>(cx);
11486 let mut selections = selections.iter().peekable();
11487 let mut contiguous_row_selections = Vec::new();
11488 let mut new_selections = Vec::new();
11489
11490 while let Some(selection) = selections.next() {
11491 // Find all the selections that span a contiguous row range
11492 let (start_row, end_row) = consume_contiguous_rows(
11493 &mut contiguous_row_selections,
11494 selection,
11495 &display_map,
11496 &mut selections,
11497 );
11498
11499 // Move the text spanned by the row range to be after the last line of the row range
11500 if end_row.0 <= buffer.max_point().row {
11501 let range_to_move =
11502 MultiBufferPoint::new(start_row.0, 0)..MultiBufferPoint::new(end_row.0, 0);
11503 let insertion_point = display_map
11504 .next_line_boundary(MultiBufferPoint::new(end_row.0, 0))
11505 .0;
11506
11507 // Don't move lines across excerpt boundaries
11508 if buffer
11509 .excerpt_containing(range_to_move.start..insertion_point)
11510 .is_some()
11511 {
11512 let mut text = String::from("\n");
11513 text.extend(buffer.text_for_range(range_to_move.clone()));
11514 text.pop(); // Drop trailing newline
11515 edits.push((
11516 buffer.anchor_after(range_to_move.start)
11517 ..buffer.anchor_before(range_to_move.end),
11518 String::new(),
11519 ));
11520 let insertion_anchor = buffer.anchor_after(insertion_point);
11521 edits.push((insertion_anchor..insertion_anchor, text));
11522
11523 let row_delta = insertion_point.row - range_to_move.end.row + 1;
11524
11525 // Move selections down
11526 new_selections.extend(contiguous_row_selections.drain(..).map(
11527 |mut selection| {
11528 selection.start.row += row_delta;
11529 selection.end.row += row_delta;
11530 selection
11531 },
11532 ));
11533
11534 // Move folds down
11535 unfold_ranges.push(range_to_move.clone());
11536 for fold in display_map.folds_in_range(
11537 buffer.anchor_before(range_to_move.start)
11538 ..buffer.anchor_after(range_to_move.end),
11539 ) {
11540 let mut start = fold.range.start.to_point(&buffer);
11541 let mut end = fold.range.end.to_point(&buffer);
11542 start.row += row_delta;
11543 end.row += row_delta;
11544 refold_creases.push(Crease::simple(start..end, fold.placeholder.clone()));
11545 }
11546 }
11547 }
11548
11549 // If we didn't move line(s), preserve the existing selections
11550 new_selections.append(&mut contiguous_row_selections);
11551 }
11552
11553 self.transact(window, cx, |this, window, cx| {
11554 this.unfold_ranges(&unfold_ranges, true, true, cx);
11555 this.buffer.update(cx, |buffer, cx| {
11556 for (range, text) in edits {
11557 buffer.edit([(range, text)], None, cx);
11558 }
11559 });
11560 this.fold_creases(refold_creases, true, window, cx);
11561 this.change_selections(Default::default(), window, cx, |s| s.select(new_selections));
11562 });
11563 }
11564
11565 pub fn transpose(&mut self, _: &Transpose, window: &mut Window, cx: &mut Context<Self>) {
11566 self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
11567 let text_layout_details = &self.text_layout_details(window);
11568 self.transact(window, cx, |this, window, cx| {
11569 let edits = this.change_selections(Default::default(), window, cx, |s| {
11570 let mut edits: Vec<(Range<usize>, String)> = Default::default();
11571 s.move_with(|display_map, selection| {
11572 if !selection.is_empty() {
11573 return;
11574 }
11575
11576 let mut head = selection.head();
11577 let mut transpose_offset = head.to_offset(display_map, Bias::Right);
11578 if head.column() == display_map.line_len(head.row()) {
11579 transpose_offset = display_map
11580 .buffer_snapshot
11581 .clip_offset(transpose_offset.saturating_sub(1), Bias::Left);
11582 }
11583
11584 if transpose_offset == 0 {
11585 return;
11586 }
11587
11588 *head.column_mut() += 1;
11589 head = display_map.clip_point(head, Bias::Right);
11590 let goal = SelectionGoal::HorizontalPosition(
11591 display_map
11592 .x_for_display_point(head, text_layout_details)
11593 .into(),
11594 );
11595 selection.collapse_to(head, goal);
11596
11597 let transpose_start = display_map
11598 .buffer_snapshot
11599 .clip_offset(transpose_offset.saturating_sub(1), Bias::Left);
11600 if edits.last().map_or(true, |e| e.0.end <= transpose_start) {
11601 let transpose_end = display_map
11602 .buffer_snapshot
11603 .clip_offset(transpose_offset + 1, Bias::Right);
11604 if let Some(ch) =
11605 display_map.buffer_snapshot.chars_at(transpose_start).next()
11606 {
11607 edits.push((transpose_start..transpose_offset, String::new()));
11608 edits.push((transpose_end..transpose_end, ch.to_string()));
11609 }
11610 }
11611 });
11612 edits
11613 });
11614 this.buffer
11615 .update(cx, |buffer, cx| buffer.edit(edits, None, cx));
11616 let selections = this.selections.all::<usize>(cx);
11617 this.change_selections(Default::default(), window, cx, |s| {
11618 s.select(selections);
11619 });
11620 });
11621 }
11622
11623 pub fn rewrap(&mut self, _: &Rewrap, _: &mut Window, cx: &mut Context<Self>) {
11624 self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
11625 if self.mode.is_single_line() {
11626 cx.propagate();
11627 return;
11628 }
11629
11630 self.rewrap_impl(RewrapOptions::default(), cx)
11631 }
11632
11633 pub fn rewrap_impl(&mut self, options: RewrapOptions, cx: &mut Context<Self>) {
11634 let buffer = self.buffer.read(cx).snapshot(cx);
11635 let selections = self.selections.all::<Point>(cx);
11636
11637 // Split selections to respect paragraph, indent, and comment prefix boundaries.
11638 let wrap_ranges = selections.into_iter().flat_map(|selection| {
11639 let mut non_blank_rows_iter = (selection.start.row..=selection.end.row)
11640 .filter(|row| !buffer.is_line_blank(MultiBufferRow(*row)))
11641 .peekable();
11642
11643 let first_row = if let Some(&row) = non_blank_rows_iter.peek() {
11644 row
11645 } else {
11646 return Vec::new();
11647 };
11648
11649 let language_settings = buffer.language_settings_at(selection.head(), cx);
11650 let language_scope = buffer.language_scope_at(selection.head());
11651
11652 let indent_and_prefix_for_row =
11653 |row: u32| -> (IndentSize, Option<String>, Option<String>) {
11654 let indent = buffer.indent_size_for_line(MultiBufferRow(row));
11655 let (comment_prefix, rewrap_prefix) =
11656 if let Some(language_scope) = &language_scope {
11657 let indent_end = Point::new(row, indent.len);
11658 let comment_prefix = language_scope
11659 .line_comment_prefixes()
11660 .iter()
11661 .find(|prefix| buffer.contains_str_at(indent_end, prefix))
11662 .map(|prefix| prefix.to_string());
11663 let line_end = Point::new(row, buffer.line_len(MultiBufferRow(row)));
11664 let line_text_after_indent = buffer
11665 .text_for_range(indent_end..line_end)
11666 .collect::<String>();
11667 let rewrap_prefix = language_scope
11668 .rewrap_prefixes()
11669 .iter()
11670 .find_map(|prefix_regex| {
11671 prefix_regex.find(&line_text_after_indent).map(|mat| {
11672 if mat.start() == 0 {
11673 Some(mat.as_str().to_string())
11674 } else {
11675 None
11676 }
11677 })
11678 })
11679 .flatten();
11680 (comment_prefix, rewrap_prefix)
11681 } else {
11682 (None, None)
11683 };
11684 (indent, comment_prefix, rewrap_prefix)
11685 };
11686
11687 let mut ranges = Vec::new();
11688 let from_empty_selection = selection.is_empty();
11689
11690 let mut current_range_start = first_row;
11691 let mut prev_row = first_row;
11692 let (
11693 mut current_range_indent,
11694 mut current_range_comment_prefix,
11695 mut current_range_rewrap_prefix,
11696 ) = indent_and_prefix_for_row(first_row);
11697
11698 for row in non_blank_rows_iter.skip(1) {
11699 let has_paragraph_break = row > prev_row + 1;
11700
11701 let (row_indent, row_comment_prefix, row_rewrap_prefix) =
11702 indent_and_prefix_for_row(row);
11703
11704 let has_indent_change = row_indent != current_range_indent;
11705 let has_comment_change = row_comment_prefix != current_range_comment_prefix;
11706
11707 let has_boundary_change = has_comment_change
11708 || row_rewrap_prefix.is_some()
11709 || (has_indent_change && current_range_comment_prefix.is_some());
11710
11711 if has_paragraph_break || has_boundary_change {
11712 ranges.push((
11713 language_settings.clone(),
11714 Point::new(current_range_start, 0)
11715 ..Point::new(prev_row, buffer.line_len(MultiBufferRow(prev_row))),
11716 current_range_indent,
11717 current_range_comment_prefix.clone(),
11718 current_range_rewrap_prefix.clone(),
11719 from_empty_selection,
11720 ));
11721 current_range_start = row;
11722 current_range_indent = row_indent;
11723 current_range_comment_prefix = row_comment_prefix;
11724 current_range_rewrap_prefix = row_rewrap_prefix;
11725 }
11726 prev_row = row;
11727 }
11728
11729 ranges.push((
11730 language_settings.clone(),
11731 Point::new(current_range_start, 0)
11732 ..Point::new(prev_row, buffer.line_len(MultiBufferRow(prev_row))),
11733 current_range_indent,
11734 current_range_comment_prefix,
11735 current_range_rewrap_prefix,
11736 from_empty_selection,
11737 ));
11738
11739 ranges
11740 });
11741
11742 let mut edits = Vec::new();
11743 let mut rewrapped_row_ranges = Vec::<RangeInclusive<u32>>::new();
11744
11745 for (
11746 language_settings,
11747 wrap_range,
11748 indent_size,
11749 comment_prefix,
11750 rewrap_prefix,
11751 from_empty_selection,
11752 ) in wrap_ranges
11753 {
11754 let mut start_row = wrap_range.start.row;
11755 let mut end_row = wrap_range.end.row;
11756
11757 // Skip selections that overlap with a range that has already been rewrapped.
11758 let selection_range = start_row..end_row;
11759 if rewrapped_row_ranges
11760 .iter()
11761 .any(|range| range.overlaps(&selection_range))
11762 {
11763 continue;
11764 }
11765
11766 let tab_size = language_settings.tab_size;
11767
11768 let indent_prefix = indent_size.chars().collect::<String>();
11769 let mut line_prefix = indent_prefix.clone();
11770 let mut inside_comment = false;
11771 if let Some(prefix) = &comment_prefix {
11772 line_prefix.push_str(prefix);
11773 inside_comment = true;
11774 }
11775 if let Some(prefix) = &rewrap_prefix {
11776 line_prefix.push_str(prefix);
11777 }
11778
11779 let allow_rewrap_based_on_language = match language_settings.allow_rewrap {
11780 RewrapBehavior::InComments => inside_comment,
11781 RewrapBehavior::InSelections => !wrap_range.is_empty(),
11782 RewrapBehavior::Anywhere => true,
11783 };
11784
11785 let should_rewrap = options.override_language_settings
11786 || allow_rewrap_based_on_language
11787 || self.hard_wrap.is_some();
11788 if !should_rewrap {
11789 continue;
11790 }
11791
11792 if from_empty_selection {
11793 'expand_upwards: while start_row > 0 {
11794 let prev_row = start_row - 1;
11795 if buffer.contains_str_at(Point::new(prev_row, 0), &line_prefix)
11796 && buffer.line_len(MultiBufferRow(prev_row)) as usize > line_prefix.len()
11797 && !buffer.is_line_blank(MultiBufferRow(prev_row))
11798 {
11799 start_row = prev_row;
11800 } else {
11801 break 'expand_upwards;
11802 }
11803 }
11804
11805 'expand_downwards: while end_row < buffer.max_point().row {
11806 let next_row = end_row + 1;
11807 if buffer.contains_str_at(Point::new(next_row, 0), &line_prefix)
11808 && buffer.line_len(MultiBufferRow(next_row)) as usize > line_prefix.len()
11809 && !buffer.is_line_blank(MultiBufferRow(next_row))
11810 {
11811 end_row = next_row;
11812 } else {
11813 break 'expand_downwards;
11814 }
11815 }
11816 }
11817
11818 let start = Point::new(start_row, 0);
11819 let start_offset = start.to_offset(&buffer);
11820 let end = Point::new(end_row, buffer.line_len(MultiBufferRow(end_row)));
11821 let selection_text = buffer.text_for_range(start..end).collect::<String>();
11822 let Some(lines_without_prefixes) = selection_text
11823 .lines()
11824 .enumerate()
11825 .map(|(ix, line)| {
11826 let line_trimmed = line.trim_start();
11827 if rewrap_prefix.is_some() && ix > 0 {
11828 Ok(line_trimmed)
11829 } else {
11830 line_trimmed
11831 .strip_prefix(&line_prefix.trim_start())
11832 .with_context(|| {
11833 format!("line did not start with prefix {line_prefix:?}: {line:?}")
11834 })
11835 }
11836 })
11837 .collect::<Result<Vec<_>, _>>()
11838 .log_err()
11839 else {
11840 continue;
11841 };
11842
11843 let wrap_column = self.hard_wrap.unwrap_or_else(|| {
11844 buffer
11845 .language_settings_at(Point::new(start_row, 0), cx)
11846 .preferred_line_length as usize
11847 });
11848
11849 let subsequent_lines_prefix = if let Some(rewrap_prefix_str) = &rewrap_prefix {
11850 format!("{}{}", indent_prefix, " ".repeat(rewrap_prefix_str.len()))
11851 } else {
11852 line_prefix.clone()
11853 };
11854
11855 let wrapped_text = wrap_with_prefix(
11856 line_prefix,
11857 subsequent_lines_prefix,
11858 lines_without_prefixes.join("\n"),
11859 wrap_column,
11860 tab_size,
11861 options.preserve_existing_whitespace,
11862 );
11863
11864 // TODO: should always use char-based diff while still supporting cursor behavior that
11865 // matches vim.
11866 let mut diff_options = DiffOptions::default();
11867 if options.override_language_settings {
11868 diff_options.max_word_diff_len = 0;
11869 diff_options.max_word_diff_line_count = 0;
11870 } else {
11871 diff_options.max_word_diff_len = usize::MAX;
11872 diff_options.max_word_diff_line_count = usize::MAX;
11873 }
11874
11875 for (old_range, new_text) in
11876 text_diff_with_options(&selection_text, &wrapped_text, diff_options)
11877 {
11878 let edit_start = buffer.anchor_after(start_offset + old_range.start);
11879 let edit_end = buffer.anchor_after(start_offset + old_range.end);
11880 edits.push((edit_start..edit_end, new_text));
11881 }
11882
11883 rewrapped_row_ranges.push(start_row..=end_row);
11884 }
11885
11886 self.buffer
11887 .update(cx, |buffer, cx| buffer.edit(edits, None, cx));
11888 }
11889
11890 pub fn cut_common(&mut self, window: &mut Window, cx: &mut Context<Self>) -> ClipboardItem {
11891 let mut text = String::new();
11892 let buffer = self.buffer.read(cx).snapshot(cx);
11893 let mut selections = self.selections.all::<Point>(cx);
11894 let mut clipboard_selections = Vec::with_capacity(selections.len());
11895 {
11896 let max_point = buffer.max_point();
11897 let mut is_first = true;
11898 for selection in &mut selections {
11899 let is_entire_line = selection.is_empty() || self.selections.line_mode;
11900 if is_entire_line {
11901 selection.start = Point::new(selection.start.row, 0);
11902 if !selection.is_empty() && selection.end.column == 0 {
11903 selection.end = cmp::min(max_point, selection.end);
11904 } else {
11905 selection.end = cmp::min(max_point, Point::new(selection.end.row + 1, 0));
11906 }
11907 selection.goal = SelectionGoal::None;
11908 }
11909 if is_first {
11910 is_first = false;
11911 } else {
11912 text += "\n";
11913 }
11914 let mut len = 0;
11915 for chunk in buffer.text_for_range(selection.start..selection.end) {
11916 text.push_str(chunk);
11917 len += chunk.len();
11918 }
11919 clipboard_selections.push(ClipboardSelection {
11920 len,
11921 is_entire_line,
11922 first_line_indent: buffer
11923 .indent_size_for_line(MultiBufferRow(selection.start.row))
11924 .len,
11925 });
11926 }
11927 }
11928
11929 self.transact(window, cx, |this, window, cx| {
11930 this.change_selections(Default::default(), window, cx, |s| {
11931 s.select(selections);
11932 });
11933 this.insert("", window, cx);
11934 });
11935 ClipboardItem::new_string_with_json_metadata(text, clipboard_selections)
11936 }
11937
11938 pub fn cut(&mut self, _: &Cut, window: &mut Window, cx: &mut Context<Self>) {
11939 self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
11940 let item = self.cut_common(window, cx);
11941 cx.write_to_clipboard(item);
11942 }
11943
11944 pub fn kill_ring_cut(&mut self, _: &KillRingCut, window: &mut Window, cx: &mut Context<Self>) {
11945 self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
11946 self.change_selections(SelectionEffects::no_scroll(), window, cx, |s| {
11947 s.move_with(|snapshot, sel| {
11948 if sel.is_empty() {
11949 sel.end = DisplayPoint::new(sel.end.row(), snapshot.line_len(sel.end.row()))
11950 }
11951 });
11952 });
11953 let item = self.cut_common(window, cx);
11954 cx.set_global(KillRing(item))
11955 }
11956
11957 pub fn kill_ring_yank(
11958 &mut self,
11959 _: &KillRingYank,
11960 window: &mut Window,
11961 cx: &mut Context<Self>,
11962 ) {
11963 self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
11964 let (text, metadata) = if let Some(KillRing(item)) = cx.try_global() {
11965 if let Some(ClipboardEntry::String(kill_ring)) = item.entries().first() {
11966 (kill_ring.text().to_string(), kill_ring.metadata_json())
11967 } else {
11968 return;
11969 }
11970 } else {
11971 return;
11972 };
11973 self.do_paste(&text, metadata, false, window, cx);
11974 }
11975
11976 pub fn copy_and_trim(&mut self, _: &CopyAndTrim, _: &mut Window, cx: &mut Context<Self>) {
11977 self.do_copy(true, cx);
11978 }
11979
11980 pub fn copy(&mut self, _: &Copy, _: &mut Window, cx: &mut Context<Self>) {
11981 self.do_copy(false, cx);
11982 }
11983
11984 fn do_copy(&self, strip_leading_indents: bool, cx: &mut Context<Self>) {
11985 let selections = self.selections.all::<Point>(cx);
11986 let buffer = self.buffer.read(cx).read(cx);
11987 let mut text = String::new();
11988
11989 let mut clipboard_selections = Vec::with_capacity(selections.len());
11990 {
11991 let max_point = buffer.max_point();
11992 let mut is_first = true;
11993 for selection in &selections {
11994 let mut start = selection.start;
11995 let mut end = selection.end;
11996 let is_entire_line = selection.is_empty() || self.selections.line_mode;
11997 if is_entire_line {
11998 start = Point::new(start.row, 0);
11999 end = cmp::min(max_point, Point::new(end.row + 1, 0));
12000 }
12001
12002 let mut trimmed_selections = Vec::new();
12003 if strip_leading_indents && end.row.saturating_sub(start.row) > 0 {
12004 let row = MultiBufferRow(start.row);
12005 let first_indent = buffer.indent_size_for_line(row);
12006 if first_indent.len == 0 || start.column > first_indent.len {
12007 trimmed_selections.push(start..end);
12008 } else {
12009 trimmed_selections.push(
12010 Point::new(row.0, first_indent.len)
12011 ..Point::new(row.0, buffer.line_len(row)),
12012 );
12013 for row in start.row + 1..=end.row {
12014 let mut line_len = buffer.line_len(MultiBufferRow(row));
12015 if row == end.row {
12016 line_len = end.column;
12017 }
12018 if line_len == 0 {
12019 trimmed_selections
12020 .push(Point::new(row, 0)..Point::new(row, line_len));
12021 continue;
12022 }
12023 let row_indent_size = buffer.indent_size_for_line(MultiBufferRow(row));
12024 if row_indent_size.len >= first_indent.len {
12025 trimmed_selections.push(
12026 Point::new(row, first_indent.len)..Point::new(row, line_len),
12027 );
12028 } else {
12029 trimmed_selections.clear();
12030 trimmed_selections.push(start..end);
12031 break;
12032 }
12033 }
12034 }
12035 } else {
12036 trimmed_selections.push(start..end);
12037 }
12038
12039 for trimmed_range in trimmed_selections {
12040 if is_first {
12041 is_first = false;
12042 } else {
12043 text += "\n";
12044 }
12045 let mut len = 0;
12046 for chunk in buffer.text_for_range(trimmed_range.start..trimmed_range.end) {
12047 text.push_str(chunk);
12048 len += chunk.len();
12049 }
12050 clipboard_selections.push(ClipboardSelection {
12051 len,
12052 is_entire_line,
12053 first_line_indent: buffer
12054 .indent_size_for_line(MultiBufferRow(trimmed_range.start.row))
12055 .len,
12056 });
12057 }
12058 }
12059 }
12060
12061 cx.write_to_clipboard(ClipboardItem::new_string_with_json_metadata(
12062 text,
12063 clipboard_selections,
12064 ));
12065 }
12066
12067 pub fn do_paste(
12068 &mut self,
12069 text: &String,
12070 clipboard_selections: Option<Vec<ClipboardSelection>>,
12071 handle_entire_lines: bool,
12072 window: &mut Window,
12073 cx: &mut Context<Self>,
12074 ) {
12075 if self.read_only(cx) {
12076 return;
12077 }
12078
12079 let clipboard_text = Cow::Borrowed(text);
12080
12081 self.transact(window, cx, |this, window, cx| {
12082 if let Some(mut clipboard_selections) = clipboard_selections {
12083 let old_selections = this.selections.all::<usize>(cx);
12084 let all_selections_were_entire_line =
12085 clipboard_selections.iter().all(|s| s.is_entire_line);
12086 let first_selection_indent_column =
12087 clipboard_selections.first().map(|s| s.first_line_indent);
12088 if clipboard_selections.len() != old_selections.len() {
12089 clipboard_selections.drain(..);
12090 }
12091 let cursor_offset = this.selections.last::<usize>(cx).head();
12092 let mut auto_indent_on_paste = true;
12093
12094 this.buffer.update(cx, |buffer, cx| {
12095 let snapshot = buffer.read(cx);
12096 auto_indent_on_paste = snapshot
12097 .language_settings_at(cursor_offset, cx)
12098 .auto_indent_on_paste;
12099
12100 let mut start_offset = 0;
12101 let mut edits = Vec::new();
12102 let mut original_indent_columns = Vec::new();
12103 for (ix, selection) in old_selections.iter().enumerate() {
12104 let to_insert;
12105 let entire_line;
12106 let original_indent_column;
12107 if let Some(clipboard_selection) = clipboard_selections.get(ix) {
12108 let end_offset = start_offset + clipboard_selection.len;
12109 to_insert = &clipboard_text[start_offset..end_offset];
12110 entire_line = clipboard_selection.is_entire_line;
12111 start_offset = end_offset + 1;
12112 original_indent_column = Some(clipboard_selection.first_line_indent);
12113 } else {
12114 to_insert = clipboard_text.as_str();
12115 entire_line = all_selections_were_entire_line;
12116 original_indent_column = first_selection_indent_column
12117 }
12118
12119 // If the corresponding selection was empty when this slice of the
12120 // clipboard text was written, then the entire line containing the
12121 // selection was copied. If this selection is also currently empty,
12122 // then paste the line before the current line of the buffer.
12123 let range = if selection.is_empty() && handle_entire_lines && entire_line {
12124 let column = selection.start.to_point(&snapshot).column as usize;
12125 let line_start = selection.start - column;
12126 line_start..line_start
12127 } else {
12128 selection.range()
12129 };
12130
12131 edits.push((range, to_insert));
12132 original_indent_columns.push(original_indent_column);
12133 }
12134 drop(snapshot);
12135
12136 buffer.edit(
12137 edits,
12138 if auto_indent_on_paste {
12139 Some(AutoindentMode::Block {
12140 original_indent_columns,
12141 })
12142 } else {
12143 None
12144 },
12145 cx,
12146 );
12147 });
12148
12149 let selections = this.selections.all::<usize>(cx);
12150 this.change_selections(Default::default(), window, cx, |s| s.select(selections));
12151 } else {
12152 this.insert(&clipboard_text, window, cx);
12153 }
12154 });
12155 }
12156
12157 pub fn paste(&mut self, _: &Paste, window: &mut Window, cx: &mut Context<Self>) {
12158 self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
12159 if let Some(item) = cx.read_from_clipboard() {
12160 let entries = item.entries();
12161
12162 match entries.first() {
12163 // For now, we only support applying metadata if there's one string. In the future, we can incorporate all the selections
12164 // of all the pasted entries.
12165 Some(ClipboardEntry::String(clipboard_string)) if entries.len() == 1 => self
12166 .do_paste(
12167 clipboard_string.text(),
12168 clipboard_string.metadata_json::<Vec<ClipboardSelection>>(),
12169 true,
12170 window,
12171 cx,
12172 ),
12173 _ => self.do_paste(&item.text().unwrap_or_default(), None, true, window, cx),
12174 }
12175 }
12176 }
12177
12178 pub fn undo(&mut self, _: &Undo, window: &mut Window, cx: &mut Context<Self>) {
12179 if self.read_only(cx) {
12180 return;
12181 }
12182
12183 self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
12184
12185 if let Some(transaction_id) = self.buffer.update(cx, |buffer, cx| buffer.undo(cx)) {
12186 if let Some((selections, _)) =
12187 self.selection_history.transaction(transaction_id).cloned()
12188 {
12189 self.change_selections(SelectionEffects::no_scroll(), window, cx, |s| {
12190 s.select_anchors(selections.to_vec());
12191 });
12192 } else {
12193 log::error!(
12194 "No entry in selection_history found for undo. \
12195 This may correspond to a bug where undo does not update the selection. \
12196 If this is occurring, please add details to \
12197 https://github.com/zed-industries/zed/issues/22692"
12198 );
12199 }
12200 self.request_autoscroll(Autoscroll::fit(), cx);
12201 self.unmark_text(window, cx);
12202 self.refresh_inline_completion(true, false, window, cx);
12203 cx.emit(EditorEvent::Edited { transaction_id });
12204 cx.emit(EditorEvent::TransactionUndone { transaction_id });
12205 }
12206 }
12207
12208 pub fn redo(&mut self, _: &Redo, window: &mut Window, cx: &mut Context<Self>) {
12209 if self.read_only(cx) {
12210 return;
12211 }
12212
12213 self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
12214
12215 if let Some(transaction_id) = self.buffer.update(cx, |buffer, cx| buffer.redo(cx)) {
12216 if let Some((_, Some(selections))) =
12217 self.selection_history.transaction(transaction_id).cloned()
12218 {
12219 self.change_selections(SelectionEffects::no_scroll(), window, cx, |s| {
12220 s.select_anchors(selections.to_vec());
12221 });
12222 } else {
12223 log::error!(
12224 "No entry in selection_history found for redo. \
12225 This may correspond to a bug where undo does not update the selection. \
12226 If this is occurring, please add details to \
12227 https://github.com/zed-industries/zed/issues/22692"
12228 );
12229 }
12230 self.request_autoscroll(Autoscroll::fit(), cx);
12231 self.unmark_text(window, cx);
12232 self.refresh_inline_completion(true, false, window, cx);
12233 cx.emit(EditorEvent::Edited { transaction_id });
12234 }
12235 }
12236
12237 pub fn finalize_last_transaction(&mut self, cx: &mut Context<Self>) {
12238 self.buffer
12239 .update(cx, |buffer, cx| buffer.finalize_last_transaction(cx));
12240 }
12241
12242 pub fn group_until_transaction(&mut self, tx_id: TransactionId, cx: &mut Context<Self>) {
12243 self.buffer
12244 .update(cx, |buffer, cx| buffer.group_until_transaction(tx_id, cx));
12245 }
12246
12247 pub fn move_left(&mut self, _: &MoveLeft, window: &mut Window, cx: &mut Context<Self>) {
12248 self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
12249 self.change_selections(Default::default(), window, cx, |s| {
12250 s.move_with(|map, selection| {
12251 let cursor = if selection.is_empty() {
12252 movement::left(map, selection.start)
12253 } else {
12254 selection.start
12255 };
12256 selection.collapse_to(cursor, SelectionGoal::None);
12257 });
12258 })
12259 }
12260
12261 pub fn select_left(&mut self, _: &SelectLeft, window: &mut Window, cx: &mut Context<Self>) {
12262 self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
12263 self.change_selections(Default::default(), window, cx, |s| {
12264 s.move_heads_with(|map, head, _| (movement::left(map, head), SelectionGoal::None));
12265 })
12266 }
12267
12268 pub fn move_right(&mut self, _: &MoveRight, window: &mut Window, cx: &mut Context<Self>) {
12269 self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
12270 self.change_selections(Default::default(), window, cx, |s| {
12271 s.move_with(|map, selection| {
12272 let cursor = if selection.is_empty() {
12273 movement::right(map, selection.end)
12274 } else {
12275 selection.end
12276 };
12277 selection.collapse_to(cursor, SelectionGoal::None)
12278 });
12279 })
12280 }
12281
12282 pub fn select_right(&mut self, _: &SelectRight, window: &mut Window, cx: &mut Context<Self>) {
12283 self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
12284 self.change_selections(Default::default(), window, cx, |s| {
12285 s.move_heads_with(|map, head, _| (movement::right(map, head), SelectionGoal::None));
12286 })
12287 }
12288
12289 pub fn move_up(&mut self, _: &MoveUp, window: &mut Window, cx: &mut Context<Self>) {
12290 if self.take_rename(true, window, cx).is_some() {
12291 return;
12292 }
12293
12294 if self.mode.is_single_line() {
12295 cx.propagate();
12296 return;
12297 }
12298
12299 self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
12300
12301 let text_layout_details = &self.text_layout_details(window);
12302 let selection_count = self.selections.count();
12303 let first_selection = self.selections.first_anchor();
12304
12305 self.change_selections(Default::default(), window, cx, |s| {
12306 s.move_with(|map, selection| {
12307 if !selection.is_empty() {
12308 selection.goal = SelectionGoal::None;
12309 }
12310 let (cursor, goal) = movement::up(
12311 map,
12312 selection.start,
12313 selection.goal,
12314 false,
12315 text_layout_details,
12316 );
12317 selection.collapse_to(cursor, goal);
12318 });
12319 });
12320
12321 if selection_count == 1 && first_selection.range() == self.selections.first_anchor().range()
12322 {
12323 cx.propagate();
12324 }
12325 }
12326
12327 pub fn move_up_by_lines(
12328 &mut self,
12329 action: &MoveUpByLines,
12330 window: &mut Window,
12331 cx: &mut Context<Self>,
12332 ) {
12333 if self.take_rename(true, window, cx).is_some() {
12334 return;
12335 }
12336
12337 if self.mode.is_single_line() {
12338 cx.propagate();
12339 return;
12340 }
12341
12342 self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
12343
12344 let text_layout_details = &self.text_layout_details(window);
12345
12346 self.change_selections(Default::default(), window, cx, |s| {
12347 s.move_with(|map, selection| {
12348 if !selection.is_empty() {
12349 selection.goal = SelectionGoal::None;
12350 }
12351 let (cursor, goal) = movement::up_by_rows(
12352 map,
12353 selection.start,
12354 action.lines,
12355 selection.goal,
12356 false,
12357 text_layout_details,
12358 );
12359 selection.collapse_to(cursor, goal);
12360 });
12361 })
12362 }
12363
12364 pub fn move_down_by_lines(
12365 &mut self,
12366 action: &MoveDownByLines,
12367 window: &mut Window,
12368 cx: &mut Context<Self>,
12369 ) {
12370 if self.take_rename(true, window, cx).is_some() {
12371 return;
12372 }
12373
12374 if self.mode.is_single_line() {
12375 cx.propagate();
12376 return;
12377 }
12378
12379 self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
12380
12381 let text_layout_details = &self.text_layout_details(window);
12382
12383 self.change_selections(Default::default(), window, cx, |s| {
12384 s.move_with(|map, selection| {
12385 if !selection.is_empty() {
12386 selection.goal = SelectionGoal::None;
12387 }
12388 let (cursor, goal) = movement::down_by_rows(
12389 map,
12390 selection.start,
12391 action.lines,
12392 selection.goal,
12393 false,
12394 text_layout_details,
12395 );
12396 selection.collapse_to(cursor, goal);
12397 });
12398 })
12399 }
12400
12401 pub fn select_down_by_lines(
12402 &mut self,
12403 action: &SelectDownByLines,
12404 window: &mut Window,
12405 cx: &mut Context<Self>,
12406 ) {
12407 self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
12408 let text_layout_details = &self.text_layout_details(window);
12409 self.change_selections(Default::default(), window, cx, |s| {
12410 s.move_heads_with(|map, head, goal| {
12411 movement::down_by_rows(map, head, action.lines, goal, false, text_layout_details)
12412 })
12413 })
12414 }
12415
12416 pub fn select_up_by_lines(
12417 &mut self,
12418 action: &SelectUpByLines,
12419 window: &mut Window,
12420 cx: &mut Context<Self>,
12421 ) {
12422 self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
12423 let text_layout_details = &self.text_layout_details(window);
12424 self.change_selections(Default::default(), window, cx, |s| {
12425 s.move_heads_with(|map, head, goal| {
12426 movement::up_by_rows(map, head, action.lines, goal, false, text_layout_details)
12427 })
12428 })
12429 }
12430
12431 pub fn select_page_up(
12432 &mut self,
12433 _: &SelectPageUp,
12434 window: &mut Window,
12435 cx: &mut Context<Self>,
12436 ) {
12437 let Some(row_count) = self.visible_row_count() else {
12438 return;
12439 };
12440
12441 self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
12442
12443 let text_layout_details = &self.text_layout_details(window);
12444
12445 self.change_selections(Default::default(), window, cx, |s| {
12446 s.move_heads_with(|map, head, goal| {
12447 movement::up_by_rows(map, head, row_count, goal, false, text_layout_details)
12448 })
12449 })
12450 }
12451
12452 pub fn move_page_up(
12453 &mut self,
12454 action: &MovePageUp,
12455 window: &mut Window,
12456 cx: &mut Context<Self>,
12457 ) {
12458 if self.take_rename(true, window, cx).is_some() {
12459 return;
12460 }
12461
12462 if self
12463 .context_menu
12464 .borrow_mut()
12465 .as_mut()
12466 .map(|menu| menu.select_first(self.completion_provider.as_deref(), window, cx))
12467 .unwrap_or(false)
12468 {
12469 return;
12470 }
12471
12472 if matches!(self.mode, EditorMode::SingleLine { .. }) {
12473 cx.propagate();
12474 return;
12475 }
12476
12477 let Some(row_count) = self.visible_row_count() else {
12478 return;
12479 };
12480
12481 self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
12482
12483 let effects = if action.center_cursor {
12484 SelectionEffects::scroll(Autoscroll::center())
12485 } else {
12486 SelectionEffects::default()
12487 };
12488
12489 let text_layout_details = &self.text_layout_details(window);
12490
12491 self.change_selections(effects, window, cx, |s| {
12492 s.move_with(|map, selection| {
12493 if !selection.is_empty() {
12494 selection.goal = SelectionGoal::None;
12495 }
12496 let (cursor, goal) = movement::up_by_rows(
12497 map,
12498 selection.end,
12499 row_count,
12500 selection.goal,
12501 false,
12502 text_layout_details,
12503 );
12504 selection.collapse_to(cursor, goal);
12505 });
12506 });
12507 }
12508
12509 pub fn select_up(&mut self, _: &SelectUp, window: &mut Window, cx: &mut Context<Self>) {
12510 self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
12511 let text_layout_details = &self.text_layout_details(window);
12512 self.change_selections(Default::default(), window, cx, |s| {
12513 s.move_heads_with(|map, head, goal| {
12514 movement::up(map, head, goal, false, text_layout_details)
12515 })
12516 })
12517 }
12518
12519 pub fn move_down(&mut self, _: &MoveDown, window: &mut Window, cx: &mut Context<Self>) {
12520 self.take_rename(true, window, cx);
12521
12522 if self.mode.is_single_line() {
12523 cx.propagate();
12524 return;
12525 }
12526
12527 self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
12528
12529 let text_layout_details = &self.text_layout_details(window);
12530 let selection_count = self.selections.count();
12531 let first_selection = self.selections.first_anchor();
12532
12533 self.change_selections(Default::default(), window, cx, |s| {
12534 s.move_with(|map, selection| {
12535 if !selection.is_empty() {
12536 selection.goal = SelectionGoal::None;
12537 }
12538 let (cursor, goal) = movement::down(
12539 map,
12540 selection.end,
12541 selection.goal,
12542 false,
12543 text_layout_details,
12544 );
12545 selection.collapse_to(cursor, goal);
12546 });
12547 });
12548
12549 if selection_count == 1 && first_selection.range() == self.selections.first_anchor().range()
12550 {
12551 cx.propagate();
12552 }
12553 }
12554
12555 pub fn select_page_down(
12556 &mut self,
12557 _: &SelectPageDown,
12558 window: &mut Window,
12559 cx: &mut Context<Self>,
12560 ) {
12561 let Some(row_count) = self.visible_row_count() else {
12562 return;
12563 };
12564
12565 self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
12566
12567 let text_layout_details = &self.text_layout_details(window);
12568
12569 self.change_selections(Default::default(), window, cx, |s| {
12570 s.move_heads_with(|map, head, goal| {
12571 movement::down_by_rows(map, head, row_count, goal, false, text_layout_details)
12572 })
12573 })
12574 }
12575
12576 pub fn move_page_down(
12577 &mut self,
12578 action: &MovePageDown,
12579 window: &mut Window,
12580 cx: &mut Context<Self>,
12581 ) {
12582 if self.take_rename(true, window, cx).is_some() {
12583 return;
12584 }
12585
12586 if self
12587 .context_menu
12588 .borrow_mut()
12589 .as_mut()
12590 .map(|menu| menu.select_last(self.completion_provider.as_deref(), window, cx))
12591 .unwrap_or(false)
12592 {
12593 return;
12594 }
12595
12596 if matches!(self.mode, EditorMode::SingleLine { .. }) {
12597 cx.propagate();
12598 return;
12599 }
12600
12601 let Some(row_count) = self.visible_row_count() else {
12602 return;
12603 };
12604
12605 self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
12606
12607 let effects = if action.center_cursor {
12608 SelectionEffects::scroll(Autoscroll::center())
12609 } else {
12610 SelectionEffects::default()
12611 };
12612
12613 let text_layout_details = &self.text_layout_details(window);
12614 self.change_selections(effects, window, cx, |s| {
12615 s.move_with(|map, selection| {
12616 if !selection.is_empty() {
12617 selection.goal = SelectionGoal::None;
12618 }
12619 let (cursor, goal) = movement::down_by_rows(
12620 map,
12621 selection.end,
12622 row_count,
12623 selection.goal,
12624 false,
12625 text_layout_details,
12626 );
12627 selection.collapse_to(cursor, goal);
12628 });
12629 });
12630 }
12631
12632 pub fn select_down(&mut self, _: &SelectDown, window: &mut Window, cx: &mut Context<Self>) {
12633 self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
12634 let text_layout_details = &self.text_layout_details(window);
12635 self.change_selections(Default::default(), window, cx, |s| {
12636 s.move_heads_with(|map, head, goal| {
12637 movement::down(map, head, goal, false, text_layout_details)
12638 })
12639 });
12640 }
12641
12642 pub fn context_menu_first(
12643 &mut self,
12644 _: &ContextMenuFirst,
12645 window: &mut Window,
12646 cx: &mut Context<Self>,
12647 ) {
12648 if let Some(context_menu) = self.context_menu.borrow_mut().as_mut() {
12649 context_menu.select_first(self.completion_provider.as_deref(), window, cx);
12650 }
12651 }
12652
12653 pub fn context_menu_prev(
12654 &mut self,
12655 _: &ContextMenuPrevious,
12656 window: &mut Window,
12657 cx: &mut Context<Self>,
12658 ) {
12659 if let Some(context_menu) = self.context_menu.borrow_mut().as_mut() {
12660 context_menu.select_prev(self.completion_provider.as_deref(), window, cx);
12661 }
12662 }
12663
12664 pub fn context_menu_next(
12665 &mut self,
12666 _: &ContextMenuNext,
12667 window: &mut Window,
12668 cx: &mut Context<Self>,
12669 ) {
12670 if let Some(context_menu) = self.context_menu.borrow_mut().as_mut() {
12671 context_menu.select_next(self.completion_provider.as_deref(), window, cx);
12672 }
12673 }
12674
12675 pub fn context_menu_last(
12676 &mut self,
12677 _: &ContextMenuLast,
12678 window: &mut Window,
12679 cx: &mut Context<Self>,
12680 ) {
12681 if let Some(context_menu) = self.context_menu.borrow_mut().as_mut() {
12682 context_menu.select_last(self.completion_provider.as_deref(), window, cx);
12683 }
12684 }
12685
12686 pub fn signature_help_prev(
12687 &mut self,
12688 _: &SignatureHelpPrevious,
12689 _: &mut Window,
12690 cx: &mut Context<Self>,
12691 ) {
12692 if let Some(popover) = self.signature_help_state.popover_mut() {
12693 if popover.current_signature == 0 {
12694 popover.current_signature = popover.signatures.len() - 1;
12695 } else {
12696 popover.current_signature -= 1;
12697 }
12698 cx.notify();
12699 }
12700 }
12701
12702 pub fn signature_help_next(
12703 &mut self,
12704 _: &SignatureHelpNext,
12705 _: &mut Window,
12706 cx: &mut Context<Self>,
12707 ) {
12708 if let Some(popover) = self.signature_help_state.popover_mut() {
12709 if popover.current_signature + 1 == popover.signatures.len() {
12710 popover.current_signature = 0;
12711 } else {
12712 popover.current_signature += 1;
12713 }
12714 cx.notify();
12715 }
12716 }
12717
12718 pub fn move_to_previous_word_start(
12719 &mut self,
12720 _: &MoveToPreviousWordStart,
12721 window: &mut Window,
12722 cx: &mut Context<Self>,
12723 ) {
12724 self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
12725 self.change_selections(Default::default(), window, cx, |s| {
12726 s.move_cursors_with(|map, head, _| {
12727 (
12728 movement::previous_word_start(map, head),
12729 SelectionGoal::None,
12730 )
12731 });
12732 })
12733 }
12734
12735 pub fn move_to_previous_subword_start(
12736 &mut self,
12737 _: &MoveToPreviousSubwordStart,
12738 window: &mut Window,
12739 cx: &mut Context<Self>,
12740 ) {
12741 self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
12742 self.change_selections(Default::default(), window, cx, |s| {
12743 s.move_cursors_with(|map, head, _| {
12744 (
12745 movement::previous_subword_start(map, head),
12746 SelectionGoal::None,
12747 )
12748 });
12749 })
12750 }
12751
12752 pub fn select_to_previous_word_start(
12753 &mut self,
12754 _: &SelectToPreviousWordStart,
12755 window: &mut Window,
12756 cx: &mut Context<Self>,
12757 ) {
12758 self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
12759 self.change_selections(Default::default(), window, cx, |s| {
12760 s.move_heads_with(|map, head, _| {
12761 (
12762 movement::previous_word_start(map, head),
12763 SelectionGoal::None,
12764 )
12765 });
12766 })
12767 }
12768
12769 pub fn select_to_previous_subword_start(
12770 &mut self,
12771 _: &SelectToPreviousSubwordStart,
12772 window: &mut Window,
12773 cx: &mut Context<Self>,
12774 ) {
12775 self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
12776 self.change_selections(Default::default(), window, cx, |s| {
12777 s.move_heads_with(|map, head, _| {
12778 (
12779 movement::previous_subword_start(map, head),
12780 SelectionGoal::None,
12781 )
12782 });
12783 })
12784 }
12785
12786 pub fn delete_to_previous_word_start(
12787 &mut self,
12788 action: &DeleteToPreviousWordStart,
12789 window: &mut Window,
12790 cx: &mut Context<Self>,
12791 ) {
12792 self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
12793 self.transact(window, cx, |this, window, cx| {
12794 this.select_autoclose_pair(window, cx);
12795 this.change_selections(Default::default(), window, cx, |s| {
12796 s.move_with(|map, selection| {
12797 if selection.is_empty() {
12798 let cursor = if action.ignore_newlines {
12799 movement::previous_word_start(map, selection.head())
12800 } else {
12801 movement::previous_word_start_or_newline(map, selection.head())
12802 };
12803 selection.set_head(cursor, SelectionGoal::None);
12804 }
12805 });
12806 });
12807 this.insert("", window, cx);
12808 });
12809 }
12810
12811 pub fn delete_to_previous_subword_start(
12812 &mut self,
12813 _: &DeleteToPreviousSubwordStart,
12814 window: &mut Window,
12815 cx: &mut Context<Self>,
12816 ) {
12817 self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
12818 self.transact(window, cx, |this, window, cx| {
12819 this.select_autoclose_pair(window, cx);
12820 this.change_selections(Default::default(), window, cx, |s| {
12821 s.move_with(|map, selection| {
12822 if selection.is_empty() {
12823 let cursor = movement::previous_subword_start(map, selection.head());
12824 selection.set_head(cursor, SelectionGoal::None);
12825 }
12826 });
12827 });
12828 this.insert("", window, cx);
12829 });
12830 }
12831
12832 pub fn move_to_next_word_end(
12833 &mut self,
12834 _: &MoveToNextWordEnd,
12835 window: &mut Window,
12836 cx: &mut Context<Self>,
12837 ) {
12838 self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
12839 self.change_selections(Default::default(), window, cx, |s| {
12840 s.move_cursors_with(|map, head, _| {
12841 (movement::next_word_end(map, head), SelectionGoal::None)
12842 });
12843 })
12844 }
12845
12846 pub fn move_to_next_subword_end(
12847 &mut self,
12848 _: &MoveToNextSubwordEnd,
12849 window: &mut Window,
12850 cx: &mut Context<Self>,
12851 ) {
12852 self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
12853 self.change_selections(Default::default(), window, cx, |s| {
12854 s.move_cursors_with(|map, head, _| {
12855 (movement::next_subword_end(map, head), SelectionGoal::None)
12856 });
12857 })
12858 }
12859
12860 pub fn select_to_next_word_end(
12861 &mut self,
12862 _: &SelectToNextWordEnd,
12863 window: &mut Window,
12864 cx: &mut Context<Self>,
12865 ) {
12866 self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
12867 self.change_selections(Default::default(), window, cx, |s| {
12868 s.move_heads_with(|map, head, _| {
12869 (movement::next_word_end(map, head), SelectionGoal::None)
12870 });
12871 })
12872 }
12873
12874 pub fn select_to_next_subword_end(
12875 &mut self,
12876 _: &SelectToNextSubwordEnd,
12877 window: &mut Window,
12878 cx: &mut Context<Self>,
12879 ) {
12880 self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
12881 self.change_selections(Default::default(), window, cx, |s| {
12882 s.move_heads_with(|map, head, _| {
12883 (movement::next_subword_end(map, head), SelectionGoal::None)
12884 });
12885 })
12886 }
12887
12888 pub fn delete_to_next_word_end(
12889 &mut self,
12890 action: &DeleteToNextWordEnd,
12891 window: &mut Window,
12892 cx: &mut Context<Self>,
12893 ) {
12894 self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
12895 self.transact(window, cx, |this, window, cx| {
12896 this.change_selections(Default::default(), window, cx, |s| {
12897 s.move_with(|map, selection| {
12898 if selection.is_empty() {
12899 let cursor = if action.ignore_newlines {
12900 movement::next_word_end(map, selection.head())
12901 } else {
12902 movement::next_word_end_or_newline(map, selection.head())
12903 };
12904 selection.set_head(cursor, SelectionGoal::None);
12905 }
12906 });
12907 });
12908 this.insert("", window, cx);
12909 });
12910 }
12911
12912 pub fn delete_to_next_subword_end(
12913 &mut self,
12914 _: &DeleteToNextSubwordEnd,
12915 window: &mut Window,
12916 cx: &mut Context<Self>,
12917 ) {
12918 self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
12919 self.transact(window, cx, |this, window, cx| {
12920 this.change_selections(Default::default(), window, cx, |s| {
12921 s.move_with(|map, selection| {
12922 if selection.is_empty() {
12923 let cursor = movement::next_subword_end(map, selection.head());
12924 selection.set_head(cursor, SelectionGoal::None);
12925 }
12926 });
12927 });
12928 this.insert("", window, cx);
12929 });
12930 }
12931
12932 pub fn move_to_beginning_of_line(
12933 &mut self,
12934 action: &MoveToBeginningOfLine,
12935 window: &mut Window,
12936 cx: &mut Context<Self>,
12937 ) {
12938 self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
12939 self.change_selections(Default::default(), window, cx, |s| {
12940 s.move_cursors_with(|map, head, _| {
12941 (
12942 movement::indented_line_beginning(
12943 map,
12944 head,
12945 action.stop_at_soft_wraps,
12946 action.stop_at_indent,
12947 ),
12948 SelectionGoal::None,
12949 )
12950 });
12951 })
12952 }
12953
12954 pub fn select_to_beginning_of_line(
12955 &mut self,
12956 action: &SelectToBeginningOfLine,
12957 window: &mut Window,
12958 cx: &mut Context<Self>,
12959 ) {
12960 self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
12961 self.change_selections(Default::default(), window, cx, |s| {
12962 s.move_heads_with(|map, head, _| {
12963 (
12964 movement::indented_line_beginning(
12965 map,
12966 head,
12967 action.stop_at_soft_wraps,
12968 action.stop_at_indent,
12969 ),
12970 SelectionGoal::None,
12971 )
12972 });
12973 });
12974 }
12975
12976 pub fn delete_to_beginning_of_line(
12977 &mut self,
12978 action: &DeleteToBeginningOfLine,
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.change_selections(Default::default(), window, cx, |s| {
12985 s.move_with(|_, selection| {
12986 selection.reversed = true;
12987 });
12988 });
12989
12990 this.select_to_beginning_of_line(
12991 &SelectToBeginningOfLine {
12992 stop_at_soft_wraps: false,
12993 stop_at_indent: action.stop_at_indent,
12994 },
12995 window,
12996 cx,
12997 );
12998 this.backspace(&Backspace, window, cx);
12999 });
13000 }
13001
13002 pub fn move_to_end_of_line(
13003 &mut self,
13004 action: &MoveToEndOfLine,
13005 window: &mut Window,
13006 cx: &mut Context<Self>,
13007 ) {
13008 self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
13009 self.change_selections(Default::default(), window, cx, |s| {
13010 s.move_cursors_with(|map, head, _| {
13011 (
13012 movement::line_end(map, head, action.stop_at_soft_wraps),
13013 SelectionGoal::None,
13014 )
13015 });
13016 })
13017 }
13018
13019 pub fn select_to_end_of_line(
13020 &mut self,
13021 action: &SelectToEndOfLine,
13022 window: &mut Window,
13023 cx: &mut Context<Self>,
13024 ) {
13025 self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
13026 self.change_selections(Default::default(), window, cx, |s| {
13027 s.move_heads_with(|map, head, _| {
13028 (
13029 movement::line_end(map, head, action.stop_at_soft_wraps),
13030 SelectionGoal::None,
13031 )
13032 });
13033 })
13034 }
13035
13036 pub fn delete_to_end_of_line(
13037 &mut self,
13038 _: &DeleteToEndOfLine,
13039 window: &mut Window,
13040 cx: &mut Context<Self>,
13041 ) {
13042 self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
13043 self.transact(window, cx, |this, window, cx| {
13044 this.select_to_end_of_line(
13045 &SelectToEndOfLine {
13046 stop_at_soft_wraps: false,
13047 },
13048 window,
13049 cx,
13050 );
13051 this.delete(&Delete, window, cx);
13052 });
13053 }
13054
13055 pub fn cut_to_end_of_line(
13056 &mut self,
13057 _: &CutToEndOfLine,
13058 window: &mut Window,
13059 cx: &mut Context<Self>,
13060 ) {
13061 self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
13062 self.transact(window, cx, |this, window, cx| {
13063 this.select_to_end_of_line(
13064 &SelectToEndOfLine {
13065 stop_at_soft_wraps: false,
13066 },
13067 window,
13068 cx,
13069 );
13070 this.cut(&Cut, window, cx);
13071 });
13072 }
13073
13074 pub fn move_to_start_of_paragraph(
13075 &mut self,
13076 _: &MoveToStartOfParagraph,
13077 window: &mut Window,
13078 cx: &mut Context<Self>,
13079 ) {
13080 if matches!(self.mode, EditorMode::SingleLine { .. }) {
13081 cx.propagate();
13082 return;
13083 }
13084 self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
13085 self.change_selections(Default::default(), window, cx, |s| {
13086 s.move_with(|map, selection| {
13087 selection.collapse_to(
13088 movement::start_of_paragraph(map, selection.head(), 1),
13089 SelectionGoal::None,
13090 )
13091 });
13092 })
13093 }
13094
13095 pub fn move_to_end_of_paragraph(
13096 &mut self,
13097 _: &MoveToEndOfParagraph,
13098 window: &mut Window,
13099 cx: &mut Context<Self>,
13100 ) {
13101 if matches!(self.mode, EditorMode::SingleLine { .. }) {
13102 cx.propagate();
13103 return;
13104 }
13105 self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
13106 self.change_selections(Default::default(), window, cx, |s| {
13107 s.move_with(|map, selection| {
13108 selection.collapse_to(
13109 movement::end_of_paragraph(map, selection.head(), 1),
13110 SelectionGoal::None,
13111 )
13112 });
13113 })
13114 }
13115
13116 pub fn select_to_start_of_paragraph(
13117 &mut self,
13118 _: &SelectToStartOfParagraph,
13119 window: &mut Window,
13120 cx: &mut Context<Self>,
13121 ) {
13122 if matches!(self.mode, EditorMode::SingleLine { .. }) {
13123 cx.propagate();
13124 return;
13125 }
13126 self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
13127 self.change_selections(Default::default(), window, cx, |s| {
13128 s.move_heads_with(|map, head, _| {
13129 (
13130 movement::start_of_paragraph(map, head, 1),
13131 SelectionGoal::None,
13132 )
13133 });
13134 })
13135 }
13136
13137 pub fn select_to_end_of_paragraph(
13138 &mut self,
13139 _: &SelectToEndOfParagraph,
13140 window: &mut Window,
13141 cx: &mut Context<Self>,
13142 ) {
13143 if matches!(self.mode, EditorMode::SingleLine { .. }) {
13144 cx.propagate();
13145 return;
13146 }
13147 self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
13148 self.change_selections(Default::default(), window, cx, |s| {
13149 s.move_heads_with(|map, head, _| {
13150 (
13151 movement::end_of_paragraph(map, head, 1),
13152 SelectionGoal::None,
13153 )
13154 });
13155 })
13156 }
13157
13158 pub fn move_to_start_of_excerpt(
13159 &mut self,
13160 _: &MoveToStartOfExcerpt,
13161 window: &mut Window,
13162 cx: &mut Context<Self>,
13163 ) {
13164 if matches!(self.mode, EditorMode::SingleLine { .. }) {
13165 cx.propagate();
13166 return;
13167 }
13168 self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
13169 self.change_selections(Default::default(), window, cx, |s| {
13170 s.move_with(|map, selection| {
13171 selection.collapse_to(
13172 movement::start_of_excerpt(
13173 map,
13174 selection.head(),
13175 workspace::searchable::Direction::Prev,
13176 ),
13177 SelectionGoal::None,
13178 )
13179 });
13180 })
13181 }
13182
13183 pub fn move_to_start_of_next_excerpt(
13184 &mut self,
13185 _: &MoveToStartOfNextExcerpt,
13186 window: &mut Window,
13187 cx: &mut Context<Self>,
13188 ) {
13189 if matches!(self.mode, EditorMode::SingleLine { .. }) {
13190 cx.propagate();
13191 return;
13192 }
13193
13194 self.change_selections(Default::default(), window, cx, |s| {
13195 s.move_with(|map, selection| {
13196 selection.collapse_to(
13197 movement::start_of_excerpt(
13198 map,
13199 selection.head(),
13200 workspace::searchable::Direction::Next,
13201 ),
13202 SelectionGoal::None,
13203 )
13204 });
13205 })
13206 }
13207
13208 pub fn move_to_end_of_excerpt(
13209 &mut self,
13210 _: &MoveToEndOfExcerpt,
13211 window: &mut Window,
13212 cx: &mut Context<Self>,
13213 ) {
13214 if matches!(self.mode, EditorMode::SingleLine { .. }) {
13215 cx.propagate();
13216 return;
13217 }
13218 self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
13219 self.change_selections(Default::default(), window, cx, |s| {
13220 s.move_with(|map, selection| {
13221 selection.collapse_to(
13222 movement::end_of_excerpt(
13223 map,
13224 selection.head(),
13225 workspace::searchable::Direction::Next,
13226 ),
13227 SelectionGoal::None,
13228 )
13229 });
13230 })
13231 }
13232
13233 pub fn move_to_end_of_previous_excerpt(
13234 &mut self,
13235 _: &MoveToEndOfPreviousExcerpt,
13236 window: &mut Window,
13237 cx: &mut Context<Self>,
13238 ) {
13239 if matches!(self.mode, EditorMode::SingleLine { .. }) {
13240 cx.propagate();
13241 return;
13242 }
13243 self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
13244 self.change_selections(Default::default(), window, cx, |s| {
13245 s.move_with(|map, selection| {
13246 selection.collapse_to(
13247 movement::end_of_excerpt(
13248 map,
13249 selection.head(),
13250 workspace::searchable::Direction::Prev,
13251 ),
13252 SelectionGoal::None,
13253 )
13254 });
13255 })
13256 }
13257
13258 pub fn select_to_start_of_excerpt(
13259 &mut self,
13260 _: &SelectToStartOfExcerpt,
13261 window: &mut Window,
13262 cx: &mut Context<Self>,
13263 ) {
13264 if matches!(self.mode, EditorMode::SingleLine { .. }) {
13265 cx.propagate();
13266 return;
13267 }
13268 self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
13269 self.change_selections(Default::default(), window, cx, |s| {
13270 s.move_heads_with(|map, head, _| {
13271 (
13272 movement::start_of_excerpt(map, head, workspace::searchable::Direction::Prev),
13273 SelectionGoal::None,
13274 )
13275 });
13276 })
13277 }
13278
13279 pub fn select_to_start_of_next_excerpt(
13280 &mut self,
13281 _: &SelectToStartOfNextExcerpt,
13282 window: &mut Window,
13283 cx: &mut Context<Self>,
13284 ) {
13285 if matches!(self.mode, EditorMode::SingleLine { .. }) {
13286 cx.propagate();
13287 return;
13288 }
13289 self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
13290 self.change_selections(Default::default(), window, cx, |s| {
13291 s.move_heads_with(|map, head, _| {
13292 (
13293 movement::start_of_excerpt(map, head, workspace::searchable::Direction::Next),
13294 SelectionGoal::None,
13295 )
13296 });
13297 })
13298 }
13299
13300 pub fn select_to_end_of_excerpt(
13301 &mut self,
13302 _: &SelectToEndOfExcerpt,
13303 window: &mut Window,
13304 cx: &mut Context<Self>,
13305 ) {
13306 if matches!(self.mode, EditorMode::SingleLine { .. }) {
13307 cx.propagate();
13308 return;
13309 }
13310 self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
13311 self.change_selections(Default::default(), window, cx, |s| {
13312 s.move_heads_with(|map, head, _| {
13313 (
13314 movement::end_of_excerpt(map, head, workspace::searchable::Direction::Next),
13315 SelectionGoal::None,
13316 )
13317 });
13318 })
13319 }
13320
13321 pub fn select_to_end_of_previous_excerpt(
13322 &mut self,
13323 _: &SelectToEndOfPreviousExcerpt,
13324 window: &mut Window,
13325 cx: &mut Context<Self>,
13326 ) {
13327 if matches!(self.mode, EditorMode::SingleLine { .. }) {
13328 cx.propagate();
13329 return;
13330 }
13331 self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
13332 self.change_selections(Default::default(), window, cx, |s| {
13333 s.move_heads_with(|map, head, _| {
13334 (
13335 movement::end_of_excerpt(map, head, workspace::searchable::Direction::Prev),
13336 SelectionGoal::None,
13337 )
13338 });
13339 })
13340 }
13341
13342 pub fn move_to_beginning(
13343 &mut self,
13344 _: &MoveToBeginning,
13345 window: &mut Window,
13346 cx: &mut Context<Self>,
13347 ) {
13348 if matches!(self.mode, EditorMode::SingleLine { .. }) {
13349 cx.propagate();
13350 return;
13351 }
13352 self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
13353 self.change_selections(Default::default(), window, cx, |s| {
13354 s.select_ranges(vec![0..0]);
13355 });
13356 }
13357
13358 pub fn select_to_beginning(
13359 &mut self,
13360 _: &SelectToBeginning,
13361 window: &mut Window,
13362 cx: &mut Context<Self>,
13363 ) {
13364 let mut selection = self.selections.last::<Point>(cx);
13365 selection.set_head(Point::zero(), SelectionGoal::None);
13366 self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
13367 self.change_selections(Default::default(), window, cx, |s| {
13368 s.select(vec![selection]);
13369 });
13370 }
13371
13372 pub fn move_to_end(&mut self, _: &MoveToEnd, window: &mut Window, cx: &mut Context<Self>) {
13373 if matches!(self.mode, EditorMode::SingleLine { .. }) {
13374 cx.propagate();
13375 return;
13376 }
13377 self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
13378 let cursor = self.buffer.read(cx).read(cx).len();
13379 self.change_selections(Default::default(), window, cx, |s| {
13380 s.select_ranges(vec![cursor..cursor])
13381 });
13382 }
13383
13384 pub fn set_nav_history(&mut self, nav_history: Option<ItemNavHistory>) {
13385 self.nav_history = nav_history;
13386 }
13387
13388 pub fn nav_history(&self) -> Option<&ItemNavHistory> {
13389 self.nav_history.as_ref()
13390 }
13391
13392 pub fn create_nav_history_entry(&mut self, cx: &mut Context<Self>) {
13393 self.push_to_nav_history(
13394 self.selections.newest_anchor().head(),
13395 None,
13396 false,
13397 true,
13398 cx,
13399 );
13400 }
13401
13402 fn push_to_nav_history(
13403 &mut self,
13404 cursor_anchor: Anchor,
13405 new_position: Option<Point>,
13406 is_deactivate: bool,
13407 always: bool,
13408 cx: &mut Context<Self>,
13409 ) {
13410 if let Some(nav_history) = self.nav_history.as_mut() {
13411 let buffer = self.buffer.read(cx).read(cx);
13412 let cursor_position = cursor_anchor.to_point(&buffer);
13413 let scroll_state = self.scroll_manager.anchor();
13414 let scroll_top_row = scroll_state.top_row(&buffer);
13415 drop(buffer);
13416
13417 if let Some(new_position) = new_position {
13418 let row_delta = (new_position.row as i64 - cursor_position.row as i64).abs();
13419 if row_delta == 0 || (row_delta < MIN_NAVIGATION_HISTORY_ROW_DELTA && !always) {
13420 return;
13421 }
13422 }
13423
13424 nav_history.push(
13425 Some(NavigationData {
13426 cursor_anchor,
13427 cursor_position,
13428 scroll_anchor: scroll_state,
13429 scroll_top_row,
13430 }),
13431 cx,
13432 );
13433 cx.emit(EditorEvent::PushedToNavHistory {
13434 anchor: cursor_anchor,
13435 is_deactivate,
13436 })
13437 }
13438 }
13439
13440 pub fn select_to_end(&mut self, _: &SelectToEnd, window: &mut Window, cx: &mut Context<Self>) {
13441 self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
13442 let buffer = self.buffer.read(cx).snapshot(cx);
13443 let mut selection = self.selections.first::<usize>(cx);
13444 selection.set_head(buffer.len(), SelectionGoal::None);
13445 self.change_selections(Default::default(), window, cx, |s| {
13446 s.select(vec![selection]);
13447 });
13448 }
13449
13450 pub fn select_all(&mut self, _: &SelectAll, window: &mut Window, cx: &mut Context<Self>) {
13451 self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
13452 let end = self.buffer.read(cx).read(cx).len();
13453 self.change_selections(SelectionEffects::no_scroll(), window, cx, |s| {
13454 s.select_ranges(vec![0..end]);
13455 });
13456 }
13457
13458 pub fn select_line(&mut self, _: &SelectLine, window: &mut Window, cx: &mut Context<Self>) {
13459 self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
13460 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
13461 let mut selections = self.selections.all::<Point>(cx);
13462 let max_point = display_map.buffer_snapshot.max_point();
13463 for selection in &mut selections {
13464 let rows = selection.spanned_rows(true, &display_map);
13465 selection.start = Point::new(rows.start.0, 0);
13466 selection.end = cmp::min(max_point, Point::new(rows.end.0, 0));
13467 selection.reversed = false;
13468 }
13469 self.change_selections(Default::default(), window, cx, |s| {
13470 s.select(selections);
13471 });
13472 }
13473
13474 pub fn split_selection_into_lines(
13475 &mut self,
13476 _: &SplitSelectionIntoLines,
13477 window: &mut Window,
13478 cx: &mut Context<Self>,
13479 ) {
13480 let selections = self
13481 .selections
13482 .all::<Point>(cx)
13483 .into_iter()
13484 .map(|selection| selection.start..selection.end)
13485 .collect::<Vec<_>>();
13486 self.unfold_ranges(&selections, true, true, cx);
13487
13488 let mut new_selection_ranges = Vec::new();
13489 {
13490 let buffer = self.buffer.read(cx).read(cx);
13491 for selection in selections {
13492 for row in selection.start.row..selection.end.row {
13493 let cursor = Point::new(row, buffer.line_len(MultiBufferRow(row)));
13494 new_selection_ranges.push(cursor..cursor);
13495 }
13496
13497 let is_multiline_selection = selection.start.row != selection.end.row;
13498 // Don't insert last one if it's a multi-line selection ending at the start of a line,
13499 // so this action feels more ergonomic when paired with other selection operations
13500 let should_skip_last = is_multiline_selection && selection.end.column == 0;
13501 if !should_skip_last {
13502 new_selection_ranges.push(selection.end..selection.end);
13503 }
13504 }
13505 }
13506 self.change_selections(Default::default(), window, cx, |s| {
13507 s.select_ranges(new_selection_ranges);
13508 });
13509 }
13510
13511 pub fn add_selection_above(
13512 &mut self,
13513 _: &AddSelectionAbove,
13514 window: &mut Window,
13515 cx: &mut Context<Self>,
13516 ) {
13517 self.add_selection(true, window, cx);
13518 }
13519
13520 pub fn add_selection_below(
13521 &mut self,
13522 _: &AddSelectionBelow,
13523 window: &mut Window,
13524 cx: &mut Context<Self>,
13525 ) {
13526 self.add_selection(false, window, cx);
13527 }
13528
13529 fn add_selection(&mut self, above: bool, window: &mut Window, cx: &mut Context<Self>) {
13530 self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
13531
13532 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
13533 let all_selections = self.selections.all::<Point>(cx);
13534 let text_layout_details = self.text_layout_details(window);
13535
13536 let (mut columnar_selections, new_selections_to_columnarize) = {
13537 if let Some(state) = self.add_selections_state.as_ref() {
13538 let columnar_selection_ids: HashSet<_> = state
13539 .groups
13540 .iter()
13541 .flat_map(|group| group.stack.iter())
13542 .copied()
13543 .collect();
13544
13545 all_selections
13546 .into_iter()
13547 .partition(|s| columnar_selection_ids.contains(&s.id))
13548 } else {
13549 (Vec::new(), all_selections)
13550 }
13551 };
13552
13553 let mut state = self
13554 .add_selections_state
13555 .take()
13556 .unwrap_or_else(|| AddSelectionsState { groups: Vec::new() });
13557
13558 for selection in new_selections_to_columnarize {
13559 let range = selection.display_range(&display_map).sorted();
13560 let start_x = display_map.x_for_display_point(range.start, &text_layout_details);
13561 let end_x = display_map.x_for_display_point(range.end, &text_layout_details);
13562 let positions = start_x.min(end_x)..start_x.max(end_x);
13563 let mut stack = Vec::new();
13564 for row in range.start.row().0..=range.end.row().0 {
13565 if let Some(selection) = self.selections.build_columnar_selection(
13566 &display_map,
13567 DisplayRow(row),
13568 &positions,
13569 selection.reversed,
13570 &text_layout_details,
13571 ) {
13572 stack.push(selection.id);
13573 columnar_selections.push(selection);
13574 }
13575 }
13576 if !stack.is_empty() {
13577 if above {
13578 stack.reverse();
13579 }
13580 state.groups.push(AddSelectionsGroup { above, stack });
13581 }
13582 }
13583
13584 let mut final_selections = Vec::new();
13585 let end_row = if above {
13586 DisplayRow(0)
13587 } else {
13588 display_map.max_point().row()
13589 };
13590
13591 let mut last_added_item_per_group = HashMap::default();
13592 for group in state.groups.iter_mut() {
13593 if let Some(last_id) = group.stack.last() {
13594 last_added_item_per_group.insert(*last_id, group);
13595 }
13596 }
13597
13598 for selection in columnar_selections {
13599 if let Some(group) = last_added_item_per_group.get_mut(&selection.id) {
13600 if above == group.above {
13601 let range = selection.display_range(&display_map).sorted();
13602 debug_assert_eq!(range.start.row(), range.end.row());
13603 let mut row = range.start.row();
13604 let positions =
13605 if let SelectionGoal::HorizontalRange { start, end } = selection.goal {
13606 px(start)..px(end)
13607 } else {
13608 let start_x =
13609 display_map.x_for_display_point(range.start, &text_layout_details);
13610 let end_x =
13611 display_map.x_for_display_point(range.end, &text_layout_details);
13612 start_x.min(end_x)..start_x.max(end_x)
13613 };
13614
13615 let mut maybe_new_selection = None;
13616 while row != end_row {
13617 if above {
13618 row.0 -= 1;
13619 } else {
13620 row.0 += 1;
13621 }
13622 if let Some(new_selection) = self.selections.build_columnar_selection(
13623 &display_map,
13624 row,
13625 &positions,
13626 selection.reversed,
13627 &text_layout_details,
13628 ) {
13629 maybe_new_selection = Some(new_selection);
13630 break;
13631 }
13632 }
13633
13634 if let Some(new_selection) = maybe_new_selection {
13635 group.stack.push(new_selection.id);
13636 if above {
13637 final_selections.push(new_selection);
13638 final_selections.push(selection);
13639 } else {
13640 final_selections.push(selection);
13641 final_selections.push(new_selection);
13642 }
13643 } else {
13644 final_selections.push(selection);
13645 }
13646 } else {
13647 group.stack.pop();
13648 }
13649 } else {
13650 final_selections.push(selection);
13651 }
13652 }
13653
13654 self.change_selections(Default::default(), window, cx, |s| {
13655 s.select(final_selections);
13656 });
13657
13658 let final_selection_ids: HashSet<_> = self
13659 .selections
13660 .all::<Point>(cx)
13661 .iter()
13662 .map(|s| s.id)
13663 .collect();
13664 state.groups.retain_mut(|group| {
13665 // selections might get merged above so we remove invalid items from stacks
13666 group.stack.retain(|id| final_selection_ids.contains(id));
13667
13668 // single selection in stack can be treated as initial state
13669 group.stack.len() > 1
13670 });
13671
13672 if !state.groups.is_empty() {
13673 self.add_selections_state = Some(state);
13674 }
13675 }
13676
13677 fn select_match_ranges(
13678 &mut self,
13679 range: Range<usize>,
13680 reversed: bool,
13681 replace_newest: bool,
13682 auto_scroll: Option<Autoscroll>,
13683 window: &mut Window,
13684 cx: &mut Context<Editor>,
13685 ) {
13686 self.unfold_ranges(
13687 std::slice::from_ref(&range),
13688 false,
13689 auto_scroll.is_some(),
13690 cx,
13691 );
13692 let effects = if let Some(scroll) = auto_scroll {
13693 SelectionEffects::scroll(scroll)
13694 } else {
13695 SelectionEffects::no_scroll()
13696 };
13697 self.change_selections(effects, window, cx, |s| {
13698 if replace_newest {
13699 s.delete(s.newest_anchor().id);
13700 }
13701 if reversed {
13702 s.insert_range(range.end..range.start);
13703 } else {
13704 s.insert_range(range);
13705 }
13706 });
13707 }
13708
13709 pub fn select_next_match_internal(
13710 &mut self,
13711 display_map: &DisplaySnapshot,
13712 replace_newest: bool,
13713 autoscroll: Option<Autoscroll>,
13714 window: &mut Window,
13715 cx: &mut Context<Self>,
13716 ) -> Result<()> {
13717 let buffer = &display_map.buffer_snapshot;
13718 let mut selections = self.selections.all::<usize>(cx);
13719 if let Some(mut select_next_state) = self.select_next_state.take() {
13720 let query = &select_next_state.query;
13721 if !select_next_state.done {
13722 let first_selection = selections.iter().min_by_key(|s| s.id).unwrap();
13723 let last_selection = selections.iter().max_by_key(|s| s.id).unwrap();
13724 let mut next_selected_range = None;
13725
13726 let bytes_after_last_selection =
13727 buffer.bytes_in_range(last_selection.end..buffer.len());
13728 let bytes_before_first_selection = buffer.bytes_in_range(0..first_selection.start);
13729 let query_matches = query
13730 .stream_find_iter(bytes_after_last_selection)
13731 .map(|result| (last_selection.end, result))
13732 .chain(
13733 query
13734 .stream_find_iter(bytes_before_first_selection)
13735 .map(|result| (0, result)),
13736 );
13737
13738 for (start_offset, query_match) in query_matches {
13739 let query_match = query_match.unwrap(); // can only fail due to I/O
13740 let offset_range =
13741 start_offset + query_match.start()..start_offset + query_match.end();
13742
13743 if !select_next_state.wordwise
13744 || (!buffer.is_inside_word(offset_range.start, false)
13745 && !buffer.is_inside_word(offset_range.end, false))
13746 {
13747 // TODO: This is n^2, because we might check all the selections
13748 if !selections
13749 .iter()
13750 .any(|selection| selection.range().overlaps(&offset_range))
13751 {
13752 next_selected_range = Some(offset_range);
13753 break;
13754 }
13755 }
13756 }
13757
13758 if let Some(next_selected_range) = next_selected_range {
13759 self.select_match_ranges(
13760 next_selected_range,
13761 last_selection.reversed,
13762 replace_newest,
13763 autoscroll,
13764 window,
13765 cx,
13766 );
13767 } else {
13768 select_next_state.done = true;
13769 }
13770 }
13771
13772 self.select_next_state = Some(select_next_state);
13773 } else {
13774 let mut only_carets = true;
13775 let mut same_text_selected = true;
13776 let mut selected_text = None;
13777
13778 let mut selections_iter = selections.iter().peekable();
13779 while let Some(selection) = selections_iter.next() {
13780 if selection.start != selection.end {
13781 only_carets = false;
13782 }
13783
13784 if same_text_selected {
13785 if selected_text.is_none() {
13786 selected_text =
13787 Some(buffer.text_for_range(selection.range()).collect::<String>());
13788 }
13789
13790 if let Some(next_selection) = selections_iter.peek() {
13791 if next_selection.range().len() == selection.range().len() {
13792 let next_selected_text = buffer
13793 .text_for_range(next_selection.range())
13794 .collect::<String>();
13795 if Some(next_selected_text) != selected_text {
13796 same_text_selected = false;
13797 selected_text = None;
13798 }
13799 } else {
13800 same_text_selected = false;
13801 selected_text = None;
13802 }
13803 }
13804 }
13805 }
13806
13807 if only_carets {
13808 for selection in &mut selections {
13809 let (word_range, _) = buffer.surrounding_word(selection.start, false);
13810 selection.start = word_range.start;
13811 selection.end = word_range.end;
13812 selection.goal = SelectionGoal::None;
13813 selection.reversed = false;
13814 self.select_match_ranges(
13815 selection.start..selection.end,
13816 selection.reversed,
13817 replace_newest,
13818 autoscroll,
13819 window,
13820 cx,
13821 );
13822 }
13823
13824 if selections.len() == 1 {
13825 let selection = selections
13826 .last()
13827 .expect("ensured that there's only one selection");
13828 let query = buffer
13829 .text_for_range(selection.start..selection.end)
13830 .collect::<String>();
13831 let is_empty = query.is_empty();
13832 let select_state = SelectNextState {
13833 query: AhoCorasick::new(&[query])?,
13834 wordwise: true,
13835 done: is_empty,
13836 };
13837 self.select_next_state = Some(select_state);
13838 } else {
13839 self.select_next_state = None;
13840 }
13841 } else if let Some(selected_text) = selected_text {
13842 self.select_next_state = Some(SelectNextState {
13843 query: AhoCorasick::new(&[selected_text])?,
13844 wordwise: false,
13845 done: false,
13846 });
13847 self.select_next_match_internal(
13848 display_map,
13849 replace_newest,
13850 autoscroll,
13851 window,
13852 cx,
13853 )?;
13854 }
13855 }
13856 Ok(())
13857 }
13858
13859 pub fn select_all_matches(
13860 &mut self,
13861 _action: &SelectAllMatches,
13862 window: &mut Window,
13863 cx: &mut Context<Self>,
13864 ) -> Result<()> {
13865 self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
13866
13867 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
13868
13869 self.select_next_match_internal(&display_map, false, None, window, cx)?;
13870 let Some(select_next_state) = self.select_next_state.as_mut() else {
13871 return Ok(());
13872 };
13873 if select_next_state.done {
13874 return Ok(());
13875 }
13876
13877 let mut new_selections = Vec::new();
13878
13879 let reversed = self.selections.oldest::<usize>(cx).reversed;
13880 let buffer = &display_map.buffer_snapshot;
13881 let query_matches = select_next_state
13882 .query
13883 .stream_find_iter(buffer.bytes_in_range(0..buffer.len()));
13884
13885 for query_match in query_matches.into_iter() {
13886 let query_match = query_match.context("query match for select all action")?; // can only fail due to I/O
13887 let offset_range = if reversed {
13888 query_match.end()..query_match.start()
13889 } else {
13890 query_match.start()..query_match.end()
13891 };
13892
13893 if !select_next_state.wordwise
13894 || (!buffer.is_inside_word(offset_range.start, false)
13895 && !buffer.is_inside_word(offset_range.end, false))
13896 {
13897 new_selections.push(offset_range.start..offset_range.end);
13898 }
13899 }
13900
13901 select_next_state.done = true;
13902
13903 if new_selections.is_empty() {
13904 log::error!("bug: new_selections is empty in select_all_matches");
13905 return Ok(());
13906 }
13907
13908 self.unfold_ranges(&new_selections.clone(), false, false, cx);
13909 self.change_selections(SelectionEffects::no_scroll(), window, cx, |selections| {
13910 selections.select_ranges(new_selections)
13911 });
13912
13913 Ok(())
13914 }
13915
13916 pub fn select_next(
13917 &mut self,
13918 action: &SelectNext,
13919 window: &mut Window,
13920 cx: &mut Context<Self>,
13921 ) -> Result<()> {
13922 self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
13923 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
13924 self.select_next_match_internal(
13925 &display_map,
13926 action.replace_newest,
13927 Some(Autoscroll::newest()),
13928 window,
13929 cx,
13930 )?;
13931 Ok(())
13932 }
13933
13934 pub fn select_previous(
13935 &mut self,
13936 action: &SelectPrevious,
13937 window: &mut Window,
13938 cx: &mut Context<Self>,
13939 ) -> Result<()> {
13940 self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
13941 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
13942 let buffer = &display_map.buffer_snapshot;
13943 let mut selections = self.selections.all::<usize>(cx);
13944 if let Some(mut select_prev_state) = self.select_prev_state.take() {
13945 let query = &select_prev_state.query;
13946 if !select_prev_state.done {
13947 let first_selection = selections.iter().min_by_key(|s| s.id).unwrap();
13948 let last_selection = selections.iter().max_by_key(|s| s.id).unwrap();
13949 let mut next_selected_range = None;
13950 // When we're iterating matches backwards, the oldest match will actually be the furthest one in the buffer.
13951 let bytes_before_last_selection =
13952 buffer.reversed_bytes_in_range(0..last_selection.start);
13953 let bytes_after_first_selection =
13954 buffer.reversed_bytes_in_range(first_selection.end..buffer.len());
13955 let query_matches = query
13956 .stream_find_iter(bytes_before_last_selection)
13957 .map(|result| (last_selection.start, result))
13958 .chain(
13959 query
13960 .stream_find_iter(bytes_after_first_selection)
13961 .map(|result| (buffer.len(), result)),
13962 );
13963 for (end_offset, query_match) in query_matches {
13964 let query_match = query_match.unwrap(); // can only fail due to I/O
13965 let offset_range =
13966 end_offset - query_match.end()..end_offset - query_match.start();
13967
13968 if !select_prev_state.wordwise
13969 || (!buffer.is_inside_word(offset_range.start, false)
13970 && !buffer.is_inside_word(offset_range.end, false))
13971 {
13972 next_selected_range = Some(offset_range);
13973 break;
13974 }
13975 }
13976
13977 if let Some(next_selected_range) = next_selected_range {
13978 self.select_match_ranges(
13979 next_selected_range,
13980 last_selection.reversed,
13981 action.replace_newest,
13982 Some(Autoscroll::newest()),
13983 window,
13984 cx,
13985 );
13986 } else {
13987 select_prev_state.done = true;
13988 }
13989 }
13990
13991 self.select_prev_state = Some(select_prev_state);
13992 } else {
13993 let mut only_carets = true;
13994 let mut same_text_selected = true;
13995 let mut selected_text = None;
13996
13997 let mut selections_iter = selections.iter().peekable();
13998 while let Some(selection) = selections_iter.next() {
13999 if selection.start != selection.end {
14000 only_carets = false;
14001 }
14002
14003 if same_text_selected {
14004 if selected_text.is_none() {
14005 selected_text =
14006 Some(buffer.text_for_range(selection.range()).collect::<String>());
14007 }
14008
14009 if let Some(next_selection) = selections_iter.peek() {
14010 if next_selection.range().len() == selection.range().len() {
14011 let next_selected_text = buffer
14012 .text_for_range(next_selection.range())
14013 .collect::<String>();
14014 if Some(next_selected_text) != selected_text {
14015 same_text_selected = false;
14016 selected_text = None;
14017 }
14018 } else {
14019 same_text_selected = false;
14020 selected_text = None;
14021 }
14022 }
14023 }
14024 }
14025
14026 if only_carets {
14027 for selection in &mut selections {
14028 let (word_range, _) = buffer.surrounding_word(selection.start, false);
14029 selection.start = word_range.start;
14030 selection.end = word_range.end;
14031 selection.goal = SelectionGoal::None;
14032 selection.reversed = false;
14033 self.select_match_ranges(
14034 selection.start..selection.end,
14035 selection.reversed,
14036 action.replace_newest,
14037 Some(Autoscroll::newest()),
14038 window,
14039 cx,
14040 );
14041 }
14042 if selections.len() == 1 {
14043 let selection = selections
14044 .last()
14045 .expect("ensured that there's only one selection");
14046 let query = buffer
14047 .text_for_range(selection.start..selection.end)
14048 .collect::<String>();
14049 let is_empty = query.is_empty();
14050 let select_state = SelectNextState {
14051 query: AhoCorasick::new(&[query.chars().rev().collect::<String>()])?,
14052 wordwise: true,
14053 done: is_empty,
14054 };
14055 self.select_prev_state = Some(select_state);
14056 } else {
14057 self.select_prev_state = None;
14058 }
14059 } else if let Some(selected_text) = selected_text {
14060 self.select_prev_state = Some(SelectNextState {
14061 query: AhoCorasick::new(&[selected_text.chars().rev().collect::<String>()])?,
14062 wordwise: false,
14063 done: false,
14064 });
14065 self.select_previous(action, window, cx)?;
14066 }
14067 }
14068 Ok(())
14069 }
14070
14071 pub fn find_next_match(
14072 &mut self,
14073 _: &FindNextMatch,
14074 window: &mut Window,
14075 cx: &mut Context<Self>,
14076 ) -> Result<()> {
14077 let selections = self.selections.disjoint_anchors();
14078 match selections.first() {
14079 Some(first) if selections.len() >= 2 => {
14080 self.change_selections(Default::default(), window, cx, |s| {
14081 s.select_ranges([first.range()]);
14082 });
14083 }
14084 _ => self.select_next(
14085 &SelectNext {
14086 replace_newest: true,
14087 },
14088 window,
14089 cx,
14090 )?,
14091 }
14092 Ok(())
14093 }
14094
14095 pub fn find_previous_match(
14096 &mut self,
14097 _: &FindPreviousMatch,
14098 window: &mut Window,
14099 cx: &mut Context<Self>,
14100 ) -> Result<()> {
14101 let selections = self.selections.disjoint_anchors();
14102 match selections.last() {
14103 Some(last) if selections.len() >= 2 => {
14104 self.change_selections(Default::default(), window, cx, |s| {
14105 s.select_ranges([last.range()]);
14106 });
14107 }
14108 _ => self.select_previous(
14109 &SelectPrevious {
14110 replace_newest: true,
14111 },
14112 window,
14113 cx,
14114 )?,
14115 }
14116 Ok(())
14117 }
14118
14119 pub fn toggle_comments(
14120 &mut self,
14121 action: &ToggleComments,
14122 window: &mut Window,
14123 cx: &mut Context<Self>,
14124 ) {
14125 if self.read_only(cx) {
14126 return;
14127 }
14128 self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
14129 let text_layout_details = &self.text_layout_details(window);
14130 self.transact(window, cx, |this, window, cx| {
14131 let mut selections = this.selections.all::<MultiBufferPoint>(cx);
14132 let mut edits = Vec::new();
14133 let mut selection_edit_ranges = Vec::new();
14134 let mut last_toggled_row = None;
14135 let snapshot = this.buffer.read(cx).read(cx);
14136 let empty_str: Arc<str> = Arc::default();
14137 let mut suffixes_inserted = Vec::new();
14138 let ignore_indent = action.ignore_indent;
14139
14140 fn comment_prefix_range(
14141 snapshot: &MultiBufferSnapshot,
14142 row: MultiBufferRow,
14143 comment_prefix: &str,
14144 comment_prefix_whitespace: &str,
14145 ignore_indent: bool,
14146 ) -> Range<Point> {
14147 let indent_size = if ignore_indent {
14148 0
14149 } else {
14150 snapshot.indent_size_for_line(row).len
14151 };
14152
14153 let start = Point::new(row.0, indent_size);
14154
14155 let mut line_bytes = snapshot
14156 .bytes_in_range(start..snapshot.max_point())
14157 .flatten()
14158 .copied();
14159
14160 // If this line currently begins with the line comment prefix, then record
14161 // the range containing the prefix.
14162 if line_bytes
14163 .by_ref()
14164 .take(comment_prefix.len())
14165 .eq(comment_prefix.bytes())
14166 {
14167 // Include any whitespace that matches the comment prefix.
14168 let matching_whitespace_len = line_bytes
14169 .zip(comment_prefix_whitespace.bytes())
14170 .take_while(|(a, b)| a == b)
14171 .count() as u32;
14172 let end = Point::new(
14173 start.row,
14174 start.column + comment_prefix.len() as u32 + matching_whitespace_len,
14175 );
14176 start..end
14177 } else {
14178 start..start
14179 }
14180 }
14181
14182 fn comment_suffix_range(
14183 snapshot: &MultiBufferSnapshot,
14184 row: MultiBufferRow,
14185 comment_suffix: &str,
14186 comment_suffix_has_leading_space: bool,
14187 ) -> Range<Point> {
14188 let end = Point::new(row.0, snapshot.line_len(row));
14189 let suffix_start_column = end.column.saturating_sub(comment_suffix.len() as u32);
14190
14191 let mut line_end_bytes = snapshot
14192 .bytes_in_range(Point::new(end.row, suffix_start_column.saturating_sub(1))..end)
14193 .flatten()
14194 .copied();
14195
14196 let leading_space_len = if suffix_start_column > 0
14197 && line_end_bytes.next() == Some(b' ')
14198 && comment_suffix_has_leading_space
14199 {
14200 1
14201 } else {
14202 0
14203 };
14204
14205 // If this line currently begins with the line comment prefix, then record
14206 // the range containing the prefix.
14207 if line_end_bytes.by_ref().eq(comment_suffix.bytes()) {
14208 let start = Point::new(end.row, suffix_start_column - leading_space_len);
14209 start..end
14210 } else {
14211 end..end
14212 }
14213 }
14214
14215 // TODO: Handle selections that cross excerpts
14216 for selection in &mut selections {
14217 let start_column = snapshot
14218 .indent_size_for_line(MultiBufferRow(selection.start.row))
14219 .len;
14220 let language = if let Some(language) =
14221 snapshot.language_scope_at(Point::new(selection.start.row, start_column))
14222 {
14223 language
14224 } else {
14225 continue;
14226 };
14227
14228 selection_edit_ranges.clear();
14229
14230 // If multiple selections contain a given row, avoid processing that
14231 // row more than once.
14232 let mut start_row = MultiBufferRow(selection.start.row);
14233 if last_toggled_row == Some(start_row) {
14234 start_row = start_row.next_row();
14235 }
14236 let end_row =
14237 if selection.end.row > selection.start.row && selection.end.column == 0 {
14238 MultiBufferRow(selection.end.row - 1)
14239 } else {
14240 MultiBufferRow(selection.end.row)
14241 };
14242 last_toggled_row = Some(end_row);
14243
14244 if start_row > end_row {
14245 continue;
14246 }
14247
14248 // If the language has line comments, toggle those.
14249 let mut full_comment_prefixes = language.line_comment_prefixes().to_vec();
14250
14251 // If ignore_indent is set, trim spaces from the right side of all full_comment_prefixes
14252 if ignore_indent {
14253 full_comment_prefixes = full_comment_prefixes
14254 .into_iter()
14255 .map(|s| Arc::from(s.trim_end()))
14256 .collect();
14257 }
14258
14259 if !full_comment_prefixes.is_empty() {
14260 let first_prefix = full_comment_prefixes
14261 .first()
14262 .expect("prefixes is non-empty");
14263 let prefix_trimmed_lengths = full_comment_prefixes
14264 .iter()
14265 .map(|p| p.trim_end_matches(' ').len())
14266 .collect::<SmallVec<[usize; 4]>>();
14267
14268 let mut all_selection_lines_are_comments = true;
14269
14270 for row in start_row.0..=end_row.0 {
14271 let row = MultiBufferRow(row);
14272 if start_row < end_row && snapshot.is_line_blank(row) {
14273 continue;
14274 }
14275
14276 let prefix_range = full_comment_prefixes
14277 .iter()
14278 .zip(prefix_trimmed_lengths.iter().copied())
14279 .map(|(prefix, trimmed_prefix_len)| {
14280 comment_prefix_range(
14281 snapshot.deref(),
14282 row,
14283 &prefix[..trimmed_prefix_len],
14284 &prefix[trimmed_prefix_len..],
14285 ignore_indent,
14286 )
14287 })
14288 .max_by_key(|range| range.end.column - range.start.column)
14289 .expect("prefixes is non-empty");
14290
14291 if prefix_range.is_empty() {
14292 all_selection_lines_are_comments = false;
14293 }
14294
14295 selection_edit_ranges.push(prefix_range);
14296 }
14297
14298 if all_selection_lines_are_comments {
14299 edits.extend(
14300 selection_edit_ranges
14301 .iter()
14302 .cloned()
14303 .map(|range| (range, empty_str.clone())),
14304 );
14305 } else {
14306 let min_column = selection_edit_ranges
14307 .iter()
14308 .map(|range| range.start.column)
14309 .min()
14310 .unwrap_or(0);
14311 edits.extend(selection_edit_ranges.iter().map(|range| {
14312 let position = Point::new(range.start.row, min_column);
14313 (position..position, first_prefix.clone())
14314 }));
14315 }
14316 } else if let Some((full_comment_prefix, comment_suffix)) =
14317 language.block_comment_delimiters()
14318 {
14319 let comment_prefix = full_comment_prefix.trim_end_matches(' ');
14320 let comment_prefix_whitespace = &full_comment_prefix[comment_prefix.len()..];
14321 let prefix_range = comment_prefix_range(
14322 snapshot.deref(),
14323 start_row,
14324 comment_prefix,
14325 comment_prefix_whitespace,
14326 ignore_indent,
14327 );
14328 let suffix_range = comment_suffix_range(
14329 snapshot.deref(),
14330 end_row,
14331 comment_suffix.trim_start_matches(' '),
14332 comment_suffix.starts_with(' '),
14333 );
14334
14335 if prefix_range.is_empty() || suffix_range.is_empty() {
14336 edits.push((
14337 prefix_range.start..prefix_range.start,
14338 full_comment_prefix.clone(),
14339 ));
14340 edits.push((suffix_range.end..suffix_range.end, comment_suffix.clone()));
14341 suffixes_inserted.push((end_row, comment_suffix.len()));
14342 } else {
14343 edits.push((prefix_range, empty_str.clone()));
14344 edits.push((suffix_range, empty_str.clone()));
14345 }
14346 } else {
14347 continue;
14348 }
14349 }
14350
14351 drop(snapshot);
14352 this.buffer.update(cx, |buffer, cx| {
14353 buffer.edit(edits, None, cx);
14354 });
14355
14356 // Adjust selections so that they end before any comment suffixes that
14357 // were inserted.
14358 let mut suffixes_inserted = suffixes_inserted.into_iter().peekable();
14359 let mut selections = this.selections.all::<Point>(cx);
14360 let snapshot = this.buffer.read(cx).read(cx);
14361 for selection in &mut selections {
14362 while let Some((row, suffix_len)) = suffixes_inserted.peek().copied() {
14363 match row.cmp(&MultiBufferRow(selection.end.row)) {
14364 Ordering::Less => {
14365 suffixes_inserted.next();
14366 continue;
14367 }
14368 Ordering::Greater => break,
14369 Ordering::Equal => {
14370 if selection.end.column == snapshot.line_len(row) {
14371 if selection.is_empty() {
14372 selection.start.column -= suffix_len as u32;
14373 }
14374 selection.end.column -= suffix_len as u32;
14375 }
14376 break;
14377 }
14378 }
14379 }
14380 }
14381
14382 drop(snapshot);
14383 this.change_selections(Default::default(), window, cx, |s| s.select(selections));
14384
14385 let selections = this.selections.all::<Point>(cx);
14386 let selections_on_single_row = selections.windows(2).all(|selections| {
14387 selections[0].start.row == selections[1].start.row
14388 && selections[0].end.row == selections[1].end.row
14389 && selections[0].start.row == selections[0].end.row
14390 });
14391 let selections_selecting = selections
14392 .iter()
14393 .any(|selection| selection.start != selection.end);
14394 let advance_downwards = action.advance_downwards
14395 && selections_on_single_row
14396 && !selections_selecting
14397 && !matches!(this.mode, EditorMode::SingleLine { .. });
14398
14399 if advance_downwards {
14400 let snapshot = this.buffer.read(cx).snapshot(cx);
14401
14402 this.change_selections(Default::default(), window, cx, |s| {
14403 s.move_cursors_with(|display_snapshot, display_point, _| {
14404 let mut point = display_point.to_point(display_snapshot);
14405 point.row += 1;
14406 point = snapshot.clip_point(point, Bias::Left);
14407 let display_point = point.to_display_point(display_snapshot);
14408 let goal = SelectionGoal::HorizontalPosition(
14409 display_snapshot
14410 .x_for_display_point(display_point, text_layout_details)
14411 .into(),
14412 );
14413 (display_point, goal)
14414 })
14415 });
14416 }
14417 });
14418 }
14419
14420 pub fn select_enclosing_symbol(
14421 &mut self,
14422 _: &SelectEnclosingSymbol,
14423 window: &mut Window,
14424 cx: &mut Context<Self>,
14425 ) {
14426 self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
14427
14428 let buffer = self.buffer.read(cx).snapshot(cx);
14429 let old_selections = self.selections.all::<usize>(cx).into_boxed_slice();
14430
14431 fn update_selection(
14432 selection: &Selection<usize>,
14433 buffer_snap: &MultiBufferSnapshot,
14434 ) -> Option<Selection<usize>> {
14435 let cursor = selection.head();
14436 let (_buffer_id, symbols) = buffer_snap.symbols_containing(cursor, None)?;
14437 for symbol in symbols.iter().rev() {
14438 let start = symbol.range.start.to_offset(buffer_snap);
14439 let end = symbol.range.end.to_offset(buffer_snap);
14440 let new_range = start..end;
14441 if start < selection.start || end > selection.end {
14442 return Some(Selection {
14443 id: selection.id,
14444 start: new_range.start,
14445 end: new_range.end,
14446 goal: SelectionGoal::None,
14447 reversed: selection.reversed,
14448 });
14449 }
14450 }
14451 None
14452 }
14453
14454 let mut selected_larger_symbol = false;
14455 let new_selections = old_selections
14456 .iter()
14457 .map(|selection| match update_selection(selection, &buffer) {
14458 Some(new_selection) => {
14459 if new_selection.range() != selection.range() {
14460 selected_larger_symbol = true;
14461 }
14462 new_selection
14463 }
14464 None => selection.clone(),
14465 })
14466 .collect::<Vec<_>>();
14467
14468 if selected_larger_symbol {
14469 self.change_selections(Default::default(), window, cx, |s| {
14470 s.select(new_selections);
14471 });
14472 }
14473 }
14474
14475 pub fn select_larger_syntax_node(
14476 &mut self,
14477 _: &SelectLargerSyntaxNode,
14478 window: &mut Window,
14479 cx: &mut Context<Self>,
14480 ) {
14481 let Some(visible_row_count) = self.visible_row_count() else {
14482 return;
14483 };
14484 let old_selections: Box<[_]> = self.selections.all::<usize>(cx).into();
14485 if old_selections.is_empty() {
14486 return;
14487 }
14488
14489 self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
14490
14491 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
14492 let buffer = self.buffer.read(cx).snapshot(cx);
14493
14494 let mut selected_larger_node = false;
14495 let mut new_selections = old_selections
14496 .iter()
14497 .map(|selection| {
14498 let old_range = selection.start..selection.end;
14499
14500 if let Some((node, _)) = buffer.syntax_ancestor(old_range.clone()) {
14501 // manually select word at selection
14502 if ["string_content", "inline"].contains(&node.kind()) {
14503 let (word_range, _) = buffer.surrounding_word(old_range.start, false);
14504 // ignore if word is already selected
14505 if !word_range.is_empty() && old_range != word_range {
14506 let (last_word_range, _) =
14507 buffer.surrounding_word(old_range.end, false);
14508 // only select word if start and end point belongs to same word
14509 if word_range == last_word_range {
14510 selected_larger_node = true;
14511 return Selection {
14512 id: selection.id,
14513 start: word_range.start,
14514 end: word_range.end,
14515 goal: SelectionGoal::None,
14516 reversed: selection.reversed,
14517 };
14518 }
14519 }
14520 }
14521 }
14522
14523 let mut new_range = old_range.clone();
14524 while let Some((_node, containing_range)) =
14525 buffer.syntax_ancestor(new_range.clone())
14526 {
14527 new_range = match containing_range {
14528 MultiOrSingleBufferOffsetRange::Single(_) => break,
14529 MultiOrSingleBufferOffsetRange::Multi(range) => range,
14530 };
14531 if !display_map.intersects_fold(new_range.start)
14532 && !display_map.intersects_fold(new_range.end)
14533 {
14534 break;
14535 }
14536 }
14537
14538 selected_larger_node |= new_range != old_range;
14539 Selection {
14540 id: selection.id,
14541 start: new_range.start,
14542 end: new_range.end,
14543 goal: SelectionGoal::None,
14544 reversed: selection.reversed,
14545 }
14546 })
14547 .collect::<Vec<_>>();
14548
14549 if !selected_larger_node {
14550 return; // don't put this call in the history
14551 }
14552
14553 // scroll based on transformation done to the last selection created by the user
14554 let (last_old, last_new) = old_selections
14555 .last()
14556 .zip(new_selections.last().cloned())
14557 .expect("old_selections isn't empty");
14558
14559 // revert selection
14560 let is_selection_reversed = {
14561 let should_newest_selection_be_reversed = last_old.start != last_new.start;
14562 new_selections.last_mut().expect("checked above").reversed =
14563 should_newest_selection_be_reversed;
14564 should_newest_selection_be_reversed
14565 };
14566
14567 if selected_larger_node {
14568 self.select_syntax_node_history.disable_clearing = true;
14569 self.change_selections(SelectionEffects::no_scroll(), window, cx, |s| {
14570 s.select(new_selections.clone());
14571 });
14572 self.select_syntax_node_history.disable_clearing = false;
14573 }
14574
14575 let start_row = last_new.start.to_display_point(&display_map).row().0;
14576 let end_row = last_new.end.to_display_point(&display_map).row().0;
14577 let selection_height = end_row - start_row + 1;
14578 let scroll_margin_rows = self.vertical_scroll_margin() as u32;
14579
14580 let fits_on_the_screen = visible_row_count >= selection_height + scroll_margin_rows * 2;
14581 let scroll_behavior = if fits_on_the_screen {
14582 self.request_autoscroll(Autoscroll::fit(), cx);
14583 SelectSyntaxNodeScrollBehavior::FitSelection
14584 } else if is_selection_reversed {
14585 self.scroll_cursor_top(&ScrollCursorTop, window, cx);
14586 SelectSyntaxNodeScrollBehavior::CursorTop
14587 } else {
14588 self.scroll_cursor_bottom(&ScrollCursorBottom, window, cx);
14589 SelectSyntaxNodeScrollBehavior::CursorBottom
14590 };
14591
14592 self.select_syntax_node_history.push((
14593 old_selections,
14594 scroll_behavior,
14595 is_selection_reversed,
14596 ));
14597 }
14598
14599 pub fn select_smaller_syntax_node(
14600 &mut self,
14601 _: &SelectSmallerSyntaxNode,
14602 window: &mut Window,
14603 cx: &mut Context<Self>,
14604 ) {
14605 self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
14606
14607 if let Some((mut selections, scroll_behavior, is_selection_reversed)) =
14608 self.select_syntax_node_history.pop()
14609 {
14610 if let Some(selection) = selections.last_mut() {
14611 selection.reversed = is_selection_reversed;
14612 }
14613
14614 self.select_syntax_node_history.disable_clearing = true;
14615 self.change_selections(SelectionEffects::no_scroll(), window, cx, |s| {
14616 s.select(selections.to_vec());
14617 });
14618 self.select_syntax_node_history.disable_clearing = false;
14619
14620 match scroll_behavior {
14621 SelectSyntaxNodeScrollBehavior::CursorTop => {
14622 self.scroll_cursor_top(&ScrollCursorTop, window, cx);
14623 }
14624 SelectSyntaxNodeScrollBehavior::FitSelection => {
14625 self.request_autoscroll(Autoscroll::fit(), cx);
14626 }
14627 SelectSyntaxNodeScrollBehavior::CursorBottom => {
14628 self.scroll_cursor_bottom(&ScrollCursorBottom, window, cx);
14629 }
14630 }
14631 }
14632 }
14633
14634 fn refresh_runnables(&mut self, window: &mut Window, cx: &mut Context<Self>) -> Task<()> {
14635 if !EditorSettings::get_global(cx).gutter.runnables {
14636 self.clear_tasks();
14637 return Task::ready(());
14638 }
14639 let project = self.project.as_ref().map(Entity::downgrade);
14640 let task_sources = self.lsp_task_sources(cx);
14641 let multi_buffer = self.buffer.downgrade();
14642 cx.spawn_in(window, async move |editor, cx| {
14643 cx.background_executor().timer(UPDATE_DEBOUNCE).await;
14644 let Some(project) = project.and_then(|p| p.upgrade()) else {
14645 return;
14646 };
14647 let Ok(display_snapshot) = editor.update(cx, |this, cx| {
14648 this.display_map.update(cx, |map, cx| map.snapshot(cx))
14649 }) else {
14650 return;
14651 };
14652
14653 let hide_runnables = project
14654 .update(cx, |project, cx| {
14655 // Do not display any test indicators in non-dev server remote projects.
14656 project.is_via_collab() && project.ssh_connection_string(cx).is_none()
14657 })
14658 .unwrap_or(true);
14659 if hide_runnables {
14660 return;
14661 }
14662 let new_rows =
14663 cx.background_spawn({
14664 let snapshot = display_snapshot.clone();
14665 async move {
14666 Self::fetch_runnable_ranges(&snapshot, Anchor::min()..Anchor::max())
14667 }
14668 })
14669 .await;
14670 let Ok(lsp_tasks) =
14671 cx.update(|_, cx| crate::lsp_tasks(project.clone(), &task_sources, None, cx))
14672 else {
14673 return;
14674 };
14675 let lsp_tasks = lsp_tasks.await;
14676
14677 let Ok(mut lsp_tasks_by_rows) = cx.update(|_, cx| {
14678 lsp_tasks
14679 .into_iter()
14680 .flat_map(|(kind, tasks)| {
14681 tasks.into_iter().filter_map(move |(location, task)| {
14682 Some((kind.clone(), location?, task))
14683 })
14684 })
14685 .fold(HashMap::default(), |mut acc, (kind, location, task)| {
14686 let buffer = location.target.buffer;
14687 let buffer_snapshot = buffer.read(cx).snapshot();
14688 let offset = display_snapshot.buffer_snapshot.excerpts().find_map(
14689 |(excerpt_id, snapshot, _)| {
14690 if snapshot.remote_id() == buffer_snapshot.remote_id() {
14691 display_snapshot
14692 .buffer_snapshot
14693 .anchor_in_excerpt(excerpt_id, location.target.range.start)
14694 } else {
14695 None
14696 }
14697 },
14698 );
14699 if let Some(offset) = offset {
14700 let task_buffer_range =
14701 location.target.range.to_point(&buffer_snapshot);
14702 let context_buffer_range =
14703 task_buffer_range.to_offset(&buffer_snapshot);
14704 let context_range = BufferOffset(context_buffer_range.start)
14705 ..BufferOffset(context_buffer_range.end);
14706
14707 acc.entry((buffer_snapshot.remote_id(), task_buffer_range.start.row))
14708 .or_insert_with(|| RunnableTasks {
14709 templates: Vec::new(),
14710 offset,
14711 column: task_buffer_range.start.column,
14712 extra_variables: HashMap::default(),
14713 context_range,
14714 })
14715 .templates
14716 .push((kind, task.original_task().clone()));
14717 }
14718
14719 acc
14720 })
14721 }) else {
14722 return;
14723 };
14724
14725 let Ok(prefer_lsp) = multi_buffer.update(cx, |buffer, cx| {
14726 buffer.language_settings(cx).tasks.prefer_lsp
14727 }) else {
14728 return;
14729 };
14730
14731 let rows = Self::runnable_rows(
14732 project,
14733 display_snapshot,
14734 prefer_lsp && !lsp_tasks_by_rows.is_empty(),
14735 new_rows,
14736 cx.clone(),
14737 )
14738 .await;
14739 editor
14740 .update(cx, |editor, _| {
14741 editor.clear_tasks();
14742 for (key, mut value) in rows {
14743 if let Some(lsp_tasks) = lsp_tasks_by_rows.remove(&key) {
14744 value.templates.extend(lsp_tasks.templates);
14745 }
14746
14747 editor.insert_tasks(key, value);
14748 }
14749 for (key, value) in lsp_tasks_by_rows {
14750 editor.insert_tasks(key, value);
14751 }
14752 })
14753 .ok();
14754 })
14755 }
14756 fn fetch_runnable_ranges(
14757 snapshot: &DisplaySnapshot,
14758 range: Range<Anchor>,
14759 ) -> Vec<language::RunnableRange> {
14760 snapshot.buffer_snapshot.runnable_ranges(range).collect()
14761 }
14762
14763 fn runnable_rows(
14764 project: Entity<Project>,
14765 snapshot: DisplaySnapshot,
14766 prefer_lsp: bool,
14767 runnable_ranges: Vec<RunnableRange>,
14768 cx: AsyncWindowContext,
14769 ) -> Task<Vec<((BufferId, BufferRow), RunnableTasks)>> {
14770 cx.spawn(async move |cx| {
14771 let mut runnable_rows = Vec::with_capacity(runnable_ranges.len());
14772 for mut runnable in runnable_ranges {
14773 let Some(tasks) = cx
14774 .update(|_, cx| Self::templates_with_tags(&project, &mut runnable.runnable, cx))
14775 .ok()
14776 else {
14777 continue;
14778 };
14779 let mut tasks = tasks.await;
14780
14781 if prefer_lsp {
14782 tasks.retain(|(task_kind, _)| {
14783 !matches!(task_kind, TaskSourceKind::Language { .. })
14784 });
14785 }
14786 if tasks.is_empty() {
14787 continue;
14788 }
14789
14790 let point = runnable.run_range.start.to_point(&snapshot.buffer_snapshot);
14791 let Some(row) = snapshot
14792 .buffer_snapshot
14793 .buffer_line_for_row(MultiBufferRow(point.row))
14794 .map(|(_, range)| range.start.row)
14795 else {
14796 continue;
14797 };
14798
14799 let context_range =
14800 BufferOffset(runnable.full_range.start)..BufferOffset(runnable.full_range.end);
14801 runnable_rows.push((
14802 (runnable.buffer_id, row),
14803 RunnableTasks {
14804 templates: tasks,
14805 offset: snapshot
14806 .buffer_snapshot
14807 .anchor_before(runnable.run_range.start),
14808 context_range,
14809 column: point.column,
14810 extra_variables: runnable.extra_captures,
14811 },
14812 ));
14813 }
14814 runnable_rows
14815 })
14816 }
14817
14818 fn templates_with_tags(
14819 project: &Entity<Project>,
14820 runnable: &mut Runnable,
14821 cx: &mut App,
14822 ) -> Task<Vec<(TaskSourceKind, TaskTemplate)>> {
14823 let (inventory, worktree_id, file) = project.read_with(cx, |project, cx| {
14824 let (worktree_id, file) = project
14825 .buffer_for_id(runnable.buffer, cx)
14826 .and_then(|buffer| buffer.read(cx).file())
14827 .map(|file| (file.worktree_id(cx), file.clone()))
14828 .unzip();
14829
14830 (
14831 project.task_store().read(cx).task_inventory().cloned(),
14832 worktree_id,
14833 file,
14834 )
14835 });
14836
14837 let tags = mem::take(&mut runnable.tags);
14838 let language = runnable.language.clone();
14839 cx.spawn(async move |cx| {
14840 let mut templates_with_tags = Vec::new();
14841 if let Some(inventory) = inventory {
14842 for RunnableTag(tag) in tags {
14843 let Ok(new_tasks) = inventory.update(cx, |inventory, cx| {
14844 inventory.list_tasks(file.clone(), Some(language.clone()), worktree_id, cx)
14845 }) else {
14846 return templates_with_tags;
14847 };
14848 templates_with_tags.extend(new_tasks.await.into_iter().filter(
14849 move |(_, template)| {
14850 template.tags.iter().any(|source_tag| source_tag == &tag)
14851 },
14852 ));
14853 }
14854 }
14855 templates_with_tags.sort_by_key(|(kind, _)| kind.to_owned());
14856
14857 if let Some((leading_tag_source, _)) = templates_with_tags.first() {
14858 // Strongest source wins; if we have worktree tag binding, prefer that to
14859 // global and language bindings;
14860 // if we have a global binding, prefer that to language binding.
14861 let first_mismatch = templates_with_tags
14862 .iter()
14863 .position(|(tag_source, _)| tag_source != leading_tag_source);
14864 if let Some(index) = first_mismatch {
14865 templates_with_tags.truncate(index);
14866 }
14867 }
14868
14869 templates_with_tags
14870 })
14871 }
14872
14873 pub fn move_to_enclosing_bracket(
14874 &mut self,
14875 _: &MoveToEnclosingBracket,
14876 window: &mut Window,
14877 cx: &mut Context<Self>,
14878 ) {
14879 self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
14880 self.change_selections(Default::default(), window, cx, |s| {
14881 s.move_offsets_with(|snapshot, selection| {
14882 let Some(enclosing_bracket_ranges) =
14883 snapshot.enclosing_bracket_ranges(selection.start..selection.end)
14884 else {
14885 return;
14886 };
14887
14888 let mut best_length = usize::MAX;
14889 let mut best_inside = false;
14890 let mut best_in_bracket_range = false;
14891 let mut best_destination = None;
14892 for (open, close) in enclosing_bracket_ranges {
14893 let close = close.to_inclusive();
14894 let length = close.end() - open.start;
14895 let inside = selection.start >= open.end && selection.end <= *close.start();
14896 let in_bracket_range = open.to_inclusive().contains(&selection.head())
14897 || close.contains(&selection.head());
14898
14899 // If best is next to a bracket and current isn't, skip
14900 if !in_bracket_range && best_in_bracket_range {
14901 continue;
14902 }
14903
14904 // Prefer smaller lengths unless best is inside and current isn't
14905 if length > best_length && (best_inside || !inside) {
14906 continue;
14907 }
14908
14909 best_length = length;
14910 best_inside = inside;
14911 best_in_bracket_range = in_bracket_range;
14912 best_destination = Some(
14913 if close.contains(&selection.start) && close.contains(&selection.end) {
14914 if inside { open.end } else { open.start }
14915 } else if inside {
14916 *close.start()
14917 } else {
14918 *close.end()
14919 },
14920 );
14921 }
14922
14923 if let Some(destination) = best_destination {
14924 selection.collapse_to(destination, SelectionGoal::None);
14925 }
14926 })
14927 });
14928 }
14929
14930 pub fn undo_selection(
14931 &mut self,
14932 _: &UndoSelection,
14933 window: &mut Window,
14934 cx: &mut Context<Self>,
14935 ) {
14936 self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
14937 if let Some(entry) = self.selection_history.undo_stack.pop_back() {
14938 self.selection_history.mode = SelectionHistoryMode::Undoing;
14939 self.with_selection_effects_deferred(window, cx, |this, window, cx| {
14940 this.end_selection(window, cx);
14941 this.change_selections(
14942 SelectionEffects::scroll(Autoscroll::newest()),
14943 window,
14944 cx,
14945 |s| s.select_anchors(entry.selections.to_vec()),
14946 );
14947 });
14948 self.selection_history.mode = SelectionHistoryMode::Normal;
14949
14950 self.select_next_state = entry.select_next_state;
14951 self.select_prev_state = entry.select_prev_state;
14952 self.add_selections_state = entry.add_selections_state;
14953 }
14954 }
14955
14956 pub fn redo_selection(
14957 &mut self,
14958 _: &RedoSelection,
14959 window: &mut Window,
14960 cx: &mut Context<Self>,
14961 ) {
14962 self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
14963 if let Some(entry) = self.selection_history.redo_stack.pop_back() {
14964 self.selection_history.mode = SelectionHistoryMode::Redoing;
14965 self.with_selection_effects_deferred(window, cx, |this, window, cx| {
14966 this.end_selection(window, cx);
14967 this.change_selections(
14968 SelectionEffects::scroll(Autoscroll::newest()),
14969 window,
14970 cx,
14971 |s| s.select_anchors(entry.selections.to_vec()),
14972 );
14973 });
14974 self.selection_history.mode = SelectionHistoryMode::Normal;
14975
14976 self.select_next_state = entry.select_next_state;
14977 self.select_prev_state = entry.select_prev_state;
14978 self.add_selections_state = entry.add_selections_state;
14979 }
14980 }
14981
14982 pub fn expand_excerpts(
14983 &mut self,
14984 action: &ExpandExcerpts,
14985 _: &mut Window,
14986 cx: &mut Context<Self>,
14987 ) {
14988 self.expand_excerpts_for_direction(action.lines, ExpandExcerptDirection::UpAndDown, cx)
14989 }
14990
14991 pub fn expand_excerpts_down(
14992 &mut self,
14993 action: &ExpandExcerptsDown,
14994 _: &mut Window,
14995 cx: &mut Context<Self>,
14996 ) {
14997 self.expand_excerpts_for_direction(action.lines, ExpandExcerptDirection::Down, cx)
14998 }
14999
15000 pub fn expand_excerpts_up(
15001 &mut self,
15002 action: &ExpandExcerptsUp,
15003 _: &mut Window,
15004 cx: &mut Context<Self>,
15005 ) {
15006 self.expand_excerpts_for_direction(action.lines, ExpandExcerptDirection::Up, cx)
15007 }
15008
15009 pub fn expand_excerpts_for_direction(
15010 &mut self,
15011 lines: u32,
15012 direction: ExpandExcerptDirection,
15013
15014 cx: &mut Context<Self>,
15015 ) {
15016 let selections = self.selections.disjoint_anchors();
15017
15018 let lines = if lines == 0 {
15019 EditorSettings::get_global(cx).expand_excerpt_lines
15020 } else {
15021 lines
15022 };
15023
15024 self.buffer.update(cx, |buffer, cx| {
15025 let snapshot = buffer.snapshot(cx);
15026 let mut excerpt_ids = selections
15027 .iter()
15028 .flat_map(|selection| snapshot.excerpt_ids_for_range(selection.range()))
15029 .collect::<Vec<_>>();
15030 excerpt_ids.sort();
15031 excerpt_ids.dedup();
15032 buffer.expand_excerpts(excerpt_ids, lines, direction, cx)
15033 })
15034 }
15035
15036 pub fn expand_excerpt(
15037 &mut self,
15038 excerpt: ExcerptId,
15039 direction: ExpandExcerptDirection,
15040 window: &mut Window,
15041 cx: &mut Context<Self>,
15042 ) {
15043 let current_scroll_position = self.scroll_position(cx);
15044 let lines_to_expand = EditorSettings::get_global(cx).expand_excerpt_lines;
15045 let mut should_scroll_up = false;
15046
15047 if direction == ExpandExcerptDirection::Down {
15048 let multi_buffer = self.buffer.read(cx);
15049 let snapshot = multi_buffer.snapshot(cx);
15050 if let Some(buffer_id) = snapshot.buffer_id_for_excerpt(excerpt) {
15051 if let Some(buffer) = multi_buffer.buffer(buffer_id) {
15052 if let Some(excerpt_range) = snapshot.buffer_range_for_excerpt(excerpt) {
15053 let buffer_snapshot = buffer.read(cx).snapshot();
15054 let excerpt_end_row =
15055 Point::from_anchor(&excerpt_range.end, &buffer_snapshot).row;
15056 let last_row = buffer_snapshot.max_point().row;
15057 let lines_below = last_row.saturating_sub(excerpt_end_row);
15058 should_scroll_up = lines_below >= lines_to_expand;
15059 }
15060 }
15061 }
15062 }
15063
15064 self.buffer.update(cx, |buffer, cx| {
15065 buffer.expand_excerpts([excerpt], lines_to_expand, direction, cx)
15066 });
15067
15068 if should_scroll_up {
15069 let new_scroll_position =
15070 current_scroll_position + gpui::Point::new(0.0, lines_to_expand as f32);
15071 self.set_scroll_position(new_scroll_position, window, cx);
15072 }
15073 }
15074
15075 pub fn go_to_singleton_buffer_point(
15076 &mut self,
15077 point: Point,
15078 window: &mut Window,
15079 cx: &mut Context<Self>,
15080 ) {
15081 self.go_to_singleton_buffer_range(point..point, window, cx);
15082 }
15083
15084 pub fn go_to_singleton_buffer_range(
15085 &mut self,
15086 range: Range<Point>,
15087 window: &mut Window,
15088 cx: &mut Context<Self>,
15089 ) {
15090 let multibuffer = self.buffer().read(cx);
15091 let Some(buffer) = multibuffer.as_singleton() else {
15092 return;
15093 };
15094 let Some(start) = multibuffer.buffer_point_to_anchor(&buffer, range.start, cx) else {
15095 return;
15096 };
15097 let Some(end) = multibuffer.buffer_point_to_anchor(&buffer, range.end, cx) else {
15098 return;
15099 };
15100 self.change_selections(
15101 SelectionEffects::default().nav_history(true),
15102 window,
15103 cx,
15104 |s| s.select_anchor_ranges([start..end]),
15105 );
15106 }
15107
15108 pub fn go_to_diagnostic(
15109 &mut self,
15110 action: &GoToDiagnostic,
15111 window: &mut Window,
15112 cx: &mut Context<Self>,
15113 ) {
15114 if !self.diagnostics_enabled() {
15115 return;
15116 }
15117 self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
15118 self.go_to_diagnostic_impl(Direction::Next, action.severity, window, cx)
15119 }
15120
15121 pub fn go_to_prev_diagnostic(
15122 &mut self,
15123 action: &GoToPreviousDiagnostic,
15124 window: &mut Window,
15125 cx: &mut Context<Self>,
15126 ) {
15127 if !self.diagnostics_enabled() {
15128 return;
15129 }
15130 self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
15131 self.go_to_diagnostic_impl(Direction::Prev, action.severity, window, cx)
15132 }
15133
15134 pub fn go_to_diagnostic_impl(
15135 &mut self,
15136 direction: Direction,
15137 severity: GoToDiagnosticSeverityFilter,
15138 window: &mut Window,
15139 cx: &mut Context<Self>,
15140 ) {
15141 let buffer = self.buffer.read(cx).snapshot(cx);
15142 let selection = self.selections.newest::<usize>(cx);
15143
15144 let mut active_group_id = None;
15145 if let ActiveDiagnostic::Group(active_group) = &self.active_diagnostics {
15146 if active_group.active_range.start.to_offset(&buffer) == selection.start {
15147 active_group_id = Some(active_group.group_id);
15148 }
15149 }
15150
15151 fn filtered(
15152 snapshot: EditorSnapshot,
15153 severity: GoToDiagnosticSeverityFilter,
15154 diagnostics: impl Iterator<Item = DiagnosticEntry<usize>>,
15155 ) -> impl Iterator<Item = DiagnosticEntry<usize>> {
15156 diagnostics
15157 .filter(move |entry| severity.matches(entry.diagnostic.severity))
15158 .filter(|entry| entry.range.start != entry.range.end)
15159 .filter(|entry| !entry.diagnostic.is_unnecessary)
15160 .filter(move |entry| !snapshot.intersects_fold(entry.range.start))
15161 }
15162
15163 let snapshot = self.snapshot(window, cx);
15164 let before = filtered(
15165 snapshot.clone(),
15166 severity,
15167 buffer
15168 .diagnostics_in_range(0..selection.start)
15169 .filter(|entry| entry.range.start <= selection.start),
15170 );
15171 let after = filtered(
15172 snapshot,
15173 severity,
15174 buffer
15175 .diagnostics_in_range(selection.start..buffer.len())
15176 .filter(|entry| entry.range.start >= selection.start),
15177 );
15178
15179 let mut found: Option<DiagnosticEntry<usize>> = None;
15180 if direction == Direction::Prev {
15181 'outer: for prev_diagnostics in [before.collect::<Vec<_>>(), after.collect::<Vec<_>>()]
15182 {
15183 for diagnostic in prev_diagnostics.into_iter().rev() {
15184 if diagnostic.range.start != selection.start
15185 || active_group_id
15186 .is_some_and(|active| diagnostic.diagnostic.group_id < active)
15187 {
15188 found = Some(diagnostic);
15189 break 'outer;
15190 }
15191 }
15192 }
15193 } else {
15194 for diagnostic in after.chain(before) {
15195 if diagnostic.range.start != selection.start
15196 || active_group_id.is_some_and(|active| diagnostic.diagnostic.group_id > active)
15197 {
15198 found = Some(diagnostic);
15199 break;
15200 }
15201 }
15202 }
15203 let Some(next_diagnostic) = found else {
15204 return;
15205 };
15206
15207 let Some(buffer_id) = buffer.anchor_after(next_diagnostic.range.start).buffer_id else {
15208 return;
15209 };
15210 self.change_selections(Default::default(), window, cx, |s| {
15211 s.select_ranges(vec![
15212 next_diagnostic.range.start..next_diagnostic.range.start,
15213 ])
15214 });
15215 self.activate_diagnostics(buffer_id, next_diagnostic, window, cx);
15216 self.refresh_inline_completion(false, true, window, cx);
15217 }
15218
15219 pub fn go_to_next_hunk(&mut self, _: &GoToHunk, window: &mut Window, cx: &mut Context<Self>) {
15220 self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
15221 let snapshot = self.snapshot(window, cx);
15222 let selection = self.selections.newest::<Point>(cx);
15223 self.go_to_hunk_before_or_after_position(
15224 &snapshot,
15225 selection.head(),
15226 Direction::Next,
15227 window,
15228 cx,
15229 );
15230 }
15231
15232 pub fn go_to_hunk_before_or_after_position(
15233 &mut self,
15234 snapshot: &EditorSnapshot,
15235 position: Point,
15236 direction: Direction,
15237 window: &mut Window,
15238 cx: &mut Context<Editor>,
15239 ) {
15240 let row = if direction == Direction::Next {
15241 self.hunk_after_position(snapshot, position)
15242 .map(|hunk| hunk.row_range.start)
15243 } else {
15244 self.hunk_before_position(snapshot, position)
15245 };
15246
15247 if let Some(row) = row {
15248 let destination = Point::new(row.0, 0);
15249 let autoscroll = Autoscroll::center();
15250
15251 self.unfold_ranges(&[destination..destination], false, false, cx);
15252 self.change_selections(SelectionEffects::scroll(autoscroll), window, cx, |s| {
15253 s.select_ranges([destination..destination]);
15254 });
15255 }
15256 }
15257
15258 fn hunk_after_position(
15259 &mut self,
15260 snapshot: &EditorSnapshot,
15261 position: Point,
15262 ) -> Option<MultiBufferDiffHunk> {
15263 snapshot
15264 .buffer_snapshot
15265 .diff_hunks_in_range(position..snapshot.buffer_snapshot.max_point())
15266 .find(|hunk| hunk.row_range.start.0 > position.row)
15267 .or_else(|| {
15268 snapshot
15269 .buffer_snapshot
15270 .diff_hunks_in_range(Point::zero()..position)
15271 .find(|hunk| hunk.row_range.end.0 < position.row)
15272 })
15273 }
15274
15275 fn go_to_prev_hunk(
15276 &mut self,
15277 _: &GoToPreviousHunk,
15278 window: &mut Window,
15279 cx: &mut Context<Self>,
15280 ) {
15281 self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
15282 let snapshot = self.snapshot(window, cx);
15283 let selection = self.selections.newest::<Point>(cx);
15284 self.go_to_hunk_before_or_after_position(
15285 &snapshot,
15286 selection.head(),
15287 Direction::Prev,
15288 window,
15289 cx,
15290 );
15291 }
15292
15293 fn hunk_before_position(
15294 &mut self,
15295 snapshot: &EditorSnapshot,
15296 position: Point,
15297 ) -> Option<MultiBufferRow> {
15298 snapshot
15299 .buffer_snapshot
15300 .diff_hunk_before(position)
15301 .or_else(|| snapshot.buffer_snapshot.diff_hunk_before(Point::MAX))
15302 }
15303
15304 fn go_to_next_change(
15305 &mut self,
15306 _: &GoToNextChange,
15307 window: &mut Window,
15308 cx: &mut Context<Self>,
15309 ) {
15310 if let Some(selections) = self
15311 .change_list
15312 .next_change(1, Direction::Next)
15313 .map(|s| s.to_vec())
15314 {
15315 self.change_selections(Default::default(), window, cx, |s| {
15316 let map = s.display_map();
15317 s.select_display_ranges(selections.iter().map(|a| {
15318 let point = a.to_display_point(&map);
15319 point..point
15320 }))
15321 })
15322 }
15323 }
15324
15325 fn go_to_previous_change(
15326 &mut self,
15327 _: &GoToPreviousChange,
15328 window: &mut Window,
15329 cx: &mut Context<Self>,
15330 ) {
15331 if let Some(selections) = self
15332 .change_list
15333 .next_change(1, Direction::Prev)
15334 .map(|s| s.to_vec())
15335 {
15336 self.change_selections(Default::default(), window, cx, |s| {
15337 let map = s.display_map();
15338 s.select_display_ranges(selections.iter().map(|a| {
15339 let point = a.to_display_point(&map);
15340 point..point
15341 }))
15342 })
15343 }
15344 }
15345
15346 fn go_to_line<T: 'static>(
15347 &mut self,
15348 position: Anchor,
15349 highlight_color: Option<Hsla>,
15350 window: &mut Window,
15351 cx: &mut Context<Self>,
15352 ) {
15353 let snapshot = self.snapshot(window, cx).display_snapshot;
15354 let position = position.to_point(&snapshot.buffer_snapshot);
15355 let start = snapshot
15356 .buffer_snapshot
15357 .clip_point(Point::new(position.row, 0), Bias::Left);
15358 let end = start + Point::new(1, 0);
15359 let start = snapshot.buffer_snapshot.anchor_before(start);
15360 let end = snapshot.buffer_snapshot.anchor_before(end);
15361
15362 self.highlight_rows::<T>(
15363 start..end,
15364 highlight_color
15365 .unwrap_or_else(|| cx.theme().colors().editor_highlighted_line_background),
15366 Default::default(),
15367 cx,
15368 );
15369
15370 if self.buffer.read(cx).is_singleton() {
15371 self.request_autoscroll(Autoscroll::center().for_anchor(start), cx);
15372 }
15373 }
15374
15375 pub fn go_to_definition(
15376 &mut self,
15377 _: &GoToDefinition,
15378 window: &mut Window,
15379 cx: &mut Context<Self>,
15380 ) -> Task<Result<Navigated>> {
15381 let definition =
15382 self.go_to_definition_of_kind(GotoDefinitionKind::Symbol, false, window, cx);
15383 let fallback_strategy = EditorSettings::get_global(cx).go_to_definition_fallback;
15384 cx.spawn_in(window, async move |editor, cx| {
15385 if definition.await? == Navigated::Yes {
15386 return Ok(Navigated::Yes);
15387 }
15388 match fallback_strategy {
15389 GoToDefinitionFallback::None => Ok(Navigated::No),
15390 GoToDefinitionFallback::FindAllReferences => {
15391 match editor.update_in(cx, |editor, window, cx| {
15392 editor.find_all_references(&FindAllReferences, window, cx)
15393 })? {
15394 Some(references) => references.await,
15395 None => Ok(Navigated::No),
15396 }
15397 }
15398 }
15399 })
15400 }
15401
15402 pub fn go_to_declaration(
15403 &mut self,
15404 _: &GoToDeclaration,
15405 window: &mut Window,
15406 cx: &mut Context<Self>,
15407 ) -> Task<Result<Navigated>> {
15408 self.go_to_definition_of_kind(GotoDefinitionKind::Declaration, false, window, cx)
15409 }
15410
15411 pub fn go_to_declaration_split(
15412 &mut self,
15413 _: &GoToDeclaration,
15414 window: &mut Window,
15415 cx: &mut Context<Self>,
15416 ) -> Task<Result<Navigated>> {
15417 self.go_to_definition_of_kind(GotoDefinitionKind::Declaration, true, window, cx)
15418 }
15419
15420 pub fn go_to_implementation(
15421 &mut self,
15422 _: &GoToImplementation,
15423 window: &mut Window,
15424 cx: &mut Context<Self>,
15425 ) -> Task<Result<Navigated>> {
15426 self.go_to_definition_of_kind(GotoDefinitionKind::Implementation, false, window, cx)
15427 }
15428
15429 pub fn go_to_implementation_split(
15430 &mut self,
15431 _: &GoToImplementationSplit,
15432 window: &mut Window,
15433 cx: &mut Context<Self>,
15434 ) -> Task<Result<Navigated>> {
15435 self.go_to_definition_of_kind(GotoDefinitionKind::Implementation, true, window, cx)
15436 }
15437
15438 pub fn go_to_type_definition(
15439 &mut self,
15440 _: &GoToTypeDefinition,
15441 window: &mut Window,
15442 cx: &mut Context<Self>,
15443 ) -> Task<Result<Navigated>> {
15444 self.go_to_definition_of_kind(GotoDefinitionKind::Type, false, window, cx)
15445 }
15446
15447 pub fn go_to_definition_split(
15448 &mut self,
15449 _: &GoToDefinitionSplit,
15450 window: &mut Window,
15451 cx: &mut Context<Self>,
15452 ) -> Task<Result<Navigated>> {
15453 self.go_to_definition_of_kind(GotoDefinitionKind::Symbol, true, window, cx)
15454 }
15455
15456 pub fn go_to_type_definition_split(
15457 &mut self,
15458 _: &GoToTypeDefinitionSplit,
15459 window: &mut Window,
15460 cx: &mut Context<Self>,
15461 ) -> Task<Result<Navigated>> {
15462 self.go_to_definition_of_kind(GotoDefinitionKind::Type, true, window, cx)
15463 }
15464
15465 fn go_to_definition_of_kind(
15466 &mut self,
15467 kind: GotoDefinitionKind,
15468 split: bool,
15469 window: &mut Window,
15470 cx: &mut Context<Self>,
15471 ) -> Task<Result<Navigated>> {
15472 let Some(provider) = self.semantics_provider.clone() else {
15473 return Task::ready(Ok(Navigated::No));
15474 };
15475 let head = self.selections.newest::<usize>(cx).head();
15476 let buffer = self.buffer.read(cx);
15477 let (buffer, head) = if let Some(text_anchor) = buffer.text_anchor_for_position(head, cx) {
15478 text_anchor
15479 } else {
15480 return Task::ready(Ok(Navigated::No));
15481 };
15482
15483 let Some(definitions) = provider.definitions(&buffer, head, kind, cx) else {
15484 return Task::ready(Ok(Navigated::No));
15485 };
15486
15487 cx.spawn_in(window, async move |editor, cx| {
15488 let definitions = definitions.await?;
15489 let navigated = editor
15490 .update_in(cx, |editor, window, cx| {
15491 editor.navigate_to_hover_links(
15492 Some(kind),
15493 definitions
15494 .into_iter()
15495 .filter(|location| {
15496 hover_links::exclude_link_to_position(&buffer, &head, location, cx)
15497 })
15498 .map(HoverLink::Text)
15499 .collect::<Vec<_>>(),
15500 split,
15501 window,
15502 cx,
15503 )
15504 })?
15505 .await?;
15506 anyhow::Ok(navigated)
15507 })
15508 }
15509
15510 pub fn open_url(&mut self, _: &OpenUrl, window: &mut Window, cx: &mut Context<Self>) {
15511 let selection = self.selections.newest_anchor();
15512 let head = selection.head();
15513 let tail = selection.tail();
15514
15515 let Some((buffer, start_position)) =
15516 self.buffer.read(cx).text_anchor_for_position(head, cx)
15517 else {
15518 return;
15519 };
15520
15521 let end_position = if head != tail {
15522 let Some((_, pos)) = self.buffer.read(cx).text_anchor_for_position(tail, cx) else {
15523 return;
15524 };
15525 Some(pos)
15526 } else {
15527 None
15528 };
15529
15530 let url_finder = cx.spawn_in(window, async move |editor, cx| {
15531 let url = if let Some(end_pos) = end_position {
15532 find_url_from_range(&buffer, start_position..end_pos, cx.clone())
15533 } else {
15534 find_url(&buffer, start_position, cx.clone()).map(|(_, url)| url)
15535 };
15536
15537 if let Some(url) = url {
15538 editor.update(cx, |_, cx| {
15539 cx.open_url(&url);
15540 })
15541 } else {
15542 Ok(())
15543 }
15544 });
15545
15546 url_finder.detach();
15547 }
15548
15549 pub fn open_selected_filename(
15550 &mut self,
15551 _: &OpenSelectedFilename,
15552 window: &mut Window,
15553 cx: &mut Context<Self>,
15554 ) {
15555 let Some(workspace) = self.workspace() else {
15556 return;
15557 };
15558
15559 let position = self.selections.newest_anchor().head();
15560
15561 let Some((buffer, buffer_position)) =
15562 self.buffer.read(cx).text_anchor_for_position(position, cx)
15563 else {
15564 return;
15565 };
15566
15567 let project = self.project.clone();
15568
15569 cx.spawn_in(window, async move |_, cx| {
15570 let result = find_file(&buffer, project, buffer_position, cx).await;
15571
15572 if let Some((_, path)) = result {
15573 workspace
15574 .update_in(cx, |workspace, window, cx| {
15575 workspace.open_resolved_path(path, window, cx)
15576 })?
15577 .await?;
15578 }
15579 anyhow::Ok(())
15580 })
15581 .detach();
15582 }
15583
15584 pub(crate) fn navigate_to_hover_links(
15585 &mut self,
15586 kind: Option<GotoDefinitionKind>,
15587 mut definitions: Vec<HoverLink>,
15588 split: bool,
15589 window: &mut Window,
15590 cx: &mut Context<Editor>,
15591 ) -> Task<Result<Navigated>> {
15592 // If there is one definition, just open it directly
15593 if definitions.len() == 1 {
15594 let definition = definitions.pop().unwrap();
15595
15596 enum TargetTaskResult {
15597 Location(Option<Location>),
15598 AlreadyNavigated,
15599 }
15600
15601 let target_task = match definition {
15602 HoverLink::Text(link) => {
15603 Task::ready(anyhow::Ok(TargetTaskResult::Location(Some(link.target))))
15604 }
15605 HoverLink::InlayHint(lsp_location, server_id) => {
15606 let computation =
15607 self.compute_target_location(lsp_location, server_id, window, cx);
15608 cx.background_spawn(async move {
15609 let location = computation.await?;
15610 Ok(TargetTaskResult::Location(location))
15611 })
15612 }
15613 HoverLink::Url(url) => {
15614 cx.open_url(&url);
15615 Task::ready(Ok(TargetTaskResult::AlreadyNavigated))
15616 }
15617 HoverLink::File(path) => {
15618 if let Some(workspace) = self.workspace() {
15619 cx.spawn_in(window, async move |_, cx| {
15620 workspace
15621 .update_in(cx, |workspace, window, cx| {
15622 workspace.open_resolved_path(path, window, cx)
15623 })?
15624 .await
15625 .map(|_| TargetTaskResult::AlreadyNavigated)
15626 })
15627 } else {
15628 Task::ready(Ok(TargetTaskResult::Location(None)))
15629 }
15630 }
15631 };
15632 cx.spawn_in(window, async move |editor, cx| {
15633 let target = match target_task.await.context("target resolution task")? {
15634 TargetTaskResult::AlreadyNavigated => return Ok(Navigated::Yes),
15635 TargetTaskResult::Location(None) => return Ok(Navigated::No),
15636 TargetTaskResult::Location(Some(target)) => target,
15637 };
15638
15639 editor.update_in(cx, |editor, window, cx| {
15640 let Some(workspace) = editor.workspace() else {
15641 return Navigated::No;
15642 };
15643 let pane = workspace.read(cx).active_pane().clone();
15644
15645 let range = target.range.to_point(target.buffer.read(cx));
15646 let range = editor.range_for_match(&range);
15647 let range = collapse_multiline_range(range);
15648
15649 if !split
15650 && Some(&target.buffer) == editor.buffer.read(cx).as_singleton().as_ref()
15651 {
15652 editor.go_to_singleton_buffer_range(range.clone(), window, cx);
15653 } else {
15654 window.defer(cx, move |window, cx| {
15655 let target_editor: Entity<Self> =
15656 workspace.update(cx, |workspace, cx| {
15657 let pane = if split {
15658 workspace.adjacent_pane(window, cx)
15659 } else {
15660 workspace.active_pane().clone()
15661 };
15662
15663 workspace.open_project_item(
15664 pane,
15665 target.buffer.clone(),
15666 true,
15667 true,
15668 window,
15669 cx,
15670 )
15671 });
15672 target_editor.update(cx, |target_editor, cx| {
15673 // When selecting a definition in a different buffer, disable the nav history
15674 // to avoid creating a history entry at the previous cursor location.
15675 pane.update(cx, |pane, _| pane.disable_history());
15676 target_editor.go_to_singleton_buffer_range(range, window, cx);
15677 pane.update(cx, |pane, _| pane.enable_history());
15678 });
15679 });
15680 }
15681 Navigated::Yes
15682 })
15683 })
15684 } else if !definitions.is_empty() {
15685 cx.spawn_in(window, async move |editor, cx| {
15686 let (title, location_tasks, workspace) = editor
15687 .update_in(cx, |editor, window, cx| {
15688 let tab_kind = match kind {
15689 Some(GotoDefinitionKind::Implementation) => "Implementations",
15690 _ => "Definitions",
15691 };
15692 let title = definitions
15693 .iter()
15694 .find_map(|definition| match definition {
15695 HoverLink::Text(link) => link.origin.as_ref().map(|origin| {
15696 let buffer = origin.buffer.read(cx);
15697 format!(
15698 "{} for {}",
15699 tab_kind,
15700 buffer
15701 .text_for_range(origin.range.clone())
15702 .collect::<String>()
15703 )
15704 }),
15705 HoverLink::InlayHint(_, _) => None,
15706 HoverLink::Url(_) => None,
15707 HoverLink::File(_) => None,
15708 })
15709 .unwrap_or(tab_kind.to_string());
15710 let location_tasks = definitions
15711 .into_iter()
15712 .map(|definition| match definition {
15713 HoverLink::Text(link) => Task::ready(Ok(Some(link.target))),
15714 HoverLink::InlayHint(lsp_location, server_id) => editor
15715 .compute_target_location(lsp_location, server_id, window, cx),
15716 HoverLink::Url(_) => Task::ready(Ok(None)),
15717 HoverLink::File(_) => Task::ready(Ok(None)),
15718 })
15719 .collect::<Vec<_>>();
15720 (title, location_tasks, editor.workspace().clone())
15721 })
15722 .context("location tasks preparation")?;
15723
15724 let locations: Vec<Location> = future::join_all(location_tasks)
15725 .await
15726 .into_iter()
15727 .filter_map(|location| location.transpose())
15728 .collect::<Result<_>>()
15729 .context("location tasks")?;
15730
15731 if locations.is_empty() {
15732 return Ok(Navigated::No);
15733 }
15734
15735 let Some(workspace) = workspace else {
15736 return Ok(Navigated::No);
15737 };
15738
15739 let opened = workspace
15740 .update_in(cx, |workspace, window, cx| {
15741 Self::open_locations_in_multibuffer(
15742 workspace,
15743 locations,
15744 title,
15745 split,
15746 MultibufferSelectionMode::First,
15747 window,
15748 cx,
15749 )
15750 })
15751 .ok();
15752
15753 anyhow::Ok(Navigated::from_bool(opened.is_some()))
15754 })
15755 } else {
15756 Task::ready(Ok(Navigated::No))
15757 }
15758 }
15759
15760 fn compute_target_location(
15761 &self,
15762 lsp_location: lsp::Location,
15763 server_id: LanguageServerId,
15764 window: &mut Window,
15765 cx: &mut Context<Self>,
15766 ) -> Task<anyhow::Result<Option<Location>>> {
15767 let Some(project) = self.project.clone() else {
15768 return Task::ready(Ok(None));
15769 };
15770
15771 cx.spawn_in(window, async move |editor, cx| {
15772 let location_task = editor.update(cx, |_, cx| {
15773 project.update(cx, |project, cx| {
15774 let language_server_name = project
15775 .language_server_statuses(cx)
15776 .find(|(id, _)| server_id == *id)
15777 .map(|(_, status)| LanguageServerName::from(status.name.as_str()));
15778 language_server_name.map(|language_server_name| {
15779 project.open_local_buffer_via_lsp(
15780 lsp_location.uri.clone(),
15781 server_id,
15782 language_server_name,
15783 cx,
15784 )
15785 })
15786 })
15787 })?;
15788 let location = match location_task {
15789 Some(task) => Some({
15790 let target_buffer_handle = task.await.context("open local buffer")?;
15791 let range = target_buffer_handle.read_with(cx, |target_buffer, _| {
15792 let target_start = target_buffer
15793 .clip_point_utf16(point_from_lsp(lsp_location.range.start), Bias::Left);
15794 let target_end = target_buffer
15795 .clip_point_utf16(point_from_lsp(lsp_location.range.end), Bias::Left);
15796 target_buffer.anchor_after(target_start)
15797 ..target_buffer.anchor_before(target_end)
15798 })?;
15799 Location {
15800 buffer: target_buffer_handle,
15801 range,
15802 }
15803 }),
15804 None => None,
15805 };
15806 Ok(location)
15807 })
15808 }
15809
15810 pub fn find_all_references(
15811 &mut self,
15812 _: &FindAllReferences,
15813 window: &mut Window,
15814 cx: &mut Context<Self>,
15815 ) -> Option<Task<Result<Navigated>>> {
15816 let selection = self.selections.newest::<usize>(cx);
15817 let multi_buffer = self.buffer.read(cx);
15818 let head = selection.head();
15819
15820 let multi_buffer_snapshot = multi_buffer.snapshot(cx);
15821 let head_anchor = multi_buffer_snapshot.anchor_at(
15822 head,
15823 if head < selection.tail() {
15824 Bias::Right
15825 } else {
15826 Bias::Left
15827 },
15828 );
15829
15830 match self
15831 .find_all_references_task_sources
15832 .binary_search_by(|anchor| anchor.cmp(&head_anchor, &multi_buffer_snapshot))
15833 {
15834 Ok(_) => {
15835 log::info!(
15836 "Ignoring repeated FindAllReferences invocation with the position of already running task"
15837 );
15838 return None;
15839 }
15840 Err(i) => {
15841 self.find_all_references_task_sources.insert(i, head_anchor);
15842 }
15843 }
15844
15845 let (buffer, head) = multi_buffer.text_anchor_for_position(head, cx)?;
15846 let workspace = self.workspace()?;
15847 let project = workspace.read(cx).project().clone();
15848 let references = project.update(cx, |project, cx| project.references(&buffer, head, cx));
15849 Some(cx.spawn_in(window, async move |editor, cx| {
15850 let _cleanup = cx.on_drop(&editor, move |editor, _| {
15851 if let Ok(i) = editor
15852 .find_all_references_task_sources
15853 .binary_search_by(|anchor| anchor.cmp(&head_anchor, &multi_buffer_snapshot))
15854 {
15855 editor.find_all_references_task_sources.remove(i);
15856 }
15857 });
15858
15859 let locations = references.await?;
15860 if locations.is_empty() {
15861 return anyhow::Ok(Navigated::No);
15862 }
15863
15864 workspace.update_in(cx, |workspace, window, cx| {
15865 let title = locations
15866 .first()
15867 .as_ref()
15868 .map(|location| {
15869 let buffer = location.buffer.read(cx);
15870 format!(
15871 "References to `{}`",
15872 buffer
15873 .text_for_range(location.range.clone())
15874 .collect::<String>()
15875 )
15876 })
15877 .unwrap();
15878 Self::open_locations_in_multibuffer(
15879 workspace,
15880 locations,
15881 title,
15882 false,
15883 MultibufferSelectionMode::First,
15884 window,
15885 cx,
15886 );
15887 Navigated::Yes
15888 })
15889 }))
15890 }
15891
15892 /// Opens a multibuffer with the given project locations in it
15893 pub fn open_locations_in_multibuffer(
15894 workspace: &mut Workspace,
15895 mut locations: Vec<Location>,
15896 title: String,
15897 split: bool,
15898 multibuffer_selection_mode: MultibufferSelectionMode,
15899 window: &mut Window,
15900 cx: &mut Context<Workspace>,
15901 ) {
15902 if locations.is_empty() {
15903 log::error!("bug: open_locations_in_multibuffer called with empty list of locations");
15904 return;
15905 }
15906
15907 // If there are multiple definitions, open them in a multibuffer
15908 locations.sort_by_key(|location| location.buffer.read(cx).remote_id());
15909 let mut locations = locations.into_iter().peekable();
15910 let mut ranges: Vec<Range<Anchor>> = Vec::new();
15911 let capability = workspace.project().read(cx).capability();
15912
15913 let excerpt_buffer = cx.new(|cx| {
15914 let mut multibuffer = MultiBuffer::new(capability);
15915 while let Some(location) = locations.next() {
15916 let buffer = location.buffer.read(cx);
15917 let mut ranges_for_buffer = Vec::new();
15918 let range = location.range.to_point(buffer);
15919 ranges_for_buffer.push(range.clone());
15920
15921 while let Some(next_location) = locations.peek() {
15922 if next_location.buffer == location.buffer {
15923 ranges_for_buffer.push(next_location.range.to_point(buffer));
15924 locations.next();
15925 } else {
15926 break;
15927 }
15928 }
15929
15930 ranges_for_buffer.sort_by_key(|range| (range.start, Reverse(range.end)));
15931 let (new_ranges, _) = multibuffer.set_excerpts_for_path(
15932 PathKey::for_buffer(&location.buffer, cx),
15933 location.buffer.clone(),
15934 ranges_for_buffer,
15935 DEFAULT_MULTIBUFFER_CONTEXT,
15936 cx,
15937 );
15938 ranges.extend(new_ranges)
15939 }
15940
15941 multibuffer.with_title(title)
15942 });
15943
15944 let editor = cx.new(|cx| {
15945 Editor::for_multibuffer(
15946 excerpt_buffer,
15947 Some(workspace.project().clone()),
15948 window,
15949 cx,
15950 )
15951 });
15952 editor.update(cx, |editor, cx| {
15953 match multibuffer_selection_mode {
15954 MultibufferSelectionMode::First => {
15955 if let Some(first_range) = ranges.first() {
15956 editor.change_selections(
15957 SelectionEffects::no_scroll(),
15958 window,
15959 cx,
15960 |selections| {
15961 selections.clear_disjoint();
15962 selections
15963 .select_anchor_ranges(std::iter::once(first_range.clone()));
15964 },
15965 );
15966 }
15967 editor.highlight_background::<Self>(
15968 &ranges,
15969 |theme| theme.colors().editor_highlighted_line_background,
15970 cx,
15971 );
15972 }
15973 MultibufferSelectionMode::All => {
15974 editor.change_selections(
15975 SelectionEffects::no_scroll(),
15976 window,
15977 cx,
15978 |selections| {
15979 selections.clear_disjoint();
15980 selections.select_anchor_ranges(ranges);
15981 },
15982 );
15983 }
15984 }
15985 editor.register_buffers_with_language_servers(cx);
15986 });
15987
15988 let item = Box::new(editor);
15989 let item_id = item.item_id();
15990
15991 if split {
15992 workspace.split_item(SplitDirection::Right, item.clone(), window, cx);
15993 } else {
15994 if PreviewTabsSettings::get_global(cx).enable_preview_from_code_navigation {
15995 let (preview_item_id, preview_item_idx) =
15996 workspace.active_pane().read_with(cx, |pane, _| {
15997 (pane.preview_item_id(), pane.preview_item_idx())
15998 });
15999
16000 workspace.add_item_to_active_pane(item.clone(), preview_item_idx, true, window, cx);
16001
16002 if let Some(preview_item_id) = preview_item_id {
16003 workspace.active_pane().update(cx, |pane, cx| {
16004 pane.remove_item(preview_item_id, false, false, window, cx);
16005 });
16006 }
16007 } else {
16008 workspace.add_item_to_active_pane(item.clone(), None, true, window, cx);
16009 }
16010 }
16011 workspace.active_pane().update(cx, |pane, cx| {
16012 pane.set_preview_item_id(Some(item_id), cx);
16013 });
16014 }
16015
16016 pub fn rename(
16017 &mut self,
16018 _: &Rename,
16019 window: &mut Window,
16020 cx: &mut Context<Self>,
16021 ) -> Option<Task<Result<()>>> {
16022 use language::ToOffset as _;
16023
16024 let provider = self.semantics_provider.clone()?;
16025 let selection = self.selections.newest_anchor().clone();
16026 let (cursor_buffer, cursor_buffer_position) = self
16027 .buffer
16028 .read(cx)
16029 .text_anchor_for_position(selection.head(), cx)?;
16030 let (tail_buffer, cursor_buffer_position_end) = self
16031 .buffer
16032 .read(cx)
16033 .text_anchor_for_position(selection.tail(), cx)?;
16034 if tail_buffer != cursor_buffer {
16035 return None;
16036 }
16037
16038 let snapshot = cursor_buffer.read(cx).snapshot();
16039 let cursor_buffer_offset = cursor_buffer_position.to_offset(&snapshot);
16040 let cursor_buffer_offset_end = cursor_buffer_position_end.to_offset(&snapshot);
16041 let prepare_rename = provider
16042 .range_for_rename(&cursor_buffer, cursor_buffer_position, cx)
16043 .unwrap_or_else(|| Task::ready(Ok(None)));
16044 drop(snapshot);
16045
16046 Some(cx.spawn_in(window, async move |this, cx| {
16047 let rename_range = if let Some(range) = prepare_rename.await? {
16048 Some(range)
16049 } else {
16050 this.update(cx, |this, cx| {
16051 let buffer = this.buffer.read(cx).snapshot(cx);
16052 let mut buffer_highlights = this
16053 .document_highlights_for_position(selection.head(), &buffer)
16054 .filter(|highlight| {
16055 highlight.start.excerpt_id == selection.head().excerpt_id
16056 && highlight.end.excerpt_id == selection.head().excerpt_id
16057 });
16058 buffer_highlights
16059 .next()
16060 .map(|highlight| highlight.start.text_anchor..highlight.end.text_anchor)
16061 })?
16062 };
16063 if let Some(rename_range) = rename_range {
16064 this.update_in(cx, |this, window, cx| {
16065 let snapshot = cursor_buffer.read(cx).snapshot();
16066 let rename_buffer_range = rename_range.to_offset(&snapshot);
16067 let cursor_offset_in_rename_range =
16068 cursor_buffer_offset.saturating_sub(rename_buffer_range.start);
16069 let cursor_offset_in_rename_range_end =
16070 cursor_buffer_offset_end.saturating_sub(rename_buffer_range.start);
16071
16072 this.take_rename(false, window, cx);
16073 let buffer = this.buffer.read(cx).read(cx);
16074 let cursor_offset = selection.head().to_offset(&buffer);
16075 let rename_start = cursor_offset.saturating_sub(cursor_offset_in_rename_range);
16076 let rename_end = rename_start + rename_buffer_range.len();
16077 let range = buffer.anchor_before(rename_start)..buffer.anchor_after(rename_end);
16078 let mut old_highlight_id = None;
16079 let old_name: Arc<str> = buffer
16080 .chunks(rename_start..rename_end, true)
16081 .map(|chunk| {
16082 if old_highlight_id.is_none() {
16083 old_highlight_id = chunk.syntax_highlight_id;
16084 }
16085 chunk.text
16086 })
16087 .collect::<String>()
16088 .into();
16089
16090 drop(buffer);
16091
16092 // Position the selection in the rename editor so that it matches the current selection.
16093 this.show_local_selections = false;
16094 let rename_editor = cx.new(|cx| {
16095 let mut editor = Editor::single_line(window, cx);
16096 editor.buffer.update(cx, |buffer, cx| {
16097 buffer.edit([(0..0, old_name.clone())], None, cx)
16098 });
16099 let rename_selection_range = match cursor_offset_in_rename_range
16100 .cmp(&cursor_offset_in_rename_range_end)
16101 {
16102 Ordering::Equal => {
16103 editor.select_all(&SelectAll, window, cx);
16104 return editor;
16105 }
16106 Ordering::Less => {
16107 cursor_offset_in_rename_range..cursor_offset_in_rename_range_end
16108 }
16109 Ordering::Greater => {
16110 cursor_offset_in_rename_range_end..cursor_offset_in_rename_range
16111 }
16112 };
16113 if rename_selection_range.end > old_name.len() {
16114 editor.select_all(&SelectAll, window, cx);
16115 } else {
16116 editor.change_selections(Default::default(), window, cx, |s| {
16117 s.select_ranges([rename_selection_range]);
16118 });
16119 }
16120 editor
16121 });
16122 cx.subscribe(&rename_editor, |_, _, e: &EditorEvent, cx| {
16123 if e == &EditorEvent::Focused {
16124 cx.emit(EditorEvent::FocusedIn)
16125 }
16126 })
16127 .detach();
16128
16129 let write_highlights =
16130 this.clear_background_highlights::<DocumentHighlightWrite>(cx);
16131 let read_highlights =
16132 this.clear_background_highlights::<DocumentHighlightRead>(cx);
16133 let ranges = write_highlights
16134 .iter()
16135 .flat_map(|(_, ranges)| ranges.iter())
16136 .chain(read_highlights.iter().flat_map(|(_, ranges)| ranges.iter()))
16137 .cloned()
16138 .collect();
16139
16140 this.highlight_text::<Rename>(
16141 ranges,
16142 HighlightStyle {
16143 fade_out: Some(0.6),
16144 ..Default::default()
16145 },
16146 cx,
16147 );
16148 let rename_focus_handle = rename_editor.focus_handle(cx);
16149 window.focus(&rename_focus_handle);
16150 let block_id = this.insert_blocks(
16151 [BlockProperties {
16152 style: BlockStyle::Flex,
16153 placement: BlockPlacement::Below(range.start),
16154 height: Some(1),
16155 render: Arc::new({
16156 let rename_editor = rename_editor.clone();
16157 move |cx: &mut BlockContext| {
16158 let mut text_style = cx.editor_style.text.clone();
16159 if let Some(highlight_style) = old_highlight_id
16160 .and_then(|h| h.style(&cx.editor_style.syntax))
16161 {
16162 text_style = text_style.highlight(highlight_style);
16163 }
16164 div()
16165 .block_mouse_except_scroll()
16166 .pl(cx.anchor_x)
16167 .child(EditorElement::new(
16168 &rename_editor,
16169 EditorStyle {
16170 background: cx.theme().system().transparent,
16171 local_player: cx.editor_style.local_player,
16172 text: text_style,
16173 scrollbar_width: cx.editor_style.scrollbar_width,
16174 syntax: cx.editor_style.syntax.clone(),
16175 status: cx.editor_style.status.clone(),
16176 inlay_hints_style: HighlightStyle {
16177 font_weight: Some(FontWeight::BOLD),
16178 ..make_inlay_hints_style(cx.app)
16179 },
16180 inline_completion_styles: make_suggestion_styles(
16181 cx.app,
16182 ),
16183 ..EditorStyle::default()
16184 },
16185 ))
16186 .into_any_element()
16187 }
16188 }),
16189 priority: 0,
16190 }],
16191 Some(Autoscroll::fit()),
16192 cx,
16193 )[0];
16194 this.pending_rename = Some(RenameState {
16195 range,
16196 old_name,
16197 editor: rename_editor,
16198 block_id,
16199 });
16200 })?;
16201 }
16202
16203 Ok(())
16204 }))
16205 }
16206
16207 pub fn confirm_rename(
16208 &mut self,
16209 _: &ConfirmRename,
16210 window: &mut Window,
16211 cx: &mut Context<Self>,
16212 ) -> Option<Task<Result<()>>> {
16213 let rename = self.take_rename(false, window, cx)?;
16214 let workspace = self.workspace()?.downgrade();
16215 let (buffer, start) = self
16216 .buffer
16217 .read(cx)
16218 .text_anchor_for_position(rename.range.start, cx)?;
16219 let (end_buffer, _) = self
16220 .buffer
16221 .read(cx)
16222 .text_anchor_for_position(rename.range.end, cx)?;
16223 if buffer != end_buffer {
16224 return None;
16225 }
16226
16227 let old_name = rename.old_name;
16228 let new_name = rename.editor.read(cx).text(cx);
16229
16230 let rename = self.semantics_provider.as_ref()?.perform_rename(
16231 &buffer,
16232 start,
16233 new_name.clone(),
16234 cx,
16235 )?;
16236
16237 Some(cx.spawn_in(window, async move |editor, cx| {
16238 let project_transaction = rename.await?;
16239 Self::open_project_transaction(
16240 &editor,
16241 workspace,
16242 project_transaction,
16243 format!("Rename: {} → {}", old_name, new_name),
16244 cx,
16245 )
16246 .await?;
16247
16248 editor.update(cx, |editor, cx| {
16249 editor.refresh_document_highlights(cx);
16250 })?;
16251 Ok(())
16252 }))
16253 }
16254
16255 fn take_rename(
16256 &mut self,
16257 moving_cursor: bool,
16258 window: &mut Window,
16259 cx: &mut Context<Self>,
16260 ) -> Option<RenameState> {
16261 let rename = self.pending_rename.take()?;
16262 if rename.editor.focus_handle(cx).is_focused(window) {
16263 window.focus(&self.focus_handle);
16264 }
16265
16266 self.remove_blocks(
16267 [rename.block_id].into_iter().collect(),
16268 Some(Autoscroll::fit()),
16269 cx,
16270 );
16271 self.clear_highlights::<Rename>(cx);
16272 self.show_local_selections = true;
16273
16274 if moving_cursor {
16275 let cursor_in_rename_editor = rename.editor.update(cx, |editor, cx| {
16276 editor.selections.newest::<usize>(cx).head()
16277 });
16278
16279 // Update the selection to match the position of the selection inside
16280 // the rename editor.
16281 let snapshot = self.buffer.read(cx).read(cx);
16282 let rename_range = rename.range.to_offset(&snapshot);
16283 let cursor_in_editor = snapshot
16284 .clip_offset(rename_range.start + cursor_in_rename_editor, Bias::Left)
16285 .min(rename_range.end);
16286 drop(snapshot);
16287
16288 self.change_selections(SelectionEffects::no_scroll(), window, cx, |s| {
16289 s.select_ranges(vec![cursor_in_editor..cursor_in_editor])
16290 });
16291 } else {
16292 self.refresh_document_highlights(cx);
16293 }
16294
16295 Some(rename)
16296 }
16297
16298 pub fn pending_rename(&self) -> Option<&RenameState> {
16299 self.pending_rename.as_ref()
16300 }
16301
16302 fn format(
16303 &mut self,
16304 _: &Format,
16305 window: &mut Window,
16306 cx: &mut Context<Self>,
16307 ) -> Option<Task<Result<()>>> {
16308 self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
16309
16310 let project = match &self.project {
16311 Some(project) => project.clone(),
16312 None => return None,
16313 };
16314
16315 Some(self.perform_format(
16316 project,
16317 FormatTrigger::Manual,
16318 FormatTarget::Buffers(self.buffer.read(cx).all_buffers()),
16319 window,
16320 cx,
16321 ))
16322 }
16323
16324 fn format_selections(
16325 &mut self,
16326 _: &FormatSelections,
16327 window: &mut Window,
16328 cx: &mut Context<Self>,
16329 ) -> Option<Task<Result<()>>> {
16330 self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
16331
16332 let project = match &self.project {
16333 Some(project) => project.clone(),
16334 None => return None,
16335 };
16336
16337 let ranges = self
16338 .selections
16339 .all_adjusted(cx)
16340 .into_iter()
16341 .map(|selection| selection.range())
16342 .collect_vec();
16343
16344 Some(self.perform_format(
16345 project,
16346 FormatTrigger::Manual,
16347 FormatTarget::Ranges(ranges),
16348 window,
16349 cx,
16350 ))
16351 }
16352
16353 fn perform_format(
16354 &mut self,
16355 project: Entity<Project>,
16356 trigger: FormatTrigger,
16357 target: FormatTarget,
16358 window: &mut Window,
16359 cx: &mut Context<Self>,
16360 ) -> Task<Result<()>> {
16361 let buffer = self.buffer.clone();
16362 let (buffers, target) = match target {
16363 FormatTarget::Buffers(buffers) => (buffers, LspFormatTarget::Buffers),
16364 FormatTarget::Ranges(selection_ranges) => {
16365 let multi_buffer = buffer.read(cx);
16366 let snapshot = multi_buffer.read(cx);
16367 let mut buffers = HashSet::default();
16368 let mut buffer_id_to_ranges: BTreeMap<BufferId, Vec<Range<text::Anchor>>> =
16369 BTreeMap::new();
16370 for selection_range in selection_ranges {
16371 for (buffer, buffer_range, _) in
16372 snapshot.range_to_buffer_ranges(selection_range)
16373 {
16374 let buffer_id = buffer.remote_id();
16375 let start = buffer.anchor_before(buffer_range.start);
16376 let end = buffer.anchor_after(buffer_range.end);
16377 buffers.insert(multi_buffer.buffer(buffer_id).unwrap());
16378 buffer_id_to_ranges
16379 .entry(buffer_id)
16380 .and_modify(|buffer_ranges| buffer_ranges.push(start..end))
16381 .or_insert_with(|| vec![start..end]);
16382 }
16383 }
16384 (buffers, LspFormatTarget::Ranges(buffer_id_to_ranges))
16385 }
16386 };
16387
16388 let transaction_id_prev = buffer.read(cx).last_transaction_id(cx);
16389 let selections_prev = transaction_id_prev
16390 .and_then(|transaction_id_prev| {
16391 // default to selections as they were after the last edit, if we have them,
16392 // instead of how they are now.
16393 // This will make it so that editing, moving somewhere else, formatting, then undoing the format
16394 // will take you back to where you made the last edit, instead of staying where you scrolled
16395 self.selection_history
16396 .transaction(transaction_id_prev)
16397 .map(|t| t.0.clone())
16398 })
16399 .unwrap_or_else(|| {
16400 log::info!("Failed to determine selections from before format. Falling back to selections when format was initiated");
16401 self.selections.disjoint_anchors()
16402 });
16403
16404 let mut timeout = cx.background_executor().timer(FORMAT_TIMEOUT).fuse();
16405 let format = project.update(cx, |project, cx| {
16406 project.format(buffers, target, true, trigger, cx)
16407 });
16408
16409 cx.spawn_in(window, async move |editor, cx| {
16410 let transaction = futures::select_biased! {
16411 transaction = format.log_err().fuse() => transaction,
16412 () = timeout => {
16413 log::warn!("timed out waiting for formatting");
16414 None
16415 }
16416 };
16417
16418 buffer
16419 .update(cx, |buffer, cx| {
16420 if let Some(transaction) = transaction {
16421 if !buffer.is_singleton() {
16422 buffer.push_transaction(&transaction.0, cx);
16423 }
16424 }
16425 cx.notify();
16426 })
16427 .ok();
16428
16429 if let Some(transaction_id_now) =
16430 buffer.read_with(cx, |b, cx| b.last_transaction_id(cx))?
16431 {
16432 let has_new_transaction = transaction_id_prev != Some(transaction_id_now);
16433 if has_new_transaction {
16434 _ = editor.update(cx, |editor, _| {
16435 editor
16436 .selection_history
16437 .insert_transaction(transaction_id_now, selections_prev);
16438 });
16439 }
16440 }
16441
16442 Ok(())
16443 })
16444 }
16445
16446 fn organize_imports(
16447 &mut self,
16448 _: &OrganizeImports,
16449 window: &mut Window,
16450 cx: &mut Context<Self>,
16451 ) -> Option<Task<Result<()>>> {
16452 self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
16453 let project = match &self.project {
16454 Some(project) => project.clone(),
16455 None => return None,
16456 };
16457 Some(self.perform_code_action_kind(
16458 project,
16459 CodeActionKind::SOURCE_ORGANIZE_IMPORTS,
16460 window,
16461 cx,
16462 ))
16463 }
16464
16465 fn perform_code_action_kind(
16466 &mut self,
16467 project: Entity<Project>,
16468 kind: CodeActionKind,
16469 window: &mut Window,
16470 cx: &mut Context<Self>,
16471 ) -> Task<Result<()>> {
16472 let buffer = self.buffer.clone();
16473 let buffers = buffer.read(cx).all_buffers();
16474 let mut timeout = cx.background_executor().timer(CODE_ACTION_TIMEOUT).fuse();
16475 let apply_action = project.update(cx, |project, cx| {
16476 project.apply_code_action_kind(buffers, kind, true, cx)
16477 });
16478 cx.spawn_in(window, async move |_, cx| {
16479 let transaction = futures::select_biased! {
16480 () = timeout => {
16481 log::warn!("timed out waiting for executing code action");
16482 None
16483 }
16484 transaction = apply_action.log_err().fuse() => transaction,
16485 };
16486 buffer
16487 .update(cx, |buffer, cx| {
16488 // check if we need this
16489 if let Some(transaction) = transaction {
16490 if !buffer.is_singleton() {
16491 buffer.push_transaction(&transaction.0, cx);
16492 }
16493 }
16494 cx.notify();
16495 })
16496 .ok();
16497 Ok(())
16498 })
16499 }
16500
16501 pub fn restart_language_server(
16502 &mut self,
16503 _: &RestartLanguageServer,
16504 _: &mut Window,
16505 cx: &mut Context<Self>,
16506 ) {
16507 if let Some(project) = self.project.clone() {
16508 self.buffer.update(cx, |multi_buffer, cx| {
16509 project.update(cx, |project, cx| {
16510 project.restart_language_servers_for_buffers(
16511 multi_buffer.all_buffers().into_iter().collect(),
16512 HashSet::default(),
16513 cx,
16514 );
16515 });
16516 })
16517 }
16518 }
16519
16520 pub fn stop_language_server(
16521 &mut self,
16522 _: &StopLanguageServer,
16523 _: &mut Window,
16524 cx: &mut Context<Self>,
16525 ) {
16526 if let Some(project) = self.project.clone() {
16527 self.buffer.update(cx, |multi_buffer, cx| {
16528 project.update(cx, |project, cx| {
16529 project.stop_language_servers_for_buffers(
16530 multi_buffer.all_buffers().into_iter().collect(),
16531 HashSet::default(),
16532 cx,
16533 );
16534 cx.emit(project::Event::RefreshInlayHints);
16535 });
16536 });
16537 }
16538 }
16539
16540 fn cancel_language_server_work(
16541 workspace: &mut Workspace,
16542 _: &actions::CancelLanguageServerWork,
16543 _: &mut Window,
16544 cx: &mut Context<Workspace>,
16545 ) {
16546 let project = workspace.project();
16547 let buffers = workspace
16548 .active_item(cx)
16549 .and_then(|item| item.act_as::<Editor>(cx))
16550 .map_or(HashSet::default(), |editor| {
16551 editor.read(cx).buffer.read(cx).all_buffers()
16552 });
16553 project.update(cx, |project, cx| {
16554 project.cancel_language_server_work_for_buffers(buffers, cx);
16555 });
16556 }
16557
16558 fn show_character_palette(
16559 &mut self,
16560 _: &ShowCharacterPalette,
16561 window: &mut Window,
16562 _: &mut Context<Self>,
16563 ) {
16564 window.show_character_palette();
16565 }
16566
16567 fn refresh_active_diagnostics(&mut self, cx: &mut Context<Editor>) {
16568 if !self.diagnostics_enabled() {
16569 return;
16570 }
16571
16572 if let ActiveDiagnostic::Group(active_diagnostics) = &mut self.active_diagnostics {
16573 let buffer = self.buffer.read(cx).snapshot(cx);
16574 let primary_range_start = active_diagnostics.active_range.start.to_offset(&buffer);
16575 let primary_range_end = active_diagnostics.active_range.end.to_offset(&buffer);
16576 let is_valid = buffer
16577 .diagnostics_in_range::<usize>(primary_range_start..primary_range_end)
16578 .any(|entry| {
16579 entry.diagnostic.is_primary
16580 && !entry.range.is_empty()
16581 && entry.range.start == primary_range_start
16582 && entry.diagnostic.message == active_diagnostics.active_message
16583 });
16584
16585 if !is_valid {
16586 self.dismiss_diagnostics(cx);
16587 }
16588 }
16589 }
16590
16591 pub fn active_diagnostic_group(&self) -> Option<&ActiveDiagnosticGroup> {
16592 match &self.active_diagnostics {
16593 ActiveDiagnostic::Group(group) => Some(group),
16594 _ => None,
16595 }
16596 }
16597
16598 pub fn set_all_diagnostics_active(&mut self, cx: &mut Context<Self>) {
16599 if !self.diagnostics_enabled() {
16600 return;
16601 }
16602 self.dismiss_diagnostics(cx);
16603 self.active_diagnostics = ActiveDiagnostic::All;
16604 }
16605
16606 fn activate_diagnostics(
16607 &mut self,
16608 buffer_id: BufferId,
16609 diagnostic: DiagnosticEntry<usize>,
16610 window: &mut Window,
16611 cx: &mut Context<Self>,
16612 ) {
16613 if !self.diagnostics_enabled() || matches!(self.active_diagnostics, ActiveDiagnostic::All) {
16614 return;
16615 }
16616 self.dismiss_diagnostics(cx);
16617 let snapshot = self.snapshot(window, cx);
16618 let buffer = self.buffer.read(cx).snapshot(cx);
16619 let Some(renderer) = GlobalDiagnosticRenderer::global(cx) else {
16620 return;
16621 };
16622
16623 let diagnostic_group = buffer
16624 .diagnostic_group(buffer_id, diagnostic.diagnostic.group_id)
16625 .collect::<Vec<_>>();
16626
16627 let blocks =
16628 renderer.render_group(diagnostic_group, buffer_id, snapshot, cx.weak_entity(), cx);
16629
16630 let blocks = self.display_map.update(cx, |display_map, cx| {
16631 display_map.insert_blocks(blocks, cx).into_iter().collect()
16632 });
16633 self.active_diagnostics = ActiveDiagnostic::Group(ActiveDiagnosticGroup {
16634 active_range: buffer.anchor_before(diagnostic.range.start)
16635 ..buffer.anchor_after(diagnostic.range.end),
16636 active_message: diagnostic.diagnostic.message.clone(),
16637 group_id: diagnostic.diagnostic.group_id,
16638 blocks,
16639 });
16640 cx.notify();
16641 }
16642
16643 fn dismiss_diagnostics(&mut self, cx: &mut Context<Self>) {
16644 if matches!(self.active_diagnostics, ActiveDiagnostic::All) {
16645 return;
16646 };
16647
16648 let prev = mem::replace(&mut self.active_diagnostics, ActiveDiagnostic::None);
16649 if let ActiveDiagnostic::Group(group) = prev {
16650 self.display_map.update(cx, |display_map, cx| {
16651 display_map.remove_blocks(group.blocks, cx);
16652 });
16653 cx.notify();
16654 }
16655 }
16656
16657 /// Disable inline diagnostics rendering for this editor.
16658 pub fn disable_inline_diagnostics(&mut self) {
16659 self.inline_diagnostics_enabled = false;
16660 self.inline_diagnostics_update = Task::ready(());
16661 self.inline_diagnostics.clear();
16662 }
16663
16664 pub fn disable_diagnostics(&mut self, cx: &mut Context<Self>) {
16665 self.diagnostics_enabled = false;
16666 self.dismiss_diagnostics(cx);
16667 self.inline_diagnostics_update = Task::ready(());
16668 self.inline_diagnostics.clear();
16669 }
16670
16671 pub fn diagnostics_enabled(&self) -> bool {
16672 self.diagnostics_enabled && self.mode.is_full()
16673 }
16674
16675 pub fn inline_diagnostics_enabled(&self) -> bool {
16676 self.inline_diagnostics_enabled && self.diagnostics_enabled()
16677 }
16678
16679 pub fn show_inline_diagnostics(&self) -> bool {
16680 self.show_inline_diagnostics
16681 }
16682
16683 pub fn toggle_inline_diagnostics(
16684 &mut self,
16685 _: &ToggleInlineDiagnostics,
16686 window: &mut Window,
16687 cx: &mut Context<Editor>,
16688 ) {
16689 self.show_inline_diagnostics = !self.show_inline_diagnostics;
16690 self.refresh_inline_diagnostics(false, window, cx);
16691 }
16692
16693 pub fn set_max_diagnostics_severity(&mut self, severity: DiagnosticSeverity, cx: &mut App) {
16694 self.diagnostics_max_severity = severity;
16695 self.display_map.update(cx, |display_map, _| {
16696 display_map.diagnostics_max_severity = self.diagnostics_max_severity;
16697 });
16698 }
16699
16700 pub fn toggle_diagnostics(
16701 &mut self,
16702 _: &ToggleDiagnostics,
16703 window: &mut Window,
16704 cx: &mut Context<Editor>,
16705 ) {
16706 if !self.diagnostics_enabled() {
16707 return;
16708 }
16709
16710 let new_severity = if self.diagnostics_max_severity == DiagnosticSeverity::Off {
16711 EditorSettings::get_global(cx)
16712 .diagnostics_max_severity
16713 .filter(|severity| severity != &DiagnosticSeverity::Off)
16714 .unwrap_or(DiagnosticSeverity::Hint)
16715 } else {
16716 DiagnosticSeverity::Off
16717 };
16718 self.set_max_diagnostics_severity(new_severity, cx);
16719 if self.diagnostics_max_severity == DiagnosticSeverity::Off {
16720 self.active_diagnostics = ActiveDiagnostic::None;
16721 self.inline_diagnostics_update = Task::ready(());
16722 self.inline_diagnostics.clear();
16723 } else {
16724 self.refresh_inline_diagnostics(false, window, cx);
16725 }
16726
16727 cx.notify();
16728 }
16729
16730 pub fn toggle_minimap(
16731 &mut self,
16732 _: &ToggleMinimap,
16733 window: &mut Window,
16734 cx: &mut Context<Editor>,
16735 ) {
16736 if self.supports_minimap(cx) {
16737 self.set_minimap_visibility(self.minimap_visibility.toggle_visibility(), window, cx);
16738 }
16739 }
16740
16741 fn refresh_inline_diagnostics(
16742 &mut self,
16743 debounce: bool,
16744 window: &mut Window,
16745 cx: &mut Context<Self>,
16746 ) {
16747 let max_severity = ProjectSettings::get_global(cx)
16748 .diagnostics
16749 .inline
16750 .max_severity
16751 .unwrap_or(self.diagnostics_max_severity);
16752
16753 if !self.inline_diagnostics_enabled()
16754 || !self.show_inline_diagnostics
16755 || max_severity == DiagnosticSeverity::Off
16756 {
16757 self.inline_diagnostics_update = Task::ready(());
16758 self.inline_diagnostics.clear();
16759 return;
16760 }
16761
16762 let debounce_ms = ProjectSettings::get_global(cx)
16763 .diagnostics
16764 .inline
16765 .update_debounce_ms;
16766 let debounce = if debounce && debounce_ms > 0 {
16767 Some(Duration::from_millis(debounce_ms))
16768 } else {
16769 None
16770 };
16771 self.inline_diagnostics_update = cx.spawn_in(window, async move |editor, cx| {
16772 if let Some(debounce) = debounce {
16773 cx.background_executor().timer(debounce).await;
16774 }
16775 let Some(snapshot) = editor.upgrade().and_then(|editor| {
16776 editor
16777 .update(cx, |editor, cx| editor.buffer().read(cx).snapshot(cx))
16778 .ok()
16779 }) else {
16780 return;
16781 };
16782
16783 let new_inline_diagnostics = cx
16784 .background_spawn(async move {
16785 let mut inline_diagnostics = Vec::<(Anchor, InlineDiagnostic)>::new();
16786 for diagnostic_entry in snapshot.diagnostics_in_range(0..snapshot.len()) {
16787 let message = diagnostic_entry
16788 .diagnostic
16789 .message
16790 .split_once('\n')
16791 .map(|(line, _)| line)
16792 .map(SharedString::new)
16793 .unwrap_or_else(|| {
16794 SharedString::from(diagnostic_entry.diagnostic.message)
16795 });
16796 let start_anchor = snapshot.anchor_before(diagnostic_entry.range.start);
16797 let (Ok(i) | Err(i)) = inline_diagnostics
16798 .binary_search_by(|(probe, _)| probe.cmp(&start_anchor, &snapshot));
16799 inline_diagnostics.insert(
16800 i,
16801 (
16802 start_anchor,
16803 InlineDiagnostic {
16804 message,
16805 group_id: diagnostic_entry.diagnostic.group_id,
16806 start: diagnostic_entry.range.start.to_point(&snapshot),
16807 is_primary: diagnostic_entry.diagnostic.is_primary,
16808 severity: diagnostic_entry.diagnostic.severity,
16809 },
16810 ),
16811 );
16812 }
16813 inline_diagnostics
16814 })
16815 .await;
16816
16817 editor
16818 .update(cx, |editor, cx| {
16819 editor.inline_diagnostics = new_inline_diagnostics;
16820 cx.notify();
16821 })
16822 .ok();
16823 });
16824 }
16825
16826 fn pull_diagnostics(
16827 &mut self,
16828 buffer_id: Option<BufferId>,
16829 window: &Window,
16830 cx: &mut Context<Self>,
16831 ) -> Option<()> {
16832 if !self.mode().is_full() {
16833 return None;
16834 }
16835 let pull_diagnostics_settings = ProjectSettings::get_global(cx)
16836 .diagnostics
16837 .lsp_pull_diagnostics;
16838 if !pull_diagnostics_settings.enabled {
16839 return None;
16840 }
16841 let project = self.project.as_ref()?.downgrade();
16842 let debounce = Duration::from_millis(pull_diagnostics_settings.debounce_ms);
16843 let mut buffers = self.buffer.read(cx).all_buffers();
16844 if let Some(buffer_id) = buffer_id {
16845 buffers.retain(|buffer| buffer.read(cx).remote_id() == buffer_id);
16846 }
16847
16848 self.pull_diagnostics_task = cx.spawn_in(window, async move |editor, cx| {
16849 cx.background_executor().timer(debounce).await;
16850
16851 let Ok(mut pull_diagnostics_tasks) = cx.update(|_, cx| {
16852 buffers
16853 .into_iter()
16854 .filter_map(|buffer| {
16855 project
16856 .update(cx, |project, cx| {
16857 project.lsp_store().update(cx, |lsp_store, cx| {
16858 lsp_store.pull_diagnostics_for_buffer(buffer, cx)
16859 })
16860 })
16861 .ok()
16862 })
16863 .collect::<FuturesUnordered<_>>()
16864 }) else {
16865 return;
16866 };
16867
16868 while let Some(pull_task) = pull_diagnostics_tasks.next().await {
16869 match pull_task {
16870 Ok(()) => {
16871 if editor
16872 .update_in(cx, |editor, window, cx| {
16873 editor.update_diagnostics_state(window, cx);
16874 })
16875 .is_err()
16876 {
16877 return;
16878 }
16879 }
16880 Err(e) => log::error!("Failed to update project diagnostics: {e:#}"),
16881 }
16882 }
16883 });
16884
16885 Some(())
16886 }
16887
16888 pub fn set_selections_from_remote(
16889 &mut self,
16890 selections: Vec<Selection<Anchor>>,
16891 pending_selection: Option<Selection<Anchor>>,
16892 window: &mut Window,
16893 cx: &mut Context<Self>,
16894 ) {
16895 let old_cursor_position = self.selections.newest_anchor().head();
16896 self.selections.change_with(cx, |s| {
16897 s.select_anchors(selections);
16898 if let Some(pending_selection) = pending_selection {
16899 s.set_pending(pending_selection, SelectMode::Character);
16900 } else {
16901 s.clear_pending();
16902 }
16903 });
16904 self.selections_did_change(
16905 false,
16906 &old_cursor_position,
16907 SelectionEffects::default(),
16908 window,
16909 cx,
16910 );
16911 }
16912
16913 pub fn transact(
16914 &mut self,
16915 window: &mut Window,
16916 cx: &mut Context<Self>,
16917 update: impl FnOnce(&mut Self, &mut Window, &mut Context<Self>),
16918 ) -> Option<TransactionId> {
16919 self.with_selection_effects_deferred(window, cx, |this, window, cx| {
16920 this.start_transaction_at(Instant::now(), window, cx);
16921 update(this, window, cx);
16922 this.end_transaction_at(Instant::now(), cx)
16923 })
16924 }
16925
16926 pub fn start_transaction_at(
16927 &mut self,
16928 now: Instant,
16929 window: &mut Window,
16930 cx: &mut Context<Self>,
16931 ) {
16932 self.end_selection(window, cx);
16933 if let Some(tx_id) = self
16934 .buffer
16935 .update(cx, |buffer, cx| buffer.start_transaction_at(now, cx))
16936 {
16937 self.selection_history
16938 .insert_transaction(tx_id, self.selections.disjoint_anchors());
16939 cx.emit(EditorEvent::TransactionBegun {
16940 transaction_id: tx_id,
16941 })
16942 }
16943 }
16944
16945 pub fn end_transaction_at(
16946 &mut self,
16947 now: Instant,
16948 cx: &mut Context<Self>,
16949 ) -> Option<TransactionId> {
16950 if let Some(transaction_id) = self
16951 .buffer
16952 .update(cx, |buffer, cx| buffer.end_transaction_at(now, cx))
16953 {
16954 if let Some((_, end_selections)) =
16955 self.selection_history.transaction_mut(transaction_id)
16956 {
16957 *end_selections = Some(self.selections.disjoint_anchors());
16958 } else {
16959 log::error!("unexpectedly ended a transaction that wasn't started by this editor");
16960 }
16961
16962 cx.emit(EditorEvent::Edited { transaction_id });
16963 Some(transaction_id)
16964 } else {
16965 None
16966 }
16967 }
16968
16969 pub fn set_mark(&mut self, _: &actions::SetMark, window: &mut Window, cx: &mut Context<Self>) {
16970 if self.selection_mark_mode {
16971 self.change_selections(SelectionEffects::no_scroll(), window, cx, |s| {
16972 s.move_with(|_, sel| {
16973 sel.collapse_to(sel.head(), SelectionGoal::None);
16974 });
16975 })
16976 }
16977 self.selection_mark_mode = true;
16978 cx.notify();
16979 }
16980
16981 pub fn swap_selection_ends(
16982 &mut self,
16983 _: &actions::SwapSelectionEnds,
16984 window: &mut Window,
16985 cx: &mut Context<Self>,
16986 ) {
16987 self.change_selections(SelectionEffects::no_scroll(), window, cx, |s| {
16988 s.move_with(|_, sel| {
16989 if sel.start != sel.end {
16990 sel.reversed = !sel.reversed
16991 }
16992 });
16993 });
16994 self.request_autoscroll(Autoscroll::newest(), cx);
16995 cx.notify();
16996 }
16997
16998 pub fn toggle_focus(
16999 workspace: &mut Workspace,
17000 _: &actions::ToggleFocus,
17001 window: &mut Window,
17002 cx: &mut Context<Workspace>,
17003 ) {
17004 let Some(item) = workspace.recent_active_item_by_type::<Self>(cx) else {
17005 return;
17006 };
17007 workspace.activate_item(&item, true, true, window, cx);
17008 }
17009
17010 pub fn toggle_fold(
17011 &mut self,
17012 _: &actions::ToggleFold,
17013 window: &mut Window,
17014 cx: &mut Context<Self>,
17015 ) {
17016 if self.is_singleton(cx) {
17017 let selection = self.selections.newest::<Point>(cx);
17018
17019 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
17020 let range = if selection.is_empty() {
17021 let point = selection.head().to_display_point(&display_map);
17022 let start = DisplayPoint::new(point.row(), 0).to_point(&display_map);
17023 let end = DisplayPoint::new(point.row(), display_map.line_len(point.row()))
17024 .to_point(&display_map);
17025 start..end
17026 } else {
17027 selection.range()
17028 };
17029 if display_map.folds_in_range(range).next().is_some() {
17030 self.unfold_lines(&Default::default(), window, cx)
17031 } else {
17032 self.fold(&Default::default(), window, cx)
17033 }
17034 } else {
17035 let multi_buffer_snapshot = self.buffer.read(cx).snapshot(cx);
17036 let buffer_ids: HashSet<_> = self
17037 .selections
17038 .disjoint_anchor_ranges()
17039 .flat_map(|range| multi_buffer_snapshot.buffer_ids_for_range(range))
17040 .collect();
17041
17042 let should_unfold = buffer_ids
17043 .iter()
17044 .any(|buffer_id| self.is_buffer_folded(*buffer_id, cx));
17045
17046 for buffer_id in buffer_ids {
17047 if should_unfold {
17048 self.unfold_buffer(buffer_id, cx);
17049 } else {
17050 self.fold_buffer(buffer_id, cx);
17051 }
17052 }
17053 }
17054 }
17055
17056 pub fn toggle_fold_recursive(
17057 &mut self,
17058 _: &actions::ToggleFoldRecursive,
17059 window: &mut Window,
17060 cx: &mut Context<Self>,
17061 ) {
17062 let selection = self.selections.newest::<Point>(cx);
17063
17064 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
17065 let range = if selection.is_empty() {
17066 let point = selection.head().to_display_point(&display_map);
17067 let start = DisplayPoint::new(point.row(), 0).to_point(&display_map);
17068 let end = DisplayPoint::new(point.row(), display_map.line_len(point.row()))
17069 .to_point(&display_map);
17070 start..end
17071 } else {
17072 selection.range()
17073 };
17074 if display_map.folds_in_range(range).next().is_some() {
17075 self.unfold_recursive(&Default::default(), window, cx)
17076 } else {
17077 self.fold_recursive(&Default::default(), window, cx)
17078 }
17079 }
17080
17081 pub fn fold(&mut self, _: &actions::Fold, window: &mut Window, cx: &mut Context<Self>) {
17082 if self.is_singleton(cx) {
17083 let mut to_fold = Vec::new();
17084 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
17085 let selections = self.selections.all_adjusted(cx);
17086
17087 for selection in selections {
17088 let range = selection.range().sorted();
17089 let buffer_start_row = range.start.row;
17090
17091 if range.start.row != range.end.row {
17092 let mut found = false;
17093 let mut row = range.start.row;
17094 while row <= range.end.row {
17095 if let Some(crease) = display_map.crease_for_buffer_row(MultiBufferRow(row))
17096 {
17097 found = true;
17098 row = crease.range().end.row + 1;
17099 to_fold.push(crease);
17100 } else {
17101 row += 1
17102 }
17103 }
17104 if found {
17105 continue;
17106 }
17107 }
17108
17109 for row in (0..=range.start.row).rev() {
17110 if let Some(crease) = display_map.crease_for_buffer_row(MultiBufferRow(row)) {
17111 if crease.range().end.row >= buffer_start_row {
17112 to_fold.push(crease);
17113 if row <= range.start.row {
17114 break;
17115 }
17116 }
17117 }
17118 }
17119 }
17120
17121 self.fold_creases(to_fold, true, window, cx);
17122 } else {
17123 let multi_buffer_snapshot = self.buffer.read(cx).snapshot(cx);
17124 let buffer_ids = self
17125 .selections
17126 .disjoint_anchor_ranges()
17127 .flat_map(|range| multi_buffer_snapshot.buffer_ids_for_range(range))
17128 .collect::<HashSet<_>>();
17129 for buffer_id in buffer_ids {
17130 self.fold_buffer(buffer_id, cx);
17131 }
17132 }
17133 }
17134
17135 pub fn toggle_fold_all(
17136 &mut self,
17137 _: &actions::ToggleFoldAll,
17138 window: &mut Window,
17139 cx: &mut Context<Self>,
17140 ) {
17141 if self.buffer.read(cx).is_singleton() {
17142 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
17143 let has_folds = display_map
17144 .folds_in_range(0..display_map.buffer_snapshot.len())
17145 .next()
17146 .is_some();
17147
17148 if has_folds {
17149 self.unfold_all(&actions::UnfoldAll, window, cx);
17150 } else {
17151 self.fold_all(&actions::FoldAll, window, cx);
17152 }
17153 } else {
17154 let buffer_ids = self.buffer.read(cx).excerpt_buffer_ids();
17155 let should_unfold = buffer_ids
17156 .iter()
17157 .any(|buffer_id| self.is_buffer_folded(*buffer_id, cx));
17158
17159 self.toggle_fold_multiple_buffers = cx.spawn_in(window, async move |editor, cx| {
17160 editor
17161 .update_in(cx, |editor, _, cx| {
17162 for buffer_id in buffer_ids {
17163 if should_unfold {
17164 editor.unfold_buffer(buffer_id, cx);
17165 } else {
17166 editor.fold_buffer(buffer_id, cx);
17167 }
17168 }
17169 })
17170 .ok();
17171 });
17172 }
17173 }
17174
17175 fn fold_at_level(
17176 &mut self,
17177 fold_at: &FoldAtLevel,
17178 window: &mut Window,
17179 cx: &mut Context<Self>,
17180 ) {
17181 if !self.buffer.read(cx).is_singleton() {
17182 return;
17183 }
17184
17185 let fold_at_level = fold_at.0;
17186 let snapshot = self.buffer.read(cx).snapshot(cx);
17187 let mut to_fold = Vec::new();
17188 let mut stack = vec![(0, snapshot.max_row().0, 1)];
17189
17190 while let Some((mut start_row, end_row, current_level)) = stack.pop() {
17191 while start_row < end_row {
17192 match self
17193 .snapshot(window, cx)
17194 .crease_for_buffer_row(MultiBufferRow(start_row))
17195 {
17196 Some(crease) => {
17197 let nested_start_row = crease.range().start.row + 1;
17198 let nested_end_row = crease.range().end.row;
17199
17200 if current_level < fold_at_level {
17201 stack.push((nested_start_row, nested_end_row, current_level + 1));
17202 } else if current_level == fold_at_level {
17203 to_fold.push(crease);
17204 }
17205
17206 start_row = nested_end_row + 1;
17207 }
17208 None => start_row += 1,
17209 }
17210 }
17211 }
17212
17213 self.fold_creases(to_fold, true, window, cx);
17214 }
17215
17216 pub fn fold_all(&mut self, _: &actions::FoldAll, window: &mut Window, cx: &mut Context<Self>) {
17217 if self.buffer.read(cx).is_singleton() {
17218 let mut fold_ranges = Vec::new();
17219 let snapshot = self.buffer.read(cx).snapshot(cx);
17220
17221 for row in 0..snapshot.max_row().0 {
17222 if let Some(foldable_range) = self
17223 .snapshot(window, cx)
17224 .crease_for_buffer_row(MultiBufferRow(row))
17225 {
17226 fold_ranges.push(foldable_range);
17227 }
17228 }
17229
17230 self.fold_creases(fold_ranges, true, window, cx);
17231 } else {
17232 self.toggle_fold_multiple_buffers = cx.spawn_in(window, async move |editor, cx| {
17233 editor
17234 .update_in(cx, |editor, _, cx| {
17235 for buffer_id in editor.buffer.read(cx).excerpt_buffer_ids() {
17236 editor.fold_buffer(buffer_id, cx);
17237 }
17238 })
17239 .ok();
17240 });
17241 }
17242 }
17243
17244 pub fn fold_function_bodies(
17245 &mut self,
17246 _: &actions::FoldFunctionBodies,
17247 window: &mut Window,
17248 cx: &mut Context<Self>,
17249 ) {
17250 let snapshot = self.buffer.read(cx).snapshot(cx);
17251
17252 let ranges = snapshot
17253 .text_object_ranges(0..snapshot.len(), TreeSitterOptions::default())
17254 .filter_map(|(range, obj)| (obj == TextObject::InsideFunction).then_some(range))
17255 .collect::<Vec<_>>();
17256
17257 let creases = ranges
17258 .into_iter()
17259 .map(|range| Crease::simple(range, self.display_map.read(cx).fold_placeholder.clone()))
17260 .collect();
17261
17262 self.fold_creases(creases, true, window, cx);
17263 }
17264
17265 pub fn fold_recursive(
17266 &mut self,
17267 _: &actions::FoldRecursive,
17268 window: &mut Window,
17269 cx: &mut Context<Self>,
17270 ) {
17271 let mut to_fold = Vec::new();
17272 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
17273 let selections = self.selections.all_adjusted(cx);
17274
17275 for selection in selections {
17276 let range = selection.range().sorted();
17277 let buffer_start_row = range.start.row;
17278
17279 if range.start.row != range.end.row {
17280 let mut found = false;
17281 for row in range.start.row..=range.end.row {
17282 if let Some(crease) = display_map.crease_for_buffer_row(MultiBufferRow(row)) {
17283 found = true;
17284 to_fold.push(crease);
17285 }
17286 }
17287 if found {
17288 continue;
17289 }
17290 }
17291
17292 for row in (0..=range.start.row).rev() {
17293 if let Some(crease) = display_map.crease_for_buffer_row(MultiBufferRow(row)) {
17294 if crease.range().end.row >= buffer_start_row {
17295 to_fold.push(crease);
17296 } else {
17297 break;
17298 }
17299 }
17300 }
17301 }
17302
17303 self.fold_creases(to_fold, true, window, cx);
17304 }
17305
17306 pub fn fold_at(
17307 &mut self,
17308 buffer_row: MultiBufferRow,
17309 window: &mut Window,
17310 cx: &mut Context<Self>,
17311 ) {
17312 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
17313
17314 if let Some(crease) = display_map.crease_for_buffer_row(buffer_row) {
17315 let autoscroll = self
17316 .selections
17317 .all::<Point>(cx)
17318 .iter()
17319 .any(|selection| crease.range().overlaps(&selection.range()));
17320
17321 self.fold_creases(vec![crease], autoscroll, window, cx);
17322 }
17323 }
17324
17325 pub fn unfold_lines(&mut self, _: &UnfoldLines, _window: &mut Window, cx: &mut Context<Self>) {
17326 if self.is_singleton(cx) {
17327 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
17328 let buffer = &display_map.buffer_snapshot;
17329 let selections = self.selections.all::<Point>(cx);
17330 let ranges = selections
17331 .iter()
17332 .map(|s| {
17333 let range = s.display_range(&display_map).sorted();
17334 let mut start = range.start.to_point(&display_map);
17335 let mut end = range.end.to_point(&display_map);
17336 start.column = 0;
17337 end.column = buffer.line_len(MultiBufferRow(end.row));
17338 start..end
17339 })
17340 .collect::<Vec<_>>();
17341
17342 self.unfold_ranges(&ranges, true, true, cx);
17343 } else {
17344 let multi_buffer_snapshot = self.buffer.read(cx).snapshot(cx);
17345 let buffer_ids = self
17346 .selections
17347 .disjoint_anchor_ranges()
17348 .flat_map(|range| multi_buffer_snapshot.buffer_ids_for_range(range))
17349 .collect::<HashSet<_>>();
17350 for buffer_id in buffer_ids {
17351 self.unfold_buffer(buffer_id, cx);
17352 }
17353 }
17354 }
17355
17356 pub fn unfold_recursive(
17357 &mut self,
17358 _: &UnfoldRecursive,
17359 _window: &mut Window,
17360 cx: &mut Context<Self>,
17361 ) {
17362 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
17363 let selections = self.selections.all::<Point>(cx);
17364 let ranges = selections
17365 .iter()
17366 .map(|s| {
17367 let mut range = s.display_range(&display_map).sorted();
17368 *range.start.column_mut() = 0;
17369 *range.end.column_mut() = display_map.line_len(range.end.row());
17370 let start = range.start.to_point(&display_map);
17371 let end = range.end.to_point(&display_map);
17372 start..end
17373 })
17374 .collect::<Vec<_>>();
17375
17376 self.unfold_ranges(&ranges, true, true, cx);
17377 }
17378
17379 pub fn unfold_at(
17380 &mut self,
17381 buffer_row: MultiBufferRow,
17382 _window: &mut Window,
17383 cx: &mut Context<Self>,
17384 ) {
17385 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
17386
17387 let intersection_range = Point::new(buffer_row.0, 0)
17388 ..Point::new(
17389 buffer_row.0,
17390 display_map.buffer_snapshot.line_len(buffer_row),
17391 );
17392
17393 let autoscroll = self
17394 .selections
17395 .all::<Point>(cx)
17396 .iter()
17397 .any(|selection| RangeExt::overlaps(&selection.range(), &intersection_range));
17398
17399 self.unfold_ranges(&[intersection_range], true, autoscroll, cx);
17400 }
17401
17402 pub fn unfold_all(
17403 &mut self,
17404 _: &actions::UnfoldAll,
17405 _window: &mut Window,
17406 cx: &mut Context<Self>,
17407 ) {
17408 if self.buffer.read(cx).is_singleton() {
17409 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
17410 self.unfold_ranges(&[0..display_map.buffer_snapshot.len()], true, true, cx);
17411 } else {
17412 self.toggle_fold_multiple_buffers = cx.spawn(async move |editor, cx| {
17413 editor
17414 .update(cx, |editor, cx| {
17415 for buffer_id in editor.buffer.read(cx).excerpt_buffer_ids() {
17416 editor.unfold_buffer(buffer_id, cx);
17417 }
17418 })
17419 .ok();
17420 });
17421 }
17422 }
17423
17424 pub fn fold_selected_ranges(
17425 &mut self,
17426 _: &FoldSelectedRanges,
17427 window: &mut Window,
17428 cx: &mut Context<Self>,
17429 ) {
17430 let selections = self.selections.all_adjusted(cx);
17431 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
17432 let ranges = selections
17433 .into_iter()
17434 .map(|s| Crease::simple(s.range(), display_map.fold_placeholder.clone()))
17435 .collect::<Vec<_>>();
17436 self.fold_creases(ranges, true, window, cx);
17437 }
17438
17439 pub fn fold_ranges<T: ToOffset + Clone>(
17440 &mut self,
17441 ranges: Vec<Range<T>>,
17442 auto_scroll: bool,
17443 window: &mut Window,
17444 cx: &mut Context<Self>,
17445 ) {
17446 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
17447 let ranges = ranges
17448 .into_iter()
17449 .map(|r| Crease::simple(r, display_map.fold_placeholder.clone()))
17450 .collect::<Vec<_>>();
17451 self.fold_creases(ranges, auto_scroll, window, cx);
17452 }
17453
17454 pub fn fold_creases<T: ToOffset + Clone>(
17455 &mut self,
17456 creases: Vec<Crease<T>>,
17457 auto_scroll: bool,
17458 _window: &mut Window,
17459 cx: &mut Context<Self>,
17460 ) {
17461 if creases.is_empty() {
17462 return;
17463 }
17464
17465 self.display_map.update(cx, |map, cx| map.fold(creases, cx));
17466
17467 if auto_scroll {
17468 self.request_autoscroll(Autoscroll::fit(), cx);
17469 }
17470
17471 cx.notify();
17472
17473 self.scrollbar_marker_state.dirty = true;
17474 self.folds_did_change(cx);
17475 }
17476
17477 /// Removes any folds whose ranges intersect any of the given ranges.
17478 pub fn unfold_ranges<T: ToOffset + Clone>(
17479 &mut self,
17480 ranges: &[Range<T>],
17481 inclusive: bool,
17482 auto_scroll: bool,
17483 cx: &mut Context<Self>,
17484 ) {
17485 self.remove_folds_with(ranges, auto_scroll, cx, |map, cx| {
17486 map.unfold_intersecting(ranges.iter().cloned(), inclusive, cx)
17487 });
17488 self.folds_did_change(cx);
17489 }
17490
17491 pub fn fold_buffer(&mut self, buffer_id: BufferId, cx: &mut Context<Self>) {
17492 if self.buffer().read(cx).is_singleton() || self.is_buffer_folded(buffer_id, cx) {
17493 return;
17494 }
17495 let folded_excerpts = self.buffer().read(cx).excerpts_for_buffer(buffer_id, cx);
17496 self.display_map.update(cx, |display_map, cx| {
17497 display_map.fold_buffers([buffer_id], cx)
17498 });
17499 cx.emit(EditorEvent::BufferFoldToggled {
17500 ids: folded_excerpts.iter().map(|&(id, _)| id).collect(),
17501 folded: true,
17502 });
17503 cx.notify();
17504 }
17505
17506 pub fn unfold_buffer(&mut self, buffer_id: BufferId, cx: &mut Context<Self>) {
17507 if self.buffer().read(cx).is_singleton() || !self.is_buffer_folded(buffer_id, cx) {
17508 return;
17509 }
17510 let unfolded_excerpts = self.buffer().read(cx).excerpts_for_buffer(buffer_id, cx);
17511 self.display_map.update(cx, |display_map, cx| {
17512 display_map.unfold_buffers([buffer_id], cx);
17513 });
17514 cx.emit(EditorEvent::BufferFoldToggled {
17515 ids: unfolded_excerpts.iter().map(|&(id, _)| id).collect(),
17516 folded: false,
17517 });
17518 cx.notify();
17519 }
17520
17521 pub fn is_buffer_folded(&self, buffer: BufferId, cx: &App) -> bool {
17522 self.display_map.read(cx).is_buffer_folded(buffer)
17523 }
17524
17525 pub fn folded_buffers<'a>(&self, cx: &'a App) -> &'a HashSet<BufferId> {
17526 self.display_map.read(cx).folded_buffers()
17527 }
17528
17529 pub fn disable_header_for_buffer(&mut self, buffer_id: BufferId, cx: &mut Context<Self>) {
17530 self.display_map.update(cx, |display_map, cx| {
17531 display_map.disable_header_for_buffer(buffer_id, cx);
17532 });
17533 cx.notify();
17534 }
17535
17536 /// Removes any folds with the given ranges.
17537 pub fn remove_folds_with_type<T: ToOffset + Clone>(
17538 &mut self,
17539 ranges: &[Range<T>],
17540 type_id: TypeId,
17541 auto_scroll: bool,
17542 cx: &mut Context<Self>,
17543 ) {
17544 self.remove_folds_with(ranges, auto_scroll, cx, |map, cx| {
17545 map.remove_folds_with_type(ranges.iter().cloned(), type_id, cx)
17546 });
17547 self.folds_did_change(cx);
17548 }
17549
17550 fn remove_folds_with<T: ToOffset + Clone>(
17551 &mut self,
17552 ranges: &[Range<T>],
17553 auto_scroll: bool,
17554 cx: &mut Context<Self>,
17555 update: impl FnOnce(&mut DisplayMap, &mut Context<DisplayMap>),
17556 ) {
17557 if ranges.is_empty() {
17558 return;
17559 }
17560
17561 let mut buffers_affected = HashSet::default();
17562 let multi_buffer = self.buffer().read(cx);
17563 for range in ranges {
17564 if let Some((_, buffer, _)) = multi_buffer.excerpt_containing(range.start.clone(), cx) {
17565 buffers_affected.insert(buffer.read(cx).remote_id());
17566 };
17567 }
17568
17569 self.display_map.update(cx, update);
17570
17571 if auto_scroll {
17572 self.request_autoscroll(Autoscroll::fit(), cx);
17573 }
17574
17575 cx.notify();
17576 self.scrollbar_marker_state.dirty = true;
17577 self.active_indent_guides_state.dirty = true;
17578 }
17579
17580 pub fn update_renderer_widths(
17581 &mut self,
17582 widths: impl IntoIterator<Item = (ChunkRendererId, Pixels)>,
17583 cx: &mut Context<Self>,
17584 ) -> bool {
17585 self.display_map
17586 .update(cx, |map, cx| map.update_fold_widths(widths, cx))
17587 }
17588
17589 pub fn default_fold_placeholder(&self, cx: &App) -> FoldPlaceholder {
17590 self.display_map.read(cx).fold_placeholder.clone()
17591 }
17592
17593 pub fn set_expand_all_diff_hunks(&mut self, cx: &mut App) {
17594 self.buffer.update(cx, |buffer, cx| {
17595 buffer.set_all_diff_hunks_expanded(cx);
17596 });
17597 }
17598
17599 pub fn expand_all_diff_hunks(
17600 &mut self,
17601 _: &ExpandAllDiffHunks,
17602 _window: &mut Window,
17603 cx: &mut Context<Self>,
17604 ) {
17605 self.buffer.update(cx, |buffer, cx| {
17606 buffer.expand_diff_hunks(vec![Anchor::min()..Anchor::max()], cx)
17607 });
17608 }
17609
17610 pub fn toggle_selected_diff_hunks(
17611 &mut self,
17612 _: &ToggleSelectedDiffHunks,
17613 _window: &mut Window,
17614 cx: &mut Context<Self>,
17615 ) {
17616 let ranges: Vec<_> = self.selections.disjoint.iter().map(|s| s.range()).collect();
17617 self.toggle_diff_hunks_in_ranges(ranges, cx);
17618 }
17619
17620 pub fn diff_hunks_in_ranges<'a>(
17621 &'a self,
17622 ranges: &'a [Range<Anchor>],
17623 buffer: &'a MultiBufferSnapshot,
17624 ) -> impl 'a + Iterator<Item = MultiBufferDiffHunk> {
17625 ranges.iter().flat_map(move |range| {
17626 let end_excerpt_id = range.end.excerpt_id;
17627 let range = range.to_point(buffer);
17628 let mut peek_end = range.end;
17629 if range.end.row < buffer.max_row().0 {
17630 peek_end = Point::new(range.end.row + 1, 0);
17631 }
17632 buffer
17633 .diff_hunks_in_range(range.start..peek_end)
17634 .filter(move |hunk| hunk.excerpt_id.cmp(&end_excerpt_id, buffer).is_le())
17635 })
17636 }
17637
17638 pub fn has_stageable_diff_hunks_in_ranges(
17639 &self,
17640 ranges: &[Range<Anchor>],
17641 snapshot: &MultiBufferSnapshot,
17642 ) -> bool {
17643 let mut hunks = self.diff_hunks_in_ranges(ranges, &snapshot);
17644 hunks.any(|hunk| hunk.status().has_secondary_hunk())
17645 }
17646
17647 pub fn toggle_staged_selected_diff_hunks(
17648 &mut self,
17649 _: &::git::ToggleStaged,
17650 _: &mut Window,
17651 cx: &mut Context<Self>,
17652 ) {
17653 let snapshot = self.buffer.read(cx).snapshot(cx);
17654 let ranges: Vec<_> = self.selections.disjoint.iter().map(|s| s.range()).collect();
17655 let stage = self.has_stageable_diff_hunks_in_ranges(&ranges, &snapshot);
17656 self.stage_or_unstage_diff_hunks(stage, ranges, cx);
17657 }
17658
17659 pub fn set_render_diff_hunk_controls(
17660 &mut self,
17661 render_diff_hunk_controls: RenderDiffHunkControlsFn,
17662 cx: &mut Context<Self>,
17663 ) {
17664 self.render_diff_hunk_controls = render_diff_hunk_controls;
17665 cx.notify();
17666 }
17667
17668 pub fn stage_and_next(
17669 &mut self,
17670 _: &::git::StageAndNext,
17671 window: &mut Window,
17672 cx: &mut Context<Self>,
17673 ) {
17674 self.do_stage_or_unstage_and_next(true, window, cx);
17675 }
17676
17677 pub fn unstage_and_next(
17678 &mut self,
17679 _: &::git::UnstageAndNext,
17680 window: &mut Window,
17681 cx: &mut Context<Self>,
17682 ) {
17683 self.do_stage_or_unstage_and_next(false, window, cx);
17684 }
17685
17686 pub fn stage_or_unstage_diff_hunks(
17687 &mut self,
17688 stage: bool,
17689 ranges: Vec<Range<Anchor>>,
17690 cx: &mut Context<Self>,
17691 ) {
17692 let task = self.save_buffers_for_ranges_if_needed(&ranges, cx);
17693 cx.spawn(async move |this, cx| {
17694 task.await?;
17695 this.update(cx, |this, cx| {
17696 let snapshot = this.buffer.read(cx).snapshot(cx);
17697 let chunk_by = this
17698 .diff_hunks_in_ranges(&ranges, &snapshot)
17699 .chunk_by(|hunk| hunk.buffer_id);
17700 for (buffer_id, hunks) in &chunk_by {
17701 this.do_stage_or_unstage(stage, buffer_id, hunks, cx);
17702 }
17703 })
17704 })
17705 .detach_and_log_err(cx);
17706 }
17707
17708 fn save_buffers_for_ranges_if_needed(
17709 &mut self,
17710 ranges: &[Range<Anchor>],
17711 cx: &mut Context<Editor>,
17712 ) -> Task<Result<()>> {
17713 let multibuffer = self.buffer.read(cx);
17714 let snapshot = multibuffer.read(cx);
17715 let buffer_ids: HashSet<_> = ranges
17716 .iter()
17717 .flat_map(|range| snapshot.buffer_ids_for_range(range.clone()))
17718 .collect();
17719 drop(snapshot);
17720
17721 let mut buffers = HashSet::default();
17722 for buffer_id in buffer_ids {
17723 if let Some(buffer_entity) = multibuffer.buffer(buffer_id) {
17724 let buffer = buffer_entity.read(cx);
17725 if buffer.file().is_some_and(|file| file.disk_state().exists()) && buffer.is_dirty()
17726 {
17727 buffers.insert(buffer_entity);
17728 }
17729 }
17730 }
17731
17732 if let Some(project) = &self.project {
17733 project.update(cx, |project, cx| project.save_buffers(buffers, cx))
17734 } else {
17735 Task::ready(Ok(()))
17736 }
17737 }
17738
17739 fn do_stage_or_unstage_and_next(
17740 &mut self,
17741 stage: bool,
17742 window: &mut Window,
17743 cx: &mut Context<Self>,
17744 ) {
17745 let ranges = self.selections.disjoint_anchor_ranges().collect::<Vec<_>>();
17746
17747 if ranges.iter().any(|range| range.start != range.end) {
17748 self.stage_or_unstage_diff_hunks(stage, ranges, cx);
17749 return;
17750 }
17751
17752 self.stage_or_unstage_diff_hunks(stage, ranges, cx);
17753 let snapshot = self.snapshot(window, cx);
17754 let position = self.selections.newest::<Point>(cx).head();
17755 let mut row = snapshot
17756 .buffer_snapshot
17757 .diff_hunks_in_range(position..snapshot.buffer_snapshot.max_point())
17758 .find(|hunk| hunk.row_range.start.0 > position.row)
17759 .map(|hunk| hunk.row_range.start);
17760
17761 let all_diff_hunks_expanded = self.buffer().read(cx).all_diff_hunks_expanded();
17762 // Outside of the project diff editor, wrap around to the beginning.
17763 if !all_diff_hunks_expanded {
17764 row = row.or_else(|| {
17765 snapshot
17766 .buffer_snapshot
17767 .diff_hunks_in_range(Point::zero()..position)
17768 .find(|hunk| hunk.row_range.end.0 < position.row)
17769 .map(|hunk| hunk.row_range.start)
17770 });
17771 }
17772
17773 if let Some(row) = row {
17774 let destination = Point::new(row.0, 0);
17775 let autoscroll = Autoscroll::center();
17776
17777 self.unfold_ranges(&[destination..destination], false, false, cx);
17778 self.change_selections(SelectionEffects::scroll(autoscroll), window, cx, |s| {
17779 s.select_ranges([destination..destination]);
17780 });
17781 }
17782 }
17783
17784 fn do_stage_or_unstage(
17785 &self,
17786 stage: bool,
17787 buffer_id: BufferId,
17788 hunks: impl Iterator<Item = MultiBufferDiffHunk>,
17789 cx: &mut App,
17790 ) -> Option<()> {
17791 let project = self.project.as_ref()?;
17792 let buffer = project.read(cx).buffer_for_id(buffer_id, cx)?;
17793 let diff = self.buffer.read(cx).diff_for(buffer_id)?;
17794 let buffer_snapshot = buffer.read(cx).snapshot();
17795 let file_exists = buffer_snapshot
17796 .file()
17797 .is_some_and(|file| file.disk_state().exists());
17798 diff.update(cx, |diff, cx| {
17799 diff.stage_or_unstage_hunks(
17800 stage,
17801 &hunks
17802 .map(|hunk| buffer_diff::DiffHunk {
17803 buffer_range: hunk.buffer_range,
17804 diff_base_byte_range: hunk.diff_base_byte_range,
17805 secondary_status: hunk.secondary_status,
17806 range: Point::zero()..Point::zero(), // unused
17807 })
17808 .collect::<Vec<_>>(),
17809 &buffer_snapshot,
17810 file_exists,
17811 cx,
17812 )
17813 });
17814 None
17815 }
17816
17817 pub fn expand_selected_diff_hunks(&mut self, cx: &mut Context<Self>) {
17818 let ranges: Vec<_> = self.selections.disjoint.iter().map(|s| s.range()).collect();
17819 self.buffer
17820 .update(cx, |buffer, cx| buffer.expand_diff_hunks(ranges, cx))
17821 }
17822
17823 pub fn clear_expanded_diff_hunks(&mut self, cx: &mut Context<Self>) -> bool {
17824 self.buffer.update(cx, |buffer, cx| {
17825 let ranges = vec![Anchor::min()..Anchor::max()];
17826 if !buffer.all_diff_hunks_expanded()
17827 && buffer.has_expanded_diff_hunks_in_ranges(&ranges, cx)
17828 {
17829 buffer.collapse_diff_hunks(ranges, cx);
17830 true
17831 } else {
17832 false
17833 }
17834 })
17835 }
17836
17837 fn toggle_diff_hunks_in_ranges(
17838 &mut self,
17839 ranges: Vec<Range<Anchor>>,
17840 cx: &mut Context<Editor>,
17841 ) {
17842 self.buffer.update(cx, |buffer, cx| {
17843 let expand = !buffer.has_expanded_diff_hunks_in_ranges(&ranges, cx);
17844 buffer.expand_or_collapse_diff_hunks(ranges, expand, cx);
17845 })
17846 }
17847
17848 fn toggle_single_diff_hunk(&mut self, range: Range<Anchor>, cx: &mut Context<Self>) {
17849 self.buffer.update(cx, |buffer, cx| {
17850 let snapshot = buffer.snapshot(cx);
17851 let excerpt_id = range.end.excerpt_id;
17852 let point_range = range.to_point(&snapshot);
17853 let expand = !buffer.single_hunk_is_expanded(range, cx);
17854 buffer.expand_or_collapse_diff_hunks_inner([(point_range, excerpt_id)], expand, cx);
17855 })
17856 }
17857
17858 pub(crate) fn apply_all_diff_hunks(
17859 &mut self,
17860 _: &ApplyAllDiffHunks,
17861 window: &mut Window,
17862 cx: &mut Context<Self>,
17863 ) {
17864 self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
17865
17866 let buffers = self.buffer.read(cx).all_buffers();
17867 for branch_buffer in buffers {
17868 branch_buffer.update(cx, |branch_buffer, cx| {
17869 branch_buffer.merge_into_base(Vec::new(), cx);
17870 });
17871 }
17872
17873 if let Some(project) = self.project.clone() {
17874 self.save(
17875 SaveOptions {
17876 format: true,
17877 autosave: false,
17878 },
17879 project,
17880 window,
17881 cx,
17882 )
17883 .detach_and_log_err(cx);
17884 }
17885 }
17886
17887 pub(crate) fn apply_selected_diff_hunks(
17888 &mut self,
17889 _: &ApplyDiffHunk,
17890 window: &mut Window,
17891 cx: &mut Context<Self>,
17892 ) {
17893 self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
17894 let snapshot = self.snapshot(window, cx);
17895 let hunks = snapshot.hunks_for_ranges(self.selections.ranges(cx));
17896 let mut ranges_by_buffer = HashMap::default();
17897 self.transact(window, cx, |editor, _window, cx| {
17898 for hunk in hunks {
17899 if let Some(buffer) = editor.buffer.read(cx).buffer(hunk.buffer_id) {
17900 ranges_by_buffer
17901 .entry(buffer.clone())
17902 .or_insert_with(Vec::new)
17903 .push(hunk.buffer_range.to_offset(buffer.read(cx)));
17904 }
17905 }
17906
17907 for (buffer, ranges) in ranges_by_buffer {
17908 buffer.update(cx, |buffer, cx| {
17909 buffer.merge_into_base(ranges, cx);
17910 });
17911 }
17912 });
17913
17914 if let Some(project) = self.project.clone() {
17915 self.save(
17916 SaveOptions {
17917 format: true,
17918 autosave: false,
17919 },
17920 project,
17921 window,
17922 cx,
17923 )
17924 .detach_and_log_err(cx);
17925 }
17926 }
17927
17928 pub fn set_gutter_hovered(&mut self, hovered: bool, cx: &mut Context<Self>) {
17929 if hovered != self.gutter_hovered {
17930 self.gutter_hovered = hovered;
17931 cx.notify();
17932 }
17933 }
17934
17935 pub fn insert_blocks(
17936 &mut self,
17937 blocks: impl IntoIterator<Item = BlockProperties<Anchor>>,
17938 autoscroll: Option<Autoscroll>,
17939 cx: &mut Context<Self>,
17940 ) -> Vec<CustomBlockId> {
17941 let blocks = self
17942 .display_map
17943 .update(cx, |display_map, cx| display_map.insert_blocks(blocks, cx));
17944 if let Some(autoscroll) = autoscroll {
17945 self.request_autoscroll(autoscroll, cx);
17946 }
17947 cx.notify();
17948 blocks
17949 }
17950
17951 pub fn resize_blocks(
17952 &mut self,
17953 heights: HashMap<CustomBlockId, u32>,
17954 autoscroll: Option<Autoscroll>,
17955 cx: &mut Context<Self>,
17956 ) {
17957 self.display_map
17958 .update(cx, |display_map, cx| display_map.resize_blocks(heights, cx));
17959 if let Some(autoscroll) = autoscroll {
17960 self.request_autoscroll(autoscroll, cx);
17961 }
17962 cx.notify();
17963 }
17964
17965 pub fn replace_blocks(
17966 &mut self,
17967 renderers: HashMap<CustomBlockId, RenderBlock>,
17968 autoscroll: Option<Autoscroll>,
17969 cx: &mut Context<Self>,
17970 ) {
17971 self.display_map
17972 .update(cx, |display_map, _cx| display_map.replace_blocks(renderers));
17973 if let Some(autoscroll) = autoscroll {
17974 self.request_autoscroll(autoscroll, cx);
17975 }
17976 cx.notify();
17977 }
17978
17979 pub fn remove_blocks(
17980 &mut self,
17981 block_ids: HashSet<CustomBlockId>,
17982 autoscroll: Option<Autoscroll>,
17983 cx: &mut Context<Self>,
17984 ) {
17985 self.display_map.update(cx, |display_map, cx| {
17986 display_map.remove_blocks(block_ids, cx)
17987 });
17988 if let Some(autoscroll) = autoscroll {
17989 self.request_autoscroll(autoscroll, cx);
17990 }
17991 cx.notify();
17992 }
17993
17994 pub fn row_for_block(
17995 &self,
17996 block_id: CustomBlockId,
17997 cx: &mut Context<Self>,
17998 ) -> Option<DisplayRow> {
17999 self.display_map
18000 .update(cx, |map, cx| map.row_for_block(block_id, cx))
18001 }
18002
18003 pub(crate) fn set_focused_block(&mut self, focused_block: FocusedBlock) {
18004 self.focused_block = Some(focused_block);
18005 }
18006
18007 pub(crate) fn take_focused_block(&mut self) -> Option<FocusedBlock> {
18008 self.focused_block.take()
18009 }
18010
18011 pub fn insert_creases(
18012 &mut self,
18013 creases: impl IntoIterator<Item = Crease<Anchor>>,
18014 cx: &mut Context<Self>,
18015 ) -> Vec<CreaseId> {
18016 self.display_map
18017 .update(cx, |map, cx| map.insert_creases(creases, cx))
18018 }
18019
18020 pub fn remove_creases(
18021 &mut self,
18022 ids: impl IntoIterator<Item = CreaseId>,
18023 cx: &mut Context<Self>,
18024 ) -> Vec<(CreaseId, Range<Anchor>)> {
18025 self.display_map
18026 .update(cx, |map, cx| map.remove_creases(ids, cx))
18027 }
18028
18029 pub fn longest_row(&self, cx: &mut App) -> DisplayRow {
18030 self.display_map
18031 .update(cx, |map, cx| map.snapshot(cx))
18032 .longest_row()
18033 }
18034
18035 pub fn max_point(&self, cx: &mut App) -> DisplayPoint {
18036 self.display_map
18037 .update(cx, |map, cx| map.snapshot(cx))
18038 .max_point()
18039 }
18040
18041 pub fn text(&self, cx: &App) -> String {
18042 self.buffer.read(cx).read(cx).text()
18043 }
18044
18045 pub fn is_empty(&self, cx: &App) -> bool {
18046 self.buffer.read(cx).read(cx).is_empty()
18047 }
18048
18049 pub fn text_option(&self, cx: &App) -> Option<String> {
18050 let text = self.text(cx);
18051 let text = text.trim();
18052
18053 if text.is_empty() {
18054 return None;
18055 }
18056
18057 Some(text.to_string())
18058 }
18059
18060 pub fn set_text(
18061 &mut self,
18062 text: impl Into<Arc<str>>,
18063 window: &mut Window,
18064 cx: &mut Context<Self>,
18065 ) {
18066 self.transact(window, cx, |this, _, cx| {
18067 this.buffer
18068 .read(cx)
18069 .as_singleton()
18070 .expect("you can only call set_text on editors for singleton buffers")
18071 .update(cx, |buffer, cx| buffer.set_text(text, cx));
18072 });
18073 }
18074
18075 pub fn display_text(&self, cx: &mut App) -> String {
18076 self.display_map
18077 .update(cx, |map, cx| map.snapshot(cx))
18078 .text()
18079 }
18080
18081 fn create_minimap(
18082 &self,
18083 minimap_settings: MinimapSettings,
18084 window: &mut Window,
18085 cx: &mut Context<Self>,
18086 ) -> Option<Entity<Self>> {
18087 (minimap_settings.minimap_enabled() && self.is_singleton(cx))
18088 .then(|| self.initialize_new_minimap(minimap_settings, window, cx))
18089 }
18090
18091 fn initialize_new_minimap(
18092 &self,
18093 minimap_settings: MinimapSettings,
18094 window: &mut Window,
18095 cx: &mut Context<Self>,
18096 ) -> Entity<Self> {
18097 const MINIMAP_FONT_WEIGHT: gpui::FontWeight = gpui::FontWeight::BLACK;
18098
18099 let mut minimap = Editor::new_internal(
18100 EditorMode::Minimap {
18101 parent: cx.weak_entity(),
18102 },
18103 self.buffer.clone(),
18104 None,
18105 Some(self.display_map.clone()),
18106 window,
18107 cx,
18108 );
18109 minimap.scroll_manager.clone_state(&self.scroll_manager);
18110 minimap.set_text_style_refinement(TextStyleRefinement {
18111 font_size: Some(MINIMAP_FONT_SIZE),
18112 font_weight: Some(MINIMAP_FONT_WEIGHT),
18113 ..Default::default()
18114 });
18115 minimap.update_minimap_configuration(minimap_settings, cx);
18116 cx.new(|_| minimap)
18117 }
18118
18119 fn update_minimap_configuration(&mut self, minimap_settings: MinimapSettings, cx: &App) {
18120 let current_line_highlight = minimap_settings
18121 .current_line_highlight
18122 .unwrap_or_else(|| EditorSettings::get_global(cx).current_line_highlight);
18123 self.set_current_line_highlight(Some(current_line_highlight));
18124 }
18125
18126 pub fn minimap(&self) -> Option<&Entity<Self>> {
18127 self.minimap
18128 .as_ref()
18129 .filter(|_| self.minimap_visibility.visible())
18130 }
18131
18132 pub fn wrap_guides(&self, cx: &App) -> SmallVec<[(usize, bool); 2]> {
18133 let mut wrap_guides = smallvec![];
18134
18135 if self.show_wrap_guides == Some(false) {
18136 return wrap_guides;
18137 }
18138
18139 let settings = self.buffer.read(cx).language_settings(cx);
18140 if settings.show_wrap_guides {
18141 match self.soft_wrap_mode(cx) {
18142 SoftWrap::Column(soft_wrap) => {
18143 wrap_guides.push((soft_wrap as usize, true));
18144 }
18145 SoftWrap::Bounded(soft_wrap) => {
18146 wrap_guides.push((soft_wrap as usize, true));
18147 }
18148 SoftWrap::GitDiff | SoftWrap::None | SoftWrap::EditorWidth => {}
18149 }
18150 wrap_guides.extend(settings.wrap_guides.iter().map(|guide| (*guide, false)))
18151 }
18152
18153 wrap_guides
18154 }
18155
18156 pub fn soft_wrap_mode(&self, cx: &App) -> SoftWrap {
18157 let settings = self.buffer.read(cx).language_settings(cx);
18158 let mode = self.soft_wrap_mode_override.unwrap_or(settings.soft_wrap);
18159 match mode {
18160 language_settings::SoftWrap::PreferLine | language_settings::SoftWrap::None => {
18161 SoftWrap::None
18162 }
18163 language_settings::SoftWrap::EditorWidth => SoftWrap::EditorWidth,
18164 language_settings::SoftWrap::PreferredLineLength => {
18165 SoftWrap::Column(settings.preferred_line_length)
18166 }
18167 language_settings::SoftWrap::Bounded => {
18168 SoftWrap::Bounded(settings.preferred_line_length)
18169 }
18170 }
18171 }
18172
18173 pub fn set_soft_wrap_mode(
18174 &mut self,
18175 mode: language_settings::SoftWrap,
18176
18177 cx: &mut Context<Self>,
18178 ) {
18179 self.soft_wrap_mode_override = Some(mode);
18180 cx.notify();
18181 }
18182
18183 pub fn set_hard_wrap(&mut self, hard_wrap: Option<usize>, cx: &mut Context<Self>) {
18184 self.hard_wrap = hard_wrap;
18185 cx.notify();
18186 }
18187
18188 pub fn set_text_style_refinement(&mut self, style: TextStyleRefinement) {
18189 self.text_style_refinement = Some(style);
18190 }
18191
18192 /// called by the Element so we know what style we were most recently rendered with.
18193 pub(crate) fn set_style(
18194 &mut self,
18195 style: EditorStyle,
18196 window: &mut Window,
18197 cx: &mut Context<Self>,
18198 ) {
18199 // We intentionally do not inform the display map about the minimap style
18200 // so that wrapping is not recalculated and stays consistent for the editor
18201 // and its linked minimap.
18202 if !self.mode.is_minimap() {
18203 let rem_size = window.rem_size();
18204 self.display_map.update(cx, |map, cx| {
18205 map.set_font(
18206 style.text.font(),
18207 style.text.font_size.to_pixels(rem_size),
18208 cx,
18209 )
18210 });
18211 }
18212 self.style = Some(style);
18213 }
18214
18215 pub fn style(&self) -> Option<&EditorStyle> {
18216 self.style.as_ref()
18217 }
18218
18219 // Called by the element. This method is not designed to be called outside of the editor
18220 // element's layout code because it does not notify when rewrapping is computed synchronously.
18221 pub(crate) fn set_wrap_width(&self, width: Option<Pixels>, cx: &mut App) -> bool {
18222 self.display_map
18223 .update(cx, |map, cx| map.set_wrap_width(width, cx))
18224 }
18225
18226 pub fn set_soft_wrap(&mut self) {
18227 self.soft_wrap_mode_override = Some(language_settings::SoftWrap::EditorWidth)
18228 }
18229
18230 pub fn toggle_soft_wrap(&mut self, _: &ToggleSoftWrap, _: &mut Window, cx: &mut Context<Self>) {
18231 if self.soft_wrap_mode_override.is_some() {
18232 self.soft_wrap_mode_override.take();
18233 } else {
18234 let soft_wrap = match self.soft_wrap_mode(cx) {
18235 SoftWrap::GitDiff => return,
18236 SoftWrap::None => language_settings::SoftWrap::EditorWidth,
18237 SoftWrap::EditorWidth | SoftWrap::Column(_) | SoftWrap::Bounded(_) => {
18238 language_settings::SoftWrap::None
18239 }
18240 };
18241 self.soft_wrap_mode_override = Some(soft_wrap);
18242 }
18243 cx.notify();
18244 }
18245
18246 pub fn toggle_tab_bar(&mut self, _: &ToggleTabBar, _: &mut Window, cx: &mut Context<Self>) {
18247 let Some(workspace) = self.workspace() else {
18248 return;
18249 };
18250 let fs = workspace.read(cx).app_state().fs.clone();
18251 let current_show = TabBarSettings::get_global(cx).show;
18252 update_settings_file::<TabBarSettings>(fs, cx, move |setting, _| {
18253 setting.show = Some(!current_show);
18254 });
18255 }
18256
18257 pub fn toggle_indent_guides(
18258 &mut self,
18259 _: &ToggleIndentGuides,
18260 _: &mut Window,
18261 cx: &mut Context<Self>,
18262 ) {
18263 let currently_enabled = self.should_show_indent_guides().unwrap_or_else(|| {
18264 self.buffer
18265 .read(cx)
18266 .language_settings(cx)
18267 .indent_guides
18268 .enabled
18269 });
18270 self.show_indent_guides = Some(!currently_enabled);
18271 cx.notify();
18272 }
18273
18274 fn should_show_indent_guides(&self) -> Option<bool> {
18275 self.show_indent_guides
18276 }
18277
18278 pub fn toggle_line_numbers(
18279 &mut self,
18280 _: &ToggleLineNumbers,
18281 _: &mut Window,
18282 cx: &mut Context<Self>,
18283 ) {
18284 let mut editor_settings = EditorSettings::get_global(cx).clone();
18285 editor_settings.gutter.line_numbers = !editor_settings.gutter.line_numbers;
18286 EditorSettings::override_global(editor_settings, cx);
18287 }
18288
18289 pub fn line_numbers_enabled(&self, cx: &App) -> bool {
18290 if let Some(show_line_numbers) = self.show_line_numbers {
18291 return show_line_numbers;
18292 }
18293 EditorSettings::get_global(cx).gutter.line_numbers
18294 }
18295
18296 pub fn should_use_relative_line_numbers(&self, cx: &mut App) -> bool {
18297 self.use_relative_line_numbers
18298 .unwrap_or(EditorSettings::get_global(cx).relative_line_numbers)
18299 }
18300
18301 pub fn toggle_relative_line_numbers(
18302 &mut self,
18303 _: &ToggleRelativeLineNumbers,
18304 _: &mut Window,
18305 cx: &mut Context<Self>,
18306 ) {
18307 let is_relative = self.should_use_relative_line_numbers(cx);
18308 self.set_relative_line_number(Some(!is_relative), cx)
18309 }
18310
18311 pub fn set_relative_line_number(&mut self, is_relative: Option<bool>, cx: &mut Context<Self>) {
18312 self.use_relative_line_numbers = is_relative;
18313 cx.notify();
18314 }
18315
18316 pub fn set_show_gutter(&mut self, show_gutter: bool, cx: &mut Context<Self>) {
18317 self.show_gutter = show_gutter;
18318 cx.notify();
18319 }
18320
18321 pub fn set_show_scrollbars(&mut self, show: bool, cx: &mut Context<Self>) {
18322 self.show_scrollbars = ScrollbarAxes {
18323 horizontal: show,
18324 vertical: show,
18325 };
18326 cx.notify();
18327 }
18328
18329 pub fn set_show_vertical_scrollbar(&mut self, show: bool, cx: &mut Context<Self>) {
18330 self.show_scrollbars.vertical = show;
18331 cx.notify();
18332 }
18333
18334 pub fn set_show_horizontal_scrollbar(&mut self, show: bool, cx: &mut Context<Self>) {
18335 self.show_scrollbars.horizontal = show;
18336 cx.notify();
18337 }
18338
18339 pub fn set_minimap_visibility(
18340 &mut self,
18341 minimap_visibility: MinimapVisibility,
18342 window: &mut Window,
18343 cx: &mut Context<Self>,
18344 ) {
18345 if self.minimap_visibility != minimap_visibility {
18346 if minimap_visibility.visible() && self.minimap.is_none() {
18347 let minimap_settings = EditorSettings::get_global(cx).minimap;
18348 self.minimap =
18349 self.create_minimap(minimap_settings.with_show_override(), window, cx);
18350 }
18351 self.minimap_visibility = minimap_visibility;
18352 cx.notify();
18353 }
18354 }
18355
18356 pub fn disable_scrollbars_and_minimap(&mut self, window: &mut Window, cx: &mut Context<Self>) {
18357 self.set_show_scrollbars(false, cx);
18358 self.set_minimap_visibility(MinimapVisibility::Disabled, window, cx);
18359 }
18360
18361 pub fn hide_minimap_by_default(&mut self, window: &mut Window, cx: &mut Context<Self>) {
18362 self.set_minimap_visibility(self.minimap_visibility.hidden(), window, cx);
18363 }
18364
18365 /// Normally the text in full mode and auto height editors is padded on the
18366 /// left side by roughly half a character width for improved hit testing.
18367 ///
18368 /// Use this method to disable this for cases where this is not wanted (e.g.
18369 /// if you want to align the editor text with some other text above or below)
18370 /// or if you want to add this padding to single-line editors.
18371 pub fn set_offset_content(&mut self, offset_content: bool, cx: &mut Context<Self>) {
18372 self.offset_content = offset_content;
18373 cx.notify();
18374 }
18375
18376 pub fn set_show_line_numbers(&mut self, show_line_numbers: bool, cx: &mut Context<Self>) {
18377 self.show_line_numbers = Some(show_line_numbers);
18378 cx.notify();
18379 }
18380
18381 pub fn disable_expand_excerpt_buttons(&mut self, cx: &mut Context<Self>) {
18382 self.disable_expand_excerpt_buttons = true;
18383 cx.notify();
18384 }
18385
18386 pub fn set_show_git_diff_gutter(&mut self, show_git_diff_gutter: bool, cx: &mut Context<Self>) {
18387 self.show_git_diff_gutter = Some(show_git_diff_gutter);
18388 cx.notify();
18389 }
18390
18391 pub fn set_show_code_actions(&mut self, show_code_actions: bool, cx: &mut Context<Self>) {
18392 self.show_code_actions = Some(show_code_actions);
18393 cx.notify();
18394 }
18395
18396 pub fn set_show_runnables(&mut self, show_runnables: bool, cx: &mut Context<Self>) {
18397 self.show_runnables = Some(show_runnables);
18398 cx.notify();
18399 }
18400
18401 pub fn set_show_breakpoints(&mut self, show_breakpoints: bool, cx: &mut Context<Self>) {
18402 self.show_breakpoints = Some(show_breakpoints);
18403 cx.notify();
18404 }
18405
18406 pub fn set_masked(&mut self, masked: bool, cx: &mut Context<Self>) {
18407 if self.display_map.read(cx).masked != masked {
18408 self.display_map.update(cx, |map, _| map.masked = masked);
18409 }
18410 cx.notify()
18411 }
18412
18413 pub fn set_show_wrap_guides(&mut self, show_wrap_guides: bool, cx: &mut Context<Self>) {
18414 self.show_wrap_guides = Some(show_wrap_guides);
18415 cx.notify();
18416 }
18417
18418 pub fn set_show_indent_guides(&mut self, show_indent_guides: bool, cx: &mut Context<Self>) {
18419 self.show_indent_guides = Some(show_indent_guides);
18420 cx.notify();
18421 }
18422
18423 pub fn working_directory(&self, cx: &App) -> Option<PathBuf> {
18424 if let Some(buffer) = self.buffer().read(cx).as_singleton() {
18425 if let Some(file) = buffer.read(cx).file().and_then(|f| f.as_local()) {
18426 if let Some(dir) = file.abs_path(cx).parent() {
18427 return Some(dir.to_owned());
18428 }
18429 }
18430
18431 if let Some(project_path) = buffer.read(cx).project_path(cx) {
18432 return Some(project_path.path.to_path_buf());
18433 }
18434 }
18435
18436 None
18437 }
18438
18439 fn target_file<'a>(&self, cx: &'a App) -> Option<&'a dyn language::LocalFile> {
18440 self.active_excerpt(cx)?
18441 .1
18442 .read(cx)
18443 .file()
18444 .and_then(|f| f.as_local())
18445 }
18446
18447 pub fn target_file_abs_path(&self, cx: &mut Context<Self>) -> Option<PathBuf> {
18448 self.active_excerpt(cx).and_then(|(_, buffer, _)| {
18449 let buffer = buffer.read(cx);
18450 if let Some(project_path) = buffer.project_path(cx) {
18451 let project = self.project.as_ref()?.read(cx);
18452 project.absolute_path(&project_path, cx)
18453 } else {
18454 buffer
18455 .file()
18456 .and_then(|file| file.as_local().map(|file| file.abs_path(cx)))
18457 }
18458 })
18459 }
18460
18461 fn target_file_path(&self, cx: &mut Context<Self>) -> Option<PathBuf> {
18462 self.active_excerpt(cx).and_then(|(_, buffer, _)| {
18463 let project_path = buffer.read(cx).project_path(cx)?;
18464 let project = self.project.as_ref()?.read(cx);
18465 let entry = project.entry_for_path(&project_path, cx)?;
18466 let path = entry.path.to_path_buf();
18467 Some(path)
18468 })
18469 }
18470
18471 pub fn reveal_in_finder(
18472 &mut self,
18473 _: &RevealInFileManager,
18474 _window: &mut Window,
18475 cx: &mut Context<Self>,
18476 ) {
18477 if let Some(target) = self.target_file(cx) {
18478 cx.reveal_path(&target.abs_path(cx));
18479 }
18480 }
18481
18482 pub fn copy_path(
18483 &mut self,
18484 _: &zed_actions::workspace::CopyPath,
18485 _window: &mut Window,
18486 cx: &mut Context<Self>,
18487 ) {
18488 if let Some(path) = self.target_file_abs_path(cx) {
18489 if let Some(path) = path.to_str() {
18490 cx.write_to_clipboard(ClipboardItem::new_string(path.to_string()));
18491 }
18492 }
18493 }
18494
18495 pub fn copy_relative_path(
18496 &mut self,
18497 _: &zed_actions::workspace::CopyRelativePath,
18498 _window: &mut Window,
18499 cx: &mut Context<Self>,
18500 ) {
18501 if let Some(path) = self.target_file_path(cx) {
18502 if let Some(path) = path.to_str() {
18503 cx.write_to_clipboard(ClipboardItem::new_string(path.to_string()));
18504 }
18505 }
18506 }
18507
18508 pub fn project_path(&self, cx: &App) -> Option<ProjectPath> {
18509 if let Some(buffer) = self.buffer.read(cx).as_singleton() {
18510 buffer.read(cx).project_path(cx)
18511 } else {
18512 None
18513 }
18514 }
18515
18516 // Returns true if the editor handled a go-to-line request
18517 pub fn go_to_active_debug_line(&mut self, window: &mut Window, cx: &mut Context<Self>) -> bool {
18518 maybe!({
18519 let breakpoint_store = self.breakpoint_store.as_ref()?;
18520
18521 let Some(active_stack_frame) = breakpoint_store.read(cx).active_position().cloned()
18522 else {
18523 self.clear_row_highlights::<ActiveDebugLine>();
18524 return None;
18525 };
18526
18527 let position = active_stack_frame.position;
18528 let buffer_id = position.buffer_id?;
18529 let snapshot = self
18530 .project
18531 .as_ref()?
18532 .read(cx)
18533 .buffer_for_id(buffer_id, cx)?
18534 .read(cx)
18535 .snapshot();
18536
18537 let mut handled = false;
18538 for (id, ExcerptRange { context, .. }) in
18539 self.buffer.read(cx).excerpts_for_buffer(buffer_id, cx)
18540 {
18541 if context.start.cmp(&position, &snapshot).is_ge()
18542 || context.end.cmp(&position, &snapshot).is_lt()
18543 {
18544 continue;
18545 }
18546 let snapshot = self.buffer.read(cx).snapshot(cx);
18547 let multibuffer_anchor = snapshot.anchor_in_excerpt(id, position)?;
18548
18549 handled = true;
18550 self.clear_row_highlights::<ActiveDebugLine>();
18551
18552 self.go_to_line::<ActiveDebugLine>(
18553 multibuffer_anchor,
18554 Some(cx.theme().colors().editor_debugger_active_line_background),
18555 window,
18556 cx,
18557 );
18558
18559 cx.notify();
18560 }
18561
18562 handled.then_some(())
18563 })
18564 .is_some()
18565 }
18566
18567 pub fn copy_file_name_without_extension(
18568 &mut self,
18569 _: &CopyFileNameWithoutExtension,
18570 _: &mut Window,
18571 cx: &mut Context<Self>,
18572 ) {
18573 if let Some(file) = self.target_file(cx) {
18574 if let Some(file_stem) = file.path().file_stem() {
18575 if let Some(name) = file_stem.to_str() {
18576 cx.write_to_clipboard(ClipboardItem::new_string(name.to_string()));
18577 }
18578 }
18579 }
18580 }
18581
18582 pub fn copy_file_name(&mut self, _: &CopyFileName, _: &mut Window, cx: &mut Context<Self>) {
18583 if let Some(file) = self.target_file(cx) {
18584 if let Some(file_name) = file.path().file_name() {
18585 if let Some(name) = file_name.to_str() {
18586 cx.write_to_clipboard(ClipboardItem::new_string(name.to_string()));
18587 }
18588 }
18589 }
18590 }
18591
18592 pub fn toggle_git_blame(
18593 &mut self,
18594 _: &::git::Blame,
18595 window: &mut Window,
18596 cx: &mut Context<Self>,
18597 ) {
18598 self.show_git_blame_gutter = !self.show_git_blame_gutter;
18599
18600 if self.show_git_blame_gutter && !self.has_blame_entries(cx) {
18601 self.start_git_blame(true, window, cx);
18602 }
18603
18604 cx.notify();
18605 }
18606
18607 pub fn toggle_git_blame_inline(
18608 &mut self,
18609 _: &ToggleGitBlameInline,
18610 window: &mut Window,
18611 cx: &mut Context<Self>,
18612 ) {
18613 self.toggle_git_blame_inline_internal(true, window, cx);
18614 cx.notify();
18615 }
18616
18617 pub fn open_git_blame_commit(
18618 &mut self,
18619 _: &OpenGitBlameCommit,
18620 window: &mut Window,
18621 cx: &mut Context<Self>,
18622 ) {
18623 self.open_git_blame_commit_internal(window, cx);
18624 }
18625
18626 fn open_git_blame_commit_internal(
18627 &mut self,
18628 window: &mut Window,
18629 cx: &mut Context<Self>,
18630 ) -> Option<()> {
18631 let blame = self.blame.as_ref()?;
18632 let snapshot = self.snapshot(window, cx);
18633 let cursor = self.selections.newest::<Point>(cx).head();
18634 let (buffer, point, _) = snapshot.buffer_snapshot.point_to_buffer_point(cursor)?;
18635 let blame_entry = blame
18636 .update(cx, |blame, cx| {
18637 blame
18638 .blame_for_rows(
18639 &[RowInfo {
18640 buffer_id: Some(buffer.remote_id()),
18641 buffer_row: Some(point.row),
18642 ..Default::default()
18643 }],
18644 cx,
18645 )
18646 .next()
18647 })
18648 .flatten()?;
18649 let renderer = cx.global::<GlobalBlameRenderer>().0.clone();
18650 let repo = blame.read(cx).repository(cx)?;
18651 let workspace = self.workspace()?.downgrade();
18652 renderer.open_blame_commit(blame_entry, repo, workspace, window, cx);
18653 None
18654 }
18655
18656 pub fn git_blame_inline_enabled(&self) -> bool {
18657 self.git_blame_inline_enabled
18658 }
18659
18660 pub fn toggle_selection_menu(
18661 &mut self,
18662 _: &ToggleSelectionMenu,
18663 _: &mut Window,
18664 cx: &mut Context<Self>,
18665 ) {
18666 self.show_selection_menu = self
18667 .show_selection_menu
18668 .map(|show_selections_menu| !show_selections_menu)
18669 .or_else(|| Some(!EditorSettings::get_global(cx).toolbar.selections_menu));
18670
18671 cx.notify();
18672 }
18673
18674 pub fn selection_menu_enabled(&self, cx: &App) -> bool {
18675 self.show_selection_menu
18676 .unwrap_or_else(|| EditorSettings::get_global(cx).toolbar.selections_menu)
18677 }
18678
18679 fn start_git_blame(
18680 &mut self,
18681 user_triggered: bool,
18682 window: &mut Window,
18683 cx: &mut Context<Self>,
18684 ) {
18685 if let Some(project) = self.project.as_ref() {
18686 let Some(buffer) = self.buffer().read(cx).as_singleton() else {
18687 return;
18688 };
18689
18690 if buffer.read(cx).file().is_none() {
18691 return;
18692 }
18693
18694 let focused = self.focus_handle(cx).contains_focused(window, cx);
18695
18696 let project = project.clone();
18697 let blame = cx.new(|cx| GitBlame::new(buffer, project, user_triggered, focused, cx));
18698 self.blame_subscription =
18699 Some(cx.observe_in(&blame, window, |_, _, _, cx| cx.notify()));
18700 self.blame = Some(blame);
18701 }
18702 }
18703
18704 fn toggle_git_blame_inline_internal(
18705 &mut self,
18706 user_triggered: bool,
18707 window: &mut Window,
18708 cx: &mut Context<Self>,
18709 ) {
18710 if self.git_blame_inline_enabled {
18711 self.git_blame_inline_enabled = false;
18712 self.show_git_blame_inline = false;
18713 self.show_git_blame_inline_delay_task.take();
18714 } else {
18715 self.git_blame_inline_enabled = true;
18716 self.start_git_blame_inline(user_triggered, window, cx);
18717 }
18718
18719 cx.notify();
18720 }
18721
18722 fn start_git_blame_inline(
18723 &mut self,
18724 user_triggered: bool,
18725 window: &mut Window,
18726 cx: &mut Context<Self>,
18727 ) {
18728 self.start_git_blame(user_triggered, window, cx);
18729
18730 if ProjectSettings::get_global(cx)
18731 .git
18732 .inline_blame_delay()
18733 .is_some()
18734 {
18735 self.start_inline_blame_timer(window, cx);
18736 } else {
18737 self.show_git_blame_inline = true
18738 }
18739 }
18740
18741 pub fn blame(&self) -> Option<&Entity<GitBlame>> {
18742 self.blame.as_ref()
18743 }
18744
18745 pub fn show_git_blame_gutter(&self) -> bool {
18746 self.show_git_blame_gutter
18747 }
18748
18749 pub fn render_git_blame_gutter(&self, cx: &App) -> bool {
18750 !self.mode().is_minimap() && self.show_git_blame_gutter && self.has_blame_entries(cx)
18751 }
18752
18753 pub fn render_git_blame_inline(&self, window: &Window, cx: &App) -> bool {
18754 self.show_git_blame_inline
18755 && (self.focus_handle.is_focused(window) || self.inline_blame_popover.is_some())
18756 && !self.newest_selection_head_on_empty_line(cx)
18757 && self.has_blame_entries(cx)
18758 }
18759
18760 fn has_blame_entries(&self, cx: &App) -> bool {
18761 self.blame()
18762 .map_or(false, |blame| blame.read(cx).has_generated_entries())
18763 }
18764
18765 fn newest_selection_head_on_empty_line(&self, cx: &App) -> bool {
18766 let cursor_anchor = self.selections.newest_anchor().head();
18767
18768 let snapshot = self.buffer.read(cx).snapshot(cx);
18769 let buffer_row = MultiBufferRow(cursor_anchor.to_point(&snapshot).row);
18770
18771 snapshot.line_len(buffer_row) == 0
18772 }
18773
18774 fn get_permalink_to_line(&self, cx: &mut Context<Self>) -> Task<Result<url::Url>> {
18775 let buffer_and_selection = maybe!({
18776 let selection = self.selections.newest::<Point>(cx);
18777 let selection_range = selection.range();
18778
18779 let multi_buffer = self.buffer().read(cx);
18780 let multi_buffer_snapshot = multi_buffer.snapshot(cx);
18781 let buffer_ranges = multi_buffer_snapshot.range_to_buffer_ranges(selection_range);
18782
18783 let (buffer, range, _) = if selection.reversed {
18784 buffer_ranges.first()
18785 } else {
18786 buffer_ranges.last()
18787 }?;
18788
18789 let selection = text::ToPoint::to_point(&range.start, &buffer).row
18790 ..text::ToPoint::to_point(&range.end, &buffer).row;
18791 Some((
18792 multi_buffer.buffer(buffer.remote_id()).unwrap().clone(),
18793 selection,
18794 ))
18795 });
18796
18797 let Some((buffer, selection)) = buffer_and_selection else {
18798 return Task::ready(Err(anyhow!("failed to determine buffer and selection")));
18799 };
18800
18801 let Some(project) = self.project.as_ref() else {
18802 return Task::ready(Err(anyhow!("editor does not have project")));
18803 };
18804
18805 project.update(cx, |project, cx| {
18806 project.get_permalink_to_line(&buffer, selection, cx)
18807 })
18808 }
18809
18810 pub fn copy_permalink_to_line(
18811 &mut self,
18812 _: &CopyPermalinkToLine,
18813 window: &mut Window,
18814 cx: &mut Context<Self>,
18815 ) {
18816 let permalink_task = self.get_permalink_to_line(cx);
18817 let workspace = self.workspace();
18818
18819 cx.spawn_in(window, async move |_, cx| match permalink_task.await {
18820 Ok(permalink) => {
18821 cx.update(|_, cx| {
18822 cx.write_to_clipboard(ClipboardItem::new_string(permalink.to_string()));
18823 })
18824 .ok();
18825 }
18826 Err(err) => {
18827 let message = format!("Failed to copy permalink: {err}");
18828
18829 anyhow::Result::<()>::Err(err).log_err();
18830
18831 if let Some(workspace) = workspace {
18832 workspace
18833 .update_in(cx, |workspace, _, cx| {
18834 struct CopyPermalinkToLine;
18835
18836 workspace.show_toast(
18837 Toast::new(
18838 NotificationId::unique::<CopyPermalinkToLine>(),
18839 message,
18840 ),
18841 cx,
18842 )
18843 })
18844 .ok();
18845 }
18846 }
18847 })
18848 .detach();
18849 }
18850
18851 pub fn copy_file_location(
18852 &mut self,
18853 _: &CopyFileLocation,
18854 _: &mut Window,
18855 cx: &mut Context<Self>,
18856 ) {
18857 let selection = self.selections.newest::<Point>(cx).start.row + 1;
18858 if let Some(file) = self.target_file(cx) {
18859 if let Some(path) = file.path().to_str() {
18860 cx.write_to_clipboard(ClipboardItem::new_string(format!("{path}:{selection}")));
18861 }
18862 }
18863 }
18864
18865 pub fn open_permalink_to_line(
18866 &mut self,
18867 _: &OpenPermalinkToLine,
18868 window: &mut Window,
18869 cx: &mut Context<Self>,
18870 ) {
18871 let permalink_task = self.get_permalink_to_line(cx);
18872 let workspace = self.workspace();
18873
18874 cx.spawn_in(window, async move |_, cx| match permalink_task.await {
18875 Ok(permalink) => {
18876 cx.update(|_, cx| {
18877 cx.open_url(permalink.as_ref());
18878 })
18879 .ok();
18880 }
18881 Err(err) => {
18882 let message = format!("Failed to open permalink: {err}");
18883
18884 anyhow::Result::<()>::Err(err).log_err();
18885
18886 if let Some(workspace) = workspace {
18887 workspace
18888 .update(cx, |workspace, cx| {
18889 struct OpenPermalinkToLine;
18890
18891 workspace.show_toast(
18892 Toast::new(
18893 NotificationId::unique::<OpenPermalinkToLine>(),
18894 message,
18895 ),
18896 cx,
18897 )
18898 })
18899 .ok();
18900 }
18901 }
18902 })
18903 .detach();
18904 }
18905
18906 pub fn insert_uuid_v4(
18907 &mut self,
18908 _: &InsertUuidV4,
18909 window: &mut Window,
18910 cx: &mut Context<Self>,
18911 ) {
18912 self.insert_uuid(UuidVersion::V4, window, cx);
18913 }
18914
18915 pub fn insert_uuid_v7(
18916 &mut self,
18917 _: &InsertUuidV7,
18918 window: &mut Window,
18919 cx: &mut Context<Self>,
18920 ) {
18921 self.insert_uuid(UuidVersion::V7, window, cx);
18922 }
18923
18924 fn insert_uuid(&mut self, version: UuidVersion, window: &mut Window, cx: &mut Context<Self>) {
18925 self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
18926 self.transact(window, cx, |this, window, cx| {
18927 let edits = this
18928 .selections
18929 .all::<Point>(cx)
18930 .into_iter()
18931 .map(|selection| {
18932 let uuid = match version {
18933 UuidVersion::V4 => uuid::Uuid::new_v4(),
18934 UuidVersion::V7 => uuid::Uuid::now_v7(),
18935 };
18936
18937 (selection.range(), uuid.to_string())
18938 });
18939 this.edit(edits, cx);
18940 this.refresh_inline_completion(true, false, window, cx);
18941 });
18942 }
18943
18944 pub fn open_selections_in_multibuffer(
18945 &mut self,
18946 _: &OpenSelectionsInMultibuffer,
18947 window: &mut Window,
18948 cx: &mut Context<Self>,
18949 ) {
18950 let multibuffer = self.buffer.read(cx);
18951
18952 let Some(buffer) = multibuffer.as_singleton() else {
18953 return;
18954 };
18955
18956 let Some(workspace) = self.workspace() else {
18957 return;
18958 };
18959
18960 let title = multibuffer.title(cx).to_string();
18961
18962 let locations = self
18963 .selections
18964 .all_anchors(cx)
18965 .into_iter()
18966 .map(|selection| Location {
18967 buffer: buffer.clone(),
18968 range: selection.start.text_anchor..selection.end.text_anchor,
18969 })
18970 .collect::<Vec<_>>();
18971
18972 cx.spawn_in(window, async move |_, cx| {
18973 workspace.update_in(cx, |workspace, window, cx| {
18974 Self::open_locations_in_multibuffer(
18975 workspace,
18976 locations,
18977 format!("Selections for '{title}'"),
18978 false,
18979 MultibufferSelectionMode::All,
18980 window,
18981 cx,
18982 );
18983 })
18984 })
18985 .detach();
18986 }
18987
18988 /// Adds a row highlight for the given range. If a row has multiple highlights, the
18989 /// last highlight added will be used.
18990 ///
18991 /// If the range ends at the beginning of a line, then that line will not be highlighted.
18992 pub fn highlight_rows<T: 'static>(
18993 &mut self,
18994 range: Range<Anchor>,
18995 color: Hsla,
18996 options: RowHighlightOptions,
18997 cx: &mut Context<Self>,
18998 ) {
18999 let snapshot = self.buffer().read(cx).snapshot(cx);
19000 let row_highlights = self.highlighted_rows.entry(TypeId::of::<T>()).or_default();
19001 let ix = row_highlights.binary_search_by(|highlight| {
19002 Ordering::Equal
19003 .then_with(|| highlight.range.start.cmp(&range.start, &snapshot))
19004 .then_with(|| highlight.range.end.cmp(&range.end, &snapshot))
19005 });
19006
19007 if let Err(mut ix) = ix {
19008 let index = post_inc(&mut self.highlight_order);
19009
19010 // If this range intersects with the preceding highlight, then merge it with
19011 // the preceding highlight. Otherwise insert a new highlight.
19012 let mut merged = false;
19013 if ix > 0 {
19014 let prev_highlight = &mut row_highlights[ix - 1];
19015 if prev_highlight
19016 .range
19017 .end
19018 .cmp(&range.start, &snapshot)
19019 .is_ge()
19020 {
19021 ix -= 1;
19022 if prev_highlight.range.end.cmp(&range.end, &snapshot).is_lt() {
19023 prev_highlight.range.end = range.end;
19024 }
19025 merged = true;
19026 prev_highlight.index = index;
19027 prev_highlight.color = color;
19028 prev_highlight.options = options;
19029 }
19030 }
19031
19032 if !merged {
19033 row_highlights.insert(
19034 ix,
19035 RowHighlight {
19036 range: range.clone(),
19037 index,
19038 color,
19039 options,
19040 type_id: TypeId::of::<T>(),
19041 },
19042 );
19043 }
19044
19045 // If any of the following highlights intersect with this one, merge them.
19046 while let Some(next_highlight) = row_highlights.get(ix + 1) {
19047 let highlight = &row_highlights[ix];
19048 if next_highlight
19049 .range
19050 .start
19051 .cmp(&highlight.range.end, &snapshot)
19052 .is_le()
19053 {
19054 if next_highlight
19055 .range
19056 .end
19057 .cmp(&highlight.range.end, &snapshot)
19058 .is_gt()
19059 {
19060 row_highlights[ix].range.end = next_highlight.range.end;
19061 }
19062 row_highlights.remove(ix + 1);
19063 } else {
19064 break;
19065 }
19066 }
19067 }
19068 }
19069
19070 /// Remove any highlighted row ranges of the given type that intersect the
19071 /// given ranges.
19072 pub fn remove_highlighted_rows<T: 'static>(
19073 &mut self,
19074 ranges_to_remove: Vec<Range<Anchor>>,
19075 cx: &mut Context<Self>,
19076 ) {
19077 let snapshot = self.buffer().read(cx).snapshot(cx);
19078 let row_highlights = self.highlighted_rows.entry(TypeId::of::<T>()).or_default();
19079 let mut ranges_to_remove = ranges_to_remove.iter().peekable();
19080 row_highlights.retain(|highlight| {
19081 while let Some(range_to_remove) = ranges_to_remove.peek() {
19082 match range_to_remove.end.cmp(&highlight.range.start, &snapshot) {
19083 Ordering::Less | Ordering::Equal => {
19084 ranges_to_remove.next();
19085 }
19086 Ordering::Greater => {
19087 match range_to_remove.start.cmp(&highlight.range.end, &snapshot) {
19088 Ordering::Less | Ordering::Equal => {
19089 return false;
19090 }
19091 Ordering::Greater => break,
19092 }
19093 }
19094 }
19095 }
19096
19097 true
19098 })
19099 }
19100
19101 /// Clear all anchor ranges for a certain highlight context type, so no corresponding rows will be highlighted.
19102 pub fn clear_row_highlights<T: 'static>(&mut self) {
19103 self.highlighted_rows.remove(&TypeId::of::<T>());
19104 }
19105
19106 /// For a highlight given context type, gets all anchor ranges that will be used for row highlighting.
19107 pub fn highlighted_rows<T: 'static>(&self) -> impl '_ + Iterator<Item = (Range<Anchor>, Hsla)> {
19108 self.highlighted_rows
19109 .get(&TypeId::of::<T>())
19110 .map_or(&[] as &[_], |vec| vec.as_slice())
19111 .iter()
19112 .map(|highlight| (highlight.range.clone(), highlight.color))
19113 }
19114
19115 /// Merges all anchor ranges for all context types ever set, picking the last highlight added in case of a row conflict.
19116 /// Returns a map of display rows that are highlighted and their corresponding highlight color.
19117 /// Allows to ignore certain kinds of highlights.
19118 pub fn highlighted_display_rows(
19119 &self,
19120 window: &mut Window,
19121 cx: &mut App,
19122 ) -> BTreeMap<DisplayRow, LineHighlight> {
19123 let snapshot = self.snapshot(window, cx);
19124 let mut used_highlight_orders = HashMap::default();
19125 self.highlighted_rows
19126 .iter()
19127 .flat_map(|(_, highlighted_rows)| highlighted_rows.iter())
19128 .fold(
19129 BTreeMap::<DisplayRow, LineHighlight>::new(),
19130 |mut unique_rows, highlight| {
19131 let start = highlight.range.start.to_display_point(&snapshot);
19132 let end = highlight.range.end.to_display_point(&snapshot);
19133 let start_row = start.row().0;
19134 let end_row = if highlight.range.end.text_anchor != text::Anchor::MAX
19135 && end.column() == 0
19136 {
19137 end.row().0.saturating_sub(1)
19138 } else {
19139 end.row().0
19140 };
19141 for row in start_row..=end_row {
19142 let used_index =
19143 used_highlight_orders.entry(row).or_insert(highlight.index);
19144 if highlight.index >= *used_index {
19145 *used_index = highlight.index;
19146 unique_rows.insert(
19147 DisplayRow(row),
19148 LineHighlight {
19149 include_gutter: highlight.options.include_gutter,
19150 border: None,
19151 background: highlight.color.into(),
19152 type_id: Some(highlight.type_id),
19153 },
19154 );
19155 }
19156 }
19157 unique_rows
19158 },
19159 )
19160 }
19161
19162 pub fn highlighted_display_row_for_autoscroll(
19163 &self,
19164 snapshot: &DisplaySnapshot,
19165 ) -> Option<DisplayRow> {
19166 self.highlighted_rows
19167 .values()
19168 .flat_map(|highlighted_rows| highlighted_rows.iter())
19169 .filter_map(|highlight| {
19170 if highlight.options.autoscroll {
19171 Some(highlight.range.start.to_display_point(snapshot).row())
19172 } else {
19173 None
19174 }
19175 })
19176 .min()
19177 }
19178
19179 pub fn set_search_within_ranges(&mut self, ranges: &[Range<Anchor>], cx: &mut Context<Self>) {
19180 self.highlight_background::<SearchWithinRange>(
19181 ranges,
19182 |colors| colors.colors().editor_document_highlight_read_background,
19183 cx,
19184 )
19185 }
19186
19187 pub fn set_breadcrumb_header(&mut self, new_header: String) {
19188 self.breadcrumb_header = Some(new_header);
19189 }
19190
19191 pub fn clear_search_within_ranges(&mut self, cx: &mut Context<Self>) {
19192 self.clear_background_highlights::<SearchWithinRange>(cx);
19193 }
19194
19195 pub fn highlight_background<T: 'static>(
19196 &mut self,
19197 ranges: &[Range<Anchor>],
19198 color_fetcher: fn(&Theme) -> Hsla,
19199 cx: &mut Context<Self>,
19200 ) {
19201 self.background_highlights.insert(
19202 HighlightKey::Type(TypeId::of::<T>()),
19203 (color_fetcher, Arc::from(ranges)),
19204 );
19205 self.scrollbar_marker_state.dirty = true;
19206 cx.notify();
19207 }
19208
19209 pub fn highlight_background_key<T: 'static>(
19210 &mut self,
19211 key: usize,
19212 ranges: &[Range<Anchor>],
19213 color_fetcher: fn(&Theme) -> Hsla,
19214 cx: &mut Context<Self>,
19215 ) {
19216 self.background_highlights.insert(
19217 HighlightKey::TypePlus(TypeId::of::<T>(), key),
19218 (color_fetcher, Arc::from(ranges)),
19219 );
19220 self.scrollbar_marker_state.dirty = true;
19221 cx.notify();
19222 }
19223
19224 pub fn clear_background_highlights<T: 'static>(
19225 &mut self,
19226 cx: &mut Context<Self>,
19227 ) -> Option<BackgroundHighlight> {
19228 let text_highlights = self
19229 .background_highlights
19230 .remove(&HighlightKey::Type(TypeId::of::<T>()))?;
19231 if !text_highlights.1.is_empty() {
19232 self.scrollbar_marker_state.dirty = true;
19233 cx.notify();
19234 }
19235 Some(text_highlights)
19236 }
19237
19238 pub fn highlight_gutter<T: 'static>(
19239 &mut self,
19240 ranges: impl Into<Vec<Range<Anchor>>>,
19241 color_fetcher: fn(&App) -> Hsla,
19242 cx: &mut Context<Self>,
19243 ) {
19244 self.gutter_highlights
19245 .insert(TypeId::of::<T>(), (color_fetcher, ranges.into()));
19246 cx.notify();
19247 }
19248
19249 pub fn clear_gutter_highlights<T: 'static>(
19250 &mut self,
19251 cx: &mut Context<Self>,
19252 ) -> Option<GutterHighlight> {
19253 cx.notify();
19254 self.gutter_highlights.remove(&TypeId::of::<T>())
19255 }
19256
19257 pub fn insert_gutter_highlight<T: 'static>(
19258 &mut self,
19259 range: Range<Anchor>,
19260 color_fetcher: fn(&App) -> Hsla,
19261 cx: &mut Context<Self>,
19262 ) {
19263 let snapshot = self.buffer().read(cx).snapshot(cx);
19264 let mut highlights = self
19265 .gutter_highlights
19266 .remove(&TypeId::of::<T>())
19267 .map(|(_, highlights)| highlights)
19268 .unwrap_or_default();
19269 let ix = highlights.binary_search_by(|highlight| {
19270 Ordering::Equal
19271 .then_with(|| highlight.start.cmp(&range.start, &snapshot))
19272 .then_with(|| highlight.end.cmp(&range.end, &snapshot))
19273 });
19274 if let Err(ix) = ix {
19275 highlights.insert(ix, range);
19276 }
19277 self.gutter_highlights
19278 .insert(TypeId::of::<T>(), (color_fetcher, highlights));
19279 }
19280
19281 pub fn remove_gutter_highlights<T: 'static>(
19282 &mut self,
19283 ranges_to_remove: Vec<Range<Anchor>>,
19284 cx: &mut Context<Self>,
19285 ) {
19286 let snapshot = self.buffer().read(cx).snapshot(cx);
19287 let Some((color_fetcher, mut gutter_highlights)) =
19288 self.gutter_highlights.remove(&TypeId::of::<T>())
19289 else {
19290 return;
19291 };
19292 let mut ranges_to_remove = ranges_to_remove.iter().peekable();
19293 gutter_highlights.retain(|highlight| {
19294 while let Some(range_to_remove) = ranges_to_remove.peek() {
19295 match range_to_remove.end.cmp(&highlight.start, &snapshot) {
19296 Ordering::Less | Ordering::Equal => {
19297 ranges_to_remove.next();
19298 }
19299 Ordering::Greater => {
19300 match range_to_remove.start.cmp(&highlight.end, &snapshot) {
19301 Ordering::Less | Ordering::Equal => {
19302 return false;
19303 }
19304 Ordering::Greater => break,
19305 }
19306 }
19307 }
19308 }
19309
19310 true
19311 });
19312 self.gutter_highlights
19313 .insert(TypeId::of::<T>(), (color_fetcher, gutter_highlights));
19314 }
19315
19316 #[cfg(feature = "test-support")]
19317 pub fn all_text_highlights(
19318 &self,
19319 window: &mut Window,
19320 cx: &mut Context<Self>,
19321 ) -> Vec<(HighlightStyle, Vec<Range<DisplayPoint>>)> {
19322 let snapshot = self.snapshot(window, cx);
19323 self.display_map.update(cx, |display_map, _| {
19324 display_map
19325 .all_text_highlights()
19326 .map(|highlight| {
19327 let (style, ranges) = highlight.as_ref();
19328 (
19329 *style,
19330 ranges
19331 .iter()
19332 .map(|range| range.clone().to_display_points(&snapshot))
19333 .collect(),
19334 )
19335 })
19336 .collect()
19337 })
19338 }
19339
19340 #[cfg(feature = "test-support")]
19341 pub fn all_text_background_highlights(
19342 &self,
19343 window: &mut Window,
19344 cx: &mut Context<Self>,
19345 ) -> Vec<(Range<DisplayPoint>, Hsla)> {
19346 let snapshot = self.snapshot(window, cx);
19347 let buffer = &snapshot.buffer_snapshot;
19348 let start = buffer.anchor_before(0);
19349 let end = buffer.anchor_after(buffer.len());
19350 self.background_highlights_in_range(start..end, &snapshot, cx.theme())
19351 }
19352
19353 #[cfg(feature = "test-support")]
19354 pub fn search_background_highlights(&mut self, cx: &mut Context<Self>) -> Vec<Range<Point>> {
19355 let snapshot = self.buffer().read(cx).snapshot(cx);
19356
19357 let highlights = self
19358 .background_highlights
19359 .get(&HighlightKey::Type(TypeId::of::<
19360 items::BufferSearchHighlights,
19361 >()));
19362
19363 if let Some((_color, ranges)) = highlights {
19364 ranges
19365 .iter()
19366 .map(|range| range.start.to_point(&snapshot)..range.end.to_point(&snapshot))
19367 .collect_vec()
19368 } else {
19369 vec![]
19370 }
19371 }
19372
19373 fn document_highlights_for_position<'a>(
19374 &'a self,
19375 position: Anchor,
19376 buffer: &'a MultiBufferSnapshot,
19377 ) -> impl 'a + Iterator<Item = &'a Range<Anchor>> {
19378 let read_highlights = self
19379 .background_highlights
19380 .get(&HighlightKey::Type(TypeId::of::<DocumentHighlightRead>()))
19381 .map(|h| &h.1);
19382 let write_highlights = self
19383 .background_highlights
19384 .get(&HighlightKey::Type(TypeId::of::<DocumentHighlightWrite>()))
19385 .map(|h| &h.1);
19386 let left_position = position.bias_left(buffer);
19387 let right_position = position.bias_right(buffer);
19388 read_highlights
19389 .into_iter()
19390 .chain(write_highlights)
19391 .flat_map(move |ranges| {
19392 let start_ix = match ranges.binary_search_by(|probe| {
19393 let cmp = probe.end.cmp(&left_position, buffer);
19394 if cmp.is_ge() {
19395 Ordering::Greater
19396 } else {
19397 Ordering::Less
19398 }
19399 }) {
19400 Ok(i) | Err(i) => i,
19401 };
19402
19403 ranges[start_ix..]
19404 .iter()
19405 .take_while(move |range| range.start.cmp(&right_position, buffer).is_le())
19406 })
19407 }
19408
19409 pub fn has_background_highlights<T: 'static>(&self) -> bool {
19410 self.background_highlights
19411 .get(&HighlightKey::Type(TypeId::of::<T>()))
19412 .map_or(false, |(_, highlights)| !highlights.is_empty())
19413 }
19414
19415 pub fn background_highlights_in_range(
19416 &self,
19417 search_range: Range<Anchor>,
19418 display_snapshot: &DisplaySnapshot,
19419 theme: &Theme,
19420 ) -> Vec<(Range<DisplayPoint>, Hsla)> {
19421 let mut results = Vec::new();
19422 for (color_fetcher, ranges) in self.background_highlights.values() {
19423 let color = color_fetcher(theme);
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 pub fn background_highlight_row_ranges<T: 'static>(
19454 &self,
19455 search_range: Range<Anchor>,
19456 display_snapshot: &DisplaySnapshot,
19457 count: usize,
19458 ) -> Vec<RangeInclusive<DisplayPoint>> {
19459 let mut results = Vec::new();
19460 let Some((_, ranges)) = self
19461 .background_highlights
19462 .get(&HighlightKey::Type(TypeId::of::<T>()))
19463 else {
19464 return vec![];
19465 };
19466
19467 let start_ix = match ranges.binary_search_by(|probe| {
19468 let cmp = probe
19469 .end
19470 .cmp(&search_range.start, &display_snapshot.buffer_snapshot);
19471 if cmp.is_gt() {
19472 Ordering::Greater
19473 } else {
19474 Ordering::Less
19475 }
19476 }) {
19477 Ok(i) | Err(i) => i,
19478 };
19479 let mut push_region = |start: Option<Point>, end: Option<Point>| {
19480 if let (Some(start_display), Some(end_display)) = (start, end) {
19481 results.push(
19482 start_display.to_display_point(display_snapshot)
19483 ..=end_display.to_display_point(display_snapshot),
19484 );
19485 }
19486 };
19487 let mut start_row: Option<Point> = None;
19488 let mut end_row: Option<Point> = None;
19489 if ranges.len() > count {
19490 return Vec::new();
19491 }
19492 for range in &ranges[start_ix..] {
19493 if range
19494 .start
19495 .cmp(&search_range.end, &display_snapshot.buffer_snapshot)
19496 .is_ge()
19497 {
19498 break;
19499 }
19500 let end = range.end.to_point(&display_snapshot.buffer_snapshot);
19501 if let Some(current_row) = &end_row {
19502 if end.row == current_row.row {
19503 continue;
19504 }
19505 }
19506 let start = range.start.to_point(&display_snapshot.buffer_snapshot);
19507 if start_row.is_none() {
19508 assert_eq!(end_row, None);
19509 start_row = Some(start);
19510 end_row = Some(end);
19511 continue;
19512 }
19513 if let Some(current_end) = end_row.as_mut() {
19514 if start.row > current_end.row + 1 {
19515 push_region(start_row, end_row);
19516 start_row = Some(start);
19517 end_row = Some(end);
19518 } else {
19519 // Merge two hunks.
19520 *current_end = end;
19521 }
19522 } else {
19523 unreachable!();
19524 }
19525 }
19526 // We might still have a hunk that was not rendered (if there was a search hit on the last line)
19527 push_region(start_row, end_row);
19528 results
19529 }
19530
19531 pub fn gutter_highlights_in_range(
19532 &self,
19533 search_range: Range<Anchor>,
19534 display_snapshot: &DisplaySnapshot,
19535 cx: &App,
19536 ) -> Vec<(Range<DisplayPoint>, Hsla)> {
19537 let mut results = Vec::new();
19538 for (color_fetcher, ranges) in self.gutter_highlights.values() {
19539 let color = color_fetcher(cx);
19540 let start_ix = match ranges.binary_search_by(|probe| {
19541 let cmp = probe
19542 .end
19543 .cmp(&search_range.start, &display_snapshot.buffer_snapshot);
19544 if cmp.is_gt() {
19545 Ordering::Greater
19546 } else {
19547 Ordering::Less
19548 }
19549 }) {
19550 Ok(i) | Err(i) => i,
19551 };
19552 for range in &ranges[start_ix..] {
19553 if range
19554 .start
19555 .cmp(&search_range.end, &display_snapshot.buffer_snapshot)
19556 .is_ge()
19557 {
19558 break;
19559 }
19560
19561 let start = range.start.to_display_point(display_snapshot);
19562 let end = range.end.to_display_point(display_snapshot);
19563 results.push((start..end, color))
19564 }
19565 }
19566 results
19567 }
19568
19569 /// Get the text ranges corresponding to the redaction query
19570 pub fn redacted_ranges(
19571 &self,
19572 search_range: Range<Anchor>,
19573 display_snapshot: &DisplaySnapshot,
19574 cx: &App,
19575 ) -> Vec<Range<DisplayPoint>> {
19576 display_snapshot
19577 .buffer_snapshot
19578 .redacted_ranges(search_range, |file| {
19579 if let Some(file) = file {
19580 file.is_private()
19581 && EditorSettings::get(
19582 Some(SettingsLocation {
19583 worktree_id: file.worktree_id(cx),
19584 path: file.path().as_ref(),
19585 }),
19586 cx,
19587 )
19588 .redact_private_values
19589 } else {
19590 false
19591 }
19592 })
19593 .map(|range| {
19594 range.start.to_display_point(display_snapshot)
19595 ..range.end.to_display_point(display_snapshot)
19596 })
19597 .collect()
19598 }
19599
19600 pub fn highlight_text_key<T: 'static>(
19601 &mut self,
19602 key: usize,
19603 ranges: Vec<Range<Anchor>>,
19604 style: HighlightStyle,
19605 cx: &mut Context<Self>,
19606 ) {
19607 self.display_map.update(cx, |map, _| {
19608 map.highlight_text(
19609 HighlightKey::TypePlus(TypeId::of::<T>(), key),
19610 ranges,
19611 style,
19612 );
19613 });
19614 cx.notify();
19615 }
19616
19617 pub fn highlight_text<T: 'static>(
19618 &mut self,
19619 ranges: Vec<Range<Anchor>>,
19620 style: HighlightStyle,
19621 cx: &mut Context<Self>,
19622 ) {
19623 self.display_map.update(cx, |map, _| {
19624 map.highlight_text(HighlightKey::Type(TypeId::of::<T>()), ranges, style)
19625 });
19626 cx.notify();
19627 }
19628
19629 pub(crate) fn highlight_inlays<T: 'static>(
19630 &mut self,
19631 highlights: Vec<InlayHighlight>,
19632 style: HighlightStyle,
19633 cx: &mut Context<Self>,
19634 ) {
19635 self.display_map.update(cx, |map, _| {
19636 map.highlight_inlays(TypeId::of::<T>(), highlights, style)
19637 });
19638 cx.notify();
19639 }
19640
19641 pub fn text_highlights<'a, T: 'static>(
19642 &'a self,
19643 cx: &'a App,
19644 ) -> Option<(HighlightStyle, &'a [Range<Anchor>])> {
19645 self.display_map.read(cx).text_highlights(TypeId::of::<T>())
19646 }
19647
19648 pub fn clear_highlights<T: 'static>(&mut self, cx: &mut Context<Self>) {
19649 let cleared = self
19650 .display_map
19651 .update(cx, |map, _| map.clear_highlights(TypeId::of::<T>()));
19652 if cleared {
19653 cx.notify();
19654 }
19655 }
19656
19657 pub fn show_local_cursors(&self, window: &mut Window, cx: &mut App) -> bool {
19658 (self.read_only(cx) || self.blink_manager.read(cx).visible())
19659 && self.focus_handle.is_focused(window)
19660 }
19661
19662 pub fn set_show_cursor_when_unfocused(&mut self, is_enabled: bool, cx: &mut Context<Self>) {
19663 self.show_cursor_when_unfocused = is_enabled;
19664 cx.notify();
19665 }
19666
19667 fn on_buffer_changed(&mut self, _: Entity<MultiBuffer>, cx: &mut Context<Self>) {
19668 cx.notify();
19669 }
19670
19671 fn on_debug_session_event(
19672 &mut self,
19673 _session: Entity<Session>,
19674 event: &SessionEvent,
19675 cx: &mut Context<Self>,
19676 ) {
19677 match event {
19678 SessionEvent::InvalidateInlineValue => {
19679 self.refresh_inline_values(cx);
19680 }
19681 _ => {}
19682 }
19683 }
19684
19685 pub fn refresh_inline_values(&mut self, cx: &mut Context<Self>) {
19686 let Some(project) = self.project.clone() else {
19687 return;
19688 };
19689
19690 if !self.inline_value_cache.enabled {
19691 let inlays = std::mem::take(&mut self.inline_value_cache.inlays);
19692 self.splice_inlays(&inlays, Vec::new(), cx);
19693 return;
19694 }
19695
19696 let current_execution_position = self
19697 .highlighted_rows
19698 .get(&TypeId::of::<ActiveDebugLine>())
19699 .and_then(|lines| lines.last().map(|line| line.range.end));
19700
19701 self.inline_value_cache.refresh_task = cx.spawn(async move |editor, cx| {
19702 let inline_values = editor
19703 .update(cx, |editor, cx| {
19704 let Some(current_execution_position) = current_execution_position else {
19705 return Some(Task::ready(Ok(Vec::new())));
19706 };
19707
19708 let buffer = editor.buffer.read_with(cx, |buffer, cx| {
19709 let snapshot = buffer.snapshot(cx);
19710
19711 let excerpt = snapshot.excerpt_containing(
19712 current_execution_position..current_execution_position,
19713 )?;
19714
19715 editor.buffer.read(cx).buffer(excerpt.buffer_id())
19716 })?;
19717
19718 let range =
19719 buffer.read(cx).anchor_before(0)..current_execution_position.text_anchor;
19720
19721 project.inline_values(buffer, range, cx)
19722 })
19723 .ok()
19724 .flatten()?
19725 .await
19726 .context("refreshing debugger inlays")
19727 .log_err()?;
19728
19729 let mut buffer_inline_values: HashMap<BufferId, Vec<InlayHint>> = HashMap::default();
19730
19731 for (buffer_id, inline_value) in inline_values
19732 .into_iter()
19733 .filter_map(|hint| Some((hint.position.buffer_id?, hint)))
19734 {
19735 buffer_inline_values
19736 .entry(buffer_id)
19737 .or_default()
19738 .push(inline_value);
19739 }
19740
19741 editor
19742 .update(cx, |editor, cx| {
19743 let snapshot = editor.buffer.read(cx).snapshot(cx);
19744 let mut new_inlays = Vec::default();
19745
19746 for (excerpt_id, buffer_snapshot, _) in snapshot.excerpts() {
19747 let buffer_id = buffer_snapshot.remote_id();
19748 buffer_inline_values
19749 .get(&buffer_id)
19750 .into_iter()
19751 .flatten()
19752 .for_each(|hint| {
19753 let inlay = Inlay::debugger(
19754 post_inc(&mut editor.next_inlay_id),
19755 Anchor::in_buffer(excerpt_id, buffer_id, hint.position),
19756 hint.text(),
19757 );
19758 if !inlay.text.chars().contains(&'\n') {
19759 new_inlays.push(inlay);
19760 }
19761 });
19762 }
19763
19764 let mut inlay_ids = new_inlays.iter().map(|inlay| inlay.id).collect();
19765 std::mem::swap(&mut editor.inline_value_cache.inlays, &mut inlay_ids);
19766
19767 editor.splice_inlays(&inlay_ids, new_inlays, cx);
19768 })
19769 .ok()?;
19770 Some(())
19771 });
19772 }
19773
19774 fn on_buffer_event(
19775 &mut self,
19776 multibuffer: &Entity<MultiBuffer>,
19777 event: &multi_buffer::Event,
19778 window: &mut Window,
19779 cx: &mut Context<Self>,
19780 ) {
19781 match event {
19782 multi_buffer::Event::Edited {
19783 singleton_buffer_edited,
19784 edited_buffer,
19785 } => {
19786 self.scrollbar_marker_state.dirty = true;
19787 self.active_indent_guides_state.dirty = true;
19788 self.refresh_active_diagnostics(cx);
19789 self.refresh_code_actions(window, cx);
19790 self.refresh_selected_text_highlights(true, window, cx);
19791 self.refresh_single_line_folds(window, cx);
19792 refresh_matching_bracket_highlights(self, window, cx);
19793 if self.has_active_inline_completion() {
19794 self.update_visible_inline_completion(window, cx);
19795 }
19796 if let Some(project) = self.project.as_ref() {
19797 if let Some(edited_buffer) = edited_buffer {
19798 project.update(cx, |project, cx| {
19799 self.registered_buffers
19800 .entry(edited_buffer.read(cx).remote_id())
19801 .or_insert_with(|| {
19802 project
19803 .register_buffer_with_language_servers(&edited_buffer, cx)
19804 });
19805 });
19806 }
19807 }
19808 cx.emit(EditorEvent::BufferEdited);
19809 cx.emit(SearchEvent::MatchesInvalidated);
19810
19811 if let Some(buffer) = edited_buffer {
19812 self.update_lsp_data(false, Some(buffer.read(cx).remote_id()), window, cx);
19813 }
19814
19815 if *singleton_buffer_edited {
19816 if let Some(buffer) = edited_buffer {
19817 if buffer.read(cx).file().is_none() {
19818 cx.emit(EditorEvent::TitleChanged);
19819 }
19820 }
19821 if let Some(project) = &self.project {
19822 #[allow(clippy::mutable_key_type)]
19823 let languages_affected = multibuffer.update(cx, |multibuffer, cx| {
19824 multibuffer
19825 .all_buffers()
19826 .into_iter()
19827 .filter_map(|buffer| {
19828 buffer.update(cx, |buffer, cx| {
19829 let language = buffer.language()?;
19830 let should_discard = project.update(cx, |project, cx| {
19831 project.is_local()
19832 && !project.has_language_servers_for(buffer, cx)
19833 });
19834 should_discard.not().then_some(language.clone())
19835 })
19836 })
19837 .collect::<HashSet<_>>()
19838 });
19839 if !languages_affected.is_empty() {
19840 self.refresh_inlay_hints(
19841 InlayHintRefreshReason::BufferEdited(languages_affected),
19842 cx,
19843 );
19844 }
19845 }
19846 }
19847
19848 let Some(project) = &self.project else { return };
19849 let (telemetry, is_via_ssh) = {
19850 let project = project.read(cx);
19851 let telemetry = project.client().telemetry().clone();
19852 let is_via_ssh = project.is_via_ssh();
19853 (telemetry, is_via_ssh)
19854 };
19855 refresh_linked_ranges(self, window, cx);
19856 telemetry.log_edit_event("editor", is_via_ssh);
19857 }
19858 multi_buffer::Event::ExcerptsAdded {
19859 buffer,
19860 predecessor,
19861 excerpts,
19862 } => {
19863 self.tasks_update_task = Some(self.refresh_runnables(window, cx));
19864 let buffer_id = buffer.read(cx).remote_id();
19865 if self.buffer.read(cx).diff_for(buffer_id).is_none() {
19866 if let Some(project) = &self.project {
19867 update_uncommitted_diff_for_buffer(
19868 cx.entity(),
19869 project,
19870 [buffer.clone()],
19871 self.buffer.clone(),
19872 cx,
19873 )
19874 .detach();
19875 }
19876 }
19877 self.update_lsp_data(false, Some(buffer_id), window, cx);
19878 cx.emit(EditorEvent::ExcerptsAdded {
19879 buffer: buffer.clone(),
19880 predecessor: *predecessor,
19881 excerpts: excerpts.clone(),
19882 });
19883 self.refresh_inlay_hints(InlayHintRefreshReason::NewLinesShown, cx);
19884 }
19885 multi_buffer::Event::ExcerptsRemoved {
19886 ids,
19887 removed_buffer_ids,
19888 } => {
19889 self.refresh_inlay_hints(InlayHintRefreshReason::ExcerptsRemoved(ids.clone()), cx);
19890 let buffer = self.buffer.read(cx);
19891 self.registered_buffers
19892 .retain(|buffer_id, _| buffer.buffer(*buffer_id).is_some());
19893 jsx_tag_auto_close::refresh_enabled_in_any_buffer(self, multibuffer, cx);
19894 cx.emit(EditorEvent::ExcerptsRemoved {
19895 ids: ids.clone(),
19896 removed_buffer_ids: removed_buffer_ids.clone(),
19897 });
19898 }
19899 multi_buffer::Event::ExcerptsEdited {
19900 excerpt_ids,
19901 buffer_ids,
19902 } => {
19903 self.display_map.update(cx, |map, cx| {
19904 map.unfold_buffers(buffer_ids.iter().copied(), cx)
19905 });
19906 cx.emit(EditorEvent::ExcerptsEdited {
19907 ids: excerpt_ids.clone(),
19908 });
19909 }
19910 multi_buffer::Event::ExcerptsExpanded { ids } => {
19911 self.refresh_inlay_hints(InlayHintRefreshReason::NewLinesShown, cx);
19912 cx.emit(EditorEvent::ExcerptsExpanded { ids: ids.clone() })
19913 }
19914 multi_buffer::Event::Reparsed(buffer_id) => {
19915 self.tasks_update_task = Some(self.refresh_runnables(window, cx));
19916 jsx_tag_auto_close::refresh_enabled_in_any_buffer(self, multibuffer, cx);
19917
19918 cx.emit(EditorEvent::Reparsed(*buffer_id));
19919 }
19920 multi_buffer::Event::DiffHunksToggled => {
19921 self.tasks_update_task = Some(self.refresh_runnables(window, cx));
19922 }
19923 multi_buffer::Event::LanguageChanged(buffer_id) => {
19924 linked_editing_ranges::refresh_linked_ranges(self, window, cx);
19925 jsx_tag_auto_close::refresh_enabled_in_any_buffer(self, multibuffer, cx);
19926 cx.emit(EditorEvent::Reparsed(*buffer_id));
19927 cx.notify();
19928 }
19929 multi_buffer::Event::DirtyChanged => cx.emit(EditorEvent::DirtyChanged),
19930 multi_buffer::Event::Saved => cx.emit(EditorEvent::Saved),
19931 multi_buffer::Event::FileHandleChanged
19932 | multi_buffer::Event::Reloaded
19933 | multi_buffer::Event::BufferDiffChanged => cx.emit(EditorEvent::TitleChanged),
19934 multi_buffer::Event::Closed => cx.emit(EditorEvent::Closed),
19935 multi_buffer::Event::DiagnosticsUpdated => {
19936 self.update_diagnostics_state(window, cx);
19937 }
19938 _ => {}
19939 };
19940 }
19941
19942 fn update_diagnostics_state(&mut self, window: &mut Window, cx: &mut Context<'_, Editor>) {
19943 if !self.diagnostics_enabled() {
19944 return;
19945 }
19946 self.refresh_active_diagnostics(cx);
19947 self.refresh_inline_diagnostics(true, window, cx);
19948 self.scrollbar_marker_state.dirty = true;
19949 cx.notify();
19950 }
19951
19952 pub fn start_temporary_diff_override(&mut self) {
19953 self.load_diff_task.take();
19954 self.temporary_diff_override = true;
19955 }
19956
19957 pub fn end_temporary_diff_override(&mut self, cx: &mut Context<Self>) {
19958 self.temporary_diff_override = false;
19959 self.set_render_diff_hunk_controls(Arc::new(render_diff_hunk_controls), cx);
19960 self.buffer.update(cx, |buffer, cx| {
19961 buffer.set_all_diff_hunks_collapsed(cx);
19962 });
19963
19964 if let Some(project) = self.project.clone() {
19965 self.load_diff_task = Some(
19966 update_uncommitted_diff_for_buffer(
19967 cx.entity(),
19968 &project,
19969 self.buffer.read(cx).all_buffers(),
19970 self.buffer.clone(),
19971 cx,
19972 )
19973 .shared(),
19974 );
19975 }
19976 }
19977
19978 fn on_display_map_changed(
19979 &mut self,
19980 _: Entity<DisplayMap>,
19981 _: &mut Window,
19982 cx: &mut Context<Self>,
19983 ) {
19984 cx.notify();
19985 }
19986
19987 fn settings_changed(&mut self, window: &mut Window, cx: &mut Context<Self>) {
19988 if self.diagnostics_enabled() {
19989 let new_severity = EditorSettings::get_global(cx)
19990 .diagnostics_max_severity
19991 .unwrap_or(DiagnosticSeverity::Hint);
19992 self.set_max_diagnostics_severity(new_severity, cx);
19993 }
19994 self.tasks_update_task = Some(self.refresh_runnables(window, cx));
19995 self.update_edit_prediction_settings(cx);
19996 self.refresh_inline_completion(true, false, window, cx);
19997 self.refresh_inline_values(cx);
19998 self.refresh_inlay_hints(
19999 InlayHintRefreshReason::SettingsChange(inlay_hint_settings(
20000 self.selections.newest_anchor().head(),
20001 &self.buffer.read(cx).snapshot(cx),
20002 cx,
20003 )),
20004 cx,
20005 );
20006
20007 let old_cursor_shape = self.cursor_shape;
20008
20009 {
20010 let editor_settings = EditorSettings::get_global(cx);
20011 self.scroll_manager.vertical_scroll_margin = editor_settings.vertical_scroll_margin;
20012 self.show_breadcrumbs = editor_settings.toolbar.breadcrumbs;
20013 self.cursor_shape = editor_settings.cursor_shape.unwrap_or_default();
20014 self.hide_mouse_mode = editor_settings.hide_mouse.unwrap_or_default();
20015 }
20016
20017 if old_cursor_shape != self.cursor_shape {
20018 cx.emit(EditorEvent::CursorShapeChanged);
20019 }
20020
20021 let project_settings = ProjectSettings::get_global(cx);
20022 self.serialize_dirty_buffers =
20023 !self.mode.is_minimap() && project_settings.session.restore_unsaved_buffers;
20024
20025 if self.mode.is_full() {
20026 let show_inline_diagnostics = project_settings.diagnostics.inline.enabled;
20027 let inline_blame_enabled = project_settings.git.inline_blame_enabled();
20028 if self.show_inline_diagnostics != show_inline_diagnostics {
20029 self.show_inline_diagnostics = show_inline_diagnostics;
20030 self.refresh_inline_diagnostics(false, window, cx);
20031 }
20032
20033 if self.git_blame_inline_enabled != inline_blame_enabled {
20034 self.toggle_git_blame_inline_internal(false, window, cx);
20035 }
20036
20037 let minimap_settings = EditorSettings::get_global(cx).minimap;
20038 if self.minimap_visibility != MinimapVisibility::Disabled {
20039 if self.minimap_visibility.settings_visibility()
20040 != minimap_settings.minimap_enabled()
20041 {
20042 self.set_minimap_visibility(
20043 MinimapVisibility::for_mode(self.mode(), cx),
20044 window,
20045 cx,
20046 );
20047 } else if let Some(minimap_entity) = self.minimap.as_ref() {
20048 minimap_entity.update(cx, |minimap_editor, cx| {
20049 minimap_editor.update_minimap_configuration(minimap_settings, cx)
20050 })
20051 }
20052 }
20053 }
20054
20055 if let Some(inlay_splice) = self.colors.as_mut().and_then(|colors| {
20056 colors.render_mode_updated(EditorSettings::get_global(cx).lsp_document_colors)
20057 }) {
20058 if !inlay_splice.to_insert.is_empty() || !inlay_splice.to_remove.is_empty() {
20059 self.splice_inlays(&inlay_splice.to_remove, inlay_splice.to_insert, cx);
20060 }
20061 self.refresh_colors(false, None, window, cx);
20062 }
20063
20064 cx.notify();
20065 }
20066
20067 pub fn set_searchable(&mut self, searchable: bool) {
20068 self.searchable = searchable;
20069 }
20070
20071 pub fn searchable(&self) -> bool {
20072 self.searchable
20073 }
20074
20075 fn open_proposed_changes_editor(
20076 &mut self,
20077 _: &OpenProposedChangesEditor,
20078 window: &mut Window,
20079 cx: &mut Context<Self>,
20080 ) {
20081 let Some(workspace) = self.workspace() else {
20082 cx.propagate();
20083 return;
20084 };
20085
20086 let selections = self.selections.all::<usize>(cx);
20087 let multi_buffer = self.buffer.read(cx);
20088 let multi_buffer_snapshot = multi_buffer.snapshot(cx);
20089 let mut new_selections_by_buffer = HashMap::default();
20090 for selection in selections {
20091 for (buffer, range, _) in
20092 multi_buffer_snapshot.range_to_buffer_ranges(selection.start..selection.end)
20093 {
20094 let mut range = range.to_point(buffer);
20095 range.start.column = 0;
20096 range.end.column = buffer.line_len(range.end.row);
20097 new_selections_by_buffer
20098 .entry(multi_buffer.buffer(buffer.remote_id()).unwrap())
20099 .or_insert(Vec::new())
20100 .push(range)
20101 }
20102 }
20103
20104 let proposed_changes_buffers = new_selections_by_buffer
20105 .into_iter()
20106 .map(|(buffer, ranges)| ProposedChangeLocation { buffer, ranges })
20107 .collect::<Vec<_>>();
20108 let proposed_changes_editor = cx.new(|cx| {
20109 ProposedChangesEditor::new(
20110 "Proposed changes",
20111 proposed_changes_buffers,
20112 self.project.clone(),
20113 window,
20114 cx,
20115 )
20116 });
20117
20118 window.defer(cx, move |window, cx| {
20119 workspace.update(cx, |workspace, cx| {
20120 workspace.active_pane().update(cx, |pane, cx| {
20121 pane.add_item(
20122 Box::new(proposed_changes_editor),
20123 true,
20124 true,
20125 None,
20126 window,
20127 cx,
20128 );
20129 });
20130 });
20131 });
20132 }
20133
20134 pub fn open_excerpts_in_split(
20135 &mut self,
20136 _: &OpenExcerptsSplit,
20137 window: &mut Window,
20138 cx: &mut Context<Self>,
20139 ) {
20140 self.open_excerpts_common(None, true, window, cx)
20141 }
20142
20143 pub fn open_excerpts(&mut self, _: &OpenExcerpts, window: &mut Window, cx: &mut Context<Self>) {
20144 self.open_excerpts_common(None, false, window, cx)
20145 }
20146
20147 fn open_excerpts_common(
20148 &mut self,
20149 jump_data: Option<JumpData>,
20150 split: bool,
20151 window: &mut Window,
20152 cx: &mut Context<Self>,
20153 ) {
20154 let Some(workspace) = self.workspace() else {
20155 cx.propagate();
20156 return;
20157 };
20158
20159 if self.buffer.read(cx).is_singleton() {
20160 cx.propagate();
20161 return;
20162 }
20163
20164 let mut new_selections_by_buffer = HashMap::default();
20165 match &jump_data {
20166 Some(JumpData::MultiBufferPoint {
20167 excerpt_id,
20168 position,
20169 anchor,
20170 line_offset_from_top,
20171 }) => {
20172 let multi_buffer_snapshot = self.buffer.read(cx).snapshot(cx);
20173 if let Some(buffer) = multi_buffer_snapshot
20174 .buffer_id_for_excerpt(*excerpt_id)
20175 .and_then(|buffer_id| self.buffer.read(cx).buffer(buffer_id))
20176 {
20177 let buffer_snapshot = buffer.read(cx).snapshot();
20178 let jump_to_point = if buffer_snapshot.can_resolve(anchor) {
20179 language::ToPoint::to_point(anchor, &buffer_snapshot)
20180 } else {
20181 buffer_snapshot.clip_point(*position, Bias::Left)
20182 };
20183 let jump_to_offset = buffer_snapshot.point_to_offset(jump_to_point);
20184 new_selections_by_buffer.insert(
20185 buffer,
20186 (
20187 vec![jump_to_offset..jump_to_offset],
20188 Some(*line_offset_from_top),
20189 ),
20190 );
20191 }
20192 }
20193 Some(JumpData::MultiBufferRow {
20194 row,
20195 line_offset_from_top,
20196 }) => {
20197 let point = MultiBufferPoint::new(row.0, 0);
20198 if let Some((buffer, buffer_point, _)) =
20199 self.buffer.read(cx).point_to_buffer_point(point, cx)
20200 {
20201 let buffer_offset = buffer.read(cx).point_to_offset(buffer_point);
20202 new_selections_by_buffer
20203 .entry(buffer)
20204 .or_insert((Vec::new(), Some(*line_offset_from_top)))
20205 .0
20206 .push(buffer_offset..buffer_offset)
20207 }
20208 }
20209 None => {
20210 let selections = self.selections.all::<usize>(cx);
20211 let multi_buffer = self.buffer.read(cx);
20212 for selection in selections {
20213 for (snapshot, range, _, anchor) in multi_buffer
20214 .snapshot(cx)
20215 .range_to_buffer_ranges_with_deleted_hunks(selection.range())
20216 {
20217 if let Some(anchor) = anchor {
20218 // selection is in a deleted hunk
20219 let Some(buffer_id) = anchor.buffer_id else {
20220 continue;
20221 };
20222 let Some(buffer_handle) = multi_buffer.buffer(buffer_id) else {
20223 continue;
20224 };
20225 let offset = text::ToOffset::to_offset(
20226 &anchor.text_anchor,
20227 &buffer_handle.read(cx).snapshot(),
20228 );
20229 let range = offset..offset;
20230 new_selections_by_buffer
20231 .entry(buffer_handle)
20232 .or_insert((Vec::new(), None))
20233 .0
20234 .push(range)
20235 } else {
20236 let Some(buffer_handle) = multi_buffer.buffer(snapshot.remote_id())
20237 else {
20238 continue;
20239 };
20240 new_selections_by_buffer
20241 .entry(buffer_handle)
20242 .or_insert((Vec::new(), None))
20243 .0
20244 .push(range)
20245 }
20246 }
20247 }
20248 }
20249 }
20250
20251 new_selections_by_buffer
20252 .retain(|buffer, _| Self::can_open_excerpts_in_file(buffer.read(cx).file()));
20253
20254 if new_selections_by_buffer.is_empty() {
20255 return;
20256 }
20257
20258 // We defer the pane interaction because we ourselves are a workspace item
20259 // and activating a new item causes the pane to call a method on us reentrantly,
20260 // which panics if we're on the stack.
20261 window.defer(cx, move |window, cx| {
20262 workspace.update(cx, |workspace, cx| {
20263 let pane = if split {
20264 workspace.adjacent_pane(window, cx)
20265 } else {
20266 workspace.active_pane().clone()
20267 };
20268
20269 for (buffer, (ranges, scroll_offset)) in new_selections_by_buffer {
20270 let editor = buffer
20271 .read(cx)
20272 .file()
20273 .is_none()
20274 .then(|| {
20275 // Handle file-less buffers separately: those are not really the project items, so won't have a project path or entity id,
20276 // so `workspace.open_project_item` will never find them, always opening a new editor.
20277 // Instead, we try to activate the existing editor in the pane first.
20278 let (editor, pane_item_index) =
20279 pane.read(cx).items().enumerate().find_map(|(i, item)| {
20280 let editor = item.downcast::<Editor>()?;
20281 let singleton_buffer =
20282 editor.read(cx).buffer().read(cx).as_singleton()?;
20283 if singleton_buffer == buffer {
20284 Some((editor, i))
20285 } else {
20286 None
20287 }
20288 })?;
20289 pane.update(cx, |pane, cx| {
20290 pane.activate_item(pane_item_index, true, true, window, cx)
20291 });
20292 Some(editor)
20293 })
20294 .flatten()
20295 .unwrap_or_else(|| {
20296 workspace.open_project_item::<Self>(
20297 pane.clone(),
20298 buffer,
20299 true,
20300 true,
20301 window,
20302 cx,
20303 )
20304 });
20305
20306 editor.update(cx, |editor, cx| {
20307 let autoscroll = match scroll_offset {
20308 Some(scroll_offset) => Autoscroll::top_relative(scroll_offset as usize),
20309 None => Autoscroll::newest(),
20310 };
20311 let nav_history = editor.nav_history.take();
20312 editor.change_selections(
20313 SelectionEffects::scroll(autoscroll),
20314 window,
20315 cx,
20316 |s| {
20317 s.select_ranges(ranges);
20318 },
20319 );
20320 editor.nav_history = nav_history;
20321 });
20322 }
20323 })
20324 });
20325 }
20326
20327 // For now, don't allow opening excerpts in buffers that aren't backed by
20328 // regular project files.
20329 fn can_open_excerpts_in_file(file: Option<&Arc<dyn language::File>>) -> bool {
20330 file.map_or(true, |file| project::File::from_dyn(Some(file)).is_some())
20331 }
20332
20333 fn marked_text_ranges(&self, cx: &App) -> Option<Vec<Range<OffsetUtf16>>> {
20334 let snapshot = self.buffer.read(cx).read(cx);
20335 let (_, ranges) = self.text_highlights::<InputComposition>(cx)?;
20336 Some(
20337 ranges
20338 .iter()
20339 .map(move |range| {
20340 range.start.to_offset_utf16(&snapshot)..range.end.to_offset_utf16(&snapshot)
20341 })
20342 .collect(),
20343 )
20344 }
20345
20346 fn selection_replacement_ranges(
20347 &self,
20348 range: Range<OffsetUtf16>,
20349 cx: &mut App,
20350 ) -> Vec<Range<OffsetUtf16>> {
20351 let selections = self.selections.all::<OffsetUtf16>(cx);
20352 let newest_selection = selections
20353 .iter()
20354 .max_by_key(|selection| selection.id)
20355 .unwrap();
20356 let start_delta = range.start.0 as isize - newest_selection.start.0 as isize;
20357 let end_delta = range.end.0 as isize - newest_selection.end.0 as isize;
20358 let snapshot = self.buffer.read(cx).read(cx);
20359 selections
20360 .into_iter()
20361 .map(|mut selection| {
20362 selection.start.0 =
20363 (selection.start.0 as isize).saturating_add(start_delta) as usize;
20364 selection.end.0 = (selection.end.0 as isize).saturating_add(end_delta) as usize;
20365 snapshot.clip_offset_utf16(selection.start, Bias::Left)
20366 ..snapshot.clip_offset_utf16(selection.end, Bias::Right)
20367 })
20368 .collect()
20369 }
20370
20371 fn report_editor_event(
20372 &self,
20373 event_type: &'static str,
20374 file_extension: Option<String>,
20375 cx: &App,
20376 ) {
20377 if cfg!(any(test, feature = "test-support")) {
20378 return;
20379 }
20380
20381 let Some(project) = &self.project else { return };
20382
20383 // If None, we are in a file without an extension
20384 let file = self
20385 .buffer
20386 .read(cx)
20387 .as_singleton()
20388 .and_then(|b| b.read(cx).file());
20389 let file_extension = file_extension.or(file
20390 .as_ref()
20391 .and_then(|file| Path::new(file.file_name(cx)).extension())
20392 .and_then(|e| e.to_str())
20393 .map(|a| a.to_string()));
20394
20395 let vim_mode = vim_enabled(cx);
20396
20397 let edit_predictions_provider = all_language_settings(file, cx).edit_predictions.provider;
20398 let copilot_enabled = edit_predictions_provider
20399 == language::language_settings::EditPredictionProvider::Copilot;
20400 let copilot_enabled_for_language = self
20401 .buffer
20402 .read(cx)
20403 .language_settings(cx)
20404 .show_edit_predictions;
20405
20406 let project = project.read(cx);
20407 telemetry::event!(
20408 event_type,
20409 file_extension,
20410 vim_mode,
20411 copilot_enabled,
20412 copilot_enabled_for_language,
20413 edit_predictions_provider,
20414 is_via_ssh = project.is_via_ssh(),
20415 );
20416 }
20417
20418 /// Copy the highlighted chunks to the clipboard as JSON. The format is an array of lines,
20419 /// with each line being an array of {text, highlight} objects.
20420 fn copy_highlight_json(
20421 &mut self,
20422 _: &CopyHighlightJson,
20423 window: &mut Window,
20424 cx: &mut Context<Self>,
20425 ) {
20426 #[derive(Serialize)]
20427 struct Chunk<'a> {
20428 text: String,
20429 highlight: Option<&'a str>,
20430 }
20431
20432 let snapshot = self.buffer.read(cx).snapshot(cx);
20433 let range = self
20434 .selected_text_range(false, window, cx)
20435 .and_then(|selection| {
20436 if selection.range.is_empty() {
20437 None
20438 } else {
20439 Some(selection.range)
20440 }
20441 })
20442 .unwrap_or_else(|| 0..snapshot.len());
20443
20444 let chunks = snapshot.chunks(range, true);
20445 let mut lines = Vec::new();
20446 let mut line: VecDeque<Chunk> = VecDeque::new();
20447
20448 let Some(style) = self.style.as_ref() else {
20449 return;
20450 };
20451
20452 for chunk in chunks {
20453 let highlight = chunk
20454 .syntax_highlight_id
20455 .and_then(|id| id.name(&style.syntax));
20456 let mut chunk_lines = chunk.text.split('\n').peekable();
20457 while let Some(text) = chunk_lines.next() {
20458 let mut merged_with_last_token = false;
20459 if let Some(last_token) = line.back_mut() {
20460 if last_token.highlight == highlight {
20461 last_token.text.push_str(text);
20462 merged_with_last_token = true;
20463 }
20464 }
20465
20466 if !merged_with_last_token {
20467 line.push_back(Chunk {
20468 text: text.into(),
20469 highlight,
20470 });
20471 }
20472
20473 if chunk_lines.peek().is_some() {
20474 if line.len() > 1 && line.front().unwrap().text.is_empty() {
20475 line.pop_front();
20476 }
20477 if line.len() > 1 && line.back().unwrap().text.is_empty() {
20478 line.pop_back();
20479 }
20480
20481 lines.push(mem::take(&mut line));
20482 }
20483 }
20484 }
20485
20486 let Some(lines) = serde_json::to_string_pretty(&lines).log_err() else {
20487 return;
20488 };
20489 cx.write_to_clipboard(ClipboardItem::new_string(lines));
20490 }
20491
20492 pub fn open_context_menu(
20493 &mut self,
20494 _: &OpenContextMenu,
20495 window: &mut Window,
20496 cx: &mut Context<Self>,
20497 ) {
20498 self.request_autoscroll(Autoscroll::newest(), cx);
20499 let position = self.selections.newest_display(cx).start;
20500 mouse_context_menu::deploy_context_menu(self, None, position, window, cx);
20501 }
20502
20503 pub fn inlay_hint_cache(&self) -> &InlayHintCache {
20504 &self.inlay_hint_cache
20505 }
20506
20507 pub fn replay_insert_event(
20508 &mut self,
20509 text: &str,
20510 relative_utf16_range: Option<Range<isize>>,
20511 window: &mut Window,
20512 cx: &mut Context<Self>,
20513 ) {
20514 if !self.input_enabled {
20515 cx.emit(EditorEvent::InputIgnored { text: text.into() });
20516 return;
20517 }
20518 if let Some(relative_utf16_range) = relative_utf16_range {
20519 let selections = self.selections.all::<OffsetUtf16>(cx);
20520 self.change_selections(SelectionEffects::no_scroll(), window, cx, |s| {
20521 let new_ranges = selections.into_iter().map(|range| {
20522 let start = OffsetUtf16(
20523 range
20524 .head()
20525 .0
20526 .saturating_add_signed(relative_utf16_range.start),
20527 );
20528 let end = OffsetUtf16(
20529 range
20530 .head()
20531 .0
20532 .saturating_add_signed(relative_utf16_range.end),
20533 );
20534 start..end
20535 });
20536 s.select_ranges(new_ranges);
20537 });
20538 }
20539
20540 self.handle_input(text, window, cx);
20541 }
20542
20543 pub fn supports_inlay_hints(&self, cx: &mut App) -> bool {
20544 let Some(provider) = self.semantics_provider.as_ref() else {
20545 return false;
20546 };
20547
20548 let mut supports = false;
20549 self.buffer().update(cx, |this, cx| {
20550 this.for_each_buffer(|buffer| {
20551 supports |= provider.supports_inlay_hints(buffer, cx);
20552 });
20553 });
20554
20555 supports
20556 }
20557
20558 pub fn is_focused(&self, window: &Window) -> bool {
20559 self.focus_handle.is_focused(window)
20560 }
20561
20562 fn handle_focus(&mut self, window: &mut Window, cx: &mut Context<Self>) {
20563 cx.emit(EditorEvent::Focused);
20564
20565 if let Some(descendant) = self
20566 .last_focused_descendant
20567 .take()
20568 .and_then(|descendant| descendant.upgrade())
20569 {
20570 window.focus(&descendant);
20571 } else {
20572 if let Some(blame) = self.blame.as_ref() {
20573 blame.update(cx, GitBlame::focus)
20574 }
20575
20576 self.blink_manager.update(cx, BlinkManager::enable);
20577 self.show_cursor_names(window, cx);
20578 self.buffer.update(cx, |buffer, cx| {
20579 buffer.finalize_last_transaction(cx);
20580 if self.leader_id.is_none() {
20581 buffer.set_active_selections(
20582 &self.selections.disjoint_anchors(),
20583 self.selections.line_mode,
20584 self.cursor_shape,
20585 cx,
20586 );
20587 }
20588 });
20589 }
20590 }
20591
20592 fn handle_focus_in(&mut self, _: &mut Window, cx: &mut Context<Self>) {
20593 cx.emit(EditorEvent::FocusedIn)
20594 }
20595
20596 fn handle_focus_out(
20597 &mut self,
20598 event: FocusOutEvent,
20599 _window: &mut Window,
20600 cx: &mut Context<Self>,
20601 ) {
20602 if event.blurred != self.focus_handle {
20603 self.last_focused_descendant = Some(event.blurred);
20604 }
20605 self.selection_drag_state = SelectionDragState::None;
20606 self.refresh_inlay_hints(InlayHintRefreshReason::ModifiersChanged(false), cx);
20607 }
20608
20609 pub fn handle_blur(&mut self, window: &mut Window, cx: &mut Context<Self>) {
20610 self.blink_manager.update(cx, BlinkManager::disable);
20611 self.buffer
20612 .update(cx, |buffer, cx| buffer.remove_active_selections(cx));
20613
20614 if let Some(blame) = self.blame.as_ref() {
20615 blame.update(cx, GitBlame::blur)
20616 }
20617 if !self.hover_state.focused(window, cx) {
20618 hide_hover(self, cx);
20619 }
20620 if !self
20621 .context_menu
20622 .borrow()
20623 .as_ref()
20624 .is_some_and(|context_menu| context_menu.focused(window, cx))
20625 {
20626 self.hide_context_menu(window, cx);
20627 }
20628 self.discard_inline_completion(false, cx);
20629 cx.emit(EditorEvent::Blurred);
20630 cx.notify();
20631 }
20632
20633 pub fn observe_pending_input(&mut self, window: &mut Window, cx: &mut Context<Self>) {
20634 let mut pending: String = window
20635 .pending_input_keystrokes()
20636 .into_iter()
20637 .flatten()
20638 .filter_map(|keystroke| {
20639 if keystroke.modifiers.is_subset_of(&Modifiers::shift()) {
20640 keystroke.key_char.clone()
20641 } else {
20642 None
20643 }
20644 })
20645 .collect();
20646
20647 if !self.input_enabled || self.read_only || !self.focus_handle.is_focused(window) {
20648 pending = "".to_string();
20649 }
20650
20651 let existing_pending = self
20652 .text_highlights::<PendingInput>(cx)
20653 .map(|(_, ranges)| ranges.iter().cloned().collect::<Vec<_>>());
20654 if existing_pending.is_none() && pending.is_empty() {
20655 return;
20656 }
20657 let transaction =
20658 self.transact(window, cx, |this, window, cx| {
20659 let selections = this.selections.all::<usize>(cx);
20660 let edits = selections
20661 .iter()
20662 .map(|selection| (selection.end..selection.end, pending.clone()));
20663 this.edit(edits, cx);
20664 this.change_selections(SelectionEffects::no_scroll(), window, cx, |s| {
20665 s.select_ranges(selections.into_iter().enumerate().map(|(ix, sel)| {
20666 sel.start + ix * pending.len()..sel.end + ix * pending.len()
20667 }));
20668 });
20669 if let Some(existing_ranges) = existing_pending {
20670 let edits = existing_ranges.iter().map(|range| (range.clone(), ""));
20671 this.edit(edits, cx);
20672 }
20673 });
20674
20675 let snapshot = self.snapshot(window, cx);
20676 let ranges = self
20677 .selections
20678 .all::<usize>(cx)
20679 .into_iter()
20680 .map(|selection| {
20681 snapshot.buffer_snapshot.anchor_after(selection.end)
20682 ..snapshot
20683 .buffer_snapshot
20684 .anchor_before(selection.end + pending.len())
20685 })
20686 .collect();
20687
20688 if pending.is_empty() {
20689 self.clear_highlights::<PendingInput>(cx);
20690 } else {
20691 self.highlight_text::<PendingInput>(
20692 ranges,
20693 HighlightStyle {
20694 underline: Some(UnderlineStyle {
20695 thickness: px(1.),
20696 color: None,
20697 wavy: false,
20698 }),
20699 ..Default::default()
20700 },
20701 cx,
20702 );
20703 }
20704
20705 self.ime_transaction = self.ime_transaction.or(transaction);
20706 if let Some(transaction) = self.ime_transaction {
20707 self.buffer.update(cx, |buffer, cx| {
20708 buffer.group_until_transaction(transaction, cx);
20709 });
20710 }
20711
20712 if self.text_highlights::<PendingInput>(cx).is_none() {
20713 self.ime_transaction.take();
20714 }
20715 }
20716
20717 pub fn register_action_renderer(
20718 &mut self,
20719 listener: impl Fn(&Editor, &mut Window, &mut Context<Editor>) + 'static,
20720 ) -> Subscription {
20721 let id = self.next_editor_action_id.post_inc();
20722 self.editor_actions
20723 .borrow_mut()
20724 .insert(id, Box::new(listener));
20725
20726 let editor_actions = self.editor_actions.clone();
20727 Subscription::new(move || {
20728 editor_actions.borrow_mut().remove(&id);
20729 })
20730 }
20731
20732 pub fn register_action<A: Action>(
20733 &mut self,
20734 listener: impl Fn(&A, &mut Window, &mut App) + 'static,
20735 ) -> Subscription {
20736 let id = self.next_editor_action_id.post_inc();
20737 let listener = Arc::new(listener);
20738 self.editor_actions.borrow_mut().insert(
20739 id,
20740 Box::new(move |_, window, _| {
20741 let listener = listener.clone();
20742 window.on_action(TypeId::of::<A>(), move |action, phase, window, cx| {
20743 let action = action.downcast_ref().unwrap();
20744 if phase == DispatchPhase::Bubble {
20745 listener(action, window, cx)
20746 }
20747 })
20748 }),
20749 );
20750
20751 let editor_actions = self.editor_actions.clone();
20752 Subscription::new(move || {
20753 editor_actions.borrow_mut().remove(&id);
20754 })
20755 }
20756
20757 pub fn file_header_size(&self) -> u32 {
20758 FILE_HEADER_HEIGHT
20759 }
20760
20761 pub fn restore(
20762 &mut self,
20763 revert_changes: HashMap<BufferId, Vec<(Range<text::Anchor>, Rope)>>,
20764 window: &mut Window,
20765 cx: &mut Context<Self>,
20766 ) {
20767 let workspace = self.workspace();
20768 let project = self.project.as_ref();
20769 let save_tasks = self.buffer().update(cx, |multi_buffer, cx| {
20770 let mut tasks = Vec::new();
20771 for (buffer_id, changes) in revert_changes {
20772 if let Some(buffer) = multi_buffer.buffer(buffer_id) {
20773 buffer.update(cx, |buffer, cx| {
20774 buffer.edit(
20775 changes
20776 .into_iter()
20777 .map(|(range, text)| (range, text.to_string())),
20778 None,
20779 cx,
20780 );
20781 });
20782
20783 if let Some(project) =
20784 project.filter(|_| multi_buffer.all_diff_hunks_expanded())
20785 {
20786 project.update(cx, |project, cx| {
20787 tasks.push((buffer.clone(), project.save_buffer(buffer, cx)));
20788 })
20789 }
20790 }
20791 }
20792 tasks
20793 });
20794 cx.spawn_in(window, async move |_, cx| {
20795 for (buffer, task) in save_tasks {
20796 let result = task.await;
20797 if result.is_err() {
20798 let Some(path) = buffer
20799 .read_with(cx, |buffer, cx| buffer.project_path(cx))
20800 .ok()
20801 else {
20802 continue;
20803 };
20804 if let Some((workspace, path)) = workspace.as_ref().zip(path) {
20805 let Some(task) = cx
20806 .update_window_entity(&workspace, |workspace, window, cx| {
20807 workspace
20808 .open_path_preview(path, None, false, false, false, window, cx)
20809 })
20810 .ok()
20811 else {
20812 continue;
20813 };
20814 task.await.log_err();
20815 }
20816 }
20817 }
20818 })
20819 .detach();
20820 self.change_selections(SelectionEffects::no_scroll(), window, cx, |selections| {
20821 selections.refresh()
20822 });
20823 }
20824
20825 pub fn to_pixel_point(
20826 &self,
20827 source: multi_buffer::Anchor,
20828 editor_snapshot: &EditorSnapshot,
20829 window: &mut Window,
20830 ) -> Option<gpui::Point<Pixels>> {
20831 let source_point = source.to_display_point(editor_snapshot);
20832 self.display_to_pixel_point(source_point, editor_snapshot, window)
20833 }
20834
20835 pub fn display_to_pixel_point(
20836 &self,
20837 source: DisplayPoint,
20838 editor_snapshot: &EditorSnapshot,
20839 window: &mut Window,
20840 ) -> Option<gpui::Point<Pixels>> {
20841 let line_height = self.style()?.text.line_height_in_pixels(window.rem_size());
20842 let text_layout_details = self.text_layout_details(window);
20843 let scroll_top = text_layout_details
20844 .scroll_anchor
20845 .scroll_position(editor_snapshot)
20846 .y;
20847
20848 if source.row().as_f32() < scroll_top.floor() {
20849 return None;
20850 }
20851 let source_x = editor_snapshot.x_for_display_point(source, &text_layout_details);
20852 let source_y = line_height * (source.row().as_f32() - scroll_top);
20853 Some(gpui::Point::new(source_x, source_y))
20854 }
20855
20856 pub fn has_visible_completions_menu(&self) -> bool {
20857 !self.edit_prediction_preview_is_active()
20858 && self.context_menu.borrow().as_ref().map_or(false, |menu| {
20859 menu.visible() && matches!(menu, CodeContextMenu::Completions(_))
20860 })
20861 }
20862
20863 pub fn register_addon<T: Addon>(&mut self, instance: T) {
20864 if self.mode.is_minimap() {
20865 return;
20866 }
20867 self.addons
20868 .insert(std::any::TypeId::of::<T>(), Box::new(instance));
20869 }
20870
20871 pub fn unregister_addon<T: Addon>(&mut self) {
20872 self.addons.remove(&std::any::TypeId::of::<T>());
20873 }
20874
20875 pub fn addon<T: Addon>(&self) -> Option<&T> {
20876 let type_id = std::any::TypeId::of::<T>();
20877 self.addons
20878 .get(&type_id)
20879 .and_then(|item| item.to_any().downcast_ref::<T>())
20880 }
20881
20882 pub fn addon_mut<T: Addon>(&mut self) -> Option<&mut T> {
20883 let type_id = std::any::TypeId::of::<T>();
20884 self.addons
20885 .get_mut(&type_id)
20886 .and_then(|item| item.to_any_mut()?.downcast_mut::<T>())
20887 }
20888
20889 fn character_dimensions(&self, window: &mut Window) -> CharacterDimensions {
20890 let text_layout_details = self.text_layout_details(window);
20891 let style = &text_layout_details.editor_style;
20892 let font_id = window.text_system().resolve_font(&style.text.font());
20893 let font_size = style.text.font_size.to_pixels(window.rem_size());
20894 let line_height = style.text.line_height_in_pixels(window.rem_size());
20895 let em_width = window.text_system().em_width(font_id, font_size).unwrap();
20896 let em_advance = window.text_system().em_advance(font_id, font_size).unwrap();
20897
20898 CharacterDimensions {
20899 em_width,
20900 em_advance,
20901 line_height,
20902 }
20903 }
20904
20905 pub fn wait_for_diff_to_load(&self) -> Option<Shared<Task<()>>> {
20906 self.load_diff_task.clone()
20907 }
20908
20909 fn read_metadata_from_db(
20910 &mut self,
20911 item_id: u64,
20912 workspace_id: WorkspaceId,
20913 window: &mut Window,
20914 cx: &mut Context<Editor>,
20915 ) {
20916 if self.is_singleton(cx)
20917 && !self.mode.is_minimap()
20918 && WorkspaceSettings::get(None, cx).restore_on_startup != RestoreOnStartupBehavior::None
20919 {
20920 let buffer_snapshot = OnceCell::new();
20921
20922 if let Some(folds) = DB.get_editor_folds(item_id, workspace_id).log_err() {
20923 if !folds.is_empty() {
20924 let snapshot =
20925 buffer_snapshot.get_or_init(|| self.buffer.read(cx).snapshot(cx));
20926 self.fold_ranges(
20927 folds
20928 .into_iter()
20929 .map(|(start, end)| {
20930 snapshot.clip_offset(start, Bias::Left)
20931 ..snapshot.clip_offset(end, Bias::Right)
20932 })
20933 .collect(),
20934 false,
20935 window,
20936 cx,
20937 );
20938 }
20939 }
20940
20941 if let Some(selections) = DB.get_editor_selections(item_id, workspace_id).log_err() {
20942 if !selections.is_empty() {
20943 let snapshot =
20944 buffer_snapshot.get_or_init(|| self.buffer.read(cx).snapshot(cx));
20945 // skip adding the initial selection to selection history
20946 self.selection_history.mode = SelectionHistoryMode::Skipping;
20947 self.change_selections(SelectionEffects::no_scroll(), window, cx, |s| {
20948 s.select_ranges(selections.into_iter().map(|(start, end)| {
20949 snapshot.clip_offset(start, Bias::Left)
20950 ..snapshot.clip_offset(end, Bias::Right)
20951 }));
20952 });
20953 self.selection_history.mode = SelectionHistoryMode::Normal;
20954 }
20955 };
20956 }
20957
20958 self.read_scroll_position_from_db(item_id, workspace_id, window, cx);
20959 }
20960
20961 fn update_lsp_data(
20962 &mut self,
20963 ignore_cache: bool,
20964 for_buffer: Option<BufferId>,
20965 window: &mut Window,
20966 cx: &mut Context<'_, Self>,
20967 ) {
20968 self.pull_diagnostics(for_buffer, window, cx);
20969 self.refresh_colors(ignore_cache, for_buffer, window, cx);
20970 }
20971}
20972
20973fn vim_enabled(cx: &App) -> bool {
20974 cx.global::<SettingsStore>()
20975 .raw_user_settings()
20976 .get("vim_mode")
20977 == Some(&serde_json::Value::Bool(true))
20978}
20979
20980fn process_completion_for_edit(
20981 completion: &Completion,
20982 intent: CompletionIntent,
20983 buffer: &Entity<Buffer>,
20984 cursor_position: &text::Anchor,
20985 cx: &mut Context<Editor>,
20986) -> CompletionEdit {
20987 let buffer = buffer.read(cx);
20988 let buffer_snapshot = buffer.snapshot();
20989 let (snippet, new_text) = if completion.is_snippet() {
20990 // Workaround for typescript language server issues so that methods don't expand within
20991 // strings and functions with type expressions. The previous point is used because the query
20992 // for function identifier doesn't match when the cursor is immediately after. See PR #30312
20993 let mut snippet_source = completion.new_text.clone();
20994 let mut previous_point = text::ToPoint::to_point(cursor_position, buffer);
20995 previous_point.column = previous_point.column.saturating_sub(1);
20996 if let Some(scope) = buffer_snapshot.language_scope_at(previous_point) {
20997 if scope.prefers_label_for_snippet_in_completion() {
20998 if let Some(label) = completion.label() {
20999 if matches!(
21000 completion.kind(),
21001 Some(CompletionItemKind::FUNCTION) | Some(CompletionItemKind::METHOD)
21002 ) {
21003 snippet_source = label;
21004 }
21005 }
21006 }
21007 }
21008 match Snippet::parse(&snippet_source).log_err() {
21009 Some(parsed_snippet) => (Some(parsed_snippet.clone()), parsed_snippet.text),
21010 None => (None, completion.new_text.clone()),
21011 }
21012 } else {
21013 (None, completion.new_text.clone())
21014 };
21015
21016 let mut range_to_replace = {
21017 let replace_range = &completion.replace_range;
21018 if let CompletionSource::Lsp {
21019 insert_range: Some(insert_range),
21020 ..
21021 } = &completion.source
21022 {
21023 debug_assert_eq!(
21024 insert_range.start, replace_range.start,
21025 "insert_range and replace_range should start at the same position"
21026 );
21027 debug_assert!(
21028 insert_range
21029 .start
21030 .cmp(&cursor_position, &buffer_snapshot)
21031 .is_le(),
21032 "insert_range should start before or at cursor position"
21033 );
21034 debug_assert!(
21035 replace_range
21036 .start
21037 .cmp(&cursor_position, &buffer_snapshot)
21038 .is_le(),
21039 "replace_range should start before or at cursor position"
21040 );
21041 debug_assert!(
21042 insert_range
21043 .end
21044 .cmp(&cursor_position, &buffer_snapshot)
21045 .is_le(),
21046 "insert_range should end before or at cursor position"
21047 );
21048
21049 let should_replace = match intent {
21050 CompletionIntent::CompleteWithInsert => false,
21051 CompletionIntent::CompleteWithReplace => true,
21052 CompletionIntent::Complete | CompletionIntent::Compose => {
21053 let insert_mode =
21054 language_settings(buffer.language().map(|l| l.name()), buffer.file(), cx)
21055 .completions
21056 .lsp_insert_mode;
21057 match insert_mode {
21058 LspInsertMode::Insert => false,
21059 LspInsertMode::Replace => true,
21060 LspInsertMode::ReplaceSubsequence => {
21061 let mut text_to_replace = buffer.chars_for_range(
21062 buffer.anchor_before(replace_range.start)
21063 ..buffer.anchor_after(replace_range.end),
21064 );
21065 let mut current_needle = text_to_replace.next();
21066 for haystack_ch in completion.label.text.chars() {
21067 if let Some(needle_ch) = current_needle {
21068 if haystack_ch.eq_ignore_ascii_case(&needle_ch) {
21069 current_needle = text_to_replace.next();
21070 }
21071 }
21072 }
21073 current_needle.is_none()
21074 }
21075 LspInsertMode::ReplaceSuffix => {
21076 if replace_range
21077 .end
21078 .cmp(&cursor_position, &buffer_snapshot)
21079 .is_gt()
21080 {
21081 let range_after_cursor = *cursor_position..replace_range.end;
21082 let text_after_cursor = buffer
21083 .text_for_range(
21084 buffer.anchor_before(range_after_cursor.start)
21085 ..buffer.anchor_after(range_after_cursor.end),
21086 )
21087 .collect::<String>()
21088 .to_ascii_lowercase();
21089 completion
21090 .label
21091 .text
21092 .to_ascii_lowercase()
21093 .ends_with(&text_after_cursor)
21094 } else {
21095 true
21096 }
21097 }
21098 }
21099 }
21100 };
21101
21102 if should_replace {
21103 replace_range.clone()
21104 } else {
21105 insert_range.clone()
21106 }
21107 } else {
21108 replace_range.clone()
21109 }
21110 };
21111
21112 if range_to_replace
21113 .end
21114 .cmp(&cursor_position, &buffer_snapshot)
21115 .is_lt()
21116 {
21117 range_to_replace.end = *cursor_position;
21118 }
21119
21120 CompletionEdit {
21121 new_text,
21122 replace_range: range_to_replace.to_offset(&buffer),
21123 snippet,
21124 }
21125}
21126
21127struct CompletionEdit {
21128 new_text: String,
21129 replace_range: Range<usize>,
21130 snippet: Option<Snippet>,
21131}
21132
21133fn insert_extra_newline_brackets(
21134 buffer: &MultiBufferSnapshot,
21135 range: Range<usize>,
21136 language: &language::LanguageScope,
21137) -> bool {
21138 let leading_whitespace_len = buffer
21139 .reversed_chars_at(range.start)
21140 .take_while(|c| c.is_whitespace() && *c != '\n')
21141 .map(|c| c.len_utf8())
21142 .sum::<usize>();
21143 let trailing_whitespace_len = buffer
21144 .chars_at(range.end)
21145 .take_while(|c| c.is_whitespace() && *c != '\n')
21146 .map(|c| c.len_utf8())
21147 .sum::<usize>();
21148 let range = range.start - leading_whitespace_len..range.end + trailing_whitespace_len;
21149
21150 language.brackets().any(|(pair, enabled)| {
21151 let pair_start = pair.start.trim_end();
21152 let pair_end = pair.end.trim_start();
21153
21154 enabled
21155 && pair.newline
21156 && buffer.contains_str_at(range.end, pair_end)
21157 && buffer.contains_str_at(range.start.saturating_sub(pair_start.len()), pair_start)
21158 })
21159}
21160
21161fn insert_extra_newline_tree_sitter(buffer: &MultiBufferSnapshot, range: Range<usize>) -> bool {
21162 let (buffer, range) = match buffer.range_to_buffer_ranges(range).as_slice() {
21163 [(buffer, range, _)] => (*buffer, range.clone()),
21164 _ => return false,
21165 };
21166 let pair = {
21167 let mut result: Option<BracketMatch> = None;
21168
21169 for pair in buffer
21170 .all_bracket_ranges(range.clone())
21171 .filter(move |pair| {
21172 pair.open_range.start <= range.start && pair.close_range.end >= range.end
21173 })
21174 {
21175 let len = pair.close_range.end - pair.open_range.start;
21176
21177 if let Some(existing) = &result {
21178 let existing_len = existing.close_range.end - existing.open_range.start;
21179 if len > existing_len {
21180 continue;
21181 }
21182 }
21183
21184 result = Some(pair);
21185 }
21186
21187 result
21188 };
21189 let Some(pair) = pair else {
21190 return false;
21191 };
21192 pair.newline_only
21193 && buffer
21194 .chars_for_range(pair.open_range.end..range.start)
21195 .chain(buffer.chars_for_range(range.end..pair.close_range.start))
21196 .all(|c| c.is_whitespace() && c != '\n')
21197}
21198
21199fn update_uncommitted_diff_for_buffer(
21200 editor: Entity<Editor>,
21201 project: &Entity<Project>,
21202 buffers: impl IntoIterator<Item = Entity<Buffer>>,
21203 buffer: Entity<MultiBuffer>,
21204 cx: &mut App,
21205) -> Task<()> {
21206 let mut tasks = Vec::new();
21207 project.update(cx, |project, cx| {
21208 for buffer in buffers {
21209 if project::File::from_dyn(buffer.read(cx).file()).is_some() {
21210 tasks.push(project.open_uncommitted_diff(buffer.clone(), cx))
21211 }
21212 }
21213 });
21214 cx.spawn(async move |cx| {
21215 let diffs = future::join_all(tasks).await;
21216 if editor
21217 .read_with(cx, |editor, _cx| editor.temporary_diff_override)
21218 .unwrap_or(false)
21219 {
21220 return;
21221 }
21222
21223 buffer
21224 .update(cx, |buffer, cx| {
21225 for diff in diffs.into_iter().flatten() {
21226 buffer.add_diff(diff, cx);
21227 }
21228 })
21229 .ok();
21230 })
21231}
21232
21233fn char_len_with_expanded_tabs(offset: usize, text: &str, tab_size: NonZeroU32) -> usize {
21234 let tab_size = tab_size.get() as usize;
21235 let mut width = offset;
21236
21237 for ch in text.chars() {
21238 width += if ch == '\t' {
21239 tab_size - (width % tab_size)
21240 } else {
21241 1
21242 };
21243 }
21244
21245 width - offset
21246}
21247
21248#[cfg(test)]
21249mod tests {
21250 use super::*;
21251
21252 #[test]
21253 fn test_string_size_with_expanded_tabs() {
21254 let nz = |val| NonZeroU32::new(val).unwrap();
21255 assert_eq!(char_len_with_expanded_tabs(0, "", nz(4)), 0);
21256 assert_eq!(char_len_with_expanded_tabs(0, "hello", nz(4)), 5);
21257 assert_eq!(char_len_with_expanded_tabs(0, "\thello", nz(4)), 9);
21258 assert_eq!(char_len_with_expanded_tabs(0, "abc\tab", nz(4)), 6);
21259 assert_eq!(char_len_with_expanded_tabs(0, "hello\t", nz(4)), 8);
21260 assert_eq!(char_len_with_expanded_tabs(0, "\t\t", nz(8)), 16);
21261 assert_eq!(char_len_with_expanded_tabs(0, "x\t", nz(8)), 8);
21262 assert_eq!(char_len_with_expanded_tabs(7, "x\t", nz(8)), 9);
21263 }
21264}
21265
21266/// Tokenizes a string into runs of text that should stick together, or that is whitespace.
21267struct WordBreakingTokenizer<'a> {
21268 input: &'a str,
21269}
21270
21271impl<'a> WordBreakingTokenizer<'a> {
21272 fn new(input: &'a str) -> Self {
21273 Self { input }
21274 }
21275}
21276
21277fn is_char_ideographic(ch: char) -> bool {
21278 use unicode_script::Script::*;
21279 use unicode_script::UnicodeScript;
21280 matches!(ch.script(), Han | Tangut | Yi)
21281}
21282
21283fn is_grapheme_ideographic(text: &str) -> bool {
21284 text.chars().any(is_char_ideographic)
21285}
21286
21287fn is_grapheme_whitespace(text: &str) -> bool {
21288 text.chars().any(|x| x.is_whitespace())
21289}
21290
21291fn should_stay_with_preceding_ideograph(text: &str) -> bool {
21292 text.chars().next().map_or(false, |ch| {
21293 matches!(ch, '。' | '、' | ',' | '?' | '!' | ':' | ';' | '…')
21294 })
21295}
21296
21297#[derive(PartialEq, Eq, Debug, Clone, Copy)]
21298enum WordBreakToken<'a> {
21299 Word { token: &'a str, grapheme_len: usize },
21300 InlineWhitespace { token: &'a str, grapheme_len: usize },
21301 Newline,
21302}
21303
21304impl<'a> Iterator for WordBreakingTokenizer<'a> {
21305 /// Yields a span, the count of graphemes in the token, and whether it was
21306 /// whitespace. Note that it also breaks at word boundaries.
21307 type Item = WordBreakToken<'a>;
21308
21309 fn next(&mut self) -> Option<Self::Item> {
21310 use unicode_segmentation::UnicodeSegmentation;
21311 if self.input.is_empty() {
21312 return None;
21313 }
21314
21315 let mut iter = self.input.graphemes(true).peekable();
21316 let mut offset = 0;
21317 let mut grapheme_len = 0;
21318 if let Some(first_grapheme) = iter.next() {
21319 let is_newline = first_grapheme == "\n";
21320 let is_whitespace = is_grapheme_whitespace(first_grapheme);
21321 offset += first_grapheme.len();
21322 grapheme_len += 1;
21323 if is_grapheme_ideographic(first_grapheme) && !is_whitespace {
21324 if let Some(grapheme) = iter.peek().copied() {
21325 if should_stay_with_preceding_ideograph(grapheme) {
21326 offset += grapheme.len();
21327 grapheme_len += 1;
21328 }
21329 }
21330 } else {
21331 let mut words = self.input[offset..].split_word_bound_indices().peekable();
21332 let mut next_word_bound = words.peek().copied();
21333 if next_word_bound.map_or(false, |(i, _)| i == 0) {
21334 next_word_bound = words.next();
21335 }
21336 while let Some(grapheme) = iter.peek().copied() {
21337 if next_word_bound.map_or(false, |(i, _)| i == offset) {
21338 break;
21339 };
21340 if is_grapheme_whitespace(grapheme) != is_whitespace
21341 || (grapheme == "\n") != is_newline
21342 {
21343 break;
21344 };
21345 offset += grapheme.len();
21346 grapheme_len += 1;
21347 iter.next();
21348 }
21349 }
21350 let token = &self.input[..offset];
21351 self.input = &self.input[offset..];
21352 if token == "\n" {
21353 Some(WordBreakToken::Newline)
21354 } else if is_whitespace {
21355 Some(WordBreakToken::InlineWhitespace {
21356 token,
21357 grapheme_len,
21358 })
21359 } else {
21360 Some(WordBreakToken::Word {
21361 token,
21362 grapheme_len,
21363 })
21364 }
21365 } else {
21366 None
21367 }
21368 }
21369}
21370
21371#[test]
21372fn test_word_breaking_tokenizer() {
21373 let tests: &[(&str, &[WordBreakToken<'static>])] = &[
21374 ("", &[]),
21375 (" ", &[whitespace(" ", 2)]),
21376 ("Ʒ", &[word("Ʒ", 1)]),
21377 ("Ǽ", &[word("Ǽ", 1)]),
21378 ("⋑", &[word("⋑", 1)]),
21379 ("⋑⋑", &[word("⋑⋑", 2)]),
21380 (
21381 "原理,进而",
21382 &[word("原", 1), word("理,", 2), word("进", 1), word("而", 1)],
21383 ),
21384 (
21385 "hello world",
21386 &[word("hello", 5), whitespace(" ", 1), word("world", 5)],
21387 ),
21388 (
21389 "hello, world",
21390 &[word("hello,", 6), whitespace(" ", 1), word("world", 5)],
21391 ),
21392 (
21393 " hello world",
21394 &[
21395 whitespace(" ", 2),
21396 word("hello", 5),
21397 whitespace(" ", 1),
21398 word("world", 5),
21399 ],
21400 ),
21401 (
21402 "这是什么 \n 钢笔",
21403 &[
21404 word("这", 1),
21405 word("是", 1),
21406 word("什", 1),
21407 word("么", 1),
21408 whitespace(" ", 1),
21409 newline(),
21410 whitespace(" ", 1),
21411 word("钢", 1),
21412 word("笔", 1),
21413 ],
21414 ),
21415 (" mutton", &[whitespace(" ", 1), word("mutton", 6)]),
21416 ];
21417
21418 fn word(token: &'static str, grapheme_len: usize) -> WordBreakToken<'static> {
21419 WordBreakToken::Word {
21420 token,
21421 grapheme_len,
21422 }
21423 }
21424
21425 fn whitespace(token: &'static str, grapheme_len: usize) -> WordBreakToken<'static> {
21426 WordBreakToken::InlineWhitespace {
21427 token,
21428 grapheme_len,
21429 }
21430 }
21431
21432 fn newline() -> WordBreakToken<'static> {
21433 WordBreakToken::Newline
21434 }
21435
21436 for (input, result) in tests {
21437 assert_eq!(
21438 WordBreakingTokenizer::new(input)
21439 .collect::<Vec<_>>()
21440 .as_slice(),
21441 *result,
21442 );
21443 }
21444}
21445
21446fn wrap_with_prefix(
21447 first_line_prefix: String,
21448 subsequent_lines_prefix: String,
21449 unwrapped_text: String,
21450 wrap_column: usize,
21451 tab_size: NonZeroU32,
21452 preserve_existing_whitespace: bool,
21453) -> String {
21454 let first_line_prefix_len = char_len_with_expanded_tabs(0, &first_line_prefix, tab_size);
21455 let subsequent_lines_prefix_len =
21456 char_len_with_expanded_tabs(0, &subsequent_lines_prefix, tab_size);
21457 let mut wrapped_text = String::new();
21458 let mut current_line = first_line_prefix.clone();
21459 let mut is_first_line = true;
21460
21461 let tokenizer = WordBreakingTokenizer::new(&unwrapped_text);
21462 let mut current_line_len = first_line_prefix_len;
21463 let mut in_whitespace = false;
21464 for token in tokenizer {
21465 let have_preceding_whitespace = in_whitespace;
21466 match token {
21467 WordBreakToken::Word {
21468 token,
21469 grapheme_len,
21470 } => {
21471 in_whitespace = false;
21472 let current_prefix_len = if is_first_line {
21473 first_line_prefix_len
21474 } else {
21475 subsequent_lines_prefix_len
21476 };
21477 if current_line_len + grapheme_len > wrap_column
21478 && current_line_len != current_prefix_len
21479 {
21480 wrapped_text.push_str(current_line.trim_end());
21481 wrapped_text.push('\n');
21482 is_first_line = false;
21483 current_line = subsequent_lines_prefix.clone();
21484 current_line_len = subsequent_lines_prefix_len;
21485 }
21486 current_line.push_str(token);
21487 current_line_len += grapheme_len;
21488 }
21489 WordBreakToken::InlineWhitespace {
21490 mut token,
21491 mut grapheme_len,
21492 } => {
21493 in_whitespace = true;
21494 if have_preceding_whitespace && !preserve_existing_whitespace {
21495 continue;
21496 }
21497 if !preserve_existing_whitespace {
21498 token = " ";
21499 grapheme_len = 1;
21500 }
21501 let current_prefix_len = if is_first_line {
21502 first_line_prefix_len
21503 } else {
21504 subsequent_lines_prefix_len
21505 };
21506 if current_line_len + grapheme_len > wrap_column {
21507 wrapped_text.push_str(current_line.trim_end());
21508 wrapped_text.push('\n');
21509 is_first_line = false;
21510 current_line = subsequent_lines_prefix.clone();
21511 current_line_len = subsequent_lines_prefix_len;
21512 } else if current_line_len != current_prefix_len || preserve_existing_whitespace {
21513 current_line.push_str(token);
21514 current_line_len += grapheme_len;
21515 }
21516 }
21517 WordBreakToken::Newline => {
21518 in_whitespace = true;
21519 let current_prefix_len = if is_first_line {
21520 first_line_prefix_len
21521 } else {
21522 subsequent_lines_prefix_len
21523 };
21524 if preserve_existing_whitespace {
21525 wrapped_text.push_str(current_line.trim_end());
21526 wrapped_text.push('\n');
21527 is_first_line = false;
21528 current_line = subsequent_lines_prefix.clone();
21529 current_line_len = subsequent_lines_prefix_len;
21530 } else if have_preceding_whitespace {
21531 continue;
21532 } else if current_line_len + 1 > wrap_column
21533 && current_line_len != current_prefix_len
21534 {
21535 wrapped_text.push_str(current_line.trim_end());
21536 wrapped_text.push('\n');
21537 is_first_line = false;
21538 current_line = subsequent_lines_prefix.clone();
21539 current_line_len = subsequent_lines_prefix_len;
21540 } else if current_line_len != current_prefix_len {
21541 current_line.push(' ');
21542 current_line_len += 1;
21543 }
21544 }
21545 }
21546 }
21547
21548 if !current_line.is_empty() {
21549 wrapped_text.push_str(¤t_line);
21550 }
21551 wrapped_text
21552}
21553
21554#[test]
21555fn test_wrap_with_prefix() {
21556 assert_eq!(
21557 wrap_with_prefix(
21558 "# ".to_string(),
21559 "# ".to_string(),
21560 "abcdefg".to_string(),
21561 4,
21562 NonZeroU32::new(4).unwrap(),
21563 false,
21564 ),
21565 "# abcdefg"
21566 );
21567 assert_eq!(
21568 wrap_with_prefix(
21569 "".to_string(),
21570 "".to_string(),
21571 "\thello world".to_string(),
21572 8,
21573 NonZeroU32::new(4).unwrap(),
21574 false,
21575 ),
21576 "hello\nworld"
21577 );
21578 assert_eq!(
21579 wrap_with_prefix(
21580 "// ".to_string(),
21581 "// ".to_string(),
21582 "xx \nyy zz aa bb cc".to_string(),
21583 12,
21584 NonZeroU32::new(4).unwrap(),
21585 false,
21586 ),
21587 "// xx yy zz\n// aa bb cc"
21588 );
21589 assert_eq!(
21590 wrap_with_prefix(
21591 String::new(),
21592 String::new(),
21593 "这是什么 \n 钢笔".to_string(),
21594 3,
21595 NonZeroU32::new(4).unwrap(),
21596 false,
21597 ),
21598 "这是什\n么 钢\n笔"
21599 );
21600}
21601
21602pub trait CollaborationHub {
21603 fn collaborators<'a>(&self, cx: &'a App) -> &'a HashMap<PeerId, Collaborator>;
21604 fn user_participant_indices<'a>(&self, cx: &'a App) -> &'a HashMap<u64, ParticipantIndex>;
21605 fn user_names(&self, cx: &App) -> HashMap<u64, SharedString>;
21606}
21607
21608impl CollaborationHub for Entity<Project> {
21609 fn collaborators<'a>(&self, cx: &'a App) -> &'a HashMap<PeerId, Collaborator> {
21610 self.read(cx).collaborators()
21611 }
21612
21613 fn user_participant_indices<'a>(&self, cx: &'a App) -> &'a HashMap<u64, ParticipantIndex> {
21614 self.read(cx).user_store().read(cx).participant_indices()
21615 }
21616
21617 fn user_names(&self, cx: &App) -> HashMap<u64, SharedString> {
21618 let this = self.read(cx);
21619 let user_ids = this.collaborators().values().map(|c| c.user_id);
21620 this.user_store().read(cx).participant_names(user_ids, cx)
21621 }
21622}
21623
21624pub trait SemanticsProvider {
21625 fn hover(
21626 &self,
21627 buffer: &Entity<Buffer>,
21628 position: text::Anchor,
21629 cx: &mut App,
21630 ) -> Option<Task<Vec<project::Hover>>>;
21631
21632 fn inline_values(
21633 &self,
21634 buffer_handle: Entity<Buffer>,
21635 range: Range<text::Anchor>,
21636 cx: &mut App,
21637 ) -> Option<Task<anyhow::Result<Vec<InlayHint>>>>;
21638
21639 fn inlay_hints(
21640 &self,
21641 buffer_handle: Entity<Buffer>,
21642 range: Range<text::Anchor>,
21643 cx: &mut App,
21644 ) -> Option<Task<anyhow::Result<Vec<InlayHint>>>>;
21645
21646 fn resolve_inlay_hint(
21647 &self,
21648 hint: InlayHint,
21649 buffer_handle: Entity<Buffer>,
21650 server_id: LanguageServerId,
21651 cx: &mut App,
21652 ) -> Option<Task<anyhow::Result<InlayHint>>>;
21653
21654 fn supports_inlay_hints(&self, buffer: &Entity<Buffer>, cx: &mut App) -> bool;
21655
21656 fn document_highlights(
21657 &self,
21658 buffer: &Entity<Buffer>,
21659 position: text::Anchor,
21660 cx: &mut App,
21661 ) -> Option<Task<Result<Vec<DocumentHighlight>>>>;
21662
21663 fn definitions(
21664 &self,
21665 buffer: &Entity<Buffer>,
21666 position: text::Anchor,
21667 kind: GotoDefinitionKind,
21668 cx: &mut App,
21669 ) -> Option<Task<Result<Vec<LocationLink>>>>;
21670
21671 fn range_for_rename(
21672 &self,
21673 buffer: &Entity<Buffer>,
21674 position: text::Anchor,
21675 cx: &mut App,
21676 ) -> Option<Task<Result<Option<Range<text::Anchor>>>>>;
21677
21678 fn perform_rename(
21679 &self,
21680 buffer: &Entity<Buffer>,
21681 position: text::Anchor,
21682 new_name: String,
21683 cx: &mut App,
21684 ) -> Option<Task<Result<ProjectTransaction>>>;
21685}
21686
21687pub trait CompletionProvider {
21688 fn completions(
21689 &self,
21690 excerpt_id: ExcerptId,
21691 buffer: &Entity<Buffer>,
21692 buffer_position: text::Anchor,
21693 trigger: CompletionContext,
21694 window: &mut Window,
21695 cx: &mut Context<Editor>,
21696 ) -> Task<Result<Vec<CompletionResponse>>>;
21697
21698 fn resolve_completions(
21699 &self,
21700 _buffer: Entity<Buffer>,
21701 _completion_indices: Vec<usize>,
21702 _completions: Rc<RefCell<Box<[Completion]>>>,
21703 _cx: &mut Context<Editor>,
21704 ) -> Task<Result<bool>> {
21705 Task::ready(Ok(false))
21706 }
21707
21708 fn apply_additional_edits_for_completion(
21709 &self,
21710 _buffer: Entity<Buffer>,
21711 _completions: Rc<RefCell<Box<[Completion]>>>,
21712 _completion_index: usize,
21713 _push_to_history: bool,
21714 _cx: &mut Context<Editor>,
21715 ) -> Task<Result<Option<language::Transaction>>> {
21716 Task::ready(Ok(None))
21717 }
21718
21719 fn is_completion_trigger(
21720 &self,
21721 buffer: &Entity<Buffer>,
21722 position: language::Anchor,
21723 text: &str,
21724 trigger_in_words: bool,
21725 menu_is_open: bool,
21726 cx: &mut Context<Editor>,
21727 ) -> bool;
21728
21729 fn selection_changed(&self, _mat: Option<&StringMatch>, _window: &mut Window, _cx: &mut App) {}
21730
21731 fn sort_completions(&self) -> bool {
21732 true
21733 }
21734
21735 fn filter_completions(&self) -> bool {
21736 true
21737 }
21738}
21739
21740pub trait CodeActionProvider {
21741 fn id(&self) -> Arc<str>;
21742
21743 fn code_actions(
21744 &self,
21745 buffer: &Entity<Buffer>,
21746 range: Range<text::Anchor>,
21747 window: &mut Window,
21748 cx: &mut App,
21749 ) -> Task<Result<Vec<CodeAction>>>;
21750
21751 fn apply_code_action(
21752 &self,
21753 buffer_handle: Entity<Buffer>,
21754 action: CodeAction,
21755 excerpt_id: ExcerptId,
21756 push_to_history: bool,
21757 window: &mut Window,
21758 cx: &mut App,
21759 ) -> Task<Result<ProjectTransaction>>;
21760}
21761
21762impl CodeActionProvider for Entity<Project> {
21763 fn id(&self) -> Arc<str> {
21764 "project".into()
21765 }
21766
21767 fn code_actions(
21768 &self,
21769 buffer: &Entity<Buffer>,
21770 range: Range<text::Anchor>,
21771 _window: &mut Window,
21772 cx: &mut App,
21773 ) -> Task<Result<Vec<CodeAction>>> {
21774 self.update(cx, |project, cx| {
21775 let code_lens = project.code_lens(buffer, range.clone(), cx);
21776 let code_actions = project.code_actions(buffer, range, None, cx);
21777 cx.background_spawn(async move {
21778 let (code_lens, code_actions) = join(code_lens, code_actions).await;
21779 Ok(code_lens
21780 .context("code lens fetch")?
21781 .into_iter()
21782 .chain(code_actions.context("code action fetch")?)
21783 .collect())
21784 })
21785 })
21786 }
21787
21788 fn apply_code_action(
21789 &self,
21790 buffer_handle: Entity<Buffer>,
21791 action: CodeAction,
21792 _excerpt_id: ExcerptId,
21793 push_to_history: bool,
21794 _window: &mut Window,
21795 cx: &mut App,
21796 ) -> Task<Result<ProjectTransaction>> {
21797 self.update(cx, |project, cx| {
21798 project.apply_code_action(buffer_handle, action, push_to_history, cx)
21799 })
21800 }
21801}
21802
21803fn snippet_completions(
21804 project: &Project,
21805 buffer: &Entity<Buffer>,
21806 buffer_position: text::Anchor,
21807 cx: &mut App,
21808) -> Task<Result<CompletionResponse>> {
21809 let languages = buffer.read(cx).languages_at(buffer_position);
21810 let snippet_store = project.snippets().read(cx);
21811
21812 let scopes: Vec<_> = languages
21813 .iter()
21814 .filter_map(|language| {
21815 let language_name = language.lsp_id();
21816 let snippets = snippet_store.snippets_for(Some(language_name), cx);
21817
21818 if snippets.is_empty() {
21819 None
21820 } else {
21821 Some((language.default_scope(), snippets))
21822 }
21823 })
21824 .collect();
21825
21826 if scopes.is_empty() {
21827 return Task::ready(Ok(CompletionResponse {
21828 completions: vec![],
21829 is_incomplete: false,
21830 }));
21831 }
21832
21833 let snapshot = buffer.read(cx).text_snapshot();
21834 let chars: String = snapshot
21835 .reversed_chars_for_range(text::Anchor::MIN..buffer_position)
21836 .collect();
21837 let executor = cx.background_executor().clone();
21838
21839 cx.background_spawn(async move {
21840 let mut is_incomplete = false;
21841 let mut completions: Vec<Completion> = Vec::new();
21842 for (scope, snippets) in scopes.into_iter() {
21843 let classifier = CharClassifier::new(Some(scope)).for_completion(true);
21844 let mut last_word = chars
21845 .chars()
21846 .take_while(|c| classifier.is_word(*c))
21847 .collect::<String>();
21848 last_word = last_word.chars().rev().collect();
21849
21850 if last_word.is_empty() {
21851 return Ok(CompletionResponse {
21852 completions: vec![],
21853 is_incomplete: true,
21854 });
21855 }
21856
21857 let as_offset = text::ToOffset::to_offset(&buffer_position, &snapshot);
21858 let to_lsp = |point: &text::Anchor| {
21859 let end = text::ToPointUtf16::to_point_utf16(point, &snapshot);
21860 point_to_lsp(end)
21861 };
21862 let lsp_end = to_lsp(&buffer_position);
21863
21864 let candidates = snippets
21865 .iter()
21866 .enumerate()
21867 .flat_map(|(ix, snippet)| {
21868 snippet
21869 .prefix
21870 .iter()
21871 .map(move |prefix| StringMatchCandidate::new(ix, &prefix))
21872 })
21873 .collect::<Vec<StringMatchCandidate>>();
21874
21875 const MAX_RESULTS: usize = 100;
21876 let mut matches = fuzzy::match_strings(
21877 &candidates,
21878 &last_word,
21879 last_word.chars().any(|c| c.is_uppercase()),
21880 true,
21881 MAX_RESULTS,
21882 &Default::default(),
21883 executor.clone(),
21884 )
21885 .await;
21886
21887 if matches.len() >= MAX_RESULTS {
21888 is_incomplete = true;
21889 }
21890
21891 // Remove all candidates where the query's start does not match the start of any word in the candidate
21892 if let Some(query_start) = last_word.chars().next() {
21893 matches.retain(|string_match| {
21894 split_words(&string_match.string).any(|word| {
21895 // Check that the first codepoint of the word as lowercase matches the first
21896 // codepoint of the query as lowercase
21897 word.chars()
21898 .flat_map(|codepoint| codepoint.to_lowercase())
21899 .zip(query_start.to_lowercase())
21900 .all(|(word_cp, query_cp)| word_cp == query_cp)
21901 })
21902 });
21903 }
21904
21905 let matched_strings = matches
21906 .into_iter()
21907 .map(|m| m.string)
21908 .collect::<HashSet<_>>();
21909
21910 completions.extend(snippets.iter().filter_map(|snippet| {
21911 let matching_prefix = snippet
21912 .prefix
21913 .iter()
21914 .find(|prefix| matched_strings.contains(*prefix))?;
21915 let start = as_offset - last_word.len();
21916 let start = snapshot.anchor_before(start);
21917 let range = start..buffer_position;
21918 let lsp_start = to_lsp(&start);
21919 let lsp_range = lsp::Range {
21920 start: lsp_start,
21921 end: lsp_end,
21922 };
21923 Some(Completion {
21924 replace_range: range,
21925 new_text: snippet.body.clone(),
21926 source: CompletionSource::Lsp {
21927 insert_range: None,
21928 server_id: LanguageServerId(usize::MAX),
21929 resolved: true,
21930 lsp_completion: Box::new(lsp::CompletionItem {
21931 label: snippet.prefix.first().unwrap().clone(),
21932 kind: Some(CompletionItemKind::SNIPPET),
21933 label_details: snippet.description.as_ref().map(|description| {
21934 lsp::CompletionItemLabelDetails {
21935 detail: Some(description.clone()),
21936 description: None,
21937 }
21938 }),
21939 insert_text_format: Some(InsertTextFormat::SNIPPET),
21940 text_edit: Some(lsp::CompletionTextEdit::InsertAndReplace(
21941 lsp::InsertReplaceEdit {
21942 new_text: snippet.body.clone(),
21943 insert: lsp_range,
21944 replace: lsp_range,
21945 },
21946 )),
21947 filter_text: Some(snippet.body.clone()),
21948 sort_text: Some(char::MAX.to_string()),
21949 ..lsp::CompletionItem::default()
21950 }),
21951 lsp_defaults: None,
21952 },
21953 label: CodeLabel {
21954 text: matching_prefix.clone(),
21955 runs: Vec::new(),
21956 filter_range: 0..matching_prefix.len(),
21957 },
21958 icon_path: None,
21959 documentation: Some(CompletionDocumentation::SingleLineAndMultiLinePlainText {
21960 single_line: snippet.name.clone().into(),
21961 plain_text: snippet
21962 .description
21963 .clone()
21964 .map(|description| description.into()),
21965 }),
21966 insert_text_mode: None,
21967 confirm: None,
21968 })
21969 }))
21970 }
21971
21972 Ok(CompletionResponse {
21973 completions,
21974 is_incomplete,
21975 })
21976 })
21977}
21978
21979impl CompletionProvider for Entity<Project> {
21980 fn completions(
21981 &self,
21982 _excerpt_id: ExcerptId,
21983 buffer: &Entity<Buffer>,
21984 buffer_position: text::Anchor,
21985 options: CompletionContext,
21986 _window: &mut Window,
21987 cx: &mut Context<Editor>,
21988 ) -> Task<Result<Vec<CompletionResponse>>> {
21989 self.update(cx, |project, cx| {
21990 let snippets = snippet_completions(project, buffer, buffer_position, cx);
21991 let project_completions = project.completions(buffer, buffer_position, options, cx);
21992 cx.background_spawn(async move {
21993 let mut responses = project_completions.await?;
21994 let snippets = snippets.await?;
21995 if !snippets.completions.is_empty() {
21996 responses.push(snippets);
21997 }
21998 Ok(responses)
21999 })
22000 })
22001 }
22002
22003 fn resolve_completions(
22004 &self,
22005 buffer: Entity<Buffer>,
22006 completion_indices: Vec<usize>,
22007 completions: Rc<RefCell<Box<[Completion]>>>,
22008 cx: &mut Context<Editor>,
22009 ) -> Task<Result<bool>> {
22010 self.update(cx, |project, cx| {
22011 project.lsp_store().update(cx, |lsp_store, cx| {
22012 lsp_store.resolve_completions(buffer, completion_indices, completions, cx)
22013 })
22014 })
22015 }
22016
22017 fn apply_additional_edits_for_completion(
22018 &self,
22019 buffer: Entity<Buffer>,
22020 completions: Rc<RefCell<Box<[Completion]>>>,
22021 completion_index: usize,
22022 push_to_history: bool,
22023 cx: &mut Context<Editor>,
22024 ) -> Task<Result<Option<language::Transaction>>> {
22025 self.update(cx, |project, cx| {
22026 project.lsp_store().update(cx, |lsp_store, cx| {
22027 lsp_store.apply_additional_edits_for_completion(
22028 buffer,
22029 completions,
22030 completion_index,
22031 push_to_history,
22032 cx,
22033 )
22034 })
22035 })
22036 }
22037
22038 fn is_completion_trigger(
22039 &self,
22040 buffer: &Entity<Buffer>,
22041 position: language::Anchor,
22042 text: &str,
22043 trigger_in_words: bool,
22044 menu_is_open: bool,
22045 cx: &mut Context<Editor>,
22046 ) -> bool {
22047 let mut chars = text.chars();
22048 let char = if let Some(char) = chars.next() {
22049 char
22050 } else {
22051 return false;
22052 };
22053 if chars.next().is_some() {
22054 return false;
22055 }
22056
22057 let buffer = buffer.read(cx);
22058 let snapshot = buffer.snapshot();
22059 if !menu_is_open && !snapshot.settings_at(position, cx).show_completions_on_input {
22060 return false;
22061 }
22062 let classifier = snapshot.char_classifier_at(position).for_completion(true);
22063 if trigger_in_words && classifier.is_word(char) {
22064 return true;
22065 }
22066
22067 buffer.completion_triggers().contains(text)
22068 }
22069}
22070
22071impl SemanticsProvider for Entity<Project> {
22072 fn hover(
22073 &self,
22074 buffer: &Entity<Buffer>,
22075 position: text::Anchor,
22076 cx: &mut App,
22077 ) -> Option<Task<Vec<project::Hover>>> {
22078 Some(self.update(cx, |project, cx| project.hover(buffer, position, cx)))
22079 }
22080
22081 fn document_highlights(
22082 &self,
22083 buffer: &Entity<Buffer>,
22084 position: text::Anchor,
22085 cx: &mut App,
22086 ) -> Option<Task<Result<Vec<DocumentHighlight>>>> {
22087 Some(self.update(cx, |project, cx| {
22088 project.document_highlights(buffer, position, cx)
22089 }))
22090 }
22091
22092 fn definitions(
22093 &self,
22094 buffer: &Entity<Buffer>,
22095 position: text::Anchor,
22096 kind: GotoDefinitionKind,
22097 cx: &mut App,
22098 ) -> Option<Task<Result<Vec<LocationLink>>>> {
22099 Some(self.update(cx, |project, cx| match kind {
22100 GotoDefinitionKind::Symbol => project.definitions(&buffer, position, cx),
22101 GotoDefinitionKind::Declaration => project.declarations(&buffer, position, cx),
22102 GotoDefinitionKind::Type => project.type_definitions(&buffer, position, cx),
22103 GotoDefinitionKind::Implementation => project.implementations(&buffer, position, cx),
22104 }))
22105 }
22106
22107 fn supports_inlay_hints(&self, buffer: &Entity<Buffer>, cx: &mut App) -> bool {
22108 // TODO: make this work for remote projects
22109 self.update(cx, |project, cx| {
22110 if project
22111 .active_debug_session(cx)
22112 .is_some_and(|(session, _)| session.read(cx).any_stopped_thread())
22113 {
22114 return true;
22115 }
22116
22117 buffer.update(cx, |buffer, cx| {
22118 project.any_language_server_supports_inlay_hints(buffer, cx)
22119 })
22120 })
22121 }
22122
22123 fn inline_values(
22124 &self,
22125 buffer_handle: Entity<Buffer>,
22126 range: Range<text::Anchor>,
22127 cx: &mut App,
22128 ) -> Option<Task<anyhow::Result<Vec<InlayHint>>>> {
22129 self.update(cx, |project, cx| {
22130 let (session, active_stack_frame) = project.active_debug_session(cx)?;
22131
22132 Some(project.inline_values(session, active_stack_frame, buffer_handle, range, cx))
22133 })
22134 }
22135
22136 fn inlay_hints(
22137 &self,
22138 buffer_handle: Entity<Buffer>,
22139 range: Range<text::Anchor>,
22140 cx: &mut App,
22141 ) -> Option<Task<anyhow::Result<Vec<InlayHint>>>> {
22142 Some(self.update(cx, |project, cx| {
22143 project.inlay_hints(buffer_handle, range, cx)
22144 }))
22145 }
22146
22147 fn resolve_inlay_hint(
22148 &self,
22149 hint: InlayHint,
22150 buffer_handle: Entity<Buffer>,
22151 server_id: LanguageServerId,
22152 cx: &mut App,
22153 ) -> Option<Task<anyhow::Result<InlayHint>>> {
22154 Some(self.update(cx, |project, cx| {
22155 project.resolve_inlay_hint(hint, buffer_handle, server_id, cx)
22156 }))
22157 }
22158
22159 fn range_for_rename(
22160 &self,
22161 buffer: &Entity<Buffer>,
22162 position: text::Anchor,
22163 cx: &mut App,
22164 ) -> Option<Task<Result<Option<Range<text::Anchor>>>>> {
22165 Some(self.update(cx, |project, cx| {
22166 let buffer = buffer.clone();
22167 let task = project.prepare_rename(buffer.clone(), position, cx);
22168 cx.spawn(async move |_, cx| {
22169 Ok(match task.await? {
22170 PrepareRenameResponse::Success(range) => Some(range),
22171 PrepareRenameResponse::InvalidPosition => None,
22172 PrepareRenameResponse::OnlyUnpreparedRenameSupported => {
22173 // Fallback on using TreeSitter info to determine identifier range
22174 buffer.read_with(cx, |buffer, _| {
22175 let snapshot = buffer.snapshot();
22176 let (range, kind) = snapshot.surrounding_word(position, false);
22177 if kind != Some(CharKind::Word) {
22178 return None;
22179 }
22180 Some(
22181 snapshot.anchor_before(range.start)
22182 ..snapshot.anchor_after(range.end),
22183 )
22184 })?
22185 }
22186 })
22187 })
22188 }))
22189 }
22190
22191 fn perform_rename(
22192 &self,
22193 buffer: &Entity<Buffer>,
22194 position: text::Anchor,
22195 new_name: String,
22196 cx: &mut App,
22197 ) -> Option<Task<Result<ProjectTransaction>>> {
22198 Some(self.update(cx, |project, cx| {
22199 project.perform_rename(buffer.clone(), position, new_name, cx)
22200 }))
22201 }
22202}
22203
22204fn inlay_hint_settings(
22205 location: Anchor,
22206 snapshot: &MultiBufferSnapshot,
22207 cx: &mut Context<Editor>,
22208) -> InlayHintSettings {
22209 let file = snapshot.file_at(location);
22210 let language = snapshot.language_at(location).map(|l| l.name());
22211 language_settings(language, file, cx).inlay_hints
22212}
22213
22214fn consume_contiguous_rows(
22215 contiguous_row_selections: &mut Vec<Selection<Point>>,
22216 selection: &Selection<Point>,
22217 display_map: &DisplaySnapshot,
22218 selections: &mut Peekable<std::slice::Iter<Selection<Point>>>,
22219) -> (MultiBufferRow, MultiBufferRow) {
22220 contiguous_row_selections.push(selection.clone());
22221 let start_row = MultiBufferRow(selection.start.row);
22222 let mut end_row = ending_row(selection, display_map);
22223
22224 while let Some(next_selection) = selections.peek() {
22225 if next_selection.start.row <= end_row.0 {
22226 end_row = ending_row(next_selection, display_map);
22227 contiguous_row_selections.push(selections.next().unwrap().clone());
22228 } else {
22229 break;
22230 }
22231 }
22232 (start_row, end_row)
22233}
22234
22235fn ending_row(next_selection: &Selection<Point>, display_map: &DisplaySnapshot) -> MultiBufferRow {
22236 if next_selection.end.column > 0 || next_selection.is_empty() {
22237 MultiBufferRow(display_map.next_line_boundary(next_selection.end).0.row + 1)
22238 } else {
22239 MultiBufferRow(next_selection.end.row)
22240 }
22241}
22242
22243impl EditorSnapshot {
22244 pub fn remote_selections_in_range<'a>(
22245 &'a self,
22246 range: &'a Range<Anchor>,
22247 collaboration_hub: &dyn CollaborationHub,
22248 cx: &'a App,
22249 ) -> impl 'a + Iterator<Item = RemoteSelection> {
22250 let participant_names = collaboration_hub.user_names(cx);
22251 let participant_indices = collaboration_hub.user_participant_indices(cx);
22252 let collaborators_by_peer_id = collaboration_hub.collaborators(cx);
22253 let collaborators_by_replica_id = collaborators_by_peer_id
22254 .values()
22255 .map(|collaborator| (collaborator.replica_id, collaborator))
22256 .collect::<HashMap<_, _>>();
22257 self.buffer_snapshot
22258 .selections_in_range(range, false)
22259 .filter_map(move |(replica_id, line_mode, cursor_shape, selection)| {
22260 if replica_id == AGENT_REPLICA_ID {
22261 Some(RemoteSelection {
22262 replica_id,
22263 selection,
22264 cursor_shape,
22265 line_mode,
22266 collaborator_id: CollaboratorId::Agent,
22267 user_name: Some("Agent".into()),
22268 color: cx.theme().players().agent(),
22269 })
22270 } else {
22271 let collaborator = collaborators_by_replica_id.get(&replica_id)?;
22272 let participant_index = participant_indices.get(&collaborator.user_id).copied();
22273 let user_name = participant_names.get(&collaborator.user_id).cloned();
22274 Some(RemoteSelection {
22275 replica_id,
22276 selection,
22277 cursor_shape,
22278 line_mode,
22279 collaborator_id: CollaboratorId::PeerId(collaborator.peer_id),
22280 user_name,
22281 color: if let Some(index) = participant_index {
22282 cx.theme().players().color_for_participant(index.0)
22283 } else {
22284 cx.theme().players().absent()
22285 },
22286 })
22287 }
22288 })
22289 }
22290
22291 pub fn hunks_for_ranges(
22292 &self,
22293 ranges: impl IntoIterator<Item = Range<Point>>,
22294 ) -> Vec<MultiBufferDiffHunk> {
22295 let mut hunks = Vec::new();
22296 let mut processed_buffer_rows: HashMap<BufferId, HashSet<Range<text::Anchor>>> =
22297 HashMap::default();
22298 for query_range in ranges {
22299 let query_rows =
22300 MultiBufferRow(query_range.start.row)..MultiBufferRow(query_range.end.row + 1);
22301 for hunk in self.buffer_snapshot.diff_hunks_in_range(
22302 Point::new(query_rows.start.0, 0)..Point::new(query_rows.end.0, 0),
22303 ) {
22304 // Include deleted hunks that are adjacent to the query range, because
22305 // otherwise they would be missed.
22306 let mut intersects_range = hunk.row_range.overlaps(&query_rows);
22307 if hunk.status().is_deleted() {
22308 intersects_range |= hunk.row_range.start == query_rows.end;
22309 intersects_range |= hunk.row_range.end == query_rows.start;
22310 }
22311 if intersects_range {
22312 if !processed_buffer_rows
22313 .entry(hunk.buffer_id)
22314 .or_default()
22315 .insert(hunk.buffer_range.start..hunk.buffer_range.end)
22316 {
22317 continue;
22318 }
22319 hunks.push(hunk);
22320 }
22321 }
22322 }
22323
22324 hunks
22325 }
22326
22327 fn display_diff_hunks_for_rows<'a>(
22328 &'a self,
22329 display_rows: Range<DisplayRow>,
22330 folded_buffers: &'a HashSet<BufferId>,
22331 ) -> impl 'a + Iterator<Item = DisplayDiffHunk> {
22332 let buffer_start = DisplayPoint::new(display_rows.start, 0).to_point(self);
22333 let buffer_end = DisplayPoint::new(display_rows.end, 0).to_point(self);
22334
22335 self.buffer_snapshot
22336 .diff_hunks_in_range(buffer_start..buffer_end)
22337 .filter_map(|hunk| {
22338 if folded_buffers.contains(&hunk.buffer_id) {
22339 return None;
22340 }
22341
22342 let hunk_start_point = Point::new(hunk.row_range.start.0, 0);
22343 let hunk_end_point = Point::new(hunk.row_range.end.0, 0);
22344
22345 let hunk_display_start = self.point_to_display_point(hunk_start_point, Bias::Left);
22346 let hunk_display_end = self.point_to_display_point(hunk_end_point, Bias::Right);
22347
22348 let display_hunk = if hunk_display_start.column() != 0 {
22349 DisplayDiffHunk::Folded {
22350 display_row: hunk_display_start.row(),
22351 }
22352 } else {
22353 let mut end_row = hunk_display_end.row();
22354 if hunk_display_end.column() > 0 {
22355 end_row.0 += 1;
22356 }
22357 let is_created_file = hunk.is_created_file();
22358 DisplayDiffHunk::Unfolded {
22359 status: hunk.status(),
22360 diff_base_byte_range: hunk.diff_base_byte_range,
22361 display_row_range: hunk_display_start.row()..end_row,
22362 multi_buffer_range: Anchor::range_in_buffer(
22363 hunk.excerpt_id,
22364 hunk.buffer_id,
22365 hunk.buffer_range,
22366 ),
22367 is_created_file,
22368 }
22369 };
22370
22371 Some(display_hunk)
22372 })
22373 }
22374
22375 pub fn language_at<T: ToOffset>(&self, position: T) -> Option<&Arc<Language>> {
22376 self.display_snapshot.buffer_snapshot.language_at(position)
22377 }
22378
22379 pub fn is_focused(&self) -> bool {
22380 self.is_focused
22381 }
22382
22383 pub fn placeholder_text(&self) -> Option<&Arc<str>> {
22384 self.placeholder_text.as_ref()
22385 }
22386
22387 pub fn scroll_position(&self) -> gpui::Point<f32> {
22388 self.scroll_anchor.scroll_position(&self.display_snapshot)
22389 }
22390
22391 fn gutter_dimensions(
22392 &self,
22393 font_id: FontId,
22394 font_size: Pixels,
22395 max_line_number_width: Pixels,
22396 cx: &App,
22397 ) -> Option<GutterDimensions> {
22398 if !self.show_gutter {
22399 return None;
22400 }
22401
22402 let ch_width = cx.text_system().ch_width(font_id, font_size).log_err()?;
22403 let ch_advance = cx.text_system().ch_advance(font_id, font_size).log_err()?;
22404
22405 let show_git_gutter = self.show_git_diff_gutter.unwrap_or_else(|| {
22406 matches!(
22407 ProjectSettings::get_global(cx).git.git_gutter,
22408 Some(GitGutterSetting::TrackedFiles)
22409 )
22410 });
22411 let gutter_settings = EditorSettings::get_global(cx).gutter;
22412 let show_line_numbers = self
22413 .show_line_numbers
22414 .unwrap_or(gutter_settings.line_numbers);
22415 let line_gutter_width = if show_line_numbers {
22416 // Avoid flicker-like gutter resizes when the line number gains another digit by
22417 // only resizing the gutter on files with > 10**min_line_number_digits lines.
22418 let min_width_for_number_on_gutter =
22419 ch_advance * gutter_settings.min_line_number_digits as f32;
22420 max_line_number_width.max(min_width_for_number_on_gutter)
22421 } else {
22422 0.0.into()
22423 };
22424
22425 let show_runnables = self.show_runnables.unwrap_or(gutter_settings.runnables);
22426 let show_breakpoints = self.show_breakpoints.unwrap_or(gutter_settings.breakpoints);
22427
22428 let git_blame_entries_width =
22429 self.git_blame_gutter_max_author_length
22430 .map(|max_author_length| {
22431 let renderer = cx.global::<GlobalBlameRenderer>().0.clone();
22432 const MAX_RELATIVE_TIMESTAMP: &str = "60 minutes ago";
22433
22434 /// The number of characters to dedicate to gaps and margins.
22435 const SPACING_WIDTH: usize = 4;
22436
22437 let max_char_count = max_author_length.min(renderer.max_author_length())
22438 + ::git::SHORT_SHA_LENGTH
22439 + MAX_RELATIVE_TIMESTAMP.len()
22440 + SPACING_WIDTH;
22441
22442 ch_advance * max_char_count
22443 });
22444
22445 let is_singleton = self.buffer_snapshot.is_singleton();
22446
22447 let mut left_padding = git_blame_entries_width.unwrap_or(Pixels::ZERO);
22448 left_padding += if !is_singleton {
22449 ch_width * 4.0
22450 } else if show_runnables || show_breakpoints {
22451 ch_width * 3.0
22452 } else if show_git_gutter && show_line_numbers {
22453 ch_width * 2.0
22454 } else if show_git_gutter || show_line_numbers {
22455 ch_width
22456 } else {
22457 px(0.)
22458 };
22459
22460 let shows_folds = is_singleton && gutter_settings.folds;
22461
22462 let right_padding = if shows_folds && show_line_numbers {
22463 ch_width * 4.0
22464 } else if shows_folds || (!is_singleton && show_line_numbers) {
22465 ch_width * 3.0
22466 } else if show_line_numbers {
22467 ch_width
22468 } else {
22469 px(0.)
22470 };
22471
22472 Some(GutterDimensions {
22473 left_padding,
22474 right_padding,
22475 width: line_gutter_width + left_padding + right_padding,
22476 margin: GutterDimensions::default_gutter_margin(font_id, font_size, cx),
22477 git_blame_entries_width,
22478 })
22479 }
22480
22481 pub fn render_crease_toggle(
22482 &self,
22483 buffer_row: MultiBufferRow,
22484 row_contains_cursor: bool,
22485 editor: Entity<Editor>,
22486 window: &mut Window,
22487 cx: &mut App,
22488 ) -> Option<AnyElement> {
22489 let folded = self.is_line_folded(buffer_row);
22490 let mut is_foldable = false;
22491
22492 if let Some(crease) = self
22493 .crease_snapshot
22494 .query_row(buffer_row, &self.buffer_snapshot)
22495 {
22496 is_foldable = true;
22497 match crease {
22498 Crease::Inline { render_toggle, .. } | Crease::Block { render_toggle, .. } => {
22499 if let Some(render_toggle) = render_toggle {
22500 let toggle_callback =
22501 Arc::new(move |folded, window: &mut Window, cx: &mut App| {
22502 if folded {
22503 editor.update(cx, |editor, cx| {
22504 editor.fold_at(buffer_row, window, cx)
22505 });
22506 } else {
22507 editor.update(cx, |editor, cx| {
22508 editor.unfold_at(buffer_row, window, cx)
22509 });
22510 }
22511 });
22512 return Some((render_toggle)(
22513 buffer_row,
22514 folded,
22515 toggle_callback,
22516 window,
22517 cx,
22518 ));
22519 }
22520 }
22521 }
22522 }
22523
22524 is_foldable |= self.starts_indent(buffer_row);
22525
22526 if folded || (is_foldable && (row_contains_cursor || self.gutter_hovered)) {
22527 Some(
22528 Disclosure::new(("gutter_crease", buffer_row.0), !folded)
22529 .toggle_state(folded)
22530 .on_click(window.listener_for(&editor, move |this, _e, window, cx| {
22531 if folded {
22532 this.unfold_at(buffer_row, window, cx);
22533 } else {
22534 this.fold_at(buffer_row, window, cx);
22535 }
22536 }))
22537 .into_any_element(),
22538 )
22539 } else {
22540 None
22541 }
22542 }
22543
22544 pub fn render_crease_trailer(
22545 &self,
22546 buffer_row: MultiBufferRow,
22547 window: &mut Window,
22548 cx: &mut App,
22549 ) -> Option<AnyElement> {
22550 let folded = self.is_line_folded(buffer_row);
22551 if let Crease::Inline { render_trailer, .. } = self
22552 .crease_snapshot
22553 .query_row(buffer_row, &self.buffer_snapshot)?
22554 {
22555 let render_trailer = render_trailer.as_ref()?;
22556 Some(render_trailer(buffer_row, folded, window, cx))
22557 } else {
22558 None
22559 }
22560 }
22561}
22562
22563impl Deref for EditorSnapshot {
22564 type Target = DisplaySnapshot;
22565
22566 fn deref(&self) -> &Self::Target {
22567 &self.display_snapshot
22568 }
22569}
22570
22571#[derive(Clone, Debug, PartialEq, Eq)]
22572pub enum EditorEvent {
22573 InputIgnored {
22574 text: Arc<str>,
22575 },
22576 InputHandled {
22577 utf16_range_to_replace: Option<Range<isize>>,
22578 text: Arc<str>,
22579 },
22580 ExcerptsAdded {
22581 buffer: Entity<Buffer>,
22582 predecessor: ExcerptId,
22583 excerpts: Vec<(ExcerptId, ExcerptRange<language::Anchor>)>,
22584 },
22585 ExcerptsRemoved {
22586 ids: Vec<ExcerptId>,
22587 removed_buffer_ids: Vec<BufferId>,
22588 },
22589 BufferFoldToggled {
22590 ids: Vec<ExcerptId>,
22591 folded: bool,
22592 },
22593 ExcerptsEdited {
22594 ids: Vec<ExcerptId>,
22595 },
22596 ExcerptsExpanded {
22597 ids: Vec<ExcerptId>,
22598 },
22599 BufferEdited,
22600 Edited {
22601 transaction_id: clock::Lamport,
22602 },
22603 Reparsed(BufferId),
22604 Focused,
22605 FocusedIn,
22606 Blurred,
22607 DirtyChanged,
22608 Saved,
22609 TitleChanged,
22610 DiffBaseChanged,
22611 SelectionsChanged {
22612 local: bool,
22613 },
22614 ScrollPositionChanged {
22615 local: bool,
22616 autoscroll: bool,
22617 },
22618 Closed,
22619 TransactionUndone {
22620 transaction_id: clock::Lamport,
22621 },
22622 TransactionBegun {
22623 transaction_id: clock::Lamport,
22624 },
22625 Reloaded,
22626 CursorShapeChanged,
22627 PushedToNavHistory {
22628 anchor: Anchor,
22629 is_deactivate: bool,
22630 },
22631}
22632
22633impl EventEmitter<EditorEvent> for Editor {}
22634
22635impl Focusable for Editor {
22636 fn focus_handle(&self, _cx: &App) -> FocusHandle {
22637 self.focus_handle.clone()
22638 }
22639}
22640
22641impl Render for Editor {
22642 fn render(&mut self, _: &mut Window, cx: &mut Context<Self>) -> impl IntoElement {
22643 let settings = ThemeSettings::get_global(cx);
22644
22645 let mut text_style = match self.mode {
22646 EditorMode::SingleLine { .. } | EditorMode::AutoHeight { .. } => TextStyle {
22647 color: cx.theme().colors().editor_foreground,
22648 font_family: settings.ui_font.family.clone(),
22649 font_features: settings.ui_font.features.clone(),
22650 font_fallbacks: settings.ui_font.fallbacks.clone(),
22651 font_size: rems(0.875).into(),
22652 font_weight: settings.ui_font.weight,
22653 line_height: relative(settings.buffer_line_height.value()),
22654 ..Default::default()
22655 },
22656 EditorMode::Full { .. } | EditorMode::Minimap { .. } => TextStyle {
22657 color: cx.theme().colors().editor_foreground,
22658 font_family: settings.buffer_font.family.clone(),
22659 font_features: settings.buffer_font.features.clone(),
22660 font_fallbacks: settings.buffer_font.fallbacks.clone(),
22661 font_size: settings.buffer_font_size(cx).into(),
22662 font_weight: settings.buffer_font.weight,
22663 line_height: relative(settings.buffer_line_height.value()),
22664 ..Default::default()
22665 },
22666 };
22667 if let Some(text_style_refinement) = &self.text_style_refinement {
22668 text_style.refine(text_style_refinement)
22669 }
22670
22671 let background = match self.mode {
22672 EditorMode::SingleLine { .. } => cx.theme().system().transparent,
22673 EditorMode::AutoHeight { .. } => cx.theme().system().transparent,
22674 EditorMode::Full { .. } => cx.theme().colors().editor_background,
22675 EditorMode::Minimap { .. } => cx.theme().colors().editor_background.opacity(0.7),
22676 };
22677
22678 EditorElement::new(
22679 &cx.entity(),
22680 EditorStyle {
22681 background,
22682 border: cx.theme().colors().border,
22683 local_player: cx.theme().players().local(),
22684 text: text_style,
22685 scrollbar_width: EditorElement::SCROLLBAR_WIDTH,
22686 syntax: cx.theme().syntax().clone(),
22687 status: cx.theme().status().clone(),
22688 inlay_hints_style: make_inlay_hints_style(cx),
22689 inline_completion_styles: make_suggestion_styles(cx),
22690 unnecessary_code_fade: ThemeSettings::get_global(cx).unnecessary_code_fade,
22691 show_underlines: self.diagnostics_enabled(),
22692 },
22693 )
22694 }
22695}
22696
22697impl EntityInputHandler for Editor {
22698 fn text_for_range(
22699 &mut self,
22700 range_utf16: Range<usize>,
22701 adjusted_range: &mut Option<Range<usize>>,
22702 _: &mut Window,
22703 cx: &mut Context<Self>,
22704 ) -> Option<String> {
22705 let snapshot = self.buffer.read(cx).read(cx);
22706 let start = snapshot.clip_offset_utf16(OffsetUtf16(range_utf16.start), Bias::Left);
22707 let end = snapshot.clip_offset_utf16(OffsetUtf16(range_utf16.end), Bias::Right);
22708 if (start.0..end.0) != range_utf16 {
22709 adjusted_range.replace(start.0..end.0);
22710 }
22711 Some(snapshot.text_for_range(start..end).collect())
22712 }
22713
22714 fn selected_text_range(
22715 &mut self,
22716 ignore_disabled_input: bool,
22717 _: &mut Window,
22718 cx: &mut Context<Self>,
22719 ) -> Option<UTF16Selection> {
22720 // Prevent the IME menu from appearing when holding down an alphabetic key
22721 // while input is disabled.
22722 if !ignore_disabled_input && !self.input_enabled {
22723 return None;
22724 }
22725
22726 let selection = self.selections.newest::<OffsetUtf16>(cx);
22727 let range = selection.range();
22728
22729 Some(UTF16Selection {
22730 range: range.start.0..range.end.0,
22731 reversed: selection.reversed,
22732 })
22733 }
22734
22735 fn marked_text_range(&self, _: &mut Window, cx: &mut Context<Self>) -> Option<Range<usize>> {
22736 let snapshot = self.buffer.read(cx).read(cx);
22737 let range = self.text_highlights::<InputComposition>(cx)?.1.first()?;
22738 Some(range.start.to_offset_utf16(&snapshot).0..range.end.to_offset_utf16(&snapshot).0)
22739 }
22740
22741 fn unmark_text(&mut self, _: &mut Window, cx: &mut Context<Self>) {
22742 self.clear_highlights::<InputComposition>(cx);
22743 self.ime_transaction.take();
22744 }
22745
22746 fn replace_text_in_range(
22747 &mut self,
22748 range_utf16: Option<Range<usize>>,
22749 text: &str,
22750 window: &mut Window,
22751 cx: &mut Context<Self>,
22752 ) {
22753 if !self.input_enabled {
22754 cx.emit(EditorEvent::InputIgnored { text: text.into() });
22755 return;
22756 }
22757
22758 self.transact(window, cx, |this, window, cx| {
22759 let new_selected_ranges = if let Some(range_utf16) = range_utf16 {
22760 let range_utf16 = OffsetUtf16(range_utf16.start)..OffsetUtf16(range_utf16.end);
22761 Some(this.selection_replacement_ranges(range_utf16, cx))
22762 } else {
22763 this.marked_text_ranges(cx)
22764 };
22765
22766 let range_to_replace = new_selected_ranges.as_ref().and_then(|ranges_to_replace| {
22767 let newest_selection_id = this.selections.newest_anchor().id;
22768 this.selections
22769 .all::<OffsetUtf16>(cx)
22770 .iter()
22771 .zip(ranges_to_replace.iter())
22772 .find_map(|(selection, range)| {
22773 if selection.id == newest_selection_id {
22774 Some(
22775 (range.start.0 as isize - selection.head().0 as isize)
22776 ..(range.end.0 as isize - selection.head().0 as isize),
22777 )
22778 } else {
22779 None
22780 }
22781 })
22782 });
22783
22784 cx.emit(EditorEvent::InputHandled {
22785 utf16_range_to_replace: range_to_replace,
22786 text: text.into(),
22787 });
22788
22789 if let Some(new_selected_ranges) = new_selected_ranges {
22790 this.change_selections(SelectionEffects::no_scroll(), window, cx, |selections| {
22791 selections.select_ranges(new_selected_ranges)
22792 });
22793 this.backspace(&Default::default(), window, cx);
22794 }
22795
22796 this.handle_input(text, window, cx);
22797 });
22798
22799 if let Some(transaction) = self.ime_transaction {
22800 self.buffer.update(cx, |buffer, cx| {
22801 buffer.group_until_transaction(transaction, cx);
22802 });
22803 }
22804
22805 self.unmark_text(window, cx);
22806 }
22807
22808 fn replace_and_mark_text_in_range(
22809 &mut self,
22810 range_utf16: Option<Range<usize>>,
22811 text: &str,
22812 new_selected_range_utf16: Option<Range<usize>>,
22813 window: &mut Window,
22814 cx: &mut Context<Self>,
22815 ) {
22816 if !self.input_enabled {
22817 return;
22818 }
22819
22820 let transaction = self.transact(window, cx, |this, window, cx| {
22821 let ranges_to_replace = if let Some(mut marked_ranges) = this.marked_text_ranges(cx) {
22822 let snapshot = this.buffer.read(cx).read(cx);
22823 if let Some(relative_range_utf16) = range_utf16.as_ref() {
22824 for marked_range in &mut marked_ranges {
22825 marked_range.end.0 = marked_range.start.0 + relative_range_utf16.end;
22826 marked_range.start.0 += relative_range_utf16.start;
22827 marked_range.start =
22828 snapshot.clip_offset_utf16(marked_range.start, Bias::Left);
22829 marked_range.end =
22830 snapshot.clip_offset_utf16(marked_range.end, Bias::Right);
22831 }
22832 }
22833 Some(marked_ranges)
22834 } else if let Some(range_utf16) = range_utf16 {
22835 let range_utf16 = OffsetUtf16(range_utf16.start)..OffsetUtf16(range_utf16.end);
22836 Some(this.selection_replacement_ranges(range_utf16, cx))
22837 } else {
22838 None
22839 };
22840
22841 let range_to_replace = ranges_to_replace.as_ref().and_then(|ranges_to_replace| {
22842 let newest_selection_id = this.selections.newest_anchor().id;
22843 this.selections
22844 .all::<OffsetUtf16>(cx)
22845 .iter()
22846 .zip(ranges_to_replace.iter())
22847 .find_map(|(selection, range)| {
22848 if selection.id == newest_selection_id {
22849 Some(
22850 (range.start.0 as isize - selection.head().0 as isize)
22851 ..(range.end.0 as isize - selection.head().0 as isize),
22852 )
22853 } else {
22854 None
22855 }
22856 })
22857 });
22858
22859 cx.emit(EditorEvent::InputHandled {
22860 utf16_range_to_replace: range_to_replace,
22861 text: text.into(),
22862 });
22863
22864 if let Some(ranges) = ranges_to_replace {
22865 this.change_selections(SelectionEffects::no_scroll(), window, cx, |s| {
22866 s.select_ranges(ranges)
22867 });
22868 }
22869
22870 let marked_ranges = {
22871 let snapshot = this.buffer.read(cx).read(cx);
22872 this.selections
22873 .disjoint_anchors()
22874 .iter()
22875 .map(|selection| {
22876 selection.start.bias_left(&snapshot)..selection.end.bias_right(&snapshot)
22877 })
22878 .collect::<Vec<_>>()
22879 };
22880
22881 if text.is_empty() {
22882 this.unmark_text(window, cx);
22883 } else {
22884 this.highlight_text::<InputComposition>(
22885 marked_ranges.clone(),
22886 HighlightStyle {
22887 underline: Some(UnderlineStyle {
22888 thickness: px(1.),
22889 color: None,
22890 wavy: false,
22891 }),
22892 ..Default::default()
22893 },
22894 cx,
22895 );
22896 }
22897
22898 // Disable auto-closing when composing text (i.e. typing a `"` on a Brazilian keyboard)
22899 let use_autoclose = this.use_autoclose;
22900 let use_auto_surround = this.use_auto_surround;
22901 this.set_use_autoclose(false);
22902 this.set_use_auto_surround(false);
22903 this.handle_input(text, window, cx);
22904 this.set_use_autoclose(use_autoclose);
22905 this.set_use_auto_surround(use_auto_surround);
22906
22907 if let Some(new_selected_range) = new_selected_range_utf16 {
22908 let snapshot = this.buffer.read(cx).read(cx);
22909 let new_selected_ranges = marked_ranges
22910 .into_iter()
22911 .map(|marked_range| {
22912 let insertion_start = marked_range.start.to_offset_utf16(&snapshot).0;
22913 let new_start = OffsetUtf16(new_selected_range.start + insertion_start);
22914 let new_end = OffsetUtf16(new_selected_range.end + insertion_start);
22915 snapshot.clip_offset_utf16(new_start, Bias::Left)
22916 ..snapshot.clip_offset_utf16(new_end, Bias::Right)
22917 })
22918 .collect::<Vec<_>>();
22919
22920 drop(snapshot);
22921 this.change_selections(SelectionEffects::no_scroll(), window, cx, |selections| {
22922 selections.select_ranges(new_selected_ranges)
22923 });
22924 }
22925 });
22926
22927 self.ime_transaction = self.ime_transaction.or(transaction);
22928 if let Some(transaction) = self.ime_transaction {
22929 self.buffer.update(cx, |buffer, cx| {
22930 buffer.group_until_transaction(transaction, cx);
22931 });
22932 }
22933
22934 if self.text_highlights::<InputComposition>(cx).is_none() {
22935 self.ime_transaction.take();
22936 }
22937 }
22938
22939 fn bounds_for_range(
22940 &mut self,
22941 range_utf16: Range<usize>,
22942 element_bounds: gpui::Bounds<Pixels>,
22943 window: &mut Window,
22944 cx: &mut Context<Self>,
22945 ) -> Option<gpui::Bounds<Pixels>> {
22946 let text_layout_details = self.text_layout_details(window);
22947 let CharacterDimensions {
22948 em_width,
22949 em_advance,
22950 line_height,
22951 } = self.character_dimensions(window);
22952
22953 let snapshot = self.snapshot(window, cx);
22954 let scroll_position = snapshot.scroll_position();
22955 let scroll_left = scroll_position.x * em_advance;
22956
22957 let start = OffsetUtf16(range_utf16.start).to_display_point(&snapshot);
22958 let x = snapshot.x_for_display_point(start, &text_layout_details) - scroll_left
22959 + self.gutter_dimensions.full_width();
22960 let y = line_height * (start.row().as_f32() - scroll_position.y);
22961
22962 Some(Bounds {
22963 origin: element_bounds.origin + point(x, y),
22964 size: size(em_width, line_height),
22965 })
22966 }
22967
22968 fn character_index_for_point(
22969 &mut self,
22970 point: gpui::Point<Pixels>,
22971 _window: &mut Window,
22972 _cx: &mut Context<Self>,
22973 ) -> Option<usize> {
22974 let position_map = self.last_position_map.as_ref()?;
22975 if !position_map.text_hitbox.contains(&point) {
22976 return None;
22977 }
22978 let display_point = position_map.point_for_position(point).previous_valid;
22979 let anchor = position_map
22980 .snapshot
22981 .display_point_to_anchor(display_point, Bias::Left);
22982 let utf16_offset = anchor.to_offset_utf16(&position_map.snapshot.buffer_snapshot);
22983 Some(utf16_offset.0)
22984 }
22985}
22986
22987trait SelectionExt {
22988 fn display_range(&self, map: &DisplaySnapshot) -> Range<DisplayPoint>;
22989 fn spanned_rows(
22990 &self,
22991 include_end_if_at_line_start: bool,
22992 map: &DisplaySnapshot,
22993 ) -> Range<MultiBufferRow>;
22994}
22995
22996impl<T: ToPoint + ToOffset> SelectionExt for Selection<T> {
22997 fn display_range(&self, map: &DisplaySnapshot) -> Range<DisplayPoint> {
22998 let start = self
22999 .start
23000 .to_point(&map.buffer_snapshot)
23001 .to_display_point(map);
23002 let end = self
23003 .end
23004 .to_point(&map.buffer_snapshot)
23005 .to_display_point(map);
23006 if self.reversed {
23007 end..start
23008 } else {
23009 start..end
23010 }
23011 }
23012
23013 fn spanned_rows(
23014 &self,
23015 include_end_if_at_line_start: bool,
23016 map: &DisplaySnapshot,
23017 ) -> Range<MultiBufferRow> {
23018 let start = self.start.to_point(&map.buffer_snapshot);
23019 let mut end = self.end.to_point(&map.buffer_snapshot);
23020 if !include_end_if_at_line_start && start.row != end.row && end.column == 0 {
23021 end.row -= 1;
23022 }
23023
23024 let buffer_start = map.prev_line_boundary(start).0;
23025 let buffer_end = map.next_line_boundary(end).0;
23026 MultiBufferRow(buffer_start.row)..MultiBufferRow(buffer_end.row + 1)
23027 }
23028}
23029
23030impl<T: InvalidationRegion> InvalidationStack<T> {
23031 fn invalidate<S>(&mut self, selections: &[Selection<S>], buffer: &MultiBufferSnapshot)
23032 where
23033 S: Clone + ToOffset,
23034 {
23035 while let Some(region) = self.last() {
23036 let all_selections_inside_invalidation_ranges =
23037 if selections.len() == region.ranges().len() {
23038 selections
23039 .iter()
23040 .zip(region.ranges().iter().map(|r| r.to_offset(buffer)))
23041 .all(|(selection, invalidation_range)| {
23042 let head = selection.head().to_offset(buffer);
23043 invalidation_range.start <= head && invalidation_range.end >= head
23044 })
23045 } else {
23046 false
23047 };
23048
23049 if all_selections_inside_invalidation_ranges {
23050 break;
23051 } else {
23052 self.pop();
23053 }
23054 }
23055 }
23056}
23057
23058impl<T> Default for InvalidationStack<T> {
23059 fn default() -> Self {
23060 Self(Default::default())
23061 }
23062}
23063
23064impl<T> Deref for InvalidationStack<T> {
23065 type Target = Vec<T>;
23066
23067 fn deref(&self) -> &Self::Target {
23068 &self.0
23069 }
23070}
23071
23072impl<T> DerefMut for InvalidationStack<T> {
23073 fn deref_mut(&mut self) -> &mut Self::Target {
23074 &mut self.0
23075 }
23076}
23077
23078impl InvalidationRegion for SnippetState {
23079 fn ranges(&self) -> &[Range<Anchor>] {
23080 &self.ranges[self.active_index]
23081 }
23082}
23083
23084fn inline_completion_edit_text(
23085 current_snapshot: &BufferSnapshot,
23086 edits: &[(Range<Anchor>, String)],
23087 edit_preview: &EditPreview,
23088 include_deletions: bool,
23089 cx: &App,
23090) -> HighlightedText {
23091 let edits = edits
23092 .iter()
23093 .map(|(anchor, text)| {
23094 (
23095 anchor.start.text_anchor..anchor.end.text_anchor,
23096 text.clone(),
23097 )
23098 })
23099 .collect::<Vec<_>>();
23100
23101 edit_preview.highlight_edits(current_snapshot, &edits, include_deletions, cx)
23102}
23103
23104pub fn diagnostic_style(severity: lsp::DiagnosticSeverity, colors: &StatusColors) -> Hsla {
23105 match severity {
23106 lsp::DiagnosticSeverity::ERROR => colors.error,
23107 lsp::DiagnosticSeverity::WARNING => colors.warning,
23108 lsp::DiagnosticSeverity::INFORMATION => colors.info,
23109 lsp::DiagnosticSeverity::HINT => colors.info,
23110 _ => colors.ignored,
23111 }
23112}
23113
23114pub fn styled_runs_for_code_label<'a>(
23115 label: &'a CodeLabel,
23116 syntax_theme: &'a theme::SyntaxTheme,
23117) -> impl 'a + Iterator<Item = (Range<usize>, HighlightStyle)> {
23118 let fade_out = HighlightStyle {
23119 fade_out: Some(0.35),
23120 ..Default::default()
23121 };
23122
23123 let mut prev_end = label.filter_range.end;
23124 label
23125 .runs
23126 .iter()
23127 .enumerate()
23128 .flat_map(move |(ix, (range, highlight_id))| {
23129 let style = if let Some(style) = highlight_id.style(syntax_theme) {
23130 style
23131 } else {
23132 return Default::default();
23133 };
23134 let mut muted_style = style;
23135 muted_style.highlight(fade_out);
23136
23137 let mut runs = SmallVec::<[(Range<usize>, HighlightStyle); 3]>::new();
23138 if range.start >= label.filter_range.end {
23139 if range.start > prev_end {
23140 runs.push((prev_end..range.start, fade_out));
23141 }
23142 runs.push((range.clone(), muted_style));
23143 } else if range.end <= label.filter_range.end {
23144 runs.push((range.clone(), style));
23145 } else {
23146 runs.push((range.start..label.filter_range.end, style));
23147 runs.push((label.filter_range.end..range.end, muted_style));
23148 }
23149 prev_end = cmp::max(prev_end, range.end);
23150
23151 if ix + 1 == label.runs.len() && label.text.len() > prev_end {
23152 runs.push((prev_end..label.text.len(), fade_out));
23153 }
23154
23155 runs
23156 })
23157}
23158
23159pub(crate) fn split_words(text: &str) -> impl std::iter::Iterator<Item = &str> + '_ {
23160 let mut prev_index = 0;
23161 let mut prev_codepoint: Option<char> = None;
23162 text.char_indices()
23163 .chain([(text.len(), '\0')])
23164 .filter_map(move |(index, codepoint)| {
23165 let prev_codepoint = prev_codepoint.replace(codepoint)?;
23166 let is_boundary = index == text.len()
23167 || !prev_codepoint.is_uppercase() && codepoint.is_uppercase()
23168 || !prev_codepoint.is_alphanumeric() && codepoint.is_alphanumeric();
23169 if is_boundary {
23170 let chunk = &text[prev_index..index];
23171 prev_index = index;
23172 Some(chunk)
23173 } else {
23174 None
23175 }
23176 })
23177}
23178
23179pub trait RangeToAnchorExt: Sized {
23180 fn to_anchors(self, snapshot: &MultiBufferSnapshot) -> Range<Anchor>;
23181
23182 fn to_display_points(self, snapshot: &EditorSnapshot) -> Range<DisplayPoint> {
23183 let anchor_range = self.to_anchors(&snapshot.buffer_snapshot);
23184 anchor_range.start.to_display_point(snapshot)..anchor_range.end.to_display_point(snapshot)
23185 }
23186}
23187
23188impl<T: ToOffset> RangeToAnchorExt for Range<T> {
23189 fn to_anchors(self, snapshot: &MultiBufferSnapshot) -> Range<Anchor> {
23190 let start_offset = self.start.to_offset(snapshot);
23191 let end_offset = self.end.to_offset(snapshot);
23192 if start_offset == end_offset {
23193 snapshot.anchor_before(start_offset)..snapshot.anchor_before(end_offset)
23194 } else {
23195 snapshot.anchor_after(self.start)..snapshot.anchor_before(self.end)
23196 }
23197 }
23198}
23199
23200pub trait RowExt {
23201 fn as_f32(&self) -> f32;
23202
23203 fn next_row(&self) -> Self;
23204
23205 fn previous_row(&self) -> Self;
23206
23207 fn minus(&self, other: Self) -> u32;
23208}
23209
23210impl RowExt for DisplayRow {
23211 fn as_f32(&self) -> f32 {
23212 self.0 as f32
23213 }
23214
23215 fn next_row(&self) -> Self {
23216 Self(self.0 + 1)
23217 }
23218
23219 fn previous_row(&self) -> Self {
23220 Self(self.0.saturating_sub(1))
23221 }
23222
23223 fn minus(&self, other: Self) -> u32 {
23224 self.0 - other.0
23225 }
23226}
23227
23228impl RowExt for MultiBufferRow {
23229 fn as_f32(&self) -> f32 {
23230 self.0 as f32
23231 }
23232
23233 fn next_row(&self) -> Self {
23234 Self(self.0 + 1)
23235 }
23236
23237 fn previous_row(&self) -> Self {
23238 Self(self.0.saturating_sub(1))
23239 }
23240
23241 fn minus(&self, other: Self) -> u32 {
23242 self.0 - other.0
23243 }
23244}
23245
23246trait RowRangeExt {
23247 type Row;
23248
23249 fn len(&self) -> usize;
23250
23251 fn iter_rows(&self) -> impl DoubleEndedIterator<Item = Self::Row>;
23252}
23253
23254impl RowRangeExt for Range<MultiBufferRow> {
23255 type Row = MultiBufferRow;
23256
23257 fn len(&self) -> usize {
23258 (self.end.0 - self.start.0) as usize
23259 }
23260
23261 fn iter_rows(&self) -> impl DoubleEndedIterator<Item = MultiBufferRow> {
23262 (self.start.0..self.end.0).map(MultiBufferRow)
23263 }
23264}
23265
23266impl RowRangeExt for Range<DisplayRow> {
23267 type Row = DisplayRow;
23268
23269 fn len(&self) -> usize {
23270 (self.end.0 - self.start.0) as usize
23271 }
23272
23273 fn iter_rows(&self) -> impl DoubleEndedIterator<Item = DisplayRow> {
23274 (self.start.0..self.end.0).map(DisplayRow)
23275 }
23276}
23277
23278/// If select range has more than one line, we
23279/// just point the cursor to range.start.
23280fn collapse_multiline_range(range: Range<Point>) -> Range<Point> {
23281 if range.start.row == range.end.row {
23282 range
23283 } else {
23284 range.start..range.start
23285 }
23286}
23287pub struct KillRing(ClipboardItem);
23288impl Global for KillRing {}
23289
23290const UPDATE_DEBOUNCE: Duration = Duration::from_millis(50);
23291
23292enum BreakpointPromptEditAction {
23293 Log,
23294 Condition,
23295 HitCondition,
23296}
23297
23298struct BreakpointPromptEditor {
23299 pub(crate) prompt: Entity<Editor>,
23300 editor: WeakEntity<Editor>,
23301 breakpoint_anchor: Anchor,
23302 breakpoint: Breakpoint,
23303 edit_action: BreakpointPromptEditAction,
23304 block_ids: HashSet<CustomBlockId>,
23305 editor_margins: Arc<Mutex<EditorMargins>>,
23306 _subscriptions: Vec<Subscription>,
23307}
23308
23309impl BreakpointPromptEditor {
23310 const MAX_LINES: u8 = 4;
23311
23312 fn new(
23313 editor: WeakEntity<Editor>,
23314 breakpoint_anchor: Anchor,
23315 breakpoint: Breakpoint,
23316 edit_action: BreakpointPromptEditAction,
23317 window: &mut Window,
23318 cx: &mut Context<Self>,
23319 ) -> Self {
23320 let base_text = match edit_action {
23321 BreakpointPromptEditAction::Log => breakpoint.message.as_ref(),
23322 BreakpointPromptEditAction::Condition => breakpoint.condition.as_ref(),
23323 BreakpointPromptEditAction::HitCondition => breakpoint.hit_condition.as_ref(),
23324 }
23325 .map(|msg| msg.to_string())
23326 .unwrap_or_default();
23327
23328 let buffer = cx.new(|cx| Buffer::local(base_text, cx));
23329 let buffer = cx.new(|cx| MultiBuffer::singleton(buffer, cx));
23330
23331 let prompt = cx.new(|cx| {
23332 let mut prompt = Editor::new(
23333 EditorMode::AutoHeight {
23334 min_lines: 1,
23335 max_lines: Some(Self::MAX_LINES as usize),
23336 },
23337 buffer,
23338 None,
23339 window,
23340 cx,
23341 );
23342 prompt.set_soft_wrap_mode(language::language_settings::SoftWrap::EditorWidth, cx);
23343 prompt.set_show_cursor_when_unfocused(false, cx);
23344 prompt.set_placeholder_text(
23345 match edit_action {
23346 BreakpointPromptEditAction::Log => "Message to log when a breakpoint is hit. Expressions within {} are interpolated.",
23347 BreakpointPromptEditAction::Condition => "Condition when a breakpoint is hit. Expressions within {} are interpolated.",
23348 BreakpointPromptEditAction::HitCondition => "How many breakpoint hits to ignore",
23349 },
23350 cx,
23351 );
23352
23353 prompt
23354 });
23355
23356 Self {
23357 prompt,
23358 editor,
23359 breakpoint_anchor,
23360 breakpoint,
23361 edit_action,
23362 editor_margins: Arc::new(Mutex::new(EditorMargins::default())),
23363 block_ids: Default::default(),
23364 _subscriptions: vec![],
23365 }
23366 }
23367
23368 pub(crate) fn add_block_ids(&mut self, block_ids: Vec<CustomBlockId>) {
23369 self.block_ids.extend(block_ids)
23370 }
23371
23372 fn confirm(&mut self, _: &menu::Confirm, window: &mut Window, cx: &mut Context<Self>) {
23373 if let Some(editor) = self.editor.upgrade() {
23374 let message = self
23375 .prompt
23376 .read(cx)
23377 .buffer
23378 .read(cx)
23379 .as_singleton()
23380 .expect("A multi buffer in breakpoint prompt isn't possible")
23381 .read(cx)
23382 .as_rope()
23383 .to_string();
23384
23385 editor.update(cx, |editor, cx| {
23386 editor.edit_breakpoint_at_anchor(
23387 self.breakpoint_anchor,
23388 self.breakpoint.clone(),
23389 match self.edit_action {
23390 BreakpointPromptEditAction::Log => {
23391 BreakpointEditAction::EditLogMessage(message.into())
23392 }
23393 BreakpointPromptEditAction::Condition => {
23394 BreakpointEditAction::EditCondition(message.into())
23395 }
23396 BreakpointPromptEditAction::HitCondition => {
23397 BreakpointEditAction::EditHitCondition(message.into())
23398 }
23399 },
23400 cx,
23401 );
23402
23403 editor.remove_blocks(self.block_ids.clone(), None, cx);
23404 cx.focus_self(window);
23405 });
23406 }
23407 }
23408
23409 fn cancel(&mut self, _: &menu::Cancel, window: &mut Window, cx: &mut Context<Self>) {
23410 self.editor
23411 .update(cx, |editor, cx| {
23412 editor.remove_blocks(self.block_ids.clone(), None, cx);
23413 window.focus(&editor.focus_handle);
23414 })
23415 .log_err();
23416 }
23417
23418 fn render_prompt_editor(&self, cx: &mut Context<Self>) -> impl IntoElement {
23419 let settings = ThemeSettings::get_global(cx);
23420 let text_style = TextStyle {
23421 color: if self.prompt.read(cx).read_only(cx) {
23422 cx.theme().colors().text_disabled
23423 } else {
23424 cx.theme().colors().text
23425 },
23426 font_family: settings.buffer_font.family.clone(),
23427 font_fallbacks: settings.buffer_font.fallbacks.clone(),
23428 font_size: settings.buffer_font_size(cx).into(),
23429 font_weight: settings.buffer_font.weight,
23430 line_height: relative(settings.buffer_line_height.value()),
23431 ..Default::default()
23432 };
23433 EditorElement::new(
23434 &self.prompt,
23435 EditorStyle {
23436 background: cx.theme().colors().editor_background,
23437 local_player: cx.theme().players().local(),
23438 text: text_style,
23439 ..Default::default()
23440 },
23441 )
23442 }
23443}
23444
23445impl Render for BreakpointPromptEditor {
23446 fn render(&mut self, window: &mut Window, cx: &mut Context<Self>) -> impl IntoElement {
23447 let editor_margins = *self.editor_margins.lock();
23448 let gutter_dimensions = editor_margins.gutter;
23449 h_flex()
23450 .key_context("Editor")
23451 .bg(cx.theme().colors().editor_background)
23452 .border_y_1()
23453 .border_color(cx.theme().status().info_border)
23454 .size_full()
23455 .py(window.line_height() / 2.5)
23456 .on_action(cx.listener(Self::confirm))
23457 .on_action(cx.listener(Self::cancel))
23458 .child(h_flex().w(gutter_dimensions.full_width() + (gutter_dimensions.margin / 2.0)))
23459 .child(div().flex_1().child(self.render_prompt_editor(cx)))
23460 }
23461}
23462
23463impl Focusable for BreakpointPromptEditor {
23464 fn focus_handle(&self, cx: &App) -> FocusHandle {
23465 self.prompt.focus_handle(cx)
23466 }
23467}
23468
23469fn all_edits_insertions_or_deletions(
23470 edits: &Vec<(Range<Anchor>, String)>,
23471 snapshot: &MultiBufferSnapshot,
23472) -> bool {
23473 let mut all_insertions = true;
23474 let mut all_deletions = true;
23475
23476 for (range, new_text) in edits.iter() {
23477 let range_is_empty = range.to_offset(&snapshot).is_empty();
23478 let text_is_empty = new_text.is_empty();
23479
23480 if range_is_empty != text_is_empty {
23481 if range_is_empty {
23482 all_deletions = false;
23483 } else {
23484 all_insertions = false;
23485 }
23486 } else {
23487 return false;
23488 }
23489
23490 if !all_insertions && !all_deletions {
23491 return false;
23492 }
23493 }
23494 all_insertions || all_deletions
23495}
23496
23497struct MissingEditPredictionKeybindingTooltip;
23498
23499impl Render for MissingEditPredictionKeybindingTooltip {
23500 fn render(&mut self, window: &mut Window, cx: &mut Context<Self>) -> impl IntoElement {
23501 ui::tooltip_container(window, cx, |container, _, cx| {
23502 container
23503 .flex_shrink_0()
23504 .max_w_80()
23505 .min_h(rems_from_px(124.))
23506 .justify_between()
23507 .child(
23508 v_flex()
23509 .flex_1()
23510 .text_ui_sm(cx)
23511 .child(Label::new("Conflict with Accept Keybinding"))
23512 .child("Your keymap currently overrides the default accept keybinding. To continue, assign one keybinding for the `editor::AcceptEditPrediction` action.")
23513 )
23514 .child(
23515 h_flex()
23516 .pb_1()
23517 .gap_1()
23518 .items_end()
23519 .w_full()
23520 .child(Button::new("open-keymap", "Assign Keybinding").size(ButtonSize::Compact).on_click(|_ev, window, cx| {
23521 window.dispatch_action(zed_actions::OpenKeymap.boxed_clone(), cx)
23522 }))
23523 .child(Button::new("see-docs", "See Docs").size(ButtonSize::Compact).on_click(|_ev, _window, cx| {
23524 cx.open_url("https://zed.dev/docs/completions#edit-predictions-missing-keybinding");
23525 })),
23526 )
23527 })
23528 }
23529}
23530
23531#[derive(Debug, Clone, Copy, PartialEq)]
23532pub struct LineHighlight {
23533 pub background: Background,
23534 pub border: Option<gpui::Hsla>,
23535 pub include_gutter: bool,
23536 pub type_id: Option<TypeId>,
23537}
23538
23539struct LineManipulationResult {
23540 pub new_text: String,
23541 pub line_count_before: usize,
23542 pub line_count_after: usize,
23543}
23544
23545fn render_diff_hunk_controls(
23546 row: u32,
23547 status: &DiffHunkStatus,
23548 hunk_range: Range<Anchor>,
23549 is_created_file: bool,
23550 line_height: Pixels,
23551 editor: &Entity<Editor>,
23552 _window: &mut Window,
23553 cx: &mut App,
23554) -> AnyElement {
23555 h_flex()
23556 .h(line_height)
23557 .mr_1()
23558 .gap_1()
23559 .px_0p5()
23560 .pb_1()
23561 .border_x_1()
23562 .border_b_1()
23563 .border_color(cx.theme().colors().border_variant)
23564 .rounded_b_lg()
23565 .bg(cx.theme().colors().editor_background)
23566 .gap_1()
23567 .block_mouse_except_scroll()
23568 .shadow_md()
23569 .child(if status.has_secondary_hunk() {
23570 Button::new(("stage", row as u64), "Stage")
23571 .alpha(if status.is_pending() { 0.66 } else { 1.0 })
23572 .tooltip({
23573 let focus_handle = editor.focus_handle(cx);
23574 move |window, cx| {
23575 Tooltip::for_action_in(
23576 "Stage Hunk",
23577 &::git::ToggleStaged,
23578 &focus_handle,
23579 window,
23580 cx,
23581 )
23582 }
23583 })
23584 .on_click({
23585 let editor = editor.clone();
23586 move |_event, _window, cx| {
23587 editor.update(cx, |editor, cx| {
23588 editor.stage_or_unstage_diff_hunks(
23589 true,
23590 vec![hunk_range.start..hunk_range.start],
23591 cx,
23592 );
23593 });
23594 }
23595 })
23596 } else {
23597 Button::new(("unstage", row as u64), "Unstage")
23598 .alpha(if status.is_pending() { 0.66 } else { 1.0 })
23599 .tooltip({
23600 let focus_handle = editor.focus_handle(cx);
23601 move |window, cx| {
23602 Tooltip::for_action_in(
23603 "Unstage Hunk",
23604 &::git::ToggleStaged,
23605 &focus_handle,
23606 window,
23607 cx,
23608 )
23609 }
23610 })
23611 .on_click({
23612 let editor = editor.clone();
23613 move |_event, _window, cx| {
23614 editor.update(cx, |editor, cx| {
23615 editor.stage_or_unstage_diff_hunks(
23616 false,
23617 vec![hunk_range.start..hunk_range.start],
23618 cx,
23619 );
23620 });
23621 }
23622 })
23623 })
23624 .child(
23625 Button::new(("restore", row as u64), "Restore")
23626 .tooltip({
23627 let focus_handle = editor.focus_handle(cx);
23628 move |window, cx| {
23629 Tooltip::for_action_in(
23630 "Restore Hunk",
23631 &::git::Restore,
23632 &focus_handle,
23633 window,
23634 cx,
23635 )
23636 }
23637 })
23638 .on_click({
23639 let editor = editor.clone();
23640 move |_event, window, cx| {
23641 editor.update(cx, |editor, cx| {
23642 let snapshot = editor.snapshot(window, cx);
23643 let point = hunk_range.start.to_point(&snapshot.buffer_snapshot);
23644 editor.restore_hunks_in_ranges(vec![point..point], window, cx);
23645 });
23646 }
23647 })
23648 .disabled(is_created_file),
23649 )
23650 .when(
23651 !editor.read(cx).buffer().read(cx).all_diff_hunks_expanded(),
23652 |el| {
23653 el.child(
23654 IconButton::new(("next-hunk", row as u64), IconName::ArrowDown)
23655 .shape(IconButtonShape::Square)
23656 .icon_size(IconSize::Small)
23657 // .disabled(!has_multiple_hunks)
23658 .tooltip({
23659 let focus_handle = editor.focus_handle(cx);
23660 move |window, cx| {
23661 Tooltip::for_action_in(
23662 "Next Hunk",
23663 &GoToHunk,
23664 &focus_handle,
23665 window,
23666 cx,
23667 )
23668 }
23669 })
23670 .on_click({
23671 let editor = editor.clone();
23672 move |_event, window, cx| {
23673 editor.update(cx, |editor, cx| {
23674 let snapshot = editor.snapshot(window, cx);
23675 let position =
23676 hunk_range.end.to_point(&snapshot.buffer_snapshot);
23677 editor.go_to_hunk_before_or_after_position(
23678 &snapshot,
23679 position,
23680 Direction::Next,
23681 window,
23682 cx,
23683 );
23684 editor.expand_selected_diff_hunks(cx);
23685 });
23686 }
23687 }),
23688 )
23689 .child(
23690 IconButton::new(("prev-hunk", row as u64), IconName::ArrowUp)
23691 .shape(IconButtonShape::Square)
23692 .icon_size(IconSize::Small)
23693 // .disabled(!has_multiple_hunks)
23694 .tooltip({
23695 let focus_handle = editor.focus_handle(cx);
23696 move |window, cx| {
23697 Tooltip::for_action_in(
23698 "Previous Hunk",
23699 &GoToPreviousHunk,
23700 &focus_handle,
23701 window,
23702 cx,
23703 )
23704 }
23705 })
23706 .on_click({
23707 let editor = editor.clone();
23708 move |_event, window, cx| {
23709 editor.update(cx, |editor, cx| {
23710 let snapshot = editor.snapshot(window, cx);
23711 let point =
23712 hunk_range.start.to_point(&snapshot.buffer_snapshot);
23713 editor.go_to_hunk_before_or_after_position(
23714 &snapshot,
23715 point,
23716 Direction::Prev,
23717 window,
23718 cx,
23719 );
23720 editor.expand_selected_diff_hunks(cx);
23721 });
23722 }
23723 }),
23724 )
23725 },
23726 )
23727 .into_any_element()
23728}