1#![allow(rustdoc::private_intra_doc_links)]
2//! This is the place where everything editor-related is stored (data-wise) and displayed (ui-wise).
3//! The main point of interest in this crate is [`Editor`] type, which is used in every other Zed part as a user input element.
4//! It comes in different flavors: single line, multiline and a fixed height one.
5//!
6//! Editor contains of multiple large submodules:
7//! * [`element`] — the place where all rendering happens
8//! * [`display_map`] - chunks up text in the editor into the logical blocks, establishes coordinates and mapping between each of them.
9//! Contains all metadata related to text transformations (folds, fake inlay text insertions, soft wraps, tab markup, etc.).
10//! * [`inlay_hint_cache`] - is a storage of inlay hints out of LSP requests, responsible for querying LSP and updating `display_map`'s state accordingly.
11//!
12//! All other submodules and structs are mostly concerned with holding editor data about the way it displays current buffer region(s).
13//!
14//! If you're looking to improve Vim mode, you should check out Vim crate that wraps Editor and overrides its behavior.
15pub mod actions;
16mod blink_manager;
17mod clangd_ext;
18pub mod code_context_menus;
19pub mod display_map;
20mod editor_settings;
21mod editor_settings_controls;
22mod element;
23mod git;
24mod highlight_matching_bracket;
25mod hover_links;
26pub mod hover_popover;
27mod indent_guides;
28mod inlay_hint_cache;
29pub mod items;
30mod jsx_tag_auto_close;
31mod linked_editing_ranges;
32mod lsp_colors;
33mod lsp_ext;
34mod mouse_context_menu;
35pub mod movement;
36mod persistence;
37mod proposed_changes_editor;
38mod rust_analyzer_ext;
39pub mod scroll;
40mod selections_collection;
41pub mod tasks;
42
43#[cfg(test)]
44mod code_completion_tests;
45#[cfg(test)]
46mod editor_tests;
47#[cfg(test)]
48mod inline_completion_tests;
49mod signature_help;
50#[cfg(any(test, feature = "test-support"))]
51pub mod test;
52
53pub(crate) use actions::*;
54pub use actions::{AcceptEditPrediction, OpenExcerpts, OpenExcerptsSplit};
55use aho_corasick::AhoCorasick;
56use anyhow::{Context as _, Result, anyhow};
57use blink_manager::BlinkManager;
58use buffer_diff::DiffHunkStatus;
59use client::{Collaborator, ParticipantIndex};
60use clock::{AGENT_REPLICA_ID, ReplicaId};
61use collections::{BTreeMap, HashMap, HashSet, VecDeque};
62use convert_case::{Case, Casing};
63use dap::TelemetrySpawnLocation;
64use display_map::*;
65pub use display_map::{ChunkRenderer, ChunkRendererContext, DisplayPoint, FoldPlaceholder};
66pub use editor_settings::{
67 CurrentLineHighlight, DocumentColorsRenderMode, EditorSettings, HideMouseMode,
68 ScrollBeyondLastLine, ScrollbarAxes, SearchSettings, ShowScrollbar,
69};
70use editor_settings::{GoToDefinitionFallback, Minimap as MinimapSettings};
71pub use editor_settings_controls::*;
72use element::{AcceptEditPredictionBinding, LineWithInvisibles, PositionMap, layout_line};
73pub use element::{
74 CursorLayout, EditorElement, HighlightedRange, HighlightedRangeLine, PointForPosition,
75};
76use futures::{
77 FutureExt, StreamExt as _,
78 future::{self, Shared, join},
79 stream::FuturesUnordered,
80};
81use fuzzy::{StringMatch, StringMatchCandidate};
82use lsp_colors::LspColorData;
83
84use ::git::blame::BlameEntry;
85use ::git::{Restore, blame::ParsedCommitMessage};
86use code_context_menus::{
87 AvailableCodeAction, CodeActionContents, CodeActionsItem, CodeActionsMenu, CodeContextMenu,
88 CompletionsMenu, ContextMenuOrigin,
89};
90use git::blame::{GitBlame, GlobalBlameRenderer};
91use gpui::{
92 Action, Animation, AnimationExt, AnyElement, App, AppContext, AsyncWindowContext,
93 AvailableSpace, Background, Bounds, ClickEvent, ClipboardEntry, ClipboardItem, Context,
94 DispatchPhase, Edges, Entity, EntityInputHandler, EventEmitter, FocusHandle, FocusOutEvent,
95 Focusable, FontId, FontWeight, Global, HighlightStyle, Hsla, KeyContext, Modifiers,
96 MouseButton, MouseDownEvent, PaintQuad, ParentElement, Pixels, Render, ScrollHandle,
97 SharedString, Size, Stateful, Styled, Subscription, Task, TextStyle, TextStyleRefinement,
98 UTF16Selection, UnderlineStyle, UniformListScrollHandle, WeakEntity, WeakFocusHandle, Window,
99 div, point, prelude::*, pulsating_between, px, relative, size,
100};
101use highlight_matching_bracket::refresh_matching_bracket_highlights;
102use hover_links::{HoverLink, HoveredLinkState, InlayHighlight, find_file};
103pub use hover_popover::hover_markdown_style;
104use hover_popover::{HoverState, hide_hover};
105use indent_guides::ActiveIndentGuidesState;
106use inlay_hint_cache::{InlayHintCache, InlaySplice, InvalidationStrategy};
107pub use inline_completion::Direction;
108use inline_completion::{EditPredictionProvider, InlineCompletionProviderHandle};
109pub use items::MAX_TAB_TITLE_LEN;
110use itertools::Itertools;
111use language::{
112 AutoindentMode, BracketMatch, BracketPair, Buffer, Capability, CharKind, CodeLabel,
113 CursorShape, DiagnosticEntry, DiffOptions, DocumentationConfig, EditPredictionsMode,
114 EditPreview, HighlightedText, IndentKind, IndentSize, Language, OffsetRangeExt, Point,
115 Selection, SelectionGoal, TextObject, TransactionId, TreeSitterOptions, WordsQuery,
116 language_settings::{
117 self, InlayHintSettings, LspInsertMode, RewrapBehavior, WordsCompletionMode,
118 all_language_settings, language_settings,
119 },
120 point_from_lsp, text_diff_with_options,
121};
122use language::{BufferRow, CharClassifier, Runnable, RunnableRange, point_to_lsp};
123use linked_editing_ranges::refresh_linked_ranges;
124use markdown::Markdown;
125use mouse_context_menu::MouseContextMenu;
126use persistence::DB;
127use project::{
128 BreakpointWithPosition, CompletionResponse, ProjectPath,
129 debugger::{
130 breakpoint_store::{
131 BreakpointEditAction, BreakpointSessionState, BreakpointState, BreakpointStore,
132 BreakpointStoreEvent,
133 },
134 session::{Session, SessionEvent},
135 },
136 git_store::{GitStoreEvent, RepositoryEvent},
137 project_settings::DiagnosticSeverity,
138};
139
140pub use git::blame::BlameRenderer;
141pub use proposed_changes_editor::{
142 ProposedChangeLocation, ProposedChangesEditor, ProposedChangesEditorToolbar,
143};
144use std::{cell::OnceCell, iter::Peekable, ops::Not};
145use task::{ResolvedTask, RunnableTag, TaskTemplate, TaskVariables};
146
147pub use lsp::CompletionContext;
148use lsp::{
149 CodeActionKind, CompletionItemKind, CompletionTriggerKind, InsertTextFormat, InsertTextMode,
150 LanguageServerId, LanguageServerName,
151};
152
153use language::BufferSnapshot;
154pub use lsp_ext::lsp_tasks;
155use movement::TextLayoutDetails;
156pub use multi_buffer::{
157 Anchor, AnchorRangeExt, ExcerptId, ExcerptRange, MultiBuffer, MultiBufferSnapshot, PathKey,
158 RowInfo, ToOffset, ToPoint,
159};
160use multi_buffer::{
161 ExcerptInfo, ExpandExcerptDirection, MultiBufferDiffHunk, MultiBufferPoint, MultiBufferRow,
162 MultiOrSingleBufferOffsetRange, ToOffsetUtf16,
163};
164use parking_lot::Mutex;
165use project::{
166 CodeAction, Completion, CompletionIntent, CompletionSource, DocumentHighlight, InlayHint,
167 Location, LocationLink, PrepareRenameResponse, Project, ProjectItem, ProjectTransaction,
168 TaskSourceKind,
169 debugger::breakpoint_store::Breakpoint,
170 lsp_store::{CompletionDocumentation, FormatTrigger, LspFormatTarget, OpenLspBufferHandle},
171 project_settings::{GitGutterSetting, ProjectSettings},
172};
173use rand::prelude::*;
174use rpc::{ErrorExt, proto::*};
175use scroll::{Autoscroll, OngoingScroll, ScrollAnchor, ScrollManager, ScrollbarAutoHide};
176use selections_collection::{
177 MutableSelectionsCollection, SelectionsCollection, resolve_selections,
178};
179use serde::{Deserialize, Serialize};
180use settings::{Settings, SettingsLocation, SettingsStore, update_settings_file};
181use smallvec::{SmallVec, smallvec};
182use snippet::Snippet;
183use std::sync::Arc;
184use std::{
185 any::TypeId,
186 borrow::Cow,
187 cell::RefCell,
188 cmp::{self, Ordering, Reverse},
189 mem,
190 num::NonZeroU32,
191 ops::{ControlFlow, Deref, DerefMut, Range, RangeInclusive},
192 path::{Path, PathBuf},
193 rc::Rc,
194 time::{Duration, Instant},
195};
196pub use sum_tree::Bias;
197use sum_tree::TreeMap;
198use text::{BufferId, FromAnchor, OffsetUtf16, Rope};
199use theme::{
200 ActiveTheme, PlayerColor, StatusColors, SyntaxTheme, Theme, ThemeSettings,
201 observe_buffer_font_size_adjustment,
202};
203use ui::{
204 ButtonSize, ButtonStyle, ContextMenu, Disclosure, IconButton, IconButtonShape, IconName,
205 IconSize, Indicator, Key, Tooltip, h_flex, prelude::*,
206};
207use util::{RangeExt, ResultExt, TryFutureExt, maybe, post_inc};
208use workspace::{
209 CollaboratorId, Item as WorkspaceItem, ItemId, ItemNavHistory, OpenInTerminal, OpenTerminal,
210 RestoreOnStartupBehavior, SERIALIZATION_THROTTLE_TIME, SplitDirection, TabBarSettings, Toast,
211 ViewId, Workspace, WorkspaceId, WorkspaceSettings,
212 item::{ItemHandle, PreviewTabsSettings, SaveOptions},
213 notifications::{DetachAndPromptErr, NotificationId, NotifyTaskExt},
214 searchable::SearchEvent,
215};
216
217use crate::{
218 code_context_menus::CompletionsMenuSource,
219 hover_links::{find_url, find_url_from_range},
220};
221use crate::{
222 editor_settings::MultiCursorModifier,
223 signature_help::{SignatureHelpHiddenBy, SignatureHelpState},
224};
225
226pub const FILE_HEADER_HEIGHT: u32 = 2;
227pub const MULTI_BUFFER_EXCERPT_HEADER_HEIGHT: u32 = 1;
228pub const DEFAULT_MULTIBUFFER_CONTEXT: u32 = 2;
229const CURSOR_BLINK_INTERVAL: Duration = Duration::from_millis(500);
230const MAX_LINE_LEN: usize = 1024;
231const MIN_NAVIGATION_HISTORY_ROW_DELTA: i64 = 10;
232const MAX_SELECTION_HISTORY_LEN: usize = 1024;
233pub(crate) const CURSORS_VISIBLE_FOR: Duration = Duration::from_millis(2000);
234#[doc(hidden)]
235pub const CODE_ACTIONS_DEBOUNCE_TIMEOUT: Duration = Duration::from_millis(250);
236const SELECTION_HIGHLIGHT_DEBOUNCE_TIMEOUT: Duration = Duration::from_millis(100);
237
238pub(crate) const CODE_ACTION_TIMEOUT: Duration = Duration::from_secs(5);
239pub(crate) const FORMAT_TIMEOUT: Duration = Duration::from_secs(5);
240pub(crate) const SCROLL_CENTER_TOP_BOTTOM_DEBOUNCE_TIMEOUT: Duration = Duration::from_secs(1);
241
242pub(crate) const EDIT_PREDICTION_KEY_CONTEXT: &str = "edit_prediction";
243pub(crate) const EDIT_PREDICTION_CONFLICT_KEY_CONTEXT: &str = "edit_prediction_conflict";
244pub(crate) const MINIMAP_FONT_SIZE: AbsoluteLength = AbsoluteLength::Pixels(px(2.));
245
246pub type RenderDiffHunkControlsFn = Arc<
247 dyn Fn(
248 u32,
249 &DiffHunkStatus,
250 Range<Anchor>,
251 bool,
252 Pixels,
253 &Entity<Editor>,
254 &mut Window,
255 &mut App,
256 ) -> AnyElement,
257>;
258
259struct InlineValueCache {
260 enabled: bool,
261 inlays: Vec<InlayId>,
262 refresh_task: Task<Option<()>>,
263}
264
265impl InlineValueCache {
266 fn new(enabled: bool) -> Self {
267 Self {
268 enabled,
269 inlays: Vec::new(),
270 refresh_task: Task::ready(None),
271 }
272 }
273}
274
275#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
276pub enum InlayId {
277 InlineCompletion(usize),
278 DebuggerValue(usize),
279 // LSP
280 Hint(usize),
281 Color(usize),
282}
283
284impl InlayId {
285 fn id(&self) -> usize {
286 match self {
287 Self::InlineCompletion(id) => *id,
288 Self::DebuggerValue(id) => *id,
289 Self::Hint(id) => *id,
290 Self::Color(id) => *id,
291 }
292 }
293}
294
295pub enum ActiveDebugLine {}
296pub enum DebugStackFrameLine {}
297enum DocumentHighlightRead {}
298enum DocumentHighlightWrite {}
299enum InputComposition {}
300pub enum PendingInput {}
301enum SelectedTextHighlight {}
302
303pub enum ConflictsOuter {}
304pub enum ConflictsOurs {}
305pub enum ConflictsTheirs {}
306pub enum ConflictsOursMarker {}
307pub enum ConflictsTheirsMarker {}
308
309#[derive(Debug, Copy, Clone, PartialEq, Eq)]
310pub enum Navigated {
311 Yes,
312 No,
313}
314
315impl Navigated {
316 pub fn from_bool(yes: bool) -> Navigated {
317 if yes { Navigated::Yes } else { Navigated::No }
318 }
319}
320
321#[derive(Debug, Clone, PartialEq, Eq)]
322enum DisplayDiffHunk {
323 Folded {
324 display_row: DisplayRow,
325 },
326 Unfolded {
327 is_created_file: bool,
328 diff_base_byte_range: Range<usize>,
329 display_row_range: Range<DisplayRow>,
330 multi_buffer_range: Range<Anchor>,
331 status: DiffHunkStatus,
332 },
333}
334
335pub enum HideMouseCursorOrigin {
336 TypingAction,
337 MovementAction,
338}
339
340pub fn init_settings(cx: &mut App) {
341 EditorSettings::register(cx);
342}
343
344pub fn init(cx: &mut App) {
345 init_settings(cx);
346
347 cx.set_global(GlobalBlameRenderer(Arc::new(())));
348
349 workspace::register_project_item::<Editor>(cx);
350 workspace::FollowableViewRegistry::register::<Editor>(cx);
351 workspace::register_serializable_item::<Editor>(cx);
352
353 cx.observe_new(
354 |workspace: &mut Workspace, _: Option<&mut Window>, _cx: &mut Context<Workspace>| {
355 workspace.register_action(Editor::new_file);
356 workspace.register_action(Editor::new_file_vertical);
357 workspace.register_action(Editor::new_file_horizontal);
358 workspace.register_action(Editor::cancel_language_server_work);
359 },
360 )
361 .detach();
362
363 cx.on_action(move |_: &workspace::NewFile, cx| {
364 let app_state = workspace::AppState::global(cx);
365 if let Some(app_state) = app_state.upgrade() {
366 workspace::open_new(
367 Default::default(),
368 app_state,
369 cx,
370 |workspace, window, cx| {
371 Editor::new_file(workspace, &Default::default(), window, cx)
372 },
373 )
374 .detach();
375 }
376 });
377 cx.on_action(move |_: &workspace::NewWindow, cx| {
378 let app_state = workspace::AppState::global(cx);
379 if let Some(app_state) = app_state.upgrade() {
380 workspace::open_new(
381 Default::default(),
382 app_state,
383 cx,
384 |workspace, window, cx| {
385 cx.activate(true);
386 Editor::new_file(workspace, &Default::default(), window, cx)
387 },
388 )
389 .detach();
390 }
391 });
392}
393
394pub fn set_blame_renderer(renderer: impl BlameRenderer + 'static, cx: &mut App) {
395 cx.set_global(GlobalBlameRenderer(Arc::new(renderer)));
396}
397
398pub trait DiagnosticRenderer {
399 fn render_group(
400 &self,
401 diagnostic_group: Vec<DiagnosticEntry<Point>>,
402 buffer_id: BufferId,
403 snapshot: EditorSnapshot,
404 editor: WeakEntity<Editor>,
405 cx: &mut App,
406 ) -> Vec<BlockProperties<Anchor>>;
407
408 fn render_hover(
409 &self,
410 diagnostic_group: Vec<DiagnosticEntry<Point>>,
411 range: Range<Point>,
412 buffer_id: BufferId,
413 cx: &mut App,
414 ) -> Option<Entity<markdown::Markdown>>;
415
416 fn open_link(
417 &self,
418 editor: &mut Editor,
419 link: SharedString,
420 window: &mut Window,
421 cx: &mut Context<Editor>,
422 );
423}
424
425pub(crate) struct GlobalDiagnosticRenderer(pub Arc<dyn DiagnosticRenderer>);
426
427impl GlobalDiagnosticRenderer {
428 fn global(cx: &App) -> Option<Arc<dyn DiagnosticRenderer>> {
429 cx.try_global::<Self>().map(|g| g.0.clone())
430 }
431}
432
433impl gpui::Global for GlobalDiagnosticRenderer {}
434pub fn set_diagnostic_renderer(renderer: impl DiagnosticRenderer + 'static, cx: &mut App) {
435 cx.set_global(GlobalDiagnosticRenderer(Arc::new(renderer)));
436}
437
438pub struct SearchWithinRange;
439
440trait InvalidationRegion {
441 fn ranges(&self) -> &[Range<Anchor>];
442}
443
444#[derive(Clone, Debug, PartialEq)]
445pub enum SelectPhase {
446 Begin {
447 position: DisplayPoint,
448 add: bool,
449 click_count: usize,
450 },
451 BeginColumnar {
452 position: DisplayPoint,
453 reset: bool,
454 mode: ColumnarMode,
455 goal_column: u32,
456 },
457 Extend {
458 position: DisplayPoint,
459 click_count: usize,
460 },
461 Update {
462 position: DisplayPoint,
463 goal_column: u32,
464 scroll_delta: gpui::Point<f32>,
465 },
466 End,
467}
468
469#[derive(Clone, Debug, PartialEq)]
470pub enum ColumnarMode {
471 FromMouse,
472 FromSelection,
473}
474
475#[derive(Clone, Debug)]
476pub enum SelectMode {
477 Character,
478 Word(Range<Anchor>),
479 Line(Range<Anchor>),
480 All,
481}
482
483#[derive(Clone, PartialEq, Eq, Debug)]
484pub enum EditorMode {
485 SingleLine {
486 auto_width: bool,
487 },
488 AutoHeight {
489 min_lines: usize,
490 max_lines: Option<usize>,
491 },
492 Full {
493 /// When set to `true`, the editor will scale its UI elements with the buffer font size.
494 scale_ui_elements_with_buffer_font_size: bool,
495 /// When set to `true`, the editor will render a background for the active line.
496 show_active_line_background: bool,
497 /// When set to `true`, the editor's height will be determined by its content.
498 sized_by_content: bool,
499 },
500 Minimap {
501 parent: WeakEntity<Editor>,
502 },
503}
504
505impl EditorMode {
506 pub fn full() -> Self {
507 Self::Full {
508 scale_ui_elements_with_buffer_font_size: true,
509 show_active_line_background: true,
510 sized_by_content: false,
511 }
512 }
513
514 #[inline]
515 pub fn is_full(&self) -> bool {
516 matches!(self, Self::Full { .. })
517 }
518
519 #[inline]
520 pub fn is_single_line(&self) -> bool {
521 matches!(self, Self::SingleLine { .. })
522 }
523
524 #[inline]
525 fn is_minimap(&self) -> bool {
526 matches!(self, Self::Minimap { .. })
527 }
528}
529
530#[derive(Copy, Clone, Debug)]
531pub enum SoftWrap {
532 /// Prefer not to wrap at all.
533 ///
534 /// Note: this is currently internal, as actually limited by [`crate::MAX_LINE_LEN`] until it wraps.
535 /// The mode is used inside git diff hunks, where it's seems currently more useful to not wrap as much as possible.
536 GitDiff,
537 /// Prefer a single line generally, unless an overly long line is encountered.
538 None,
539 /// Soft wrap lines that exceed the editor width.
540 EditorWidth,
541 /// Soft wrap lines at the preferred line length.
542 Column(u32),
543 /// Soft wrap line at the preferred line length or the editor width (whichever is smaller).
544 Bounded(u32),
545}
546
547#[derive(Clone)]
548pub struct EditorStyle {
549 pub background: Hsla,
550 pub border: Hsla,
551 pub local_player: PlayerColor,
552 pub text: TextStyle,
553 pub scrollbar_width: Pixels,
554 pub syntax: Arc<SyntaxTheme>,
555 pub status: StatusColors,
556 pub inlay_hints_style: HighlightStyle,
557 pub inline_completion_styles: InlineCompletionStyles,
558 pub unnecessary_code_fade: f32,
559 pub show_underlines: bool,
560}
561
562impl Default for EditorStyle {
563 fn default() -> Self {
564 Self {
565 background: Hsla::default(),
566 border: Hsla::default(),
567 local_player: PlayerColor::default(),
568 text: TextStyle::default(),
569 scrollbar_width: Pixels::default(),
570 syntax: Default::default(),
571 // HACK: Status colors don't have a real default.
572 // We should look into removing the status colors from the editor
573 // style and retrieve them directly from the theme.
574 status: StatusColors::dark(),
575 inlay_hints_style: HighlightStyle::default(),
576 inline_completion_styles: InlineCompletionStyles {
577 insertion: HighlightStyle::default(),
578 whitespace: HighlightStyle::default(),
579 },
580 unnecessary_code_fade: Default::default(),
581 show_underlines: true,
582 }
583 }
584}
585
586pub fn make_inlay_hints_style(cx: &mut App) -> HighlightStyle {
587 let show_background = language_settings::language_settings(None, None, cx)
588 .inlay_hints
589 .show_background;
590
591 HighlightStyle {
592 color: Some(cx.theme().status().hint),
593 background_color: show_background.then(|| cx.theme().status().hint_background),
594 ..HighlightStyle::default()
595 }
596}
597
598pub fn make_suggestion_styles(cx: &mut App) -> InlineCompletionStyles {
599 InlineCompletionStyles {
600 insertion: HighlightStyle {
601 color: Some(cx.theme().status().predictive),
602 ..HighlightStyle::default()
603 },
604 whitespace: HighlightStyle {
605 background_color: Some(cx.theme().status().created_background),
606 ..HighlightStyle::default()
607 },
608 }
609}
610
611type CompletionId = usize;
612
613pub(crate) enum EditDisplayMode {
614 TabAccept,
615 DiffPopover,
616 Inline,
617}
618
619enum InlineCompletion {
620 Edit {
621 edits: Vec<(Range<Anchor>, String)>,
622 edit_preview: Option<EditPreview>,
623 display_mode: EditDisplayMode,
624 snapshot: BufferSnapshot,
625 },
626 Move {
627 target: Anchor,
628 snapshot: BufferSnapshot,
629 },
630}
631
632struct InlineCompletionState {
633 inlay_ids: Vec<InlayId>,
634 completion: InlineCompletion,
635 completion_id: Option<SharedString>,
636 invalidation_range: Range<Anchor>,
637}
638
639enum EditPredictionSettings {
640 Disabled,
641 Enabled {
642 show_in_menu: bool,
643 preview_requires_modifier: bool,
644 },
645}
646
647enum InlineCompletionHighlight {}
648
649#[derive(Debug, Clone)]
650struct InlineDiagnostic {
651 message: SharedString,
652 group_id: usize,
653 is_primary: bool,
654 start: Point,
655 severity: lsp::DiagnosticSeverity,
656}
657
658pub enum MenuInlineCompletionsPolicy {
659 Never,
660 ByProvider,
661}
662
663pub enum EditPredictionPreview {
664 /// Modifier is not pressed
665 Inactive { released_too_fast: bool },
666 /// Modifier pressed
667 Active {
668 since: Instant,
669 previous_scroll_position: Option<ScrollAnchor>,
670 },
671}
672
673impl EditPredictionPreview {
674 pub fn released_too_fast(&self) -> bool {
675 match self {
676 EditPredictionPreview::Inactive { released_too_fast } => *released_too_fast,
677 EditPredictionPreview::Active { .. } => false,
678 }
679 }
680
681 pub fn set_previous_scroll_position(&mut self, scroll_position: Option<ScrollAnchor>) {
682 if let EditPredictionPreview::Active {
683 previous_scroll_position,
684 ..
685 } = self
686 {
687 *previous_scroll_position = scroll_position;
688 }
689 }
690}
691
692pub struct ContextMenuOptions {
693 pub min_entries_visible: usize,
694 pub max_entries_visible: usize,
695 pub placement: Option<ContextMenuPlacement>,
696}
697
698#[derive(Debug, Clone, PartialEq, Eq)]
699pub enum ContextMenuPlacement {
700 Above,
701 Below,
702}
703
704#[derive(Copy, Clone, Eq, PartialEq, PartialOrd, Ord, Debug, Default)]
705struct EditorActionId(usize);
706
707impl EditorActionId {
708 pub fn post_inc(&mut self) -> Self {
709 let answer = self.0;
710
711 *self = Self(answer + 1);
712
713 Self(answer)
714 }
715}
716
717// type GetFieldEditorTheme = dyn Fn(&theme::Theme) -> theme::FieldEditor;
718// type OverrideTextStyle = dyn Fn(&EditorStyle) -> Option<HighlightStyle>;
719
720type BackgroundHighlight = (fn(&Theme) -> Hsla, Arc<[Range<Anchor>]>);
721type GutterHighlight = (fn(&App) -> Hsla, Vec<Range<Anchor>>);
722
723#[derive(Default)]
724struct ScrollbarMarkerState {
725 scrollbar_size: Size<Pixels>,
726 dirty: bool,
727 markers: Arc<[PaintQuad]>,
728 pending_refresh: Option<Task<Result<()>>>,
729}
730
731impl ScrollbarMarkerState {
732 fn should_refresh(&self, scrollbar_size: Size<Pixels>) -> bool {
733 self.pending_refresh.is_none() && (self.scrollbar_size != scrollbar_size || self.dirty)
734 }
735}
736
737#[derive(Clone, Copy, PartialEq, Eq)]
738pub enum MinimapVisibility {
739 Disabled,
740 Enabled {
741 /// The configuration currently present in the users settings.
742 setting_configuration: bool,
743 /// Whether to override the currently set visibility from the users setting.
744 toggle_override: bool,
745 },
746}
747
748impl MinimapVisibility {
749 fn for_mode(mode: &EditorMode, cx: &App) -> Self {
750 if mode.is_full() {
751 Self::Enabled {
752 setting_configuration: EditorSettings::get_global(cx).minimap.minimap_enabled(),
753 toggle_override: false,
754 }
755 } else {
756 Self::Disabled
757 }
758 }
759
760 fn hidden(&self) -> Self {
761 match *self {
762 Self::Enabled {
763 setting_configuration,
764 ..
765 } => Self::Enabled {
766 setting_configuration,
767 toggle_override: setting_configuration,
768 },
769 Self::Disabled => Self::Disabled,
770 }
771 }
772
773 fn disabled(&self) -> bool {
774 match *self {
775 Self::Disabled => true,
776 _ => false,
777 }
778 }
779
780 fn settings_visibility(&self) -> bool {
781 match *self {
782 Self::Enabled {
783 setting_configuration,
784 ..
785 } => setting_configuration,
786 _ => false,
787 }
788 }
789
790 fn visible(&self) -> bool {
791 match *self {
792 Self::Enabled {
793 setting_configuration,
794 toggle_override,
795 } => setting_configuration ^ toggle_override,
796 _ => false,
797 }
798 }
799
800 fn toggle_visibility(&self) -> Self {
801 match *self {
802 Self::Enabled {
803 toggle_override,
804 setting_configuration,
805 } => Self::Enabled {
806 setting_configuration,
807 toggle_override: !toggle_override,
808 },
809 Self::Disabled => Self::Disabled,
810 }
811 }
812}
813
814#[derive(Clone, Debug)]
815struct RunnableTasks {
816 templates: Vec<(TaskSourceKind, TaskTemplate)>,
817 offset: multi_buffer::Anchor,
818 // We need the column at which the task context evaluation should take place (when we're spawning it via gutter).
819 column: u32,
820 // Values of all named captures, including those starting with '_'
821 extra_variables: HashMap<String, String>,
822 // Full range of the tagged region. We use it to determine which `extra_variables` to grab for context resolution in e.g. a modal.
823 context_range: Range<BufferOffset>,
824}
825
826impl RunnableTasks {
827 fn resolve<'a>(
828 &'a self,
829 cx: &'a task::TaskContext,
830 ) -> impl Iterator<Item = (TaskSourceKind, ResolvedTask)> + 'a {
831 self.templates.iter().filter_map(|(kind, template)| {
832 template
833 .resolve_task(&kind.to_id_base(), cx)
834 .map(|task| (kind.clone(), task))
835 })
836 }
837}
838
839#[derive(Clone)]
840pub struct ResolvedTasks {
841 templates: SmallVec<[(TaskSourceKind, ResolvedTask); 1]>,
842 position: Anchor,
843}
844
845#[derive(Copy, Clone, Debug, PartialEq, PartialOrd)]
846struct BufferOffset(usize);
847
848// Addons allow storing per-editor state in other crates (e.g. Vim)
849pub trait Addon: 'static {
850 fn extend_key_context(&self, _: &mut KeyContext, _: &App) {}
851
852 fn render_buffer_header_controls(
853 &self,
854 _: &ExcerptInfo,
855 _: &Window,
856 _: &App,
857 ) -> Option<AnyElement> {
858 None
859 }
860
861 fn to_any(&self) -> &dyn std::any::Any;
862
863 fn to_any_mut(&mut self) -> Option<&mut dyn std::any::Any> {
864 None
865 }
866}
867
868struct ChangeLocation {
869 current: Option<Vec<Anchor>>,
870 original: Vec<Anchor>,
871}
872impl ChangeLocation {
873 fn locations(&self) -> &[Anchor] {
874 self.current.as_ref().unwrap_or(&self.original)
875 }
876}
877
878/// A set of caret positions, registered when the editor was edited.
879pub struct ChangeList {
880 changes: Vec<ChangeLocation>,
881 /// Currently "selected" change.
882 position: Option<usize>,
883}
884
885impl ChangeList {
886 pub fn new() -> Self {
887 Self {
888 changes: Vec::new(),
889 position: None,
890 }
891 }
892
893 /// Moves to the next change in the list (based on the direction given) and returns the caret positions for the next change.
894 /// If reaches the end of the list in the direction, returns the corresponding change until called for a different direction.
895 pub fn next_change(&mut self, count: usize, direction: Direction) -> Option<&[Anchor]> {
896 if self.changes.is_empty() {
897 return None;
898 }
899
900 let prev = self.position.unwrap_or(self.changes.len());
901 let next = if direction == Direction::Prev {
902 prev.saturating_sub(count)
903 } else {
904 (prev + count).min(self.changes.len() - 1)
905 };
906 self.position = Some(next);
907 self.changes.get(next).map(|change| change.locations())
908 }
909
910 /// Adds a new change to the list, resetting the change list position.
911 pub fn push_to_change_list(&mut self, group: bool, new_positions: Vec<Anchor>) {
912 self.position.take();
913 if let Some(last) = self.changes.last_mut()
914 && group
915 {
916 last.current = Some(new_positions)
917 } else {
918 self.changes.push(ChangeLocation {
919 original: new_positions,
920 current: None,
921 });
922 }
923 }
924
925 pub fn last(&self) -> Option<&[Anchor]> {
926 self.changes.last().map(|change| change.locations())
927 }
928
929 pub fn last_before_grouping(&self) -> Option<&[Anchor]> {
930 self.changes.last().map(|change| change.original.as_slice())
931 }
932
933 pub fn invert_last_group(&mut self) {
934 if let Some(last) = self.changes.last_mut() {
935 if let Some(current) = last.current.as_mut() {
936 mem::swap(&mut last.original, current);
937 }
938 }
939 }
940}
941
942#[derive(Clone)]
943struct InlineBlamePopoverState {
944 scroll_handle: ScrollHandle,
945 commit_message: Option<ParsedCommitMessage>,
946 markdown: Entity<Markdown>,
947}
948
949struct InlineBlamePopover {
950 position: gpui::Point<Pixels>,
951 hide_task: Option<Task<()>>,
952 popover_bounds: Option<Bounds<Pixels>>,
953 popover_state: InlineBlamePopoverState,
954}
955
956enum SelectionDragState {
957 /// State when no drag related activity is detected.
958 None,
959 /// State when the mouse is down on a selection that is about to be dragged.
960 ReadyToDrag {
961 selection: Selection<Anchor>,
962 click_position: gpui::Point<Pixels>,
963 mouse_down_time: Instant,
964 },
965 /// State when the mouse is dragging the selection in the editor.
966 Dragging {
967 selection: Selection<Anchor>,
968 drop_cursor: Selection<Anchor>,
969 hide_drop_cursor: bool,
970 },
971}
972
973enum ColumnarSelectionState {
974 FromMouse {
975 selection_tail: Anchor,
976 display_point: Option<DisplayPoint>,
977 },
978 FromSelection {
979 selection_tail: Anchor,
980 },
981}
982
983/// Represents a breakpoint indicator that shows up when hovering over lines in the gutter that don't have
984/// a breakpoint on them.
985#[derive(Clone, Copy, Debug, PartialEq, Eq)]
986struct PhantomBreakpointIndicator {
987 display_row: DisplayRow,
988 /// There's a small debounce between hovering over the line and showing the indicator.
989 /// We don't want to show the indicator when moving the mouse from editor to e.g. project panel.
990 is_active: bool,
991 collides_with_existing_breakpoint: bool,
992}
993
994/// Zed's primary implementation of text input, allowing users to edit a [`MultiBuffer`].
995///
996/// See the [module level documentation](self) for more information.
997pub struct Editor {
998 focus_handle: FocusHandle,
999 last_focused_descendant: Option<WeakFocusHandle>,
1000 /// The text buffer being edited
1001 buffer: Entity<MultiBuffer>,
1002 /// Map of how text in the buffer should be displayed.
1003 /// Handles soft wraps, folds, fake inlay text insertions, etc.
1004 pub display_map: Entity<DisplayMap>,
1005 pub selections: SelectionsCollection,
1006 pub scroll_manager: ScrollManager,
1007 /// When inline assist editors are linked, they all render cursors because
1008 /// typing enters text into each of them, even the ones that aren't focused.
1009 pub(crate) show_cursor_when_unfocused: bool,
1010 columnar_selection_state: Option<ColumnarSelectionState>,
1011 add_selections_state: Option<AddSelectionsState>,
1012 select_next_state: Option<SelectNextState>,
1013 select_prev_state: Option<SelectNextState>,
1014 selection_history: SelectionHistory,
1015 defer_selection_effects: bool,
1016 deferred_selection_effects_state: Option<DeferredSelectionEffectsState>,
1017 autoclose_regions: Vec<AutocloseRegion>,
1018 snippet_stack: InvalidationStack<SnippetState>,
1019 select_syntax_node_history: SelectSyntaxNodeHistory,
1020 ime_transaction: Option<TransactionId>,
1021 pub diagnostics_max_severity: DiagnosticSeverity,
1022 active_diagnostics: ActiveDiagnostic,
1023 show_inline_diagnostics: bool,
1024 inline_diagnostics_update: Task<()>,
1025 inline_diagnostics_enabled: bool,
1026 diagnostics_enabled: bool,
1027 inline_diagnostics: Vec<(Anchor, InlineDiagnostic)>,
1028 soft_wrap_mode_override: Option<language_settings::SoftWrap>,
1029 hard_wrap: Option<usize>,
1030
1031 // TODO: make this a access method
1032 pub project: Option<Entity<Project>>,
1033 semantics_provider: Option<Rc<dyn SemanticsProvider>>,
1034 completion_provider: Option<Rc<dyn CompletionProvider>>,
1035 collaboration_hub: Option<Box<dyn CollaborationHub>>,
1036 blink_manager: Entity<BlinkManager>,
1037 show_cursor_names: bool,
1038 hovered_cursors: HashMap<HoveredCursor, Task<()>>,
1039 pub show_local_selections: bool,
1040 mode: EditorMode,
1041 show_breadcrumbs: bool,
1042 show_gutter: bool,
1043 show_scrollbars: ScrollbarAxes,
1044 minimap_visibility: MinimapVisibility,
1045 offset_content: bool,
1046 disable_expand_excerpt_buttons: bool,
1047 show_line_numbers: Option<bool>,
1048 use_relative_line_numbers: Option<bool>,
1049 show_git_diff_gutter: Option<bool>,
1050 show_code_actions: Option<bool>,
1051 show_runnables: Option<bool>,
1052 show_breakpoints: Option<bool>,
1053 show_wrap_guides: Option<bool>,
1054 show_indent_guides: Option<bool>,
1055 placeholder_text: Option<Arc<str>>,
1056 highlight_order: usize,
1057 highlighted_rows: HashMap<TypeId, Vec<RowHighlight>>,
1058 background_highlights: TreeMap<HighlightKey, BackgroundHighlight>,
1059 gutter_highlights: TreeMap<TypeId, GutterHighlight>,
1060 scrollbar_marker_state: ScrollbarMarkerState,
1061 active_indent_guides_state: ActiveIndentGuidesState,
1062 nav_history: Option<ItemNavHistory>,
1063 context_menu: RefCell<Option<CodeContextMenu>>,
1064 context_menu_options: Option<ContextMenuOptions>,
1065 mouse_context_menu: Option<MouseContextMenu>,
1066 completion_tasks: Vec<(CompletionId, Task<()>)>,
1067 inline_blame_popover: Option<InlineBlamePopover>,
1068 inline_blame_popover_show_task: Option<Task<()>>,
1069 signature_help_state: SignatureHelpState,
1070 auto_signature_help: Option<bool>,
1071 find_all_references_task_sources: Vec<Anchor>,
1072 next_completion_id: CompletionId,
1073 available_code_actions: Option<(Location, Rc<[AvailableCodeAction]>)>,
1074 code_actions_task: Option<Task<Result<()>>>,
1075 quick_selection_highlight_task: Option<(Range<Anchor>, Task<()>)>,
1076 debounced_selection_highlight_task: Option<(Range<Anchor>, Task<()>)>,
1077 document_highlights_task: Option<Task<()>>,
1078 linked_editing_range_task: Option<Task<Option<()>>>,
1079 linked_edit_ranges: linked_editing_ranges::LinkedEditingRanges,
1080 pending_rename: Option<RenameState>,
1081 searchable: bool,
1082 cursor_shape: CursorShape,
1083 current_line_highlight: Option<CurrentLineHighlight>,
1084 collapse_matches: bool,
1085 autoindent_mode: Option<AutoindentMode>,
1086 workspace: Option<(WeakEntity<Workspace>, Option<WorkspaceId>)>,
1087 input_enabled: bool,
1088 use_modal_editing: bool,
1089 read_only: bool,
1090 leader_id: Option<CollaboratorId>,
1091 remote_id: Option<ViewId>,
1092 pub hover_state: HoverState,
1093 pending_mouse_down: Option<Rc<RefCell<Option<MouseDownEvent>>>>,
1094 gutter_hovered: bool,
1095 hovered_link_state: Option<HoveredLinkState>,
1096 edit_prediction_provider: Option<RegisteredInlineCompletionProvider>,
1097 code_action_providers: Vec<Rc<dyn CodeActionProvider>>,
1098 active_inline_completion: Option<InlineCompletionState>,
1099 /// Used to prevent flickering as the user types while the menu is open
1100 stale_inline_completion_in_menu: Option<InlineCompletionState>,
1101 edit_prediction_settings: EditPredictionSettings,
1102 inline_completions_hidden_for_vim_mode: bool,
1103 show_inline_completions_override: Option<bool>,
1104 menu_inline_completions_policy: MenuInlineCompletionsPolicy,
1105 edit_prediction_preview: EditPredictionPreview,
1106 edit_prediction_indent_conflict: bool,
1107 edit_prediction_requires_modifier_in_indent_conflict: bool,
1108 inlay_hint_cache: InlayHintCache,
1109 next_inlay_id: usize,
1110 _subscriptions: Vec<Subscription>,
1111 pixel_position_of_newest_cursor: Option<gpui::Point<Pixels>>,
1112 gutter_dimensions: GutterDimensions,
1113 style: Option<EditorStyle>,
1114 text_style_refinement: Option<TextStyleRefinement>,
1115 next_editor_action_id: EditorActionId,
1116 editor_actions: Rc<
1117 RefCell<BTreeMap<EditorActionId, Box<dyn Fn(&Editor, &mut Window, &mut Context<Self>)>>>,
1118 >,
1119 use_autoclose: bool,
1120 use_auto_surround: bool,
1121 auto_replace_emoji_shortcode: bool,
1122 jsx_tag_auto_close_enabled_in_any_buffer: bool,
1123 show_git_blame_gutter: bool,
1124 show_git_blame_inline: bool,
1125 show_git_blame_inline_delay_task: Option<Task<()>>,
1126 git_blame_inline_enabled: bool,
1127 render_diff_hunk_controls: RenderDiffHunkControlsFn,
1128 serialize_dirty_buffers: bool,
1129 show_selection_menu: Option<bool>,
1130 blame: Option<Entity<GitBlame>>,
1131 blame_subscription: Option<Subscription>,
1132 custom_context_menu: Option<
1133 Box<
1134 dyn 'static
1135 + Fn(
1136 &mut Self,
1137 DisplayPoint,
1138 &mut Window,
1139 &mut Context<Self>,
1140 ) -> Option<Entity<ui::ContextMenu>>,
1141 >,
1142 >,
1143 last_bounds: Option<Bounds<Pixels>>,
1144 last_position_map: Option<Rc<PositionMap>>,
1145 expect_bounds_change: Option<Bounds<Pixels>>,
1146 tasks: BTreeMap<(BufferId, BufferRow), RunnableTasks>,
1147 tasks_update_task: Option<Task<()>>,
1148 breakpoint_store: Option<Entity<BreakpointStore>>,
1149 gutter_breakpoint_indicator: (Option<PhantomBreakpointIndicator>, Option<Task<()>>),
1150 hovered_diff_hunk_row: Option<DisplayRow>,
1151 pull_diagnostics_task: Task<()>,
1152 in_project_search: bool,
1153 previous_search_ranges: Option<Arc<[Range<Anchor>]>>,
1154 breadcrumb_header: Option<String>,
1155 focused_block: Option<FocusedBlock>,
1156 next_scroll_position: NextScrollCursorCenterTopBottom,
1157 addons: HashMap<TypeId, Box<dyn Addon>>,
1158 registered_buffers: HashMap<BufferId, OpenLspBufferHandle>,
1159 load_diff_task: Option<Shared<Task<()>>>,
1160 /// Whether we are temporarily displaying a diff other than git's
1161 temporary_diff_override: bool,
1162 selection_mark_mode: bool,
1163 toggle_fold_multiple_buffers: Task<()>,
1164 _scroll_cursor_center_top_bottom_task: Task<()>,
1165 serialize_selections: Task<()>,
1166 serialize_folds: Task<()>,
1167 mouse_cursor_hidden: bool,
1168 minimap: Option<Entity<Self>>,
1169 hide_mouse_mode: HideMouseMode,
1170 pub change_list: ChangeList,
1171 inline_value_cache: InlineValueCache,
1172 selection_drag_state: SelectionDragState,
1173 next_color_inlay_id: usize,
1174 colors: Option<LspColorData>,
1175 folding_newlines: Task<()>,
1176}
1177
1178#[derive(Copy, Clone, Debug, PartialEq, Eq, Default)]
1179enum NextScrollCursorCenterTopBottom {
1180 #[default]
1181 Center,
1182 Top,
1183 Bottom,
1184}
1185
1186impl NextScrollCursorCenterTopBottom {
1187 fn next(&self) -> Self {
1188 match self {
1189 Self::Center => Self::Top,
1190 Self::Top => Self::Bottom,
1191 Self::Bottom => Self::Center,
1192 }
1193 }
1194}
1195
1196#[derive(Clone)]
1197pub struct EditorSnapshot {
1198 pub mode: EditorMode,
1199 show_gutter: bool,
1200 show_line_numbers: Option<bool>,
1201 show_git_diff_gutter: Option<bool>,
1202 show_code_actions: Option<bool>,
1203 show_runnables: Option<bool>,
1204 show_breakpoints: Option<bool>,
1205 git_blame_gutter_max_author_length: Option<usize>,
1206 pub display_snapshot: DisplaySnapshot,
1207 pub placeholder_text: Option<Arc<str>>,
1208 is_focused: bool,
1209 scroll_anchor: ScrollAnchor,
1210 ongoing_scroll: OngoingScroll,
1211 current_line_highlight: CurrentLineHighlight,
1212 gutter_hovered: bool,
1213}
1214
1215#[derive(Default, Debug, Clone, Copy)]
1216pub struct GutterDimensions {
1217 pub left_padding: Pixels,
1218 pub right_padding: Pixels,
1219 pub width: Pixels,
1220 pub margin: Pixels,
1221 pub git_blame_entries_width: Option<Pixels>,
1222}
1223
1224impl GutterDimensions {
1225 fn default_with_margin(font_id: FontId, font_size: Pixels, cx: &App) -> Self {
1226 Self {
1227 margin: Self::default_gutter_margin(font_id, font_size, cx),
1228 ..Default::default()
1229 }
1230 }
1231
1232 fn default_gutter_margin(font_id: FontId, font_size: Pixels, cx: &App) -> Pixels {
1233 -cx.text_system().descent(font_id, font_size)
1234 }
1235 /// The full width of the space taken up by the gutter.
1236 pub fn full_width(&self) -> Pixels {
1237 self.margin + self.width
1238 }
1239
1240 /// The width of the space reserved for the fold indicators,
1241 /// use alongside 'justify_end' and `gutter_width` to
1242 /// right align content with the line numbers
1243 pub fn fold_area_width(&self) -> Pixels {
1244 self.margin + self.right_padding
1245 }
1246}
1247
1248struct CharacterDimensions {
1249 em_width: Pixels,
1250 em_advance: Pixels,
1251 line_height: Pixels,
1252}
1253
1254#[derive(Debug)]
1255pub struct RemoteSelection {
1256 pub replica_id: ReplicaId,
1257 pub selection: Selection<Anchor>,
1258 pub cursor_shape: CursorShape,
1259 pub collaborator_id: CollaboratorId,
1260 pub line_mode: bool,
1261 pub user_name: Option<SharedString>,
1262 pub color: PlayerColor,
1263}
1264
1265#[derive(Clone, Debug)]
1266struct SelectionHistoryEntry {
1267 selections: Arc<[Selection<Anchor>]>,
1268 select_next_state: Option<SelectNextState>,
1269 select_prev_state: Option<SelectNextState>,
1270 add_selections_state: Option<AddSelectionsState>,
1271}
1272
1273#[derive(Copy, Clone, Debug, PartialEq, Eq)]
1274enum SelectionHistoryMode {
1275 Normal,
1276 Undoing,
1277 Redoing,
1278 Skipping,
1279}
1280
1281#[derive(Clone, PartialEq, Eq, Hash)]
1282struct HoveredCursor {
1283 replica_id: u16,
1284 selection_id: usize,
1285}
1286
1287impl Default for SelectionHistoryMode {
1288 fn default() -> Self {
1289 Self::Normal
1290 }
1291}
1292
1293#[derive(Debug)]
1294/// SelectionEffects controls the side-effects of updating the selection.
1295///
1296/// The default behaviour does "what you mostly want":
1297/// - it pushes to the nav history if the cursor moved by >10 lines
1298/// - it re-triggers completion requests
1299/// - it scrolls to fit
1300///
1301/// You might want to modify these behaviours. For example when doing a "jump"
1302/// like go to definition, we always want to add to nav history; but when scrolling
1303/// in vim mode we never do.
1304///
1305/// Similarly, you might want to disable scrolling if you don't want the viewport to
1306/// move.
1307pub struct SelectionEffects {
1308 nav_history: Option<bool>,
1309 completions: bool,
1310 scroll: Option<Autoscroll>,
1311}
1312
1313impl Default for SelectionEffects {
1314 fn default() -> Self {
1315 Self {
1316 nav_history: None,
1317 completions: true,
1318 scroll: Some(Autoscroll::fit()),
1319 }
1320 }
1321}
1322impl SelectionEffects {
1323 pub fn scroll(scroll: Autoscroll) -> Self {
1324 Self {
1325 scroll: Some(scroll),
1326 ..Default::default()
1327 }
1328 }
1329
1330 pub fn no_scroll() -> Self {
1331 Self {
1332 scroll: None,
1333 ..Default::default()
1334 }
1335 }
1336
1337 pub fn completions(self, completions: bool) -> Self {
1338 Self {
1339 completions,
1340 ..self
1341 }
1342 }
1343
1344 pub fn nav_history(self, nav_history: bool) -> Self {
1345 Self {
1346 nav_history: Some(nav_history),
1347 ..self
1348 }
1349 }
1350}
1351
1352struct DeferredSelectionEffectsState {
1353 changed: bool,
1354 effects: SelectionEffects,
1355 old_cursor_position: Anchor,
1356 history_entry: SelectionHistoryEntry,
1357}
1358
1359#[derive(Default)]
1360struct SelectionHistory {
1361 #[allow(clippy::type_complexity)]
1362 selections_by_transaction:
1363 HashMap<TransactionId, (Arc<[Selection<Anchor>]>, Option<Arc<[Selection<Anchor>]>>)>,
1364 mode: SelectionHistoryMode,
1365 undo_stack: VecDeque<SelectionHistoryEntry>,
1366 redo_stack: VecDeque<SelectionHistoryEntry>,
1367}
1368
1369impl SelectionHistory {
1370 #[track_caller]
1371 fn insert_transaction(
1372 &mut self,
1373 transaction_id: TransactionId,
1374 selections: Arc<[Selection<Anchor>]>,
1375 ) {
1376 if selections.is_empty() {
1377 log::error!(
1378 "SelectionHistory::insert_transaction called with empty selections. Caller: {}",
1379 std::panic::Location::caller()
1380 );
1381 return;
1382 }
1383 self.selections_by_transaction
1384 .insert(transaction_id, (selections, None));
1385 }
1386
1387 #[allow(clippy::type_complexity)]
1388 fn transaction(
1389 &self,
1390 transaction_id: TransactionId,
1391 ) -> Option<&(Arc<[Selection<Anchor>]>, Option<Arc<[Selection<Anchor>]>>)> {
1392 self.selections_by_transaction.get(&transaction_id)
1393 }
1394
1395 #[allow(clippy::type_complexity)]
1396 fn transaction_mut(
1397 &mut self,
1398 transaction_id: TransactionId,
1399 ) -> Option<&mut (Arc<[Selection<Anchor>]>, Option<Arc<[Selection<Anchor>]>>)> {
1400 self.selections_by_transaction.get_mut(&transaction_id)
1401 }
1402
1403 fn push(&mut self, entry: SelectionHistoryEntry) {
1404 if !entry.selections.is_empty() {
1405 match self.mode {
1406 SelectionHistoryMode::Normal => {
1407 self.push_undo(entry);
1408 self.redo_stack.clear();
1409 }
1410 SelectionHistoryMode::Undoing => self.push_redo(entry),
1411 SelectionHistoryMode::Redoing => self.push_undo(entry),
1412 SelectionHistoryMode::Skipping => {}
1413 }
1414 }
1415 }
1416
1417 fn push_undo(&mut self, entry: SelectionHistoryEntry) {
1418 if self
1419 .undo_stack
1420 .back()
1421 .map_or(true, |e| e.selections != entry.selections)
1422 {
1423 self.undo_stack.push_back(entry);
1424 if self.undo_stack.len() > MAX_SELECTION_HISTORY_LEN {
1425 self.undo_stack.pop_front();
1426 }
1427 }
1428 }
1429
1430 fn push_redo(&mut self, entry: SelectionHistoryEntry) {
1431 if self
1432 .redo_stack
1433 .back()
1434 .map_or(true, |e| e.selections != entry.selections)
1435 {
1436 self.redo_stack.push_back(entry);
1437 if self.redo_stack.len() > MAX_SELECTION_HISTORY_LEN {
1438 self.redo_stack.pop_front();
1439 }
1440 }
1441 }
1442}
1443
1444#[derive(Clone, Copy)]
1445pub struct RowHighlightOptions {
1446 pub autoscroll: bool,
1447 pub include_gutter: bool,
1448}
1449
1450impl Default for RowHighlightOptions {
1451 fn default() -> Self {
1452 Self {
1453 autoscroll: Default::default(),
1454 include_gutter: true,
1455 }
1456 }
1457}
1458
1459struct RowHighlight {
1460 index: usize,
1461 range: Range<Anchor>,
1462 color: Hsla,
1463 options: RowHighlightOptions,
1464 type_id: TypeId,
1465}
1466
1467#[derive(Clone, Debug)]
1468struct AddSelectionsState {
1469 groups: Vec<AddSelectionsGroup>,
1470}
1471
1472#[derive(Clone, Debug)]
1473struct AddSelectionsGroup {
1474 above: bool,
1475 stack: Vec<usize>,
1476}
1477
1478#[derive(Clone)]
1479struct SelectNextState {
1480 query: AhoCorasick,
1481 wordwise: bool,
1482 done: bool,
1483}
1484
1485impl std::fmt::Debug for SelectNextState {
1486 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
1487 f.debug_struct(std::any::type_name::<Self>())
1488 .field("wordwise", &self.wordwise)
1489 .field("done", &self.done)
1490 .finish()
1491 }
1492}
1493
1494#[derive(Debug)]
1495struct AutocloseRegion {
1496 selection_id: usize,
1497 range: Range<Anchor>,
1498 pair: BracketPair,
1499}
1500
1501#[derive(Debug)]
1502struct SnippetState {
1503 ranges: Vec<Vec<Range<Anchor>>>,
1504 active_index: usize,
1505 choices: Vec<Option<Vec<String>>>,
1506}
1507
1508#[doc(hidden)]
1509pub struct RenameState {
1510 pub range: Range<Anchor>,
1511 pub old_name: Arc<str>,
1512 pub editor: Entity<Editor>,
1513 block_id: CustomBlockId,
1514}
1515
1516struct InvalidationStack<T>(Vec<T>);
1517
1518struct RegisteredInlineCompletionProvider {
1519 provider: Arc<dyn InlineCompletionProviderHandle>,
1520 _subscription: Subscription,
1521}
1522
1523#[derive(Debug, PartialEq, Eq)]
1524pub struct ActiveDiagnosticGroup {
1525 pub active_range: Range<Anchor>,
1526 pub active_message: String,
1527 pub group_id: usize,
1528 pub blocks: HashSet<CustomBlockId>,
1529}
1530
1531#[derive(Debug, PartialEq, Eq)]
1532
1533pub(crate) enum ActiveDiagnostic {
1534 None,
1535 All,
1536 Group(ActiveDiagnosticGroup),
1537}
1538
1539#[derive(Serialize, Deserialize, Clone, Debug)]
1540pub struct ClipboardSelection {
1541 /// The number of bytes in this selection.
1542 pub len: usize,
1543 /// Whether this was a full-line selection.
1544 pub is_entire_line: bool,
1545 /// The indentation of the first line when this content was originally copied.
1546 pub first_line_indent: u32,
1547}
1548
1549// selections, scroll behavior, was newest selection reversed
1550type SelectSyntaxNodeHistoryState = (
1551 Box<[Selection<usize>]>,
1552 SelectSyntaxNodeScrollBehavior,
1553 bool,
1554);
1555
1556#[derive(Default)]
1557struct SelectSyntaxNodeHistory {
1558 stack: Vec<SelectSyntaxNodeHistoryState>,
1559 // disable temporarily to allow changing selections without losing the stack
1560 pub disable_clearing: bool,
1561}
1562
1563impl SelectSyntaxNodeHistory {
1564 pub fn try_clear(&mut self) {
1565 if !self.disable_clearing {
1566 self.stack.clear();
1567 }
1568 }
1569
1570 pub fn push(&mut self, selection: SelectSyntaxNodeHistoryState) {
1571 self.stack.push(selection);
1572 }
1573
1574 pub fn pop(&mut self) -> Option<SelectSyntaxNodeHistoryState> {
1575 self.stack.pop()
1576 }
1577}
1578
1579enum SelectSyntaxNodeScrollBehavior {
1580 CursorTop,
1581 FitSelection,
1582 CursorBottom,
1583}
1584
1585#[derive(Debug)]
1586pub(crate) struct NavigationData {
1587 cursor_anchor: Anchor,
1588 cursor_position: Point,
1589 scroll_anchor: ScrollAnchor,
1590 scroll_top_row: u32,
1591}
1592
1593#[derive(Debug, Clone, Copy, PartialEq, Eq)]
1594pub enum GotoDefinitionKind {
1595 Symbol,
1596 Declaration,
1597 Type,
1598 Implementation,
1599}
1600
1601#[derive(Debug, Clone)]
1602enum InlayHintRefreshReason {
1603 ModifiersChanged(bool),
1604 Toggle(bool),
1605 SettingsChange(InlayHintSettings),
1606 NewLinesShown,
1607 BufferEdited(HashSet<Arc<Language>>),
1608 RefreshRequested,
1609 ExcerptsRemoved(Vec<ExcerptId>),
1610}
1611
1612impl InlayHintRefreshReason {
1613 fn description(&self) -> &'static str {
1614 match self {
1615 Self::ModifiersChanged(_) => "modifiers changed",
1616 Self::Toggle(_) => "toggle",
1617 Self::SettingsChange(_) => "settings change",
1618 Self::NewLinesShown => "new lines shown",
1619 Self::BufferEdited(_) => "buffer edited",
1620 Self::RefreshRequested => "refresh requested",
1621 Self::ExcerptsRemoved(_) => "excerpts removed",
1622 }
1623 }
1624}
1625
1626pub enum FormatTarget {
1627 Buffers(HashSet<Entity<Buffer>>),
1628 Ranges(Vec<Range<MultiBufferPoint>>),
1629}
1630
1631pub(crate) struct FocusedBlock {
1632 id: BlockId,
1633 focus_handle: WeakFocusHandle,
1634}
1635
1636#[derive(Clone)]
1637enum JumpData {
1638 MultiBufferRow {
1639 row: MultiBufferRow,
1640 line_offset_from_top: u32,
1641 },
1642 MultiBufferPoint {
1643 excerpt_id: ExcerptId,
1644 position: Point,
1645 anchor: text::Anchor,
1646 line_offset_from_top: u32,
1647 },
1648}
1649
1650pub enum MultibufferSelectionMode {
1651 First,
1652 All,
1653}
1654
1655#[derive(Clone, Copy, Debug, Default)]
1656pub struct RewrapOptions {
1657 pub override_language_settings: bool,
1658 pub preserve_existing_whitespace: bool,
1659}
1660
1661impl Editor {
1662 pub fn single_line(window: &mut Window, cx: &mut Context<Self>) -> Self {
1663 let buffer = cx.new(|cx| Buffer::local("", cx));
1664 let buffer = cx.new(|cx| MultiBuffer::singleton(buffer, cx));
1665 Self::new(
1666 EditorMode::SingleLine { auto_width: false },
1667 buffer,
1668 None,
1669 window,
1670 cx,
1671 )
1672 }
1673
1674 pub fn multi_line(window: &mut Window, cx: &mut Context<Self>) -> Self {
1675 let buffer = cx.new(|cx| Buffer::local("", cx));
1676 let buffer = cx.new(|cx| MultiBuffer::singleton(buffer, cx));
1677 Self::new(EditorMode::full(), buffer, None, window, cx)
1678 }
1679
1680 pub fn auto_width(window: &mut Window, cx: &mut Context<Self>) -> Self {
1681 let buffer = cx.new(|cx| Buffer::local("", cx));
1682 let buffer = cx.new(|cx| MultiBuffer::singleton(buffer, cx));
1683 Self::new(
1684 EditorMode::SingleLine { auto_width: true },
1685 buffer,
1686 None,
1687 window,
1688 cx,
1689 )
1690 }
1691
1692 pub fn auto_height(
1693 min_lines: usize,
1694 max_lines: usize,
1695 window: &mut Window,
1696 cx: &mut Context<Self>,
1697 ) -> Self {
1698 let buffer = cx.new(|cx| Buffer::local("", cx));
1699 let buffer = cx.new(|cx| MultiBuffer::singleton(buffer, cx));
1700 Self::new(
1701 EditorMode::AutoHeight {
1702 min_lines,
1703 max_lines: Some(max_lines),
1704 },
1705 buffer,
1706 None,
1707 window,
1708 cx,
1709 )
1710 }
1711
1712 /// Creates a new auto-height editor with a minimum number of lines but no maximum.
1713 /// The editor grows as tall as needed to fit its content.
1714 pub fn auto_height_unbounded(
1715 min_lines: usize,
1716 window: &mut Window,
1717 cx: &mut Context<Self>,
1718 ) -> Self {
1719 let buffer = cx.new(|cx| Buffer::local("", cx));
1720 let buffer = cx.new(|cx| MultiBuffer::singleton(buffer, cx));
1721 Self::new(
1722 EditorMode::AutoHeight {
1723 min_lines,
1724 max_lines: None,
1725 },
1726 buffer,
1727 None,
1728 window,
1729 cx,
1730 )
1731 }
1732
1733 pub fn for_buffer(
1734 buffer: Entity<Buffer>,
1735 project: Option<Entity<Project>>,
1736 window: &mut Window,
1737 cx: &mut Context<Self>,
1738 ) -> Self {
1739 let buffer = cx.new(|cx| MultiBuffer::singleton(buffer, cx));
1740 Self::new(EditorMode::full(), buffer, project, window, cx)
1741 }
1742
1743 pub fn for_multibuffer(
1744 buffer: Entity<MultiBuffer>,
1745 project: Option<Entity<Project>>,
1746 window: &mut Window,
1747 cx: &mut Context<Self>,
1748 ) -> Self {
1749 Self::new(EditorMode::full(), buffer, project, window, cx)
1750 }
1751
1752 pub fn clone(&self, window: &mut Window, cx: &mut Context<Self>) -> Self {
1753 let mut clone = Self::new(
1754 self.mode.clone(),
1755 self.buffer.clone(),
1756 self.project.clone(),
1757 window,
1758 cx,
1759 );
1760 self.display_map.update(cx, |display_map, cx| {
1761 let snapshot = display_map.snapshot(cx);
1762 clone.display_map.update(cx, |display_map, cx| {
1763 display_map.set_state(&snapshot, cx);
1764 });
1765 });
1766 clone.folds_did_change(cx);
1767 clone.selections.clone_state(&self.selections);
1768 clone.scroll_manager.clone_state(&self.scroll_manager);
1769 clone.searchable = self.searchable;
1770 clone.read_only = self.read_only;
1771 clone
1772 }
1773
1774 pub fn new(
1775 mode: EditorMode,
1776 buffer: Entity<MultiBuffer>,
1777 project: Option<Entity<Project>>,
1778 window: &mut Window,
1779 cx: &mut Context<Self>,
1780 ) -> Self {
1781 Editor::new_internal(mode, buffer, project, None, window, cx)
1782 }
1783
1784 fn new_internal(
1785 mode: EditorMode,
1786 buffer: Entity<MultiBuffer>,
1787 project: Option<Entity<Project>>,
1788 display_map: Option<Entity<DisplayMap>>,
1789 window: &mut Window,
1790 cx: &mut Context<Self>,
1791 ) -> Self {
1792 debug_assert!(
1793 display_map.is_none() || mode.is_minimap(),
1794 "Providing a display map for a new editor is only intended for the minimap and might have unindended side effects otherwise!"
1795 );
1796
1797 let full_mode = mode.is_full();
1798 let diagnostics_max_severity = if full_mode {
1799 EditorSettings::get_global(cx)
1800 .diagnostics_max_severity
1801 .unwrap_or(DiagnosticSeverity::Hint)
1802 } else {
1803 DiagnosticSeverity::Off
1804 };
1805 let style = window.text_style();
1806 let font_size = style.font_size.to_pixels(window.rem_size());
1807 let editor = cx.entity().downgrade();
1808 let fold_placeholder = FoldPlaceholder {
1809 constrain_width: true,
1810 render: Arc::new(move |fold_id, fold_range, cx| {
1811 let editor = editor.clone();
1812 div()
1813 .id(fold_id)
1814 .bg(cx.theme().colors().ghost_element_background)
1815 .hover(|style| style.bg(cx.theme().colors().ghost_element_hover))
1816 .active(|style| style.bg(cx.theme().colors().ghost_element_active))
1817 .rounded_xs()
1818 .size_full()
1819 .cursor_pointer()
1820 .child("⋯")
1821 .on_mouse_down(MouseButton::Left, |_, _, cx| cx.stop_propagation())
1822 .on_click(move |_, _window, cx| {
1823 editor
1824 .update(cx, |editor, cx| {
1825 editor.unfold_ranges(
1826 &[fold_range.start..fold_range.end],
1827 true,
1828 false,
1829 cx,
1830 );
1831 cx.stop_propagation();
1832 })
1833 .ok();
1834 })
1835 .into_any()
1836 }),
1837 merge_adjacent: true,
1838 ..FoldPlaceholder::default()
1839 };
1840 let display_map = display_map.unwrap_or_else(|| {
1841 cx.new(|cx| {
1842 DisplayMap::new(
1843 buffer.clone(),
1844 style.font(),
1845 font_size,
1846 None,
1847 FILE_HEADER_HEIGHT,
1848 MULTI_BUFFER_EXCERPT_HEADER_HEIGHT,
1849 fold_placeholder,
1850 diagnostics_max_severity,
1851 cx,
1852 )
1853 })
1854 });
1855
1856 let selections = SelectionsCollection::new(display_map.clone(), buffer.clone());
1857
1858 let blink_manager = cx.new(|cx| BlinkManager::new(CURSOR_BLINK_INTERVAL, cx));
1859
1860 let soft_wrap_mode_override = matches!(mode, EditorMode::SingleLine { .. })
1861 .then(|| language_settings::SoftWrap::None);
1862
1863 let mut project_subscriptions = Vec::new();
1864 if mode.is_full() {
1865 if let Some(project) = project.as_ref() {
1866 project_subscriptions.push(cx.subscribe_in(
1867 project,
1868 window,
1869 |editor, _, event, window, cx| match event {
1870 project::Event::RefreshCodeLens => {
1871 // we always query lens with actions, without storing them, always refreshing them
1872 }
1873 project::Event::RefreshInlayHints => {
1874 editor
1875 .refresh_inlay_hints(InlayHintRefreshReason::RefreshRequested, cx);
1876 }
1877 project::Event::LanguageServerAdded(..)
1878 | project::Event::LanguageServerRemoved(..) => {
1879 if editor.tasks_update_task.is_none() {
1880 editor.tasks_update_task =
1881 Some(editor.refresh_runnables(window, cx));
1882 }
1883 editor.update_lsp_data(true, None, window, cx);
1884 }
1885 project::Event::SnippetEdit(id, snippet_edits) => {
1886 if let Some(buffer) = editor.buffer.read(cx).buffer(*id) {
1887 let focus_handle = editor.focus_handle(cx);
1888 if focus_handle.is_focused(window) {
1889 let snapshot = buffer.read(cx).snapshot();
1890 for (range, snippet) in snippet_edits {
1891 let editor_range =
1892 language::range_from_lsp(*range).to_offset(&snapshot);
1893 editor
1894 .insert_snippet(
1895 &[editor_range],
1896 snippet.clone(),
1897 window,
1898 cx,
1899 )
1900 .ok();
1901 }
1902 }
1903 }
1904 }
1905 _ => {}
1906 },
1907 ));
1908 if let Some(task_inventory) = project
1909 .read(cx)
1910 .task_store()
1911 .read(cx)
1912 .task_inventory()
1913 .cloned()
1914 {
1915 project_subscriptions.push(cx.observe_in(
1916 &task_inventory,
1917 window,
1918 |editor, _, window, cx| {
1919 editor.tasks_update_task = Some(editor.refresh_runnables(window, cx));
1920 },
1921 ));
1922 };
1923
1924 project_subscriptions.push(cx.subscribe_in(
1925 &project.read(cx).breakpoint_store(),
1926 window,
1927 |editor, _, event, window, cx| match event {
1928 BreakpointStoreEvent::ClearDebugLines => {
1929 editor.clear_row_highlights::<ActiveDebugLine>();
1930 editor.refresh_inline_values(cx);
1931 }
1932 BreakpointStoreEvent::SetDebugLine => {
1933 if editor.go_to_active_debug_line(window, cx) {
1934 cx.stop_propagation();
1935 }
1936
1937 editor.refresh_inline_values(cx);
1938 }
1939 _ => {}
1940 },
1941 ));
1942 let git_store = project.read(cx).git_store().clone();
1943 let project = project.clone();
1944 project_subscriptions.push(cx.subscribe(&git_store, move |this, _, event, cx| {
1945 match event {
1946 GitStoreEvent::RepositoryUpdated(
1947 _,
1948 RepositoryEvent::Updated {
1949 new_instance: true, ..
1950 },
1951 _,
1952 ) => {
1953 this.load_diff_task = Some(
1954 update_uncommitted_diff_for_buffer(
1955 cx.entity(),
1956 &project,
1957 this.buffer.read(cx).all_buffers(),
1958 this.buffer.clone(),
1959 cx,
1960 )
1961 .shared(),
1962 );
1963 }
1964 _ => {}
1965 }
1966 }));
1967 }
1968 }
1969
1970 let buffer_snapshot = buffer.read(cx).snapshot(cx);
1971
1972 let inlay_hint_settings =
1973 inlay_hint_settings(selections.newest_anchor().head(), &buffer_snapshot, cx);
1974 let focus_handle = cx.focus_handle();
1975 cx.on_focus(&focus_handle, window, Self::handle_focus)
1976 .detach();
1977 cx.on_focus_in(&focus_handle, window, Self::handle_focus_in)
1978 .detach();
1979 cx.on_focus_out(&focus_handle, window, Self::handle_focus_out)
1980 .detach();
1981 cx.on_blur(&focus_handle, window, Self::handle_blur)
1982 .detach();
1983 cx.observe_pending_input(window, Self::observe_pending_input)
1984 .detach();
1985
1986 let show_indent_guides = if matches!(mode, EditorMode::SingleLine { .. }) {
1987 Some(false)
1988 } else {
1989 None
1990 };
1991
1992 let breakpoint_store = match (&mode, project.as_ref()) {
1993 (EditorMode::Full { .. }, Some(project)) => Some(project.read(cx).breakpoint_store()),
1994 _ => None,
1995 };
1996
1997 let mut code_action_providers = Vec::new();
1998 let mut load_uncommitted_diff = None;
1999 if let Some(project) = project.clone() {
2000 load_uncommitted_diff = Some(
2001 update_uncommitted_diff_for_buffer(
2002 cx.entity(),
2003 &project,
2004 buffer.read(cx).all_buffers(),
2005 buffer.clone(),
2006 cx,
2007 )
2008 .shared(),
2009 );
2010 code_action_providers.push(Rc::new(project) as Rc<_>);
2011 }
2012
2013 let mut editor = Self {
2014 focus_handle,
2015 show_cursor_when_unfocused: false,
2016 last_focused_descendant: None,
2017 buffer: buffer.clone(),
2018 display_map: display_map.clone(),
2019 selections,
2020 scroll_manager: ScrollManager::new(cx),
2021 columnar_selection_state: None,
2022 add_selections_state: None,
2023 select_next_state: None,
2024 select_prev_state: None,
2025 selection_history: SelectionHistory::default(),
2026 defer_selection_effects: false,
2027 deferred_selection_effects_state: None,
2028 autoclose_regions: Vec::new(),
2029 snippet_stack: InvalidationStack::default(),
2030 select_syntax_node_history: SelectSyntaxNodeHistory::default(),
2031 ime_transaction: None,
2032 active_diagnostics: ActiveDiagnostic::None,
2033 show_inline_diagnostics: ProjectSettings::get_global(cx).diagnostics.inline.enabled,
2034 inline_diagnostics_update: Task::ready(()),
2035 inline_diagnostics: Vec::new(),
2036 soft_wrap_mode_override,
2037 diagnostics_max_severity,
2038 hard_wrap: None,
2039 completion_provider: project.clone().map(|project| Rc::new(project) as _),
2040 semantics_provider: project.clone().map(|project| Rc::new(project) as _),
2041 collaboration_hub: project.clone().map(|project| Box::new(project) as _),
2042 project,
2043 blink_manager: blink_manager.clone(),
2044 show_local_selections: true,
2045 show_scrollbars: ScrollbarAxes {
2046 horizontal: full_mode,
2047 vertical: full_mode,
2048 },
2049 minimap_visibility: MinimapVisibility::for_mode(&mode, cx),
2050 offset_content: !matches!(mode, EditorMode::SingleLine { .. }),
2051 show_breadcrumbs: EditorSettings::get_global(cx).toolbar.breadcrumbs,
2052 show_gutter: mode.is_full(),
2053 show_line_numbers: None,
2054 use_relative_line_numbers: None,
2055 disable_expand_excerpt_buttons: false,
2056 show_git_diff_gutter: None,
2057 show_code_actions: None,
2058 show_runnables: None,
2059 show_breakpoints: None,
2060 show_wrap_guides: None,
2061 show_indent_guides,
2062 placeholder_text: None,
2063 highlight_order: 0,
2064 highlighted_rows: HashMap::default(),
2065 background_highlights: TreeMap::default(),
2066 gutter_highlights: TreeMap::default(),
2067 scrollbar_marker_state: ScrollbarMarkerState::default(),
2068 active_indent_guides_state: ActiveIndentGuidesState::default(),
2069 nav_history: None,
2070 context_menu: RefCell::new(None),
2071 context_menu_options: None,
2072 mouse_context_menu: None,
2073 completion_tasks: Vec::new(),
2074 inline_blame_popover: None,
2075 inline_blame_popover_show_task: None,
2076 signature_help_state: SignatureHelpState::default(),
2077 auto_signature_help: None,
2078 find_all_references_task_sources: Vec::new(),
2079 next_completion_id: 0,
2080 next_inlay_id: 0,
2081 code_action_providers,
2082 available_code_actions: None,
2083 code_actions_task: None,
2084 quick_selection_highlight_task: None,
2085 debounced_selection_highlight_task: None,
2086 document_highlights_task: None,
2087 linked_editing_range_task: None,
2088 pending_rename: None,
2089 searchable: true,
2090 cursor_shape: EditorSettings::get_global(cx)
2091 .cursor_shape
2092 .unwrap_or_default(),
2093 current_line_highlight: None,
2094 autoindent_mode: Some(AutoindentMode::EachLine),
2095 collapse_matches: false,
2096 workspace: None,
2097 input_enabled: true,
2098 use_modal_editing: mode.is_full(),
2099 read_only: mode.is_minimap(),
2100 use_autoclose: true,
2101 use_auto_surround: true,
2102 auto_replace_emoji_shortcode: false,
2103 jsx_tag_auto_close_enabled_in_any_buffer: false,
2104 leader_id: None,
2105 remote_id: None,
2106 hover_state: HoverState::default(),
2107 pending_mouse_down: None,
2108 hovered_link_state: None,
2109 edit_prediction_provider: None,
2110 active_inline_completion: None,
2111 stale_inline_completion_in_menu: None,
2112 edit_prediction_preview: EditPredictionPreview::Inactive {
2113 released_too_fast: false,
2114 },
2115 inline_diagnostics_enabled: mode.is_full(),
2116 diagnostics_enabled: mode.is_full(),
2117 inline_value_cache: InlineValueCache::new(inlay_hint_settings.show_value_hints),
2118 inlay_hint_cache: InlayHintCache::new(inlay_hint_settings),
2119
2120 gutter_hovered: false,
2121 pixel_position_of_newest_cursor: None,
2122 last_bounds: None,
2123 last_position_map: None,
2124 expect_bounds_change: None,
2125 gutter_dimensions: GutterDimensions::default(),
2126 style: None,
2127 show_cursor_names: false,
2128 hovered_cursors: HashMap::default(),
2129 next_editor_action_id: EditorActionId::default(),
2130 editor_actions: Rc::default(),
2131 inline_completions_hidden_for_vim_mode: false,
2132 show_inline_completions_override: None,
2133 menu_inline_completions_policy: MenuInlineCompletionsPolicy::ByProvider,
2134 edit_prediction_settings: EditPredictionSettings::Disabled,
2135 edit_prediction_indent_conflict: false,
2136 edit_prediction_requires_modifier_in_indent_conflict: true,
2137 custom_context_menu: None,
2138 show_git_blame_gutter: false,
2139 show_git_blame_inline: false,
2140 show_selection_menu: None,
2141 show_git_blame_inline_delay_task: None,
2142 git_blame_inline_enabled: ProjectSettings::get_global(cx).git.inline_blame_enabled(),
2143 render_diff_hunk_controls: Arc::new(render_diff_hunk_controls),
2144 serialize_dirty_buffers: !mode.is_minimap()
2145 && ProjectSettings::get_global(cx)
2146 .session
2147 .restore_unsaved_buffers,
2148 blame: None,
2149 blame_subscription: None,
2150 tasks: BTreeMap::default(),
2151
2152 breakpoint_store,
2153 gutter_breakpoint_indicator: (None, None),
2154 hovered_diff_hunk_row: None,
2155 _subscriptions: vec![
2156 cx.observe(&buffer, Self::on_buffer_changed),
2157 cx.subscribe_in(&buffer, window, Self::on_buffer_event),
2158 cx.observe_in(&display_map, window, Self::on_display_map_changed),
2159 cx.observe(&blink_manager, |_, _, cx| cx.notify()),
2160 cx.observe_global_in::<SettingsStore>(window, Self::settings_changed),
2161 observe_buffer_font_size_adjustment(cx, |_, cx| cx.notify()),
2162 cx.observe_window_activation(window, |editor, window, cx| {
2163 let active = window.is_window_active();
2164 editor.blink_manager.update(cx, |blink_manager, cx| {
2165 if active {
2166 blink_manager.enable(cx);
2167 } else {
2168 blink_manager.disable(cx);
2169 }
2170 });
2171 if active {
2172 editor.show_mouse_cursor(cx);
2173 }
2174 }),
2175 ],
2176 tasks_update_task: None,
2177 pull_diagnostics_task: Task::ready(()),
2178 colors: None,
2179 next_color_inlay_id: 0,
2180 linked_edit_ranges: Default::default(),
2181 in_project_search: false,
2182 previous_search_ranges: None,
2183 breadcrumb_header: None,
2184 focused_block: None,
2185 next_scroll_position: NextScrollCursorCenterTopBottom::default(),
2186 addons: HashMap::default(),
2187 registered_buffers: HashMap::default(),
2188 _scroll_cursor_center_top_bottom_task: Task::ready(()),
2189 selection_mark_mode: false,
2190 toggle_fold_multiple_buffers: Task::ready(()),
2191 serialize_selections: Task::ready(()),
2192 serialize_folds: Task::ready(()),
2193 text_style_refinement: None,
2194 load_diff_task: load_uncommitted_diff,
2195 temporary_diff_override: false,
2196 mouse_cursor_hidden: false,
2197 minimap: None,
2198 hide_mouse_mode: EditorSettings::get_global(cx)
2199 .hide_mouse
2200 .unwrap_or_default(),
2201 change_list: ChangeList::new(),
2202 mode,
2203 selection_drag_state: SelectionDragState::None,
2204 folding_newlines: Task::ready(()),
2205 };
2206 if let Some(breakpoints) = editor.breakpoint_store.as_ref() {
2207 editor
2208 ._subscriptions
2209 .push(cx.observe(breakpoints, |_, _, cx| {
2210 cx.notify();
2211 }));
2212 }
2213 editor.tasks_update_task = Some(editor.refresh_runnables(window, cx));
2214 editor._subscriptions.extend(project_subscriptions);
2215
2216 editor._subscriptions.push(cx.subscribe_in(
2217 &cx.entity(),
2218 window,
2219 |editor, _, e: &EditorEvent, window, cx| match e {
2220 EditorEvent::ScrollPositionChanged { local, .. } => {
2221 if *local {
2222 let new_anchor = editor.scroll_manager.anchor();
2223 let snapshot = editor.snapshot(window, cx);
2224 editor.update_restoration_data(cx, move |data| {
2225 data.scroll_position = (
2226 new_anchor.top_row(&snapshot.buffer_snapshot),
2227 new_anchor.offset,
2228 );
2229 });
2230 editor.hide_signature_help(cx, SignatureHelpHiddenBy::Escape);
2231 editor.inline_blame_popover.take();
2232 }
2233 }
2234 EditorEvent::Edited { .. } => {
2235 if !vim_enabled(cx) {
2236 let (map, selections) = editor.selections.all_adjusted_display(cx);
2237 let pop_state = editor
2238 .change_list
2239 .last()
2240 .map(|previous| {
2241 previous.len() == selections.len()
2242 && previous.iter().enumerate().all(|(ix, p)| {
2243 p.to_display_point(&map).row()
2244 == selections[ix].head().row()
2245 })
2246 })
2247 .unwrap_or(false);
2248 let new_positions = selections
2249 .into_iter()
2250 .map(|s| map.display_point_to_anchor(s.head(), Bias::Left))
2251 .collect();
2252 editor
2253 .change_list
2254 .push_to_change_list(pop_state, new_positions);
2255 }
2256 }
2257 _ => (),
2258 },
2259 ));
2260
2261 if let Some(dap_store) = editor
2262 .project
2263 .as_ref()
2264 .map(|project| project.read(cx).dap_store())
2265 {
2266 let weak_editor = cx.weak_entity();
2267
2268 editor
2269 ._subscriptions
2270 .push(
2271 cx.observe_new::<project::debugger::session::Session>(move |_, _, cx| {
2272 let session_entity = cx.entity();
2273 weak_editor
2274 .update(cx, |editor, cx| {
2275 editor._subscriptions.push(
2276 cx.subscribe(&session_entity, Self::on_debug_session_event),
2277 );
2278 })
2279 .ok();
2280 }),
2281 );
2282
2283 for session in dap_store.read(cx).sessions().cloned().collect::<Vec<_>>() {
2284 editor
2285 ._subscriptions
2286 .push(cx.subscribe(&session, Self::on_debug_session_event));
2287 }
2288 }
2289
2290 // skip adding the initial selection to selection history
2291 editor.selection_history.mode = SelectionHistoryMode::Skipping;
2292 editor.end_selection(window, cx);
2293 editor.selection_history.mode = SelectionHistoryMode::Normal;
2294
2295 editor.scroll_manager.show_scrollbars(window, cx);
2296 jsx_tag_auto_close::refresh_enabled_in_any_buffer(&mut editor, &buffer, cx);
2297
2298 if full_mode {
2299 let should_auto_hide_scrollbars = cx.should_auto_hide_scrollbars();
2300 cx.set_global(ScrollbarAutoHide(should_auto_hide_scrollbars));
2301
2302 if editor.git_blame_inline_enabled {
2303 editor.start_git_blame_inline(false, window, cx);
2304 }
2305
2306 editor.go_to_active_debug_line(window, cx);
2307
2308 if let Some(buffer) = buffer.read(cx).as_singleton() {
2309 if let Some(project) = editor.project.as_ref() {
2310 let handle = project.update(cx, |project, cx| {
2311 project.register_buffer_with_language_servers(&buffer, cx)
2312 });
2313 editor
2314 .registered_buffers
2315 .insert(buffer.read(cx).remote_id(), handle);
2316 }
2317 }
2318
2319 editor.minimap =
2320 editor.create_minimap(EditorSettings::get_global(cx).minimap, window, cx);
2321 editor.colors = Some(LspColorData::new(cx));
2322 editor.update_lsp_data(false, None, window, cx);
2323 }
2324
2325 editor.report_editor_event("Editor Opened", None, cx);
2326 editor
2327 }
2328
2329 pub fn deploy_mouse_context_menu(
2330 &mut self,
2331 position: gpui::Point<Pixels>,
2332 context_menu: Entity<ContextMenu>,
2333 window: &mut Window,
2334 cx: &mut Context<Self>,
2335 ) {
2336 self.mouse_context_menu = Some(MouseContextMenu::new(
2337 self,
2338 crate::mouse_context_menu::MenuPosition::PinnedToScreen(position),
2339 context_menu,
2340 window,
2341 cx,
2342 ));
2343 }
2344
2345 pub fn mouse_menu_is_focused(&self, window: &Window, cx: &App) -> bool {
2346 self.mouse_context_menu
2347 .as_ref()
2348 .is_some_and(|menu| menu.context_menu.focus_handle(cx).is_focused(window))
2349 }
2350
2351 pub fn key_context(&self, window: &Window, cx: &App) -> KeyContext {
2352 self.key_context_internal(self.has_active_inline_completion(), window, cx)
2353 }
2354
2355 fn key_context_internal(
2356 &self,
2357 has_active_edit_prediction: bool,
2358 window: &Window,
2359 cx: &App,
2360 ) -> KeyContext {
2361 let mut key_context = KeyContext::new_with_defaults();
2362 key_context.add("Editor");
2363 let mode = match self.mode {
2364 EditorMode::SingleLine { .. } => "single_line",
2365 EditorMode::AutoHeight { .. } => "auto_height",
2366 EditorMode::Minimap { .. } => "minimap",
2367 EditorMode::Full { .. } => "full",
2368 };
2369
2370 if EditorSettings::jupyter_enabled(cx) {
2371 key_context.add("jupyter");
2372 }
2373
2374 key_context.set("mode", mode);
2375 if self.pending_rename.is_some() {
2376 key_context.add("renaming");
2377 }
2378
2379 match self.context_menu.borrow().as_ref() {
2380 Some(CodeContextMenu::Completions(_)) => {
2381 key_context.add("menu");
2382 key_context.add("showing_completions");
2383 }
2384 Some(CodeContextMenu::CodeActions(_)) => {
2385 key_context.add("menu");
2386 key_context.add("showing_code_actions")
2387 }
2388 None => {}
2389 }
2390
2391 if self.signature_help_state.has_multiple_signatures() {
2392 key_context.add("showing_signature_help");
2393 }
2394
2395 // Disable vim contexts when a sub-editor (e.g. rename/inline assistant) is focused.
2396 if !self.focus_handle(cx).contains_focused(window, cx)
2397 || (self.is_focused(window) || self.mouse_menu_is_focused(window, cx))
2398 {
2399 for addon in self.addons.values() {
2400 addon.extend_key_context(&mut key_context, cx)
2401 }
2402 }
2403
2404 if let Some(singleton_buffer) = self.buffer.read(cx).as_singleton() {
2405 if let Some(extension) = singleton_buffer
2406 .read(cx)
2407 .file()
2408 .and_then(|file| file.path().extension()?.to_str())
2409 {
2410 key_context.set("extension", extension.to_string());
2411 }
2412 } else {
2413 key_context.add("multibuffer");
2414 }
2415
2416 if has_active_edit_prediction {
2417 if self.edit_prediction_in_conflict() {
2418 key_context.add(EDIT_PREDICTION_CONFLICT_KEY_CONTEXT);
2419 } else {
2420 key_context.add(EDIT_PREDICTION_KEY_CONTEXT);
2421 key_context.add("copilot_suggestion");
2422 }
2423 }
2424
2425 if self.selection_mark_mode {
2426 key_context.add("selection_mode");
2427 }
2428
2429 key_context
2430 }
2431
2432 fn show_mouse_cursor(&mut self, cx: &mut Context<Self>) {
2433 if self.mouse_cursor_hidden {
2434 self.mouse_cursor_hidden = false;
2435 cx.notify();
2436 }
2437 }
2438
2439 pub fn hide_mouse_cursor(&mut self, origin: HideMouseCursorOrigin, cx: &mut Context<Self>) {
2440 let hide_mouse_cursor = match origin {
2441 HideMouseCursorOrigin::TypingAction => {
2442 matches!(
2443 self.hide_mouse_mode,
2444 HideMouseMode::OnTyping | HideMouseMode::OnTypingAndMovement
2445 )
2446 }
2447 HideMouseCursorOrigin::MovementAction => {
2448 matches!(self.hide_mouse_mode, HideMouseMode::OnTypingAndMovement)
2449 }
2450 };
2451 if self.mouse_cursor_hidden != hide_mouse_cursor {
2452 self.mouse_cursor_hidden = hide_mouse_cursor;
2453 cx.notify();
2454 }
2455 }
2456
2457 pub fn edit_prediction_in_conflict(&self) -> bool {
2458 if !self.show_edit_predictions_in_menu() {
2459 return false;
2460 }
2461
2462 let showing_completions = self
2463 .context_menu
2464 .borrow()
2465 .as_ref()
2466 .map_or(false, |context| {
2467 matches!(context, CodeContextMenu::Completions(_))
2468 });
2469
2470 showing_completions
2471 || self.edit_prediction_requires_modifier()
2472 // Require modifier key when the cursor is on leading whitespace, to allow `tab`
2473 // bindings to insert tab characters.
2474 || (self.edit_prediction_requires_modifier_in_indent_conflict && self.edit_prediction_indent_conflict)
2475 }
2476
2477 pub fn accept_edit_prediction_keybind(
2478 &self,
2479 accept_partial: bool,
2480 window: &Window,
2481 cx: &App,
2482 ) -> AcceptEditPredictionBinding {
2483 let key_context = self.key_context_internal(true, window, cx);
2484 let in_conflict = self.edit_prediction_in_conflict();
2485
2486 let bindings = if accept_partial {
2487 window.bindings_for_action_in_context(&AcceptPartialEditPrediction, key_context)
2488 } else {
2489 window.bindings_for_action_in_context(&AcceptEditPrediction, key_context)
2490 };
2491
2492 // TODO: if the binding contains multiple keystrokes, display all of them, not
2493 // just the first one.
2494 AcceptEditPredictionBinding(bindings.into_iter().rev().find(|binding| {
2495 !in_conflict
2496 || binding
2497 .keystrokes()
2498 .first()
2499 .map_or(false, |keystroke| keystroke.modifiers.modified())
2500 }))
2501 }
2502
2503 pub fn new_file(
2504 workspace: &mut Workspace,
2505 _: &workspace::NewFile,
2506 window: &mut Window,
2507 cx: &mut Context<Workspace>,
2508 ) {
2509 Self::new_in_workspace(workspace, window, cx).detach_and_prompt_err(
2510 "Failed to create buffer",
2511 window,
2512 cx,
2513 |e, _, _| match e.error_code() {
2514 ErrorCode::RemoteUpgradeRequired => Some(format!(
2515 "The remote instance of Zed does not support this yet. It must be upgraded to {}",
2516 e.error_tag("required").unwrap_or("the latest version")
2517 )),
2518 _ => None,
2519 },
2520 );
2521 }
2522
2523 pub fn new_in_workspace(
2524 workspace: &mut Workspace,
2525 window: &mut Window,
2526 cx: &mut Context<Workspace>,
2527 ) -> Task<Result<Entity<Editor>>> {
2528 let project = workspace.project().clone();
2529 let create = project.update(cx, |project, cx| project.create_buffer(cx));
2530
2531 cx.spawn_in(window, async move |workspace, cx| {
2532 let buffer = create.await?;
2533 workspace.update_in(cx, |workspace, window, cx| {
2534 let editor =
2535 cx.new(|cx| Editor::for_buffer(buffer, Some(project.clone()), window, cx));
2536 workspace.add_item_to_active_pane(Box::new(editor.clone()), None, true, window, cx);
2537 editor
2538 })
2539 })
2540 }
2541
2542 fn new_file_vertical(
2543 workspace: &mut Workspace,
2544 _: &workspace::NewFileSplitVertical,
2545 window: &mut Window,
2546 cx: &mut Context<Workspace>,
2547 ) {
2548 Self::new_file_in_direction(workspace, SplitDirection::vertical(cx), window, cx)
2549 }
2550
2551 fn new_file_horizontal(
2552 workspace: &mut Workspace,
2553 _: &workspace::NewFileSplitHorizontal,
2554 window: &mut Window,
2555 cx: &mut Context<Workspace>,
2556 ) {
2557 Self::new_file_in_direction(workspace, SplitDirection::horizontal(cx), window, cx)
2558 }
2559
2560 fn new_file_in_direction(
2561 workspace: &mut Workspace,
2562 direction: SplitDirection,
2563 window: &mut Window,
2564 cx: &mut Context<Workspace>,
2565 ) {
2566 let project = workspace.project().clone();
2567 let create = project.update(cx, |project, cx| project.create_buffer(cx));
2568
2569 cx.spawn_in(window, async move |workspace, cx| {
2570 let buffer = create.await?;
2571 workspace.update_in(cx, move |workspace, window, cx| {
2572 workspace.split_item(
2573 direction,
2574 Box::new(
2575 cx.new(|cx| Editor::for_buffer(buffer, Some(project.clone()), window, cx)),
2576 ),
2577 window,
2578 cx,
2579 )
2580 })?;
2581 anyhow::Ok(())
2582 })
2583 .detach_and_prompt_err("Failed to create buffer", window, cx, |e, _, _| {
2584 match e.error_code() {
2585 ErrorCode::RemoteUpgradeRequired => Some(format!(
2586 "The remote instance of Zed does not support this yet. It must be upgraded to {}",
2587 e.error_tag("required").unwrap_or("the latest version")
2588 )),
2589 _ => None,
2590 }
2591 });
2592 }
2593
2594 pub fn leader_id(&self) -> Option<CollaboratorId> {
2595 self.leader_id
2596 }
2597
2598 pub fn buffer(&self) -> &Entity<MultiBuffer> {
2599 &self.buffer
2600 }
2601
2602 pub fn workspace(&self) -> Option<Entity<Workspace>> {
2603 self.workspace.as_ref()?.0.upgrade()
2604 }
2605
2606 pub fn title<'a>(&self, cx: &'a App) -> Cow<'a, str> {
2607 self.buffer().read(cx).title(cx)
2608 }
2609
2610 pub fn snapshot(&self, window: &mut Window, cx: &mut App) -> EditorSnapshot {
2611 let git_blame_gutter_max_author_length = self
2612 .render_git_blame_gutter(cx)
2613 .then(|| {
2614 if let Some(blame) = self.blame.as_ref() {
2615 let max_author_length =
2616 blame.update(cx, |blame, cx| blame.max_author_length(cx));
2617 Some(max_author_length)
2618 } else {
2619 None
2620 }
2621 })
2622 .flatten();
2623
2624 EditorSnapshot {
2625 mode: self.mode.clone(),
2626 show_gutter: self.show_gutter,
2627 show_line_numbers: self.show_line_numbers,
2628 show_git_diff_gutter: self.show_git_diff_gutter,
2629 show_code_actions: self.show_code_actions,
2630 show_runnables: self.show_runnables,
2631 show_breakpoints: self.show_breakpoints,
2632 git_blame_gutter_max_author_length,
2633 display_snapshot: self.display_map.update(cx, |map, cx| map.snapshot(cx)),
2634 scroll_anchor: self.scroll_manager.anchor(),
2635 ongoing_scroll: self.scroll_manager.ongoing_scroll(),
2636 placeholder_text: self.placeholder_text.clone(),
2637 is_focused: self.focus_handle.is_focused(window),
2638 current_line_highlight: self
2639 .current_line_highlight
2640 .unwrap_or_else(|| EditorSettings::get_global(cx).current_line_highlight),
2641 gutter_hovered: self.gutter_hovered,
2642 }
2643 }
2644
2645 pub fn language_at<T: ToOffset>(&self, point: T, cx: &App) -> Option<Arc<Language>> {
2646 self.buffer.read(cx).language_at(point, cx)
2647 }
2648
2649 pub fn file_at<T: ToOffset>(&self, point: T, cx: &App) -> Option<Arc<dyn language::File>> {
2650 self.buffer.read(cx).read(cx).file_at(point).cloned()
2651 }
2652
2653 pub fn active_excerpt(
2654 &self,
2655 cx: &App,
2656 ) -> Option<(ExcerptId, Entity<Buffer>, Range<text::Anchor>)> {
2657 self.buffer
2658 .read(cx)
2659 .excerpt_containing(self.selections.newest_anchor().head(), cx)
2660 }
2661
2662 pub fn mode(&self) -> &EditorMode {
2663 &self.mode
2664 }
2665
2666 pub fn set_mode(&mut self, mode: EditorMode) {
2667 self.mode = mode;
2668 }
2669
2670 pub fn collaboration_hub(&self) -> Option<&dyn CollaborationHub> {
2671 self.collaboration_hub.as_deref()
2672 }
2673
2674 pub fn set_collaboration_hub(&mut self, hub: Box<dyn CollaborationHub>) {
2675 self.collaboration_hub = Some(hub);
2676 }
2677
2678 pub fn set_in_project_search(&mut self, in_project_search: bool) {
2679 self.in_project_search = in_project_search;
2680 }
2681
2682 pub fn set_custom_context_menu(
2683 &mut self,
2684 f: impl 'static
2685 + Fn(
2686 &mut Self,
2687 DisplayPoint,
2688 &mut Window,
2689 &mut Context<Self>,
2690 ) -> Option<Entity<ui::ContextMenu>>,
2691 ) {
2692 self.custom_context_menu = Some(Box::new(f))
2693 }
2694
2695 pub fn set_completion_provider(&mut self, provider: Option<Rc<dyn CompletionProvider>>) {
2696 self.completion_provider = provider;
2697 }
2698
2699 pub fn semantics_provider(&self) -> Option<Rc<dyn SemanticsProvider>> {
2700 self.semantics_provider.clone()
2701 }
2702
2703 pub fn set_semantics_provider(&mut self, provider: Option<Rc<dyn SemanticsProvider>>) {
2704 self.semantics_provider = provider;
2705 }
2706
2707 pub fn set_edit_prediction_provider<T>(
2708 &mut self,
2709 provider: Option<Entity<T>>,
2710 window: &mut Window,
2711 cx: &mut Context<Self>,
2712 ) where
2713 T: EditPredictionProvider,
2714 {
2715 self.edit_prediction_provider =
2716 provider.map(|provider| RegisteredInlineCompletionProvider {
2717 _subscription: cx.observe_in(&provider, window, |this, _, window, cx| {
2718 if this.focus_handle.is_focused(window) {
2719 this.update_visible_inline_completion(window, cx);
2720 }
2721 }),
2722 provider: Arc::new(provider),
2723 });
2724 self.update_edit_prediction_settings(cx);
2725 self.refresh_inline_completion(false, false, window, cx);
2726 }
2727
2728 pub fn placeholder_text(&self) -> Option<&str> {
2729 self.placeholder_text.as_deref()
2730 }
2731
2732 pub fn set_placeholder_text(
2733 &mut self,
2734 placeholder_text: impl Into<Arc<str>>,
2735 cx: &mut Context<Self>,
2736 ) {
2737 let placeholder_text = Some(placeholder_text.into());
2738 if self.placeholder_text != placeholder_text {
2739 self.placeholder_text = placeholder_text;
2740 cx.notify();
2741 }
2742 }
2743
2744 pub fn set_cursor_shape(&mut self, cursor_shape: CursorShape, cx: &mut Context<Self>) {
2745 self.cursor_shape = cursor_shape;
2746
2747 // Disrupt blink for immediate user feedback that the cursor shape has changed
2748 self.blink_manager.update(cx, BlinkManager::show_cursor);
2749
2750 cx.notify();
2751 }
2752
2753 pub fn set_current_line_highlight(
2754 &mut self,
2755 current_line_highlight: Option<CurrentLineHighlight>,
2756 ) {
2757 self.current_line_highlight = current_line_highlight;
2758 }
2759
2760 pub fn set_collapse_matches(&mut self, collapse_matches: bool) {
2761 self.collapse_matches = collapse_matches;
2762 }
2763
2764 fn register_buffers_with_language_servers(&mut self, cx: &mut Context<Self>) {
2765 let buffers = self.buffer.read(cx).all_buffers();
2766 let Some(project) = self.project.as_ref() else {
2767 return;
2768 };
2769 project.update(cx, |project, cx| {
2770 for buffer in buffers {
2771 self.registered_buffers
2772 .entry(buffer.read(cx).remote_id())
2773 .or_insert_with(|| project.register_buffer_with_language_servers(&buffer, cx));
2774 }
2775 })
2776 }
2777
2778 pub fn range_for_match<T: std::marker::Copy>(&self, range: &Range<T>) -> Range<T> {
2779 if self.collapse_matches {
2780 return range.start..range.start;
2781 }
2782 range.clone()
2783 }
2784
2785 pub fn set_clip_at_line_ends(&mut self, clip: bool, cx: &mut Context<Self>) {
2786 if self.display_map.read(cx).clip_at_line_ends != clip {
2787 self.display_map
2788 .update(cx, |map, _| map.clip_at_line_ends = clip);
2789 }
2790 }
2791
2792 pub fn set_input_enabled(&mut self, input_enabled: bool) {
2793 self.input_enabled = input_enabled;
2794 }
2795
2796 pub fn set_inline_completions_hidden_for_vim_mode(
2797 &mut self,
2798 hidden: bool,
2799 window: &mut Window,
2800 cx: &mut Context<Self>,
2801 ) {
2802 if hidden != self.inline_completions_hidden_for_vim_mode {
2803 self.inline_completions_hidden_for_vim_mode = hidden;
2804 if hidden {
2805 self.update_visible_inline_completion(window, cx);
2806 } else {
2807 self.refresh_inline_completion(true, false, window, cx);
2808 }
2809 }
2810 }
2811
2812 pub fn set_menu_inline_completions_policy(&mut self, value: MenuInlineCompletionsPolicy) {
2813 self.menu_inline_completions_policy = value;
2814 }
2815
2816 pub fn set_autoindent(&mut self, autoindent: bool) {
2817 if autoindent {
2818 self.autoindent_mode = Some(AutoindentMode::EachLine);
2819 } else {
2820 self.autoindent_mode = None;
2821 }
2822 }
2823
2824 pub fn read_only(&self, cx: &App) -> bool {
2825 self.read_only || self.buffer.read(cx).read_only()
2826 }
2827
2828 pub fn set_read_only(&mut self, read_only: bool) {
2829 self.read_only = read_only;
2830 }
2831
2832 pub fn set_use_autoclose(&mut self, autoclose: bool) {
2833 self.use_autoclose = autoclose;
2834 }
2835
2836 pub fn set_use_auto_surround(&mut self, auto_surround: bool) {
2837 self.use_auto_surround = auto_surround;
2838 }
2839
2840 pub fn set_auto_replace_emoji_shortcode(&mut self, auto_replace: bool) {
2841 self.auto_replace_emoji_shortcode = auto_replace;
2842 }
2843
2844 pub fn toggle_edit_predictions(
2845 &mut self,
2846 _: &ToggleEditPrediction,
2847 window: &mut Window,
2848 cx: &mut Context<Self>,
2849 ) {
2850 if self.show_inline_completions_override.is_some() {
2851 self.set_show_edit_predictions(None, window, cx);
2852 } else {
2853 let show_edit_predictions = !self.edit_predictions_enabled();
2854 self.set_show_edit_predictions(Some(show_edit_predictions), window, cx);
2855 }
2856 }
2857
2858 pub fn set_show_edit_predictions(
2859 &mut self,
2860 show_edit_predictions: Option<bool>,
2861 window: &mut Window,
2862 cx: &mut Context<Self>,
2863 ) {
2864 self.show_inline_completions_override = show_edit_predictions;
2865 self.update_edit_prediction_settings(cx);
2866
2867 if let Some(false) = show_edit_predictions {
2868 self.discard_inline_completion(false, cx);
2869 } else {
2870 self.refresh_inline_completion(false, true, window, cx);
2871 }
2872 }
2873
2874 fn inline_completions_disabled_in_scope(
2875 &self,
2876 buffer: &Entity<Buffer>,
2877 buffer_position: language::Anchor,
2878 cx: &App,
2879 ) -> bool {
2880 let snapshot = buffer.read(cx).snapshot();
2881 let settings = snapshot.settings_at(buffer_position, cx);
2882
2883 let Some(scope) = snapshot.language_scope_at(buffer_position) else {
2884 return false;
2885 };
2886
2887 scope.override_name().map_or(false, |scope_name| {
2888 settings
2889 .edit_predictions_disabled_in
2890 .iter()
2891 .any(|s| s == scope_name)
2892 })
2893 }
2894
2895 pub fn set_use_modal_editing(&mut self, to: bool) {
2896 self.use_modal_editing = to;
2897 }
2898
2899 pub fn use_modal_editing(&self) -> bool {
2900 self.use_modal_editing
2901 }
2902
2903 fn selections_did_change(
2904 &mut self,
2905 local: bool,
2906 old_cursor_position: &Anchor,
2907 effects: SelectionEffects,
2908 window: &mut Window,
2909 cx: &mut Context<Self>,
2910 ) {
2911 window.invalidate_character_coordinates();
2912
2913 // Copy selections to primary selection buffer
2914 #[cfg(any(target_os = "linux", target_os = "freebsd"))]
2915 if local {
2916 let selections = self.selections.all::<usize>(cx);
2917 let buffer_handle = self.buffer.read(cx).read(cx);
2918
2919 let mut text = String::new();
2920 for (index, selection) in selections.iter().enumerate() {
2921 let text_for_selection = buffer_handle
2922 .text_for_range(selection.start..selection.end)
2923 .collect::<String>();
2924
2925 text.push_str(&text_for_selection);
2926 if index != selections.len() - 1 {
2927 text.push('\n');
2928 }
2929 }
2930
2931 if !text.is_empty() {
2932 cx.write_to_primary(ClipboardItem::new_string(text));
2933 }
2934 }
2935
2936 if self.focus_handle.is_focused(window) && self.leader_id.is_none() {
2937 self.buffer.update(cx, |buffer, cx| {
2938 buffer.set_active_selections(
2939 &self.selections.disjoint_anchors(),
2940 self.selections.line_mode,
2941 self.cursor_shape,
2942 cx,
2943 )
2944 });
2945 }
2946 let display_map = self
2947 .display_map
2948 .update(cx, |display_map, cx| display_map.snapshot(cx));
2949 let buffer = &display_map.buffer_snapshot;
2950 if self.selections.count() == 1 {
2951 self.add_selections_state = None;
2952 }
2953 self.select_next_state = None;
2954 self.select_prev_state = None;
2955 self.select_syntax_node_history.try_clear();
2956 self.invalidate_autoclose_regions(&self.selections.disjoint_anchors(), buffer);
2957 self.snippet_stack
2958 .invalidate(&self.selections.disjoint_anchors(), buffer);
2959 self.take_rename(false, window, cx);
2960
2961 let newest_selection = self.selections.newest_anchor();
2962 let new_cursor_position = newest_selection.head();
2963 let selection_start = newest_selection.start;
2964
2965 if effects.nav_history.is_none() || effects.nav_history == Some(true) {
2966 self.push_to_nav_history(
2967 *old_cursor_position,
2968 Some(new_cursor_position.to_point(buffer)),
2969 false,
2970 effects.nav_history == Some(true),
2971 cx,
2972 );
2973 }
2974
2975 if local {
2976 if let Some(buffer_id) = new_cursor_position.buffer_id {
2977 if !self.registered_buffers.contains_key(&buffer_id) {
2978 if let Some(project) = self.project.as_ref() {
2979 project.update(cx, |project, cx| {
2980 let Some(buffer) = self.buffer.read(cx).buffer(buffer_id) else {
2981 return;
2982 };
2983 self.registered_buffers.insert(
2984 buffer_id,
2985 project.register_buffer_with_language_servers(&buffer, cx),
2986 );
2987 })
2988 }
2989 }
2990 }
2991
2992 let mut context_menu = self.context_menu.borrow_mut();
2993 let completion_menu = match context_menu.as_ref() {
2994 Some(CodeContextMenu::Completions(menu)) => Some(menu),
2995 Some(CodeContextMenu::CodeActions(_)) => {
2996 *context_menu = None;
2997 None
2998 }
2999 None => None,
3000 };
3001 let completion_position = completion_menu.map(|menu| menu.initial_position);
3002 drop(context_menu);
3003
3004 if effects.completions {
3005 if let Some(completion_position) = completion_position {
3006 let start_offset = selection_start.to_offset(buffer);
3007 let position_matches = start_offset == completion_position.to_offset(buffer);
3008 let continue_showing = if position_matches {
3009 if self.snippet_stack.is_empty() {
3010 buffer.char_kind_before(start_offset, true) == Some(CharKind::Word)
3011 } else {
3012 // Snippet choices can be shown even when the cursor is in whitespace.
3013 // Dismissing the menu with actions like backspace is handled by
3014 // invalidation regions.
3015 true
3016 }
3017 } else {
3018 false
3019 };
3020
3021 if continue_showing {
3022 self.show_completions(&ShowCompletions { trigger: None }, window, cx);
3023 } else {
3024 self.hide_context_menu(window, cx);
3025 }
3026 }
3027 }
3028
3029 hide_hover(self, cx);
3030
3031 if old_cursor_position.to_display_point(&display_map).row()
3032 != new_cursor_position.to_display_point(&display_map).row()
3033 {
3034 self.available_code_actions.take();
3035 }
3036 self.refresh_code_actions(window, cx);
3037 self.refresh_document_highlights(cx);
3038 self.refresh_selected_text_highlights(false, window, cx);
3039 refresh_matching_bracket_highlights(self, window, cx);
3040 self.update_visible_inline_completion(window, cx);
3041 self.edit_prediction_requires_modifier_in_indent_conflict = true;
3042 linked_editing_ranges::refresh_linked_ranges(self, window, cx);
3043 self.inline_blame_popover.take();
3044 if self.git_blame_inline_enabled {
3045 self.start_inline_blame_timer(window, cx);
3046 }
3047 }
3048
3049 self.blink_manager.update(cx, BlinkManager::pause_blinking);
3050 cx.emit(EditorEvent::SelectionsChanged { local });
3051
3052 let selections = &self.selections.disjoint;
3053 if selections.len() == 1 {
3054 cx.emit(SearchEvent::ActiveMatchChanged)
3055 }
3056 if local {
3057 if let Some((_, _, buffer_snapshot)) = buffer.as_singleton() {
3058 let inmemory_selections = selections
3059 .iter()
3060 .map(|s| {
3061 text::ToPoint::to_point(&s.range().start.text_anchor, buffer_snapshot)
3062 ..text::ToPoint::to_point(&s.range().end.text_anchor, buffer_snapshot)
3063 })
3064 .collect();
3065 self.update_restoration_data(cx, |data| {
3066 data.selections = inmemory_selections;
3067 });
3068
3069 if WorkspaceSettings::get(None, cx).restore_on_startup
3070 != RestoreOnStartupBehavior::None
3071 {
3072 if let Some(workspace_id) =
3073 self.workspace.as_ref().and_then(|workspace| workspace.1)
3074 {
3075 let snapshot = self.buffer().read(cx).snapshot(cx);
3076 let selections = selections.clone();
3077 let background_executor = cx.background_executor().clone();
3078 let editor_id = cx.entity().entity_id().as_u64() as ItemId;
3079 self.serialize_selections = cx.background_spawn(async move {
3080 background_executor.timer(SERIALIZATION_THROTTLE_TIME).await;
3081 let db_selections = selections
3082 .iter()
3083 .map(|selection| {
3084 (
3085 selection.start.to_offset(&snapshot),
3086 selection.end.to_offset(&snapshot),
3087 )
3088 })
3089 .collect();
3090
3091 DB.save_editor_selections(editor_id, workspace_id, db_selections)
3092 .await
3093 .with_context(|| format!("persisting editor selections for editor {editor_id}, workspace {workspace_id:?}"))
3094 .log_err();
3095 });
3096 }
3097 }
3098 }
3099 }
3100
3101 cx.notify();
3102 }
3103
3104 fn folds_did_change(&mut self, cx: &mut Context<Self>) {
3105 use text::ToOffset as _;
3106 use text::ToPoint as _;
3107
3108 if self.mode.is_minimap()
3109 || WorkspaceSettings::get(None, cx).restore_on_startup == RestoreOnStartupBehavior::None
3110 {
3111 return;
3112 }
3113
3114 let Some(singleton) = self.buffer().read(cx).as_singleton() else {
3115 return;
3116 };
3117
3118 let snapshot = singleton.read(cx).snapshot();
3119 let inmemory_folds = self.display_map.update(cx, |display_map, cx| {
3120 let display_snapshot = display_map.snapshot(cx);
3121
3122 display_snapshot
3123 .folds_in_range(0..display_snapshot.buffer_snapshot.len())
3124 .map(|fold| {
3125 fold.range.start.text_anchor.to_point(&snapshot)
3126 ..fold.range.end.text_anchor.to_point(&snapshot)
3127 })
3128 .collect()
3129 });
3130 self.update_restoration_data(cx, |data| {
3131 data.folds = inmemory_folds;
3132 });
3133
3134 let Some(workspace_id) = self.workspace.as_ref().and_then(|workspace| workspace.1) else {
3135 return;
3136 };
3137 let background_executor = cx.background_executor().clone();
3138 let editor_id = cx.entity().entity_id().as_u64() as ItemId;
3139 let db_folds = self.display_map.update(cx, |display_map, cx| {
3140 display_map
3141 .snapshot(cx)
3142 .folds_in_range(0..snapshot.len())
3143 .map(|fold| {
3144 (
3145 fold.range.start.text_anchor.to_offset(&snapshot),
3146 fold.range.end.text_anchor.to_offset(&snapshot),
3147 )
3148 })
3149 .collect()
3150 });
3151 self.serialize_folds = cx.background_spawn(async move {
3152 background_executor.timer(SERIALIZATION_THROTTLE_TIME).await;
3153 DB.save_editor_folds(editor_id, workspace_id, db_folds)
3154 .await
3155 .with_context(|| {
3156 format!(
3157 "persisting editor folds for editor {editor_id}, workspace {workspace_id:?}"
3158 )
3159 })
3160 .log_err();
3161 });
3162 }
3163
3164 pub fn sync_selections(
3165 &mut self,
3166 other: Entity<Editor>,
3167 cx: &mut Context<Self>,
3168 ) -> gpui::Subscription {
3169 let other_selections = other.read(cx).selections.disjoint.to_vec();
3170 self.selections.change_with(cx, |selections| {
3171 selections.select_anchors(other_selections);
3172 });
3173
3174 let other_subscription =
3175 cx.subscribe(&other, |this, other, other_evt, cx| match other_evt {
3176 EditorEvent::SelectionsChanged { local: true } => {
3177 let other_selections = other.read(cx).selections.disjoint.to_vec();
3178 if other_selections.is_empty() {
3179 return;
3180 }
3181 this.selections.change_with(cx, |selections| {
3182 selections.select_anchors(other_selections);
3183 });
3184 }
3185 _ => {}
3186 });
3187
3188 let this_subscription =
3189 cx.subscribe_self::<EditorEvent>(move |this, this_evt, cx| match this_evt {
3190 EditorEvent::SelectionsChanged { local: true } => {
3191 let these_selections = this.selections.disjoint.to_vec();
3192 if these_selections.is_empty() {
3193 return;
3194 }
3195 other.update(cx, |other_editor, cx| {
3196 other_editor.selections.change_with(cx, |selections| {
3197 selections.select_anchors(these_selections);
3198 })
3199 });
3200 }
3201 _ => {}
3202 });
3203
3204 Subscription::join(other_subscription, this_subscription)
3205 }
3206
3207 /// Changes selections using the provided mutation function. Changes to `self.selections` occur
3208 /// immediately, but when run within `transact` or `with_selection_effects_deferred` other
3209 /// effects of selection change occur at the end of the transaction.
3210 pub fn change_selections<R>(
3211 &mut self,
3212 effects: SelectionEffects,
3213 window: &mut Window,
3214 cx: &mut Context<Self>,
3215 change: impl FnOnce(&mut MutableSelectionsCollection<'_>) -> R,
3216 ) -> R {
3217 if let Some(state) = &mut self.deferred_selection_effects_state {
3218 state.effects.scroll = effects.scroll.or(state.effects.scroll);
3219 state.effects.completions = effects.completions;
3220 state.effects.nav_history = effects.nav_history.or(state.effects.nav_history);
3221 let (changed, result) = self.selections.change_with(cx, change);
3222 state.changed |= changed;
3223 return result;
3224 }
3225 let mut state = DeferredSelectionEffectsState {
3226 changed: false,
3227 effects,
3228 old_cursor_position: self.selections.newest_anchor().head(),
3229 history_entry: SelectionHistoryEntry {
3230 selections: self.selections.disjoint_anchors(),
3231 select_next_state: self.select_next_state.clone(),
3232 select_prev_state: self.select_prev_state.clone(),
3233 add_selections_state: self.add_selections_state.clone(),
3234 },
3235 };
3236 let (changed, result) = self.selections.change_with(cx, change);
3237 state.changed = state.changed || changed;
3238 if self.defer_selection_effects {
3239 self.deferred_selection_effects_state = Some(state);
3240 } else {
3241 self.apply_selection_effects(state, window, cx);
3242 }
3243 result
3244 }
3245
3246 /// Defers the effects of selection change, so that the effects of multiple calls to
3247 /// `change_selections` are applied at the end. This way these intermediate states aren't added
3248 /// to selection history and the state of popovers based on selection position aren't
3249 /// erroneously updated.
3250 pub fn with_selection_effects_deferred<R>(
3251 &mut self,
3252 window: &mut Window,
3253 cx: &mut Context<Self>,
3254 update: impl FnOnce(&mut Self, &mut Window, &mut Context<Self>) -> R,
3255 ) -> R {
3256 let already_deferred = self.defer_selection_effects;
3257 self.defer_selection_effects = true;
3258 let result = update(self, window, cx);
3259 if !already_deferred {
3260 self.defer_selection_effects = false;
3261 if let Some(state) = self.deferred_selection_effects_state.take() {
3262 self.apply_selection_effects(state, window, cx);
3263 }
3264 }
3265 result
3266 }
3267
3268 fn apply_selection_effects(
3269 &mut self,
3270 state: DeferredSelectionEffectsState,
3271 window: &mut Window,
3272 cx: &mut Context<Self>,
3273 ) {
3274 if state.changed {
3275 self.selection_history.push(state.history_entry);
3276
3277 if let Some(autoscroll) = state.effects.scroll {
3278 self.request_autoscroll(autoscroll, cx);
3279 }
3280
3281 let old_cursor_position = &state.old_cursor_position;
3282
3283 self.selections_did_change(true, &old_cursor_position, state.effects, window, cx);
3284
3285 if self.should_open_signature_help_automatically(&old_cursor_position, cx) {
3286 self.show_signature_help(&ShowSignatureHelp, window, cx);
3287 }
3288 }
3289 }
3290
3291 pub fn edit<I, S, T>(&mut self, edits: I, cx: &mut Context<Self>)
3292 where
3293 I: IntoIterator<Item = (Range<S>, T)>,
3294 S: ToOffset,
3295 T: Into<Arc<str>>,
3296 {
3297 if self.read_only(cx) {
3298 return;
3299 }
3300
3301 self.buffer
3302 .update(cx, |buffer, cx| buffer.edit(edits, None, cx));
3303 }
3304
3305 pub fn edit_with_autoindent<I, S, T>(&mut self, edits: I, cx: &mut Context<Self>)
3306 where
3307 I: IntoIterator<Item = (Range<S>, T)>,
3308 S: ToOffset,
3309 T: Into<Arc<str>>,
3310 {
3311 if self.read_only(cx) {
3312 return;
3313 }
3314
3315 self.buffer.update(cx, |buffer, cx| {
3316 buffer.edit(edits, self.autoindent_mode.clone(), cx)
3317 });
3318 }
3319
3320 pub fn edit_with_block_indent<I, S, T>(
3321 &mut self,
3322 edits: I,
3323 original_indent_columns: Vec<Option<u32>>,
3324 cx: &mut Context<Self>,
3325 ) where
3326 I: IntoIterator<Item = (Range<S>, T)>,
3327 S: ToOffset,
3328 T: Into<Arc<str>>,
3329 {
3330 if self.read_only(cx) {
3331 return;
3332 }
3333
3334 self.buffer.update(cx, |buffer, cx| {
3335 buffer.edit(
3336 edits,
3337 Some(AutoindentMode::Block {
3338 original_indent_columns,
3339 }),
3340 cx,
3341 )
3342 });
3343 }
3344
3345 fn select(&mut self, phase: SelectPhase, window: &mut Window, cx: &mut Context<Self>) {
3346 self.hide_context_menu(window, cx);
3347
3348 match phase {
3349 SelectPhase::Begin {
3350 position,
3351 add,
3352 click_count,
3353 } => self.begin_selection(position, add, click_count, window, cx),
3354 SelectPhase::BeginColumnar {
3355 position,
3356 goal_column,
3357 reset,
3358 mode,
3359 } => self.begin_columnar_selection(position, goal_column, reset, mode, window, cx),
3360 SelectPhase::Extend {
3361 position,
3362 click_count,
3363 } => self.extend_selection(position, click_count, window, cx),
3364 SelectPhase::Update {
3365 position,
3366 goal_column,
3367 scroll_delta,
3368 } => self.update_selection(position, goal_column, scroll_delta, window, cx),
3369 SelectPhase::End => self.end_selection(window, cx),
3370 }
3371 }
3372
3373 fn extend_selection(
3374 &mut self,
3375 position: DisplayPoint,
3376 click_count: usize,
3377 window: &mut Window,
3378 cx: &mut Context<Self>,
3379 ) {
3380 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
3381 let tail = self.selections.newest::<usize>(cx).tail();
3382 self.begin_selection(position, false, click_count, window, cx);
3383
3384 let position = position.to_offset(&display_map, Bias::Left);
3385 let tail_anchor = display_map.buffer_snapshot.anchor_before(tail);
3386
3387 let mut pending_selection = self
3388 .selections
3389 .pending_anchor()
3390 .expect("extend_selection not called with pending selection");
3391 if position >= tail {
3392 pending_selection.start = tail_anchor;
3393 } else {
3394 pending_selection.end = tail_anchor;
3395 pending_selection.reversed = true;
3396 }
3397
3398 let mut pending_mode = self.selections.pending_mode().unwrap();
3399 match &mut pending_mode {
3400 SelectMode::Word(range) | SelectMode::Line(range) => *range = tail_anchor..tail_anchor,
3401 _ => {}
3402 }
3403
3404 let effects = if EditorSettings::get_global(cx).autoscroll_on_clicks {
3405 SelectionEffects::scroll(Autoscroll::fit())
3406 } else {
3407 SelectionEffects::no_scroll()
3408 };
3409
3410 self.change_selections(effects, window, cx, |s| {
3411 s.set_pending(pending_selection, pending_mode)
3412 });
3413 }
3414
3415 fn begin_selection(
3416 &mut self,
3417 position: DisplayPoint,
3418 add: bool,
3419 click_count: usize,
3420 window: &mut Window,
3421 cx: &mut Context<Self>,
3422 ) {
3423 if !self.focus_handle.is_focused(window) {
3424 self.last_focused_descendant = None;
3425 window.focus(&self.focus_handle);
3426 }
3427
3428 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
3429 let buffer = &display_map.buffer_snapshot;
3430 let position = display_map.clip_point(position, Bias::Left);
3431
3432 let start;
3433 let end;
3434 let mode;
3435 let mut auto_scroll;
3436 match click_count {
3437 1 => {
3438 start = buffer.anchor_before(position.to_point(&display_map));
3439 end = start;
3440 mode = SelectMode::Character;
3441 auto_scroll = true;
3442 }
3443 2 => {
3444 let position = display_map
3445 .clip_point(position, Bias::Left)
3446 .to_offset(&display_map, Bias::Left);
3447 let (range, _) = buffer.surrounding_word(position, false);
3448 start = buffer.anchor_before(range.start);
3449 end = buffer.anchor_before(range.end);
3450 mode = SelectMode::Word(start..end);
3451 auto_scroll = true;
3452 }
3453 3 => {
3454 let position = display_map
3455 .clip_point(position, Bias::Left)
3456 .to_point(&display_map);
3457 let line_start = display_map.prev_line_boundary(position).0;
3458 let next_line_start = buffer.clip_point(
3459 display_map.next_line_boundary(position).0 + Point::new(1, 0),
3460 Bias::Left,
3461 );
3462 start = buffer.anchor_before(line_start);
3463 end = buffer.anchor_before(next_line_start);
3464 mode = SelectMode::Line(start..end);
3465 auto_scroll = true;
3466 }
3467 _ => {
3468 start = buffer.anchor_before(0);
3469 end = buffer.anchor_before(buffer.len());
3470 mode = SelectMode::All;
3471 auto_scroll = false;
3472 }
3473 }
3474 auto_scroll &= EditorSettings::get_global(cx).autoscroll_on_clicks;
3475
3476 let point_to_delete: Option<usize> = {
3477 let selected_points: Vec<Selection<Point>> =
3478 self.selections.disjoint_in_range(start..end, cx);
3479
3480 if !add || click_count > 1 {
3481 None
3482 } else if !selected_points.is_empty() {
3483 Some(selected_points[0].id)
3484 } else {
3485 let clicked_point_already_selected =
3486 self.selections.disjoint.iter().find(|selection| {
3487 selection.start.to_point(buffer) == start.to_point(buffer)
3488 || selection.end.to_point(buffer) == end.to_point(buffer)
3489 });
3490
3491 clicked_point_already_selected.map(|selection| selection.id)
3492 }
3493 };
3494
3495 let selections_count = self.selections.count();
3496 let effects = if auto_scroll {
3497 SelectionEffects::default()
3498 } else {
3499 SelectionEffects::no_scroll()
3500 };
3501
3502 self.change_selections(effects, window, cx, |s| {
3503 if let Some(point_to_delete) = point_to_delete {
3504 s.delete(point_to_delete);
3505
3506 if selections_count == 1 {
3507 s.set_pending_anchor_range(start..end, mode);
3508 }
3509 } else {
3510 if !add {
3511 s.clear_disjoint();
3512 }
3513
3514 s.set_pending_anchor_range(start..end, mode);
3515 }
3516 });
3517 }
3518
3519 fn begin_columnar_selection(
3520 &mut self,
3521 position: DisplayPoint,
3522 goal_column: u32,
3523 reset: bool,
3524 mode: ColumnarMode,
3525 window: &mut Window,
3526 cx: &mut Context<Self>,
3527 ) {
3528 if !self.focus_handle.is_focused(window) {
3529 self.last_focused_descendant = None;
3530 window.focus(&self.focus_handle);
3531 }
3532
3533 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
3534
3535 if reset {
3536 let pointer_position = display_map
3537 .buffer_snapshot
3538 .anchor_before(position.to_point(&display_map));
3539
3540 self.change_selections(
3541 SelectionEffects::scroll(Autoscroll::newest()),
3542 window,
3543 cx,
3544 |s| {
3545 s.clear_disjoint();
3546 s.set_pending_anchor_range(
3547 pointer_position..pointer_position,
3548 SelectMode::Character,
3549 );
3550 },
3551 );
3552 };
3553
3554 let tail = self.selections.newest::<Point>(cx).tail();
3555 let selection_anchor = display_map.buffer_snapshot.anchor_before(tail);
3556 self.columnar_selection_state = match mode {
3557 ColumnarMode::FromMouse => Some(ColumnarSelectionState::FromMouse {
3558 selection_tail: selection_anchor,
3559 display_point: if reset {
3560 if position.column() != goal_column {
3561 Some(DisplayPoint::new(position.row(), goal_column))
3562 } else {
3563 None
3564 }
3565 } else {
3566 None
3567 },
3568 }),
3569 ColumnarMode::FromSelection => Some(ColumnarSelectionState::FromSelection {
3570 selection_tail: selection_anchor,
3571 }),
3572 };
3573
3574 if !reset {
3575 self.select_columns(position, goal_column, &display_map, window, cx);
3576 }
3577 }
3578
3579 fn update_selection(
3580 &mut self,
3581 position: DisplayPoint,
3582 goal_column: u32,
3583 scroll_delta: gpui::Point<f32>,
3584 window: &mut Window,
3585 cx: &mut Context<Self>,
3586 ) {
3587 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
3588
3589 if self.columnar_selection_state.is_some() {
3590 self.select_columns(position, goal_column, &display_map, window, cx);
3591 } else if let Some(mut pending) = self.selections.pending_anchor() {
3592 let buffer = &display_map.buffer_snapshot;
3593 let head;
3594 let tail;
3595 let mode = self.selections.pending_mode().unwrap();
3596 match &mode {
3597 SelectMode::Character => {
3598 head = position.to_point(&display_map);
3599 tail = pending.tail().to_point(buffer);
3600 }
3601 SelectMode::Word(original_range) => {
3602 let offset = display_map
3603 .clip_point(position, Bias::Left)
3604 .to_offset(&display_map, Bias::Left);
3605 let original_range = original_range.to_offset(buffer);
3606
3607 let head_offset = if buffer.is_inside_word(offset, false)
3608 || original_range.contains(&offset)
3609 {
3610 let (word_range, _) = buffer.surrounding_word(offset, false);
3611 if word_range.start < original_range.start {
3612 word_range.start
3613 } else {
3614 word_range.end
3615 }
3616 } else {
3617 offset
3618 };
3619
3620 head = head_offset.to_point(buffer);
3621 if head_offset <= original_range.start {
3622 tail = original_range.end.to_point(buffer);
3623 } else {
3624 tail = original_range.start.to_point(buffer);
3625 }
3626 }
3627 SelectMode::Line(original_range) => {
3628 let original_range = original_range.to_point(&display_map.buffer_snapshot);
3629
3630 let position = display_map
3631 .clip_point(position, Bias::Left)
3632 .to_point(&display_map);
3633 let line_start = display_map.prev_line_boundary(position).0;
3634 let next_line_start = buffer.clip_point(
3635 display_map.next_line_boundary(position).0 + Point::new(1, 0),
3636 Bias::Left,
3637 );
3638
3639 if line_start < original_range.start {
3640 head = line_start
3641 } else {
3642 head = next_line_start
3643 }
3644
3645 if head <= original_range.start {
3646 tail = original_range.end;
3647 } else {
3648 tail = original_range.start;
3649 }
3650 }
3651 SelectMode::All => {
3652 return;
3653 }
3654 };
3655
3656 if head < tail {
3657 pending.start = buffer.anchor_before(head);
3658 pending.end = buffer.anchor_before(tail);
3659 pending.reversed = true;
3660 } else {
3661 pending.start = buffer.anchor_before(tail);
3662 pending.end = buffer.anchor_before(head);
3663 pending.reversed = false;
3664 }
3665
3666 self.change_selections(SelectionEffects::no_scroll(), window, cx, |s| {
3667 s.set_pending(pending, mode);
3668 });
3669 } else {
3670 log::error!("update_selection dispatched with no pending selection");
3671 return;
3672 }
3673
3674 self.apply_scroll_delta(scroll_delta, window, cx);
3675 cx.notify();
3676 }
3677
3678 fn end_selection(&mut self, window: &mut Window, cx: &mut Context<Self>) {
3679 self.columnar_selection_state.take();
3680 if self.selections.pending_anchor().is_some() {
3681 let selections = self.selections.all::<usize>(cx);
3682 self.change_selections(SelectionEffects::no_scroll(), window, cx, |s| {
3683 s.select(selections);
3684 s.clear_pending();
3685 });
3686 }
3687 }
3688
3689 fn select_columns(
3690 &mut self,
3691 head: DisplayPoint,
3692 goal_column: u32,
3693 display_map: &DisplaySnapshot,
3694 window: &mut Window,
3695 cx: &mut Context<Self>,
3696 ) {
3697 let Some(columnar_state) = self.columnar_selection_state.as_ref() else {
3698 return;
3699 };
3700
3701 let tail = match columnar_state {
3702 ColumnarSelectionState::FromMouse {
3703 selection_tail,
3704 display_point,
3705 } => display_point.unwrap_or_else(|| selection_tail.to_display_point(&display_map)),
3706 ColumnarSelectionState::FromSelection { selection_tail } => {
3707 selection_tail.to_display_point(&display_map)
3708 }
3709 };
3710
3711 let start_row = cmp::min(tail.row(), head.row());
3712 let end_row = cmp::max(tail.row(), head.row());
3713 let start_column = cmp::min(tail.column(), goal_column);
3714 let end_column = cmp::max(tail.column(), goal_column);
3715 let reversed = start_column < tail.column();
3716
3717 let selection_ranges = (start_row.0..=end_row.0)
3718 .map(DisplayRow)
3719 .filter_map(|row| {
3720 if (matches!(columnar_state, ColumnarSelectionState::FromMouse { .. })
3721 || start_column <= display_map.line_len(row))
3722 && !display_map.is_block_line(row)
3723 {
3724 let start = display_map
3725 .clip_point(DisplayPoint::new(row, start_column), Bias::Left)
3726 .to_point(display_map);
3727 let end = display_map
3728 .clip_point(DisplayPoint::new(row, end_column), Bias::Right)
3729 .to_point(display_map);
3730 if reversed {
3731 Some(end..start)
3732 } else {
3733 Some(start..end)
3734 }
3735 } else {
3736 None
3737 }
3738 })
3739 .collect::<Vec<_>>();
3740
3741 let ranges = match columnar_state {
3742 ColumnarSelectionState::FromMouse { .. } => {
3743 let mut non_empty_ranges = selection_ranges
3744 .iter()
3745 .filter(|selection_range| selection_range.start != selection_range.end)
3746 .peekable();
3747 if non_empty_ranges.peek().is_some() {
3748 non_empty_ranges.cloned().collect()
3749 } else {
3750 selection_ranges
3751 }
3752 }
3753 _ => selection_ranges,
3754 };
3755
3756 self.change_selections(SelectionEffects::no_scroll(), window, cx, |s| {
3757 s.select_ranges(ranges);
3758 });
3759 cx.notify();
3760 }
3761
3762 pub fn has_non_empty_selection(&self, cx: &mut App) -> bool {
3763 self.selections
3764 .all_adjusted(cx)
3765 .iter()
3766 .any(|selection| !selection.is_empty())
3767 }
3768
3769 pub fn has_pending_nonempty_selection(&self) -> bool {
3770 let pending_nonempty_selection = match self.selections.pending_anchor() {
3771 Some(Selection { start, end, .. }) => start != end,
3772 None => false,
3773 };
3774
3775 pending_nonempty_selection
3776 || (self.columnar_selection_state.is_some() && self.selections.disjoint.len() > 1)
3777 }
3778
3779 pub fn has_pending_selection(&self) -> bool {
3780 self.selections.pending_anchor().is_some() || self.columnar_selection_state.is_some()
3781 }
3782
3783 pub fn cancel(&mut self, _: &Cancel, window: &mut Window, cx: &mut Context<Self>) {
3784 self.selection_mark_mode = false;
3785 self.selection_drag_state = SelectionDragState::None;
3786
3787 if self.clear_expanded_diff_hunks(cx) {
3788 cx.notify();
3789 return;
3790 }
3791 if self.dismiss_menus_and_popups(true, window, cx) {
3792 return;
3793 }
3794
3795 if self.mode.is_full()
3796 && self.change_selections(Default::default(), window, cx, |s| s.try_cancel())
3797 {
3798 return;
3799 }
3800
3801 cx.propagate();
3802 }
3803
3804 pub fn dismiss_menus_and_popups(
3805 &mut self,
3806 is_user_requested: bool,
3807 window: &mut Window,
3808 cx: &mut Context<Self>,
3809 ) -> bool {
3810 if self.take_rename(false, window, cx).is_some() {
3811 return true;
3812 }
3813
3814 if hide_hover(self, cx) {
3815 return true;
3816 }
3817
3818 if self.hide_signature_help(cx, SignatureHelpHiddenBy::Escape) {
3819 return true;
3820 }
3821
3822 if self.hide_context_menu(window, cx).is_some() {
3823 return true;
3824 }
3825
3826 if self.mouse_context_menu.take().is_some() {
3827 return true;
3828 }
3829
3830 if is_user_requested && self.discard_inline_completion(true, cx) {
3831 return true;
3832 }
3833
3834 if self.snippet_stack.pop().is_some() {
3835 return true;
3836 }
3837
3838 if self.mode.is_full() && matches!(self.active_diagnostics, ActiveDiagnostic::Group(_)) {
3839 self.dismiss_diagnostics(cx);
3840 return true;
3841 }
3842
3843 false
3844 }
3845
3846 fn linked_editing_ranges_for(
3847 &self,
3848 selection: Range<text::Anchor>,
3849 cx: &App,
3850 ) -> Option<HashMap<Entity<Buffer>, Vec<Range<text::Anchor>>>> {
3851 if self.linked_edit_ranges.is_empty() {
3852 return None;
3853 }
3854 let ((base_range, linked_ranges), buffer_snapshot, buffer) =
3855 selection.end.buffer_id.and_then(|end_buffer_id| {
3856 if selection.start.buffer_id != Some(end_buffer_id) {
3857 return None;
3858 }
3859 let buffer = self.buffer.read(cx).buffer(end_buffer_id)?;
3860 let snapshot = buffer.read(cx).snapshot();
3861 self.linked_edit_ranges
3862 .get(end_buffer_id, selection.start..selection.end, &snapshot)
3863 .map(|ranges| (ranges, snapshot, buffer))
3864 })?;
3865 use text::ToOffset as TO;
3866 // find offset from the start of current range to current cursor position
3867 let start_byte_offset = TO::to_offset(&base_range.start, &buffer_snapshot);
3868
3869 let start_offset = TO::to_offset(&selection.start, &buffer_snapshot);
3870 let start_difference = start_offset - start_byte_offset;
3871 let end_offset = TO::to_offset(&selection.end, &buffer_snapshot);
3872 let end_difference = end_offset - start_byte_offset;
3873 // Current range has associated linked ranges.
3874 let mut linked_edits = HashMap::<_, Vec<_>>::default();
3875 for range in linked_ranges.iter() {
3876 let start_offset = TO::to_offset(&range.start, &buffer_snapshot);
3877 let end_offset = start_offset + end_difference;
3878 let start_offset = start_offset + start_difference;
3879 if start_offset > buffer_snapshot.len() || end_offset > buffer_snapshot.len() {
3880 continue;
3881 }
3882 if self.selections.disjoint_anchor_ranges().any(|s| {
3883 if s.start.buffer_id != selection.start.buffer_id
3884 || s.end.buffer_id != selection.end.buffer_id
3885 {
3886 return false;
3887 }
3888 TO::to_offset(&s.start.text_anchor, &buffer_snapshot) <= end_offset
3889 && TO::to_offset(&s.end.text_anchor, &buffer_snapshot) >= start_offset
3890 }) {
3891 continue;
3892 }
3893 let start = buffer_snapshot.anchor_after(start_offset);
3894 let end = buffer_snapshot.anchor_after(end_offset);
3895 linked_edits
3896 .entry(buffer.clone())
3897 .or_default()
3898 .push(start..end);
3899 }
3900 Some(linked_edits)
3901 }
3902
3903 pub fn handle_input(&mut self, text: &str, window: &mut Window, cx: &mut Context<Self>) {
3904 let text: Arc<str> = text.into();
3905
3906 if self.read_only(cx) {
3907 return;
3908 }
3909
3910 self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
3911
3912 let selections = self.selections.all_adjusted(cx);
3913 let mut bracket_inserted = false;
3914 let mut edits = Vec::new();
3915 let mut linked_edits = HashMap::<_, Vec<_>>::default();
3916 let mut new_selections = Vec::with_capacity(selections.len());
3917 let mut new_autoclose_regions = Vec::new();
3918 let snapshot = self.buffer.read(cx).read(cx);
3919 let mut clear_linked_edit_ranges = false;
3920
3921 for (selection, autoclose_region) in
3922 self.selections_with_autoclose_regions(selections, &snapshot)
3923 {
3924 if let Some(scope) = snapshot.language_scope_at(selection.head()) {
3925 // Determine if the inserted text matches the opening or closing
3926 // bracket of any of this language's bracket pairs.
3927 let mut bracket_pair = None;
3928 let mut is_bracket_pair_start = false;
3929 let mut is_bracket_pair_end = false;
3930 if !text.is_empty() {
3931 let mut bracket_pair_matching_end = None;
3932 // `text` can be empty when a user is using IME (e.g. Chinese Wubi Simplified)
3933 // and they are removing the character that triggered IME popup.
3934 for (pair, enabled) in scope.brackets() {
3935 if !pair.close && !pair.surround {
3936 continue;
3937 }
3938
3939 if enabled && pair.start.ends_with(text.as_ref()) {
3940 let prefix_len = pair.start.len() - text.len();
3941 let preceding_text_matches_prefix = prefix_len == 0
3942 || (selection.start.column >= (prefix_len as u32)
3943 && snapshot.contains_str_at(
3944 Point::new(
3945 selection.start.row,
3946 selection.start.column - (prefix_len as u32),
3947 ),
3948 &pair.start[..prefix_len],
3949 ));
3950 if preceding_text_matches_prefix {
3951 bracket_pair = Some(pair.clone());
3952 is_bracket_pair_start = true;
3953 break;
3954 }
3955 }
3956 if pair.end.as_str() == text.as_ref() && bracket_pair_matching_end.is_none()
3957 {
3958 // take first bracket pair matching end, but don't break in case a later bracket
3959 // pair matches start
3960 bracket_pair_matching_end = Some(pair.clone());
3961 }
3962 }
3963 if let Some(end) = bracket_pair_matching_end
3964 && bracket_pair.is_none()
3965 {
3966 bracket_pair = Some(end);
3967 is_bracket_pair_end = true;
3968 }
3969 }
3970
3971 if let Some(bracket_pair) = bracket_pair {
3972 let snapshot_settings = snapshot.language_settings_at(selection.start, cx);
3973 let autoclose = self.use_autoclose && snapshot_settings.use_autoclose;
3974 let auto_surround =
3975 self.use_auto_surround && snapshot_settings.use_auto_surround;
3976 if selection.is_empty() {
3977 if is_bracket_pair_start {
3978 // If the inserted text is a suffix of an opening bracket and the
3979 // selection is preceded by the rest of the opening bracket, then
3980 // insert the closing bracket.
3981 let following_text_allows_autoclose = snapshot
3982 .chars_at(selection.start)
3983 .next()
3984 .map_or(true, |c| scope.should_autoclose_before(c));
3985
3986 let preceding_text_allows_autoclose = selection.start.column == 0
3987 || snapshot.reversed_chars_at(selection.start).next().map_or(
3988 true,
3989 |c| {
3990 bracket_pair.start != bracket_pair.end
3991 || !snapshot
3992 .char_classifier_at(selection.start)
3993 .is_word(c)
3994 },
3995 );
3996
3997 let is_closing_quote = if bracket_pair.end == bracket_pair.start
3998 && bracket_pair.start.len() == 1
3999 {
4000 let target = bracket_pair.start.chars().next().unwrap();
4001 let current_line_count = snapshot
4002 .reversed_chars_at(selection.start)
4003 .take_while(|&c| c != '\n')
4004 .filter(|&c| c == target)
4005 .count();
4006 current_line_count % 2 == 1
4007 } else {
4008 false
4009 };
4010
4011 if autoclose
4012 && bracket_pair.close
4013 && following_text_allows_autoclose
4014 && preceding_text_allows_autoclose
4015 && !is_closing_quote
4016 {
4017 let anchor = snapshot.anchor_before(selection.end);
4018 new_selections.push((selection.map(|_| anchor), text.len()));
4019 new_autoclose_regions.push((
4020 anchor,
4021 text.len(),
4022 selection.id,
4023 bracket_pair.clone(),
4024 ));
4025 edits.push((
4026 selection.range(),
4027 format!("{}{}", text, bracket_pair.end).into(),
4028 ));
4029 bracket_inserted = true;
4030 continue;
4031 }
4032 }
4033
4034 if let Some(region) = autoclose_region {
4035 // If the selection is followed by an auto-inserted closing bracket,
4036 // then don't insert that closing bracket again; just move the selection
4037 // past the closing bracket.
4038 let should_skip = selection.end == region.range.end.to_point(&snapshot)
4039 && text.as_ref() == region.pair.end.as_str();
4040 if should_skip {
4041 let anchor = snapshot.anchor_after(selection.end);
4042 new_selections
4043 .push((selection.map(|_| anchor), region.pair.end.len()));
4044 continue;
4045 }
4046 }
4047
4048 let always_treat_brackets_as_autoclosed = snapshot
4049 .language_settings_at(selection.start, cx)
4050 .always_treat_brackets_as_autoclosed;
4051 if always_treat_brackets_as_autoclosed
4052 && is_bracket_pair_end
4053 && snapshot.contains_str_at(selection.end, text.as_ref())
4054 {
4055 // Otherwise, when `always_treat_brackets_as_autoclosed` is set to `true
4056 // and the inserted text is a closing bracket and the selection is followed
4057 // by the closing bracket then move the selection past the closing bracket.
4058 let anchor = snapshot.anchor_after(selection.end);
4059 new_selections.push((selection.map(|_| anchor), text.len()));
4060 continue;
4061 }
4062 }
4063 // If an opening bracket is 1 character long and is typed while
4064 // text is selected, then surround that text with the bracket pair.
4065 else if auto_surround
4066 && bracket_pair.surround
4067 && is_bracket_pair_start
4068 && bracket_pair.start.chars().count() == 1
4069 {
4070 edits.push((selection.start..selection.start, text.clone()));
4071 edits.push((
4072 selection.end..selection.end,
4073 bracket_pair.end.as_str().into(),
4074 ));
4075 bracket_inserted = true;
4076 new_selections.push((
4077 Selection {
4078 id: selection.id,
4079 start: snapshot.anchor_after(selection.start),
4080 end: snapshot.anchor_before(selection.end),
4081 reversed: selection.reversed,
4082 goal: selection.goal,
4083 },
4084 0,
4085 ));
4086 continue;
4087 }
4088 }
4089 }
4090
4091 if self.auto_replace_emoji_shortcode
4092 && selection.is_empty()
4093 && text.as_ref().ends_with(':')
4094 {
4095 if let Some(possible_emoji_short_code) =
4096 Self::find_possible_emoji_shortcode_at_position(&snapshot, selection.start)
4097 {
4098 if !possible_emoji_short_code.is_empty() {
4099 if let Some(emoji) = emojis::get_by_shortcode(&possible_emoji_short_code) {
4100 let emoji_shortcode_start = Point::new(
4101 selection.start.row,
4102 selection.start.column - possible_emoji_short_code.len() as u32 - 1,
4103 );
4104
4105 // Remove shortcode from buffer
4106 edits.push((
4107 emoji_shortcode_start..selection.start,
4108 "".to_string().into(),
4109 ));
4110 new_selections.push((
4111 Selection {
4112 id: selection.id,
4113 start: snapshot.anchor_after(emoji_shortcode_start),
4114 end: snapshot.anchor_before(selection.start),
4115 reversed: selection.reversed,
4116 goal: selection.goal,
4117 },
4118 0,
4119 ));
4120
4121 // Insert emoji
4122 let selection_start_anchor = snapshot.anchor_after(selection.start);
4123 new_selections.push((selection.map(|_| selection_start_anchor), 0));
4124 edits.push((selection.start..selection.end, emoji.to_string().into()));
4125
4126 continue;
4127 }
4128 }
4129 }
4130 }
4131
4132 // If not handling any auto-close operation, then just replace the selected
4133 // text with the given input and move the selection to the end of the
4134 // newly inserted text.
4135 let anchor = snapshot.anchor_after(selection.end);
4136 if !self.linked_edit_ranges.is_empty() {
4137 let start_anchor = snapshot.anchor_before(selection.start);
4138
4139 let is_word_char = text.chars().next().map_or(true, |char| {
4140 let classifier = snapshot
4141 .char_classifier_at(start_anchor.to_offset(&snapshot))
4142 .ignore_punctuation(true);
4143 classifier.is_word(char)
4144 });
4145
4146 if is_word_char {
4147 if let Some(ranges) = self
4148 .linked_editing_ranges_for(start_anchor.text_anchor..anchor.text_anchor, cx)
4149 {
4150 for (buffer, edits) in ranges {
4151 linked_edits
4152 .entry(buffer.clone())
4153 .or_default()
4154 .extend(edits.into_iter().map(|range| (range, text.clone())));
4155 }
4156 }
4157 } else {
4158 clear_linked_edit_ranges = true;
4159 }
4160 }
4161
4162 new_selections.push((selection.map(|_| anchor), 0));
4163 edits.push((selection.start..selection.end, text.clone()));
4164 }
4165
4166 drop(snapshot);
4167
4168 self.transact(window, cx, |this, window, cx| {
4169 if clear_linked_edit_ranges {
4170 this.linked_edit_ranges.clear();
4171 }
4172 let initial_buffer_versions =
4173 jsx_tag_auto_close::construct_initial_buffer_versions_map(this, &edits, cx);
4174
4175 this.buffer.update(cx, |buffer, cx| {
4176 buffer.edit(edits, this.autoindent_mode.clone(), cx);
4177 });
4178 for (buffer, edits) in linked_edits {
4179 buffer.update(cx, |buffer, cx| {
4180 let snapshot = buffer.snapshot();
4181 let edits = edits
4182 .into_iter()
4183 .map(|(range, text)| {
4184 use text::ToPoint as TP;
4185 let end_point = TP::to_point(&range.end, &snapshot);
4186 let start_point = TP::to_point(&range.start, &snapshot);
4187 (start_point..end_point, text)
4188 })
4189 .sorted_by_key(|(range, _)| range.start);
4190 buffer.edit(edits, None, cx);
4191 })
4192 }
4193 let new_anchor_selections = new_selections.iter().map(|e| &e.0);
4194 let new_selection_deltas = new_selections.iter().map(|e| e.1);
4195 let map = this.display_map.update(cx, |map, cx| map.snapshot(cx));
4196 let new_selections = resolve_selections::<usize, _>(new_anchor_selections, &map)
4197 .zip(new_selection_deltas)
4198 .map(|(selection, delta)| Selection {
4199 id: selection.id,
4200 start: selection.start + delta,
4201 end: selection.end + delta,
4202 reversed: selection.reversed,
4203 goal: SelectionGoal::None,
4204 })
4205 .collect::<Vec<_>>();
4206
4207 let mut i = 0;
4208 for (position, delta, selection_id, pair) in new_autoclose_regions {
4209 let position = position.to_offset(&map.buffer_snapshot) + delta;
4210 let start = map.buffer_snapshot.anchor_before(position);
4211 let end = map.buffer_snapshot.anchor_after(position);
4212 while let Some(existing_state) = this.autoclose_regions.get(i) {
4213 match existing_state.range.start.cmp(&start, &map.buffer_snapshot) {
4214 Ordering::Less => i += 1,
4215 Ordering::Greater => break,
4216 Ordering::Equal => {
4217 match end.cmp(&existing_state.range.end, &map.buffer_snapshot) {
4218 Ordering::Less => i += 1,
4219 Ordering::Equal => break,
4220 Ordering::Greater => break,
4221 }
4222 }
4223 }
4224 }
4225 this.autoclose_regions.insert(
4226 i,
4227 AutocloseRegion {
4228 selection_id,
4229 range: start..end,
4230 pair,
4231 },
4232 );
4233 }
4234
4235 let had_active_inline_completion = this.has_active_inline_completion();
4236 this.change_selections(
4237 SelectionEffects::scroll(Autoscroll::fit()).completions(false),
4238 window,
4239 cx,
4240 |s| s.select(new_selections),
4241 );
4242
4243 if !bracket_inserted {
4244 if let Some(on_type_format_task) =
4245 this.trigger_on_type_formatting(text.to_string(), window, cx)
4246 {
4247 on_type_format_task.detach_and_log_err(cx);
4248 }
4249 }
4250
4251 let editor_settings = EditorSettings::get_global(cx);
4252 if bracket_inserted
4253 && (editor_settings.auto_signature_help
4254 || editor_settings.show_signature_help_after_edits)
4255 {
4256 this.show_signature_help(&ShowSignatureHelp, window, cx);
4257 }
4258
4259 let trigger_in_words =
4260 this.show_edit_predictions_in_menu() || !had_active_inline_completion;
4261 if this.hard_wrap.is_some() {
4262 let latest: Range<Point> = this.selections.newest(cx).range();
4263 if latest.is_empty()
4264 && this
4265 .buffer()
4266 .read(cx)
4267 .snapshot(cx)
4268 .line_len(MultiBufferRow(latest.start.row))
4269 == latest.start.column
4270 {
4271 this.rewrap_impl(
4272 RewrapOptions {
4273 override_language_settings: true,
4274 preserve_existing_whitespace: true,
4275 },
4276 cx,
4277 )
4278 }
4279 }
4280 this.trigger_completion_on_input(&text, trigger_in_words, window, cx);
4281 linked_editing_ranges::refresh_linked_ranges(this, window, cx);
4282 this.refresh_inline_completion(true, false, window, cx);
4283 jsx_tag_auto_close::handle_from(this, initial_buffer_versions, window, cx);
4284 });
4285 }
4286
4287 fn find_possible_emoji_shortcode_at_position(
4288 snapshot: &MultiBufferSnapshot,
4289 position: Point,
4290 ) -> Option<String> {
4291 let mut chars = Vec::new();
4292 let mut found_colon = false;
4293 for char in snapshot.reversed_chars_at(position).take(100) {
4294 // Found a possible emoji shortcode in the middle of the buffer
4295 if found_colon {
4296 if char.is_whitespace() {
4297 chars.reverse();
4298 return Some(chars.iter().collect());
4299 }
4300 // If the previous character is not a whitespace, we are in the middle of a word
4301 // and we only want to complete the shortcode if the word is made up of other emojis
4302 let mut containing_word = String::new();
4303 for ch in snapshot
4304 .reversed_chars_at(position)
4305 .skip(chars.len() + 1)
4306 .take(100)
4307 {
4308 if ch.is_whitespace() {
4309 break;
4310 }
4311 containing_word.push(ch);
4312 }
4313 let containing_word = containing_word.chars().rev().collect::<String>();
4314 if util::word_consists_of_emojis(containing_word.as_str()) {
4315 chars.reverse();
4316 return Some(chars.iter().collect());
4317 }
4318 }
4319
4320 if char.is_whitespace() || !char.is_ascii() {
4321 return None;
4322 }
4323 if char == ':' {
4324 found_colon = true;
4325 } else {
4326 chars.push(char);
4327 }
4328 }
4329 // Found a possible emoji shortcode at the beginning of the buffer
4330 chars.reverse();
4331 Some(chars.iter().collect())
4332 }
4333
4334 pub fn newline(&mut self, _: &Newline, window: &mut Window, cx: &mut Context<Self>) {
4335 self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
4336 self.transact(window, cx, |this, window, cx| {
4337 let (edits_with_flags, selection_info): (Vec<_>, Vec<_>) = {
4338 let selections = this.selections.all::<usize>(cx);
4339 let multi_buffer = this.buffer.read(cx);
4340 let buffer = multi_buffer.snapshot(cx);
4341 selections
4342 .iter()
4343 .map(|selection| {
4344 let start_point = selection.start.to_point(&buffer);
4345 let mut existing_indent =
4346 buffer.indent_size_for_line(MultiBufferRow(start_point.row));
4347 existing_indent.len = cmp::min(existing_indent.len, start_point.column);
4348 let start = selection.start;
4349 let end = selection.end;
4350 let selection_is_empty = start == end;
4351 let language_scope = buffer.language_scope_at(start);
4352 let (
4353 comment_delimiter,
4354 doc_delimiter,
4355 insert_extra_newline,
4356 indent_on_newline,
4357 indent_on_extra_newline,
4358 ) = if let Some(language) = &language_scope {
4359 let mut insert_extra_newline =
4360 insert_extra_newline_brackets(&buffer, start..end, language)
4361 || insert_extra_newline_tree_sitter(&buffer, start..end);
4362
4363 // Comment extension on newline is allowed only for cursor selections
4364 let comment_delimiter = maybe!({
4365 if !selection_is_empty {
4366 return None;
4367 }
4368
4369 if !multi_buffer.language_settings(cx).extend_comment_on_newline {
4370 return None;
4371 }
4372
4373 let delimiters = language.line_comment_prefixes();
4374 let max_len_of_delimiter =
4375 delimiters.iter().map(|delimiter| delimiter.len()).max()?;
4376 let (snapshot, range) =
4377 buffer.buffer_line_for_row(MultiBufferRow(start_point.row))?;
4378
4379 let num_of_whitespaces = snapshot
4380 .chars_for_range(range.clone())
4381 .take_while(|c| c.is_whitespace())
4382 .count();
4383 let comment_candidate = snapshot
4384 .chars_for_range(range.clone())
4385 .skip(num_of_whitespaces)
4386 .take(max_len_of_delimiter)
4387 .collect::<String>();
4388 let (delimiter, trimmed_len) = delimiters
4389 .iter()
4390 .filter_map(|delimiter| {
4391 let prefix = delimiter.trim_end();
4392 if comment_candidate.starts_with(prefix) {
4393 Some((delimiter, prefix.len()))
4394 } else {
4395 None
4396 }
4397 })
4398 .max_by_key(|(_, len)| *len)?;
4399
4400 if let Some((block_start, _)) = language.block_comment_delimiters()
4401 {
4402 let block_start_trimmed = block_start.trim_end();
4403 if block_start_trimmed.starts_with(delimiter.trim_end()) {
4404 let line_content = snapshot
4405 .chars_for_range(range)
4406 .skip(num_of_whitespaces)
4407 .take(block_start_trimmed.len())
4408 .collect::<String>();
4409
4410 if line_content.starts_with(block_start_trimmed) {
4411 return None;
4412 }
4413 }
4414 }
4415
4416 let cursor_is_placed_after_comment_marker =
4417 num_of_whitespaces + trimmed_len <= start_point.column as usize;
4418 if cursor_is_placed_after_comment_marker {
4419 Some(delimiter.clone())
4420 } else {
4421 None
4422 }
4423 });
4424
4425 let mut indent_on_newline = IndentSize::spaces(0);
4426 let mut indent_on_extra_newline = IndentSize::spaces(0);
4427
4428 let doc_delimiter = maybe!({
4429 if !selection_is_empty {
4430 return None;
4431 }
4432
4433 if !multi_buffer.language_settings(cx).extend_comment_on_newline {
4434 return None;
4435 }
4436
4437 let DocumentationConfig {
4438 start: start_tag,
4439 end: end_tag,
4440 prefix: delimiter,
4441 tab_size: len,
4442 } = language.documentation()?;
4443
4444 let is_within_block_comment = buffer
4445 .language_scope_at(start_point)
4446 .is_some_and(|scope| scope.override_name() == Some("comment"));
4447 if !is_within_block_comment {
4448 return None;
4449 }
4450
4451 let (snapshot, range) =
4452 buffer.buffer_line_for_row(MultiBufferRow(start_point.row))?;
4453
4454 let num_of_whitespaces = snapshot
4455 .chars_for_range(range.clone())
4456 .take_while(|c| c.is_whitespace())
4457 .count();
4458
4459 // 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.
4460 let column = start_point.column;
4461 let cursor_is_after_start_tag = {
4462 let start_tag_len = start_tag.len();
4463 let start_tag_line = snapshot
4464 .chars_for_range(range.clone())
4465 .skip(num_of_whitespaces)
4466 .take(start_tag_len)
4467 .collect::<String>();
4468 if start_tag_line.starts_with(start_tag.as_ref()) {
4469 num_of_whitespaces + start_tag_len <= column as usize
4470 } else {
4471 false
4472 }
4473 };
4474
4475 let cursor_is_after_delimiter = {
4476 let delimiter_trim = delimiter.trim_end();
4477 let delimiter_line = snapshot
4478 .chars_for_range(range.clone())
4479 .skip(num_of_whitespaces)
4480 .take(delimiter_trim.len())
4481 .collect::<String>();
4482 if delimiter_line.starts_with(delimiter_trim) {
4483 num_of_whitespaces + delimiter_trim.len() <= column as usize
4484 } else {
4485 false
4486 }
4487 };
4488
4489 let cursor_is_before_end_tag_if_exists = {
4490 let mut char_position = 0u32;
4491 let mut end_tag_offset = None;
4492
4493 'outer: for chunk in snapshot.text_for_range(range.clone()) {
4494 if let Some(byte_pos) = chunk.find(&**end_tag) {
4495 let chars_before_match =
4496 chunk[..byte_pos].chars().count() as u32;
4497 end_tag_offset =
4498 Some(char_position + chars_before_match);
4499 break 'outer;
4500 }
4501 char_position += chunk.chars().count() as u32;
4502 }
4503
4504 if let Some(end_tag_offset) = end_tag_offset {
4505 let cursor_is_before_end_tag = column <= end_tag_offset;
4506 if cursor_is_after_start_tag {
4507 if cursor_is_before_end_tag {
4508 insert_extra_newline = true;
4509 }
4510 let cursor_is_at_start_of_end_tag =
4511 column == end_tag_offset;
4512 if cursor_is_at_start_of_end_tag {
4513 indent_on_extra_newline.len = (*len).into();
4514 }
4515 }
4516 cursor_is_before_end_tag
4517 } else {
4518 true
4519 }
4520 };
4521
4522 if (cursor_is_after_start_tag || cursor_is_after_delimiter)
4523 && cursor_is_before_end_tag_if_exists
4524 {
4525 if cursor_is_after_start_tag {
4526 indent_on_newline.len = (*len).into();
4527 }
4528 Some(delimiter.clone())
4529 } else {
4530 None
4531 }
4532 });
4533
4534 (
4535 comment_delimiter,
4536 doc_delimiter,
4537 insert_extra_newline,
4538 indent_on_newline,
4539 indent_on_extra_newline,
4540 )
4541 } else {
4542 (
4543 None,
4544 None,
4545 false,
4546 IndentSize::default(),
4547 IndentSize::default(),
4548 )
4549 };
4550
4551 let prevent_auto_indent = doc_delimiter.is_some();
4552 let delimiter = comment_delimiter.or(doc_delimiter);
4553
4554 let capacity_for_delimiter =
4555 delimiter.as_deref().map(str::len).unwrap_or_default();
4556 let mut new_text = String::with_capacity(
4557 1 + capacity_for_delimiter
4558 + existing_indent.len as usize
4559 + indent_on_newline.len as usize
4560 + indent_on_extra_newline.len as usize,
4561 );
4562 new_text.push('\n');
4563 new_text.extend(existing_indent.chars());
4564 new_text.extend(indent_on_newline.chars());
4565
4566 if let Some(delimiter) = &delimiter {
4567 new_text.push_str(delimiter);
4568 }
4569
4570 if insert_extra_newline {
4571 new_text.push('\n');
4572 new_text.extend(existing_indent.chars());
4573 new_text.extend(indent_on_extra_newline.chars());
4574 }
4575
4576 let anchor = buffer.anchor_after(end);
4577 let new_selection = selection.map(|_| anchor);
4578 (
4579 ((start..end, new_text), prevent_auto_indent),
4580 (insert_extra_newline, new_selection),
4581 )
4582 })
4583 .unzip()
4584 };
4585
4586 let mut auto_indent_edits = Vec::new();
4587 let mut edits = Vec::new();
4588 for (edit, prevent_auto_indent) in edits_with_flags {
4589 if prevent_auto_indent {
4590 edits.push(edit);
4591 } else {
4592 auto_indent_edits.push(edit);
4593 }
4594 }
4595 if !edits.is_empty() {
4596 this.edit(edits, cx);
4597 }
4598 if !auto_indent_edits.is_empty() {
4599 this.edit_with_autoindent(auto_indent_edits, cx);
4600 }
4601
4602 let buffer = this.buffer.read(cx).snapshot(cx);
4603 let new_selections = selection_info
4604 .into_iter()
4605 .map(|(extra_newline_inserted, new_selection)| {
4606 let mut cursor = new_selection.end.to_point(&buffer);
4607 if extra_newline_inserted {
4608 cursor.row -= 1;
4609 cursor.column = buffer.line_len(MultiBufferRow(cursor.row));
4610 }
4611 new_selection.map(|_| cursor)
4612 })
4613 .collect();
4614
4615 this.change_selections(Default::default(), window, cx, |s| s.select(new_selections));
4616 this.refresh_inline_completion(true, false, window, cx);
4617 });
4618 }
4619
4620 pub fn newline_above(&mut self, _: &NewlineAbove, window: &mut Window, cx: &mut Context<Self>) {
4621 self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
4622
4623 let buffer = self.buffer.read(cx);
4624 let snapshot = buffer.snapshot(cx);
4625
4626 let mut edits = Vec::new();
4627 let mut rows = Vec::new();
4628
4629 for (rows_inserted, selection) in self.selections.all_adjusted(cx).into_iter().enumerate() {
4630 let cursor = selection.head();
4631 let row = cursor.row;
4632
4633 let start_of_line = snapshot.clip_point(Point::new(row, 0), Bias::Left);
4634
4635 let newline = "\n".to_string();
4636 edits.push((start_of_line..start_of_line, newline));
4637
4638 rows.push(row + rows_inserted as u32);
4639 }
4640
4641 self.transact(window, cx, |editor, window, cx| {
4642 editor.edit(edits, cx);
4643
4644 editor.change_selections(Default::default(), window, cx, |s| {
4645 let mut index = 0;
4646 s.move_cursors_with(|map, _, _| {
4647 let row = rows[index];
4648 index += 1;
4649
4650 let point = Point::new(row, 0);
4651 let boundary = map.next_line_boundary(point).1;
4652 let clipped = map.clip_point(boundary, Bias::Left);
4653
4654 (clipped, SelectionGoal::None)
4655 });
4656 });
4657
4658 let mut indent_edits = Vec::new();
4659 let multibuffer_snapshot = editor.buffer.read(cx).snapshot(cx);
4660 for row in rows {
4661 let indents = multibuffer_snapshot.suggested_indents(row..row + 1, cx);
4662 for (row, indent) in indents {
4663 if indent.len == 0 {
4664 continue;
4665 }
4666
4667 let text = match indent.kind {
4668 IndentKind::Space => " ".repeat(indent.len as usize),
4669 IndentKind::Tab => "\t".repeat(indent.len as usize),
4670 };
4671 let point = Point::new(row.0, 0);
4672 indent_edits.push((point..point, text));
4673 }
4674 }
4675 editor.edit(indent_edits, cx);
4676 });
4677 }
4678
4679 pub fn newline_below(&mut self, _: &NewlineBelow, window: &mut Window, cx: &mut Context<Self>) {
4680 self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
4681
4682 let buffer = self.buffer.read(cx);
4683 let snapshot = buffer.snapshot(cx);
4684
4685 let mut edits = Vec::new();
4686 let mut rows = Vec::new();
4687 let mut rows_inserted = 0;
4688
4689 for selection in self.selections.all_adjusted(cx) {
4690 let cursor = selection.head();
4691 let row = cursor.row;
4692
4693 let point = Point::new(row + 1, 0);
4694 let start_of_line = snapshot.clip_point(point, Bias::Left);
4695
4696 let newline = "\n".to_string();
4697 edits.push((start_of_line..start_of_line, newline));
4698
4699 rows_inserted += 1;
4700 rows.push(row + rows_inserted);
4701 }
4702
4703 self.transact(window, cx, |editor, window, cx| {
4704 editor.edit(edits, cx);
4705
4706 editor.change_selections(Default::default(), window, cx, |s| {
4707 let mut index = 0;
4708 s.move_cursors_with(|map, _, _| {
4709 let row = rows[index];
4710 index += 1;
4711
4712 let point = Point::new(row, 0);
4713 let boundary = map.next_line_boundary(point).1;
4714 let clipped = map.clip_point(boundary, Bias::Left);
4715
4716 (clipped, SelectionGoal::None)
4717 });
4718 });
4719
4720 let mut indent_edits = Vec::new();
4721 let multibuffer_snapshot = editor.buffer.read(cx).snapshot(cx);
4722 for row in rows {
4723 let indents = multibuffer_snapshot.suggested_indents(row..row + 1, cx);
4724 for (row, indent) in indents {
4725 if indent.len == 0 {
4726 continue;
4727 }
4728
4729 let text = match indent.kind {
4730 IndentKind::Space => " ".repeat(indent.len as usize),
4731 IndentKind::Tab => "\t".repeat(indent.len as usize),
4732 };
4733 let point = Point::new(row.0, 0);
4734 indent_edits.push((point..point, text));
4735 }
4736 }
4737 editor.edit(indent_edits, cx);
4738 });
4739 }
4740
4741 pub fn insert(&mut self, text: &str, window: &mut Window, cx: &mut Context<Self>) {
4742 let autoindent = text.is_empty().not().then(|| AutoindentMode::Block {
4743 original_indent_columns: Vec::new(),
4744 });
4745 self.insert_with_autoindent_mode(text, autoindent, window, cx);
4746 }
4747
4748 fn insert_with_autoindent_mode(
4749 &mut self,
4750 text: &str,
4751 autoindent_mode: Option<AutoindentMode>,
4752 window: &mut Window,
4753 cx: &mut Context<Self>,
4754 ) {
4755 if self.read_only(cx) {
4756 return;
4757 }
4758
4759 let text: Arc<str> = text.into();
4760 self.transact(window, cx, |this, window, cx| {
4761 let old_selections = this.selections.all_adjusted(cx);
4762 let selection_anchors = this.buffer.update(cx, |buffer, cx| {
4763 let anchors = {
4764 let snapshot = buffer.read(cx);
4765 old_selections
4766 .iter()
4767 .map(|s| {
4768 let anchor = snapshot.anchor_after(s.head());
4769 s.map(|_| anchor)
4770 })
4771 .collect::<Vec<_>>()
4772 };
4773 buffer.edit(
4774 old_selections
4775 .iter()
4776 .map(|s| (s.start..s.end, text.clone())),
4777 autoindent_mode,
4778 cx,
4779 );
4780 anchors
4781 });
4782
4783 this.change_selections(Default::default(), window, cx, |s| {
4784 s.select_anchors(selection_anchors);
4785 });
4786
4787 cx.notify();
4788 });
4789 }
4790
4791 fn trigger_completion_on_input(
4792 &mut self,
4793 text: &str,
4794 trigger_in_words: bool,
4795 window: &mut Window,
4796 cx: &mut Context<Self>,
4797 ) {
4798 let completions_source = self
4799 .context_menu
4800 .borrow()
4801 .as_ref()
4802 .and_then(|menu| match menu {
4803 CodeContextMenu::Completions(completions_menu) => Some(completions_menu.source),
4804 CodeContextMenu::CodeActions(_) => None,
4805 });
4806
4807 match completions_source {
4808 Some(CompletionsMenuSource::Words) => {
4809 self.show_word_completions(&ShowWordCompletions, window, cx)
4810 }
4811 Some(CompletionsMenuSource::Normal)
4812 | Some(CompletionsMenuSource::SnippetChoices)
4813 | None
4814 if self.is_completion_trigger(
4815 text,
4816 trigger_in_words,
4817 completions_source.is_some(),
4818 cx,
4819 ) =>
4820 {
4821 self.show_completions(
4822 &ShowCompletions {
4823 trigger: Some(text.to_owned()).filter(|x| !x.is_empty()),
4824 },
4825 window,
4826 cx,
4827 )
4828 }
4829 _ => {
4830 self.hide_context_menu(window, cx);
4831 }
4832 }
4833 }
4834
4835 fn is_completion_trigger(
4836 &self,
4837 text: &str,
4838 trigger_in_words: bool,
4839 menu_is_open: bool,
4840 cx: &mut Context<Self>,
4841 ) -> bool {
4842 let position = self.selections.newest_anchor().head();
4843 let multibuffer = self.buffer.read(cx);
4844 let Some(buffer) = position
4845 .buffer_id
4846 .and_then(|buffer_id| multibuffer.buffer(buffer_id).clone())
4847 else {
4848 return false;
4849 };
4850
4851 if let Some(completion_provider) = &self.completion_provider {
4852 completion_provider.is_completion_trigger(
4853 &buffer,
4854 position.text_anchor,
4855 text,
4856 trigger_in_words,
4857 menu_is_open,
4858 cx,
4859 )
4860 } else {
4861 false
4862 }
4863 }
4864
4865 /// If any empty selections is touching the start of its innermost containing autoclose
4866 /// region, expand it to select the brackets.
4867 fn select_autoclose_pair(&mut self, window: &mut Window, cx: &mut Context<Self>) {
4868 let selections = self.selections.all::<usize>(cx);
4869 let buffer = self.buffer.read(cx).read(cx);
4870 let new_selections = self
4871 .selections_with_autoclose_regions(selections, &buffer)
4872 .map(|(mut selection, region)| {
4873 if !selection.is_empty() {
4874 return selection;
4875 }
4876
4877 if let Some(region) = region {
4878 let mut range = region.range.to_offset(&buffer);
4879 if selection.start == range.start && range.start >= region.pair.start.len() {
4880 range.start -= region.pair.start.len();
4881 if buffer.contains_str_at(range.start, ®ion.pair.start)
4882 && buffer.contains_str_at(range.end, ®ion.pair.end)
4883 {
4884 range.end += region.pair.end.len();
4885 selection.start = range.start;
4886 selection.end = range.end;
4887
4888 return selection;
4889 }
4890 }
4891 }
4892
4893 let always_treat_brackets_as_autoclosed = buffer
4894 .language_settings_at(selection.start, cx)
4895 .always_treat_brackets_as_autoclosed;
4896
4897 if !always_treat_brackets_as_autoclosed {
4898 return selection;
4899 }
4900
4901 if let Some(scope) = buffer.language_scope_at(selection.start) {
4902 for (pair, enabled) in scope.brackets() {
4903 if !enabled || !pair.close {
4904 continue;
4905 }
4906
4907 if buffer.contains_str_at(selection.start, &pair.end) {
4908 let pair_start_len = pair.start.len();
4909 if buffer.contains_str_at(
4910 selection.start.saturating_sub(pair_start_len),
4911 &pair.start,
4912 ) {
4913 selection.start -= pair_start_len;
4914 selection.end += pair.end.len();
4915
4916 return selection;
4917 }
4918 }
4919 }
4920 }
4921
4922 selection
4923 })
4924 .collect();
4925
4926 drop(buffer);
4927 self.change_selections(SelectionEffects::no_scroll(), window, cx, |selections| {
4928 selections.select(new_selections)
4929 });
4930 }
4931
4932 /// Iterate the given selections, and for each one, find the smallest surrounding
4933 /// autoclose region. This uses the ordering of the selections and the autoclose
4934 /// regions to avoid repeated comparisons.
4935 fn selections_with_autoclose_regions<'a, D: ToOffset + Clone>(
4936 &'a self,
4937 selections: impl IntoIterator<Item = Selection<D>>,
4938 buffer: &'a MultiBufferSnapshot,
4939 ) -> impl Iterator<Item = (Selection<D>, Option<&'a AutocloseRegion>)> {
4940 let mut i = 0;
4941 let mut regions = self.autoclose_regions.as_slice();
4942 selections.into_iter().map(move |selection| {
4943 let range = selection.start.to_offset(buffer)..selection.end.to_offset(buffer);
4944
4945 let mut enclosing = None;
4946 while let Some(pair_state) = regions.get(i) {
4947 if pair_state.range.end.to_offset(buffer) < range.start {
4948 regions = ®ions[i + 1..];
4949 i = 0;
4950 } else if pair_state.range.start.to_offset(buffer) > range.end {
4951 break;
4952 } else {
4953 if pair_state.selection_id == selection.id {
4954 enclosing = Some(pair_state);
4955 }
4956 i += 1;
4957 }
4958 }
4959
4960 (selection, enclosing)
4961 })
4962 }
4963
4964 /// Remove any autoclose regions that no longer contain their selection.
4965 fn invalidate_autoclose_regions(
4966 &mut self,
4967 mut selections: &[Selection<Anchor>],
4968 buffer: &MultiBufferSnapshot,
4969 ) {
4970 self.autoclose_regions.retain(|state| {
4971 let mut i = 0;
4972 while let Some(selection) = selections.get(i) {
4973 if selection.end.cmp(&state.range.start, buffer).is_lt() {
4974 selections = &selections[1..];
4975 continue;
4976 }
4977 if selection.start.cmp(&state.range.end, buffer).is_gt() {
4978 break;
4979 }
4980 if selection.id == state.selection_id {
4981 return true;
4982 } else {
4983 i += 1;
4984 }
4985 }
4986 false
4987 });
4988 }
4989
4990 fn completion_query(buffer: &MultiBufferSnapshot, position: impl ToOffset) -> Option<String> {
4991 let offset = position.to_offset(buffer);
4992 let (word_range, kind) = buffer.surrounding_word(offset, true);
4993 if offset > word_range.start && kind == Some(CharKind::Word) {
4994 Some(
4995 buffer
4996 .text_for_range(word_range.start..offset)
4997 .collect::<String>(),
4998 )
4999 } else {
5000 None
5001 }
5002 }
5003
5004 pub fn toggle_inline_values(
5005 &mut self,
5006 _: &ToggleInlineValues,
5007 _: &mut Window,
5008 cx: &mut Context<Self>,
5009 ) {
5010 self.inline_value_cache.enabled = !self.inline_value_cache.enabled;
5011
5012 self.refresh_inline_values(cx);
5013 }
5014
5015 pub fn toggle_inlay_hints(
5016 &mut self,
5017 _: &ToggleInlayHints,
5018 _: &mut Window,
5019 cx: &mut Context<Self>,
5020 ) {
5021 self.refresh_inlay_hints(
5022 InlayHintRefreshReason::Toggle(!self.inlay_hints_enabled()),
5023 cx,
5024 );
5025 }
5026
5027 pub fn inlay_hints_enabled(&self) -> bool {
5028 self.inlay_hint_cache.enabled
5029 }
5030
5031 pub fn inline_values_enabled(&self) -> bool {
5032 self.inline_value_cache.enabled
5033 }
5034
5035 #[cfg(any(test, feature = "test-support"))]
5036 pub fn inline_value_inlays(&self, cx: &App) -> Vec<Inlay> {
5037 self.display_map
5038 .read(cx)
5039 .current_inlays()
5040 .filter(|inlay| matches!(inlay.id, InlayId::DebuggerValue(_)))
5041 .cloned()
5042 .collect()
5043 }
5044
5045 #[cfg(any(test, feature = "test-support"))]
5046 pub fn all_inlays(&self, cx: &App) -> Vec<Inlay> {
5047 self.display_map
5048 .read(cx)
5049 .current_inlays()
5050 .cloned()
5051 .collect()
5052 }
5053
5054 fn refresh_inlay_hints(&mut self, reason: InlayHintRefreshReason, cx: &mut Context<Self>) {
5055 if self.semantics_provider.is_none() || !self.mode.is_full() {
5056 return;
5057 }
5058
5059 let reason_description = reason.description();
5060 let ignore_debounce = matches!(
5061 reason,
5062 InlayHintRefreshReason::SettingsChange(_)
5063 | InlayHintRefreshReason::Toggle(_)
5064 | InlayHintRefreshReason::ExcerptsRemoved(_)
5065 | InlayHintRefreshReason::ModifiersChanged(_)
5066 );
5067 let (invalidate_cache, required_languages) = match reason {
5068 InlayHintRefreshReason::ModifiersChanged(enabled) => {
5069 match self.inlay_hint_cache.modifiers_override(enabled) {
5070 Some(enabled) => {
5071 if enabled {
5072 (InvalidationStrategy::RefreshRequested, None)
5073 } else {
5074 self.splice_inlays(
5075 &self
5076 .visible_inlay_hints(cx)
5077 .iter()
5078 .map(|inlay| inlay.id)
5079 .collect::<Vec<InlayId>>(),
5080 Vec::new(),
5081 cx,
5082 );
5083 return;
5084 }
5085 }
5086 None => return,
5087 }
5088 }
5089 InlayHintRefreshReason::Toggle(enabled) => {
5090 if self.inlay_hint_cache.toggle(enabled) {
5091 if enabled {
5092 (InvalidationStrategy::RefreshRequested, None)
5093 } else {
5094 self.splice_inlays(
5095 &self
5096 .visible_inlay_hints(cx)
5097 .iter()
5098 .map(|inlay| inlay.id)
5099 .collect::<Vec<InlayId>>(),
5100 Vec::new(),
5101 cx,
5102 );
5103 return;
5104 }
5105 } else {
5106 return;
5107 }
5108 }
5109 InlayHintRefreshReason::SettingsChange(new_settings) => {
5110 match self.inlay_hint_cache.update_settings(
5111 &self.buffer,
5112 new_settings,
5113 self.visible_inlay_hints(cx),
5114 cx,
5115 ) {
5116 ControlFlow::Break(Some(InlaySplice {
5117 to_remove,
5118 to_insert,
5119 })) => {
5120 self.splice_inlays(&to_remove, to_insert, cx);
5121 return;
5122 }
5123 ControlFlow::Break(None) => return,
5124 ControlFlow::Continue(()) => (InvalidationStrategy::RefreshRequested, None),
5125 }
5126 }
5127 InlayHintRefreshReason::ExcerptsRemoved(excerpts_removed) => {
5128 if let Some(InlaySplice {
5129 to_remove,
5130 to_insert,
5131 }) = self.inlay_hint_cache.remove_excerpts(&excerpts_removed)
5132 {
5133 self.splice_inlays(&to_remove, to_insert, cx);
5134 }
5135 self.display_map.update(cx, |display_map, _| {
5136 display_map.remove_inlays_for_excerpts(&excerpts_removed)
5137 });
5138 return;
5139 }
5140 InlayHintRefreshReason::NewLinesShown => (InvalidationStrategy::None, None),
5141 InlayHintRefreshReason::BufferEdited(buffer_languages) => {
5142 (InvalidationStrategy::BufferEdited, Some(buffer_languages))
5143 }
5144 InlayHintRefreshReason::RefreshRequested => {
5145 (InvalidationStrategy::RefreshRequested, None)
5146 }
5147 };
5148
5149 if let Some(InlaySplice {
5150 to_remove,
5151 to_insert,
5152 }) = self.inlay_hint_cache.spawn_hint_refresh(
5153 reason_description,
5154 self.visible_excerpts(required_languages.as_ref(), cx),
5155 invalidate_cache,
5156 ignore_debounce,
5157 cx,
5158 ) {
5159 self.splice_inlays(&to_remove, to_insert, cx);
5160 }
5161 }
5162
5163 fn visible_inlay_hints(&self, cx: &Context<Editor>) -> Vec<Inlay> {
5164 self.display_map
5165 .read(cx)
5166 .current_inlays()
5167 .filter(move |inlay| matches!(inlay.id, InlayId::Hint(_)))
5168 .cloned()
5169 .collect()
5170 }
5171
5172 pub fn visible_excerpts(
5173 &self,
5174 restrict_to_languages: Option<&HashSet<Arc<Language>>>,
5175 cx: &mut Context<Editor>,
5176 ) -> HashMap<ExcerptId, (Entity<Buffer>, clock::Global, Range<usize>)> {
5177 let Some(project) = self.project.as_ref() else {
5178 return HashMap::default();
5179 };
5180 let project = project.read(cx);
5181 let multi_buffer = self.buffer().read(cx);
5182 let multi_buffer_snapshot = multi_buffer.snapshot(cx);
5183 let multi_buffer_visible_start = self
5184 .scroll_manager
5185 .anchor()
5186 .anchor
5187 .to_point(&multi_buffer_snapshot);
5188 let multi_buffer_visible_end = multi_buffer_snapshot.clip_point(
5189 multi_buffer_visible_start
5190 + Point::new(self.visible_line_count().unwrap_or(0.).ceil() as u32, 0),
5191 Bias::Left,
5192 );
5193 let multi_buffer_visible_range = multi_buffer_visible_start..multi_buffer_visible_end;
5194 multi_buffer_snapshot
5195 .range_to_buffer_ranges(multi_buffer_visible_range)
5196 .into_iter()
5197 .filter(|(_, excerpt_visible_range, _)| !excerpt_visible_range.is_empty())
5198 .filter_map(|(buffer, excerpt_visible_range, excerpt_id)| {
5199 let buffer_file = project::File::from_dyn(buffer.file())?;
5200 let buffer_worktree = project.worktree_for_id(buffer_file.worktree_id(cx), cx)?;
5201 let worktree_entry = buffer_worktree
5202 .read(cx)
5203 .entry_for_id(buffer_file.project_entry_id(cx)?)?;
5204 if worktree_entry.is_ignored {
5205 return None;
5206 }
5207
5208 let language = buffer.language()?;
5209 if let Some(restrict_to_languages) = restrict_to_languages {
5210 if !restrict_to_languages.contains(language) {
5211 return None;
5212 }
5213 }
5214 Some((
5215 excerpt_id,
5216 (
5217 multi_buffer.buffer(buffer.remote_id()).unwrap(),
5218 buffer.version().clone(),
5219 excerpt_visible_range,
5220 ),
5221 ))
5222 })
5223 .collect()
5224 }
5225
5226 pub fn text_layout_details(&self, window: &mut Window) -> TextLayoutDetails {
5227 TextLayoutDetails {
5228 text_system: window.text_system().clone(),
5229 editor_style: self.style.clone().unwrap(),
5230 rem_size: window.rem_size(),
5231 scroll_anchor: self.scroll_manager.anchor(),
5232 visible_rows: self.visible_line_count(),
5233 vertical_scroll_margin: self.scroll_manager.vertical_scroll_margin,
5234 }
5235 }
5236
5237 pub fn splice_inlays(
5238 &self,
5239 to_remove: &[InlayId],
5240 to_insert: Vec<Inlay>,
5241 cx: &mut Context<Self>,
5242 ) {
5243 self.display_map.update(cx, |display_map, cx| {
5244 display_map.splice_inlays(to_remove, to_insert, cx)
5245 });
5246 cx.notify();
5247 }
5248
5249 fn trigger_on_type_formatting(
5250 &self,
5251 input: String,
5252 window: &mut Window,
5253 cx: &mut Context<Self>,
5254 ) -> Option<Task<Result<()>>> {
5255 if input.len() != 1 {
5256 return None;
5257 }
5258
5259 let project = self.project.as_ref()?;
5260 let position = self.selections.newest_anchor().head();
5261 let (buffer, buffer_position) = self
5262 .buffer
5263 .read(cx)
5264 .text_anchor_for_position(position, cx)?;
5265
5266 let settings = language_settings::language_settings(
5267 buffer
5268 .read(cx)
5269 .language_at(buffer_position)
5270 .map(|l| l.name()),
5271 buffer.read(cx).file(),
5272 cx,
5273 );
5274 if !settings.use_on_type_format {
5275 return None;
5276 }
5277
5278 // OnTypeFormatting returns a list of edits, no need to pass them between Zed instances,
5279 // hence we do LSP request & edit on host side only — add formats to host's history.
5280 let push_to_lsp_host_history = true;
5281 // If this is not the host, append its history with new edits.
5282 let push_to_client_history = project.read(cx).is_via_collab();
5283
5284 let on_type_formatting = project.update(cx, |project, cx| {
5285 project.on_type_format(
5286 buffer.clone(),
5287 buffer_position,
5288 input,
5289 push_to_lsp_host_history,
5290 cx,
5291 )
5292 });
5293 Some(cx.spawn_in(window, async move |editor, cx| {
5294 if let Some(transaction) = on_type_formatting.await? {
5295 if push_to_client_history {
5296 buffer
5297 .update(cx, |buffer, _| {
5298 buffer.push_transaction(transaction, Instant::now());
5299 buffer.finalize_last_transaction();
5300 })
5301 .ok();
5302 }
5303 editor.update(cx, |editor, cx| {
5304 editor.refresh_document_highlights(cx);
5305 })?;
5306 }
5307 Ok(())
5308 }))
5309 }
5310
5311 pub fn show_word_completions(
5312 &mut self,
5313 _: &ShowWordCompletions,
5314 window: &mut Window,
5315 cx: &mut Context<Self>,
5316 ) {
5317 self.open_or_update_completions_menu(Some(CompletionsMenuSource::Words), None, window, cx);
5318 }
5319
5320 pub fn show_completions(
5321 &mut self,
5322 options: &ShowCompletions,
5323 window: &mut Window,
5324 cx: &mut Context<Self>,
5325 ) {
5326 self.open_or_update_completions_menu(None, options.trigger.as_deref(), window, cx);
5327 }
5328
5329 fn open_or_update_completions_menu(
5330 &mut self,
5331 requested_source: Option<CompletionsMenuSource>,
5332 trigger: Option<&str>,
5333 window: &mut Window,
5334 cx: &mut Context<Self>,
5335 ) {
5336 if self.pending_rename.is_some() {
5337 return;
5338 }
5339
5340 let multibuffer_snapshot = self.buffer.read(cx).read(cx);
5341
5342 // Typically `start` == `end`, but with snippet tabstop choices the default choice is
5343 // inserted and selected. To handle that case, the start of the selection is used so that
5344 // the menu starts with all choices.
5345 let position = self
5346 .selections
5347 .newest_anchor()
5348 .start
5349 .bias_right(&multibuffer_snapshot);
5350 if position.diff_base_anchor.is_some() {
5351 return;
5352 }
5353 let (buffer, buffer_position) =
5354 if let Some(output) = self.buffer.read(cx).text_anchor_for_position(position, cx) {
5355 output
5356 } else {
5357 return;
5358 };
5359 let buffer_snapshot = buffer.read(cx).snapshot();
5360
5361 let query: Option<Arc<String>> =
5362 Self::completion_query(&multibuffer_snapshot, position).map(|query| query.into());
5363
5364 drop(multibuffer_snapshot);
5365
5366 let provider = match requested_source {
5367 Some(CompletionsMenuSource::Normal) | None => self.completion_provider.clone(),
5368 Some(CompletionsMenuSource::Words) => None,
5369 Some(CompletionsMenuSource::SnippetChoices) => {
5370 log::error!("bug: SnippetChoices requested_source is not handled");
5371 None
5372 }
5373 };
5374
5375 let sort_completions = provider
5376 .as_ref()
5377 .map_or(false, |provider| provider.sort_completions());
5378
5379 let filter_completions = provider
5380 .as_ref()
5381 .map_or(true, |provider| provider.filter_completions());
5382
5383 let trigger_kind = match trigger {
5384 Some(trigger) if buffer.read(cx).completion_triggers().contains(trigger) => {
5385 CompletionTriggerKind::TRIGGER_CHARACTER
5386 }
5387 _ => CompletionTriggerKind::INVOKED,
5388 };
5389 let completion_context = CompletionContext {
5390 trigger_character: trigger.and_then(|trigger| {
5391 if trigger_kind == CompletionTriggerKind::TRIGGER_CHARACTER {
5392 Some(String::from(trigger))
5393 } else {
5394 None
5395 }
5396 }),
5397 trigger_kind,
5398 };
5399
5400 // Hide the current completions menu when a trigger char is typed. Without this, cached
5401 // completions from before the trigger char may be reused (#32774). Snippet choices could
5402 // involve trigger chars, so this is skipped in that case.
5403 if trigger_kind == CompletionTriggerKind::TRIGGER_CHARACTER && self.snippet_stack.is_empty()
5404 {
5405 let menu_is_open = matches!(
5406 self.context_menu.borrow().as_ref(),
5407 Some(CodeContextMenu::Completions(_))
5408 );
5409 if menu_is_open {
5410 self.hide_context_menu(window, cx);
5411 }
5412 }
5413
5414 if let Some(CodeContextMenu::Completions(menu)) = self.context_menu.borrow_mut().as_mut() {
5415 if filter_completions {
5416 menu.filter(query.clone(), provider.clone(), window, cx);
5417 }
5418 // When `is_incomplete` is false, no need to re-query completions when the current query
5419 // is a suffix of the initial query.
5420 if !menu.is_incomplete {
5421 // If the new query is a suffix of the old query (typing more characters) and
5422 // the previous result was complete, the existing completions can be filtered.
5423 //
5424 // Note that this is always true for snippet completions.
5425 let query_matches = match (&menu.initial_query, &query) {
5426 (Some(initial_query), Some(query)) => query.starts_with(initial_query.as_ref()),
5427 (None, _) => true,
5428 _ => false,
5429 };
5430 if query_matches {
5431 let position_matches = if menu.initial_position == position {
5432 true
5433 } else {
5434 let snapshot = self.buffer.read(cx).read(cx);
5435 menu.initial_position.to_offset(&snapshot) == position.to_offset(&snapshot)
5436 };
5437 if position_matches {
5438 return;
5439 }
5440 }
5441 }
5442 };
5443
5444 let (word_replace_range, word_to_exclude) = if let (word_range, Some(CharKind::Word)) =
5445 buffer_snapshot.surrounding_word(buffer_position)
5446 {
5447 let word_to_exclude = buffer_snapshot
5448 .text_for_range(word_range.clone())
5449 .collect::<String>();
5450 (
5451 buffer_snapshot.anchor_before(word_range.start)
5452 ..buffer_snapshot.anchor_after(buffer_position),
5453 Some(word_to_exclude),
5454 )
5455 } else {
5456 (buffer_position..buffer_position, None)
5457 };
5458
5459 let language = buffer_snapshot
5460 .language_at(buffer_position)
5461 .map(|language| language.name());
5462
5463 let completion_settings =
5464 language_settings(language.clone(), buffer_snapshot.file(), cx).completions;
5465
5466 let show_completion_documentation = buffer_snapshot
5467 .settings_at(buffer_position, cx)
5468 .show_completion_documentation;
5469
5470 // The document can be large, so stay in reasonable bounds when searching for words,
5471 // otherwise completion pop-up might be slow to appear.
5472 const WORD_LOOKUP_ROWS: u32 = 5_000;
5473 let buffer_row = text::ToPoint::to_point(&buffer_position, &buffer_snapshot).row;
5474 let min_word_search = buffer_snapshot.clip_point(
5475 Point::new(buffer_row.saturating_sub(WORD_LOOKUP_ROWS), 0),
5476 Bias::Left,
5477 );
5478 let max_word_search = buffer_snapshot.clip_point(
5479 Point::new(buffer_row + WORD_LOOKUP_ROWS, 0).min(buffer_snapshot.max_point()),
5480 Bias::Right,
5481 );
5482 let word_search_range = buffer_snapshot.point_to_offset(min_word_search)
5483 ..buffer_snapshot.point_to_offset(max_word_search);
5484
5485 let skip_digits = query
5486 .as_ref()
5487 .map_or(true, |query| !query.chars().any(|c| c.is_digit(10)));
5488
5489 let (mut words, provider_responses) = match &provider {
5490 Some(provider) => {
5491 let provider_responses = provider.completions(
5492 position.excerpt_id,
5493 &buffer,
5494 buffer_position,
5495 completion_context,
5496 window,
5497 cx,
5498 );
5499
5500 let words = match completion_settings.words {
5501 WordsCompletionMode::Disabled => Task::ready(BTreeMap::default()),
5502 WordsCompletionMode::Enabled | WordsCompletionMode::Fallback => cx
5503 .background_spawn(async move {
5504 buffer_snapshot.words_in_range(WordsQuery {
5505 fuzzy_contents: None,
5506 range: word_search_range,
5507 skip_digits,
5508 })
5509 }),
5510 };
5511
5512 (words, provider_responses)
5513 }
5514 None => (
5515 cx.background_spawn(async move {
5516 buffer_snapshot.words_in_range(WordsQuery {
5517 fuzzy_contents: None,
5518 range: word_search_range,
5519 skip_digits,
5520 })
5521 }),
5522 Task::ready(Ok(Vec::new())),
5523 ),
5524 };
5525
5526 let snippet_sort_order = EditorSettings::get_global(cx).snippet_sort_order;
5527
5528 let id = post_inc(&mut self.next_completion_id);
5529 let task = cx.spawn_in(window, async move |editor, cx| {
5530 let Ok(()) = editor.update(cx, |this, _| {
5531 this.completion_tasks.retain(|(task_id, _)| *task_id >= id);
5532 }) else {
5533 return;
5534 };
5535
5536 // TODO: Ideally completions from different sources would be selectively re-queried, so
5537 // that having one source with `is_incomplete: true` doesn't cause all to be re-queried.
5538 let mut completions = Vec::new();
5539 let mut is_incomplete = false;
5540 if let Some(provider_responses) = provider_responses.await.log_err() {
5541 if !provider_responses.is_empty() {
5542 for response in provider_responses {
5543 completions.extend(response.completions);
5544 is_incomplete = is_incomplete || response.is_incomplete;
5545 }
5546 if completion_settings.words == WordsCompletionMode::Fallback {
5547 words = Task::ready(BTreeMap::default());
5548 }
5549 }
5550 }
5551
5552 let mut words = words.await;
5553 if let Some(word_to_exclude) = &word_to_exclude {
5554 words.remove(word_to_exclude);
5555 }
5556 for lsp_completion in &completions {
5557 words.remove(&lsp_completion.new_text);
5558 }
5559 completions.extend(words.into_iter().map(|(word, word_range)| Completion {
5560 replace_range: word_replace_range.clone(),
5561 new_text: word.clone(),
5562 label: CodeLabel::plain(word, None),
5563 icon_path: None,
5564 documentation: None,
5565 source: CompletionSource::BufferWord {
5566 word_range,
5567 resolved: false,
5568 },
5569 insert_text_mode: Some(InsertTextMode::AS_IS),
5570 confirm: None,
5571 }));
5572
5573 let menu = if completions.is_empty() {
5574 None
5575 } else {
5576 let Ok((mut menu, matches_task)) = editor.update(cx, |editor, cx| {
5577 let languages = editor
5578 .workspace
5579 .as_ref()
5580 .and_then(|(workspace, _)| workspace.upgrade())
5581 .map(|workspace| workspace.read(cx).app_state().languages.clone());
5582 let menu = CompletionsMenu::new(
5583 id,
5584 requested_source.unwrap_or(CompletionsMenuSource::Normal),
5585 sort_completions,
5586 show_completion_documentation,
5587 position,
5588 query.clone(),
5589 is_incomplete,
5590 buffer.clone(),
5591 completions.into(),
5592 snippet_sort_order,
5593 languages,
5594 language,
5595 cx,
5596 );
5597
5598 let query = if filter_completions { query } else { None };
5599 let matches_task = if let Some(query) = query {
5600 menu.do_async_filtering(query, cx)
5601 } else {
5602 Task::ready(menu.unfiltered_matches())
5603 };
5604 (menu, matches_task)
5605 }) else {
5606 return;
5607 };
5608
5609 let matches = matches_task.await;
5610
5611 let Ok(()) = editor.update_in(cx, |editor, window, cx| {
5612 // Newer menu already set, so exit.
5613 match editor.context_menu.borrow().as_ref() {
5614 Some(CodeContextMenu::Completions(prev_menu)) => {
5615 if prev_menu.id > id {
5616 return;
5617 }
5618 }
5619 _ => {}
5620 };
5621
5622 // Only valid to take prev_menu because it the new menu is immediately set
5623 // below, or the menu is hidden.
5624 match editor.context_menu.borrow_mut().take() {
5625 Some(CodeContextMenu::Completions(prev_menu)) => {
5626 let position_matches =
5627 if prev_menu.initial_position == menu.initial_position {
5628 true
5629 } else {
5630 let snapshot = editor.buffer.read(cx).read(cx);
5631 prev_menu.initial_position.to_offset(&snapshot)
5632 == menu.initial_position.to_offset(&snapshot)
5633 };
5634 if position_matches {
5635 // Preserve markdown cache before `set_filter_results` because it will
5636 // try to populate the documentation cache.
5637 menu.preserve_markdown_cache(prev_menu);
5638 }
5639 }
5640 _ => {}
5641 };
5642
5643 menu.set_filter_results(matches, provider, window, cx);
5644 }) else {
5645 return;
5646 };
5647
5648 menu.visible().then_some(menu)
5649 };
5650
5651 editor
5652 .update_in(cx, |editor, window, cx| {
5653 if editor.focus_handle.is_focused(window) {
5654 if let Some(menu) = menu {
5655 *editor.context_menu.borrow_mut() =
5656 Some(CodeContextMenu::Completions(menu));
5657
5658 crate::hover_popover::hide_hover(editor, cx);
5659 if editor.show_edit_predictions_in_menu() {
5660 editor.update_visible_inline_completion(window, cx);
5661 } else {
5662 editor.discard_inline_completion(false, cx);
5663 }
5664
5665 cx.notify();
5666 return;
5667 }
5668 }
5669
5670 if editor.completion_tasks.len() <= 1 {
5671 // If there are no more completion tasks and the last menu was empty, we should hide it.
5672 let was_hidden = editor.hide_context_menu(window, cx).is_none();
5673 // If it was already hidden and we don't show inline completions in the menu, we should
5674 // also show the inline-completion when available.
5675 if was_hidden && editor.show_edit_predictions_in_menu() {
5676 editor.update_visible_inline_completion(window, cx);
5677 }
5678 }
5679 })
5680 .ok();
5681 });
5682
5683 self.completion_tasks.push((id, task));
5684 }
5685
5686 #[cfg(feature = "test-support")]
5687 pub fn current_completions(&self) -> Option<Vec<project::Completion>> {
5688 let menu = self.context_menu.borrow();
5689 if let CodeContextMenu::Completions(menu) = menu.as_ref()? {
5690 let completions = menu.completions.borrow();
5691 Some(completions.to_vec())
5692 } else {
5693 None
5694 }
5695 }
5696
5697 pub fn with_completions_menu_matching_id<R>(
5698 &self,
5699 id: CompletionId,
5700 f: impl FnOnce(Option<&mut CompletionsMenu>) -> R,
5701 ) -> R {
5702 let mut context_menu = self.context_menu.borrow_mut();
5703 let Some(CodeContextMenu::Completions(completions_menu)) = &mut *context_menu else {
5704 return f(None);
5705 };
5706 if completions_menu.id != id {
5707 return f(None);
5708 }
5709 f(Some(completions_menu))
5710 }
5711
5712 pub fn confirm_completion(
5713 &mut self,
5714 action: &ConfirmCompletion,
5715 window: &mut Window,
5716 cx: &mut Context<Self>,
5717 ) -> Option<Task<Result<()>>> {
5718 self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
5719 self.do_completion(action.item_ix, CompletionIntent::Complete, window, cx)
5720 }
5721
5722 pub fn confirm_completion_insert(
5723 &mut self,
5724 _: &ConfirmCompletionInsert,
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(None, CompletionIntent::CompleteWithInsert, window, cx)
5730 }
5731
5732 pub fn confirm_completion_replace(
5733 &mut self,
5734 _: &ConfirmCompletionReplace,
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::CompleteWithReplace, window, cx)
5740 }
5741
5742 pub fn compose_completion(
5743 &mut self,
5744 action: &ComposeCompletion,
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(action.item_ix, CompletionIntent::Compose, window, cx)
5750 }
5751
5752 fn do_completion(
5753 &mut self,
5754 item_ix: Option<usize>,
5755 intent: CompletionIntent,
5756 window: &mut Window,
5757 cx: &mut Context<Editor>,
5758 ) -> Option<Task<Result<()>>> {
5759 use language::ToOffset as _;
5760
5761 let CodeContextMenu::Completions(completions_menu) = self.hide_context_menu(window, cx)?
5762 else {
5763 return None;
5764 };
5765
5766 let candidate_id = {
5767 let entries = completions_menu.entries.borrow();
5768 let mat = entries.get(item_ix.unwrap_or(completions_menu.selected_item))?;
5769 if self.show_edit_predictions_in_menu() {
5770 self.discard_inline_completion(true, cx);
5771 }
5772 mat.candidate_id
5773 };
5774
5775 let completion = completions_menu
5776 .completions
5777 .borrow()
5778 .get(candidate_id)?
5779 .clone();
5780 cx.stop_propagation();
5781
5782 let buffer_handle = completions_menu.buffer.clone();
5783
5784 let CompletionEdit {
5785 new_text,
5786 snippet,
5787 replace_range,
5788 } = process_completion_for_edit(
5789 &completion,
5790 intent,
5791 &buffer_handle,
5792 &completions_menu.initial_position.text_anchor,
5793 cx,
5794 );
5795
5796 let buffer = buffer_handle.read(cx);
5797 let snapshot = self.buffer.read(cx).snapshot(cx);
5798 let newest_anchor = self.selections.newest_anchor();
5799 let replace_range_multibuffer = {
5800 let excerpt = snapshot.excerpt_containing(newest_anchor.range()).unwrap();
5801 let multibuffer_anchor = snapshot
5802 .anchor_in_excerpt(excerpt.id(), buffer.anchor_before(replace_range.start))
5803 .unwrap()
5804 ..snapshot
5805 .anchor_in_excerpt(excerpt.id(), buffer.anchor_before(replace_range.end))
5806 .unwrap();
5807 multibuffer_anchor.start.to_offset(&snapshot)
5808 ..multibuffer_anchor.end.to_offset(&snapshot)
5809 };
5810 if newest_anchor.head().buffer_id != Some(buffer.remote_id()) {
5811 return None;
5812 }
5813
5814 let old_text = buffer
5815 .text_for_range(replace_range.clone())
5816 .collect::<String>();
5817 let lookbehind = newest_anchor
5818 .start
5819 .text_anchor
5820 .to_offset(buffer)
5821 .saturating_sub(replace_range.start);
5822 let lookahead = replace_range
5823 .end
5824 .saturating_sub(newest_anchor.end.text_anchor.to_offset(buffer));
5825 let prefix = &old_text[..old_text.len().saturating_sub(lookahead)];
5826 let suffix = &old_text[lookbehind.min(old_text.len())..];
5827
5828 let selections = self.selections.all::<usize>(cx);
5829 let mut ranges = Vec::new();
5830 let mut linked_edits = HashMap::<_, Vec<_>>::default();
5831
5832 for selection in &selections {
5833 let range = if selection.id == newest_anchor.id {
5834 replace_range_multibuffer.clone()
5835 } else {
5836 let mut range = selection.range();
5837
5838 // if prefix is present, don't duplicate it
5839 if snapshot.contains_str_at(range.start.saturating_sub(lookbehind), prefix) {
5840 range.start = range.start.saturating_sub(lookbehind);
5841
5842 // if suffix is also present, mimic the newest cursor and replace it
5843 if selection.id != newest_anchor.id
5844 && snapshot.contains_str_at(range.end, suffix)
5845 {
5846 range.end += lookahead;
5847 }
5848 }
5849 range
5850 };
5851
5852 ranges.push(range.clone());
5853
5854 if !self.linked_edit_ranges.is_empty() {
5855 let start_anchor = snapshot.anchor_before(range.start);
5856 let end_anchor = snapshot.anchor_after(range.end);
5857 if let Some(ranges) = self
5858 .linked_editing_ranges_for(start_anchor.text_anchor..end_anchor.text_anchor, cx)
5859 {
5860 for (buffer, edits) in ranges {
5861 linked_edits
5862 .entry(buffer.clone())
5863 .or_default()
5864 .extend(edits.into_iter().map(|range| (range, new_text.to_owned())));
5865 }
5866 }
5867 }
5868 }
5869
5870 let common_prefix_len = old_text
5871 .chars()
5872 .zip(new_text.chars())
5873 .take_while(|(a, b)| a == b)
5874 .map(|(a, _)| a.len_utf8())
5875 .sum::<usize>();
5876
5877 cx.emit(EditorEvent::InputHandled {
5878 utf16_range_to_replace: None,
5879 text: new_text[common_prefix_len..].into(),
5880 });
5881
5882 self.transact(window, cx, |this, window, cx| {
5883 if let Some(mut snippet) = snippet {
5884 snippet.text = new_text.to_string();
5885 this.insert_snippet(&ranges, snippet, window, cx).log_err();
5886 } else {
5887 this.buffer.update(cx, |buffer, cx| {
5888 let auto_indent = match completion.insert_text_mode {
5889 Some(InsertTextMode::AS_IS) => None,
5890 _ => this.autoindent_mode.clone(),
5891 };
5892 let edits = ranges.into_iter().map(|range| (range, new_text.as_str()));
5893 buffer.edit(edits, auto_indent, cx);
5894 });
5895 }
5896 for (buffer, edits) in linked_edits {
5897 buffer.update(cx, |buffer, cx| {
5898 let snapshot = buffer.snapshot();
5899 let edits = edits
5900 .into_iter()
5901 .map(|(range, text)| {
5902 use text::ToPoint as TP;
5903 let end_point = TP::to_point(&range.end, &snapshot);
5904 let start_point = TP::to_point(&range.start, &snapshot);
5905 (start_point..end_point, text)
5906 })
5907 .sorted_by_key(|(range, _)| range.start);
5908 buffer.edit(edits, None, cx);
5909 })
5910 }
5911
5912 this.refresh_inline_completion(true, false, window, cx);
5913 });
5914
5915 let show_new_completions_on_confirm = completion
5916 .confirm
5917 .as_ref()
5918 .map_or(false, |confirm| confirm(intent, window, cx));
5919 if show_new_completions_on_confirm {
5920 self.show_completions(&ShowCompletions { trigger: None }, window, cx);
5921 }
5922
5923 let provider = self.completion_provider.as_ref()?;
5924 drop(completion);
5925 let apply_edits = provider.apply_additional_edits_for_completion(
5926 buffer_handle,
5927 completions_menu.completions.clone(),
5928 candidate_id,
5929 true,
5930 cx,
5931 );
5932
5933 let editor_settings = EditorSettings::get_global(cx);
5934 if editor_settings.show_signature_help_after_edits || editor_settings.auto_signature_help {
5935 // After the code completion is finished, users often want to know what signatures are needed.
5936 // so we should automatically call signature_help
5937 self.show_signature_help(&ShowSignatureHelp, window, cx);
5938 }
5939
5940 Some(cx.foreground_executor().spawn(async move {
5941 apply_edits.await?;
5942 Ok(())
5943 }))
5944 }
5945
5946 pub fn toggle_code_actions(
5947 &mut self,
5948 action: &ToggleCodeActions,
5949 window: &mut Window,
5950 cx: &mut Context<Self>,
5951 ) {
5952 let quick_launch = action.quick_launch;
5953 let mut context_menu = self.context_menu.borrow_mut();
5954 if let Some(CodeContextMenu::CodeActions(code_actions)) = context_menu.as_ref() {
5955 if code_actions.deployed_from == action.deployed_from {
5956 // Toggle if we're selecting the same one
5957 *context_menu = None;
5958 cx.notify();
5959 return;
5960 } else {
5961 // Otherwise, clear it and start a new one
5962 *context_menu = None;
5963 cx.notify();
5964 }
5965 }
5966 drop(context_menu);
5967 let snapshot = self.snapshot(window, cx);
5968 let deployed_from = action.deployed_from.clone();
5969 let action = action.clone();
5970 self.completion_tasks.clear();
5971 self.discard_inline_completion(false, cx);
5972
5973 let multibuffer_point = match &action.deployed_from {
5974 Some(CodeActionSource::Indicator(row)) | Some(CodeActionSource::RunMenu(row)) => {
5975 DisplayPoint::new(*row, 0).to_point(&snapshot)
5976 }
5977 _ => self.selections.newest::<Point>(cx).head(),
5978 };
5979 let Some((buffer, buffer_row)) = snapshot
5980 .buffer_snapshot
5981 .buffer_line_for_row(MultiBufferRow(multibuffer_point.row))
5982 .and_then(|(buffer_snapshot, range)| {
5983 self.buffer()
5984 .read(cx)
5985 .buffer(buffer_snapshot.remote_id())
5986 .map(|buffer| (buffer, range.start.row))
5987 })
5988 else {
5989 return;
5990 };
5991 let buffer_id = buffer.read(cx).remote_id();
5992 let tasks = self
5993 .tasks
5994 .get(&(buffer_id, buffer_row))
5995 .map(|t| Arc::new(t.to_owned()));
5996
5997 if !self.focus_handle.is_focused(window) {
5998 return;
5999 }
6000 let project = self.project.clone();
6001
6002 let code_actions_task = match deployed_from {
6003 Some(CodeActionSource::RunMenu(_)) => Task::ready(None),
6004 _ => self.code_actions(buffer_row, window, cx),
6005 };
6006
6007 let runnable_task = match deployed_from {
6008 Some(CodeActionSource::Indicator(_)) => Task::ready(Ok(Default::default())),
6009 _ => {
6010 let mut task_context_task = Task::ready(None);
6011 if let Some(tasks) = &tasks {
6012 if let Some(project) = project {
6013 task_context_task =
6014 Self::build_tasks_context(&project, &buffer, buffer_row, &tasks, cx);
6015 }
6016 }
6017
6018 cx.spawn_in(window, {
6019 let buffer = buffer.clone();
6020 async move |editor, cx| {
6021 let task_context = task_context_task.await;
6022
6023 let resolved_tasks =
6024 tasks
6025 .zip(task_context.clone())
6026 .map(|(tasks, task_context)| ResolvedTasks {
6027 templates: tasks.resolve(&task_context).collect(),
6028 position: snapshot.buffer_snapshot.anchor_before(Point::new(
6029 multibuffer_point.row,
6030 tasks.column,
6031 )),
6032 });
6033 let debug_scenarios = editor
6034 .update(cx, |editor, cx| {
6035 editor.debug_scenarios(&resolved_tasks, &buffer, cx)
6036 })?
6037 .await;
6038 anyhow::Ok((resolved_tasks, debug_scenarios, task_context))
6039 }
6040 })
6041 }
6042 };
6043
6044 cx.spawn_in(window, async move |editor, cx| {
6045 let (resolved_tasks, debug_scenarios, task_context) = runnable_task.await?;
6046 let code_actions = code_actions_task.await;
6047 let spawn_straight_away = quick_launch
6048 && resolved_tasks
6049 .as_ref()
6050 .map_or(false, |tasks| tasks.templates.len() == 1)
6051 && code_actions
6052 .as_ref()
6053 .map_or(true, |actions| actions.is_empty())
6054 && debug_scenarios.is_empty();
6055
6056 editor.update_in(cx, |editor, window, cx| {
6057 crate::hover_popover::hide_hover(editor, cx);
6058 let actions = CodeActionContents::new(
6059 resolved_tasks,
6060 code_actions,
6061 debug_scenarios,
6062 task_context.unwrap_or_default(),
6063 );
6064
6065 // Don't show the menu if there are no actions available
6066 if actions.is_empty() {
6067 cx.notify();
6068 return Task::ready(Ok(()));
6069 }
6070
6071 *editor.context_menu.borrow_mut() =
6072 Some(CodeContextMenu::CodeActions(CodeActionsMenu {
6073 buffer,
6074 actions,
6075 selected_item: Default::default(),
6076 scroll_handle: UniformListScrollHandle::default(),
6077 deployed_from,
6078 }));
6079 cx.notify();
6080 if spawn_straight_away {
6081 if let Some(task) = editor.confirm_code_action(
6082 &ConfirmCodeAction { item_ix: Some(0) },
6083 window,
6084 cx,
6085 ) {
6086 return task;
6087 }
6088 }
6089
6090 Task::ready(Ok(()))
6091 })
6092 })
6093 .detach_and_log_err(cx);
6094 }
6095
6096 fn debug_scenarios(
6097 &mut self,
6098 resolved_tasks: &Option<ResolvedTasks>,
6099 buffer: &Entity<Buffer>,
6100 cx: &mut App,
6101 ) -> Task<Vec<task::DebugScenario>> {
6102 maybe!({
6103 let project = self.project.as_ref()?;
6104 let dap_store = project.read(cx).dap_store();
6105 let mut scenarios = vec![];
6106 let resolved_tasks = resolved_tasks.as_ref()?;
6107 let buffer = buffer.read(cx);
6108 let language = buffer.language()?;
6109 let file = buffer.file();
6110 let debug_adapter = language_settings(language.name().into(), file, cx)
6111 .debuggers
6112 .first()
6113 .map(SharedString::from)
6114 .or_else(|| language.config().debuggers.first().map(SharedString::from))?;
6115
6116 dap_store.update(cx, |dap_store, cx| {
6117 for (_, task) in &resolved_tasks.templates {
6118 let maybe_scenario = dap_store.debug_scenario_for_build_task(
6119 task.original_task().clone(),
6120 debug_adapter.clone().into(),
6121 task.display_label().to_owned().into(),
6122 cx,
6123 );
6124 scenarios.push(maybe_scenario);
6125 }
6126 });
6127 Some(cx.background_spawn(async move {
6128 let scenarios = futures::future::join_all(scenarios)
6129 .await
6130 .into_iter()
6131 .flatten()
6132 .collect::<Vec<_>>();
6133 scenarios
6134 }))
6135 })
6136 .unwrap_or_else(|| Task::ready(vec![]))
6137 }
6138
6139 fn code_actions(
6140 &mut self,
6141 buffer_row: u32,
6142 window: &mut Window,
6143 cx: &mut Context<Self>,
6144 ) -> Task<Option<Rc<[AvailableCodeAction]>>> {
6145 let mut task = self.code_actions_task.take();
6146 cx.spawn_in(window, async move |editor, cx| {
6147 while let Some(prev_task) = task {
6148 prev_task.await.log_err();
6149 task = editor
6150 .update(cx, |this, _| this.code_actions_task.take())
6151 .ok()?;
6152 }
6153
6154 editor
6155 .update(cx, |editor, cx| {
6156 editor
6157 .available_code_actions
6158 .clone()
6159 .and_then(|(location, code_actions)| {
6160 let snapshot = location.buffer.read(cx).snapshot();
6161 let point_range = location.range.to_point(&snapshot);
6162 let point_range = point_range.start.row..=point_range.end.row;
6163 if point_range.contains(&buffer_row) {
6164 Some(code_actions)
6165 } else {
6166 None
6167 }
6168 })
6169 })
6170 .ok()
6171 .flatten()
6172 })
6173 }
6174
6175 pub fn confirm_code_action(
6176 &mut self,
6177 action: &ConfirmCodeAction,
6178 window: &mut Window,
6179 cx: &mut Context<Self>,
6180 ) -> Option<Task<Result<()>>> {
6181 self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
6182
6183 let actions_menu =
6184 if let CodeContextMenu::CodeActions(menu) = self.hide_context_menu(window, cx)? {
6185 menu
6186 } else {
6187 return None;
6188 };
6189
6190 let action_ix = action.item_ix.unwrap_or(actions_menu.selected_item);
6191 let action = actions_menu.actions.get(action_ix)?;
6192 let title = action.label();
6193 let buffer = actions_menu.buffer;
6194 let workspace = self.workspace()?;
6195
6196 match action {
6197 CodeActionsItem::Task(task_source_kind, resolved_task) => {
6198 workspace.update(cx, |workspace, cx| {
6199 workspace.schedule_resolved_task(
6200 task_source_kind,
6201 resolved_task,
6202 false,
6203 window,
6204 cx,
6205 );
6206
6207 Some(Task::ready(Ok(())))
6208 })
6209 }
6210 CodeActionsItem::CodeAction {
6211 excerpt_id,
6212 action,
6213 provider,
6214 } => {
6215 let apply_code_action =
6216 provider.apply_code_action(buffer, action, excerpt_id, true, window, cx);
6217 let workspace = workspace.downgrade();
6218 Some(cx.spawn_in(window, async move |editor, cx| {
6219 let project_transaction = apply_code_action.await?;
6220 Self::open_project_transaction(
6221 &editor,
6222 workspace,
6223 project_transaction,
6224 title,
6225 cx,
6226 )
6227 .await
6228 }))
6229 }
6230 CodeActionsItem::DebugScenario(scenario) => {
6231 let context = actions_menu.actions.context.clone();
6232
6233 workspace.update(cx, |workspace, cx| {
6234 dap::send_telemetry(&scenario, TelemetrySpawnLocation::Gutter, cx);
6235 workspace.start_debug_session(
6236 scenario,
6237 context,
6238 Some(buffer),
6239 None,
6240 window,
6241 cx,
6242 );
6243 });
6244 Some(Task::ready(Ok(())))
6245 }
6246 }
6247 }
6248
6249 pub async fn open_project_transaction(
6250 this: &WeakEntity<Editor>,
6251 workspace: WeakEntity<Workspace>,
6252 transaction: ProjectTransaction,
6253 title: String,
6254 cx: &mut AsyncWindowContext,
6255 ) -> Result<()> {
6256 let mut entries = transaction.0.into_iter().collect::<Vec<_>>();
6257 cx.update(|_, cx| {
6258 entries.sort_unstable_by_key(|(buffer, _)| {
6259 buffer.read(cx).file().map(|f| f.path().clone())
6260 });
6261 })?;
6262
6263 // If the project transaction's edits are all contained within this editor, then
6264 // avoid opening a new editor to display them.
6265
6266 if let Some((buffer, transaction)) = entries.first() {
6267 if entries.len() == 1 {
6268 let excerpt = this.update(cx, |editor, cx| {
6269 editor
6270 .buffer()
6271 .read(cx)
6272 .excerpt_containing(editor.selections.newest_anchor().head(), cx)
6273 })?;
6274 if let Some((_, excerpted_buffer, excerpt_range)) = excerpt {
6275 if excerpted_buffer == *buffer {
6276 let all_edits_within_excerpt = buffer.read_with(cx, |buffer, _| {
6277 let excerpt_range = excerpt_range.to_offset(buffer);
6278 buffer
6279 .edited_ranges_for_transaction::<usize>(transaction)
6280 .all(|range| {
6281 excerpt_range.start <= range.start
6282 && excerpt_range.end >= range.end
6283 })
6284 })?;
6285
6286 if all_edits_within_excerpt {
6287 return Ok(());
6288 }
6289 }
6290 }
6291 }
6292 } else {
6293 return Ok(());
6294 }
6295
6296 let mut ranges_to_highlight = Vec::new();
6297 let excerpt_buffer = cx.new(|cx| {
6298 let mut multibuffer = MultiBuffer::new(Capability::ReadWrite).with_title(title);
6299 for (buffer_handle, transaction) in &entries {
6300 let edited_ranges = buffer_handle
6301 .read(cx)
6302 .edited_ranges_for_transaction::<Point>(transaction)
6303 .collect::<Vec<_>>();
6304 let (ranges, _) = multibuffer.set_excerpts_for_path(
6305 PathKey::for_buffer(buffer_handle, cx),
6306 buffer_handle.clone(),
6307 edited_ranges,
6308 DEFAULT_MULTIBUFFER_CONTEXT,
6309 cx,
6310 );
6311
6312 ranges_to_highlight.extend(ranges);
6313 }
6314 multibuffer.push_transaction(entries.iter().map(|(b, t)| (b, t)), cx);
6315 multibuffer
6316 })?;
6317
6318 workspace.update_in(cx, |workspace, window, cx| {
6319 let project = workspace.project().clone();
6320 let editor =
6321 cx.new(|cx| Editor::for_multibuffer(excerpt_buffer, Some(project), window, cx));
6322 workspace.add_item_to_active_pane(Box::new(editor.clone()), None, true, window, cx);
6323 editor.update(cx, |editor, cx| {
6324 editor.highlight_background::<Self>(
6325 &ranges_to_highlight,
6326 |theme| theme.colors().editor_highlighted_line_background,
6327 cx,
6328 );
6329 });
6330 })?;
6331
6332 Ok(())
6333 }
6334
6335 pub fn clear_code_action_providers(&mut self) {
6336 self.code_action_providers.clear();
6337 self.available_code_actions.take();
6338 }
6339
6340 pub fn add_code_action_provider(
6341 &mut self,
6342 provider: Rc<dyn CodeActionProvider>,
6343 window: &mut Window,
6344 cx: &mut Context<Self>,
6345 ) {
6346 if self
6347 .code_action_providers
6348 .iter()
6349 .any(|existing_provider| existing_provider.id() == provider.id())
6350 {
6351 return;
6352 }
6353
6354 self.code_action_providers.push(provider);
6355 self.refresh_code_actions(window, cx);
6356 }
6357
6358 pub fn remove_code_action_provider(
6359 &mut self,
6360 id: Arc<str>,
6361 window: &mut Window,
6362 cx: &mut Context<Self>,
6363 ) {
6364 self.code_action_providers
6365 .retain(|provider| provider.id() != id);
6366 self.refresh_code_actions(window, cx);
6367 }
6368
6369 pub fn code_actions_enabled_for_toolbar(&self, cx: &App) -> bool {
6370 !self.code_action_providers.is_empty()
6371 && EditorSettings::get_global(cx).toolbar.code_actions
6372 }
6373
6374 pub fn has_available_code_actions(&self) -> bool {
6375 self.available_code_actions
6376 .as_ref()
6377 .is_some_and(|(_, actions)| !actions.is_empty())
6378 }
6379
6380 fn render_inline_code_actions(
6381 &self,
6382 icon_size: ui::IconSize,
6383 display_row: DisplayRow,
6384 is_active: bool,
6385 cx: &mut Context<Self>,
6386 ) -> AnyElement {
6387 let show_tooltip = !self.context_menu_visible();
6388 IconButton::new("inline_code_actions", ui::IconName::BoltFilled)
6389 .icon_size(icon_size)
6390 .shape(ui::IconButtonShape::Square)
6391 .style(ButtonStyle::Transparent)
6392 .icon_color(ui::Color::Hidden)
6393 .toggle_state(is_active)
6394 .when(show_tooltip, |this| {
6395 this.tooltip({
6396 let focus_handle = self.focus_handle.clone();
6397 move |window, cx| {
6398 Tooltip::for_action_in(
6399 "Toggle Code Actions",
6400 &ToggleCodeActions {
6401 deployed_from: None,
6402 quick_launch: false,
6403 },
6404 &focus_handle,
6405 window,
6406 cx,
6407 )
6408 }
6409 })
6410 })
6411 .on_click(cx.listener(move |editor, _: &ClickEvent, window, cx| {
6412 window.focus(&editor.focus_handle(cx));
6413 editor.toggle_code_actions(
6414 &crate::actions::ToggleCodeActions {
6415 deployed_from: Some(crate::actions::CodeActionSource::Indicator(
6416 display_row,
6417 )),
6418 quick_launch: false,
6419 },
6420 window,
6421 cx,
6422 );
6423 }))
6424 .into_any_element()
6425 }
6426
6427 pub fn context_menu(&self) -> &RefCell<Option<CodeContextMenu>> {
6428 &self.context_menu
6429 }
6430
6431 fn refresh_code_actions(&mut self, window: &mut Window, cx: &mut Context<Self>) -> Option<()> {
6432 let newest_selection = self.selections.newest_anchor().clone();
6433 let newest_selection_adjusted = self.selections.newest_adjusted(cx).clone();
6434 let buffer = self.buffer.read(cx);
6435 if newest_selection.head().diff_base_anchor.is_some() {
6436 return None;
6437 }
6438 let (start_buffer, start) =
6439 buffer.text_anchor_for_position(newest_selection_adjusted.start, cx)?;
6440 let (end_buffer, end) =
6441 buffer.text_anchor_for_position(newest_selection_adjusted.end, cx)?;
6442 if start_buffer != end_buffer {
6443 return None;
6444 }
6445
6446 self.code_actions_task = Some(cx.spawn_in(window, async move |this, cx| {
6447 cx.background_executor()
6448 .timer(CODE_ACTIONS_DEBOUNCE_TIMEOUT)
6449 .await;
6450
6451 let (providers, tasks) = this.update_in(cx, |this, window, cx| {
6452 let providers = this.code_action_providers.clone();
6453 let tasks = this
6454 .code_action_providers
6455 .iter()
6456 .map(|provider| provider.code_actions(&start_buffer, start..end, window, cx))
6457 .collect::<Vec<_>>();
6458 (providers, tasks)
6459 })?;
6460
6461 let mut actions = Vec::new();
6462 for (provider, provider_actions) in
6463 providers.into_iter().zip(future::join_all(tasks).await)
6464 {
6465 if let Some(provider_actions) = provider_actions.log_err() {
6466 actions.extend(provider_actions.into_iter().map(|action| {
6467 AvailableCodeAction {
6468 excerpt_id: newest_selection.start.excerpt_id,
6469 action,
6470 provider: provider.clone(),
6471 }
6472 }));
6473 }
6474 }
6475
6476 this.update(cx, |this, cx| {
6477 this.available_code_actions = if actions.is_empty() {
6478 None
6479 } else {
6480 Some((
6481 Location {
6482 buffer: start_buffer,
6483 range: start..end,
6484 },
6485 actions.into(),
6486 ))
6487 };
6488 cx.notify();
6489 })
6490 }));
6491 None
6492 }
6493
6494 fn start_inline_blame_timer(&mut self, window: &mut Window, cx: &mut Context<Self>) {
6495 if let Some(delay) = ProjectSettings::get_global(cx).git.inline_blame_delay() {
6496 self.show_git_blame_inline = false;
6497
6498 self.show_git_blame_inline_delay_task =
6499 Some(cx.spawn_in(window, async move |this, cx| {
6500 cx.background_executor().timer(delay).await;
6501
6502 this.update(cx, |this, cx| {
6503 this.show_git_blame_inline = true;
6504 cx.notify();
6505 })
6506 .log_err();
6507 }));
6508 }
6509 }
6510
6511 fn show_blame_popover(
6512 &mut self,
6513 blame_entry: &BlameEntry,
6514 position: gpui::Point<Pixels>,
6515 cx: &mut Context<Self>,
6516 ) {
6517 if let Some(state) = &mut self.inline_blame_popover {
6518 state.hide_task.take();
6519 } else {
6520 let delay = EditorSettings::get_global(cx).hover_popover_delay;
6521 let blame_entry = blame_entry.clone();
6522 let show_task = cx.spawn(async move |editor, cx| {
6523 cx.background_executor()
6524 .timer(std::time::Duration::from_millis(delay))
6525 .await;
6526 editor
6527 .update(cx, |editor, cx| {
6528 editor.inline_blame_popover_show_task.take();
6529 let Some(blame) = editor.blame.as_ref() else {
6530 return;
6531 };
6532 let blame = blame.read(cx);
6533 let details = blame.details_for_entry(&blame_entry);
6534 let markdown = cx.new(|cx| {
6535 Markdown::new(
6536 details
6537 .as_ref()
6538 .map(|message| message.message.clone())
6539 .unwrap_or_default(),
6540 None,
6541 None,
6542 cx,
6543 )
6544 });
6545 editor.inline_blame_popover = Some(InlineBlamePopover {
6546 position,
6547 hide_task: None,
6548 popover_bounds: None,
6549 popover_state: InlineBlamePopoverState {
6550 scroll_handle: ScrollHandle::new(),
6551 commit_message: details,
6552 markdown,
6553 },
6554 });
6555 cx.notify();
6556 })
6557 .ok();
6558 });
6559 self.inline_blame_popover_show_task = Some(show_task);
6560 }
6561 }
6562
6563 fn hide_blame_popover(&mut self, cx: &mut Context<Self>) {
6564 self.inline_blame_popover_show_task.take();
6565 if let Some(state) = &mut self.inline_blame_popover {
6566 let hide_task = cx.spawn(async move |editor, cx| {
6567 cx.background_executor()
6568 .timer(std::time::Duration::from_millis(100))
6569 .await;
6570 editor
6571 .update(cx, |editor, cx| {
6572 editor.inline_blame_popover.take();
6573 cx.notify();
6574 })
6575 .ok();
6576 });
6577 state.hide_task = Some(hide_task);
6578 }
6579 }
6580
6581 fn refresh_document_highlights(&mut self, cx: &mut Context<Self>) -> Option<()> {
6582 if self.pending_rename.is_some() {
6583 return None;
6584 }
6585
6586 let provider = self.semantics_provider.clone()?;
6587 let buffer = self.buffer.read(cx);
6588 let newest_selection = self.selections.newest_anchor().clone();
6589 let cursor_position = newest_selection.head();
6590 let (cursor_buffer, cursor_buffer_position) =
6591 buffer.text_anchor_for_position(cursor_position, cx)?;
6592 let (tail_buffer, tail_buffer_position) =
6593 buffer.text_anchor_for_position(newest_selection.tail(), cx)?;
6594 if cursor_buffer != tail_buffer {
6595 return None;
6596 }
6597
6598 let snapshot = cursor_buffer.read(cx).snapshot();
6599 let (start_word_range, _) = snapshot.surrounding_word(cursor_buffer_position);
6600 let (end_word_range, _) = snapshot.surrounding_word(tail_buffer_position);
6601 if start_word_range != end_word_range {
6602 self.document_highlights_task.take();
6603 self.clear_background_highlights::<DocumentHighlightRead>(cx);
6604 self.clear_background_highlights::<DocumentHighlightWrite>(cx);
6605 return None;
6606 }
6607
6608 let debounce = EditorSettings::get_global(cx).lsp_highlight_debounce;
6609 self.document_highlights_task = Some(cx.spawn(async move |this, cx| {
6610 cx.background_executor()
6611 .timer(Duration::from_millis(debounce))
6612 .await;
6613
6614 let highlights = if let Some(highlights) = cx
6615 .update(|cx| {
6616 provider.document_highlights(&cursor_buffer, cursor_buffer_position, cx)
6617 })
6618 .ok()
6619 .flatten()
6620 {
6621 highlights.await.log_err()
6622 } else {
6623 None
6624 };
6625
6626 if let Some(highlights) = highlights {
6627 this.update(cx, |this, cx| {
6628 if this.pending_rename.is_some() {
6629 return;
6630 }
6631
6632 let buffer_id = cursor_position.buffer_id;
6633 let buffer = this.buffer.read(cx);
6634 if !buffer
6635 .text_anchor_for_position(cursor_position, cx)
6636 .map_or(false, |(buffer, _)| buffer == cursor_buffer)
6637 {
6638 return;
6639 }
6640
6641 let cursor_buffer_snapshot = cursor_buffer.read(cx);
6642 let mut write_ranges = Vec::new();
6643 let mut read_ranges = Vec::new();
6644 for highlight in highlights {
6645 for (excerpt_id, excerpt_range) in
6646 buffer.excerpts_for_buffer(cursor_buffer.read(cx).remote_id(), cx)
6647 {
6648 let start = highlight
6649 .range
6650 .start
6651 .max(&excerpt_range.context.start, cursor_buffer_snapshot);
6652 let end = highlight
6653 .range
6654 .end
6655 .min(&excerpt_range.context.end, cursor_buffer_snapshot);
6656 if start.cmp(&end, cursor_buffer_snapshot).is_ge() {
6657 continue;
6658 }
6659
6660 let range = Anchor {
6661 buffer_id,
6662 excerpt_id,
6663 text_anchor: start,
6664 diff_base_anchor: None,
6665 }..Anchor {
6666 buffer_id,
6667 excerpt_id,
6668 text_anchor: end,
6669 diff_base_anchor: None,
6670 };
6671 if highlight.kind == lsp::DocumentHighlightKind::WRITE {
6672 write_ranges.push(range);
6673 } else {
6674 read_ranges.push(range);
6675 }
6676 }
6677 }
6678
6679 this.highlight_background::<DocumentHighlightRead>(
6680 &read_ranges,
6681 |theme| theme.colors().editor_document_highlight_read_background,
6682 cx,
6683 );
6684 this.highlight_background::<DocumentHighlightWrite>(
6685 &write_ranges,
6686 |theme| theme.colors().editor_document_highlight_write_background,
6687 cx,
6688 );
6689 cx.notify();
6690 })
6691 .log_err();
6692 }
6693 }));
6694 None
6695 }
6696
6697 fn prepare_highlight_query_from_selection(
6698 &mut self,
6699 cx: &mut Context<Editor>,
6700 ) -> Option<(String, Range<Anchor>)> {
6701 if matches!(self.mode, EditorMode::SingleLine { .. }) {
6702 return None;
6703 }
6704 if !EditorSettings::get_global(cx).selection_highlight {
6705 return None;
6706 }
6707 if self.selections.count() != 1 || self.selections.line_mode {
6708 return None;
6709 }
6710 let selection = self.selections.newest::<Point>(cx);
6711 if selection.is_empty() || selection.start.row != selection.end.row {
6712 return None;
6713 }
6714 let multi_buffer_snapshot = self.buffer().read(cx).snapshot(cx);
6715 let selection_anchor_range = selection.range().to_anchors(&multi_buffer_snapshot);
6716 let query = multi_buffer_snapshot
6717 .text_for_range(selection_anchor_range.clone())
6718 .collect::<String>();
6719 if query.trim().is_empty() {
6720 return None;
6721 }
6722 Some((query, selection_anchor_range))
6723 }
6724
6725 fn update_selection_occurrence_highlights(
6726 &mut self,
6727 query_text: String,
6728 query_range: Range<Anchor>,
6729 multi_buffer_range_to_query: Range<Point>,
6730 use_debounce: bool,
6731 window: &mut Window,
6732 cx: &mut Context<Editor>,
6733 ) -> Task<()> {
6734 let multi_buffer_snapshot = self.buffer().read(cx).snapshot(cx);
6735 cx.spawn_in(window, async move |editor, cx| {
6736 if use_debounce {
6737 cx.background_executor()
6738 .timer(SELECTION_HIGHLIGHT_DEBOUNCE_TIMEOUT)
6739 .await;
6740 }
6741 let match_task = cx.background_spawn(async move {
6742 let buffer_ranges = multi_buffer_snapshot
6743 .range_to_buffer_ranges(multi_buffer_range_to_query)
6744 .into_iter()
6745 .filter(|(_, excerpt_visible_range, _)| !excerpt_visible_range.is_empty());
6746 let mut match_ranges = Vec::new();
6747 let Ok(regex) = project::search::SearchQuery::text(
6748 query_text.clone(),
6749 false,
6750 false,
6751 false,
6752 Default::default(),
6753 Default::default(),
6754 false,
6755 None,
6756 ) else {
6757 return Vec::default();
6758 };
6759 for (buffer_snapshot, search_range, excerpt_id) in buffer_ranges {
6760 match_ranges.extend(
6761 regex
6762 .search(&buffer_snapshot, Some(search_range.clone()))
6763 .await
6764 .into_iter()
6765 .filter_map(|match_range| {
6766 let match_start = buffer_snapshot
6767 .anchor_after(search_range.start + match_range.start);
6768 let match_end = buffer_snapshot
6769 .anchor_before(search_range.start + match_range.end);
6770 let match_anchor_range = Anchor::range_in_buffer(
6771 excerpt_id,
6772 buffer_snapshot.remote_id(),
6773 match_start..match_end,
6774 );
6775 (match_anchor_range != query_range).then_some(match_anchor_range)
6776 }),
6777 );
6778 }
6779 match_ranges
6780 });
6781 let match_ranges = match_task.await;
6782 editor
6783 .update_in(cx, |editor, _, cx| {
6784 editor.clear_background_highlights::<SelectedTextHighlight>(cx);
6785 if !match_ranges.is_empty() {
6786 editor.highlight_background::<SelectedTextHighlight>(
6787 &match_ranges,
6788 |theme| theme.colors().editor_document_highlight_bracket_background,
6789 cx,
6790 )
6791 }
6792 })
6793 .log_err();
6794 })
6795 }
6796
6797 fn refresh_single_line_folds(&mut self, window: &mut Window, cx: &mut Context<Editor>) {
6798 struct NewlineFold;
6799 let type_id = std::any::TypeId::of::<NewlineFold>();
6800 if !self.mode.is_single_line() {
6801 return;
6802 }
6803 let snapshot = self.snapshot(window, cx);
6804 if snapshot.buffer_snapshot.max_point().row == 0 {
6805 return;
6806 }
6807 let task = cx.background_spawn(async move {
6808 let new_newlines = snapshot
6809 .buffer_chars_at(0)
6810 .filter_map(|(c, i)| {
6811 if c == '\n' {
6812 Some(
6813 snapshot.buffer_snapshot.anchor_after(i)
6814 ..snapshot.buffer_snapshot.anchor_before(i + 1),
6815 )
6816 } else {
6817 None
6818 }
6819 })
6820 .collect::<Vec<_>>();
6821 let existing_newlines = snapshot
6822 .folds_in_range(0..snapshot.buffer_snapshot.len())
6823 .filter_map(|fold| {
6824 if fold.placeholder.type_tag == Some(type_id) {
6825 Some(fold.range.start..fold.range.end)
6826 } else {
6827 None
6828 }
6829 })
6830 .collect::<Vec<_>>();
6831
6832 (new_newlines, existing_newlines)
6833 });
6834 self.folding_newlines = cx.spawn(async move |this, cx| {
6835 let (new_newlines, existing_newlines) = task.await;
6836 if new_newlines == existing_newlines {
6837 return;
6838 }
6839 let placeholder = FoldPlaceholder {
6840 render: Arc::new(move |_, _, cx| {
6841 div()
6842 .bg(cx.theme().status().hint_background)
6843 .border_b_1()
6844 .size_full()
6845 .font(ThemeSettings::get_global(cx).buffer_font.clone())
6846 .border_color(cx.theme().status().hint)
6847 .child("\\n")
6848 .into_any()
6849 }),
6850 constrain_width: false,
6851 merge_adjacent: false,
6852 type_tag: Some(type_id),
6853 };
6854 let creases = new_newlines
6855 .into_iter()
6856 .map(|range| Crease::simple(range, placeholder.clone()))
6857 .collect();
6858 this.update(cx, |this, cx| {
6859 this.display_map.update(cx, |display_map, cx| {
6860 display_map.remove_folds_with_type(existing_newlines, type_id, cx);
6861 display_map.fold(creases, cx);
6862 });
6863 })
6864 .ok();
6865 });
6866 }
6867
6868 fn refresh_selected_text_highlights(
6869 &mut self,
6870 on_buffer_edit: bool,
6871 window: &mut Window,
6872 cx: &mut Context<Editor>,
6873 ) {
6874 let Some((query_text, query_range)) = self.prepare_highlight_query_from_selection(cx)
6875 else {
6876 self.clear_background_highlights::<SelectedTextHighlight>(cx);
6877 self.quick_selection_highlight_task.take();
6878 self.debounced_selection_highlight_task.take();
6879 return;
6880 };
6881 let multi_buffer_snapshot = self.buffer().read(cx).snapshot(cx);
6882 if on_buffer_edit
6883 || self
6884 .quick_selection_highlight_task
6885 .as_ref()
6886 .map_or(true, |(prev_anchor_range, _)| {
6887 prev_anchor_range != &query_range
6888 })
6889 {
6890 let multi_buffer_visible_start = self
6891 .scroll_manager
6892 .anchor()
6893 .anchor
6894 .to_point(&multi_buffer_snapshot);
6895 let multi_buffer_visible_end = multi_buffer_snapshot.clip_point(
6896 multi_buffer_visible_start
6897 + Point::new(self.visible_line_count().unwrap_or(0.).ceil() as u32, 0),
6898 Bias::Left,
6899 );
6900 let multi_buffer_visible_range = multi_buffer_visible_start..multi_buffer_visible_end;
6901 self.quick_selection_highlight_task = Some((
6902 query_range.clone(),
6903 self.update_selection_occurrence_highlights(
6904 query_text.clone(),
6905 query_range.clone(),
6906 multi_buffer_visible_range,
6907 false,
6908 window,
6909 cx,
6910 ),
6911 ));
6912 }
6913 if on_buffer_edit
6914 || self
6915 .debounced_selection_highlight_task
6916 .as_ref()
6917 .map_or(true, |(prev_anchor_range, _)| {
6918 prev_anchor_range != &query_range
6919 })
6920 {
6921 let multi_buffer_start = multi_buffer_snapshot
6922 .anchor_before(0)
6923 .to_point(&multi_buffer_snapshot);
6924 let multi_buffer_end = multi_buffer_snapshot
6925 .anchor_after(multi_buffer_snapshot.len())
6926 .to_point(&multi_buffer_snapshot);
6927 let multi_buffer_full_range = multi_buffer_start..multi_buffer_end;
6928 self.debounced_selection_highlight_task = Some((
6929 query_range.clone(),
6930 self.update_selection_occurrence_highlights(
6931 query_text,
6932 query_range,
6933 multi_buffer_full_range,
6934 true,
6935 window,
6936 cx,
6937 ),
6938 ));
6939 }
6940 }
6941
6942 pub fn refresh_inline_completion(
6943 &mut self,
6944 debounce: bool,
6945 user_requested: bool,
6946 window: &mut Window,
6947 cx: &mut Context<Self>,
6948 ) -> Option<()> {
6949 let provider = self.edit_prediction_provider()?;
6950 let cursor = self.selections.newest_anchor().head();
6951 let (buffer, cursor_buffer_position) =
6952 self.buffer.read(cx).text_anchor_for_position(cursor, cx)?;
6953
6954 if !self.edit_predictions_enabled_in_buffer(&buffer, cursor_buffer_position, cx) {
6955 self.discard_inline_completion(false, cx);
6956 return None;
6957 }
6958
6959 if !user_requested
6960 && (!self.should_show_edit_predictions()
6961 || !self.is_focused(window)
6962 || buffer.read(cx).is_empty())
6963 {
6964 self.discard_inline_completion(false, cx);
6965 return None;
6966 }
6967
6968 self.update_visible_inline_completion(window, cx);
6969 provider.refresh(
6970 self.project.clone(),
6971 buffer,
6972 cursor_buffer_position,
6973 debounce,
6974 cx,
6975 );
6976 Some(())
6977 }
6978
6979 fn show_edit_predictions_in_menu(&self) -> bool {
6980 match self.edit_prediction_settings {
6981 EditPredictionSettings::Disabled => false,
6982 EditPredictionSettings::Enabled { show_in_menu, .. } => show_in_menu,
6983 }
6984 }
6985
6986 pub fn edit_predictions_enabled(&self) -> bool {
6987 match self.edit_prediction_settings {
6988 EditPredictionSettings::Disabled => false,
6989 EditPredictionSettings::Enabled { .. } => true,
6990 }
6991 }
6992
6993 fn edit_prediction_requires_modifier(&self) -> bool {
6994 match self.edit_prediction_settings {
6995 EditPredictionSettings::Disabled => false,
6996 EditPredictionSettings::Enabled {
6997 preview_requires_modifier,
6998 ..
6999 } => preview_requires_modifier,
7000 }
7001 }
7002
7003 pub fn update_edit_prediction_settings(&mut self, cx: &mut Context<Self>) {
7004 if self.edit_prediction_provider.is_none() {
7005 self.edit_prediction_settings = EditPredictionSettings::Disabled;
7006 } else {
7007 let selection = self.selections.newest_anchor();
7008 let cursor = selection.head();
7009
7010 if let Some((buffer, cursor_buffer_position)) =
7011 self.buffer.read(cx).text_anchor_for_position(cursor, cx)
7012 {
7013 self.edit_prediction_settings =
7014 self.edit_prediction_settings_at_position(&buffer, cursor_buffer_position, cx);
7015 }
7016 }
7017 }
7018
7019 fn edit_prediction_settings_at_position(
7020 &self,
7021 buffer: &Entity<Buffer>,
7022 buffer_position: language::Anchor,
7023 cx: &App,
7024 ) -> EditPredictionSettings {
7025 if !self.mode.is_full()
7026 || !self.show_inline_completions_override.unwrap_or(true)
7027 || self.inline_completions_disabled_in_scope(buffer, buffer_position, cx)
7028 {
7029 return EditPredictionSettings::Disabled;
7030 }
7031
7032 let buffer = buffer.read(cx);
7033
7034 let file = buffer.file();
7035
7036 if !language_settings(buffer.language().map(|l| l.name()), file, cx).show_edit_predictions {
7037 return EditPredictionSettings::Disabled;
7038 };
7039
7040 let by_provider = matches!(
7041 self.menu_inline_completions_policy,
7042 MenuInlineCompletionsPolicy::ByProvider
7043 );
7044
7045 let show_in_menu = by_provider
7046 && self
7047 .edit_prediction_provider
7048 .as_ref()
7049 .map_or(false, |provider| {
7050 provider.provider.show_completions_in_menu()
7051 });
7052
7053 let preview_requires_modifier =
7054 all_language_settings(file, cx).edit_predictions_mode() == EditPredictionsMode::Subtle;
7055
7056 EditPredictionSettings::Enabled {
7057 show_in_menu,
7058 preview_requires_modifier,
7059 }
7060 }
7061
7062 fn should_show_edit_predictions(&self) -> bool {
7063 self.snippet_stack.is_empty() && self.edit_predictions_enabled()
7064 }
7065
7066 pub fn edit_prediction_preview_is_active(&self) -> bool {
7067 matches!(
7068 self.edit_prediction_preview,
7069 EditPredictionPreview::Active { .. }
7070 )
7071 }
7072
7073 pub fn edit_predictions_enabled_at_cursor(&self, cx: &App) -> bool {
7074 let cursor = self.selections.newest_anchor().head();
7075 if let Some((buffer, cursor_position)) =
7076 self.buffer.read(cx).text_anchor_for_position(cursor, cx)
7077 {
7078 self.edit_predictions_enabled_in_buffer(&buffer, cursor_position, cx)
7079 } else {
7080 false
7081 }
7082 }
7083
7084 pub fn supports_minimap(&self, cx: &App) -> bool {
7085 !self.minimap_visibility.disabled() && self.is_singleton(cx)
7086 }
7087
7088 fn edit_predictions_enabled_in_buffer(
7089 &self,
7090 buffer: &Entity<Buffer>,
7091 buffer_position: language::Anchor,
7092 cx: &App,
7093 ) -> bool {
7094 maybe!({
7095 if self.read_only(cx) {
7096 return Some(false);
7097 }
7098 let provider = self.edit_prediction_provider()?;
7099 if !provider.is_enabled(&buffer, buffer_position, cx) {
7100 return Some(false);
7101 }
7102 let buffer = buffer.read(cx);
7103 let Some(file) = buffer.file() else {
7104 return Some(true);
7105 };
7106 let settings = all_language_settings(Some(file), cx);
7107 Some(settings.edit_predictions_enabled_for_file(file, cx))
7108 })
7109 .unwrap_or(false)
7110 }
7111
7112 fn cycle_inline_completion(
7113 &mut self,
7114 direction: Direction,
7115 window: &mut Window,
7116 cx: &mut Context<Self>,
7117 ) -> Option<()> {
7118 let provider = self.edit_prediction_provider()?;
7119 let cursor = self.selections.newest_anchor().head();
7120 let (buffer, cursor_buffer_position) =
7121 self.buffer.read(cx).text_anchor_for_position(cursor, cx)?;
7122 if self.inline_completions_hidden_for_vim_mode || !self.should_show_edit_predictions() {
7123 return None;
7124 }
7125
7126 provider.cycle(buffer, cursor_buffer_position, direction, cx);
7127 self.update_visible_inline_completion(window, cx);
7128
7129 Some(())
7130 }
7131
7132 pub fn show_inline_completion(
7133 &mut self,
7134 _: &ShowEditPrediction,
7135 window: &mut Window,
7136 cx: &mut Context<Self>,
7137 ) {
7138 if !self.has_active_inline_completion() {
7139 self.refresh_inline_completion(false, true, window, cx);
7140 return;
7141 }
7142
7143 self.update_visible_inline_completion(window, cx);
7144 }
7145
7146 pub fn display_cursor_names(
7147 &mut self,
7148 _: &DisplayCursorNames,
7149 window: &mut Window,
7150 cx: &mut Context<Self>,
7151 ) {
7152 self.show_cursor_names(window, cx);
7153 }
7154
7155 fn show_cursor_names(&mut self, window: &mut Window, cx: &mut Context<Self>) {
7156 self.show_cursor_names = true;
7157 cx.notify();
7158 cx.spawn_in(window, async move |this, cx| {
7159 cx.background_executor().timer(CURSORS_VISIBLE_FOR).await;
7160 this.update(cx, |this, cx| {
7161 this.show_cursor_names = false;
7162 cx.notify()
7163 })
7164 .ok()
7165 })
7166 .detach();
7167 }
7168
7169 pub fn next_edit_prediction(
7170 &mut self,
7171 _: &NextEditPrediction,
7172 window: &mut Window,
7173 cx: &mut Context<Self>,
7174 ) {
7175 if self.has_active_inline_completion() {
7176 self.cycle_inline_completion(Direction::Next, window, cx);
7177 } else {
7178 let is_copilot_disabled = self
7179 .refresh_inline_completion(false, true, window, cx)
7180 .is_none();
7181 if is_copilot_disabled {
7182 cx.propagate();
7183 }
7184 }
7185 }
7186
7187 pub fn previous_edit_prediction(
7188 &mut self,
7189 _: &PreviousEditPrediction,
7190 window: &mut Window,
7191 cx: &mut Context<Self>,
7192 ) {
7193 if self.has_active_inline_completion() {
7194 self.cycle_inline_completion(Direction::Prev, window, cx);
7195 } else {
7196 let is_copilot_disabled = self
7197 .refresh_inline_completion(false, true, window, cx)
7198 .is_none();
7199 if is_copilot_disabled {
7200 cx.propagate();
7201 }
7202 }
7203 }
7204
7205 pub fn accept_edit_prediction(
7206 &mut self,
7207 _: &AcceptEditPrediction,
7208 window: &mut Window,
7209 cx: &mut Context<Self>,
7210 ) {
7211 if self.show_edit_predictions_in_menu() {
7212 self.hide_context_menu(window, cx);
7213 }
7214
7215 let Some(active_inline_completion) = self.active_inline_completion.as_ref() else {
7216 return;
7217 };
7218
7219 self.report_inline_completion_event(
7220 active_inline_completion.completion_id.clone(),
7221 true,
7222 cx,
7223 );
7224
7225 match &active_inline_completion.completion {
7226 InlineCompletion::Move { target, .. } => {
7227 let target = *target;
7228
7229 if let Some(position_map) = &self.last_position_map {
7230 if position_map
7231 .visible_row_range
7232 .contains(&target.to_display_point(&position_map.snapshot).row())
7233 || !self.edit_prediction_requires_modifier()
7234 {
7235 self.unfold_ranges(&[target..target], true, false, cx);
7236 // Note that this is also done in vim's handler of the Tab action.
7237 self.change_selections(
7238 SelectionEffects::scroll(Autoscroll::newest()),
7239 window,
7240 cx,
7241 |selections| {
7242 selections.select_anchor_ranges([target..target]);
7243 },
7244 );
7245 self.clear_row_highlights::<EditPredictionPreview>();
7246
7247 self.edit_prediction_preview
7248 .set_previous_scroll_position(None);
7249 } else {
7250 self.edit_prediction_preview
7251 .set_previous_scroll_position(Some(
7252 position_map.snapshot.scroll_anchor,
7253 ));
7254
7255 self.highlight_rows::<EditPredictionPreview>(
7256 target..target,
7257 cx.theme().colors().editor_highlighted_line_background,
7258 RowHighlightOptions {
7259 autoscroll: true,
7260 ..Default::default()
7261 },
7262 cx,
7263 );
7264 self.request_autoscroll(Autoscroll::fit(), cx);
7265 }
7266 }
7267 }
7268 InlineCompletion::Edit { edits, .. } => {
7269 if let Some(provider) = self.edit_prediction_provider() {
7270 provider.accept(cx);
7271 }
7272
7273 // Store the transaction ID and selections before applying the edit
7274 let transaction_id_prev = self.buffer.read(cx).last_transaction_id(cx);
7275
7276 let snapshot = self.buffer.read(cx).snapshot(cx);
7277 let last_edit_end = edits.last().unwrap().0.end.bias_right(&snapshot);
7278
7279 self.buffer.update(cx, |buffer, cx| {
7280 buffer.edit(edits.iter().cloned(), None, cx)
7281 });
7282
7283 self.change_selections(SelectionEffects::no_scroll(), window, cx, |s| {
7284 s.select_anchor_ranges([last_edit_end..last_edit_end]);
7285 });
7286
7287 let selections = self.selections.disjoint_anchors();
7288 if let Some(transaction_id_now) = self.buffer.read(cx).last_transaction_id(cx) {
7289 let has_new_transaction = transaction_id_prev != Some(transaction_id_now);
7290 if has_new_transaction {
7291 self.selection_history
7292 .insert_transaction(transaction_id_now, selections);
7293 }
7294 }
7295
7296 self.update_visible_inline_completion(window, cx);
7297 if self.active_inline_completion.is_none() {
7298 self.refresh_inline_completion(true, true, window, cx);
7299 }
7300
7301 cx.notify();
7302 }
7303 }
7304
7305 self.edit_prediction_requires_modifier_in_indent_conflict = false;
7306 }
7307
7308 pub fn accept_partial_inline_completion(
7309 &mut self,
7310 _: &AcceptPartialEditPrediction,
7311 window: &mut Window,
7312 cx: &mut Context<Self>,
7313 ) {
7314 let Some(active_inline_completion) = self.active_inline_completion.as_ref() else {
7315 return;
7316 };
7317 if self.selections.count() != 1 {
7318 return;
7319 }
7320
7321 self.report_inline_completion_event(
7322 active_inline_completion.completion_id.clone(),
7323 true,
7324 cx,
7325 );
7326
7327 match &active_inline_completion.completion {
7328 InlineCompletion::Move { target, .. } => {
7329 let target = *target;
7330 self.change_selections(
7331 SelectionEffects::scroll(Autoscroll::newest()),
7332 window,
7333 cx,
7334 |selections| {
7335 selections.select_anchor_ranges([target..target]);
7336 },
7337 );
7338 }
7339 InlineCompletion::Edit { edits, .. } => {
7340 // Find an insertion that starts at the cursor position.
7341 let snapshot = self.buffer.read(cx).snapshot(cx);
7342 let cursor_offset = self.selections.newest::<usize>(cx).head();
7343 let insertion = edits.iter().find_map(|(range, text)| {
7344 let range = range.to_offset(&snapshot);
7345 if range.is_empty() && range.start == cursor_offset {
7346 Some(text)
7347 } else {
7348 None
7349 }
7350 });
7351
7352 if let Some(text) = insertion {
7353 let mut partial_completion = text
7354 .chars()
7355 .by_ref()
7356 .take_while(|c| c.is_alphabetic())
7357 .collect::<String>();
7358 if partial_completion.is_empty() {
7359 partial_completion = text
7360 .chars()
7361 .by_ref()
7362 .take_while(|c| c.is_whitespace() || !c.is_alphabetic())
7363 .collect::<String>();
7364 }
7365
7366 cx.emit(EditorEvent::InputHandled {
7367 utf16_range_to_replace: None,
7368 text: partial_completion.clone().into(),
7369 });
7370
7371 self.insert_with_autoindent_mode(&partial_completion, None, window, cx);
7372
7373 self.refresh_inline_completion(true, true, window, cx);
7374 cx.notify();
7375 } else {
7376 self.accept_edit_prediction(&Default::default(), window, cx);
7377 }
7378 }
7379 }
7380 }
7381
7382 fn discard_inline_completion(
7383 &mut self,
7384 should_report_inline_completion_event: bool,
7385 cx: &mut Context<Self>,
7386 ) -> bool {
7387 if should_report_inline_completion_event {
7388 let completion_id = self
7389 .active_inline_completion
7390 .as_ref()
7391 .and_then(|active_completion| active_completion.completion_id.clone());
7392
7393 self.report_inline_completion_event(completion_id, false, cx);
7394 }
7395
7396 if let Some(provider) = self.edit_prediction_provider() {
7397 provider.discard(cx);
7398 }
7399
7400 self.take_active_inline_completion(cx)
7401 }
7402
7403 fn report_inline_completion_event(&self, id: Option<SharedString>, accepted: bool, cx: &App) {
7404 let Some(provider) = self.edit_prediction_provider() else {
7405 return;
7406 };
7407
7408 let Some((_, buffer, _)) = self
7409 .buffer
7410 .read(cx)
7411 .excerpt_containing(self.selections.newest_anchor().head(), cx)
7412 else {
7413 return;
7414 };
7415
7416 let extension = buffer
7417 .read(cx)
7418 .file()
7419 .and_then(|file| Some(file.path().extension()?.to_string_lossy().to_string()));
7420
7421 let event_type = match accepted {
7422 true => "Edit Prediction Accepted",
7423 false => "Edit Prediction Discarded",
7424 };
7425 telemetry::event!(
7426 event_type,
7427 provider = provider.name(),
7428 prediction_id = id,
7429 suggestion_accepted = accepted,
7430 file_extension = extension,
7431 );
7432 }
7433
7434 pub fn has_active_inline_completion(&self) -> bool {
7435 self.active_inline_completion.is_some()
7436 }
7437
7438 fn take_active_inline_completion(&mut self, cx: &mut Context<Self>) -> bool {
7439 let Some(active_inline_completion) = self.active_inline_completion.take() else {
7440 return false;
7441 };
7442
7443 self.splice_inlays(&active_inline_completion.inlay_ids, Default::default(), cx);
7444 self.clear_highlights::<InlineCompletionHighlight>(cx);
7445 self.stale_inline_completion_in_menu = Some(active_inline_completion);
7446 true
7447 }
7448
7449 /// Returns true when we're displaying the edit prediction popover below the cursor
7450 /// like we are not previewing and the LSP autocomplete menu is visible
7451 /// or we are in `when_holding_modifier` mode.
7452 pub fn edit_prediction_visible_in_cursor_popover(&self, has_completion: bool) -> bool {
7453 if self.edit_prediction_preview_is_active()
7454 || !self.show_edit_predictions_in_menu()
7455 || !self.edit_predictions_enabled()
7456 {
7457 return false;
7458 }
7459
7460 if self.has_visible_completions_menu() {
7461 return true;
7462 }
7463
7464 has_completion && self.edit_prediction_requires_modifier()
7465 }
7466
7467 fn handle_modifiers_changed(
7468 &mut self,
7469 modifiers: Modifiers,
7470 position_map: &PositionMap,
7471 window: &mut Window,
7472 cx: &mut Context<Self>,
7473 ) {
7474 if self.show_edit_predictions_in_menu() {
7475 self.update_edit_prediction_preview(&modifiers, window, cx);
7476 }
7477
7478 self.update_selection_mode(&modifiers, position_map, window, cx);
7479
7480 let mouse_position = window.mouse_position();
7481 if !position_map.text_hitbox.is_hovered(window) {
7482 return;
7483 }
7484
7485 self.update_hovered_link(
7486 position_map.point_for_position(mouse_position),
7487 &position_map.snapshot,
7488 modifiers,
7489 window,
7490 cx,
7491 )
7492 }
7493
7494 fn multi_cursor_modifier(invert: bool, modifiers: &Modifiers, cx: &mut Context<Self>) -> bool {
7495 let multi_cursor_setting = EditorSettings::get_global(cx).multi_cursor_modifier;
7496 if invert {
7497 match multi_cursor_setting {
7498 MultiCursorModifier::Alt => modifiers.alt,
7499 MultiCursorModifier::CmdOrCtrl => modifiers.secondary(),
7500 }
7501 } else {
7502 match multi_cursor_setting {
7503 MultiCursorModifier::Alt => modifiers.secondary(),
7504 MultiCursorModifier::CmdOrCtrl => modifiers.alt,
7505 }
7506 }
7507 }
7508
7509 fn columnar_selection_mode(
7510 modifiers: &Modifiers,
7511 cx: &mut Context<Self>,
7512 ) -> Option<ColumnarMode> {
7513 if modifiers.shift && modifiers.number_of_modifiers() == 2 {
7514 if Self::multi_cursor_modifier(false, modifiers, cx) {
7515 Some(ColumnarMode::FromMouse)
7516 } else if Self::multi_cursor_modifier(true, modifiers, cx) {
7517 Some(ColumnarMode::FromSelection)
7518 } else {
7519 None
7520 }
7521 } else {
7522 None
7523 }
7524 }
7525
7526 fn update_selection_mode(
7527 &mut self,
7528 modifiers: &Modifiers,
7529 position_map: &PositionMap,
7530 window: &mut Window,
7531 cx: &mut Context<Self>,
7532 ) {
7533 let Some(mode) = Self::columnar_selection_mode(modifiers, cx) else {
7534 return;
7535 };
7536 if self.selections.pending.is_none() {
7537 return;
7538 }
7539
7540 let mouse_position = window.mouse_position();
7541 let point_for_position = position_map.point_for_position(mouse_position);
7542 let position = point_for_position.previous_valid;
7543
7544 self.select(
7545 SelectPhase::BeginColumnar {
7546 position,
7547 reset: false,
7548 mode,
7549 goal_column: point_for_position.exact_unclipped.column(),
7550 },
7551 window,
7552 cx,
7553 );
7554 }
7555
7556 fn update_edit_prediction_preview(
7557 &mut self,
7558 modifiers: &Modifiers,
7559 window: &mut Window,
7560 cx: &mut Context<Self>,
7561 ) {
7562 let mut modifiers_held = false;
7563 if let Some(accept_keystroke) = self
7564 .accept_edit_prediction_keybind(false, window, cx)
7565 .keystroke()
7566 {
7567 modifiers_held = modifiers_held
7568 || (&accept_keystroke.modifiers == modifiers
7569 && accept_keystroke.modifiers.modified());
7570 };
7571 if let Some(accept_partial_keystroke) = self
7572 .accept_edit_prediction_keybind(true, window, cx)
7573 .keystroke()
7574 {
7575 modifiers_held = modifiers_held
7576 || (&accept_partial_keystroke.modifiers == modifiers
7577 && accept_partial_keystroke.modifiers.modified());
7578 }
7579
7580 if modifiers_held {
7581 if matches!(
7582 self.edit_prediction_preview,
7583 EditPredictionPreview::Inactive { .. }
7584 ) {
7585 self.edit_prediction_preview = EditPredictionPreview::Active {
7586 previous_scroll_position: None,
7587 since: Instant::now(),
7588 };
7589
7590 self.update_visible_inline_completion(window, cx);
7591 cx.notify();
7592 }
7593 } else if let EditPredictionPreview::Active {
7594 previous_scroll_position,
7595 since,
7596 } = self.edit_prediction_preview
7597 {
7598 if let (Some(previous_scroll_position), Some(position_map)) =
7599 (previous_scroll_position, self.last_position_map.as_ref())
7600 {
7601 self.set_scroll_position(
7602 previous_scroll_position
7603 .scroll_position(&position_map.snapshot.display_snapshot),
7604 window,
7605 cx,
7606 );
7607 }
7608
7609 self.edit_prediction_preview = EditPredictionPreview::Inactive {
7610 released_too_fast: since.elapsed() < Duration::from_millis(200),
7611 };
7612 self.clear_row_highlights::<EditPredictionPreview>();
7613 self.update_visible_inline_completion(window, cx);
7614 cx.notify();
7615 }
7616 }
7617
7618 fn update_visible_inline_completion(
7619 &mut self,
7620 _window: &mut Window,
7621 cx: &mut Context<Self>,
7622 ) -> Option<()> {
7623 let selection = self.selections.newest_anchor();
7624 let cursor = selection.head();
7625 let multibuffer = self.buffer.read(cx).snapshot(cx);
7626 let offset_selection = selection.map(|endpoint| endpoint.to_offset(&multibuffer));
7627 let excerpt_id = cursor.excerpt_id;
7628
7629 let show_in_menu = self.show_edit_predictions_in_menu();
7630 let completions_menu_has_precedence = !show_in_menu
7631 && (self.context_menu.borrow().is_some()
7632 || (!self.completion_tasks.is_empty() && !self.has_active_inline_completion()));
7633
7634 if completions_menu_has_precedence
7635 || !offset_selection.is_empty()
7636 || self
7637 .active_inline_completion
7638 .as_ref()
7639 .map_or(false, |completion| {
7640 let invalidation_range = completion.invalidation_range.to_offset(&multibuffer);
7641 let invalidation_range = invalidation_range.start..=invalidation_range.end;
7642 !invalidation_range.contains(&offset_selection.head())
7643 })
7644 {
7645 self.discard_inline_completion(false, cx);
7646 return None;
7647 }
7648
7649 self.take_active_inline_completion(cx);
7650 let Some(provider) = self.edit_prediction_provider() else {
7651 self.edit_prediction_settings = EditPredictionSettings::Disabled;
7652 return None;
7653 };
7654
7655 let (buffer, cursor_buffer_position) =
7656 self.buffer.read(cx).text_anchor_for_position(cursor, cx)?;
7657
7658 self.edit_prediction_settings =
7659 self.edit_prediction_settings_at_position(&buffer, cursor_buffer_position, cx);
7660
7661 self.edit_prediction_indent_conflict = multibuffer.is_line_whitespace_upto(cursor);
7662
7663 if self.edit_prediction_indent_conflict {
7664 let cursor_point = cursor.to_point(&multibuffer);
7665
7666 let indents = multibuffer.suggested_indents(cursor_point.row..cursor_point.row + 1, cx);
7667
7668 if let Some((_, indent)) = indents.iter().next() {
7669 if indent.len == cursor_point.column {
7670 self.edit_prediction_indent_conflict = false;
7671 }
7672 }
7673 }
7674
7675 let inline_completion = provider.suggest(&buffer, cursor_buffer_position, cx)?;
7676 let edits = inline_completion
7677 .edits
7678 .into_iter()
7679 .flat_map(|(range, new_text)| {
7680 let start = multibuffer.anchor_in_excerpt(excerpt_id, range.start)?;
7681 let end = multibuffer.anchor_in_excerpt(excerpt_id, range.end)?;
7682 Some((start..end, new_text))
7683 })
7684 .collect::<Vec<_>>();
7685 if edits.is_empty() {
7686 return None;
7687 }
7688
7689 let first_edit_start = edits.first().unwrap().0.start;
7690 let first_edit_start_point = first_edit_start.to_point(&multibuffer);
7691 let edit_start_row = first_edit_start_point.row.saturating_sub(2);
7692
7693 let last_edit_end = edits.last().unwrap().0.end;
7694 let last_edit_end_point = last_edit_end.to_point(&multibuffer);
7695 let edit_end_row = cmp::min(multibuffer.max_point().row, last_edit_end_point.row + 2);
7696
7697 let cursor_row = cursor.to_point(&multibuffer).row;
7698
7699 let snapshot = multibuffer.buffer_for_excerpt(excerpt_id).cloned()?;
7700
7701 let mut inlay_ids = Vec::new();
7702 let invalidation_row_range;
7703 let move_invalidation_row_range = if cursor_row < edit_start_row {
7704 Some(cursor_row..edit_end_row)
7705 } else if cursor_row > edit_end_row {
7706 Some(edit_start_row..cursor_row)
7707 } else {
7708 None
7709 };
7710 let is_move =
7711 move_invalidation_row_range.is_some() || self.inline_completions_hidden_for_vim_mode;
7712 let completion = if is_move {
7713 invalidation_row_range =
7714 move_invalidation_row_range.unwrap_or(edit_start_row..edit_end_row);
7715 let target = first_edit_start;
7716 InlineCompletion::Move { target, snapshot }
7717 } else {
7718 let show_completions_in_buffer = !self.edit_prediction_visible_in_cursor_popover(true)
7719 && !self.inline_completions_hidden_for_vim_mode;
7720
7721 if show_completions_in_buffer {
7722 if edits
7723 .iter()
7724 .all(|(range, _)| range.to_offset(&multibuffer).is_empty())
7725 {
7726 let mut inlays = Vec::new();
7727 for (range, new_text) in &edits {
7728 let inlay = Inlay::inline_completion(
7729 post_inc(&mut self.next_inlay_id),
7730 range.start,
7731 new_text.as_str(),
7732 );
7733 inlay_ids.push(inlay.id);
7734 inlays.push(inlay);
7735 }
7736
7737 self.splice_inlays(&[], inlays, cx);
7738 } else {
7739 let background_color = cx.theme().status().deleted_background;
7740 self.highlight_text::<InlineCompletionHighlight>(
7741 edits.iter().map(|(range, _)| range.clone()).collect(),
7742 HighlightStyle {
7743 background_color: Some(background_color),
7744 ..Default::default()
7745 },
7746 cx,
7747 );
7748 }
7749 }
7750
7751 invalidation_row_range = edit_start_row..edit_end_row;
7752
7753 let display_mode = if all_edits_insertions_or_deletions(&edits, &multibuffer) {
7754 if provider.show_tab_accept_marker() {
7755 EditDisplayMode::TabAccept
7756 } else {
7757 EditDisplayMode::Inline
7758 }
7759 } else {
7760 EditDisplayMode::DiffPopover
7761 };
7762
7763 InlineCompletion::Edit {
7764 edits,
7765 edit_preview: inline_completion.edit_preview,
7766 display_mode,
7767 snapshot,
7768 }
7769 };
7770
7771 let invalidation_range = multibuffer
7772 .anchor_before(Point::new(invalidation_row_range.start, 0))
7773 ..multibuffer.anchor_after(Point::new(
7774 invalidation_row_range.end,
7775 multibuffer.line_len(MultiBufferRow(invalidation_row_range.end)),
7776 ));
7777
7778 self.stale_inline_completion_in_menu = None;
7779 self.active_inline_completion = Some(InlineCompletionState {
7780 inlay_ids,
7781 completion,
7782 completion_id: inline_completion.id,
7783 invalidation_range,
7784 });
7785
7786 cx.notify();
7787
7788 Some(())
7789 }
7790
7791 pub fn edit_prediction_provider(&self) -> Option<Arc<dyn InlineCompletionProviderHandle>> {
7792 Some(self.edit_prediction_provider.as_ref()?.provider.clone())
7793 }
7794
7795 fn clear_tasks(&mut self) {
7796 self.tasks.clear()
7797 }
7798
7799 fn insert_tasks(&mut self, key: (BufferId, BufferRow), value: RunnableTasks) {
7800 if self.tasks.insert(key, value).is_some() {
7801 // This case should hopefully be rare, but just in case...
7802 log::error!(
7803 "multiple different run targets found on a single line, only the last target will be rendered"
7804 )
7805 }
7806 }
7807
7808 /// Get all display points of breakpoints that will be rendered within editor
7809 ///
7810 /// This function is used to handle overlaps between breakpoints and Code action/runner symbol.
7811 /// It's also used to set the color of line numbers with breakpoints to the breakpoint color.
7812 /// TODO debugger: Use this function to color toggle symbols that house nested breakpoints
7813 fn active_breakpoints(
7814 &self,
7815 range: Range<DisplayRow>,
7816 window: &mut Window,
7817 cx: &mut Context<Self>,
7818 ) -> HashMap<DisplayRow, (Anchor, Breakpoint, Option<BreakpointSessionState>)> {
7819 let mut breakpoint_display_points = HashMap::default();
7820
7821 let Some(breakpoint_store) = self.breakpoint_store.clone() else {
7822 return breakpoint_display_points;
7823 };
7824
7825 let snapshot = self.snapshot(window, cx);
7826
7827 let multi_buffer_snapshot = &snapshot.display_snapshot.buffer_snapshot;
7828 let Some(project) = self.project.as_ref() else {
7829 return breakpoint_display_points;
7830 };
7831
7832 let range = snapshot.display_point_to_point(DisplayPoint::new(range.start, 0), Bias::Left)
7833 ..snapshot.display_point_to_point(DisplayPoint::new(range.end, 0), Bias::Right);
7834
7835 for (buffer_snapshot, range, excerpt_id) in
7836 multi_buffer_snapshot.range_to_buffer_ranges(range)
7837 {
7838 let Some(buffer) = project
7839 .read(cx)
7840 .buffer_for_id(buffer_snapshot.remote_id(), cx)
7841 else {
7842 continue;
7843 };
7844 let breakpoints = breakpoint_store.read(cx).breakpoints(
7845 &buffer,
7846 Some(
7847 buffer_snapshot.anchor_before(range.start)
7848 ..buffer_snapshot.anchor_after(range.end),
7849 ),
7850 buffer_snapshot,
7851 cx,
7852 );
7853 for (breakpoint, state) in breakpoints {
7854 let multi_buffer_anchor =
7855 Anchor::in_buffer(excerpt_id, buffer_snapshot.remote_id(), breakpoint.position);
7856 let position = multi_buffer_anchor
7857 .to_point(&multi_buffer_snapshot)
7858 .to_display_point(&snapshot);
7859
7860 breakpoint_display_points.insert(
7861 position.row(),
7862 (multi_buffer_anchor, breakpoint.bp.clone(), state),
7863 );
7864 }
7865 }
7866
7867 breakpoint_display_points
7868 }
7869
7870 fn breakpoint_context_menu(
7871 &self,
7872 anchor: Anchor,
7873 window: &mut Window,
7874 cx: &mut Context<Self>,
7875 ) -> Entity<ui::ContextMenu> {
7876 let weak_editor = cx.weak_entity();
7877 let focus_handle = self.focus_handle(cx);
7878
7879 let row = self
7880 .buffer
7881 .read(cx)
7882 .snapshot(cx)
7883 .summary_for_anchor::<Point>(&anchor)
7884 .row;
7885
7886 let breakpoint = self
7887 .breakpoint_at_row(row, window, cx)
7888 .map(|(anchor, bp)| (anchor, Arc::from(bp)));
7889
7890 let log_breakpoint_msg = if breakpoint.as_ref().is_some_and(|bp| bp.1.message.is_some()) {
7891 "Edit Log Breakpoint"
7892 } else {
7893 "Set Log Breakpoint"
7894 };
7895
7896 let condition_breakpoint_msg = if breakpoint
7897 .as_ref()
7898 .is_some_and(|bp| bp.1.condition.is_some())
7899 {
7900 "Edit Condition Breakpoint"
7901 } else {
7902 "Set Condition Breakpoint"
7903 };
7904
7905 let hit_condition_breakpoint_msg = if breakpoint
7906 .as_ref()
7907 .is_some_and(|bp| bp.1.hit_condition.is_some())
7908 {
7909 "Edit Hit Condition Breakpoint"
7910 } else {
7911 "Set Hit Condition Breakpoint"
7912 };
7913
7914 let set_breakpoint_msg = if breakpoint.as_ref().is_some() {
7915 "Unset Breakpoint"
7916 } else {
7917 "Set Breakpoint"
7918 };
7919
7920 let run_to_cursor = window.is_action_available(&RunToCursor, cx);
7921
7922 let toggle_state_msg = breakpoint.as_ref().map_or(None, |bp| match bp.1.state {
7923 BreakpointState::Enabled => Some("Disable"),
7924 BreakpointState::Disabled => Some("Enable"),
7925 });
7926
7927 let (anchor, breakpoint) =
7928 breakpoint.unwrap_or_else(|| (anchor, Arc::new(Breakpoint::new_standard())));
7929
7930 ui::ContextMenu::build(window, cx, |menu, _, _cx| {
7931 menu.on_blur_subscription(Subscription::new(|| {}))
7932 .context(focus_handle)
7933 .when(run_to_cursor, |this| {
7934 let weak_editor = weak_editor.clone();
7935 this.entry("Run to cursor", None, move |window, cx| {
7936 weak_editor
7937 .update(cx, |editor, cx| {
7938 editor.change_selections(
7939 SelectionEffects::no_scroll(),
7940 window,
7941 cx,
7942 |s| s.select_ranges([Point::new(row, 0)..Point::new(row, 0)]),
7943 );
7944 })
7945 .ok();
7946
7947 window.dispatch_action(Box::new(RunToCursor), cx);
7948 })
7949 .separator()
7950 })
7951 .when_some(toggle_state_msg, |this, msg| {
7952 this.entry(msg, None, {
7953 let weak_editor = weak_editor.clone();
7954 let breakpoint = breakpoint.clone();
7955 move |_window, cx| {
7956 weak_editor
7957 .update(cx, |this, cx| {
7958 this.edit_breakpoint_at_anchor(
7959 anchor,
7960 breakpoint.as_ref().clone(),
7961 BreakpointEditAction::InvertState,
7962 cx,
7963 );
7964 })
7965 .log_err();
7966 }
7967 })
7968 })
7969 .entry(set_breakpoint_msg, None, {
7970 let weak_editor = weak_editor.clone();
7971 let breakpoint = breakpoint.clone();
7972 move |_window, cx| {
7973 weak_editor
7974 .update(cx, |this, cx| {
7975 this.edit_breakpoint_at_anchor(
7976 anchor,
7977 breakpoint.as_ref().clone(),
7978 BreakpointEditAction::Toggle,
7979 cx,
7980 );
7981 })
7982 .log_err();
7983 }
7984 })
7985 .entry(log_breakpoint_msg, None, {
7986 let breakpoint = breakpoint.clone();
7987 let weak_editor = weak_editor.clone();
7988 move |window, cx| {
7989 weak_editor
7990 .update(cx, |this, cx| {
7991 this.add_edit_breakpoint_block(
7992 anchor,
7993 breakpoint.as_ref(),
7994 BreakpointPromptEditAction::Log,
7995 window,
7996 cx,
7997 );
7998 })
7999 .log_err();
8000 }
8001 })
8002 .entry(condition_breakpoint_msg, None, {
8003 let breakpoint = breakpoint.clone();
8004 let weak_editor = weak_editor.clone();
8005 move |window, cx| {
8006 weak_editor
8007 .update(cx, |this, cx| {
8008 this.add_edit_breakpoint_block(
8009 anchor,
8010 breakpoint.as_ref(),
8011 BreakpointPromptEditAction::Condition,
8012 window,
8013 cx,
8014 );
8015 })
8016 .log_err();
8017 }
8018 })
8019 .entry(hit_condition_breakpoint_msg, None, move |window, cx| {
8020 weak_editor
8021 .update(cx, |this, cx| {
8022 this.add_edit_breakpoint_block(
8023 anchor,
8024 breakpoint.as_ref(),
8025 BreakpointPromptEditAction::HitCondition,
8026 window,
8027 cx,
8028 );
8029 })
8030 .log_err();
8031 })
8032 })
8033 }
8034
8035 fn render_breakpoint(
8036 &self,
8037 position: Anchor,
8038 row: DisplayRow,
8039 breakpoint: &Breakpoint,
8040 state: Option<BreakpointSessionState>,
8041 cx: &mut Context<Self>,
8042 ) -> IconButton {
8043 let is_rejected = state.is_some_and(|s| !s.verified);
8044 // Is it a breakpoint that shows up when hovering over gutter?
8045 let (is_phantom, collides_with_existing) = self.gutter_breakpoint_indicator.0.map_or(
8046 (false, false),
8047 |PhantomBreakpointIndicator {
8048 is_active,
8049 display_row,
8050 collides_with_existing_breakpoint,
8051 }| {
8052 (
8053 is_active && display_row == row,
8054 collides_with_existing_breakpoint,
8055 )
8056 },
8057 );
8058
8059 let (color, icon) = {
8060 let icon = match (&breakpoint.message.is_some(), breakpoint.is_disabled()) {
8061 (false, false) => ui::IconName::DebugBreakpoint,
8062 (true, false) => ui::IconName::DebugLogBreakpoint,
8063 (false, true) => ui::IconName::DebugDisabledBreakpoint,
8064 (true, true) => ui::IconName::DebugDisabledLogBreakpoint,
8065 };
8066
8067 let color = if is_phantom {
8068 Color::Hint
8069 } else if is_rejected {
8070 Color::Disabled
8071 } else {
8072 Color::Debugger
8073 };
8074
8075 (color, icon)
8076 };
8077
8078 let breakpoint = Arc::from(breakpoint.clone());
8079
8080 let alt_as_text = gpui::Keystroke {
8081 modifiers: Modifiers::secondary_key(),
8082 ..Default::default()
8083 };
8084 let primary_action_text = if breakpoint.is_disabled() {
8085 "Enable breakpoint"
8086 } else if is_phantom && !collides_with_existing {
8087 "Set breakpoint"
8088 } else {
8089 "Unset breakpoint"
8090 };
8091 let focus_handle = self.focus_handle.clone();
8092
8093 let meta = if is_rejected {
8094 SharedString::from("No executable code is associated with this line.")
8095 } else if collides_with_existing && !breakpoint.is_disabled() {
8096 SharedString::from(format!(
8097 "{alt_as_text}-click to disable,\nright-click for more options."
8098 ))
8099 } else {
8100 SharedString::from("Right-click for more options.")
8101 };
8102 IconButton::new(("breakpoint_indicator", row.0 as usize), icon)
8103 .icon_size(IconSize::XSmall)
8104 .size(ui::ButtonSize::None)
8105 .when(is_rejected, |this| {
8106 this.indicator(Indicator::icon(Icon::new(IconName::Warning)).color(Color::Warning))
8107 })
8108 .icon_color(color)
8109 .style(ButtonStyle::Transparent)
8110 .on_click(cx.listener({
8111 let breakpoint = breakpoint.clone();
8112
8113 move |editor, event: &ClickEvent, window, cx| {
8114 let edit_action = if event.modifiers().platform || breakpoint.is_disabled() {
8115 BreakpointEditAction::InvertState
8116 } else {
8117 BreakpointEditAction::Toggle
8118 };
8119
8120 window.focus(&editor.focus_handle(cx));
8121 editor.edit_breakpoint_at_anchor(
8122 position,
8123 breakpoint.as_ref().clone(),
8124 edit_action,
8125 cx,
8126 );
8127 }
8128 }))
8129 .on_right_click(cx.listener(move |editor, event: &ClickEvent, window, cx| {
8130 editor.set_breakpoint_context_menu(
8131 row,
8132 Some(position),
8133 event.down.position,
8134 window,
8135 cx,
8136 );
8137 }))
8138 .tooltip(move |window, cx| {
8139 Tooltip::with_meta_in(
8140 primary_action_text,
8141 Some(&ToggleBreakpoint),
8142 meta.clone(),
8143 &focus_handle,
8144 window,
8145 cx,
8146 )
8147 })
8148 }
8149
8150 fn build_tasks_context(
8151 project: &Entity<Project>,
8152 buffer: &Entity<Buffer>,
8153 buffer_row: u32,
8154 tasks: &Arc<RunnableTasks>,
8155 cx: &mut Context<Self>,
8156 ) -> Task<Option<task::TaskContext>> {
8157 let position = Point::new(buffer_row, tasks.column);
8158 let range_start = buffer.read(cx).anchor_at(position, Bias::Right);
8159 let location = Location {
8160 buffer: buffer.clone(),
8161 range: range_start..range_start,
8162 };
8163 // Fill in the environmental variables from the tree-sitter captures
8164 let mut captured_task_variables = TaskVariables::default();
8165 for (capture_name, value) in tasks.extra_variables.clone() {
8166 captured_task_variables.insert(
8167 task::VariableName::Custom(capture_name.into()),
8168 value.clone(),
8169 );
8170 }
8171 project.update(cx, |project, cx| {
8172 project.task_store().update(cx, |task_store, cx| {
8173 task_store.task_context_for_location(captured_task_variables, location, cx)
8174 })
8175 })
8176 }
8177
8178 pub fn spawn_nearest_task(
8179 &mut self,
8180 action: &SpawnNearestTask,
8181 window: &mut Window,
8182 cx: &mut Context<Self>,
8183 ) {
8184 let Some((workspace, _)) = self.workspace.clone() else {
8185 return;
8186 };
8187 let Some(project) = self.project.clone() else {
8188 return;
8189 };
8190
8191 // Try to find a closest, enclosing node using tree-sitter that has a
8192 // task
8193 let Some((buffer, buffer_row, tasks)) = self
8194 .find_enclosing_node_task(cx)
8195 // Or find the task that's closest in row-distance.
8196 .or_else(|| self.find_closest_task(cx))
8197 else {
8198 return;
8199 };
8200
8201 let reveal_strategy = action.reveal;
8202 let task_context = Self::build_tasks_context(&project, &buffer, buffer_row, &tasks, cx);
8203 cx.spawn_in(window, async move |_, cx| {
8204 let context = task_context.await?;
8205 let (task_source_kind, mut resolved_task) = tasks.resolve(&context).next()?;
8206
8207 let resolved = &mut resolved_task.resolved;
8208 resolved.reveal = reveal_strategy;
8209
8210 workspace
8211 .update_in(cx, |workspace, window, cx| {
8212 workspace.schedule_resolved_task(
8213 task_source_kind,
8214 resolved_task,
8215 false,
8216 window,
8217 cx,
8218 );
8219 })
8220 .ok()
8221 })
8222 .detach();
8223 }
8224
8225 fn find_closest_task(
8226 &mut self,
8227 cx: &mut Context<Self>,
8228 ) -> Option<(Entity<Buffer>, u32, Arc<RunnableTasks>)> {
8229 let cursor_row = self.selections.newest_adjusted(cx).head().row;
8230
8231 let ((buffer_id, row), tasks) = self
8232 .tasks
8233 .iter()
8234 .min_by_key(|((_, row), _)| cursor_row.abs_diff(*row))?;
8235
8236 let buffer = self.buffer.read(cx).buffer(*buffer_id)?;
8237 let tasks = Arc::new(tasks.to_owned());
8238 Some((buffer, *row, tasks))
8239 }
8240
8241 fn find_enclosing_node_task(
8242 &mut self,
8243 cx: &mut Context<Self>,
8244 ) -> Option<(Entity<Buffer>, u32, Arc<RunnableTasks>)> {
8245 let snapshot = self.buffer.read(cx).snapshot(cx);
8246 let offset = self.selections.newest::<usize>(cx).head();
8247 let excerpt = snapshot.excerpt_containing(offset..offset)?;
8248 let buffer_id = excerpt.buffer().remote_id();
8249
8250 let layer = excerpt.buffer().syntax_layer_at(offset)?;
8251 let mut cursor = layer.node().walk();
8252
8253 while cursor.goto_first_child_for_byte(offset).is_some() {
8254 if cursor.node().end_byte() == offset {
8255 cursor.goto_next_sibling();
8256 }
8257 }
8258
8259 // Ascend to the smallest ancestor that contains the range and has a task.
8260 loop {
8261 let node = cursor.node();
8262 let node_range = node.byte_range();
8263 let symbol_start_row = excerpt.buffer().offset_to_point(node.start_byte()).row;
8264
8265 // Check if this node contains our offset
8266 if node_range.start <= offset && node_range.end >= offset {
8267 // If it contains offset, check for task
8268 if let Some(tasks) = self.tasks.get(&(buffer_id, symbol_start_row)) {
8269 let buffer = self.buffer.read(cx).buffer(buffer_id)?;
8270 return Some((buffer, symbol_start_row, Arc::new(tasks.to_owned())));
8271 }
8272 }
8273
8274 if !cursor.goto_parent() {
8275 break;
8276 }
8277 }
8278 None
8279 }
8280
8281 fn render_run_indicator(
8282 &self,
8283 _style: &EditorStyle,
8284 is_active: bool,
8285 row: DisplayRow,
8286 breakpoint: Option<(Anchor, Breakpoint, Option<BreakpointSessionState>)>,
8287 cx: &mut Context<Self>,
8288 ) -> IconButton {
8289 let color = Color::Muted;
8290 let position = breakpoint.as_ref().map(|(anchor, _, _)| *anchor);
8291
8292 IconButton::new(("run_indicator", row.0 as usize), ui::IconName::Play)
8293 .shape(ui::IconButtonShape::Square)
8294 .icon_size(IconSize::XSmall)
8295 .icon_color(color)
8296 .toggle_state(is_active)
8297 .on_click(cx.listener(move |editor, e: &ClickEvent, window, cx| {
8298 let quick_launch = e.down.button == MouseButton::Left;
8299 window.focus(&editor.focus_handle(cx));
8300 editor.toggle_code_actions(
8301 &ToggleCodeActions {
8302 deployed_from: Some(CodeActionSource::RunMenu(row)),
8303 quick_launch,
8304 },
8305 window,
8306 cx,
8307 );
8308 }))
8309 .on_right_click(cx.listener(move |editor, event: &ClickEvent, window, cx| {
8310 editor.set_breakpoint_context_menu(row, position, event.down.position, window, cx);
8311 }))
8312 }
8313
8314 pub fn context_menu_visible(&self) -> bool {
8315 !self.edit_prediction_preview_is_active()
8316 && self
8317 .context_menu
8318 .borrow()
8319 .as_ref()
8320 .map_or(false, |menu| menu.visible())
8321 }
8322
8323 pub fn context_menu_origin(&self) -> Option<ContextMenuOrigin> {
8324 self.context_menu
8325 .borrow()
8326 .as_ref()
8327 .map(|menu| menu.origin())
8328 }
8329
8330 pub fn set_context_menu_options(&mut self, options: ContextMenuOptions) {
8331 self.context_menu_options = Some(options);
8332 }
8333
8334 const EDIT_PREDICTION_POPOVER_PADDING_X: Pixels = Pixels(24.);
8335 const EDIT_PREDICTION_POPOVER_PADDING_Y: Pixels = Pixels(2.);
8336
8337 fn render_edit_prediction_popover(
8338 &mut self,
8339 text_bounds: &Bounds<Pixels>,
8340 content_origin: gpui::Point<Pixels>,
8341 right_margin: Pixels,
8342 editor_snapshot: &EditorSnapshot,
8343 visible_row_range: Range<DisplayRow>,
8344 scroll_top: f32,
8345 scroll_bottom: f32,
8346 line_layouts: &[LineWithInvisibles],
8347 line_height: Pixels,
8348 scroll_pixel_position: gpui::Point<Pixels>,
8349 newest_selection_head: Option<DisplayPoint>,
8350 editor_width: Pixels,
8351 style: &EditorStyle,
8352 window: &mut Window,
8353 cx: &mut App,
8354 ) -> Option<(AnyElement, gpui::Point<Pixels>)> {
8355 if self.mode().is_minimap() {
8356 return None;
8357 }
8358 let active_inline_completion = self.active_inline_completion.as_ref()?;
8359
8360 if self.edit_prediction_visible_in_cursor_popover(true) {
8361 return None;
8362 }
8363
8364 match &active_inline_completion.completion {
8365 InlineCompletion::Move { target, .. } => {
8366 let target_display_point = target.to_display_point(editor_snapshot);
8367
8368 if self.edit_prediction_requires_modifier() {
8369 if !self.edit_prediction_preview_is_active() {
8370 return None;
8371 }
8372
8373 self.render_edit_prediction_modifier_jump_popover(
8374 text_bounds,
8375 content_origin,
8376 visible_row_range,
8377 line_layouts,
8378 line_height,
8379 scroll_pixel_position,
8380 newest_selection_head,
8381 target_display_point,
8382 window,
8383 cx,
8384 )
8385 } else {
8386 self.render_edit_prediction_eager_jump_popover(
8387 text_bounds,
8388 content_origin,
8389 editor_snapshot,
8390 visible_row_range,
8391 scroll_top,
8392 scroll_bottom,
8393 line_height,
8394 scroll_pixel_position,
8395 target_display_point,
8396 editor_width,
8397 window,
8398 cx,
8399 )
8400 }
8401 }
8402 InlineCompletion::Edit {
8403 display_mode: EditDisplayMode::Inline,
8404 ..
8405 } => None,
8406 InlineCompletion::Edit {
8407 display_mode: EditDisplayMode::TabAccept,
8408 edits,
8409 ..
8410 } => {
8411 let range = &edits.first()?.0;
8412 let target_display_point = range.end.to_display_point(editor_snapshot);
8413
8414 self.render_edit_prediction_end_of_line_popover(
8415 "Accept",
8416 editor_snapshot,
8417 visible_row_range,
8418 target_display_point,
8419 line_height,
8420 scroll_pixel_position,
8421 content_origin,
8422 editor_width,
8423 window,
8424 cx,
8425 )
8426 }
8427 InlineCompletion::Edit {
8428 edits,
8429 edit_preview,
8430 display_mode: EditDisplayMode::DiffPopover,
8431 snapshot,
8432 } => self.render_edit_prediction_diff_popover(
8433 text_bounds,
8434 content_origin,
8435 right_margin,
8436 editor_snapshot,
8437 visible_row_range,
8438 line_layouts,
8439 line_height,
8440 scroll_pixel_position,
8441 newest_selection_head,
8442 editor_width,
8443 style,
8444 edits,
8445 edit_preview,
8446 snapshot,
8447 window,
8448 cx,
8449 ),
8450 }
8451 }
8452
8453 fn render_edit_prediction_modifier_jump_popover(
8454 &mut self,
8455 text_bounds: &Bounds<Pixels>,
8456 content_origin: gpui::Point<Pixels>,
8457 visible_row_range: Range<DisplayRow>,
8458 line_layouts: &[LineWithInvisibles],
8459 line_height: Pixels,
8460 scroll_pixel_position: gpui::Point<Pixels>,
8461 newest_selection_head: Option<DisplayPoint>,
8462 target_display_point: DisplayPoint,
8463 window: &mut Window,
8464 cx: &mut App,
8465 ) -> Option<(AnyElement, gpui::Point<Pixels>)> {
8466 let scrolled_content_origin =
8467 content_origin - gpui::Point::new(scroll_pixel_position.x, Pixels(0.0));
8468
8469 const SCROLL_PADDING_Y: Pixels = px(12.);
8470
8471 if target_display_point.row() < visible_row_range.start {
8472 return self.render_edit_prediction_scroll_popover(
8473 |_| SCROLL_PADDING_Y,
8474 IconName::ArrowUp,
8475 visible_row_range,
8476 line_layouts,
8477 newest_selection_head,
8478 scrolled_content_origin,
8479 window,
8480 cx,
8481 );
8482 } else if target_display_point.row() >= visible_row_range.end {
8483 return self.render_edit_prediction_scroll_popover(
8484 |size| text_bounds.size.height - size.height - SCROLL_PADDING_Y,
8485 IconName::ArrowDown,
8486 visible_row_range,
8487 line_layouts,
8488 newest_selection_head,
8489 scrolled_content_origin,
8490 window,
8491 cx,
8492 );
8493 }
8494
8495 const POLE_WIDTH: Pixels = px(2.);
8496
8497 let line_layout =
8498 line_layouts.get(target_display_point.row().minus(visible_row_range.start) as usize)?;
8499 let target_column = target_display_point.column() as usize;
8500
8501 let target_x = line_layout.x_for_index(target_column);
8502 let target_y =
8503 (target_display_point.row().as_f32() * line_height) - scroll_pixel_position.y;
8504
8505 let flag_on_right = target_x < text_bounds.size.width / 2.;
8506
8507 let mut border_color = Self::edit_prediction_callout_popover_border_color(cx);
8508 border_color.l += 0.001;
8509
8510 let mut element = v_flex()
8511 .items_end()
8512 .when(flag_on_right, |el| el.items_start())
8513 .child(if flag_on_right {
8514 self.render_edit_prediction_line_popover("Jump", None, window, cx)?
8515 .rounded_bl(px(0.))
8516 .rounded_tl(px(0.))
8517 .border_l_2()
8518 .border_color(border_color)
8519 } else {
8520 self.render_edit_prediction_line_popover("Jump", None, window, cx)?
8521 .rounded_br(px(0.))
8522 .rounded_tr(px(0.))
8523 .border_r_2()
8524 .border_color(border_color)
8525 })
8526 .child(div().w(POLE_WIDTH).bg(border_color).h(line_height))
8527 .into_any();
8528
8529 let size = element.layout_as_root(AvailableSpace::min_size(), window, cx);
8530
8531 let mut origin = scrolled_content_origin + point(target_x, target_y)
8532 - point(
8533 if flag_on_right {
8534 POLE_WIDTH
8535 } else {
8536 size.width - POLE_WIDTH
8537 },
8538 size.height - line_height,
8539 );
8540
8541 origin.x = origin.x.max(content_origin.x);
8542
8543 element.prepaint_at(origin, window, cx);
8544
8545 Some((element, origin))
8546 }
8547
8548 fn render_edit_prediction_scroll_popover(
8549 &mut self,
8550 to_y: impl Fn(Size<Pixels>) -> Pixels,
8551 scroll_icon: IconName,
8552 visible_row_range: Range<DisplayRow>,
8553 line_layouts: &[LineWithInvisibles],
8554 newest_selection_head: Option<DisplayPoint>,
8555 scrolled_content_origin: gpui::Point<Pixels>,
8556 window: &mut Window,
8557 cx: &mut App,
8558 ) -> Option<(AnyElement, gpui::Point<Pixels>)> {
8559 let mut element = self
8560 .render_edit_prediction_line_popover("Scroll", Some(scroll_icon), window, cx)?
8561 .into_any();
8562
8563 let size = element.layout_as_root(AvailableSpace::min_size(), window, cx);
8564
8565 let cursor = newest_selection_head?;
8566 let cursor_row_layout =
8567 line_layouts.get(cursor.row().minus(visible_row_range.start) as usize)?;
8568 let cursor_column = cursor.column() as usize;
8569
8570 let cursor_character_x = cursor_row_layout.x_for_index(cursor_column);
8571
8572 let origin = scrolled_content_origin + point(cursor_character_x, to_y(size));
8573
8574 element.prepaint_at(origin, window, cx);
8575 Some((element, origin))
8576 }
8577
8578 fn render_edit_prediction_eager_jump_popover(
8579 &mut self,
8580 text_bounds: &Bounds<Pixels>,
8581 content_origin: gpui::Point<Pixels>,
8582 editor_snapshot: &EditorSnapshot,
8583 visible_row_range: Range<DisplayRow>,
8584 scroll_top: f32,
8585 scroll_bottom: f32,
8586 line_height: Pixels,
8587 scroll_pixel_position: gpui::Point<Pixels>,
8588 target_display_point: DisplayPoint,
8589 editor_width: Pixels,
8590 window: &mut Window,
8591 cx: &mut App,
8592 ) -> Option<(AnyElement, gpui::Point<Pixels>)> {
8593 if target_display_point.row().as_f32() < scroll_top {
8594 let mut element = self
8595 .render_edit_prediction_line_popover(
8596 "Jump to Edit",
8597 Some(IconName::ArrowUp),
8598 window,
8599 cx,
8600 )?
8601 .into_any();
8602
8603 let size = element.layout_as_root(AvailableSpace::min_size(), window, cx);
8604 let offset = point(
8605 (text_bounds.size.width - size.width) / 2.,
8606 Self::EDIT_PREDICTION_POPOVER_PADDING_Y,
8607 );
8608
8609 let origin = text_bounds.origin + offset;
8610 element.prepaint_at(origin, window, cx);
8611 Some((element, origin))
8612 } else if (target_display_point.row().as_f32() + 1.) > scroll_bottom {
8613 let mut element = self
8614 .render_edit_prediction_line_popover(
8615 "Jump to Edit",
8616 Some(IconName::ArrowDown),
8617 window,
8618 cx,
8619 )?
8620 .into_any();
8621
8622 let size = element.layout_as_root(AvailableSpace::min_size(), window, cx);
8623 let offset = point(
8624 (text_bounds.size.width - size.width) / 2.,
8625 text_bounds.size.height - size.height - Self::EDIT_PREDICTION_POPOVER_PADDING_Y,
8626 );
8627
8628 let origin = text_bounds.origin + offset;
8629 element.prepaint_at(origin, window, cx);
8630 Some((element, origin))
8631 } else {
8632 self.render_edit_prediction_end_of_line_popover(
8633 "Jump to Edit",
8634 editor_snapshot,
8635 visible_row_range,
8636 target_display_point,
8637 line_height,
8638 scroll_pixel_position,
8639 content_origin,
8640 editor_width,
8641 window,
8642 cx,
8643 )
8644 }
8645 }
8646
8647 fn render_edit_prediction_end_of_line_popover(
8648 self: &mut Editor,
8649 label: &'static str,
8650 editor_snapshot: &EditorSnapshot,
8651 visible_row_range: Range<DisplayRow>,
8652 target_display_point: DisplayPoint,
8653 line_height: Pixels,
8654 scroll_pixel_position: gpui::Point<Pixels>,
8655 content_origin: gpui::Point<Pixels>,
8656 editor_width: Pixels,
8657 window: &mut Window,
8658 cx: &mut App,
8659 ) -> Option<(AnyElement, gpui::Point<Pixels>)> {
8660 let target_line_end = DisplayPoint::new(
8661 target_display_point.row(),
8662 editor_snapshot.line_len(target_display_point.row()),
8663 );
8664
8665 let mut element = self
8666 .render_edit_prediction_line_popover(label, None, window, cx)?
8667 .into_any();
8668
8669 let size = element.layout_as_root(AvailableSpace::min_size(), window, cx);
8670
8671 let line_origin = self.display_to_pixel_point(target_line_end, editor_snapshot, window)?;
8672
8673 let start_point = content_origin - point(scroll_pixel_position.x, Pixels::ZERO);
8674 let mut origin = start_point
8675 + line_origin
8676 + point(Self::EDIT_PREDICTION_POPOVER_PADDING_X, Pixels::ZERO);
8677 origin.x = origin.x.max(content_origin.x);
8678
8679 let max_x = content_origin.x + editor_width - size.width;
8680
8681 if origin.x > max_x {
8682 let offset = line_height + Self::EDIT_PREDICTION_POPOVER_PADDING_Y;
8683
8684 let icon = if visible_row_range.contains(&(target_display_point.row() + 2)) {
8685 origin.y += offset;
8686 IconName::ArrowUp
8687 } else {
8688 origin.y -= offset;
8689 IconName::ArrowDown
8690 };
8691
8692 element = self
8693 .render_edit_prediction_line_popover(label, Some(icon), window, cx)?
8694 .into_any();
8695
8696 let size = element.layout_as_root(AvailableSpace::min_size(), window, cx);
8697
8698 origin.x = content_origin.x + editor_width - size.width - px(2.);
8699 }
8700
8701 element.prepaint_at(origin, window, cx);
8702 Some((element, origin))
8703 }
8704
8705 fn render_edit_prediction_diff_popover(
8706 self: &Editor,
8707 text_bounds: &Bounds<Pixels>,
8708 content_origin: gpui::Point<Pixels>,
8709 right_margin: Pixels,
8710 editor_snapshot: &EditorSnapshot,
8711 visible_row_range: Range<DisplayRow>,
8712 line_layouts: &[LineWithInvisibles],
8713 line_height: Pixels,
8714 scroll_pixel_position: gpui::Point<Pixels>,
8715 newest_selection_head: Option<DisplayPoint>,
8716 editor_width: Pixels,
8717 style: &EditorStyle,
8718 edits: &Vec<(Range<Anchor>, String)>,
8719 edit_preview: &Option<language::EditPreview>,
8720 snapshot: &language::BufferSnapshot,
8721 window: &mut Window,
8722 cx: &mut App,
8723 ) -> Option<(AnyElement, gpui::Point<Pixels>)> {
8724 let edit_start = edits
8725 .first()
8726 .unwrap()
8727 .0
8728 .start
8729 .to_display_point(editor_snapshot);
8730 let edit_end = edits
8731 .last()
8732 .unwrap()
8733 .0
8734 .end
8735 .to_display_point(editor_snapshot);
8736
8737 let is_visible = visible_row_range.contains(&edit_start.row())
8738 || visible_row_range.contains(&edit_end.row());
8739 if !is_visible {
8740 return None;
8741 }
8742
8743 let highlighted_edits =
8744 crate::inline_completion_edit_text(&snapshot, edits, edit_preview.as_ref()?, false, cx);
8745
8746 let styled_text = highlighted_edits.to_styled_text(&style.text);
8747 let line_count = highlighted_edits.text.lines().count();
8748
8749 const BORDER_WIDTH: Pixels = px(1.);
8750
8751 let keybind = self.render_edit_prediction_accept_keybind(window, cx);
8752 let has_keybind = keybind.is_some();
8753
8754 let mut element = h_flex()
8755 .items_start()
8756 .child(
8757 h_flex()
8758 .bg(cx.theme().colors().editor_background)
8759 .border(BORDER_WIDTH)
8760 .shadow_xs()
8761 .border_color(cx.theme().colors().border)
8762 .rounded_l_lg()
8763 .when(line_count > 1, |el| el.rounded_br_lg())
8764 .pr_1()
8765 .child(styled_text),
8766 )
8767 .child(
8768 h_flex()
8769 .h(line_height + BORDER_WIDTH * 2.)
8770 .px_1p5()
8771 .gap_1()
8772 // Workaround: For some reason, there's a gap if we don't do this
8773 .ml(-BORDER_WIDTH)
8774 .shadow(vec![gpui::BoxShadow {
8775 color: gpui::black().opacity(0.05),
8776 offset: point(px(1.), px(1.)),
8777 blur_radius: px(2.),
8778 spread_radius: px(0.),
8779 }])
8780 .bg(Editor::edit_prediction_line_popover_bg_color(cx))
8781 .border(BORDER_WIDTH)
8782 .border_color(cx.theme().colors().border)
8783 .rounded_r_lg()
8784 .id("edit_prediction_diff_popover_keybind")
8785 .when(!has_keybind, |el| {
8786 let status_colors = cx.theme().status();
8787
8788 el.bg(status_colors.error_background)
8789 .border_color(status_colors.error.opacity(0.6))
8790 .child(Icon::new(IconName::Info).color(Color::Error))
8791 .cursor_default()
8792 .hoverable_tooltip(move |_window, cx| {
8793 cx.new(|_| MissingEditPredictionKeybindingTooltip).into()
8794 })
8795 })
8796 .children(keybind),
8797 )
8798 .into_any();
8799
8800 let longest_row =
8801 editor_snapshot.longest_row_in_range(edit_start.row()..edit_end.row() + 1);
8802 let longest_line_width = if visible_row_range.contains(&longest_row) {
8803 line_layouts[(longest_row.0 - visible_row_range.start.0) as usize].width
8804 } else {
8805 layout_line(
8806 longest_row,
8807 editor_snapshot,
8808 style,
8809 editor_width,
8810 |_| false,
8811 window,
8812 cx,
8813 )
8814 .width
8815 };
8816
8817 let viewport_bounds =
8818 Bounds::new(Default::default(), window.viewport_size()).extend(Edges {
8819 right: -right_margin,
8820 ..Default::default()
8821 });
8822
8823 let x_after_longest =
8824 text_bounds.origin.x + longest_line_width + Self::EDIT_PREDICTION_POPOVER_PADDING_X
8825 - scroll_pixel_position.x;
8826
8827 let element_bounds = element.layout_as_root(AvailableSpace::min_size(), window, cx);
8828
8829 // Fully visible if it can be displayed within the window (allow overlapping other
8830 // panes). However, this is only allowed if the popover starts within text_bounds.
8831 let can_position_to_the_right = x_after_longest < text_bounds.right()
8832 && x_after_longest + element_bounds.width < viewport_bounds.right();
8833
8834 let mut origin = if can_position_to_the_right {
8835 point(
8836 x_after_longest,
8837 text_bounds.origin.y + edit_start.row().as_f32() * line_height
8838 - scroll_pixel_position.y,
8839 )
8840 } else {
8841 let cursor_row = newest_selection_head.map(|head| head.row());
8842 let above_edit = edit_start
8843 .row()
8844 .0
8845 .checked_sub(line_count as u32)
8846 .map(DisplayRow);
8847 let below_edit = Some(edit_end.row() + 1);
8848 let above_cursor =
8849 cursor_row.and_then(|row| row.0.checked_sub(line_count as u32).map(DisplayRow));
8850 let below_cursor = cursor_row.map(|cursor_row| cursor_row + 1);
8851
8852 // Place the edit popover adjacent to the edit if there is a location
8853 // available that is onscreen and does not obscure the cursor. Otherwise,
8854 // place it adjacent to the cursor.
8855 let row_target = [above_edit, below_edit, above_cursor, below_cursor]
8856 .into_iter()
8857 .flatten()
8858 .find(|&start_row| {
8859 let end_row = start_row + line_count as u32;
8860 visible_row_range.contains(&start_row)
8861 && visible_row_range.contains(&end_row)
8862 && cursor_row.map_or(true, |cursor_row| {
8863 !((start_row..end_row).contains(&cursor_row))
8864 })
8865 })?;
8866
8867 content_origin
8868 + point(
8869 -scroll_pixel_position.x,
8870 row_target.as_f32() * line_height - scroll_pixel_position.y,
8871 )
8872 };
8873
8874 origin.x -= BORDER_WIDTH;
8875
8876 window.defer_draw(element, origin, 1);
8877
8878 // Do not return an element, since it will already be drawn due to defer_draw.
8879 None
8880 }
8881
8882 fn edit_prediction_cursor_popover_height(&self) -> Pixels {
8883 px(30.)
8884 }
8885
8886 fn current_user_player_color(&self, cx: &mut App) -> PlayerColor {
8887 if self.read_only(cx) {
8888 cx.theme().players().read_only()
8889 } else {
8890 self.style.as_ref().unwrap().local_player
8891 }
8892 }
8893
8894 fn render_edit_prediction_accept_keybind(
8895 &self,
8896 window: &mut Window,
8897 cx: &App,
8898 ) -> Option<AnyElement> {
8899 let accept_binding = self.accept_edit_prediction_keybind(false, window, cx);
8900 let accept_keystroke = accept_binding.keystroke()?;
8901
8902 let is_platform_style_mac = PlatformStyle::platform() == PlatformStyle::Mac;
8903
8904 let modifiers_color = if accept_keystroke.modifiers == window.modifiers() {
8905 Color::Accent
8906 } else {
8907 Color::Muted
8908 };
8909
8910 h_flex()
8911 .px_0p5()
8912 .when(is_platform_style_mac, |parent| parent.gap_0p5())
8913 .font(theme::ThemeSettings::get_global(cx).buffer_font.clone())
8914 .text_size(TextSize::XSmall.rems(cx))
8915 .child(h_flex().children(ui::render_modifiers(
8916 &accept_keystroke.modifiers,
8917 PlatformStyle::platform(),
8918 Some(modifiers_color),
8919 Some(IconSize::XSmall.rems().into()),
8920 true,
8921 )))
8922 .when(is_platform_style_mac, |parent| {
8923 parent.child(accept_keystroke.key.clone())
8924 })
8925 .when(!is_platform_style_mac, |parent| {
8926 parent.child(
8927 Key::new(
8928 util::capitalize(&accept_keystroke.key),
8929 Some(Color::Default),
8930 )
8931 .size(Some(IconSize::XSmall.rems().into())),
8932 )
8933 })
8934 .into_any()
8935 .into()
8936 }
8937
8938 fn render_edit_prediction_line_popover(
8939 &self,
8940 label: impl Into<SharedString>,
8941 icon: Option<IconName>,
8942 window: &mut Window,
8943 cx: &App,
8944 ) -> Option<Stateful<Div>> {
8945 let padding_right = if icon.is_some() { px(4.) } else { px(8.) };
8946
8947 let keybind = self.render_edit_prediction_accept_keybind(window, cx);
8948 let has_keybind = keybind.is_some();
8949
8950 let result = h_flex()
8951 .id("ep-line-popover")
8952 .py_0p5()
8953 .pl_1()
8954 .pr(padding_right)
8955 .gap_1()
8956 .rounded_md()
8957 .border_1()
8958 .bg(Self::edit_prediction_line_popover_bg_color(cx))
8959 .border_color(Self::edit_prediction_callout_popover_border_color(cx))
8960 .shadow_xs()
8961 .when(!has_keybind, |el| {
8962 let status_colors = cx.theme().status();
8963
8964 el.bg(status_colors.error_background)
8965 .border_color(status_colors.error.opacity(0.6))
8966 .pl_2()
8967 .child(Icon::new(IconName::ZedPredictError).color(Color::Error))
8968 .cursor_default()
8969 .hoverable_tooltip(move |_window, cx| {
8970 cx.new(|_| MissingEditPredictionKeybindingTooltip).into()
8971 })
8972 })
8973 .children(keybind)
8974 .child(
8975 Label::new(label)
8976 .size(LabelSize::Small)
8977 .when(!has_keybind, |el| {
8978 el.color(cx.theme().status().error.into()).strikethrough()
8979 }),
8980 )
8981 .when(!has_keybind, |el| {
8982 el.child(
8983 h_flex().ml_1().child(
8984 Icon::new(IconName::Info)
8985 .size(IconSize::Small)
8986 .color(cx.theme().status().error.into()),
8987 ),
8988 )
8989 })
8990 .when_some(icon, |element, icon| {
8991 element.child(
8992 div()
8993 .mt(px(1.5))
8994 .child(Icon::new(icon).size(IconSize::Small)),
8995 )
8996 });
8997
8998 Some(result)
8999 }
9000
9001 fn edit_prediction_line_popover_bg_color(cx: &App) -> Hsla {
9002 let accent_color = cx.theme().colors().text_accent;
9003 let editor_bg_color = cx.theme().colors().editor_background;
9004 editor_bg_color.blend(accent_color.opacity(0.1))
9005 }
9006
9007 fn edit_prediction_callout_popover_border_color(cx: &App) -> Hsla {
9008 let accent_color = cx.theme().colors().text_accent;
9009 let editor_bg_color = cx.theme().colors().editor_background;
9010 editor_bg_color.blend(accent_color.opacity(0.6))
9011 }
9012
9013 fn render_edit_prediction_cursor_popover(
9014 &self,
9015 min_width: Pixels,
9016 max_width: Pixels,
9017 cursor_point: Point,
9018 style: &EditorStyle,
9019 accept_keystroke: Option<&gpui::Keystroke>,
9020 _window: &Window,
9021 cx: &mut Context<Editor>,
9022 ) -> Option<AnyElement> {
9023 let provider = self.edit_prediction_provider.as_ref()?;
9024
9025 if provider.provider.needs_terms_acceptance(cx) {
9026 return Some(
9027 h_flex()
9028 .min_w(min_width)
9029 .flex_1()
9030 .px_2()
9031 .py_1()
9032 .gap_3()
9033 .elevation_2(cx)
9034 .hover(|style| style.bg(cx.theme().colors().element_hover))
9035 .id("accept-terms")
9036 .cursor_pointer()
9037 .on_mouse_down(MouseButton::Left, |_, window, _| window.prevent_default())
9038 .on_click(cx.listener(|this, _event, window, cx| {
9039 cx.stop_propagation();
9040 this.report_editor_event("Edit Prediction Provider ToS Clicked", None, cx);
9041 window.dispatch_action(
9042 zed_actions::OpenZedPredictOnboarding.boxed_clone(),
9043 cx,
9044 );
9045 }))
9046 .child(
9047 h_flex()
9048 .flex_1()
9049 .gap_2()
9050 .child(Icon::new(IconName::ZedPredict))
9051 .child(Label::new("Accept Terms of Service"))
9052 .child(div().w_full())
9053 .child(
9054 Icon::new(IconName::ArrowUpRight)
9055 .color(Color::Muted)
9056 .size(IconSize::Small),
9057 )
9058 .into_any_element(),
9059 )
9060 .into_any(),
9061 );
9062 }
9063
9064 let is_refreshing = provider.provider.is_refreshing(cx);
9065
9066 fn pending_completion_container() -> Div {
9067 h_flex()
9068 .h_full()
9069 .flex_1()
9070 .gap_2()
9071 .child(Icon::new(IconName::ZedPredict))
9072 }
9073
9074 let completion = match &self.active_inline_completion {
9075 Some(prediction) => {
9076 if !self.has_visible_completions_menu() {
9077 const RADIUS: Pixels = px(6.);
9078 const BORDER_WIDTH: Pixels = px(1.);
9079
9080 return Some(
9081 h_flex()
9082 .elevation_2(cx)
9083 .border(BORDER_WIDTH)
9084 .border_color(cx.theme().colors().border)
9085 .when(accept_keystroke.is_none(), |el| {
9086 el.border_color(cx.theme().status().error)
9087 })
9088 .rounded(RADIUS)
9089 .rounded_tl(px(0.))
9090 .overflow_hidden()
9091 .child(div().px_1p5().child(match &prediction.completion {
9092 InlineCompletion::Move { target, snapshot } => {
9093 use text::ToPoint as _;
9094 if target.text_anchor.to_point(&snapshot).row > cursor_point.row
9095 {
9096 Icon::new(IconName::ZedPredictDown)
9097 } else {
9098 Icon::new(IconName::ZedPredictUp)
9099 }
9100 }
9101 InlineCompletion::Edit { .. } => Icon::new(IconName::ZedPredict),
9102 }))
9103 .child(
9104 h_flex()
9105 .gap_1()
9106 .py_1()
9107 .px_2()
9108 .rounded_r(RADIUS - BORDER_WIDTH)
9109 .border_l_1()
9110 .border_color(cx.theme().colors().border)
9111 .bg(Self::edit_prediction_line_popover_bg_color(cx))
9112 .when(self.edit_prediction_preview.released_too_fast(), |el| {
9113 el.child(
9114 Label::new("Hold")
9115 .size(LabelSize::Small)
9116 .when(accept_keystroke.is_none(), |el| {
9117 el.strikethrough()
9118 })
9119 .line_height_style(LineHeightStyle::UiLabel),
9120 )
9121 })
9122 .id("edit_prediction_cursor_popover_keybind")
9123 .when(accept_keystroke.is_none(), |el| {
9124 let status_colors = cx.theme().status();
9125
9126 el.bg(status_colors.error_background)
9127 .border_color(status_colors.error.opacity(0.6))
9128 .child(Icon::new(IconName::Info).color(Color::Error))
9129 .cursor_default()
9130 .hoverable_tooltip(move |_window, cx| {
9131 cx.new(|_| MissingEditPredictionKeybindingTooltip)
9132 .into()
9133 })
9134 })
9135 .when_some(
9136 accept_keystroke.as_ref(),
9137 |el, accept_keystroke| {
9138 el.child(h_flex().children(ui::render_modifiers(
9139 &accept_keystroke.modifiers,
9140 PlatformStyle::platform(),
9141 Some(Color::Default),
9142 Some(IconSize::XSmall.rems().into()),
9143 false,
9144 )))
9145 },
9146 ),
9147 )
9148 .into_any(),
9149 );
9150 }
9151
9152 self.render_edit_prediction_cursor_popover_preview(
9153 prediction,
9154 cursor_point,
9155 style,
9156 cx,
9157 )?
9158 }
9159
9160 None if is_refreshing => match &self.stale_inline_completion_in_menu {
9161 Some(stale_completion) => self.render_edit_prediction_cursor_popover_preview(
9162 stale_completion,
9163 cursor_point,
9164 style,
9165 cx,
9166 )?,
9167
9168 None => {
9169 pending_completion_container().child(Label::new("...").size(LabelSize::Small))
9170 }
9171 },
9172
9173 None => pending_completion_container().child(Label::new("No Prediction")),
9174 };
9175
9176 let completion = if is_refreshing {
9177 completion
9178 .with_animation(
9179 "loading-completion",
9180 Animation::new(Duration::from_secs(2))
9181 .repeat()
9182 .with_easing(pulsating_between(0.4, 0.8)),
9183 |label, delta| label.opacity(delta),
9184 )
9185 .into_any_element()
9186 } else {
9187 completion.into_any_element()
9188 };
9189
9190 let has_completion = self.active_inline_completion.is_some();
9191
9192 let is_platform_style_mac = PlatformStyle::platform() == PlatformStyle::Mac;
9193 Some(
9194 h_flex()
9195 .min_w(min_width)
9196 .max_w(max_width)
9197 .flex_1()
9198 .elevation_2(cx)
9199 .border_color(cx.theme().colors().border)
9200 .child(
9201 div()
9202 .flex_1()
9203 .py_1()
9204 .px_2()
9205 .overflow_hidden()
9206 .child(completion),
9207 )
9208 .when_some(accept_keystroke, |el, accept_keystroke| {
9209 if !accept_keystroke.modifiers.modified() {
9210 return el;
9211 }
9212
9213 el.child(
9214 h_flex()
9215 .h_full()
9216 .border_l_1()
9217 .rounded_r_lg()
9218 .border_color(cx.theme().colors().border)
9219 .bg(Self::edit_prediction_line_popover_bg_color(cx))
9220 .gap_1()
9221 .py_1()
9222 .px_2()
9223 .child(
9224 h_flex()
9225 .font(theme::ThemeSettings::get_global(cx).buffer_font.clone())
9226 .when(is_platform_style_mac, |parent| parent.gap_1())
9227 .child(h_flex().children(ui::render_modifiers(
9228 &accept_keystroke.modifiers,
9229 PlatformStyle::platform(),
9230 Some(if !has_completion {
9231 Color::Muted
9232 } else {
9233 Color::Default
9234 }),
9235 None,
9236 false,
9237 ))),
9238 )
9239 .child(Label::new("Preview").into_any_element())
9240 .opacity(if has_completion { 1.0 } else { 0.4 }),
9241 )
9242 })
9243 .into_any(),
9244 )
9245 }
9246
9247 fn render_edit_prediction_cursor_popover_preview(
9248 &self,
9249 completion: &InlineCompletionState,
9250 cursor_point: Point,
9251 style: &EditorStyle,
9252 cx: &mut Context<Editor>,
9253 ) -> Option<Div> {
9254 use text::ToPoint as _;
9255
9256 fn render_relative_row_jump(
9257 prefix: impl Into<String>,
9258 current_row: u32,
9259 target_row: u32,
9260 ) -> Div {
9261 let (row_diff, arrow) = if target_row < current_row {
9262 (current_row - target_row, IconName::ArrowUp)
9263 } else {
9264 (target_row - current_row, IconName::ArrowDown)
9265 };
9266
9267 h_flex()
9268 .child(
9269 Label::new(format!("{}{}", prefix.into(), row_diff))
9270 .color(Color::Muted)
9271 .size(LabelSize::Small),
9272 )
9273 .child(Icon::new(arrow).color(Color::Muted).size(IconSize::Small))
9274 }
9275
9276 match &completion.completion {
9277 InlineCompletion::Move {
9278 target, snapshot, ..
9279 } => Some(
9280 h_flex()
9281 .px_2()
9282 .gap_2()
9283 .flex_1()
9284 .child(
9285 if target.text_anchor.to_point(&snapshot).row > cursor_point.row {
9286 Icon::new(IconName::ZedPredictDown)
9287 } else {
9288 Icon::new(IconName::ZedPredictUp)
9289 },
9290 )
9291 .child(Label::new("Jump to Edit")),
9292 ),
9293
9294 InlineCompletion::Edit {
9295 edits,
9296 edit_preview,
9297 snapshot,
9298 display_mode: _,
9299 } => {
9300 let first_edit_row = edits.first()?.0.start.text_anchor.to_point(&snapshot).row;
9301
9302 let (highlighted_edits, has_more_lines) = crate::inline_completion_edit_text(
9303 &snapshot,
9304 &edits,
9305 edit_preview.as_ref()?,
9306 true,
9307 cx,
9308 )
9309 .first_line_preview();
9310
9311 let styled_text = gpui::StyledText::new(highlighted_edits.text)
9312 .with_default_highlights(&style.text, highlighted_edits.highlights);
9313
9314 let preview = h_flex()
9315 .gap_1()
9316 .min_w_16()
9317 .child(styled_text)
9318 .when(has_more_lines, |parent| parent.child("…"));
9319
9320 let left = if first_edit_row != cursor_point.row {
9321 render_relative_row_jump("", cursor_point.row, first_edit_row)
9322 .into_any_element()
9323 } else {
9324 Icon::new(IconName::ZedPredict).into_any_element()
9325 };
9326
9327 Some(
9328 h_flex()
9329 .h_full()
9330 .flex_1()
9331 .gap_2()
9332 .pr_1()
9333 .overflow_x_hidden()
9334 .font(theme::ThemeSettings::get_global(cx).buffer_font.clone())
9335 .child(left)
9336 .child(preview),
9337 )
9338 }
9339 }
9340 }
9341
9342 pub fn render_context_menu(
9343 &self,
9344 style: &EditorStyle,
9345 max_height_in_lines: u32,
9346 window: &mut Window,
9347 cx: &mut Context<Editor>,
9348 ) -> Option<AnyElement> {
9349 let menu = self.context_menu.borrow();
9350 let menu = menu.as_ref()?;
9351 if !menu.visible() {
9352 return None;
9353 };
9354 Some(menu.render(style, max_height_in_lines, window, cx))
9355 }
9356
9357 fn render_context_menu_aside(
9358 &mut self,
9359 max_size: Size<Pixels>,
9360 window: &mut Window,
9361 cx: &mut Context<Editor>,
9362 ) -> Option<AnyElement> {
9363 self.context_menu.borrow_mut().as_mut().and_then(|menu| {
9364 if menu.visible() {
9365 menu.render_aside(max_size, window, cx)
9366 } else {
9367 None
9368 }
9369 })
9370 }
9371
9372 fn hide_context_menu(
9373 &mut self,
9374 window: &mut Window,
9375 cx: &mut Context<Self>,
9376 ) -> Option<CodeContextMenu> {
9377 cx.notify();
9378 self.completion_tasks.clear();
9379 let context_menu = self.context_menu.borrow_mut().take();
9380 self.stale_inline_completion_in_menu.take();
9381 self.update_visible_inline_completion(window, cx);
9382 if let Some(CodeContextMenu::Completions(_)) = &context_menu {
9383 if let Some(completion_provider) = &self.completion_provider {
9384 completion_provider.selection_changed(None, window, cx);
9385 }
9386 }
9387 context_menu
9388 }
9389
9390 fn show_snippet_choices(
9391 &mut self,
9392 choices: &Vec<String>,
9393 selection: Range<Anchor>,
9394 cx: &mut Context<Self>,
9395 ) {
9396 let buffer_id = match (&selection.start.buffer_id, &selection.end.buffer_id) {
9397 (Some(a), Some(b)) if a == b => a,
9398 _ => {
9399 log::error!("expected anchor range to have matching buffer IDs");
9400 return;
9401 }
9402 };
9403 let multi_buffer = self.buffer().read(cx);
9404 let Some(buffer) = multi_buffer.buffer(*buffer_id) else {
9405 return;
9406 };
9407
9408 let id = post_inc(&mut self.next_completion_id);
9409 let snippet_sort_order = EditorSettings::get_global(cx).snippet_sort_order;
9410 *self.context_menu.borrow_mut() = Some(CodeContextMenu::Completions(
9411 CompletionsMenu::new_snippet_choices(
9412 id,
9413 true,
9414 choices,
9415 selection,
9416 buffer,
9417 snippet_sort_order,
9418 ),
9419 ));
9420 }
9421
9422 pub fn insert_snippet(
9423 &mut self,
9424 insertion_ranges: &[Range<usize>],
9425 snippet: Snippet,
9426 window: &mut Window,
9427 cx: &mut Context<Self>,
9428 ) -> Result<()> {
9429 struct Tabstop<T> {
9430 is_end_tabstop: bool,
9431 ranges: Vec<Range<T>>,
9432 choices: Option<Vec<String>>,
9433 }
9434
9435 let tabstops = self.buffer.update(cx, |buffer, cx| {
9436 let snippet_text: Arc<str> = snippet.text.clone().into();
9437 let edits = insertion_ranges
9438 .iter()
9439 .cloned()
9440 .map(|range| (range, snippet_text.clone()));
9441 let autoindent_mode = AutoindentMode::Block {
9442 original_indent_columns: Vec::new(),
9443 };
9444 buffer.edit(edits, Some(autoindent_mode), cx);
9445
9446 let snapshot = &*buffer.read(cx);
9447 let snippet = &snippet;
9448 snippet
9449 .tabstops
9450 .iter()
9451 .map(|tabstop| {
9452 let is_end_tabstop = tabstop.ranges.first().map_or(false, |tabstop| {
9453 tabstop.is_empty() && tabstop.start == snippet.text.len() as isize
9454 });
9455 let mut tabstop_ranges = tabstop
9456 .ranges
9457 .iter()
9458 .flat_map(|tabstop_range| {
9459 let mut delta = 0_isize;
9460 insertion_ranges.iter().map(move |insertion_range| {
9461 let insertion_start = insertion_range.start as isize + delta;
9462 delta +=
9463 snippet.text.len() as isize - insertion_range.len() as isize;
9464
9465 let start = ((insertion_start + tabstop_range.start) as usize)
9466 .min(snapshot.len());
9467 let end = ((insertion_start + tabstop_range.end) as usize)
9468 .min(snapshot.len());
9469 snapshot.anchor_before(start)..snapshot.anchor_after(end)
9470 })
9471 })
9472 .collect::<Vec<_>>();
9473 tabstop_ranges.sort_unstable_by(|a, b| a.start.cmp(&b.start, snapshot));
9474
9475 Tabstop {
9476 is_end_tabstop,
9477 ranges: tabstop_ranges,
9478 choices: tabstop.choices.clone(),
9479 }
9480 })
9481 .collect::<Vec<_>>()
9482 });
9483 if let Some(tabstop) = tabstops.first() {
9484 self.change_selections(Default::default(), window, cx, |s| {
9485 // Reverse order so that the first range is the newest created selection.
9486 // Completions will use it and autoscroll will prioritize it.
9487 s.select_ranges(tabstop.ranges.iter().rev().cloned());
9488 });
9489
9490 if let Some(choices) = &tabstop.choices {
9491 if let Some(selection) = tabstop.ranges.first() {
9492 self.show_snippet_choices(choices, selection.clone(), cx)
9493 }
9494 }
9495
9496 // If we're already at the last tabstop and it's at the end of the snippet,
9497 // we're done, we don't need to keep the state around.
9498 if !tabstop.is_end_tabstop {
9499 let choices = tabstops
9500 .iter()
9501 .map(|tabstop| tabstop.choices.clone())
9502 .collect();
9503
9504 let ranges = tabstops
9505 .into_iter()
9506 .map(|tabstop| tabstop.ranges)
9507 .collect::<Vec<_>>();
9508
9509 self.snippet_stack.push(SnippetState {
9510 active_index: 0,
9511 ranges,
9512 choices,
9513 });
9514 }
9515
9516 // Check whether the just-entered snippet ends with an auto-closable bracket.
9517 if self.autoclose_regions.is_empty() {
9518 let snapshot = self.buffer.read(cx).snapshot(cx);
9519 for selection in &mut self.selections.all::<Point>(cx) {
9520 let selection_head = selection.head();
9521 let Some(scope) = snapshot.language_scope_at(selection_head) else {
9522 continue;
9523 };
9524
9525 let mut bracket_pair = None;
9526 let next_chars = snapshot.chars_at(selection_head).collect::<String>();
9527 let prev_chars = snapshot
9528 .reversed_chars_at(selection_head)
9529 .collect::<String>();
9530 for (pair, enabled) in scope.brackets() {
9531 if enabled
9532 && pair.close
9533 && prev_chars.starts_with(pair.start.as_str())
9534 && next_chars.starts_with(pair.end.as_str())
9535 {
9536 bracket_pair = Some(pair.clone());
9537 break;
9538 }
9539 }
9540 if let Some(pair) = bracket_pair {
9541 let snapshot_settings = snapshot.language_settings_at(selection_head, cx);
9542 let autoclose_enabled =
9543 self.use_autoclose && snapshot_settings.use_autoclose;
9544 if autoclose_enabled {
9545 let start = snapshot.anchor_after(selection_head);
9546 let end = snapshot.anchor_after(selection_head);
9547 self.autoclose_regions.push(AutocloseRegion {
9548 selection_id: selection.id,
9549 range: start..end,
9550 pair,
9551 });
9552 }
9553 }
9554 }
9555 }
9556 }
9557 Ok(())
9558 }
9559
9560 pub fn move_to_next_snippet_tabstop(
9561 &mut self,
9562 window: &mut Window,
9563 cx: &mut Context<Self>,
9564 ) -> bool {
9565 self.move_to_snippet_tabstop(Bias::Right, window, cx)
9566 }
9567
9568 pub fn move_to_prev_snippet_tabstop(
9569 &mut self,
9570 window: &mut Window,
9571 cx: &mut Context<Self>,
9572 ) -> bool {
9573 self.move_to_snippet_tabstop(Bias::Left, window, cx)
9574 }
9575
9576 pub fn move_to_snippet_tabstop(
9577 &mut self,
9578 bias: Bias,
9579 window: &mut Window,
9580 cx: &mut Context<Self>,
9581 ) -> bool {
9582 if let Some(mut snippet) = self.snippet_stack.pop() {
9583 match bias {
9584 Bias::Left => {
9585 if snippet.active_index > 0 {
9586 snippet.active_index -= 1;
9587 } else {
9588 self.snippet_stack.push(snippet);
9589 return false;
9590 }
9591 }
9592 Bias::Right => {
9593 if snippet.active_index + 1 < snippet.ranges.len() {
9594 snippet.active_index += 1;
9595 } else {
9596 self.snippet_stack.push(snippet);
9597 return false;
9598 }
9599 }
9600 }
9601 if let Some(current_ranges) = snippet.ranges.get(snippet.active_index) {
9602 self.change_selections(Default::default(), window, cx, |s| {
9603 // Reverse order so that the first range is the newest created selection.
9604 // Completions will use it and autoscroll will prioritize it.
9605 s.select_ranges(current_ranges.iter().rev().cloned())
9606 });
9607
9608 if let Some(choices) = &snippet.choices[snippet.active_index] {
9609 if let Some(selection) = current_ranges.first() {
9610 self.show_snippet_choices(&choices, selection.clone(), cx);
9611 }
9612 }
9613
9614 // If snippet state is not at the last tabstop, push it back on the stack
9615 if snippet.active_index + 1 < snippet.ranges.len() {
9616 self.snippet_stack.push(snippet);
9617 }
9618 return true;
9619 }
9620 }
9621
9622 false
9623 }
9624
9625 pub fn clear(&mut self, window: &mut Window, cx: &mut Context<Self>) {
9626 self.transact(window, cx, |this, window, cx| {
9627 this.select_all(&SelectAll, window, cx);
9628 this.insert("", window, cx);
9629 });
9630 }
9631
9632 pub fn backspace(&mut self, _: &Backspace, window: &mut Window, cx: &mut Context<Self>) {
9633 self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
9634 self.transact(window, cx, |this, window, cx| {
9635 this.select_autoclose_pair(window, cx);
9636 let mut linked_ranges = HashMap::<_, Vec<_>>::default();
9637 if !this.linked_edit_ranges.is_empty() {
9638 let selections = this.selections.all::<MultiBufferPoint>(cx);
9639 let snapshot = this.buffer.read(cx).snapshot(cx);
9640
9641 for selection in selections.iter() {
9642 let selection_start = snapshot.anchor_before(selection.start).text_anchor;
9643 let selection_end = snapshot.anchor_after(selection.end).text_anchor;
9644 if selection_start.buffer_id != selection_end.buffer_id {
9645 continue;
9646 }
9647 if let Some(ranges) =
9648 this.linked_editing_ranges_for(selection_start..selection_end, cx)
9649 {
9650 for (buffer, entries) in ranges {
9651 linked_ranges.entry(buffer).or_default().extend(entries);
9652 }
9653 }
9654 }
9655 }
9656
9657 let mut selections = this.selections.all::<MultiBufferPoint>(cx);
9658 let display_map = this.display_map.update(cx, |map, cx| map.snapshot(cx));
9659 for selection in &mut selections {
9660 if selection.is_empty() {
9661 let old_head = selection.head();
9662 let mut new_head =
9663 movement::left(&display_map, old_head.to_display_point(&display_map))
9664 .to_point(&display_map);
9665 if let Some((buffer, line_buffer_range)) = display_map
9666 .buffer_snapshot
9667 .buffer_line_for_row(MultiBufferRow(old_head.row))
9668 {
9669 let indent_size = buffer.indent_size_for_line(line_buffer_range.start.row);
9670 let indent_len = match indent_size.kind {
9671 IndentKind::Space => {
9672 buffer.settings_at(line_buffer_range.start, cx).tab_size
9673 }
9674 IndentKind::Tab => NonZeroU32::new(1).unwrap(),
9675 };
9676 if old_head.column <= indent_size.len && old_head.column > 0 {
9677 let indent_len = indent_len.get();
9678 new_head = cmp::min(
9679 new_head,
9680 MultiBufferPoint::new(
9681 old_head.row,
9682 ((old_head.column - 1) / indent_len) * indent_len,
9683 ),
9684 );
9685 }
9686 }
9687
9688 selection.set_head(new_head, SelectionGoal::None);
9689 }
9690 }
9691
9692 this.change_selections(Default::default(), window, cx, |s| s.select(selections));
9693 this.insert("", window, cx);
9694 let empty_str: Arc<str> = Arc::from("");
9695 for (buffer, edits) in linked_ranges {
9696 let snapshot = buffer.read(cx).snapshot();
9697 use text::ToPoint as TP;
9698
9699 let edits = edits
9700 .into_iter()
9701 .map(|range| {
9702 let end_point = TP::to_point(&range.end, &snapshot);
9703 let mut start_point = TP::to_point(&range.start, &snapshot);
9704
9705 if end_point == start_point {
9706 let offset = text::ToOffset::to_offset(&range.start, &snapshot)
9707 .saturating_sub(1);
9708 start_point =
9709 snapshot.clip_point(TP::to_point(&offset, &snapshot), Bias::Left);
9710 };
9711
9712 (start_point..end_point, empty_str.clone())
9713 })
9714 .sorted_by_key(|(range, _)| range.start)
9715 .collect::<Vec<_>>();
9716 buffer.update(cx, |this, cx| {
9717 this.edit(edits, None, cx);
9718 })
9719 }
9720 this.refresh_inline_completion(true, false, window, cx);
9721 linked_editing_ranges::refresh_linked_ranges(this, window, cx);
9722 });
9723 }
9724
9725 pub fn delete(&mut self, _: &Delete, window: &mut Window, cx: &mut Context<Self>) {
9726 self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
9727 self.transact(window, cx, |this, window, cx| {
9728 this.change_selections(Default::default(), window, cx, |s| {
9729 s.move_with(|map, selection| {
9730 if selection.is_empty() {
9731 let cursor = movement::right(map, selection.head());
9732 selection.end = cursor;
9733 selection.reversed = true;
9734 selection.goal = SelectionGoal::None;
9735 }
9736 })
9737 });
9738 this.insert("", window, cx);
9739 this.refresh_inline_completion(true, false, window, cx);
9740 });
9741 }
9742
9743 pub fn backtab(&mut self, _: &Backtab, window: &mut Window, cx: &mut Context<Self>) {
9744 if self.mode.is_single_line() {
9745 cx.propagate();
9746 return;
9747 }
9748
9749 self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
9750 if self.move_to_prev_snippet_tabstop(window, cx) {
9751 return;
9752 }
9753 self.outdent(&Outdent, window, cx);
9754 }
9755
9756 pub fn tab(&mut self, _: &Tab, window: &mut Window, cx: &mut Context<Self>) {
9757 if self.mode.is_single_line() {
9758 cx.propagate();
9759 return;
9760 }
9761
9762 if self.move_to_next_snippet_tabstop(window, cx) {
9763 self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
9764 return;
9765 }
9766 if self.read_only(cx) {
9767 return;
9768 }
9769 self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
9770 let mut selections = self.selections.all_adjusted(cx);
9771 let buffer = self.buffer.read(cx);
9772 let snapshot = buffer.snapshot(cx);
9773 let rows_iter = selections.iter().map(|s| s.head().row);
9774 let suggested_indents = snapshot.suggested_indents(rows_iter, cx);
9775
9776 let has_some_cursor_in_whitespace = selections
9777 .iter()
9778 .filter(|selection| selection.is_empty())
9779 .any(|selection| {
9780 let cursor = selection.head();
9781 let current_indent = snapshot.indent_size_for_line(MultiBufferRow(cursor.row));
9782 cursor.column < current_indent.len
9783 });
9784
9785 let mut edits = Vec::new();
9786 let mut prev_edited_row = 0;
9787 let mut row_delta = 0;
9788 for selection in &mut selections {
9789 if selection.start.row != prev_edited_row {
9790 row_delta = 0;
9791 }
9792 prev_edited_row = selection.end.row;
9793
9794 // If the selection is non-empty, then increase the indentation of the selected lines.
9795 if !selection.is_empty() {
9796 row_delta =
9797 Self::indent_selection(buffer, &snapshot, selection, &mut edits, row_delta, cx);
9798 continue;
9799 }
9800
9801 let cursor = selection.head();
9802 let current_indent = snapshot.indent_size_for_line(MultiBufferRow(cursor.row));
9803 if let Some(suggested_indent) =
9804 suggested_indents.get(&MultiBufferRow(cursor.row)).copied()
9805 {
9806 // Don't do anything if already at suggested indent
9807 // and there is any other cursor which is not
9808 if has_some_cursor_in_whitespace
9809 && cursor.column == current_indent.len
9810 && current_indent.len == suggested_indent.len
9811 {
9812 continue;
9813 }
9814
9815 // Adjust line and move cursor to suggested indent
9816 // if cursor is not at suggested indent
9817 if cursor.column < suggested_indent.len
9818 && cursor.column <= current_indent.len
9819 && current_indent.len <= suggested_indent.len
9820 {
9821 selection.start = Point::new(cursor.row, suggested_indent.len);
9822 selection.end = selection.start;
9823 if row_delta == 0 {
9824 edits.extend(Buffer::edit_for_indent_size_adjustment(
9825 cursor.row,
9826 current_indent,
9827 suggested_indent,
9828 ));
9829 row_delta = suggested_indent.len - current_indent.len;
9830 }
9831 continue;
9832 }
9833
9834 // If current indent is more than suggested indent
9835 // only move cursor to current indent and skip indent
9836 if cursor.column < current_indent.len && current_indent.len > suggested_indent.len {
9837 selection.start = Point::new(cursor.row, current_indent.len);
9838 selection.end = selection.start;
9839 continue;
9840 }
9841 }
9842
9843 // Otherwise, insert a hard or soft tab.
9844 let settings = buffer.language_settings_at(cursor, cx);
9845 let tab_size = if settings.hard_tabs {
9846 IndentSize::tab()
9847 } else {
9848 let tab_size = settings.tab_size.get();
9849 let indent_remainder = snapshot
9850 .text_for_range(Point::new(cursor.row, 0)..cursor)
9851 .flat_map(str::chars)
9852 .fold(row_delta % tab_size, |counter: u32, c| {
9853 if c == '\t' {
9854 0
9855 } else {
9856 (counter + 1) % tab_size
9857 }
9858 });
9859
9860 let chars_to_next_tab_stop = tab_size - indent_remainder;
9861 IndentSize::spaces(chars_to_next_tab_stop)
9862 };
9863 selection.start = Point::new(cursor.row, cursor.column + row_delta + tab_size.len);
9864 selection.end = selection.start;
9865 edits.push((cursor..cursor, tab_size.chars().collect::<String>()));
9866 row_delta += tab_size.len;
9867 }
9868
9869 self.transact(window, cx, |this, window, cx| {
9870 this.buffer.update(cx, |b, cx| b.edit(edits, None, cx));
9871 this.change_selections(Default::default(), window, cx, |s| s.select(selections));
9872 this.refresh_inline_completion(true, false, window, cx);
9873 });
9874 }
9875
9876 pub fn indent(&mut self, _: &Indent, window: &mut Window, cx: &mut Context<Self>) {
9877 if self.read_only(cx) {
9878 return;
9879 }
9880 if self.mode.is_single_line() {
9881 cx.propagate();
9882 return;
9883 }
9884
9885 self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
9886 let mut selections = self.selections.all::<Point>(cx);
9887 let mut prev_edited_row = 0;
9888 let mut row_delta = 0;
9889 let mut edits = Vec::new();
9890 let buffer = self.buffer.read(cx);
9891 let snapshot = buffer.snapshot(cx);
9892 for selection in &mut selections {
9893 if selection.start.row != prev_edited_row {
9894 row_delta = 0;
9895 }
9896 prev_edited_row = selection.end.row;
9897
9898 row_delta =
9899 Self::indent_selection(buffer, &snapshot, selection, &mut edits, row_delta, cx);
9900 }
9901
9902 self.transact(window, cx, |this, window, cx| {
9903 this.buffer.update(cx, |b, cx| b.edit(edits, None, cx));
9904 this.change_selections(Default::default(), window, cx, |s| s.select(selections));
9905 });
9906 }
9907
9908 fn indent_selection(
9909 buffer: &MultiBuffer,
9910 snapshot: &MultiBufferSnapshot,
9911 selection: &mut Selection<Point>,
9912 edits: &mut Vec<(Range<Point>, String)>,
9913 delta_for_start_row: u32,
9914 cx: &App,
9915 ) -> u32 {
9916 let settings = buffer.language_settings_at(selection.start, cx);
9917 let tab_size = settings.tab_size.get();
9918 let indent_kind = if settings.hard_tabs {
9919 IndentKind::Tab
9920 } else {
9921 IndentKind::Space
9922 };
9923 let mut start_row = selection.start.row;
9924 let mut end_row = selection.end.row + 1;
9925
9926 // If a selection ends at the beginning of a line, don't indent
9927 // that last line.
9928 if selection.end.column == 0 && selection.end.row > selection.start.row {
9929 end_row -= 1;
9930 }
9931
9932 // Avoid re-indenting a row that has already been indented by a
9933 // previous selection, but still update this selection's column
9934 // to reflect that indentation.
9935 if delta_for_start_row > 0 {
9936 start_row += 1;
9937 selection.start.column += delta_for_start_row;
9938 if selection.end.row == selection.start.row {
9939 selection.end.column += delta_for_start_row;
9940 }
9941 }
9942
9943 let mut delta_for_end_row = 0;
9944 let has_multiple_rows = start_row + 1 != end_row;
9945 for row in start_row..end_row {
9946 let current_indent = snapshot.indent_size_for_line(MultiBufferRow(row));
9947 let indent_delta = match (current_indent.kind, indent_kind) {
9948 (IndentKind::Space, IndentKind::Space) => {
9949 let columns_to_next_tab_stop = tab_size - (current_indent.len % tab_size);
9950 IndentSize::spaces(columns_to_next_tab_stop)
9951 }
9952 (IndentKind::Tab, IndentKind::Space) => IndentSize::spaces(tab_size),
9953 (_, IndentKind::Tab) => IndentSize::tab(),
9954 };
9955
9956 let start = if has_multiple_rows || current_indent.len < selection.start.column {
9957 0
9958 } else {
9959 selection.start.column
9960 };
9961 let row_start = Point::new(row, start);
9962 edits.push((
9963 row_start..row_start,
9964 indent_delta.chars().collect::<String>(),
9965 ));
9966
9967 // Update this selection's endpoints to reflect the indentation.
9968 if row == selection.start.row {
9969 selection.start.column += indent_delta.len;
9970 }
9971 if row == selection.end.row {
9972 selection.end.column += indent_delta.len;
9973 delta_for_end_row = indent_delta.len;
9974 }
9975 }
9976
9977 if selection.start.row == selection.end.row {
9978 delta_for_start_row + delta_for_end_row
9979 } else {
9980 delta_for_end_row
9981 }
9982 }
9983
9984 pub fn outdent(&mut self, _: &Outdent, window: &mut Window, cx: &mut Context<Self>) {
9985 if self.read_only(cx) {
9986 return;
9987 }
9988 if self.mode.is_single_line() {
9989 cx.propagate();
9990 return;
9991 }
9992
9993 self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
9994 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
9995 let selections = self.selections.all::<Point>(cx);
9996 let mut deletion_ranges = Vec::new();
9997 let mut last_outdent = None;
9998 {
9999 let buffer = self.buffer.read(cx);
10000 let snapshot = buffer.snapshot(cx);
10001 for selection in &selections {
10002 let settings = buffer.language_settings_at(selection.start, cx);
10003 let tab_size = settings.tab_size.get();
10004 let mut rows = selection.spanned_rows(false, &display_map);
10005
10006 // Avoid re-outdenting a row that has already been outdented by a
10007 // previous selection.
10008 if let Some(last_row) = last_outdent {
10009 if last_row == rows.start {
10010 rows.start = rows.start.next_row();
10011 }
10012 }
10013 let has_multiple_rows = rows.len() > 1;
10014 for row in rows.iter_rows() {
10015 let indent_size = snapshot.indent_size_for_line(row);
10016 if indent_size.len > 0 {
10017 let deletion_len = match indent_size.kind {
10018 IndentKind::Space => {
10019 let columns_to_prev_tab_stop = indent_size.len % tab_size;
10020 if columns_to_prev_tab_stop == 0 {
10021 tab_size
10022 } else {
10023 columns_to_prev_tab_stop
10024 }
10025 }
10026 IndentKind::Tab => 1,
10027 };
10028 let start = if has_multiple_rows
10029 || deletion_len > selection.start.column
10030 || indent_size.len < selection.start.column
10031 {
10032 0
10033 } else {
10034 selection.start.column - deletion_len
10035 };
10036 deletion_ranges.push(
10037 Point::new(row.0, start)..Point::new(row.0, start + deletion_len),
10038 );
10039 last_outdent = Some(row);
10040 }
10041 }
10042 }
10043 }
10044
10045 self.transact(window, cx, |this, window, cx| {
10046 this.buffer.update(cx, |buffer, cx| {
10047 let empty_str: Arc<str> = Arc::default();
10048 buffer.edit(
10049 deletion_ranges
10050 .into_iter()
10051 .map(|range| (range, empty_str.clone())),
10052 None,
10053 cx,
10054 );
10055 });
10056 let selections = this.selections.all::<usize>(cx);
10057 this.change_selections(Default::default(), window, cx, |s| s.select(selections));
10058 });
10059 }
10060
10061 pub fn autoindent(&mut self, _: &AutoIndent, window: &mut Window, cx: &mut Context<Self>) {
10062 if self.read_only(cx) {
10063 return;
10064 }
10065 if self.mode.is_single_line() {
10066 cx.propagate();
10067 return;
10068 }
10069
10070 self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
10071 let selections = self
10072 .selections
10073 .all::<usize>(cx)
10074 .into_iter()
10075 .map(|s| s.range());
10076
10077 self.transact(window, cx, |this, window, cx| {
10078 this.buffer.update(cx, |buffer, cx| {
10079 buffer.autoindent_ranges(selections, cx);
10080 });
10081 let selections = this.selections.all::<usize>(cx);
10082 this.change_selections(Default::default(), window, cx, |s| s.select(selections));
10083 });
10084 }
10085
10086 pub fn delete_line(&mut self, _: &DeleteLine, window: &mut Window, cx: &mut Context<Self>) {
10087 self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
10088 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
10089 let selections = self.selections.all::<Point>(cx);
10090
10091 let mut new_cursors = Vec::new();
10092 let mut edit_ranges = Vec::new();
10093 let mut selections = selections.iter().peekable();
10094 while let Some(selection) = selections.next() {
10095 let mut rows = selection.spanned_rows(false, &display_map);
10096 let goal_display_column = selection.head().to_display_point(&display_map).column();
10097
10098 // Accumulate contiguous regions of rows that we want to delete.
10099 while let Some(next_selection) = selections.peek() {
10100 let next_rows = next_selection.spanned_rows(false, &display_map);
10101 if next_rows.start <= rows.end {
10102 rows.end = next_rows.end;
10103 selections.next().unwrap();
10104 } else {
10105 break;
10106 }
10107 }
10108
10109 let buffer = &display_map.buffer_snapshot;
10110 let mut edit_start = Point::new(rows.start.0, 0).to_offset(buffer);
10111 let edit_end;
10112 let cursor_buffer_row;
10113 if buffer.max_point().row >= rows.end.0 {
10114 // If there's a line after the range, delete the \n from the end of the row range
10115 // and position the cursor on the next line.
10116 edit_end = Point::new(rows.end.0, 0).to_offset(buffer);
10117 cursor_buffer_row = rows.end;
10118 } else {
10119 // If there isn't a line after the range, delete the \n from the line before the
10120 // start of the row range and position the cursor there.
10121 edit_start = edit_start.saturating_sub(1);
10122 edit_end = buffer.len();
10123 cursor_buffer_row = rows.start.previous_row();
10124 }
10125
10126 let mut cursor = Point::new(cursor_buffer_row.0, 0).to_display_point(&display_map);
10127 *cursor.column_mut() =
10128 cmp::min(goal_display_column, display_map.line_len(cursor.row()));
10129
10130 new_cursors.push((
10131 selection.id,
10132 buffer.anchor_after(cursor.to_point(&display_map)),
10133 ));
10134 edit_ranges.push(edit_start..edit_end);
10135 }
10136
10137 self.transact(window, cx, |this, window, cx| {
10138 let buffer = this.buffer.update(cx, |buffer, cx| {
10139 let empty_str: Arc<str> = Arc::default();
10140 buffer.edit(
10141 edit_ranges
10142 .into_iter()
10143 .map(|range| (range, empty_str.clone())),
10144 None,
10145 cx,
10146 );
10147 buffer.snapshot(cx)
10148 });
10149 let new_selections = new_cursors
10150 .into_iter()
10151 .map(|(id, cursor)| {
10152 let cursor = cursor.to_point(&buffer);
10153 Selection {
10154 id,
10155 start: cursor,
10156 end: cursor,
10157 reversed: false,
10158 goal: SelectionGoal::None,
10159 }
10160 })
10161 .collect();
10162
10163 this.change_selections(Default::default(), window, cx, |s| {
10164 s.select(new_selections);
10165 });
10166 });
10167 }
10168
10169 pub fn join_lines_impl(
10170 &mut self,
10171 insert_whitespace: bool,
10172 window: &mut Window,
10173 cx: &mut Context<Self>,
10174 ) {
10175 if self.read_only(cx) {
10176 return;
10177 }
10178 let mut row_ranges = Vec::<Range<MultiBufferRow>>::new();
10179 for selection in self.selections.all::<Point>(cx) {
10180 let start = MultiBufferRow(selection.start.row);
10181 // Treat single line selections as if they include the next line. Otherwise this action
10182 // would do nothing for single line selections individual cursors.
10183 let end = if selection.start.row == selection.end.row {
10184 MultiBufferRow(selection.start.row + 1)
10185 } else {
10186 MultiBufferRow(selection.end.row)
10187 };
10188
10189 if let Some(last_row_range) = row_ranges.last_mut() {
10190 if start <= last_row_range.end {
10191 last_row_range.end = end;
10192 continue;
10193 }
10194 }
10195 row_ranges.push(start..end);
10196 }
10197
10198 let snapshot = self.buffer.read(cx).snapshot(cx);
10199 let mut cursor_positions = Vec::new();
10200 for row_range in &row_ranges {
10201 let anchor = snapshot.anchor_before(Point::new(
10202 row_range.end.previous_row().0,
10203 snapshot.line_len(row_range.end.previous_row()),
10204 ));
10205 cursor_positions.push(anchor..anchor);
10206 }
10207
10208 self.transact(window, cx, |this, window, cx| {
10209 for row_range in row_ranges.into_iter().rev() {
10210 for row in row_range.iter_rows().rev() {
10211 let end_of_line = Point::new(row.0, snapshot.line_len(row));
10212 let next_line_row = row.next_row();
10213 let indent = snapshot.indent_size_for_line(next_line_row);
10214 let start_of_next_line = Point::new(next_line_row.0, indent.len);
10215
10216 let replace =
10217 if snapshot.line_len(next_line_row) > indent.len && insert_whitespace {
10218 " "
10219 } else {
10220 ""
10221 };
10222
10223 this.buffer.update(cx, |buffer, cx| {
10224 buffer.edit([(end_of_line..start_of_next_line, replace)], None, cx)
10225 });
10226 }
10227 }
10228
10229 this.change_selections(Default::default(), window, cx, |s| {
10230 s.select_anchor_ranges(cursor_positions)
10231 });
10232 });
10233 }
10234
10235 pub fn join_lines(&mut self, _: &JoinLines, window: &mut Window, cx: &mut Context<Self>) {
10236 self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
10237 self.join_lines_impl(true, window, cx);
10238 }
10239
10240 pub fn sort_lines_case_sensitive(
10241 &mut self,
10242 _: &SortLinesCaseSensitive,
10243 window: &mut Window,
10244 cx: &mut Context<Self>,
10245 ) {
10246 self.manipulate_immutable_lines(window, cx, |lines| lines.sort())
10247 }
10248
10249 pub fn sort_lines_by_length(
10250 &mut self,
10251 _: &SortLinesByLength,
10252 window: &mut Window,
10253 cx: &mut Context<Self>,
10254 ) {
10255 self.manipulate_immutable_lines(window, cx, |lines| {
10256 lines.sort_by_key(|&line| line.chars().count())
10257 })
10258 }
10259
10260 pub fn sort_lines_case_insensitive(
10261 &mut self,
10262 _: &SortLinesCaseInsensitive,
10263 window: &mut Window,
10264 cx: &mut Context<Self>,
10265 ) {
10266 self.manipulate_immutable_lines(window, cx, |lines| {
10267 lines.sort_by_key(|line| line.to_lowercase())
10268 })
10269 }
10270
10271 pub fn unique_lines_case_insensitive(
10272 &mut self,
10273 _: &UniqueLinesCaseInsensitive,
10274 window: &mut Window,
10275 cx: &mut Context<Self>,
10276 ) {
10277 self.manipulate_immutable_lines(window, cx, |lines| {
10278 let mut seen = HashSet::default();
10279 lines.retain(|line| seen.insert(line.to_lowercase()));
10280 })
10281 }
10282
10283 pub fn unique_lines_case_sensitive(
10284 &mut self,
10285 _: &UniqueLinesCaseSensitive,
10286 window: &mut Window,
10287 cx: &mut Context<Self>,
10288 ) {
10289 self.manipulate_immutable_lines(window, cx, |lines| {
10290 let mut seen = HashSet::default();
10291 lines.retain(|line| seen.insert(*line));
10292 })
10293 }
10294
10295 pub fn reload_file(&mut self, _: &ReloadFile, window: &mut Window, cx: &mut Context<Self>) {
10296 let Some(project) = self.project.clone() else {
10297 return;
10298 };
10299 self.reload(project, window, cx)
10300 .detach_and_notify_err(window, cx);
10301 }
10302
10303 pub fn restore_file(
10304 &mut self,
10305 _: &::git::RestoreFile,
10306 window: &mut Window,
10307 cx: &mut Context<Self>,
10308 ) {
10309 self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
10310 let mut buffer_ids = HashSet::default();
10311 let snapshot = self.buffer().read(cx).snapshot(cx);
10312 for selection in self.selections.all::<usize>(cx) {
10313 buffer_ids.extend(snapshot.buffer_ids_for_range(selection.range()))
10314 }
10315
10316 let buffer = self.buffer().read(cx);
10317 let ranges = buffer_ids
10318 .into_iter()
10319 .flat_map(|buffer_id| buffer.excerpt_ranges_for_buffer(buffer_id, cx))
10320 .collect::<Vec<_>>();
10321
10322 self.restore_hunks_in_ranges(ranges, window, cx);
10323 }
10324
10325 pub fn git_restore(&mut self, _: &Restore, window: &mut Window, cx: &mut Context<Self>) {
10326 self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
10327 let selections = self
10328 .selections
10329 .all(cx)
10330 .into_iter()
10331 .map(|s| s.range())
10332 .collect();
10333 self.restore_hunks_in_ranges(selections, window, cx);
10334 }
10335
10336 pub fn restore_hunks_in_ranges(
10337 &mut self,
10338 ranges: Vec<Range<Point>>,
10339 window: &mut Window,
10340 cx: &mut Context<Editor>,
10341 ) {
10342 let mut revert_changes = HashMap::default();
10343 let chunk_by = self
10344 .snapshot(window, cx)
10345 .hunks_for_ranges(ranges)
10346 .into_iter()
10347 .chunk_by(|hunk| hunk.buffer_id);
10348 for (buffer_id, hunks) in &chunk_by {
10349 let hunks = hunks.collect::<Vec<_>>();
10350 for hunk in &hunks {
10351 self.prepare_restore_change(&mut revert_changes, hunk, cx);
10352 }
10353 self.do_stage_or_unstage(false, buffer_id, hunks.into_iter(), cx);
10354 }
10355 drop(chunk_by);
10356 if !revert_changes.is_empty() {
10357 self.transact(window, cx, |editor, window, cx| {
10358 editor.restore(revert_changes, window, cx);
10359 });
10360 }
10361 }
10362
10363 pub fn open_active_item_in_terminal(
10364 &mut self,
10365 _: &OpenInTerminal,
10366 window: &mut Window,
10367 cx: &mut Context<Self>,
10368 ) {
10369 if let Some(working_directory) = self.active_excerpt(cx).and_then(|(_, buffer, _)| {
10370 let project_path = buffer.read(cx).project_path(cx)?;
10371 let project = self.project.as_ref()?.read(cx);
10372 let entry = project.entry_for_path(&project_path, cx)?;
10373 let parent = match &entry.canonical_path {
10374 Some(canonical_path) => canonical_path.to_path_buf(),
10375 None => project.absolute_path(&project_path, cx)?,
10376 }
10377 .parent()?
10378 .to_path_buf();
10379 Some(parent)
10380 }) {
10381 window.dispatch_action(OpenTerminal { working_directory }.boxed_clone(), cx);
10382 }
10383 }
10384
10385 fn set_breakpoint_context_menu(
10386 &mut self,
10387 display_row: DisplayRow,
10388 position: Option<Anchor>,
10389 clicked_point: gpui::Point<Pixels>,
10390 window: &mut Window,
10391 cx: &mut Context<Self>,
10392 ) {
10393 let source = self
10394 .buffer
10395 .read(cx)
10396 .snapshot(cx)
10397 .anchor_before(Point::new(display_row.0, 0u32));
10398
10399 let context_menu = self.breakpoint_context_menu(position.unwrap_or(source), window, cx);
10400
10401 self.mouse_context_menu = MouseContextMenu::pinned_to_editor(
10402 self,
10403 source,
10404 clicked_point,
10405 context_menu,
10406 window,
10407 cx,
10408 );
10409 }
10410
10411 fn add_edit_breakpoint_block(
10412 &mut self,
10413 anchor: Anchor,
10414 breakpoint: &Breakpoint,
10415 edit_action: BreakpointPromptEditAction,
10416 window: &mut Window,
10417 cx: &mut Context<Self>,
10418 ) {
10419 let weak_editor = cx.weak_entity();
10420 let bp_prompt = cx.new(|cx| {
10421 BreakpointPromptEditor::new(
10422 weak_editor,
10423 anchor,
10424 breakpoint.clone(),
10425 edit_action,
10426 window,
10427 cx,
10428 )
10429 });
10430
10431 let height = bp_prompt.update(cx, |this, cx| {
10432 this.prompt
10433 .update(cx, |prompt, cx| prompt.max_point(cx).row().0 + 1 + 2)
10434 });
10435 let cloned_prompt = bp_prompt.clone();
10436 let blocks = vec![BlockProperties {
10437 style: BlockStyle::Sticky,
10438 placement: BlockPlacement::Above(anchor),
10439 height: Some(height),
10440 render: Arc::new(move |cx| {
10441 *cloned_prompt.read(cx).editor_margins.lock() = *cx.margins;
10442 cloned_prompt.clone().into_any_element()
10443 }),
10444 priority: 0,
10445 render_in_minimap: true,
10446 }];
10447
10448 let focus_handle = bp_prompt.focus_handle(cx);
10449 window.focus(&focus_handle);
10450
10451 let block_ids = self.insert_blocks(blocks, None, cx);
10452 bp_prompt.update(cx, |prompt, _| {
10453 prompt.add_block_ids(block_ids);
10454 });
10455 }
10456
10457 pub(crate) fn breakpoint_at_row(
10458 &self,
10459 row: u32,
10460 window: &mut Window,
10461 cx: &mut Context<Self>,
10462 ) -> Option<(Anchor, Breakpoint)> {
10463 let snapshot = self.snapshot(window, cx);
10464 let breakpoint_position = snapshot.buffer_snapshot.anchor_before(Point::new(row, 0));
10465
10466 self.breakpoint_at_anchor(breakpoint_position, &snapshot, cx)
10467 }
10468
10469 pub(crate) fn breakpoint_at_anchor(
10470 &self,
10471 breakpoint_position: Anchor,
10472 snapshot: &EditorSnapshot,
10473 cx: &mut Context<Self>,
10474 ) -> Option<(Anchor, Breakpoint)> {
10475 let project = self.project.clone()?;
10476
10477 let buffer_id = breakpoint_position.buffer_id.or_else(|| {
10478 snapshot
10479 .buffer_snapshot
10480 .buffer_id_for_excerpt(breakpoint_position.excerpt_id)
10481 })?;
10482
10483 let enclosing_excerpt = breakpoint_position.excerpt_id;
10484 let buffer = project.read(cx).buffer_for_id(buffer_id, cx)?;
10485 let buffer_snapshot = buffer.read(cx).snapshot();
10486
10487 let row = buffer_snapshot
10488 .summary_for_anchor::<text::PointUtf16>(&breakpoint_position.text_anchor)
10489 .row;
10490
10491 let line_len = snapshot.buffer_snapshot.line_len(MultiBufferRow(row));
10492 let anchor_end = snapshot
10493 .buffer_snapshot
10494 .anchor_after(Point::new(row, line_len));
10495
10496 let bp = self
10497 .breakpoint_store
10498 .as_ref()?
10499 .read_with(cx, |breakpoint_store, cx| {
10500 breakpoint_store
10501 .breakpoints(
10502 &buffer,
10503 Some(breakpoint_position.text_anchor..anchor_end.text_anchor),
10504 &buffer_snapshot,
10505 cx,
10506 )
10507 .next()
10508 .and_then(|(bp, _)| {
10509 let breakpoint_row = buffer_snapshot
10510 .summary_for_anchor::<text::PointUtf16>(&bp.position)
10511 .row;
10512
10513 if breakpoint_row == row {
10514 snapshot
10515 .buffer_snapshot
10516 .anchor_in_excerpt(enclosing_excerpt, bp.position)
10517 .map(|position| (position, bp.bp.clone()))
10518 } else {
10519 None
10520 }
10521 })
10522 });
10523 bp
10524 }
10525
10526 pub fn edit_log_breakpoint(
10527 &mut self,
10528 _: &EditLogBreakpoint,
10529 window: &mut Window,
10530 cx: &mut Context<Self>,
10531 ) {
10532 for (anchor, breakpoint) in self.breakpoints_at_cursors(window, cx) {
10533 let breakpoint = breakpoint.unwrap_or_else(|| Breakpoint {
10534 message: None,
10535 state: BreakpointState::Enabled,
10536 condition: None,
10537 hit_condition: None,
10538 });
10539
10540 self.add_edit_breakpoint_block(
10541 anchor,
10542 &breakpoint,
10543 BreakpointPromptEditAction::Log,
10544 window,
10545 cx,
10546 );
10547 }
10548 }
10549
10550 fn breakpoints_at_cursors(
10551 &self,
10552 window: &mut Window,
10553 cx: &mut Context<Self>,
10554 ) -> Vec<(Anchor, Option<Breakpoint>)> {
10555 let snapshot = self.snapshot(window, cx);
10556 let cursors = self
10557 .selections
10558 .disjoint_anchors()
10559 .into_iter()
10560 .map(|selection| {
10561 let cursor_position: Point = selection.head().to_point(&snapshot.buffer_snapshot);
10562
10563 let breakpoint_position = self
10564 .breakpoint_at_row(cursor_position.row, window, cx)
10565 .map(|bp| bp.0)
10566 .unwrap_or_else(|| {
10567 snapshot
10568 .display_snapshot
10569 .buffer_snapshot
10570 .anchor_after(Point::new(cursor_position.row, 0))
10571 });
10572
10573 let breakpoint = self
10574 .breakpoint_at_anchor(breakpoint_position, &snapshot, cx)
10575 .map(|(anchor, breakpoint)| (anchor, Some(breakpoint)));
10576
10577 breakpoint.unwrap_or_else(|| (breakpoint_position, None))
10578 })
10579 // 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.
10580 .collect::<HashMap<Anchor, _>>();
10581
10582 cursors.into_iter().collect()
10583 }
10584
10585 pub fn enable_breakpoint(
10586 &mut self,
10587 _: &crate::actions::EnableBreakpoint,
10588 window: &mut Window,
10589 cx: &mut Context<Self>,
10590 ) {
10591 for (anchor, breakpoint) in self.breakpoints_at_cursors(window, cx) {
10592 let Some(breakpoint) = breakpoint.filter(|breakpoint| breakpoint.is_disabled()) else {
10593 continue;
10594 };
10595 self.edit_breakpoint_at_anchor(
10596 anchor,
10597 breakpoint,
10598 BreakpointEditAction::InvertState,
10599 cx,
10600 );
10601 }
10602 }
10603
10604 pub fn disable_breakpoint(
10605 &mut self,
10606 _: &crate::actions::DisableBreakpoint,
10607 window: &mut Window,
10608 cx: &mut Context<Self>,
10609 ) {
10610 for (anchor, breakpoint) in self.breakpoints_at_cursors(window, cx) {
10611 let Some(breakpoint) = breakpoint.filter(|breakpoint| breakpoint.is_enabled()) else {
10612 continue;
10613 };
10614 self.edit_breakpoint_at_anchor(
10615 anchor,
10616 breakpoint,
10617 BreakpointEditAction::InvertState,
10618 cx,
10619 );
10620 }
10621 }
10622
10623 pub fn toggle_breakpoint(
10624 &mut self,
10625 _: &crate::actions::ToggleBreakpoint,
10626 window: &mut Window,
10627 cx: &mut Context<Self>,
10628 ) {
10629 for (anchor, breakpoint) in self.breakpoints_at_cursors(window, cx) {
10630 if let Some(breakpoint) = breakpoint {
10631 self.edit_breakpoint_at_anchor(
10632 anchor,
10633 breakpoint,
10634 BreakpointEditAction::Toggle,
10635 cx,
10636 );
10637 } else {
10638 self.edit_breakpoint_at_anchor(
10639 anchor,
10640 Breakpoint::new_standard(),
10641 BreakpointEditAction::Toggle,
10642 cx,
10643 );
10644 }
10645 }
10646 }
10647
10648 pub fn edit_breakpoint_at_anchor(
10649 &mut self,
10650 breakpoint_position: Anchor,
10651 breakpoint: Breakpoint,
10652 edit_action: BreakpointEditAction,
10653 cx: &mut Context<Self>,
10654 ) {
10655 let Some(breakpoint_store) = &self.breakpoint_store else {
10656 return;
10657 };
10658
10659 let Some(buffer_id) = breakpoint_position.buffer_id.or_else(|| {
10660 if breakpoint_position == Anchor::min() {
10661 self.buffer()
10662 .read(cx)
10663 .excerpt_buffer_ids()
10664 .into_iter()
10665 .next()
10666 } else {
10667 None
10668 }
10669 }) else {
10670 return;
10671 };
10672
10673 let Some(buffer) = self.buffer().read(cx).buffer(buffer_id) else {
10674 return;
10675 };
10676
10677 breakpoint_store.update(cx, |breakpoint_store, cx| {
10678 breakpoint_store.toggle_breakpoint(
10679 buffer,
10680 BreakpointWithPosition {
10681 position: breakpoint_position.text_anchor,
10682 bp: breakpoint,
10683 },
10684 edit_action,
10685 cx,
10686 );
10687 });
10688
10689 cx.notify();
10690 }
10691
10692 #[cfg(any(test, feature = "test-support"))]
10693 pub fn breakpoint_store(&self) -> Option<Entity<BreakpointStore>> {
10694 self.breakpoint_store.clone()
10695 }
10696
10697 pub fn prepare_restore_change(
10698 &self,
10699 revert_changes: &mut HashMap<BufferId, Vec<(Range<text::Anchor>, Rope)>>,
10700 hunk: &MultiBufferDiffHunk,
10701 cx: &mut App,
10702 ) -> Option<()> {
10703 if hunk.is_created_file() {
10704 return None;
10705 }
10706 let buffer = self.buffer.read(cx);
10707 let diff = buffer.diff_for(hunk.buffer_id)?;
10708 let buffer = buffer.buffer(hunk.buffer_id)?;
10709 let buffer = buffer.read(cx);
10710 let original_text = diff
10711 .read(cx)
10712 .base_text()
10713 .as_rope()
10714 .slice(hunk.diff_base_byte_range.clone());
10715 let buffer_snapshot = buffer.snapshot();
10716 let buffer_revert_changes = revert_changes.entry(buffer.remote_id()).or_default();
10717 if let Err(i) = buffer_revert_changes.binary_search_by(|probe| {
10718 probe
10719 .0
10720 .start
10721 .cmp(&hunk.buffer_range.start, &buffer_snapshot)
10722 .then(probe.0.end.cmp(&hunk.buffer_range.end, &buffer_snapshot))
10723 }) {
10724 buffer_revert_changes.insert(i, (hunk.buffer_range.clone(), original_text));
10725 Some(())
10726 } else {
10727 None
10728 }
10729 }
10730
10731 pub fn reverse_lines(&mut self, _: &ReverseLines, window: &mut Window, cx: &mut Context<Self>) {
10732 self.manipulate_immutable_lines(window, cx, |lines| lines.reverse())
10733 }
10734
10735 pub fn shuffle_lines(&mut self, _: &ShuffleLines, window: &mut Window, cx: &mut Context<Self>) {
10736 self.manipulate_immutable_lines(window, cx, |lines| lines.shuffle(&mut thread_rng()))
10737 }
10738
10739 fn manipulate_lines<M>(
10740 &mut self,
10741 window: &mut Window,
10742 cx: &mut Context<Self>,
10743 mut manipulate: M,
10744 ) where
10745 M: FnMut(&str) -> LineManipulationResult,
10746 {
10747 self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
10748
10749 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
10750 let buffer = self.buffer.read(cx).snapshot(cx);
10751
10752 let mut edits = Vec::new();
10753
10754 let selections = self.selections.all::<Point>(cx);
10755 let mut selections = selections.iter().peekable();
10756 let mut contiguous_row_selections = Vec::new();
10757 let mut new_selections = Vec::new();
10758 let mut added_lines = 0;
10759 let mut removed_lines = 0;
10760
10761 while let Some(selection) = selections.next() {
10762 let (start_row, end_row) = consume_contiguous_rows(
10763 &mut contiguous_row_selections,
10764 selection,
10765 &display_map,
10766 &mut selections,
10767 );
10768
10769 let start_point = Point::new(start_row.0, 0);
10770 let end_point = Point::new(
10771 end_row.previous_row().0,
10772 buffer.line_len(end_row.previous_row()),
10773 );
10774 let text = buffer
10775 .text_for_range(start_point..end_point)
10776 .collect::<String>();
10777
10778 let LineManipulationResult {
10779 new_text,
10780 line_count_before,
10781 line_count_after,
10782 } = manipulate(&text);
10783
10784 edits.push((start_point..end_point, new_text));
10785
10786 // Selections must change based on added and removed line count
10787 let start_row =
10788 MultiBufferRow(start_point.row + added_lines as u32 - removed_lines as u32);
10789 let end_row = MultiBufferRow(start_row.0 + line_count_after.saturating_sub(1) as u32);
10790 new_selections.push(Selection {
10791 id: selection.id,
10792 start: start_row,
10793 end: end_row,
10794 goal: SelectionGoal::None,
10795 reversed: selection.reversed,
10796 });
10797
10798 if line_count_after > line_count_before {
10799 added_lines += line_count_after - line_count_before;
10800 } else if line_count_before > line_count_after {
10801 removed_lines += line_count_before - line_count_after;
10802 }
10803 }
10804
10805 self.transact(window, cx, |this, window, cx| {
10806 let buffer = this.buffer.update(cx, |buffer, cx| {
10807 buffer.edit(edits, None, cx);
10808 buffer.snapshot(cx)
10809 });
10810
10811 // Recalculate offsets on newly edited buffer
10812 let new_selections = new_selections
10813 .iter()
10814 .map(|s| {
10815 let start_point = Point::new(s.start.0, 0);
10816 let end_point = Point::new(s.end.0, buffer.line_len(s.end));
10817 Selection {
10818 id: s.id,
10819 start: buffer.point_to_offset(start_point),
10820 end: buffer.point_to_offset(end_point),
10821 goal: s.goal,
10822 reversed: s.reversed,
10823 }
10824 })
10825 .collect();
10826
10827 this.change_selections(Default::default(), window, cx, |s| {
10828 s.select(new_selections);
10829 });
10830
10831 this.request_autoscroll(Autoscroll::fit(), cx);
10832 });
10833 }
10834
10835 pub fn toggle_case(&mut self, _: &ToggleCase, window: &mut Window, cx: &mut Context<Self>) {
10836 self.manipulate_text(window, cx, |text| {
10837 let has_upper_case_characters = text.chars().any(|c| c.is_uppercase());
10838 if has_upper_case_characters {
10839 text.to_lowercase()
10840 } else {
10841 text.to_uppercase()
10842 }
10843 })
10844 }
10845
10846 fn manipulate_immutable_lines<Fn>(
10847 &mut self,
10848 window: &mut Window,
10849 cx: &mut Context<Self>,
10850 mut callback: Fn,
10851 ) where
10852 Fn: FnMut(&mut Vec<&str>),
10853 {
10854 self.manipulate_lines(window, cx, |text| {
10855 let mut lines: Vec<&str> = text.split('\n').collect();
10856 let line_count_before = lines.len();
10857
10858 callback(&mut lines);
10859
10860 LineManipulationResult {
10861 new_text: lines.join("\n"),
10862 line_count_before,
10863 line_count_after: lines.len(),
10864 }
10865 });
10866 }
10867
10868 fn manipulate_mutable_lines<Fn>(
10869 &mut self,
10870 window: &mut Window,
10871 cx: &mut Context<Self>,
10872 mut callback: Fn,
10873 ) where
10874 Fn: FnMut(&mut Vec<Cow<'_, str>>),
10875 {
10876 self.manipulate_lines(window, cx, |text| {
10877 let mut lines: Vec<Cow<str>> = text.split('\n').map(Cow::from).collect();
10878 let line_count_before = lines.len();
10879
10880 callback(&mut lines);
10881
10882 LineManipulationResult {
10883 new_text: lines.join("\n"),
10884 line_count_before,
10885 line_count_after: lines.len(),
10886 }
10887 });
10888 }
10889
10890 pub fn convert_indentation_to_spaces(
10891 &mut self,
10892 _: &ConvertIndentationToSpaces,
10893 window: &mut Window,
10894 cx: &mut Context<Self>,
10895 ) {
10896 let settings = self.buffer.read(cx).language_settings(cx);
10897 let tab_size = settings.tab_size.get() as usize;
10898
10899 self.manipulate_mutable_lines(window, cx, |lines| {
10900 // Allocates a reasonably sized scratch buffer once for the whole loop
10901 let mut reindented_line = String::with_capacity(MAX_LINE_LEN);
10902 // Avoids recomputing spaces that could be inserted many times
10903 let space_cache: Vec<Vec<char>> = (1..=tab_size)
10904 .map(|n| IndentSize::spaces(n as u32).chars().collect())
10905 .collect();
10906
10907 for line in lines.iter_mut().filter(|line| !line.is_empty()) {
10908 let mut chars = line.as_ref().chars();
10909 let mut col = 0;
10910 let mut changed = false;
10911
10912 while let Some(ch) = chars.next() {
10913 match ch {
10914 ' ' => {
10915 reindented_line.push(' ');
10916 col += 1;
10917 }
10918 '\t' => {
10919 // \t are converted to spaces depending on the current column
10920 let spaces_len = tab_size - (col % tab_size);
10921 reindented_line.extend(&space_cache[spaces_len - 1]);
10922 col += spaces_len;
10923 changed = true;
10924 }
10925 _ => {
10926 // If we dont append before break, the character is consumed
10927 reindented_line.push(ch);
10928 break;
10929 }
10930 }
10931 }
10932
10933 if !changed {
10934 reindented_line.clear();
10935 continue;
10936 }
10937 // Append the rest of the line and replace old reference with new one
10938 reindented_line.extend(chars);
10939 *line = Cow::Owned(reindented_line.clone());
10940 reindented_line.clear();
10941 }
10942 });
10943 }
10944
10945 pub fn convert_indentation_to_tabs(
10946 &mut self,
10947 _: &ConvertIndentationToTabs,
10948 window: &mut Window,
10949 cx: &mut Context<Self>,
10950 ) {
10951 let settings = self.buffer.read(cx).language_settings(cx);
10952 let tab_size = settings.tab_size.get() as usize;
10953
10954 self.manipulate_mutable_lines(window, cx, |lines| {
10955 // Allocates a reasonably sized buffer once for the whole loop
10956 let mut reindented_line = String::with_capacity(MAX_LINE_LEN);
10957 // Avoids recomputing spaces that could be inserted many times
10958 let space_cache: Vec<Vec<char>> = (1..=tab_size)
10959 .map(|n| IndentSize::spaces(n as u32).chars().collect())
10960 .collect();
10961
10962 for line in lines.iter_mut().filter(|line| !line.is_empty()) {
10963 let mut chars = line.chars();
10964 let mut spaces_count = 0;
10965 let mut first_non_indent_char = None;
10966 let mut changed = false;
10967
10968 while let Some(ch) = chars.next() {
10969 match ch {
10970 ' ' => {
10971 // Keep track of spaces. Append \t when we reach tab_size
10972 spaces_count += 1;
10973 changed = true;
10974 if spaces_count == tab_size {
10975 reindented_line.push('\t');
10976 spaces_count = 0;
10977 }
10978 }
10979 '\t' => {
10980 reindented_line.push('\t');
10981 spaces_count = 0;
10982 }
10983 _ => {
10984 // Dont append it yet, we might have remaining spaces
10985 first_non_indent_char = Some(ch);
10986 break;
10987 }
10988 }
10989 }
10990
10991 if !changed {
10992 reindented_line.clear();
10993 continue;
10994 }
10995 // Remaining spaces that didn't make a full tab stop
10996 if spaces_count > 0 {
10997 reindented_line.extend(&space_cache[spaces_count - 1]);
10998 }
10999 // If we consume an extra character that was not indentation, add it back
11000 if let Some(extra_char) = first_non_indent_char {
11001 reindented_line.push(extra_char);
11002 }
11003 // Append the rest of the line and replace old reference with new one
11004 reindented_line.extend(chars);
11005 *line = Cow::Owned(reindented_line.clone());
11006 reindented_line.clear();
11007 }
11008 });
11009 }
11010
11011 pub fn convert_to_upper_case(
11012 &mut self,
11013 _: &ConvertToUpperCase,
11014 window: &mut Window,
11015 cx: &mut Context<Self>,
11016 ) {
11017 self.manipulate_text(window, cx, |text| text.to_uppercase())
11018 }
11019
11020 pub fn convert_to_lower_case(
11021 &mut self,
11022 _: &ConvertToLowerCase,
11023 window: &mut Window,
11024 cx: &mut Context<Self>,
11025 ) {
11026 self.manipulate_text(window, cx, |text| text.to_lowercase())
11027 }
11028
11029 pub fn convert_to_title_case(
11030 &mut self,
11031 _: &ConvertToTitleCase,
11032 window: &mut Window,
11033 cx: &mut Context<Self>,
11034 ) {
11035 self.manipulate_text(window, cx, |text| {
11036 text.split('\n')
11037 .map(|line| line.to_case(Case::Title))
11038 .join("\n")
11039 })
11040 }
11041
11042 pub fn convert_to_snake_case(
11043 &mut self,
11044 _: &ConvertToSnakeCase,
11045 window: &mut Window,
11046 cx: &mut Context<Self>,
11047 ) {
11048 self.manipulate_text(window, cx, |text| text.to_case(Case::Snake))
11049 }
11050
11051 pub fn convert_to_kebab_case(
11052 &mut self,
11053 _: &ConvertToKebabCase,
11054 window: &mut Window,
11055 cx: &mut Context<Self>,
11056 ) {
11057 self.manipulate_text(window, cx, |text| text.to_case(Case::Kebab))
11058 }
11059
11060 pub fn convert_to_upper_camel_case(
11061 &mut self,
11062 _: &ConvertToUpperCamelCase,
11063 window: &mut Window,
11064 cx: &mut Context<Self>,
11065 ) {
11066 self.manipulate_text(window, cx, |text| {
11067 text.split('\n')
11068 .map(|line| line.to_case(Case::UpperCamel))
11069 .join("\n")
11070 })
11071 }
11072
11073 pub fn convert_to_lower_camel_case(
11074 &mut self,
11075 _: &ConvertToLowerCamelCase,
11076 window: &mut Window,
11077 cx: &mut Context<Self>,
11078 ) {
11079 self.manipulate_text(window, cx, |text| text.to_case(Case::Camel))
11080 }
11081
11082 pub fn convert_to_opposite_case(
11083 &mut self,
11084 _: &ConvertToOppositeCase,
11085 window: &mut Window,
11086 cx: &mut Context<Self>,
11087 ) {
11088 self.manipulate_text(window, cx, |text| {
11089 text.chars()
11090 .fold(String::with_capacity(text.len()), |mut t, c| {
11091 if c.is_uppercase() {
11092 t.extend(c.to_lowercase());
11093 } else {
11094 t.extend(c.to_uppercase());
11095 }
11096 t
11097 })
11098 })
11099 }
11100
11101 pub fn convert_to_rot13(
11102 &mut self,
11103 _: &ConvertToRot13,
11104 window: &mut Window,
11105 cx: &mut Context<Self>,
11106 ) {
11107 self.manipulate_text(window, cx, |text| {
11108 text.chars()
11109 .map(|c| match c {
11110 'A'..='M' | 'a'..='m' => ((c as u8) + 13) as char,
11111 'N'..='Z' | 'n'..='z' => ((c as u8) - 13) as char,
11112 _ => c,
11113 })
11114 .collect()
11115 })
11116 }
11117
11118 pub fn convert_to_rot47(
11119 &mut self,
11120 _: &ConvertToRot47,
11121 window: &mut Window,
11122 cx: &mut Context<Self>,
11123 ) {
11124 self.manipulate_text(window, cx, |text| {
11125 text.chars()
11126 .map(|c| {
11127 let code_point = c as u32;
11128 if code_point >= 33 && code_point <= 126 {
11129 return char::from_u32(33 + ((code_point + 14) % 94)).unwrap();
11130 }
11131 c
11132 })
11133 .collect()
11134 })
11135 }
11136
11137 fn manipulate_text<Fn>(&mut self, window: &mut Window, cx: &mut Context<Self>, mut callback: Fn)
11138 where
11139 Fn: FnMut(&str) -> String,
11140 {
11141 let buffer = self.buffer.read(cx).snapshot(cx);
11142
11143 let mut new_selections = Vec::new();
11144 let mut edits = Vec::new();
11145 let mut selection_adjustment = 0i32;
11146
11147 for selection in self.selections.all::<usize>(cx) {
11148 let selection_is_empty = selection.is_empty();
11149
11150 let (start, end) = if selection_is_empty {
11151 let (word_range, _) = buffer.surrounding_word(selection.start, false);
11152 (word_range.start, word_range.end)
11153 } else {
11154 (selection.start, selection.end)
11155 };
11156
11157 let text = buffer.text_for_range(start..end).collect::<String>();
11158 let old_length = text.len() as i32;
11159 let text = callback(&text);
11160
11161 new_selections.push(Selection {
11162 start: (start as i32 - selection_adjustment) as usize,
11163 end: ((start + text.len()) as i32 - selection_adjustment) as usize,
11164 goal: SelectionGoal::None,
11165 ..selection
11166 });
11167
11168 selection_adjustment += old_length - text.len() as i32;
11169
11170 edits.push((start..end, text));
11171 }
11172
11173 self.transact(window, cx, |this, window, cx| {
11174 this.buffer.update(cx, |buffer, cx| {
11175 buffer.edit(edits, None, cx);
11176 });
11177
11178 this.change_selections(Default::default(), window, cx, |s| {
11179 s.select(new_selections);
11180 });
11181
11182 this.request_autoscroll(Autoscroll::fit(), cx);
11183 });
11184 }
11185
11186 pub fn move_selection_on_drop(
11187 &mut self,
11188 selection: &Selection<Anchor>,
11189 target: DisplayPoint,
11190 is_cut: bool,
11191 window: &mut Window,
11192 cx: &mut Context<Self>,
11193 ) {
11194 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
11195 let buffer = &display_map.buffer_snapshot;
11196 let mut edits = Vec::new();
11197 let insert_point = display_map
11198 .clip_point(target, Bias::Left)
11199 .to_point(&display_map);
11200 let text = buffer
11201 .text_for_range(selection.start..selection.end)
11202 .collect::<String>();
11203 if is_cut {
11204 edits.push(((selection.start..selection.end), String::new()));
11205 }
11206 let insert_anchor = buffer.anchor_before(insert_point);
11207 edits.push(((insert_anchor..insert_anchor), text));
11208 let last_edit_start = insert_anchor.bias_left(buffer);
11209 let last_edit_end = insert_anchor.bias_right(buffer);
11210 self.transact(window, cx, |this, window, cx| {
11211 this.buffer.update(cx, |buffer, cx| {
11212 buffer.edit(edits, None, cx);
11213 });
11214 this.change_selections(Default::default(), window, cx, |s| {
11215 s.select_anchor_ranges([last_edit_start..last_edit_end]);
11216 });
11217 });
11218 }
11219
11220 pub fn clear_selection_drag_state(&mut self) {
11221 self.selection_drag_state = SelectionDragState::None;
11222 }
11223
11224 pub fn duplicate(
11225 &mut self,
11226 upwards: bool,
11227 whole_lines: bool,
11228 window: &mut Window,
11229 cx: &mut Context<Self>,
11230 ) {
11231 self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
11232
11233 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
11234 let buffer = &display_map.buffer_snapshot;
11235 let selections = self.selections.all::<Point>(cx);
11236
11237 let mut edits = Vec::new();
11238 let mut selections_iter = selections.iter().peekable();
11239 while let Some(selection) = selections_iter.next() {
11240 let mut rows = selection.spanned_rows(false, &display_map);
11241 // duplicate line-wise
11242 if whole_lines || selection.start == selection.end {
11243 // Avoid duplicating the same lines twice.
11244 while let Some(next_selection) = selections_iter.peek() {
11245 let next_rows = next_selection.spanned_rows(false, &display_map);
11246 if next_rows.start < rows.end {
11247 rows.end = next_rows.end;
11248 selections_iter.next().unwrap();
11249 } else {
11250 break;
11251 }
11252 }
11253
11254 // Copy the text from the selected row region and splice it either at the start
11255 // or end of the region.
11256 let start = Point::new(rows.start.0, 0);
11257 let end = Point::new(
11258 rows.end.previous_row().0,
11259 buffer.line_len(rows.end.previous_row()),
11260 );
11261 let text = buffer
11262 .text_for_range(start..end)
11263 .chain(Some("\n"))
11264 .collect::<String>();
11265 let insert_location = if upwards {
11266 Point::new(rows.end.0, 0)
11267 } else {
11268 start
11269 };
11270 edits.push((insert_location..insert_location, text));
11271 } else {
11272 // duplicate character-wise
11273 let start = selection.start;
11274 let end = selection.end;
11275 let text = buffer.text_for_range(start..end).collect::<String>();
11276 edits.push((selection.end..selection.end, text));
11277 }
11278 }
11279
11280 self.transact(window, cx, |this, _, cx| {
11281 this.buffer.update(cx, |buffer, cx| {
11282 buffer.edit(edits, None, cx);
11283 });
11284
11285 this.request_autoscroll(Autoscroll::fit(), cx);
11286 });
11287 }
11288
11289 pub fn duplicate_line_up(
11290 &mut self,
11291 _: &DuplicateLineUp,
11292 window: &mut Window,
11293 cx: &mut Context<Self>,
11294 ) {
11295 self.duplicate(true, true, window, cx);
11296 }
11297
11298 pub fn duplicate_line_down(
11299 &mut self,
11300 _: &DuplicateLineDown,
11301 window: &mut Window,
11302 cx: &mut Context<Self>,
11303 ) {
11304 self.duplicate(false, true, window, cx);
11305 }
11306
11307 pub fn duplicate_selection(
11308 &mut self,
11309 _: &DuplicateSelection,
11310 window: &mut Window,
11311 cx: &mut Context<Self>,
11312 ) {
11313 self.duplicate(false, false, window, cx);
11314 }
11315
11316 pub fn move_line_up(&mut self, _: &MoveLineUp, window: &mut Window, cx: &mut Context<Self>) {
11317 self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
11318 if self.mode.is_single_line() {
11319 cx.propagate();
11320 return;
11321 }
11322
11323 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
11324 let buffer = self.buffer.read(cx).snapshot(cx);
11325
11326 let mut edits = Vec::new();
11327 let mut unfold_ranges = Vec::new();
11328 let mut refold_creases = Vec::new();
11329
11330 let selections = self.selections.all::<Point>(cx);
11331 let mut selections = selections.iter().peekable();
11332 let mut contiguous_row_selections = Vec::new();
11333 let mut new_selections = Vec::new();
11334
11335 while let Some(selection) = selections.next() {
11336 // Find all the selections that span a contiguous row range
11337 let (start_row, end_row) = consume_contiguous_rows(
11338 &mut contiguous_row_selections,
11339 selection,
11340 &display_map,
11341 &mut selections,
11342 );
11343
11344 // Move the text spanned by the row range to be before the line preceding the row range
11345 if start_row.0 > 0 {
11346 let range_to_move = Point::new(
11347 start_row.previous_row().0,
11348 buffer.line_len(start_row.previous_row()),
11349 )
11350 ..Point::new(
11351 end_row.previous_row().0,
11352 buffer.line_len(end_row.previous_row()),
11353 );
11354 let insertion_point = display_map
11355 .prev_line_boundary(Point::new(start_row.previous_row().0, 0))
11356 .0;
11357
11358 // Don't move lines across excerpts
11359 if buffer
11360 .excerpt_containing(insertion_point..range_to_move.end)
11361 .is_some()
11362 {
11363 let text = buffer
11364 .text_for_range(range_to_move.clone())
11365 .flat_map(|s| s.chars())
11366 .skip(1)
11367 .chain(['\n'])
11368 .collect::<String>();
11369
11370 edits.push((
11371 buffer.anchor_after(range_to_move.start)
11372 ..buffer.anchor_before(range_to_move.end),
11373 String::new(),
11374 ));
11375 let insertion_anchor = buffer.anchor_after(insertion_point);
11376 edits.push((insertion_anchor..insertion_anchor, text));
11377
11378 let row_delta = range_to_move.start.row - insertion_point.row + 1;
11379
11380 // Move selections up
11381 new_selections.extend(contiguous_row_selections.drain(..).map(
11382 |mut selection| {
11383 selection.start.row -= row_delta;
11384 selection.end.row -= row_delta;
11385 selection
11386 },
11387 ));
11388
11389 // Move folds up
11390 unfold_ranges.push(range_to_move.clone());
11391 for fold in display_map.folds_in_range(
11392 buffer.anchor_before(range_to_move.start)
11393 ..buffer.anchor_after(range_to_move.end),
11394 ) {
11395 let mut start = fold.range.start.to_point(&buffer);
11396 let mut end = fold.range.end.to_point(&buffer);
11397 start.row -= row_delta;
11398 end.row -= row_delta;
11399 refold_creases.push(Crease::simple(start..end, fold.placeholder.clone()));
11400 }
11401 }
11402 }
11403
11404 // If we didn't move line(s), preserve the existing selections
11405 new_selections.append(&mut contiguous_row_selections);
11406 }
11407
11408 self.transact(window, cx, |this, window, cx| {
11409 this.unfold_ranges(&unfold_ranges, true, true, cx);
11410 this.buffer.update(cx, |buffer, cx| {
11411 for (range, text) in edits {
11412 buffer.edit([(range, text)], None, cx);
11413 }
11414 });
11415 this.fold_creases(refold_creases, true, window, cx);
11416 this.change_selections(Default::default(), window, cx, |s| {
11417 s.select(new_selections);
11418 })
11419 });
11420 }
11421
11422 pub fn move_line_down(
11423 &mut self,
11424 _: &MoveLineDown,
11425 window: &mut Window,
11426 cx: &mut Context<Self>,
11427 ) {
11428 self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
11429 if self.mode.is_single_line() {
11430 cx.propagate();
11431 return;
11432 }
11433
11434 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
11435 let buffer = self.buffer.read(cx).snapshot(cx);
11436
11437 let mut edits = Vec::new();
11438 let mut unfold_ranges = Vec::new();
11439 let mut refold_creases = Vec::new();
11440
11441 let selections = self.selections.all::<Point>(cx);
11442 let mut selections = selections.iter().peekable();
11443 let mut contiguous_row_selections = Vec::new();
11444 let mut new_selections = Vec::new();
11445
11446 while let Some(selection) = selections.next() {
11447 // Find all the selections that span a contiguous row range
11448 let (start_row, end_row) = consume_contiguous_rows(
11449 &mut contiguous_row_selections,
11450 selection,
11451 &display_map,
11452 &mut selections,
11453 );
11454
11455 // Move the text spanned by the row range to be after the last line of the row range
11456 if end_row.0 <= buffer.max_point().row {
11457 let range_to_move =
11458 MultiBufferPoint::new(start_row.0, 0)..MultiBufferPoint::new(end_row.0, 0);
11459 let insertion_point = display_map
11460 .next_line_boundary(MultiBufferPoint::new(end_row.0, 0))
11461 .0;
11462
11463 // Don't move lines across excerpt boundaries
11464 if buffer
11465 .excerpt_containing(range_to_move.start..insertion_point)
11466 .is_some()
11467 {
11468 let mut text = String::from("\n");
11469 text.extend(buffer.text_for_range(range_to_move.clone()));
11470 text.pop(); // Drop trailing newline
11471 edits.push((
11472 buffer.anchor_after(range_to_move.start)
11473 ..buffer.anchor_before(range_to_move.end),
11474 String::new(),
11475 ));
11476 let insertion_anchor = buffer.anchor_after(insertion_point);
11477 edits.push((insertion_anchor..insertion_anchor, text));
11478
11479 let row_delta = insertion_point.row - range_to_move.end.row + 1;
11480
11481 // Move selections down
11482 new_selections.extend(contiguous_row_selections.drain(..).map(
11483 |mut selection| {
11484 selection.start.row += row_delta;
11485 selection.end.row += row_delta;
11486 selection
11487 },
11488 ));
11489
11490 // Move folds down
11491 unfold_ranges.push(range_to_move.clone());
11492 for fold in display_map.folds_in_range(
11493 buffer.anchor_before(range_to_move.start)
11494 ..buffer.anchor_after(range_to_move.end),
11495 ) {
11496 let mut start = fold.range.start.to_point(&buffer);
11497 let mut end = fold.range.end.to_point(&buffer);
11498 start.row += row_delta;
11499 end.row += row_delta;
11500 refold_creases.push(Crease::simple(start..end, fold.placeholder.clone()));
11501 }
11502 }
11503 }
11504
11505 // If we didn't move line(s), preserve the existing selections
11506 new_selections.append(&mut contiguous_row_selections);
11507 }
11508
11509 self.transact(window, cx, |this, window, cx| {
11510 this.unfold_ranges(&unfold_ranges, true, true, cx);
11511 this.buffer.update(cx, |buffer, cx| {
11512 for (range, text) in edits {
11513 buffer.edit([(range, text)], None, cx);
11514 }
11515 });
11516 this.fold_creases(refold_creases, true, window, cx);
11517 this.change_selections(Default::default(), window, cx, |s| s.select(new_selections));
11518 });
11519 }
11520
11521 pub fn transpose(&mut self, _: &Transpose, window: &mut Window, cx: &mut Context<Self>) {
11522 self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
11523 let text_layout_details = &self.text_layout_details(window);
11524 self.transact(window, cx, |this, window, cx| {
11525 let edits = this.change_selections(Default::default(), window, cx, |s| {
11526 let mut edits: Vec<(Range<usize>, String)> = Default::default();
11527 s.move_with(|display_map, selection| {
11528 if !selection.is_empty() {
11529 return;
11530 }
11531
11532 let mut head = selection.head();
11533 let mut transpose_offset = head.to_offset(display_map, Bias::Right);
11534 if head.column() == display_map.line_len(head.row()) {
11535 transpose_offset = display_map
11536 .buffer_snapshot
11537 .clip_offset(transpose_offset.saturating_sub(1), Bias::Left);
11538 }
11539
11540 if transpose_offset == 0 {
11541 return;
11542 }
11543
11544 *head.column_mut() += 1;
11545 head = display_map.clip_point(head, Bias::Right);
11546 let goal = SelectionGoal::HorizontalPosition(
11547 display_map
11548 .x_for_display_point(head, text_layout_details)
11549 .into(),
11550 );
11551 selection.collapse_to(head, goal);
11552
11553 let transpose_start = display_map
11554 .buffer_snapshot
11555 .clip_offset(transpose_offset.saturating_sub(1), Bias::Left);
11556 if edits.last().map_or(true, |e| e.0.end <= transpose_start) {
11557 let transpose_end = display_map
11558 .buffer_snapshot
11559 .clip_offset(transpose_offset + 1, Bias::Right);
11560 if let Some(ch) =
11561 display_map.buffer_snapshot.chars_at(transpose_start).next()
11562 {
11563 edits.push((transpose_start..transpose_offset, String::new()));
11564 edits.push((transpose_end..transpose_end, ch.to_string()));
11565 }
11566 }
11567 });
11568 edits
11569 });
11570 this.buffer
11571 .update(cx, |buffer, cx| buffer.edit(edits, None, cx));
11572 let selections = this.selections.all::<usize>(cx);
11573 this.change_selections(Default::default(), window, cx, |s| {
11574 s.select(selections);
11575 });
11576 });
11577 }
11578
11579 pub fn rewrap(&mut self, _: &Rewrap, _: &mut Window, cx: &mut Context<Self>) {
11580 self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
11581 if self.mode.is_single_line() {
11582 cx.propagate();
11583 return;
11584 }
11585
11586 self.rewrap_impl(RewrapOptions::default(), cx)
11587 }
11588
11589 pub fn rewrap_impl(&mut self, options: RewrapOptions, cx: &mut Context<Self>) {
11590 let buffer = self.buffer.read(cx).snapshot(cx);
11591 let selections = self.selections.all::<Point>(cx);
11592
11593 // Split selections to respect paragraph, indent, and comment prefix boundaries.
11594 let wrap_ranges = selections.into_iter().flat_map(|selection| {
11595 let mut non_blank_rows_iter = (selection.start.row..=selection.end.row)
11596 .filter(|row| !buffer.is_line_blank(MultiBufferRow(*row)))
11597 .peekable();
11598
11599 let first_row = if let Some(&row) = non_blank_rows_iter.peek() {
11600 row
11601 } else {
11602 return Vec::new();
11603 };
11604
11605 let language_settings = buffer.language_settings_at(selection.head(), cx);
11606 let language_scope = buffer.language_scope_at(selection.head());
11607
11608 let indent_and_prefix_for_row =
11609 |row: u32| -> (IndentSize, Option<String>, Option<String>) {
11610 let indent = buffer.indent_size_for_line(MultiBufferRow(row));
11611 let (comment_prefix, rewrap_prefix) =
11612 if let Some(language_scope) = &language_scope {
11613 let indent_end = Point::new(row, indent.len);
11614 let comment_prefix = language_scope
11615 .line_comment_prefixes()
11616 .iter()
11617 .find(|prefix| buffer.contains_str_at(indent_end, prefix))
11618 .map(|prefix| prefix.to_string());
11619 let line_end = Point::new(row, buffer.line_len(MultiBufferRow(row)));
11620 let line_text_after_indent = buffer
11621 .text_for_range(indent_end..line_end)
11622 .collect::<String>();
11623 let rewrap_prefix = language_scope
11624 .rewrap_prefixes()
11625 .iter()
11626 .find_map(|prefix_regex| {
11627 prefix_regex.find(&line_text_after_indent).map(|mat| {
11628 if mat.start() == 0 {
11629 Some(mat.as_str().to_string())
11630 } else {
11631 None
11632 }
11633 })
11634 })
11635 .flatten();
11636 (comment_prefix, rewrap_prefix)
11637 } else {
11638 (None, None)
11639 };
11640 (indent, comment_prefix, rewrap_prefix)
11641 };
11642
11643 let mut ranges = Vec::new();
11644 let from_empty_selection = selection.is_empty();
11645
11646 let mut current_range_start = first_row;
11647 let mut prev_row = first_row;
11648 let (
11649 mut current_range_indent,
11650 mut current_range_comment_prefix,
11651 mut current_range_rewrap_prefix,
11652 ) = indent_and_prefix_for_row(first_row);
11653
11654 for row in non_blank_rows_iter.skip(1) {
11655 let has_paragraph_break = row > prev_row + 1;
11656
11657 let (row_indent, row_comment_prefix, row_rewrap_prefix) =
11658 indent_and_prefix_for_row(row);
11659
11660 let has_indent_change = row_indent != current_range_indent;
11661 let has_comment_change = row_comment_prefix != current_range_comment_prefix;
11662
11663 let has_boundary_change = has_comment_change
11664 || row_rewrap_prefix.is_some()
11665 || (has_indent_change && current_range_comment_prefix.is_some());
11666
11667 if has_paragraph_break || has_boundary_change {
11668 ranges.push((
11669 language_settings.clone(),
11670 Point::new(current_range_start, 0)
11671 ..Point::new(prev_row, buffer.line_len(MultiBufferRow(prev_row))),
11672 current_range_indent,
11673 current_range_comment_prefix.clone(),
11674 current_range_rewrap_prefix.clone(),
11675 from_empty_selection,
11676 ));
11677 current_range_start = row;
11678 current_range_indent = row_indent;
11679 current_range_comment_prefix = row_comment_prefix;
11680 current_range_rewrap_prefix = row_rewrap_prefix;
11681 }
11682 prev_row = row;
11683 }
11684
11685 ranges.push((
11686 language_settings.clone(),
11687 Point::new(current_range_start, 0)
11688 ..Point::new(prev_row, buffer.line_len(MultiBufferRow(prev_row))),
11689 current_range_indent,
11690 current_range_comment_prefix,
11691 current_range_rewrap_prefix,
11692 from_empty_selection,
11693 ));
11694
11695 ranges
11696 });
11697
11698 let mut edits = Vec::new();
11699 let mut rewrapped_row_ranges = Vec::<RangeInclusive<u32>>::new();
11700
11701 for (
11702 language_settings,
11703 wrap_range,
11704 indent_size,
11705 comment_prefix,
11706 rewrap_prefix,
11707 from_empty_selection,
11708 ) in wrap_ranges
11709 {
11710 let mut start_row = wrap_range.start.row;
11711 let mut end_row = wrap_range.end.row;
11712
11713 // Skip selections that overlap with a range that has already been rewrapped.
11714 let selection_range = start_row..end_row;
11715 if rewrapped_row_ranges
11716 .iter()
11717 .any(|range| range.overlaps(&selection_range))
11718 {
11719 continue;
11720 }
11721
11722 let tab_size = language_settings.tab_size;
11723
11724 let indent_prefix = indent_size.chars().collect::<String>();
11725 let mut line_prefix = indent_prefix.clone();
11726 let mut inside_comment = false;
11727 if let Some(prefix) = &comment_prefix {
11728 line_prefix.push_str(prefix);
11729 inside_comment = true;
11730 }
11731 if let Some(prefix) = &rewrap_prefix {
11732 line_prefix.push_str(prefix);
11733 }
11734
11735 let allow_rewrap_based_on_language = match language_settings.allow_rewrap {
11736 RewrapBehavior::InComments => inside_comment,
11737 RewrapBehavior::InSelections => !wrap_range.is_empty(),
11738 RewrapBehavior::Anywhere => true,
11739 };
11740
11741 let should_rewrap = options.override_language_settings
11742 || allow_rewrap_based_on_language
11743 || self.hard_wrap.is_some();
11744 if !should_rewrap {
11745 continue;
11746 }
11747
11748 if from_empty_selection {
11749 'expand_upwards: while start_row > 0 {
11750 let prev_row = start_row - 1;
11751 if buffer.contains_str_at(Point::new(prev_row, 0), &line_prefix)
11752 && buffer.line_len(MultiBufferRow(prev_row)) as usize > line_prefix.len()
11753 && !buffer.is_line_blank(MultiBufferRow(prev_row))
11754 {
11755 start_row = prev_row;
11756 } else {
11757 break 'expand_upwards;
11758 }
11759 }
11760
11761 'expand_downwards: while end_row < buffer.max_point().row {
11762 let next_row = end_row + 1;
11763 if buffer.contains_str_at(Point::new(next_row, 0), &line_prefix)
11764 && buffer.line_len(MultiBufferRow(next_row)) as usize > line_prefix.len()
11765 && !buffer.is_line_blank(MultiBufferRow(next_row))
11766 {
11767 end_row = next_row;
11768 } else {
11769 break 'expand_downwards;
11770 }
11771 }
11772 }
11773
11774 let start = Point::new(start_row, 0);
11775 let start_offset = start.to_offset(&buffer);
11776 let end = Point::new(end_row, buffer.line_len(MultiBufferRow(end_row)));
11777 let selection_text = buffer.text_for_range(start..end).collect::<String>();
11778 let Some(lines_without_prefixes) = selection_text
11779 .lines()
11780 .enumerate()
11781 .map(|(ix, line)| {
11782 let line_trimmed = line.trim_start();
11783 if rewrap_prefix.is_some() && ix > 0 {
11784 Ok(line_trimmed)
11785 } else {
11786 line_trimmed
11787 .strip_prefix(&line_prefix.trim_start())
11788 .with_context(|| {
11789 format!("line did not start with prefix {line_prefix:?}: {line:?}")
11790 })
11791 }
11792 })
11793 .collect::<Result<Vec<_>, _>>()
11794 .log_err()
11795 else {
11796 continue;
11797 };
11798
11799 let wrap_column = self.hard_wrap.unwrap_or_else(|| {
11800 buffer
11801 .language_settings_at(Point::new(start_row, 0), cx)
11802 .preferred_line_length as usize
11803 });
11804
11805 let subsequent_lines_prefix = if let Some(rewrap_prefix_str) = &rewrap_prefix {
11806 format!("{}{}", indent_prefix, " ".repeat(rewrap_prefix_str.len()))
11807 } else {
11808 line_prefix.clone()
11809 };
11810
11811 let wrapped_text = wrap_with_prefix(
11812 line_prefix,
11813 subsequent_lines_prefix,
11814 lines_without_prefixes.join("\n"),
11815 wrap_column,
11816 tab_size,
11817 options.preserve_existing_whitespace,
11818 );
11819
11820 // TODO: should always use char-based diff while still supporting cursor behavior that
11821 // matches vim.
11822 let mut diff_options = DiffOptions::default();
11823 if options.override_language_settings {
11824 diff_options.max_word_diff_len = 0;
11825 diff_options.max_word_diff_line_count = 0;
11826 } else {
11827 diff_options.max_word_diff_len = usize::MAX;
11828 diff_options.max_word_diff_line_count = usize::MAX;
11829 }
11830
11831 for (old_range, new_text) in
11832 text_diff_with_options(&selection_text, &wrapped_text, diff_options)
11833 {
11834 let edit_start = buffer.anchor_after(start_offset + old_range.start);
11835 let edit_end = buffer.anchor_after(start_offset + old_range.end);
11836 edits.push((edit_start..edit_end, new_text));
11837 }
11838
11839 rewrapped_row_ranges.push(start_row..=end_row);
11840 }
11841
11842 self.buffer
11843 .update(cx, |buffer, cx| buffer.edit(edits, None, cx));
11844 }
11845
11846 pub fn cut_common(&mut self, window: &mut Window, cx: &mut Context<Self>) -> ClipboardItem {
11847 let mut text = String::new();
11848 let buffer = self.buffer.read(cx).snapshot(cx);
11849 let mut selections = self.selections.all::<Point>(cx);
11850 let mut clipboard_selections = Vec::with_capacity(selections.len());
11851 {
11852 let max_point = buffer.max_point();
11853 let mut is_first = true;
11854 for selection in &mut selections {
11855 let is_entire_line = selection.is_empty() || self.selections.line_mode;
11856 if is_entire_line {
11857 selection.start = Point::new(selection.start.row, 0);
11858 if !selection.is_empty() && selection.end.column == 0 {
11859 selection.end = cmp::min(max_point, selection.end);
11860 } else {
11861 selection.end = cmp::min(max_point, Point::new(selection.end.row + 1, 0));
11862 }
11863 selection.goal = SelectionGoal::None;
11864 }
11865 if is_first {
11866 is_first = false;
11867 } else {
11868 text += "\n";
11869 }
11870 let mut len = 0;
11871 for chunk in buffer.text_for_range(selection.start..selection.end) {
11872 text.push_str(chunk);
11873 len += chunk.len();
11874 }
11875 clipboard_selections.push(ClipboardSelection {
11876 len,
11877 is_entire_line,
11878 first_line_indent: buffer
11879 .indent_size_for_line(MultiBufferRow(selection.start.row))
11880 .len,
11881 });
11882 }
11883 }
11884
11885 self.transact(window, cx, |this, window, cx| {
11886 this.change_selections(Default::default(), window, cx, |s| {
11887 s.select(selections);
11888 });
11889 this.insert("", window, cx);
11890 });
11891 ClipboardItem::new_string_with_json_metadata(text, clipboard_selections)
11892 }
11893
11894 pub fn cut(&mut self, _: &Cut, window: &mut Window, cx: &mut Context<Self>) {
11895 self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
11896 let item = self.cut_common(window, cx);
11897 cx.write_to_clipboard(item);
11898 }
11899
11900 pub fn kill_ring_cut(&mut self, _: &KillRingCut, window: &mut Window, cx: &mut Context<Self>) {
11901 self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
11902 self.change_selections(SelectionEffects::no_scroll(), window, cx, |s| {
11903 s.move_with(|snapshot, sel| {
11904 if sel.is_empty() {
11905 sel.end = DisplayPoint::new(sel.end.row(), snapshot.line_len(sel.end.row()))
11906 }
11907 });
11908 });
11909 let item = self.cut_common(window, cx);
11910 cx.set_global(KillRing(item))
11911 }
11912
11913 pub fn kill_ring_yank(
11914 &mut self,
11915 _: &KillRingYank,
11916 window: &mut Window,
11917 cx: &mut Context<Self>,
11918 ) {
11919 self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
11920 let (text, metadata) = if let Some(KillRing(item)) = cx.try_global() {
11921 if let Some(ClipboardEntry::String(kill_ring)) = item.entries().first() {
11922 (kill_ring.text().to_string(), kill_ring.metadata_json())
11923 } else {
11924 return;
11925 }
11926 } else {
11927 return;
11928 };
11929 self.do_paste(&text, metadata, false, window, cx);
11930 }
11931
11932 pub fn copy_and_trim(&mut self, _: &CopyAndTrim, _: &mut Window, cx: &mut Context<Self>) {
11933 self.do_copy(true, cx);
11934 }
11935
11936 pub fn copy(&mut self, _: &Copy, _: &mut Window, cx: &mut Context<Self>) {
11937 self.do_copy(false, cx);
11938 }
11939
11940 fn do_copy(&self, strip_leading_indents: bool, cx: &mut Context<Self>) {
11941 let selections = self.selections.all::<Point>(cx);
11942 let buffer = self.buffer.read(cx).read(cx);
11943 let mut text = String::new();
11944
11945 let mut clipboard_selections = Vec::with_capacity(selections.len());
11946 {
11947 let max_point = buffer.max_point();
11948 let mut is_first = true;
11949 for selection in &selections {
11950 let mut start = selection.start;
11951 let mut end = selection.end;
11952 let is_entire_line = selection.is_empty() || self.selections.line_mode;
11953 if is_entire_line {
11954 start = Point::new(start.row, 0);
11955 end = cmp::min(max_point, Point::new(end.row + 1, 0));
11956 }
11957
11958 let mut trimmed_selections = Vec::new();
11959 if strip_leading_indents && end.row.saturating_sub(start.row) > 0 {
11960 let row = MultiBufferRow(start.row);
11961 let first_indent = buffer.indent_size_for_line(row);
11962 if first_indent.len == 0 || start.column > first_indent.len {
11963 trimmed_selections.push(start..end);
11964 } else {
11965 trimmed_selections.push(
11966 Point::new(row.0, first_indent.len)
11967 ..Point::new(row.0, buffer.line_len(row)),
11968 );
11969 for row in start.row + 1..=end.row {
11970 let mut line_len = buffer.line_len(MultiBufferRow(row));
11971 if row == end.row {
11972 line_len = end.column;
11973 }
11974 if line_len == 0 {
11975 trimmed_selections
11976 .push(Point::new(row, 0)..Point::new(row, line_len));
11977 continue;
11978 }
11979 let row_indent_size = buffer.indent_size_for_line(MultiBufferRow(row));
11980 if row_indent_size.len >= first_indent.len {
11981 trimmed_selections.push(
11982 Point::new(row, first_indent.len)..Point::new(row, line_len),
11983 );
11984 } else {
11985 trimmed_selections.clear();
11986 trimmed_selections.push(start..end);
11987 break;
11988 }
11989 }
11990 }
11991 } else {
11992 trimmed_selections.push(start..end);
11993 }
11994
11995 for trimmed_range in trimmed_selections {
11996 if is_first {
11997 is_first = false;
11998 } else {
11999 text += "\n";
12000 }
12001 let mut len = 0;
12002 for chunk in buffer.text_for_range(trimmed_range.start..trimmed_range.end) {
12003 text.push_str(chunk);
12004 len += chunk.len();
12005 }
12006 clipboard_selections.push(ClipboardSelection {
12007 len,
12008 is_entire_line,
12009 first_line_indent: buffer
12010 .indent_size_for_line(MultiBufferRow(trimmed_range.start.row))
12011 .len,
12012 });
12013 }
12014 }
12015 }
12016
12017 cx.write_to_clipboard(ClipboardItem::new_string_with_json_metadata(
12018 text,
12019 clipboard_selections,
12020 ));
12021 }
12022
12023 pub fn do_paste(
12024 &mut self,
12025 text: &String,
12026 clipboard_selections: Option<Vec<ClipboardSelection>>,
12027 handle_entire_lines: bool,
12028 window: &mut Window,
12029 cx: &mut Context<Self>,
12030 ) {
12031 if self.read_only(cx) {
12032 return;
12033 }
12034
12035 let clipboard_text = Cow::Borrowed(text);
12036
12037 self.transact(window, cx, |this, window, cx| {
12038 if let Some(mut clipboard_selections) = clipboard_selections {
12039 let old_selections = this.selections.all::<usize>(cx);
12040 let all_selections_were_entire_line =
12041 clipboard_selections.iter().all(|s| s.is_entire_line);
12042 let first_selection_indent_column =
12043 clipboard_selections.first().map(|s| s.first_line_indent);
12044 if clipboard_selections.len() != old_selections.len() {
12045 clipboard_selections.drain(..);
12046 }
12047 let cursor_offset = this.selections.last::<usize>(cx).head();
12048 let mut auto_indent_on_paste = true;
12049
12050 this.buffer.update(cx, |buffer, cx| {
12051 let snapshot = buffer.read(cx);
12052 auto_indent_on_paste = snapshot
12053 .language_settings_at(cursor_offset, cx)
12054 .auto_indent_on_paste;
12055
12056 let mut start_offset = 0;
12057 let mut edits = Vec::new();
12058 let mut original_indent_columns = Vec::new();
12059 for (ix, selection) in old_selections.iter().enumerate() {
12060 let to_insert;
12061 let entire_line;
12062 let original_indent_column;
12063 if let Some(clipboard_selection) = clipboard_selections.get(ix) {
12064 let end_offset = start_offset + clipboard_selection.len;
12065 to_insert = &clipboard_text[start_offset..end_offset];
12066 entire_line = clipboard_selection.is_entire_line;
12067 start_offset = end_offset + 1;
12068 original_indent_column = Some(clipboard_selection.first_line_indent);
12069 } else {
12070 to_insert = clipboard_text.as_str();
12071 entire_line = all_selections_were_entire_line;
12072 original_indent_column = first_selection_indent_column
12073 }
12074
12075 // If the corresponding selection was empty when this slice of the
12076 // clipboard text was written, then the entire line containing the
12077 // selection was copied. If this selection is also currently empty,
12078 // then paste the line before the current line of the buffer.
12079 let range = if selection.is_empty() && handle_entire_lines && entire_line {
12080 let column = selection.start.to_point(&snapshot).column as usize;
12081 let line_start = selection.start - column;
12082 line_start..line_start
12083 } else {
12084 selection.range()
12085 };
12086
12087 edits.push((range, to_insert));
12088 original_indent_columns.push(original_indent_column);
12089 }
12090 drop(snapshot);
12091
12092 buffer.edit(
12093 edits,
12094 if auto_indent_on_paste {
12095 Some(AutoindentMode::Block {
12096 original_indent_columns,
12097 })
12098 } else {
12099 None
12100 },
12101 cx,
12102 );
12103 });
12104
12105 let selections = this.selections.all::<usize>(cx);
12106 this.change_selections(Default::default(), window, cx, |s| s.select(selections));
12107 } else {
12108 this.insert(&clipboard_text, window, cx);
12109 }
12110 });
12111 }
12112
12113 pub fn paste(&mut self, _: &Paste, window: &mut Window, cx: &mut Context<Self>) {
12114 self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
12115 if let Some(item) = cx.read_from_clipboard() {
12116 let entries = item.entries();
12117
12118 match entries.first() {
12119 // For now, we only support applying metadata if there's one string. In the future, we can incorporate all the selections
12120 // of all the pasted entries.
12121 Some(ClipboardEntry::String(clipboard_string)) if entries.len() == 1 => self
12122 .do_paste(
12123 clipboard_string.text(),
12124 clipboard_string.metadata_json::<Vec<ClipboardSelection>>(),
12125 true,
12126 window,
12127 cx,
12128 ),
12129 _ => self.do_paste(&item.text().unwrap_or_default(), None, true, window, cx),
12130 }
12131 }
12132 }
12133
12134 pub fn undo(&mut self, _: &Undo, window: &mut Window, cx: &mut Context<Self>) {
12135 if self.read_only(cx) {
12136 return;
12137 }
12138
12139 self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
12140
12141 if let Some(transaction_id) = self.buffer.update(cx, |buffer, cx| buffer.undo(cx)) {
12142 if let Some((selections, _)) =
12143 self.selection_history.transaction(transaction_id).cloned()
12144 {
12145 self.change_selections(SelectionEffects::no_scroll(), window, cx, |s| {
12146 s.select_anchors(selections.to_vec());
12147 });
12148 } else {
12149 log::error!(
12150 "No entry in selection_history found for undo. \
12151 This may correspond to a bug where undo does not update the selection. \
12152 If this is occurring, please add details to \
12153 https://github.com/zed-industries/zed/issues/22692"
12154 );
12155 }
12156 self.request_autoscroll(Autoscroll::fit(), cx);
12157 self.unmark_text(window, cx);
12158 self.refresh_inline_completion(true, false, window, cx);
12159 cx.emit(EditorEvent::Edited { transaction_id });
12160 cx.emit(EditorEvent::TransactionUndone { transaction_id });
12161 }
12162 }
12163
12164 pub fn redo(&mut self, _: &Redo, window: &mut Window, cx: &mut Context<Self>) {
12165 if self.read_only(cx) {
12166 return;
12167 }
12168
12169 self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
12170
12171 if let Some(transaction_id) = self.buffer.update(cx, |buffer, cx| buffer.redo(cx)) {
12172 if let Some((_, Some(selections))) =
12173 self.selection_history.transaction(transaction_id).cloned()
12174 {
12175 self.change_selections(SelectionEffects::no_scroll(), window, cx, |s| {
12176 s.select_anchors(selections.to_vec());
12177 });
12178 } else {
12179 log::error!(
12180 "No entry in selection_history found for redo. \
12181 This may correspond to a bug where undo does not update the selection. \
12182 If this is occurring, please add details to \
12183 https://github.com/zed-industries/zed/issues/22692"
12184 );
12185 }
12186 self.request_autoscroll(Autoscroll::fit(), cx);
12187 self.unmark_text(window, cx);
12188 self.refresh_inline_completion(true, false, window, cx);
12189 cx.emit(EditorEvent::Edited { transaction_id });
12190 }
12191 }
12192
12193 pub fn finalize_last_transaction(&mut self, cx: &mut Context<Self>) {
12194 self.buffer
12195 .update(cx, |buffer, cx| buffer.finalize_last_transaction(cx));
12196 }
12197
12198 pub fn group_until_transaction(&mut self, tx_id: TransactionId, cx: &mut Context<Self>) {
12199 self.buffer
12200 .update(cx, |buffer, cx| buffer.group_until_transaction(tx_id, cx));
12201 }
12202
12203 pub fn move_left(&mut self, _: &MoveLeft, window: &mut Window, cx: &mut Context<Self>) {
12204 self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
12205 self.change_selections(Default::default(), window, cx, |s| {
12206 s.move_with(|map, selection| {
12207 let cursor = if selection.is_empty() {
12208 movement::left(map, selection.start)
12209 } else {
12210 selection.start
12211 };
12212 selection.collapse_to(cursor, SelectionGoal::None);
12213 });
12214 })
12215 }
12216
12217 pub fn select_left(&mut self, _: &SelectLeft, window: &mut Window, cx: &mut Context<Self>) {
12218 self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
12219 self.change_selections(Default::default(), window, cx, |s| {
12220 s.move_heads_with(|map, head, _| (movement::left(map, head), SelectionGoal::None));
12221 })
12222 }
12223
12224 pub fn move_right(&mut self, _: &MoveRight, window: &mut Window, cx: &mut Context<Self>) {
12225 self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
12226 self.change_selections(Default::default(), window, cx, |s| {
12227 s.move_with(|map, selection| {
12228 let cursor = if selection.is_empty() {
12229 movement::right(map, selection.end)
12230 } else {
12231 selection.end
12232 };
12233 selection.collapse_to(cursor, SelectionGoal::None)
12234 });
12235 })
12236 }
12237
12238 pub fn select_right(&mut self, _: &SelectRight, window: &mut Window, cx: &mut Context<Self>) {
12239 self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
12240 self.change_selections(Default::default(), window, cx, |s| {
12241 s.move_heads_with(|map, head, _| (movement::right(map, head), SelectionGoal::None));
12242 })
12243 }
12244
12245 pub fn move_up(&mut self, _: &MoveUp, window: &mut Window, cx: &mut Context<Self>) {
12246 if self.take_rename(true, window, cx).is_some() {
12247 return;
12248 }
12249
12250 if self.mode.is_single_line() {
12251 cx.propagate();
12252 return;
12253 }
12254
12255 self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
12256
12257 let text_layout_details = &self.text_layout_details(window);
12258 let selection_count = self.selections.count();
12259 let first_selection = self.selections.first_anchor();
12260
12261 self.change_selections(Default::default(), window, cx, |s| {
12262 s.move_with(|map, selection| {
12263 if !selection.is_empty() {
12264 selection.goal = SelectionGoal::None;
12265 }
12266 let (cursor, goal) = movement::up(
12267 map,
12268 selection.start,
12269 selection.goal,
12270 false,
12271 text_layout_details,
12272 );
12273 selection.collapse_to(cursor, goal);
12274 });
12275 });
12276
12277 if selection_count == 1 && first_selection.range() == self.selections.first_anchor().range()
12278 {
12279 cx.propagate();
12280 }
12281 }
12282
12283 pub fn move_up_by_lines(
12284 &mut self,
12285 action: &MoveUpByLines,
12286 window: &mut Window,
12287 cx: &mut Context<Self>,
12288 ) {
12289 if self.take_rename(true, window, cx).is_some() {
12290 return;
12291 }
12292
12293 if self.mode.is_single_line() {
12294 cx.propagate();
12295 return;
12296 }
12297
12298 self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
12299
12300 let text_layout_details = &self.text_layout_details(window);
12301
12302 self.change_selections(Default::default(), window, cx, |s| {
12303 s.move_with(|map, selection| {
12304 if !selection.is_empty() {
12305 selection.goal = SelectionGoal::None;
12306 }
12307 let (cursor, goal) = movement::up_by_rows(
12308 map,
12309 selection.start,
12310 action.lines,
12311 selection.goal,
12312 false,
12313 text_layout_details,
12314 );
12315 selection.collapse_to(cursor, goal);
12316 });
12317 })
12318 }
12319
12320 pub fn move_down_by_lines(
12321 &mut self,
12322 action: &MoveDownByLines,
12323 window: &mut Window,
12324 cx: &mut Context<Self>,
12325 ) {
12326 if self.take_rename(true, window, cx).is_some() {
12327 return;
12328 }
12329
12330 if self.mode.is_single_line() {
12331 cx.propagate();
12332 return;
12333 }
12334
12335 self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
12336
12337 let text_layout_details = &self.text_layout_details(window);
12338
12339 self.change_selections(Default::default(), window, cx, |s| {
12340 s.move_with(|map, selection| {
12341 if !selection.is_empty() {
12342 selection.goal = SelectionGoal::None;
12343 }
12344 let (cursor, goal) = movement::down_by_rows(
12345 map,
12346 selection.start,
12347 action.lines,
12348 selection.goal,
12349 false,
12350 text_layout_details,
12351 );
12352 selection.collapse_to(cursor, goal);
12353 });
12354 })
12355 }
12356
12357 pub fn select_down_by_lines(
12358 &mut self,
12359 action: &SelectDownByLines,
12360 window: &mut Window,
12361 cx: &mut Context<Self>,
12362 ) {
12363 self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
12364 let text_layout_details = &self.text_layout_details(window);
12365 self.change_selections(Default::default(), window, cx, |s| {
12366 s.move_heads_with(|map, head, goal| {
12367 movement::down_by_rows(map, head, action.lines, goal, false, text_layout_details)
12368 })
12369 })
12370 }
12371
12372 pub fn select_up_by_lines(
12373 &mut self,
12374 action: &SelectUpByLines,
12375 window: &mut Window,
12376 cx: &mut Context<Self>,
12377 ) {
12378 self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
12379 let text_layout_details = &self.text_layout_details(window);
12380 self.change_selections(Default::default(), window, cx, |s| {
12381 s.move_heads_with(|map, head, goal| {
12382 movement::up_by_rows(map, head, action.lines, goal, false, text_layout_details)
12383 })
12384 })
12385 }
12386
12387 pub fn select_page_up(
12388 &mut self,
12389 _: &SelectPageUp,
12390 window: &mut Window,
12391 cx: &mut Context<Self>,
12392 ) {
12393 let Some(row_count) = self.visible_row_count() else {
12394 return;
12395 };
12396
12397 self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
12398
12399 let text_layout_details = &self.text_layout_details(window);
12400
12401 self.change_selections(Default::default(), window, cx, |s| {
12402 s.move_heads_with(|map, head, goal| {
12403 movement::up_by_rows(map, head, row_count, goal, false, text_layout_details)
12404 })
12405 })
12406 }
12407
12408 pub fn move_page_up(
12409 &mut self,
12410 action: &MovePageUp,
12411 window: &mut Window,
12412 cx: &mut Context<Self>,
12413 ) {
12414 if self.take_rename(true, window, cx).is_some() {
12415 return;
12416 }
12417
12418 if self
12419 .context_menu
12420 .borrow_mut()
12421 .as_mut()
12422 .map(|menu| menu.select_first(self.completion_provider.as_deref(), window, cx))
12423 .unwrap_or(false)
12424 {
12425 return;
12426 }
12427
12428 if matches!(self.mode, EditorMode::SingleLine { .. }) {
12429 cx.propagate();
12430 return;
12431 }
12432
12433 let Some(row_count) = self.visible_row_count() else {
12434 return;
12435 };
12436
12437 self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
12438
12439 let effects = if action.center_cursor {
12440 SelectionEffects::scroll(Autoscroll::center())
12441 } else {
12442 SelectionEffects::default()
12443 };
12444
12445 let text_layout_details = &self.text_layout_details(window);
12446
12447 self.change_selections(effects, window, cx, |s| {
12448 s.move_with(|map, selection| {
12449 if !selection.is_empty() {
12450 selection.goal = SelectionGoal::None;
12451 }
12452 let (cursor, goal) = movement::up_by_rows(
12453 map,
12454 selection.end,
12455 row_count,
12456 selection.goal,
12457 false,
12458 text_layout_details,
12459 );
12460 selection.collapse_to(cursor, goal);
12461 });
12462 });
12463 }
12464
12465 pub fn select_up(&mut self, _: &SelectUp, window: &mut Window, cx: &mut Context<Self>) {
12466 self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
12467 let text_layout_details = &self.text_layout_details(window);
12468 self.change_selections(Default::default(), window, cx, |s| {
12469 s.move_heads_with(|map, head, goal| {
12470 movement::up(map, head, goal, false, text_layout_details)
12471 })
12472 })
12473 }
12474
12475 pub fn move_down(&mut self, _: &MoveDown, window: &mut Window, cx: &mut Context<Self>) {
12476 self.take_rename(true, window, cx);
12477
12478 if self.mode.is_single_line() {
12479 cx.propagate();
12480 return;
12481 }
12482
12483 self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
12484
12485 let text_layout_details = &self.text_layout_details(window);
12486 let selection_count = self.selections.count();
12487 let first_selection = self.selections.first_anchor();
12488
12489 self.change_selections(Default::default(), window, cx, |s| {
12490 s.move_with(|map, selection| {
12491 if !selection.is_empty() {
12492 selection.goal = SelectionGoal::None;
12493 }
12494 let (cursor, goal) = movement::down(
12495 map,
12496 selection.end,
12497 selection.goal,
12498 false,
12499 text_layout_details,
12500 );
12501 selection.collapse_to(cursor, goal);
12502 });
12503 });
12504
12505 if selection_count == 1 && first_selection.range() == self.selections.first_anchor().range()
12506 {
12507 cx.propagate();
12508 }
12509 }
12510
12511 pub fn select_page_down(
12512 &mut self,
12513 _: &SelectPageDown,
12514 window: &mut Window,
12515 cx: &mut Context<Self>,
12516 ) {
12517 let Some(row_count) = self.visible_row_count() else {
12518 return;
12519 };
12520
12521 self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
12522
12523 let text_layout_details = &self.text_layout_details(window);
12524
12525 self.change_selections(Default::default(), window, cx, |s| {
12526 s.move_heads_with(|map, head, goal| {
12527 movement::down_by_rows(map, head, row_count, goal, false, text_layout_details)
12528 })
12529 })
12530 }
12531
12532 pub fn move_page_down(
12533 &mut self,
12534 action: &MovePageDown,
12535 window: &mut Window,
12536 cx: &mut Context<Self>,
12537 ) {
12538 if self.take_rename(true, window, cx).is_some() {
12539 return;
12540 }
12541
12542 if self
12543 .context_menu
12544 .borrow_mut()
12545 .as_mut()
12546 .map(|menu| menu.select_last(self.completion_provider.as_deref(), window, cx))
12547 .unwrap_or(false)
12548 {
12549 return;
12550 }
12551
12552 if matches!(self.mode, EditorMode::SingleLine { .. }) {
12553 cx.propagate();
12554 return;
12555 }
12556
12557 let Some(row_count) = self.visible_row_count() else {
12558 return;
12559 };
12560
12561 self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
12562
12563 let effects = if action.center_cursor {
12564 SelectionEffects::scroll(Autoscroll::center())
12565 } else {
12566 SelectionEffects::default()
12567 };
12568
12569 let text_layout_details = &self.text_layout_details(window);
12570 self.change_selections(effects, window, cx, |s| {
12571 s.move_with(|map, selection| {
12572 if !selection.is_empty() {
12573 selection.goal = SelectionGoal::None;
12574 }
12575 let (cursor, goal) = movement::down_by_rows(
12576 map,
12577 selection.end,
12578 row_count,
12579 selection.goal,
12580 false,
12581 text_layout_details,
12582 );
12583 selection.collapse_to(cursor, goal);
12584 });
12585 });
12586 }
12587
12588 pub fn select_down(&mut self, _: &SelectDown, window: &mut Window, cx: &mut Context<Self>) {
12589 self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
12590 let text_layout_details = &self.text_layout_details(window);
12591 self.change_selections(Default::default(), window, cx, |s| {
12592 s.move_heads_with(|map, head, goal| {
12593 movement::down(map, head, goal, false, text_layout_details)
12594 })
12595 });
12596 }
12597
12598 pub fn context_menu_first(
12599 &mut self,
12600 _: &ContextMenuFirst,
12601 window: &mut Window,
12602 cx: &mut Context<Self>,
12603 ) {
12604 if let Some(context_menu) = self.context_menu.borrow_mut().as_mut() {
12605 context_menu.select_first(self.completion_provider.as_deref(), window, cx);
12606 }
12607 }
12608
12609 pub fn context_menu_prev(
12610 &mut self,
12611 _: &ContextMenuPrevious,
12612 window: &mut Window,
12613 cx: &mut Context<Self>,
12614 ) {
12615 if let Some(context_menu) = self.context_menu.borrow_mut().as_mut() {
12616 context_menu.select_prev(self.completion_provider.as_deref(), window, cx);
12617 }
12618 }
12619
12620 pub fn context_menu_next(
12621 &mut self,
12622 _: &ContextMenuNext,
12623 window: &mut Window,
12624 cx: &mut Context<Self>,
12625 ) {
12626 if let Some(context_menu) = self.context_menu.borrow_mut().as_mut() {
12627 context_menu.select_next(self.completion_provider.as_deref(), window, cx);
12628 }
12629 }
12630
12631 pub fn context_menu_last(
12632 &mut self,
12633 _: &ContextMenuLast,
12634 window: &mut Window,
12635 cx: &mut Context<Self>,
12636 ) {
12637 if let Some(context_menu) = self.context_menu.borrow_mut().as_mut() {
12638 context_menu.select_last(self.completion_provider.as_deref(), window, cx);
12639 }
12640 }
12641
12642 pub fn signature_help_prev(
12643 &mut self,
12644 _: &SignatureHelpPrevious,
12645 _: &mut Window,
12646 cx: &mut Context<Self>,
12647 ) {
12648 if let Some(popover) = self.signature_help_state.popover_mut() {
12649 if popover.current_signature == 0 {
12650 popover.current_signature = popover.signatures.len() - 1;
12651 } else {
12652 popover.current_signature -= 1;
12653 }
12654 cx.notify();
12655 }
12656 }
12657
12658 pub fn signature_help_next(
12659 &mut self,
12660 _: &SignatureHelpNext,
12661 _: &mut Window,
12662 cx: &mut Context<Self>,
12663 ) {
12664 if let Some(popover) = self.signature_help_state.popover_mut() {
12665 if popover.current_signature + 1 == popover.signatures.len() {
12666 popover.current_signature = 0;
12667 } else {
12668 popover.current_signature += 1;
12669 }
12670 cx.notify();
12671 }
12672 }
12673
12674 pub fn move_to_previous_word_start(
12675 &mut self,
12676 _: &MoveToPreviousWordStart,
12677 window: &mut Window,
12678 cx: &mut Context<Self>,
12679 ) {
12680 self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
12681 self.change_selections(Default::default(), window, cx, |s| {
12682 s.move_cursors_with(|map, head, _| {
12683 (
12684 movement::previous_word_start(map, head),
12685 SelectionGoal::None,
12686 )
12687 });
12688 })
12689 }
12690
12691 pub fn move_to_previous_subword_start(
12692 &mut self,
12693 _: &MoveToPreviousSubwordStart,
12694 window: &mut Window,
12695 cx: &mut Context<Self>,
12696 ) {
12697 self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
12698 self.change_selections(Default::default(), window, cx, |s| {
12699 s.move_cursors_with(|map, head, _| {
12700 (
12701 movement::previous_subword_start(map, head),
12702 SelectionGoal::None,
12703 )
12704 });
12705 })
12706 }
12707
12708 pub fn select_to_previous_word_start(
12709 &mut self,
12710 _: &SelectToPreviousWordStart,
12711 window: &mut Window,
12712 cx: &mut Context<Self>,
12713 ) {
12714 self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
12715 self.change_selections(Default::default(), window, cx, |s| {
12716 s.move_heads_with(|map, head, _| {
12717 (
12718 movement::previous_word_start(map, head),
12719 SelectionGoal::None,
12720 )
12721 });
12722 })
12723 }
12724
12725 pub fn select_to_previous_subword_start(
12726 &mut self,
12727 _: &SelectToPreviousSubwordStart,
12728 window: &mut Window,
12729 cx: &mut Context<Self>,
12730 ) {
12731 self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
12732 self.change_selections(Default::default(), window, cx, |s| {
12733 s.move_heads_with(|map, head, _| {
12734 (
12735 movement::previous_subword_start(map, head),
12736 SelectionGoal::None,
12737 )
12738 });
12739 })
12740 }
12741
12742 pub fn delete_to_previous_word_start(
12743 &mut self,
12744 action: &DeleteToPreviousWordStart,
12745 window: &mut Window,
12746 cx: &mut Context<Self>,
12747 ) {
12748 self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
12749 self.transact(window, cx, |this, window, cx| {
12750 this.select_autoclose_pair(window, cx);
12751 this.change_selections(Default::default(), window, cx, |s| {
12752 s.move_with(|map, selection| {
12753 if selection.is_empty() {
12754 let cursor = if action.ignore_newlines {
12755 movement::previous_word_start(map, selection.head())
12756 } else {
12757 movement::previous_word_start_or_newline(map, selection.head())
12758 };
12759 selection.set_head(cursor, SelectionGoal::None);
12760 }
12761 });
12762 });
12763 this.insert("", window, cx);
12764 });
12765 }
12766
12767 pub fn delete_to_previous_subword_start(
12768 &mut self,
12769 _: &DeleteToPreviousSubwordStart,
12770 window: &mut Window,
12771 cx: &mut Context<Self>,
12772 ) {
12773 self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
12774 self.transact(window, cx, |this, window, cx| {
12775 this.select_autoclose_pair(window, cx);
12776 this.change_selections(Default::default(), window, cx, |s| {
12777 s.move_with(|map, selection| {
12778 if selection.is_empty() {
12779 let cursor = movement::previous_subword_start(map, selection.head());
12780 selection.set_head(cursor, SelectionGoal::None);
12781 }
12782 });
12783 });
12784 this.insert("", window, cx);
12785 });
12786 }
12787
12788 pub fn move_to_next_word_end(
12789 &mut self,
12790 _: &MoveToNextWordEnd,
12791 window: &mut Window,
12792 cx: &mut Context<Self>,
12793 ) {
12794 self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
12795 self.change_selections(Default::default(), window, cx, |s| {
12796 s.move_cursors_with(|map, head, _| {
12797 (movement::next_word_end(map, head), SelectionGoal::None)
12798 });
12799 })
12800 }
12801
12802 pub fn move_to_next_subword_end(
12803 &mut self,
12804 _: &MoveToNextSubwordEnd,
12805 window: &mut Window,
12806 cx: &mut Context<Self>,
12807 ) {
12808 self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
12809 self.change_selections(Default::default(), window, cx, |s| {
12810 s.move_cursors_with(|map, head, _| {
12811 (movement::next_subword_end(map, head), SelectionGoal::None)
12812 });
12813 })
12814 }
12815
12816 pub fn select_to_next_word_end(
12817 &mut self,
12818 _: &SelectToNextWordEnd,
12819 window: &mut Window,
12820 cx: &mut Context<Self>,
12821 ) {
12822 self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
12823 self.change_selections(Default::default(), window, cx, |s| {
12824 s.move_heads_with(|map, head, _| {
12825 (movement::next_word_end(map, head), SelectionGoal::None)
12826 });
12827 })
12828 }
12829
12830 pub fn select_to_next_subword_end(
12831 &mut self,
12832 _: &SelectToNextSubwordEnd,
12833 window: &mut Window,
12834 cx: &mut Context<Self>,
12835 ) {
12836 self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
12837 self.change_selections(Default::default(), window, cx, |s| {
12838 s.move_heads_with(|map, head, _| {
12839 (movement::next_subword_end(map, head), SelectionGoal::None)
12840 });
12841 })
12842 }
12843
12844 pub fn delete_to_next_word_end(
12845 &mut self,
12846 action: &DeleteToNextWordEnd,
12847 window: &mut Window,
12848 cx: &mut Context<Self>,
12849 ) {
12850 self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
12851 self.transact(window, cx, |this, window, cx| {
12852 this.change_selections(Default::default(), window, cx, |s| {
12853 s.move_with(|map, selection| {
12854 if selection.is_empty() {
12855 let cursor = if action.ignore_newlines {
12856 movement::next_word_end(map, selection.head())
12857 } else {
12858 movement::next_word_end_or_newline(map, selection.head())
12859 };
12860 selection.set_head(cursor, SelectionGoal::None);
12861 }
12862 });
12863 });
12864 this.insert("", window, cx);
12865 });
12866 }
12867
12868 pub fn delete_to_next_subword_end(
12869 &mut self,
12870 _: &DeleteToNextSubwordEnd,
12871 window: &mut Window,
12872 cx: &mut Context<Self>,
12873 ) {
12874 self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
12875 self.transact(window, cx, |this, window, cx| {
12876 this.change_selections(Default::default(), window, cx, |s| {
12877 s.move_with(|map, selection| {
12878 if selection.is_empty() {
12879 let cursor = movement::next_subword_end(map, selection.head());
12880 selection.set_head(cursor, SelectionGoal::None);
12881 }
12882 });
12883 });
12884 this.insert("", window, cx);
12885 });
12886 }
12887
12888 pub fn move_to_beginning_of_line(
12889 &mut self,
12890 action: &MoveToBeginningOfLine,
12891 window: &mut Window,
12892 cx: &mut Context<Self>,
12893 ) {
12894 self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
12895 self.change_selections(Default::default(), window, cx, |s| {
12896 s.move_cursors_with(|map, head, _| {
12897 (
12898 movement::indented_line_beginning(
12899 map,
12900 head,
12901 action.stop_at_soft_wraps,
12902 action.stop_at_indent,
12903 ),
12904 SelectionGoal::None,
12905 )
12906 });
12907 })
12908 }
12909
12910 pub fn select_to_beginning_of_line(
12911 &mut self,
12912 action: &SelectToBeginningOfLine,
12913 window: &mut Window,
12914 cx: &mut Context<Self>,
12915 ) {
12916 self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
12917 self.change_selections(Default::default(), window, cx, |s| {
12918 s.move_heads_with(|map, head, _| {
12919 (
12920 movement::indented_line_beginning(
12921 map,
12922 head,
12923 action.stop_at_soft_wraps,
12924 action.stop_at_indent,
12925 ),
12926 SelectionGoal::None,
12927 )
12928 });
12929 });
12930 }
12931
12932 pub fn delete_to_beginning_of_line(
12933 &mut self,
12934 action: &DeleteToBeginningOfLine,
12935 window: &mut Window,
12936 cx: &mut Context<Self>,
12937 ) {
12938 self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
12939 self.transact(window, cx, |this, window, cx| {
12940 this.change_selections(Default::default(), window, cx, |s| {
12941 s.move_with(|_, selection| {
12942 selection.reversed = true;
12943 });
12944 });
12945
12946 this.select_to_beginning_of_line(
12947 &SelectToBeginningOfLine {
12948 stop_at_soft_wraps: false,
12949 stop_at_indent: action.stop_at_indent,
12950 },
12951 window,
12952 cx,
12953 );
12954 this.backspace(&Backspace, window, cx);
12955 });
12956 }
12957
12958 pub fn move_to_end_of_line(
12959 &mut self,
12960 action: &MoveToEndOfLine,
12961 window: &mut Window,
12962 cx: &mut Context<Self>,
12963 ) {
12964 self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
12965 self.change_selections(Default::default(), window, cx, |s| {
12966 s.move_cursors_with(|map, head, _| {
12967 (
12968 movement::line_end(map, head, action.stop_at_soft_wraps),
12969 SelectionGoal::None,
12970 )
12971 });
12972 })
12973 }
12974
12975 pub fn select_to_end_of_line(
12976 &mut self,
12977 action: &SelectToEndOfLine,
12978 window: &mut Window,
12979 cx: &mut Context<Self>,
12980 ) {
12981 self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
12982 self.change_selections(Default::default(), window, cx, |s| {
12983 s.move_heads_with(|map, head, _| {
12984 (
12985 movement::line_end(map, head, action.stop_at_soft_wraps),
12986 SelectionGoal::None,
12987 )
12988 });
12989 })
12990 }
12991
12992 pub fn delete_to_end_of_line(
12993 &mut self,
12994 _: &DeleteToEndOfLine,
12995 window: &mut Window,
12996 cx: &mut Context<Self>,
12997 ) {
12998 self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
12999 self.transact(window, cx, |this, window, cx| {
13000 this.select_to_end_of_line(
13001 &SelectToEndOfLine {
13002 stop_at_soft_wraps: false,
13003 },
13004 window,
13005 cx,
13006 );
13007 this.delete(&Delete, window, cx);
13008 });
13009 }
13010
13011 pub fn cut_to_end_of_line(
13012 &mut self,
13013 _: &CutToEndOfLine,
13014 window: &mut Window,
13015 cx: &mut Context<Self>,
13016 ) {
13017 self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
13018 self.transact(window, cx, |this, window, cx| {
13019 this.select_to_end_of_line(
13020 &SelectToEndOfLine {
13021 stop_at_soft_wraps: false,
13022 },
13023 window,
13024 cx,
13025 );
13026 this.cut(&Cut, window, cx);
13027 });
13028 }
13029
13030 pub fn move_to_start_of_paragraph(
13031 &mut self,
13032 _: &MoveToStartOfParagraph,
13033 window: &mut Window,
13034 cx: &mut Context<Self>,
13035 ) {
13036 if matches!(self.mode, EditorMode::SingleLine { .. }) {
13037 cx.propagate();
13038 return;
13039 }
13040 self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
13041 self.change_selections(Default::default(), window, cx, |s| {
13042 s.move_with(|map, selection| {
13043 selection.collapse_to(
13044 movement::start_of_paragraph(map, selection.head(), 1),
13045 SelectionGoal::None,
13046 )
13047 });
13048 })
13049 }
13050
13051 pub fn move_to_end_of_paragraph(
13052 &mut self,
13053 _: &MoveToEndOfParagraph,
13054 window: &mut Window,
13055 cx: &mut Context<Self>,
13056 ) {
13057 if matches!(self.mode, EditorMode::SingleLine { .. }) {
13058 cx.propagate();
13059 return;
13060 }
13061 self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
13062 self.change_selections(Default::default(), window, cx, |s| {
13063 s.move_with(|map, selection| {
13064 selection.collapse_to(
13065 movement::end_of_paragraph(map, selection.head(), 1),
13066 SelectionGoal::None,
13067 )
13068 });
13069 })
13070 }
13071
13072 pub fn select_to_start_of_paragraph(
13073 &mut self,
13074 _: &SelectToStartOfParagraph,
13075 window: &mut Window,
13076 cx: &mut Context<Self>,
13077 ) {
13078 if matches!(self.mode, EditorMode::SingleLine { .. }) {
13079 cx.propagate();
13080 return;
13081 }
13082 self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
13083 self.change_selections(Default::default(), window, cx, |s| {
13084 s.move_heads_with(|map, head, _| {
13085 (
13086 movement::start_of_paragraph(map, head, 1),
13087 SelectionGoal::None,
13088 )
13089 });
13090 })
13091 }
13092
13093 pub fn select_to_end_of_paragraph(
13094 &mut self,
13095 _: &SelectToEndOfParagraph,
13096 window: &mut Window,
13097 cx: &mut Context<Self>,
13098 ) {
13099 if matches!(self.mode, EditorMode::SingleLine { .. }) {
13100 cx.propagate();
13101 return;
13102 }
13103 self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
13104 self.change_selections(Default::default(), window, cx, |s| {
13105 s.move_heads_with(|map, head, _| {
13106 (
13107 movement::end_of_paragraph(map, head, 1),
13108 SelectionGoal::None,
13109 )
13110 });
13111 })
13112 }
13113
13114 pub fn move_to_start_of_excerpt(
13115 &mut self,
13116 _: &MoveToStartOfExcerpt,
13117 window: &mut Window,
13118 cx: &mut Context<Self>,
13119 ) {
13120 if matches!(self.mode, EditorMode::SingleLine { .. }) {
13121 cx.propagate();
13122 return;
13123 }
13124 self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
13125 self.change_selections(Default::default(), window, cx, |s| {
13126 s.move_with(|map, selection| {
13127 selection.collapse_to(
13128 movement::start_of_excerpt(
13129 map,
13130 selection.head(),
13131 workspace::searchable::Direction::Prev,
13132 ),
13133 SelectionGoal::None,
13134 )
13135 });
13136 })
13137 }
13138
13139 pub fn move_to_start_of_next_excerpt(
13140 &mut self,
13141 _: &MoveToStartOfNextExcerpt,
13142 window: &mut Window,
13143 cx: &mut Context<Self>,
13144 ) {
13145 if matches!(self.mode, EditorMode::SingleLine { .. }) {
13146 cx.propagate();
13147 return;
13148 }
13149
13150 self.change_selections(Default::default(), window, cx, |s| {
13151 s.move_with(|map, selection| {
13152 selection.collapse_to(
13153 movement::start_of_excerpt(
13154 map,
13155 selection.head(),
13156 workspace::searchable::Direction::Next,
13157 ),
13158 SelectionGoal::None,
13159 )
13160 });
13161 })
13162 }
13163
13164 pub fn move_to_end_of_excerpt(
13165 &mut self,
13166 _: &MoveToEndOfExcerpt,
13167 window: &mut Window,
13168 cx: &mut Context<Self>,
13169 ) {
13170 if matches!(self.mode, EditorMode::SingleLine { .. }) {
13171 cx.propagate();
13172 return;
13173 }
13174 self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
13175 self.change_selections(Default::default(), window, cx, |s| {
13176 s.move_with(|map, selection| {
13177 selection.collapse_to(
13178 movement::end_of_excerpt(
13179 map,
13180 selection.head(),
13181 workspace::searchable::Direction::Next,
13182 ),
13183 SelectionGoal::None,
13184 )
13185 });
13186 })
13187 }
13188
13189 pub fn move_to_end_of_previous_excerpt(
13190 &mut self,
13191 _: &MoveToEndOfPreviousExcerpt,
13192 window: &mut Window,
13193 cx: &mut Context<Self>,
13194 ) {
13195 if matches!(self.mode, EditorMode::SingleLine { .. }) {
13196 cx.propagate();
13197 return;
13198 }
13199 self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
13200 self.change_selections(Default::default(), window, cx, |s| {
13201 s.move_with(|map, selection| {
13202 selection.collapse_to(
13203 movement::end_of_excerpt(
13204 map,
13205 selection.head(),
13206 workspace::searchable::Direction::Prev,
13207 ),
13208 SelectionGoal::None,
13209 )
13210 });
13211 })
13212 }
13213
13214 pub fn select_to_start_of_excerpt(
13215 &mut self,
13216 _: &SelectToStartOfExcerpt,
13217 window: &mut Window,
13218 cx: &mut Context<Self>,
13219 ) {
13220 if matches!(self.mode, EditorMode::SingleLine { .. }) {
13221 cx.propagate();
13222 return;
13223 }
13224 self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
13225 self.change_selections(Default::default(), window, cx, |s| {
13226 s.move_heads_with(|map, head, _| {
13227 (
13228 movement::start_of_excerpt(map, head, workspace::searchable::Direction::Prev),
13229 SelectionGoal::None,
13230 )
13231 });
13232 })
13233 }
13234
13235 pub fn select_to_start_of_next_excerpt(
13236 &mut self,
13237 _: &SelectToStartOfNextExcerpt,
13238 window: &mut Window,
13239 cx: &mut Context<Self>,
13240 ) {
13241 if matches!(self.mode, EditorMode::SingleLine { .. }) {
13242 cx.propagate();
13243 return;
13244 }
13245 self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
13246 self.change_selections(Default::default(), window, cx, |s| {
13247 s.move_heads_with(|map, head, _| {
13248 (
13249 movement::start_of_excerpt(map, head, workspace::searchable::Direction::Next),
13250 SelectionGoal::None,
13251 )
13252 });
13253 })
13254 }
13255
13256 pub fn select_to_end_of_excerpt(
13257 &mut self,
13258 _: &SelectToEndOfExcerpt,
13259 window: &mut Window,
13260 cx: &mut Context<Self>,
13261 ) {
13262 if matches!(self.mode, EditorMode::SingleLine { .. }) {
13263 cx.propagate();
13264 return;
13265 }
13266 self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
13267 self.change_selections(Default::default(), window, cx, |s| {
13268 s.move_heads_with(|map, head, _| {
13269 (
13270 movement::end_of_excerpt(map, head, workspace::searchable::Direction::Next),
13271 SelectionGoal::None,
13272 )
13273 });
13274 })
13275 }
13276
13277 pub fn select_to_end_of_previous_excerpt(
13278 &mut self,
13279 _: &SelectToEndOfPreviousExcerpt,
13280 window: &mut Window,
13281 cx: &mut Context<Self>,
13282 ) {
13283 if matches!(self.mode, EditorMode::SingleLine { .. }) {
13284 cx.propagate();
13285 return;
13286 }
13287 self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
13288 self.change_selections(Default::default(), window, cx, |s| {
13289 s.move_heads_with(|map, head, _| {
13290 (
13291 movement::end_of_excerpt(map, head, workspace::searchable::Direction::Prev),
13292 SelectionGoal::None,
13293 )
13294 });
13295 })
13296 }
13297
13298 pub fn move_to_beginning(
13299 &mut self,
13300 _: &MoveToBeginning,
13301 window: &mut Window,
13302 cx: &mut Context<Self>,
13303 ) {
13304 if matches!(self.mode, EditorMode::SingleLine { .. }) {
13305 cx.propagate();
13306 return;
13307 }
13308 self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
13309 self.change_selections(Default::default(), window, cx, |s| {
13310 s.select_ranges(vec![0..0]);
13311 });
13312 }
13313
13314 pub fn select_to_beginning(
13315 &mut self,
13316 _: &SelectToBeginning,
13317 window: &mut Window,
13318 cx: &mut Context<Self>,
13319 ) {
13320 let mut selection = self.selections.last::<Point>(cx);
13321 selection.set_head(Point::zero(), SelectionGoal::None);
13322 self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
13323 self.change_selections(Default::default(), window, cx, |s| {
13324 s.select(vec![selection]);
13325 });
13326 }
13327
13328 pub fn move_to_end(&mut self, _: &MoveToEnd, window: &mut Window, cx: &mut Context<Self>) {
13329 if matches!(self.mode, EditorMode::SingleLine { .. }) {
13330 cx.propagate();
13331 return;
13332 }
13333 self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
13334 let cursor = self.buffer.read(cx).read(cx).len();
13335 self.change_selections(Default::default(), window, cx, |s| {
13336 s.select_ranges(vec![cursor..cursor])
13337 });
13338 }
13339
13340 pub fn set_nav_history(&mut self, nav_history: Option<ItemNavHistory>) {
13341 self.nav_history = nav_history;
13342 }
13343
13344 pub fn nav_history(&self) -> Option<&ItemNavHistory> {
13345 self.nav_history.as_ref()
13346 }
13347
13348 pub fn create_nav_history_entry(&mut self, cx: &mut Context<Self>) {
13349 self.push_to_nav_history(
13350 self.selections.newest_anchor().head(),
13351 None,
13352 false,
13353 true,
13354 cx,
13355 );
13356 }
13357
13358 fn push_to_nav_history(
13359 &mut self,
13360 cursor_anchor: Anchor,
13361 new_position: Option<Point>,
13362 is_deactivate: bool,
13363 always: bool,
13364 cx: &mut Context<Self>,
13365 ) {
13366 if let Some(nav_history) = self.nav_history.as_mut() {
13367 let buffer = self.buffer.read(cx).read(cx);
13368 let cursor_position = cursor_anchor.to_point(&buffer);
13369 let scroll_state = self.scroll_manager.anchor();
13370 let scroll_top_row = scroll_state.top_row(&buffer);
13371 drop(buffer);
13372
13373 if let Some(new_position) = new_position {
13374 let row_delta = (new_position.row as i64 - cursor_position.row as i64).abs();
13375 if row_delta == 0 || (row_delta < MIN_NAVIGATION_HISTORY_ROW_DELTA && !always) {
13376 return;
13377 }
13378 }
13379
13380 nav_history.push(
13381 Some(NavigationData {
13382 cursor_anchor,
13383 cursor_position,
13384 scroll_anchor: scroll_state,
13385 scroll_top_row,
13386 }),
13387 cx,
13388 );
13389 cx.emit(EditorEvent::PushedToNavHistory {
13390 anchor: cursor_anchor,
13391 is_deactivate,
13392 })
13393 }
13394 }
13395
13396 pub fn select_to_end(&mut self, _: &SelectToEnd, window: &mut Window, cx: &mut Context<Self>) {
13397 self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
13398 let buffer = self.buffer.read(cx).snapshot(cx);
13399 let mut selection = self.selections.first::<usize>(cx);
13400 selection.set_head(buffer.len(), SelectionGoal::None);
13401 self.change_selections(Default::default(), window, cx, |s| {
13402 s.select(vec![selection]);
13403 });
13404 }
13405
13406 pub fn select_all(&mut self, _: &SelectAll, window: &mut Window, cx: &mut Context<Self>) {
13407 self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
13408 let end = self.buffer.read(cx).read(cx).len();
13409 self.change_selections(SelectionEffects::no_scroll(), window, cx, |s| {
13410 s.select_ranges(vec![0..end]);
13411 });
13412 }
13413
13414 pub fn select_line(&mut self, _: &SelectLine, window: &mut Window, cx: &mut Context<Self>) {
13415 self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
13416 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
13417 let mut selections = self.selections.all::<Point>(cx);
13418 let max_point = display_map.buffer_snapshot.max_point();
13419 for selection in &mut selections {
13420 let rows = selection.spanned_rows(true, &display_map);
13421 selection.start = Point::new(rows.start.0, 0);
13422 selection.end = cmp::min(max_point, Point::new(rows.end.0, 0));
13423 selection.reversed = false;
13424 }
13425 self.change_selections(Default::default(), window, cx, |s| {
13426 s.select(selections);
13427 });
13428 }
13429
13430 pub fn split_selection_into_lines(
13431 &mut self,
13432 _: &SplitSelectionIntoLines,
13433 window: &mut Window,
13434 cx: &mut Context<Self>,
13435 ) {
13436 let selections = self
13437 .selections
13438 .all::<Point>(cx)
13439 .into_iter()
13440 .map(|selection| selection.start..selection.end)
13441 .collect::<Vec<_>>();
13442 self.unfold_ranges(&selections, true, true, cx);
13443
13444 let mut new_selection_ranges = Vec::new();
13445 {
13446 let buffer = self.buffer.read(cx).read(cx);
13447 for selection in selections {
13448 for row in selection.start.row..selection.end.row {
13449 let cursor = Point::new(row, buffer.line_len(MultiBufferRow(row)));
13450 new_selection_ranges.push(cursor..cursor);
13451 }
13452
13453 let is_multiline_selection = selection.start.row != selection.end.row;
13454 // Don't insert last one if it's a multi-line selection ending at the start of a line,
13455 // so this action feels more ergonomic when paired with other selection operations
13456 let should_skip_last = is_multiline_selection && selection.end.column == 0;
13457 if !should_skip_last {
13458 new_selection_ranges.push(selection.end..selection.end);
13459 }
13460 }
13461 }
13462 self.change_selections(Default::default(), window, cx, |s| {
13463 s.select_ranges(new_selection_ranges);
13464 });
13465 }
13466
13467 pub fn add_selection_above(
13468 &mut self,
13469 _: &AddSelectionAbove,
13470 window: &mut Window,
13471 cx: &mut Context<Self>,
13472 ) {
13473 self.add_selection(true, window, cx);
13474 }
13475
13476 pub fn add_selection_below(
13477 &mut self,
13478 _: &AddSelectionBelow,
13479 window: &mut Window,
13480 cx: &mut Context<Self>,
13481 ) {
13482 self.add_selection(false, window, cx);
13483 }
13484
13485 fn add_selection(&mut self, above: bool, window: &mut Window, cx: &mut Context<Self>) {
13486 self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
13487
13488 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
13489 let all_selections = self.selections.all::<Point>(cx);
13490 let text_layout_details = self.text_layout_details(window);
13491
13492 let (mut columnar_selections, new_selections_to_columnarize) = {
13493 if let Some(state) = self.add_selections_state.as_ref() {
13494 let columnar_selection_ids: HashSet<_> = state
13495 .groups
13496 .iter()
13497 .flat_map(|group| group.stack.iter())
13498 .copied()
13499 .collect();
13500
13501 all_selections
13502 .into_iter()
13503 .partition(|s| columnar_selection_ids.contains(&s.id))
13504 } else {
13505 (Vec::new(), all_selections)
13506 }
13507 };
13508
13509 let mut state = self
13510 .add_selections_state
13511 .take()
13512 .unwrap_or_else(|| AddSelectionsState { groups: Vec::new() });
13513
13514 for selection in new_selections_to_columnarize {
13515 let range = selection.display_range(&display_map).sorted();
13516 let start_x = display_map.x_for_display_point(range.start, &text_layout_details);
13517 let end_x = display_map.x_for_display_point(range.end, &text_layout_details);
13518 let positions = start_x.min(end_x)..start_x.max(end_x);
13519 let mut stack = Vec::new();
13520 for row in range.start.row().0..=range.end.row().0 {
13521 if let Some(selection) = self.selections.build_columnar_selection(
13522 &display_map,
13523 DisplayRow(row),
13524 &positions,
13525 selection.reversed,
13526 &text_layout_details,
13527 ) {
13528 stack.push(selection.id);
13529 columnar_selections.push(selection);
13530 }
13531 }
13532 if !stack.is_empty() {
13533 if above {
13534 stack.reverse();
13535 }
13536 state.groups.push(AddSelectionsGroup { above, stack });
13537 }
13538 }
13539
13540 let mut final_selections = Vec::new();
13541 let end_row = if above {
13542 DisplayRow(0)
13543 } else {
13544 display_map.max_point().row()
13545 };
13546
13547 let mut last_added_item_per_group = HashMap::default();
13548 for group in state.groups.iter_mut() {
13549 if let Some(last_id) = group.stack.last() {
13550 last_added_item_per_group.insert(*last_id, group);
13551 }
13552 }
13553
13554 for selection in columnar_selections {
13555 if let Some(group) = last_added_item_per_group.get_mut(&selection.id) {
13556 if above == group.above {
13557 let range = selection.display_range(&display_map).sorted();
13558 debug_assert_eq!(range.start.row(), range.end.row());
13559 let mut row = range.start.row();
13560 let positions =
13561 if let SelectionGoal::HorizontalRange { start, end } = selection.goal {
13562 px(start)..px(end)
13563 } else {
13564 let start_x =
13565 display_map.x_for_display_point(range.start, &text_layout_details);
13566 let end_x =
13567 display_map.x_for_display_point(range.end, &text_layout_details);
13568 start_x.min(end_x)..start_x.max(end_x)
13569 };
13570
13571 let mut maybe_new_selection = None;
13572 while row != end_row {
13573 if above {
13574 row.0 -= 1;
13575 } else {
13576 row.0 += 1;
13577 }
13578 if let Some(new_selection) = self.selections.build_columnar_selection(
13579 &display_map,
13580 row,
13581 &positions,
13582 selection.reversed,
13583 &text_layout_details,
13584 ) {
13585 maybe_new_selection = Some(new_selection);
13586 break;
13587 }
13588 }
13589
13590 if let Some(new_selection) = maybe_new_selection {
13591 group.stack.push(new_selection.id);
13592 if above {
13593 final_selections.push(new_selection);
13594 final_selections.push(selection);
13595 } else {
13596 final_selections.push(selection);
13597 final_selections.push(new_selection);
13598 }
13599 } else {
13600 final_selections.push(selection);
13601 }
13602 } else {
13603 group.stack.pop();
13604 }
13605 } else {
13606 final_selections.push(selection);
13607 }
13608 }
13609
13610 self.change_selections(Default::default(), window, cx, |s| {
13611 s.select(final_selections);
13612 });
13613
13614 let final_selection_ids: HashSet<_> = self
13615 .selections
13616 .all::<Point>(cx)
13617 .iter()
13618 .map(|s| s.id)
13619 .collect();
13620 state.groups.retain_mut(|group| {
13621 // selections might get merged above so we remove invalid items from stacks
13622 group.stack.retain(|id| final_selection_ids.contains(id));
13623
13624 // single selection in stack can be treated as initial state
13625 group.stack.len() > 1
13626 });
13627
13628 if !state.groups.is_empty() {
13629 self.add_selections_state = Some(state);
13630 }
13631 }
13632
13633 fn select_match_ranges(
13634 &mut self,
13635 range: Range<usize>,
13636 reversed: bool,
13637 replace_newest: bool,
13638 auto_scroll: Option<Autoscroll>,
13639 window: &mut Window,
13640 cx: &mut Context<Editor>,
13641 ) {
13642 self.unfold_ranges(
13643 std::slice::from_ref(&range),
13644 false,
13645 auto_scroll.is_some(),
13646 cx,
13647 );
13648 let effects = if let Some(scroll) = auto_scroll {
13649 SelectionEffects::scroll(scroll)
13650 } else {
13651 SelectionEffects::no_scroll()
13652 };
13653 self.change_selections(effects, window, cx, |s| {
13654 if replace_newest {
13655 s.delete(s.newest_anchor().id);
13656 }
13657 if reversed {
13658 s.insert_range(range.end..range.start);
13659 } else {
13660 s.insert_range(range);
13661 }
13662 });
13663 }
13664
13665 pub fn select_next_match_internal(
13666 &mut self,
13667 display_map: &DisplaySnapshot,
13668 replace_newest: bool,
13669 autoscroll: Option<Autoscroll>,
13670 window: &mut Window,
13671 cx: &mut Context<Self>,
13672 ) -> Result<()> {
13673 let buffer = &display_map.buffer_snapshot;
13674 let mut selections = self.selections.all::<usize>(cx);
13675 if let Some(mut select_next_state) = self.select_next_state.take() {
13676 let query = &select_next_state.query;
13677 if !select_next_state.done {
13678 let first_selection = selections.iter().min_by_key(|s| s.id).unwrap();
13679 let last_selection = selections.iter().max_by_key(|s| s.id).unwrap();
13680 let mut next_selected_range = None;
13681
13682 let bytes_after_last_selection =
13683 buffer.bytes_in_range(last_selection.end..buffer.len());
13684 let bytes_before_first_selection = buffer.bytes_in_range(0..first_selection.start);
13685 let query_matches = query
13686 .stream_find_iter(bytes_after_last_selection)
13687 .map(|result| (last_selection.end, result))
13688 .chain(
13689 query
13690 .stream_find_iter(bytes_before_first_selection)
13691 .map(|result| (0, result)),
13692 );
13693
13694 for (start_offset, query_match) in query_matches {
13695 let query_match = query_match.unwrap(); // can only fail due to I/O
13696 let offset_range =
13697 start_offset + query_match.start()..start_offset + query_match.end();
13698
13699 if !select_next_state.wordwise
13700 || (!buffer.is_inside_word(offset_range.start, false)
13701 && !buffer.is_inside_word(offset_range.end, false))
13702 {
13703 // TODO: This is n^2, because we might check all the selections
13704 if !selections
13705 .iter()
13706 .any(|selection| selection.range().overlaps(&offset_range))
13707 {
13708 next_selected_range = Some(offset_range);
13709 break;
13710 }
13711 }
13712 }
13713
13714 if let Some(next_selected_range) = next_selected_range {
13715 self.select_match_ranges(
13716 next_selected_range,
13717 last_selection.reversed,
13718 replace_newest,
13719 autoscroll,
13720 window,
13721 cx,
13722 );
13723 } else {
13724 select_next_state.done = true;
13725 }
13726 }
13727
13728 self.select_next_state = Some(select_next_state);
13729 } else {
13730 let mut only_carets = true;
13731 let mut same_text_selected = true;
13732 let mut selected_text = None;
13733
13734 let mut selections_iter = selections.iter().peekable();
13735 while let Some(selection) = selections_iter.next() {
13736 if selection.start != selection.end {
13737 only_carets = false;
13738 }
13739
13740 if same_text_selected {
13741 if selected_text.is_none() {
13742 selected_text =
13743 Some(buffer.text_for_range(selection.range()).collect::<String>());
13744 }
13745
13746 if let Some(next_selection) = selections_iter.peek() {
13747 if next_selection.range().len() == selection.range().len() {
13748 let next_selected_text = buffer
13749 .text_for_range(next_selection.range())
13750 .collect::<String>();
13751 if Some(next_selected_text) != selected_text {
13752 same_text_selected = false;
13753 selected_text = None;
13754 }
13755 } else {
13756 same_text_selected = false;
13757 selected_text = None;
13758 }
13759 }
13760 }
13761 }
13762
13763 if only_carets {
13764 for selection in &mut selections {
13765 let (word_range, _) = buffer.surrounding_word(selection.start, false);
13766 selection.start = word_range.start;
13767 selection.end = word_range.end;
13768 selection.goal = SelectionGoal::None;
13769 selection.reversed = false;
13770 self.select_match_ranges(
13771 selection.start..selection.end,
13772 selection.reversed,
13773 replace_newest,
13774 autoscroll,
13775 window,
13776 cx,
13777 );
13778 }
13779
13780 if selections.len() == 1 {
13781 let selection = selections
13782 .last()
13783 .expect("ensured that there's only one selection");
13784 let query = buffer
13785 .text_for_range(selection.start..selection.end)
13786 .collect::<String>();
13787 let is_empty = query.is_empty();
13788 let select_state = SelectNextState {
13789 query: AhoCorasick::new(&[query])?,
13790 wordwise: true,
13791 done: is_empty,
13792 };
13793 self.select_next_state = Some(select_state);
13794 } else {
13795 self.select_next_state = None;
13796 }
13797 } else if let Some(selected_text) = selected_text {
13798 self.select_next_state = Some(SelectNextState {
13799 query: AhoCorasick::new(&[selected_text])?,
13800 wordwise: false,
13801 done: false,
13802 });
13803 self.select_next_match_internal(
13804 display_map,
13805 replace_newest,
13806 autoscroll,
13807 window,
13808 cx,
13809 )?;
13810 }
13811 }
13812 Ok(())
13813 }
13814
13815 pub fn select_all_matches(
13816 &mut self,
13817 _action: &SelectAllMatches,
13818 window: &mut Window,
13819 cx: &mut Context<Self>,
13820 ) -> Result<()> {
13821 self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
13822
13823 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
13824
13825 self.select_next_match_internal(&display_map, false, None, window, cx)?;
13826 let Some(select_next_state) = self.select_next_state.as_mut() else {
13827 return Ok(());
13828 };
13829 if select_next_state.done {
13830 return Ok(());
13831 }
13832
13833 let mut new_selections = Vec::new();
13834
13835 let reversed = self.selections.oldest::<usize>(cx).reversed;
13836 let buffer = &display_map.buffer_snapshot;
13837 let query_matches = select_next_state
13838 .query
13839 .stream_find_iter(buffer.bytes_in_range(0..buffer.len()));
13840
13841 for query_match in query_matches.into_iter() {
13842 let query_match = query_match.context("query match for select all action")?; // can only fail due to I/O
13843 let offset_range = if reversed {
13844 query_match.end()..query_match.start()
13845 } else {
13846 query_match.start()..query_match.end()
13847 };
13848
13849 if !select_next_state.wordwise
13850 || (!buffer.is_inside_word(offset_range.start, false)
13851 && !buffer.is_inside_word(offset_range.end, false))
13852 {
13853 new_selections.push(offset_range.start..offset_range.end);
13854 }
13855 }
13856
13857 select_next_state.done = true;
13858
13859 if new_selections.is_empty() {
13860 log::error!("bug: new_selections is empty in select_all_matches");
13861 return Ok(());
13862 }
13863
13864 self.unfold_ranges(&new_selections.clone(), false, false, cx);
13865 self.change_selections(SelectionEffects::no_scroll(), window, cx, |selections| {
13866 selections.select_ranges(new_selections)
13867 });
13868
13869 Ok(())
13870 }
13871
13872 pub fn select_next(
13873 &mut self,
13874 action: &SelectNext,
13875 window: &mut Window,
13876 cx: &mut Context<Self>,
13877 ) -> Result<()> {
13878 self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
13879 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
13880 self.select_next_match_internal(
13881 &display_map,
13882 action.replace_newest,
13883 Some(Autoscroll::newest()),
13884 window,
13885 cx,
13886 )?;
13887 Ok(())
13888 }
13889
13890 pub fn select_previous(
13891 &mut self,
13892 action: &SelectPrevious,
13893 window: &mut Window,
13894 cx: &mut Context<Self>,
13895 ) -> Result<()> {
13896 self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
13897 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
13898 let buffer = &display_map.buffer_snapshot;
13899 let mut selections = self.selections.all::<usize>(cx);
13900 if let Some(mut select_prev_state) = self.select_prev_state.take() {
13901 let query = &select_prev_state.query;
13902 if !select_prev_state.done {
13903 let first_selection = selections.iter().min_by_key(|s| s.id).unwrap();
13904 let last_selection = selections.iter().max_by_key(|s| s.id).unwrap();
13905 let mut next_selected_range = None;
13906 // When we're iterating matches backwards, the oldest match will actually be the furthest one in the buffer.
13907 let bytes_before_last_selection =
13908 buffer.reversed_bytes_in_range(0..last_selection.start);
13909 let bytes_after_first_selection =
13910 buffer.reversed_bytes_in_range(first_selection.end..buffer.len());
13911 let query_matches = query
13912 .stream_find_iter(bytes_before_last_selection)
13913 .map(|result| (last_selection.start, result))
13914 .chain(
13915 query
13916 .stream_find_iter(bytes_after_first_selection)
13917 .map(|result| (buffer.len(), result)),
13918 );
13919 for (end_offset, query_match) in query_matches {
13920 let query_match = query_match.unwrap(); // can only fail due to I/O
13921 let offset_range =
13922 end_offset - query_match.end()..end_offset - query_match.start();
13923
13924 if !select_prev_state.wordwise
13925 || (!buffer.is_inside_word(offset_range.start, false)
13926 && !buffer.is_inside_word(offset_range.end, false))
13927 {
13928 next_selected_range = Some(offset_range);
13929 break;
13930 }
13931 }
13932
13933 if let Some(next_selected_range) = next_selected_range {
13934 self.select_match_ranges(
13935 next_selected_range,
13936 last_selection.reversed,
13937 action.replace_newest,
13938 Some(Autoscroll::newest()),
13939 window,
13940 cx,
13941 );
13942 } else {
13943 select_prev_state.done = true;
13944 }
13945 }
13946
13947 self.select_prev_state = Some(select_prev_state);
13948 } else {
13949 let mut only_carets = true;
13950 let mut same_text_selected = true;
13951 let mut selected_text = None;
13952
13953 let mut selections_iter = selections.iter().peekable();
13954 while let Some(selection) = selections_iter.next() {
13955 if selection.start != selection.end {
13956 only_carets = false;
13957 }
13958
13959 if same_text_selected {
13960 if selected_text.is_none() {
13961 selected_text =
13962 Some(buffer.text_for_range(selection.range()).collect::<String>());
13963 }
13964
13965 if let Some(next_selection) = selections_iter.peek() {
13966 if next_selection.range().len() == selection.range().len() {
13967 let next_selected_text = buffer
13968 .text_for_range(next_selection.range())
13969 .collect::<String>();
13970 if Some(next_selected_text) != selected_text {
13971 same_text_selected = false;
13972 selected_text = None;
13973 }
13974 } else {
13975 same_text_selected = false;
13976 selected_text = None;
13977 }
13978 }
13979 }
13980 }
13981
13982 if only_carets {
13983 for selection in &mut selections {
13984 let (word_range, _) = buffer.surrounding_word(selection.start, false);
13985 selection.start = word_range.start;
13986 selection.end = word_range.end;
13987 selection.goal = SelectionGoal::None;
13988 selection.reversed = false;
13989 self.select_match_ranges(
13990 selection.start..selection.end,
13991 selection.reversed,
13992 action.replace_newest,
13993 Some(Autoscroll::newest()),
13994 window,
13995 cx,
13996 );
13997 }
13998 if selections.len() == 1 {
13999 let selection = selections
14000 .last()
14001 .expect("ensured that there's only one selection");
14002 let query = buffer
14003 .text_for_range(selection.start..selection.end)
14004 .collect::<String>();
14005 let is_empty = query.is_empty();
14006 let select_state = SelectNextState {
14007 query: AhoCorasick::new(&[query.chars().rev().collect::<String>()])?,
14008 wordwise: true,
14009 done: is_empty,
14010 };
14011 self.select_prev_state = Some(select_state);
14012 } else {
14013 self.select_prev_state = None;
14014 }
14015 } else if let Some(selected_text) = selected_text {
14016 self.select_prev_state = Some(SelectNextState {
14017 query: AhoCorasick::new(&[selected_text.chars().rev().collect::<String>()])?,
14018 wordwise: false,
14019 done: false,
14020 });
14021 self.select_previous(action, window, cx)?;
14022 }
14023 }
14024 Ok(())
14025 }
14026
14027 pub fn find_next_match(
14028 &mut self,
14029 _: &FindNextMatch,
14030 window: &mut Window,
14031 cx: &mut Context<Self>,
14032 ) -> Result<()> {
14033 let selections = self.selections.disjoint_anchors();
14034 match selections.first() {
14035 Some(first) if selections.len() >= 2 => {
14036 self.change_selections(Default::default(), window, cx, |s| {
14037 s.select_ranges([first.range()]);
14038 });
14039 }
14040 _ => self.select_next(
14041 &SelectNext {
14042 replace_newest: true,
14043 },
14044 window,
14045 cx,
14046 )?,
14047 }
14048 Ok(())
14049 }
14050
14051 pub fn find_previous_match(
14052 &mut self,
14053 _: &FindPreviousMatch,
14054 window: &mut Window,
14055 cx: &mut Context<Self>,
14056 ) -> Result<()> {
14057 let selections = self.selections.disjoint_anchors();
14058 match selections.last() {
14059 Some(last) if selections.len() >= 2 => {
14060 self.change_selections(Default::default(), window, cx, |s| {
14061 s.select_ranges([last.range()]);
14062 });
14063 }
14064 _ => self.select_previous(
14065 &SelectPrevious {
14066 replace_newest: true,
14067 },
14068 window,
14069 cx,
14070 )?,
14071 }
14072 Ok(())
14073 }
14074
14075 pub fn toggle_comments(
14076 &mut self,
14077 action: &ToggleComments,
14078 window: &mut Window,
14079 cx: &mut Context<Self>,
14080 ) {
14081 if self.read_only(cx) {
14082 return;
14083 }
14084 self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
14085 let text_layout_details = &self.text_layout_details(window);
14086 self.transact(window, cx, |this, window, cx| {
14087 let mut selections = this.selections.all::<MultiBufferPoint>(cx);
14088 let mut edits = Vec::new();
14089 let mut selection_edit_ranges = Vec::new();
14090 let mut last_toggled_row = None;
14091 let snapshot = this.buffer.read(cx).read(cx);
14092 let empty_str: Arc<str> = Arc::default();
14093 let mut suffixes_inserted = Vec::new();
14094 let ignore_indent = action.ignore_indent;
14095
14096 fn comment_prefix_range(
14097 snapshot: &MultiBufferSnapshot,
14098 row: MultiBufferRow,
14099 comment_prefix: &str,
14100 comment_prefix_whitespace: &str,
14101 ignore_indent: bool,
14102 ) -> Range<Point> {
14103 let indent_size = if ignore_indent {
14104 0
14105 } else {
14106 snapshot.indent_size_for_line(row).len
14107 };
14108
14109 let start = Point::new(row.0, indent_size);
14110
14111 let mut line_bytes = snapshot
14112 .bytes_in_range(start..snapshot.max_point())
14113 .flatten()
14114 .copied();
14115
14116 // If this line currently begins with the line comment prefix, then record
14117 // the range containing the prefix.
14118 if line_bytes
14119 .by_ref()
14120 .take(comment_prefix.len())
14121 .eq(comment_prefix.bytes())
14122 {
14123 // Include any whitespace that matches the comment prefix.
14124 let matching_whitespace_len = line_bytes
14125 .zip(comment_prefix_whitespace.bytes())
14126 .take_while(|(a, b)| a == b)
14127 .count() as u32;
14128 let end = Point::new(
14129 start.row,
14130 start.column + comment_prefix.len() as u32 + matching_whitespace_len,
14131 );
14132 start..end
14133 } else {
14134 start..start
14135 }
14136 }
14137
14138 fn comment_suffix_range(
14139 snapshot: &MultiBufferSnapshot,
14140 row: MultiBufferRow,
14141 comment_suffix: &str,
14142 comment_suffix_has_leading_space: bool,
14143 ) -> Range<Point> {
14144 let end = Point::new(row.0, snapshot.line_len(row));
14145 let suffix_start_column = end.column.saturating_sub(comment_suffix.len() as u32);
14146
14147 let mut line_end_bytes = snapshot
14148 .bytes_in_range(Point::new(end.row, suffix_start_column.saturating_sub(1))..end)
14149 .flatten()
14150 .copied();
14151
14152 let leading_space_len = if suffix_start_column > 0
14153 && line_end_bytes.next() == Some(b' ')
14154 && comment_suffix_has_leading_space
14155 {
14156 1
14157 } else {
14158 0
14159 };
14160
14161 // If this line currently begins with the line comment prefix, then record
14162 // the range containing the prefix.
14163 if line_end_bytes.by_ref().eq(comment_suffix.bytes()) {
14164 let start = Point::new(end.row, suffix_start_column - leading_space_len);
14165 start..end
14166 } else {
14167 end..end
14168 }
14169 }
14170
14171 // TODO: Handle selections that cross excerpts
14172 for selection in &mut selections {
14173 let start_column = snapshot
14174 .indent_size_for_line(MultiBufferRow(selection.start.row))
14175 .len;
14176 let language = if let Some(language) =
14177 snapshot.language_scope_at(Point::new(selection.start.row, start_column))
14178 {
14179 language
14180 } else {
14181 continue;
14182 };
14183
14184 selection_edit_ranges.clear();
14185
14186 // If multiple selections contain a given row, avoid processing that
14187 // row more than once.
14188 let mut start_row = MultiBufferRow(selection.start.row);
14189 if last_toggled_row == Some(start_row) {
14190 start_row = start_row.next_row();
14191 }
14192 let end_row =
14193 if selection.end.row > selection.start.row && selection.end.column == 0 {
14194 MultiBufferRow(selection.end.row - 1)
14195 } else {
14196 MultiBufferRow(selection.end.row)
14197 };
14198 last_toggled_row = Some(end_row);
14199
14200 if start_row > end_row {
14201 continue;
14202 }
14203
14204 // If the language has line comments, toggle those.
14205 let mut full_comment_prefixes = language.line_comment_prefixes().to_vec();
14206
14207 // If ignore_indent is set, trim spaces from the right side of all full_comment_prefixes
14208 if ignore_indent {
14209 full_comment_prefixes = full_comment_prefixes
14210 .into_iter()
14211 .map(|s| Arc::from(s.trim_end()))
14212 .collect();
14213 }
14214
14215 if !full_comment_prefixes.is_empty() {
14216 let first_prefix = full_comment_prefixes
14217 .first()
14218 .expect("prefixes is non-empty");
14219 let prefix_trimmed_lengths = full_comment_prefixes
14220 .iter()
14221 .map(|p| p.trim_end_matches(' ').len())
14222 .collect::<SmallVec<[usize; 4]>>();
14223
14224 let mut all_selection_lines_are_comments = true;
14225
14226 for row in start_row.0..=end_row.0 {
14227 let row = MultiBufferRow(row);
14228 if start_row < end_row && snapshot.is_line_blank(row) {
14229 continue;
14230 }
14231
14232 let prefix_range = full_comment_prefixes
14233 .iter()
14234 .zip(prefix_trimmed_lengths.iter().copied())
14235 .map(|(prefix, trimmed_prefix_len)| {
14236 comment_prefix_range(
14237 snapshot.deref(),
14238 row,
14239 &prefix[..trimmed_prefix_len],
14240 &prefix[trimmed_prefix_len..],
14241 ignore_indent,
14242 )
14243 })
14244 .max_by_key(|range| range.end.column - range.start.column)
14245 .expect("prefixes is non-empty");
14246
14247 if prefix_range.is_empty() {
14248 all_selection_lines_are_comments = false;
14249 }
14250
14251 selection_edit_ranges.push(prefix_range);
14252 }
14253
14254 if all_selection_lines_are_comments {
14255 edits.extend(
14256 selection_edit_ranges
14257 .iter()
14258 .cloned()
14259 .map(|range| (range, empty_str.clone())),
14260 );
14261 } else {
14262 let min_column = selection_edit_ranges
14263 .iter()
14264 .map(|range| range.start.column)
14265 .min()
14266 .unwrap_or(0);
14267 edits.extend(selection_edit_ranges.iter().map(|range| {
14268 let position = Point::new(range.start.row, min_column);
14269 (position..position, first_prefix.clone())
14270 }));
14271 }
14272 } else if let Some((full_comment_prefix, comment_suffix)) =
14273 language.block_comment_delimiters()
14274 {
14275 let comment_prefix = full_comment_prefix.trim_end_matches(' ');
14276 let comment_prefix_whitespace = &full_comment_prefix[comment_prefix.len()..];
14277 let prefix_range = comment_prefix_range(
14278 snapshot.deref(),
14279 start_row,
14280 comment_prefix,
14281 comment_prefix_whitespace,
14282 ignore_indent,
14283 );
14284 let suffix_range = comment_suffix_range(
14285 snapshot.deref(),
14286 end_row,
14287 comment_suffix.trim_start_matches(' '),
14288 comment_suffix.starts_with(' '),
14289 );
14290
14291 if prefix_range.is_empty() || suffix_range.is_empty() {
14292 edits.push((
14293 prefix_range.start..prefix_range.start,
14294 full_comment_prefix.clone(),
14295 ));
14296 edits.push((suffix_range.end..suffix_range.end, comment_suffix.clone()));
14297 suffixes_inserted.push((end_row, comment_suffix.len()));
14298 } else {
14299 edits.push((prefix_range, empty_str.clone()));
14300 edits.push((suffix_range, empty_str.clone()));
14301 }
14302 } else {
14303 continue;
14304 }
14305 }
14306
14307 drop(snapshot);
14308 this.buffer.update(cx, |buffer, cx| {
14309 buffer.edit(edits, None, cx);
14310 });
14311
14312 // Adjust selections so that they end before any comment suffixes that
14313 // were inserted.
14314 let mut suffixes_inserted = suffixes_inserted.into_iter().peekable();
14315 let mut selections = this.selections.all::<Point>(cx);
14316 let snapshot = this.buffer.read(cx).read(cx);
14317 for selection in &mut selections {
14318 while let Some((row, suffix_len)) = suffixes_inserted.peek().copied() {
14319 match row.cmp(&MultiBufferRow(selection.end.row)) {
14320 Ordering::Less => {
14321 suffixes_inserted.next();
14322 continue;
14323 }
14324 Ordering::Greater => break,
14325 Ordering::Equal => {
14326 if selection.end.column == snapshot.line_len(row) {
14327 if selection.is_empty() {
14328 selection.start.column -= suffix_len as u32;
14329 }
14330 selection.end.column -= suffix_len as u32;
14331 }
14332 break;
14333 }
14334 }
14335 }
14336 }
14337
14338 drop(snapshot);
14339 this.change_selections(Default::default(), window, cx, |s| s.select(selections));
14340
14341 let selections = this.selections.all::<Point>(cx);
14342 let selections_on_single_row = selections.windows(2).all(|selections| {
14343 selections[0].start.row == selections[1].start.row
14344 && selections[0].end.row == selections[1].end.row
14345 && selections[0].start.row == selections[0].end.row
14346 });
14347 let selections_selecting = selections
14348 .iter()
14349 .any(|selection| selection.start != selection.end);
14350 let advance_downwards = action.advance_downwards
14351 && selections_on_single_row
14352 && !selections_selecting
14353 && !matches!(this.mode, EditorMode::SingleLine { .. });
14354
14355 if advance_downwards {
14356 let snapshot = this.buffer.read(cx).snapshot(cx);
14357
14358 this.change_selections(Default::default(), window, cx, |s| {
14359 s.move_cursors_with(|display_snapshot, display_point, _| {
14360 let mut point = display_point.to_point(display_snapshot);
14361 point.row += 1;
14362 point = snapshot.clip_point(point, Bias::Left);
14363 let display_point = point.to_display_point(display_snapshot);
14364 let goal = SelectionGoal::HorizontalPosition(
14365 display_snapshot
14366 .x_for_display_point(display_point, text_layout_details)
14367 .into(),
14368 );
14369 (display_point, goal)
14370 })
14371 });
14372 }
14373 });
14374 }
14375
14376 pub fn select_enclosing_symbol(
14377 &mut self,
14378 _: &SelectEnclosingSymbol,
14379 window: &mut Window,
14380 cx: &mut Context<Self>,
14381 ) {
14382 self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
14383
14384 let buffer = self.buffer.read(cx).snapshot(cx);
14385 let old_selections = self.selections.all::<usize>(cx).into_boxed_slice();
14386
14387 fn update_selection(
14388 selection: &Selection<usize>,
14389 buffer_snap: &MultiBufferSnapshot,
14390 ) -> Option<Selection<usize>> {
14391 let cursor = selection.head();
14392 let (_buffer_id, symbols) = buffer_snap.symbols_containing(cursor, None)?;
14393 for symbol in symbols.iter().rev() {
14394 let start = symbol.range.start.to_offset(buffer_snap);
14395 let end = symbol.range.end.to_offset(buffer_snap);
14396 let new_range = start..end;
14397 if start < selection.start || end > selection.end {
14398 return Some(Selection {
14399 id: selection.id,
14400 start: new_range.start,
14401 end: new_range.end,
14402 goal: SelectionGoal::None,
14403 reversed: selection.reversed,
14404 });
14405 }
14406 }
14407 None
14408 }
14409
14410 let mut selected_larger_symbol = false;
14411 let new_selections = old_selections
14412 .iter()
14413 .map(|selection| match update_selection(selection, &buffer) {
14414 Some(new_selection) => {
14415 if new_selection.range() != selection.range() {
14416 selected_larger_symbol = true;
14417 }
14418 new_selection
14419 }
14420 None => selection.clone(),
14421 })
14422 .collect::<Vec<_>>();
14423
14424 if selected_larger_symbol {
14425 self.change_selections(Default::default(), window, cx, |s| {
14426 s.select(new_selections);
14427 });
14428 }
14429 }
14430
14431 pub fn select_larger_syntax_node(
14432 &mut self,
14433 _: &SelectLargerSyntaxNode,
14434 window: &mut Window,
14435 cx: &mut Context<Self>,
14436 ) {
14437 let Some(visible_row_count) = self.visible_row_count() else {
14438 return;
14439 };
14440 let old_selections: Box<[_]> = self.selections.all::<usize>(cx).into();
14441 if old_selections.is_empty() {
14442 return;
14443 }
14444
14445 self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
14446
14447 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
14448 let buffer = self.buffer.read(cx).snapshot(cx);
14449
14450 let mut selected_larger_node = false;
14451 let mut new_selections = old_selections
14452 .iter()
14453 .map(|selection| {
14454 let old_range = selection.start..selection.end;
14455
14456 if let Some((node, _)) = buffer.syntax_ancestor(old_range.clone()) {
14457 // manually select word at selection
14458 if ["string_content", "inline"].contains(&node.kind()) {
14459 let (word_range, _) = buffer.surrounding_word(old_range.start, false);
14460 // ignore if word is already selected
14461 if !word_range.is_empty() && old_range != word_range {
14462 let (last_word_range, _) =
14463 buffer.surrounding_word(old_range.end, false);
14464 // only select word if start and end point belongs to same word
14465 if word_range == last_word_range {
14466 selected_larger_node = true;
14467 return Selection {
14468 id: selection.id,
14469 start: word_range.start,
14470 end: word_range.end,
14471 goal: SelectionGoal::None,
14472 reversed: selection.reversed,
14473 };
14474 }
14475 }
14476 }
14477 }
14478
14479 let mut new_range = old_range.clone();
14480 while let Some((_node, containing_range)) =
14481 buffer.syntax_ancestor(new_range.clone())
14482 {
14483 new_range = match containing_range {
14484 MultiOrSingleBufferOffsetRange::Single(_) => break,
14485 MultiOrSingleBufferOffsetRange::Multi(range) => range,
14486 };
14487 if !display_map.intersects_fold(new_range.start)
14488 && !display_map.intersects_fold(new_range.end)
14489 {
14490 break;
14491 }
14492 }
14493
14494 selected_larger_node |= new_range != old_range;
14495 Selection {
14496 id: selection.id,
14497 start: new_range.start,
14498 end: new_range.end,
14499 goal: SelectionGoal::None,
14500 reversed: selection.reversed,
14501 }
14502 })
14503 .collect::<Vec<_>>();
14504
14505 if !selected_larger_node {
14506 return; // don't put this call in the history
14507 }
14508
14509 // scroll based on transformation done to the last selection created by the user
14510 let (last_old, last_new) = old_selections
14511 .last()
14512 .zip(new_selections.last().cloned())
14513 .expect("old_selections isn't empty");
14514
14515 // revert selection
14516 let is_selection_reversed = {
14517 let should_newest_selection_be_reversed = last_old.start != last_new.start;
14518 new_selections.last_mut().expect("checked above").reversed =
14519 should_newest_selection_be_reversed;
14520 should_newest_selection_be_reversed
14521 };
14522
14523 if selected_larger_node {
14524 self.select_syntax_node_history.disable_clearing = true;
14525 self.change_selections(SelectionEffects::no_scroll(), window, cx, |s| {
14526 s.select(new_selections.clone());
14527 });
14528 self.select_syntax_node_history.disable_clearing = false;
14529 }
14530
14531 let start_row = last_new.start.to_display_point(&display_map).row().0;
14532 let end_row = last_new.end.to_display_point(&display_map).row().0;
14533 let selection_height = end_row - start_row + 1;
14534 let scroll_margin_rows = self.vertical_scroll_margin() as u32;
14535
14536 let fits_on_the_screen = visible_row_count >= selection_height + scroll_margin_rows * 2;
14537 let scroll_behavior = if fits_on_the_screen {
14538 self.request_autoscroll(Autoscroll::fit(), cx);
14539 SelectSyntaxNodeScrollBehavior::FitSelection
14540 } else if is_selection_reversed {
14541 self.scroll_cursor_top(&ScrollCursorTop, window, cx);
14542 SelectSyntaxNodeScrollBehavior::CursorTop
14543 } else {
14544 self.scroll_cursor_bottom(&ScrollCursorBottom, window, cx);
14545 SelectSyntaxNodeScrollBehavior::CursorBottom
14546 };
14547
14548 self.select_syntax_node_history.push((
14549 old_selections,
14550 scroll_behavior,
14551 is_selection_reversed,
14552 ));
14553 }
14554
14555 pub fn select_smaller_syntax_node(
14556 &mut self,
14557 _: &SelectSmallerSyntaxNode,
14558 window: &mut Window,
14559 cx: &mut Context<Self>,
14560 ) {
14561 self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
14562
14563 if let Some((mut selections, scroll_behavior, is_selection_reversed)) =
14564 self.select_syntax_node_history.pop()
14565 {
14566 if let Some(selection) = selections.last_mut() {
14567 selection.reversed = is_selection_reversed;
14568 }
14569
14570 self.select_syntax_node_history.disable_clearing = true;
14571 self.change_selections(SelectionEffects::no_scroll(), window, cx, |s| {
14572 s.select(selections.to_vec());
14573 });
14574 self.select_syntax_node_history.disable_clearing = false;
14575
14576 match scroll_behavior {
14577 SelectSyntaxNodeScrollBehavior::CursorTop => {
14578 self.scroll_cursor_top(&ScrollCursorTop, window, cx);
14579 }
14580 SelectSyntaxNodeScrollBehavior::FitSelection => {
14581 self.request_autoscroll(Autoscroll::fit(), cx);
14582 }
14583 SelectSyntaxNodeScrollBehavior::CursorBottom => {
14584 self.scroll_cursor_bottom(&ScrollCursorBottom, window, cx);
14585 }
14586 }
14587 }
14588 }
14589
14590 fn refresh_runnables(&mut self, window: &mut Window, cx: &mut Context<Self>) -> Task<()> {
14591 if !EditorSettings::get_global(cx).gutter.runnables {
14592 self.clear_tasks();
14593 return Task::ready(());
14594 }
14595 let project = self.project.as_ref().map(Entity::downgrade);
14596 let task_sources = self.lsp_task_sources(cx);
14597 let multi_buffer = self.buffer.downgrade();
14598 cx.spawn_in(window, async move |editor, cx| {
14599 cx.background_executor().timer(UPDATE_DEBOUNCE).await;
14600 let Some(project) = project.and_then(|p| p.upgrade()) else {
14601 return;
14602 };
14603 let Ok(display_snapshot) = editor.update(cx, |this, cx| {
14604 this.display_map.update(cx, |map, cx| map.snapshot(cx))
14605 }) else {
14606 return;
14607 };
14608
14609 let hide_runnables = project
14610 .update(cx, |project, cx| {
14611 // Do not display any test indicators in non-dev server remote projects.
14612 project.is_via_collab() && project.ssh_connection_string(cx).is_none()
14613 })
14614 .unwrap_or(true);
14615 if hide_runnables {
14616 return;
14617 }
14618 let new_rows =
14619 cx.background_spawn({
14620 let snapshot = display_snapshot.clone();
14621 async move {
14622 Self::fetch_runnable_ranges(&snapshot, Anchor::min()..Anchor::max())
14623 }
14624 })
14625 .await;
14626 let Ok(lsp_tasks) =
14627 cx.update(|_, cx| crate::lsp_tasks(project.clone(), &task_sources, None, cx))
14628 else {
14629 return;
14630 };
14631 let lsp_tasks = lsp_tasks.await;
14632
14633 let Ok(mut lsp_tasks_by_rows) = cx.update(|_, cx| {
14634 lsp_tasks
14635 .into_iter()
14636 .flat_map(|(kind, tasks)| {
14637 tasks.into_iter().filter_map(move |(location, task)| {
14638 Some((kind.clone(), location?, task))
14639 })
14640 })
14641 .fold(HashMap::default(), |mut acc, (kind, location, task)| {
14642 let buffer = location.target.buffer;
14643 let buffer_snapshot = buffer.read(cx).snapshot();
14644 let offset = display_snapshot.buffer_snapshot.excerpts().find_map(
14645 |(excerpt_id, snapshot, _)| {
14646 if snapshot.remote_id() == buffer_snapshot.remote_id() {
14647 display_snapshot
14648 .buffer_snapshot
14649 .anchor_in_excerpt(excerpt_id, location.target.range.start)
14650 } else {
14651 None
14652 }
14653 },
14654 );
14655 if let Some(offset) = offset {
14656 let task_buffer_range =
14657 location.target.range.to_point(&buffer_snapshot);
14658 let context_buffer_range =
14659 task_buffer_range.to_offset(&buffer_snapshot);
14660 let context_range = BufferOffset(context_buffer_range.start)
14661 ..BufferOffset(context_buffer_range.end);
14662
14663 acc.entry((buffer_snapshot.remote_id(), task_buffer_range.start.row))
14664 .or_insert_with(|| RunnableTasks {
14665 templates: Vec::new(),
14666 offset,
14667 column: task_buffer_range.start.column,
14668 extra_variables: HashMap::default(),
14669 context_range,
14670 })
14671 .templates
14672 .push((kind, task.original_task().clone()));
14673 }
14674
14675 acc
14676 })
14677 }) else {
14678 return;
14679 };
14680
14681 let Ok(prefer_lsp) = multi_buffer.update(cx, |buffer, cx| {
14682 buffer.language_settings(cx).tasks.prefer_lsp
14683 }) else {
14684 return;
14685 };
14686
14687 let rows = Self::runnable_rows(
14688 project,
14689 display_snapshot,
14690 prefer_lsp && !lsp_tasks_by_rows.is_empty(),
14691 new_rows,
14692 cx.clone(),
14693 )
14694 .await;
14695 editor
14696 .update(cx, |editor, _| {
14697 editor.clear_tasks();
14698 for (key, mut value) in rows {
14699 if let Some(lsp_tasks) = lsp_tasks_by_rows.remove(&key) {
14700 value.templates.extend(lsp_tasks.templates);
14701 }
14702
14703 editor.insert_tasks(key, value);
14704 }
14705 for (key, value) in lsp_tasks_by_rows {
14706 editor.insert_tasks(key, value);
14707 }
14708 })
14709 .ok();
14710 })
14711 }
14712 fn fetch_runnable_ranges(
14713 snapshot: &DisplaySnapshot,
14714 range: Range<Anchor>,
14715 ) -> Vec<language::RunnableRange> {
14716 snapshot.buffer_snapshot.runnable_ranges(range).collect()
14717 }
14718
14719 fn runnable_rows(
14720 project: Entity<Project>,
14721 snapshot: DisplaySnapshot,
14722 prefer_lsp: bool,
14723 runnable_ranges: Vec<RunnableRange>,
14724 cx: AsyncWindowContext,
14725 ) -> Task<Vec<((BufferId, BufferRow), RunnableTasks)>> {
14726 cx.spawn(async move |cx| {
14727 let mut runnable_rows = Vec::with_capacity(runnable_ranges.len());
14728 for mut runnable in runnable_ranges {
14729 let Some(tasks) = cx
14730 .update(|_, cx| Self::templates_with_tags(&project, &mut runnable.runnable, cx))
14731 .ok()
14732 else {
14733 continue;
14734 };
14735 let mut tasks = tasks.await;
14736
14737 if prefer_lsp {
14738 tasks.retain(|(task_kind, _)| {
14739 !matches!(task_kind, TaskSourceKind::Language { .. })
14740 });
14741 }
14742 if tasks.is_empty() {
14743 continue;
14744 }
14745
14746 let point = runnable.run_range.start.to_point(&snapshot.buffer_snapshot);
14747 let Some(row) = snapshot
14748 .buffer_snapshot
14749 .buffer_line_for_row(MultiBufferRow(point.row))
14750 .map(|(_, range)| range.start.row)
14751 else {
14752 continue;
14753 };
14754
14755 let context_range =
14756 BufferOffset(runnable.full_range.start)..BufferOffset(runnable.full_range.end);
14757 runnable_rows.push((
14758 (runnable.buffer_id, row),
14759 RunnableTasks {
14760 templates: tasks,
14761 offset: snapshot
14762 .buffer_snapshot
14763 .anchor_before(runnable.run_range.start),
14764 context_range,
14765 column: point.column,
14766 extra_variables: runnable.extra_captures,
14767 },
14768 ));
14769 }
14770 runnable_rows
14771 })
14772 }
14773
14774 fn templates_with_tags(
14775 project: &Entity<Project>,
14776 runnable: &mut Runnable,
14777 cx: &mut App,
14778 ) -> Task<Vec<(TaskSourceKind, TaskTemplate)>> {
14779 let (inventory, worktree_id, file) = project.read_with(cx, |project, cx| {
14780 let (worktree_id, file) = project
14781 .buffer_for_id(runnable.buffer, cx)
14782 .and_then(|buffer| buffer.read(cx).file())
14783 .map(|file| (file.worktree_id(cx), file.clone()))
14784 .unzip();
14785
14786 (
14787 project.task_store().read(cx).task_inventory().cloned(),
14788 worktree_id,
14789 file,
14790 )
14791 });
14792
14793 let tags = mem::take(&mut runnable.tags);
14794 let language = runnable.language.clone();
14795 cx.spawn(async move |cx| {
14796 let mut templates_with_tags = Vec::new();
14797 if let Some(inventory) = inventory {
14798 for RunnableTag(tag) in tags {
14799 let Ok(new_tasks) = inventory.update(cx, |inventory, cx| {
14800 inventory.list_tasks(file.clone(), Some(language.clone()), worktree_id, cx)
14801 }) else {
14802 return templates_with_tags;
14803 };
14804 templates_with_tags.extend(new_tasks.await.into_iter().filter(
14805 move |(_, template)| {
14806 template.tags.iter().any(|source_tag| source_tag == &tag)
14807 },
14808 ));
14809 }
14810 }
14811 templates_with_tags.sort_by_key(|(kind, _)| kind.to_owned());
14812
14813 if let Some((leading_tag_source, _)) = templates_with_tags.first() {
14814 // Strongest source wins; if we have worktree tag binding, prefer that to
14815 // global and language bindings;
14816 // if we have a global binding, prefer that to language binding.
14817 let first_mismatch = templates_with_tags
14818 .iter()
14819 .position(|(tag_source, _)| tag_source != leading_tag_source);
14820 if let Some(index) = first_mismatch {
14821 templates_with_tags.truncate(index);
14822 }
14823 }
14824
14825 templates_with_tags
14826 })
14827 }
14828
14829 pub fn move_to_enclosing_bracket(
14830 &mut self,
14831 _: &MoveToEnclosingBracket,
14832 window: &mut Window,
14833 cx: &mut Context<Self>,
14834 ) {
14835 self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
14836 self.change_selections(Default::default(), window, cx, |s| {
14837 s.move_offsets_with(|snapshot, selection| {
14838 let Some(enclosing_bracket_ranges) =
14839 snapshot.enclosing_bracket_ranges(selection.start..selection.end)
14840 else {
14841 return;
14842 };
14843
14844 let mut best_length = usize::MAX;
14845 let mut best_inside = false;
14846 let mut best_in_bracket_range = false;
14847 let mut best_destination = None;
14848 for (open, close) in enclosing_bracket_ranges {
14849 let close = close.to_inclusive();
14850 let length = close.end() - open.start;
14851 let inside = selection.start >= open.end && selection.end <= *close.start();
14852 let in_bracket_range = open.to_inclusive().contains(&selection.head())
14853 || close.contains(&selection.head());
14854
14855 // If best is next to a bracket and current isn't, skip
14856 if !in_bracket_range && best_in_bracket_range {
14857 continue;
14858 }
14859
14860 // Prefer smaller lengths unless best is inside and current isn't
14861 if length > best_length && (best_inside || !inside) {
14862 continue;
14863 }
14864
14865 best_length = length;
14866 best_inside = inside;
14867 best_in_bracket_range = in_bracket_range;
14868 best_destination = Some(
14869 if close.contains(&selection.start) && close.contains(&selection.end) {
14870 if inside { open.end } else { open.start }
14871 } else if inside {
14872 *close.start()
14873 } else {
14874 *close.end()
14875 },
14876 );
14877 }
14878
14879 if let Some(destination) = best_destination {
14880 selection.collapse_to(destination, SelectionGoal::None);
14881 }
14882 })
14883 });
14884 }
14885
14886 pub fn undo_selection(
14887 &mut self,
14888 _: &UndoSelection,
14889 window: &mut Window,
14890 cx: &mut Context<Self>,
14891 ) {
14892 self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
14893 if let Some(entry) = self.selection_history.undo_stack.pop_back() {
14894 self.selection_history.mode = SelectionHistoryMode::Undoing;
14895 self.with_selection_effects_deferred(window, cx, |this, window, cx| {
14896 this.end_selection(window, cx);
14897 this.change_selections(
14898 SelectionEffects::scroll(Autoscroll::newest()),
14899 window,
14900 cx,
14901 |s| s.select_anchors(entry.selections.to_vec()),
14902 );
14903 });
14904 self.selection_history.mode = SelectionHistoryMode::Normal;
14905
14906 self.select_next_state = entry.select_next_state;
14907 self.select_prev_state = entry.select_prev_state;
14908 self.add_selections_state = entry.add_selections_state;
14909 }
14910 }
14911
14912 pub fn redo_selection(
14913 &mut self,
14914 _: &RedoSelection,
14915 window: &mut Window,
14916 cx: &mut Context<Self>,
14917 ) {
14918 self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
14919 if let Some(entry) = self.selection_history.redo_stack.pop_back() {
14920 self.selection_history.mode = SelectionHistoryMode::Redoing;
14921 self.with_selection_effects_deferred(window, cx, |this, window, cx| {
14922 this.end_selection(window, cx);
14923 this.change_selections(
14924 SelectionEffects::scroll(Autoscroll::newest()),
14925 window,
14926 cx,
14927 |s| s.select_anchors(entry.selections.to_vec()),
14928 );
14929 });
14930 self.selection_history.mode = SelectionHistoryMode::Normal;
14931
14932 self.select_next_state = entry.select_next_state;
14933 self.select_prev_state = entry.select_prev_state;
14934 self.add_selections_state = entry.add_selections_state;
14935 }
14936 }
14937
14938 pub fn expand_excerpts(
14939 &mut self,
14940 action: &ExpandExcerpts,
14941 _: &mut Window,
14942 cx: &mut Context<Self>,
14943 ) {
14944 self.expand_excerpts_for_direction(action.lines, ExpandExcerptDirection::UpAndDown, cx)
14945 }
14946
14947 pub fn expand_excerpts_down(
14948 &mut self,
14949 action: &ExpandExcerptsDown,
14950 _: &mut Window,
14951 cx: &mut Context<Self>,
14952 ) {
14953 self.expand_excerpts_for_direction(action.lines, ExpandExcerptDirection::Down, cx)
14954 }
14955
14956 pub fn expand_excerpts_up(
14957 &mut self,
14958 action: &ExpandExcerptsUp,
14959 _: &mut Window,
14960 cx: &mut Context<Self>,
14961 ) {
14962 self.expand_excerpts_for_direction(action.lines, ExpandExcerptDirection::Up, cx)
14963 }
14964
14965 pub fn expand_excerpts_for_direction(
14966 &mut self,
14967 lines: u32,
14968 direction: ExpandExcerptDirection,
14969
14970 cx: &mut Context<Self>,
14971 ) {
14972 let selections = self.selections.disjoint_anchors();
14973
14974 let lines = if lines == 0 {
14975 EditorSettings::get_global(cx).expand_excerpt_lines
14976 } else {
14977 lines
14978 };
14979
14980 self.buffer.update(cx, |buffer, cx| {
14981 let snapshot = buffer.snapshot(cx);
14982 let mut excerpt_ids = selections
14983 .iter()
14984 .flat_map(|selection| snapshot.excerpt_ids_for_range(selection.range()))
14985 .collect::<Vec<_>>();
14986 excerpt_ids.sort();
14987 excerpt_ids.dedup();
14988 buffer.expand_excerpts(excerpt_ids, lines, direction, cx)
14989 })
14990 }
14991
14992 pub fn expand_excerpt(
14993 &mut self,
14994 excerpt: ExcerptId,
14995 direction: ExpandExcerptDirection,
14996 window: &mut Window,
14997 cx: &mut Context<Self>,
14998 ) {
14999 let current_scroll_position = self.scroll_position(cx);
15000 let lines_to_expand = EditorSettings::get_global(cx).expand_excerpt_lines;
15001 let mut should_scroll_up = false;
15002
15003 if direction == ExpandExcerptDirection::Down {
15004 let multi_buffer = self.buffer.read(cx);
15005 let snapshot = multi_buffer.snapshot(cx);
15006 if let Some(buffer_id) = snapshot.buffer_id_for_excerpt(excerpt) {
15007 if let Some(buffer) = multi_buffer.buffer(buffer_id) {
15008 if let Some(excerpt_range) = snapshot.buffer_range_for_excerpt(excerpt) {
15009 let buffer_snapshot = buffer.read(cx).snapshot();
15010 let excerpt_end_row =
15011 Point::from_anchor(&excerpt_range.end, &buffer_snapshot).row;
15012 let last_row = buffer_snapshot.max_point().row;
15013 let lines_below = last_row.saturating_sub(excerpt_end_row);
15014 should_scroll_up = lines_below >= lines_to_expand;
15015 }
15016 }
15017 }
15018 }
15019
15020 self.buffer.update(cx, |buffer, cx| {
15021 buffer.expand_excerpts([excerpt], lines_to_expand, direction, cx)
15022 });
15023
15024 if should_scroll_up {
15025 let new_scroll_position =
15026 current_scroll_position + gpui::Point::new(0.0, lines_to_expand as f32);
15027 self.set_scroll_position(new_scroll_position, window, cx);
15028 }
15029 }
15030
15031 pub fn go_to_singleton_buffer_point(
15032 &mut self,
15033 point: Point,
15034 window: &mut Window,
15035 cx: &mut Context<Self>,
15036 ) {
15037 self.go_to_singleton_buffer_range(point..point, window, cx);
15038 }
15039
15040 pub fn go_to_singleton_buffer_range(
15041 &mut self,
15042 range: Range<Point>,
15043 window: &mut Window,
15044 cx: &mut Context<Self>,
15045 ) {
15046 let multibuffer = self.buffer().read(cx);
15047 let Some(buffer) = multibuffer.as_singleton() else {
15048 return;
15049 };
15050 let Some(start) = multibuffer.buffer_point_to_anchor(&buffer, range.start, cx) else {
15051 return;
15052 };
15053 let Some(end) = multibuffer.buffer_point_to_anchor(&buffer, range.end, cx) else {
15054 return;
15055 };
15056 self.change_selections(
15057 SelectionEffects::default().nav_history(true),
15058 window,
15059 cx,
15060 |s| s.select_anchor_ranges([start..end]),
15061 );
15062 }
15063
15064 pub fn go_to_diagnostic(
15065 &mut self,
15066 _: &GoToDiagnostic,
15067 window: &mut Window,
15068 cx: &mut Context<Self>,
15069 ) {
15070 if !self.diagnostics_enabled() {
15071 return;
15072 }
15073 self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
15074 self.go_to_diagnostic_impl(Direction::Next, window, cx)
15075 }
15076
15077 pub fn go_to_prev_diagnostic(
15078 &mut self,
15079 _: &GoToPreviousDiagnostic,
15080 window: &mut Window,
15081 cx: &mut Context<Self>,
15082 ) {
15083 if !self.diagnostics_enabled() {
15084 return;
15085 }
15086 self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
15087 self.go_to_diagnostic_impl(Direction::Prev, window, cx)
15088 }
15089
15090 pub fn go_to_diagnostic_impl(
15091 &mut self,
15092 direction: Direction,
15093 window: &mut Window,
15094 cx: &mut Context<Self>,
15095 ) {
15096 let buffer = self.buffer.read(cx).snapshot(cx);
15097 let selection = self.selections.newest::<usize>(cx);
15098
15099 let mut active_group_id = None;
15100 if let ActiveDiagnostic::Group(active_group) = &self.active_diagnostics {
15101 if active_group.active_range.start.to_offset(&buffer) == selection.start {
15102 active_group_id = Some(active_group.group_id);
15103 }
15104 }
15105
15106 fn filtered(
15107 snapshot: EditorSnapshot,
15108 diagnostics: impl Iterator<Item = DiagnosticEntry<usize>>,
15109 ) -> impl Iterator<Item = DiagnosticEntry<usize>> {
15110 diagnostics
15111 .filter(|entry| entry.range.start != entry.range.end)
15112 .filter(|entry| !entry.diagnostic.is_unnecessary)
15113 .filter(move |entry| !snapshot.intersects_fold(entry.range.start))
15114 }
15115
15116 let snapshot = self.snapshot(window, cx);
15117 let before = filtered(
15118 snapshot.clone(),
15119 buffer
15120 .diagnostics_in_range(0..selection.start)
15121 .filter(|entry| entry.range.start <= selection.start),
15122 );
15123 let after = filtered(
15124 snapshot,
15125 buffer
15126 .diagnostics_in_range(selection.start..buffer.len())
15127 .filter(|entry| entry.range.start >= selection.start),
15128 );
15129
15130 let mut found: Option<DiagnosticEntry<usize>> = None;
15131 if direction == Direction::Prev {
15132 'outer: for prev_diagnostics in [before.collect::<Vec<_>>(), after.collect::<Vec<_>>()]
15133 {
15134 for diagnostic in prev_diagnostics.into_iter().rev() {
15135 if diagnostic.range.start != selection.start
15136 || active_group_id
15137 .is_some_and(|active| diagnostic.diagnostic.group_id < active)
15138 {
15139 found = Some(diagnostic);
15140 break 'outer;
15141 }
15142 }
15143 }
15144 } else {
15145 for diagnostic in after.chain(before) {
15146 if diagnostic.range.start != selection.start
15147 || active_group_id.is_some_and(|active| diagnostic.diagnostic.group_id > active)
15148 {
15149 found = Some(diagnostic);
15150 break;
15151 }
15152 }
15153 }
15154 let Some(next_diagnostic) = found else {
15155 return;
15156 };
15157
15158 let Some(buffer_id) = buffer.anchor_after(next_diagnostic.range.start).buffer_id else {
15159 return;
15160 };
15161 self.change_selections(Default::default(), window, cx, |s| {
15162 s.select_ranges(vec![
15163 next_diagnostic.range.start..next_diagnostic.range.start,
15164 ])
15165 });
15166 self.activate_diagnostics(buffer_id, next_diagnostic, window, cx);
15167 self.refresh_inline_completion(false, true, window, cx);
15168 }
15169
15170 pub fn go_to_next_hunk(&mut self, _: &GoToHunk, window: &mut Window, cx: &mut Context<Self>) {
15171 self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
15172 let snapshot = self.snapshot(window, cx);
15173 let selection = self.selections.newest::<Point>(cx);
15174 self.go_to_hunk_before_or_after_position(
15175 &snapshot,
15176 selection.head(),
15177 Direction::Next,
15178 window,
15179 cx,
15180 );
15181 }
15182
15183 pub fn go_to_hunk_before_or_after_position(
15184 &mut self,
15185 snapshot: &EditorSnapshot,
15186 position: Point,
15187 direction: Direction,
15188 window: &mut Window,
15189 cx: &mut Context<Editor>,
15190 ) {
15191 let row = if direction == Direction::Next {
15192 self.hunk_after_position(snapshot, position)
15193 .map(|hunk| hunk.row_range.start)
15194 } else {
15195 self.hunk_before_position(snapshot, position)
15196 };
15197
15198 if let Some(row) = row {
15199 let destination = Point::new(row.0, 0);
15200 let autoscroll = Autoscroll::center();
15201
15202 self.unfold_ranges(&[destination..destination], false, false, cx);
15203 self.change_selections(SelectionEffects::scroll(autoscroll), window, cx, |s| {
15204 s.select_ranges([destination..destination]);
15205 });
15206 }
15207 }
15208
15209 fn hunk_after_position(
15210 &mut self,
15211 snapshot: &EditorSnapshot,
15212 position: Point,
15213 ) -> Option<MultiBufferDiffHunk> {
15214 snapshot
15215 .buffer_snapshot
15216 .diff_hunks_in_range(position..snapshot.buffer_snapshot.max_point())
15217 .find(|hunk| hunk.row_range.start.0 > position.row)
15218 .or_else(|| {
15219 snapshot
15220 .buffer_snapshot
15221 .diff_hunks_in_range(Point::zero()..position)
15222 .find(|hunk| hunk.row_range.end.0 < position.row)
15223 })
15224 }
15225
15226 fn go_to_prev_hunk(
15227 &mut self,
15228 _: &GoToPreviousHunk,
15229 window: &mut Window,
15230 cx: &mut Context<Self>,
15231 ) {
15232 self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
15233 let snapshot = self.snapshot(window, cx);
15234 let selection = self.selections.newest::<Point>(cx);
15235 self.go_to_hunk_before_or_after_position(
15236 &snapshot,
15237 selection.head(),
15238 Direction::Prev,
15239 window,
15240 cx,
15241 );
15242 }
15243
15244 fn hunk_before_position(
15245 &mut self,
15246 snapshot: &EditorSnapshot,
15247 position: Point,
15248 ) -> Option<MultiBufferRow> {
15249 snapshot
15250 .buffer_snapshot
15251 .diff_hunk_before(position)
15252 .or_else(|| snapshot.buffer_snapshot.diff_hunk_before(Point::MAX))
15253 }
15254
15255 fn go_to_next_change(
15256 &mut self,
15257 _: &GoToNextChange,
15258 window: &mut Window,
15259 cx: &mut Context<Self>,
15260 ) {
15261 if let Some(selections) = self
15262 .change_list
15263 .next_change(1, Direction::Next)
15264 .map(|s| s.to_vec())
15265 {
15266 self.change_selections(Default::default(), window, cx, |s| {
15267 let map = s.display_map();
15268 s.select_display_ranges(selections.iter().map(|a| {
15269 let point = a.to_display_point(&map);
15270 point..point
15271 }))
15272 })
15273 }
15274 }
15275
15276 fn go_to_previous_change(
15277 &mut self,
15278 _: &GoToPreviousChange,
15279 window: &mut Window,
15280 cx: &mut Context<Self>,
15281 ) {
15282 if let Some(selections) = self
15283 .change_list
15284 .next_change(1, Direction::Prev)
15285 .map(|s| s.to_vec())
15286 {
15287 self.change_selections(Default::default(), window, cx, |s| {
15288 let map = s.display_map();
15289 s.select_display_ranges(selections.iter().map(|a| {
15290 let point = a.to_display_point(&map);
15291 point..point
15292 }))
15293 })
15294 }
15295 }
15296
15297 fn go_to_line<T: 'static>(
15298 &mut self,
15299 position: Anchor,
15300 highlight_color: Option<Hsla>,
15301 window: &mut Window,
15302 cx: &mut Context<Self>,
15303 ) {
15304 let snapshot = self.snapshot(window, cx).display_snapshot;
15305 let position = position.to_point(&snapshot.buffer_snapshot);
15306 let start = snapshot
15307 .buffer_snapshot
15308 .clip_point(Point::new(position.row, 0), Bias::Left);
15309 let end = start + Point::new(1, 0);
15310 let start = snapshot.buffer_snapshot.anchor_before(start);
15311 let end = snapshot.buffer_snapshot.anchor_before(end);
15312
15313 self.highlight_rows::<T>(
15314 start..end,
15315 highlight_color
15316 .unwrap_or_else(|| cx.theme().colors().editor_highlighted_line_background),
15317 Default::default(),
15318 cx,
15319 );
15320
15321 if self.buffer.read(cx).is_singleton() {
15322 self.request_autoscroll(Autoscroll::center().for_anchor(start), cx);
15323 }
15324 }
15325
15326 pub fn go_to_definition(
15327 &mut self,
15328 _: &GoToDefinition,
15329 window: &mut Window,
15330 cx: &mut Context<Self>,
15331 ) -> Task<Result<Navigated>> {
15332 let definition =
15333 self.go_to_definition_of_kind(GotoDefinitionKind::Symbol, false, window, cx);
15334 let fallback_strategy = EditorSettings::get_global(cx).go_to_definition_fallback;
15335 cx.spawn_in(window, async move |editor, cx| {
15336 if definition.await? == Navigated::Yes {
15337 return Ok(Navigated::Yes);
15338 }
15339 match fallback_strategy {
15340 GoToDefinitionFallback::None => Ok(Navigated::No),
15341 GoToDefinitionFallback::FindAllReferences => {
15342 match editor.update_in(cx, |editor, window, cx| {
15343 editor.find_all_references(&FindAllReferences, window, cx)
15344 })? {
15345 Some(references) => references.await,
15346 None => Ok(Navigated::No),
15347 }
15348 }
15349 }
15350 })
15351 }
15352
15353 pub fn go_to_declaration(
15354 &mut self,
15355 _: &GoToDeclaration,
15356 window: &mut Window,
15357 cx: &mut Context<Self>,
15358 ) -> Task<Result<Navigated>> {
15359 self.go_to_definition_of_kind(GotoDefinitionKind::Declaration, false, window, cx)
15360 }
15361
15362 pub fn go_to_declaration_split(
15363 &mut self,
15364 _: &GoToDeclaration,
15365 window: &mut Window,
15366 cx: &mut Context<Self>,
15367 ) -> Task<Result<Navigated>> {
15368 self.go_to_definition_of_kind(GotoDefinitionKind::Declaration, true, window, cx)
15369 }
15370
15371 pub fn go_to_implementation(
15372 &mut self,
15373 _: &GoToImplementation,
15374 window: &mut Window,
15375 cx: &mut Context<Self>,
15376 ) -> Task<Result<Navigated>> {
15377 self.go_to_definition_of_kind(GotoDefinitionKind::Implementation, false, window, cx)
15378 }
15379
15380 pub fn go_to_implementation_split(
15381 &mut self,
15382 _: &GoToImplementationSplit,
15383 window: &mut Window,
15384 cx: &mut Context<Self>,
15385 ) -> Task<Result<Navigated>> {
15386 self.go_to_definition_of_kind(GotoDefinitionKind::Implementation, true, window, cx)
15387 }
15388
15389 pub fn go_to_type_definition(
15390 &mut self,
15391 _: &GoToTypeDefinition,
15392 window: &mut Window,
15393 cx: &mut Context<Self>,
15394 ) -> Task<Result<Navigated>> {
15395 self.go_to_definition_of_kind(GotoDefinitionKind::Type, false, window, cx)
15396 }
15397
15398 pub fn go_to_definition_split(
15399 &mut self,
15400 _: &GoToDefinitionSplit,
15401 window: &mut Window,
15402 cx: &mut Context<Self>,
15403 ) -> Task<Result<Navigated>> {
15404 self.go_to_definition_of_kind(GotoDefinitionKind::Symbol, true, window, cx)
15405 }
15406
15407 pub fn go_to_type_definition_split(
15408 &mut self,
15409 _: &GoToTypeDefinitionSplit,
15410 window: &mut Window,
15411 cx: &mut Context<Self>,
15412 ) -> Task<Result<Navigated>> {
15413 self.go_to_definition_of_kind(GotoDefinitionKind::Type, true, window, cx)
15414 }
15415
15416 fn go_to_definition_of_kind(
15417 &mut self,
15418 kind: GotoDefinitionKind,
15419 split: bool,
15420 window: &mut Window,
15421 cx: &mut Context<Self>,
15422 ) -> Task<Result<Navigated>> {
15423 let Some(provider) = self.semantics_provider.clone() else {
15424 return Task::ready(Ok(Navigated::No));
15425 };
15426 let head = self.selections.newest::<usize>(cx).head();
15427 let buffer = self.buffer.read(cx);
15428 let (buffer, head) = if let Some(text_anchor) = buffer.text_anchor_for_position(head, cx) {
15429 text_anchor
15430 } else {
15431 return Task::ready(Ok(Navigated::No));
15432 };
15433
15434 let Some(definitions) = provider.definitions(&buffer, head, kind, cx) else {
15435 return Task::ready(Ok(Navigated::No));
15436 };
15437
15438 cx.spawn_in(window, async move |editor, cx| {
15439 let definitions = definitions.await?;
15440 let navigated = editor
15441 .update_in(cx, |editor, window, cx| {
15442 editor.navigate_to_hover_links(
15443 Some(kind),
15444 definitions
15445 .into_iter()
15446 .filter(|location| {
15447 hover_links::exclude_link_to_position(&buffer, &head, location, cx)
15448 })
15449 .map(HoverLink::Text)
15450 .collect::<Vec<_>>(),
15451 split,
15452 window,
15453 cx,
15454 )
15455 })?
15456 .await?;
15457 anyhow::Ok(navigated)
15458 })
15459 }
15460
15461 pub fn open_url(&mut self, _: &OpenUrl, window: &mut Window, cx: &mut Context<Self>) {
15462 let selection = self.selections.newest_anchor();
15463 let head = selection.head();
15464 let tail = selection.tail();
15465
15466 let Some((buffer, start_position)) =
15467 self.buffer.read(cx).text_anchor_for_position(head, cx)
15468 else {
15469 return;
15470 };
15471
15472 let end_position = if head != tail {
15473 let Some((_, pos)) = self.buffer.read(cx).text_anchor_for_position(tail, cx) else {
15474 return;
15475 };
15476 Some(pos)
15477 } else {
15478 None
15479 };
15480
15481 let url_finder = cx.spawn_in(window, async move |editor, cx| {
15482 let url = if let Some(end_pos) = end_position {
15483 find_url_from_range(&buffer, start_position..end_pos, cx.clone())
15484 } else {
15485 find_url(&buffer, start_position, cx.clone()).map(|(_, url)| url)
15486 };
15487
15488 if let Some(url) = url {
15489 editor.update(cx, |_, cx| {
15490 cx.open_url(&url);
15491 })
15492 } else {
15493 Ok(())
15494 }
15495 });
15496
15497 url_finder.detach();
15498 }
15499
15500 pub fn open_selected_filename(
15501 &mut self,
15502 _: &OpenSelectedFilename,
15503 window: &mut Window,
15504 cx: &mut Context<Self>,
15505 ) {
15506 let Some(workspace) = self.workspace() else {
15507 return;
15508 };
15509
15510 let position = self.selections.newest_anchor().head();
15511
15512 let Some((buffer, buffer_position)) =
15513 self.buffer.read(cx).text_anchor_for_position(position, cx)
15514 else {
15515 return;
15516 };
15517
15518 let project = self.project.clone();
15519
15520 cx.spawn_in(window, async move |_, cx| {
15521 let result = find_file(&buffer, project, buffer_position, cx).await;
15522
15523 if let Some((_, path)) = result {
15524 workspace
15525 .update_in(cx, |workspace, window, cx| {
15526 workspace.open_resolved_path(path, window, cx)
15527 })?
15528 .await?;
15529 }
15530 anyhow::Ok(())
15531 })
15532 .detach();
15533 }
15534
15535 pub(crate) fn navigate_to_hover_links(
15536 &mut self,
15537 kind: Option<GotoDefinitionKind>,
15538 mut definitions: Vec<HoverLink>,
15539 split: bool,
15540 window: &mut Window,
15541 cx: &mut Context<Editor>,
15542 ) -> Task<Result<Navigated>> {
15543 // If there is one definition, just open it directly
15544 if definitions.len() == 1 {
15545 let definition = definitions.pop().unwrap();
15546
15547 enum TargetTaskResult {
15548 Location(Option<Location>),
15549 AlreadyNavigated,
15550 }
15551
15552 let target_task = match definition {
15553 HoverLink::Text(link) => {
15554 Task::ready(anyhow::Ok(TargetTaskResult::Location(Some(link.target))))
15555 }
15556 HoverLink::InlayHint(lsp_location, server_id) => {
15557 let computation =
15558 self.compute_target_location(lsp_location, server_id, window, cx);
15559 cx.background_spawn(async move {
15560 let location = computation.await?;
15561 Ok(TargetTaskResult::Location(location))
15562 })
15563 }
15564 HoverLink::Url(url) => {
15565 cx.open_url(&url);
15566 Task::ready(Ok(TargetTaskResult::AlreadyNavigated))
15567 }
15568 HoverLink::File(path) => {
15569 if let Some(workspace) = self.workspace() {
15570 cx.spawn_in(window, async move |_, cx| {
15571 workspace
15572 .update_in(cx, |workspace, window, cx| {
15573 workspace.open_resolved_path(path, window, cx)
15574 })?
15575 .await
15576 .map(|_| TargetTaskResult::AlreadyNavigated)
15577 })
15578 } else {
15579 Task::ready(Ok(TargetTaskResult::Location(None)))
15580 }
15581 }
15582 };
15583 cx.spawn_in(window, async move |editor, cx| {
15584 let target = match target_task.await.context("target resolution task")? {
15585 TargetTaskResult::AlreadyNavigated => return Ok(Navigated::Yes),
15586 TargetTaskResult::Location(None) => return Ok(Navigated::No),
15587 TargetTaskResult::Location(Some(target)) => target,
15588 };
15589
15590 editor.update_in(cx, |editor, window, cx| {
15591 let Some(workspace) = editor.workspace() else {
15592 return Navigated::No;
15593 };
15594 let pane = workspace.read(cx).active_pane().clone();
15595
15596 let range = target.range.to_point(target.buffer.read(cx));
15597 let range = editor.range_for_match(&range);
15598 let range = collapse_multiline_range(range);
15599
15600 if !split
15601 && Some(&target.buffer) == editor.buffer.read(cx).as_singleton().as_ref()
15602 {
15603 editor.go_to_singleton_buffer_range(range.clone(), window, cx);
15604 } else {
15605 window.defer(cx, move |window, cx| {
15606 let target_editor: Entity<Self> =
15607 workspace.update(cx, |workspace, cx| {
15608 let pane = if split {
15609 workspace.adjacent_pane(window, cx)
15610 } else {
15611 workspace.active_pane().clone()
15612 };
15613
15614 workspace.open_project_item(
15615 pane,
15616 target.buffer.clone(),
15617 true,
15618 true,
15619 window,
15620 cx,
15621 )
15622 });
15623 target_editor.update(cx, |target_editor, cx| {
15624 // When selecting a definition in a different buffer, disable the nav history
15625 // to avoid creating a history entry at the previous cursor location.
15626 pane.update(cx, |pane, _| pane.disable_history());
15627 target_editor.go_to_singleton_buffer_range(range, window, cx);
15628 pane.update(cx, |pane, _| pane.enable_history());
15629 });
15630 });
15631 }
15632 Navigated::Yes
15633 })
15634 })
15635 } else if !definitions.is_empty() {
15636 cx.spawn_in(window, async move |editor, cx| {
15637 let (title, location_tasks, workspace) = editor
15638 .update_in(cx, |editor, window, cx| {
15639 let tab_kind = match kind {
15640 Some(GotoDefinitionKind::Implementation) => "Implementations",
15641 _ => "Definitions",
15642 };
15643 let title = definitions
15644 .iter()
15645 .find_map(|definition| match definition {
15646 HoverLink::Text(link) => link.origin.as_ref().map(|origin| {
15647 let buffer = origin.buffer.read(cx);
15648 format!(
15649 "{} for {}",
15650 tab_kind,
15651 buffer
15652 .text_for_range(origin.range.clone())
15653 .collect::<String>()
15654 )
15655 }),
15656 HoverLink::InlayHint(_, _) => None,
15657 HoverLink::Url(_) => None,
15658 HoverLink::File(_) => None,
15659 })
15660 .unwrap_or(tab_kind.to_string());
15661 let location_tasks = definitions
15662 .into_iter()
15663 .map(|definition| match definition {
15664 HoverLink::Text(link) => Task::ready(Ok(Some(link.target))),
15665 HoverLink::InlayHint(lsp_location, server_id) => editor
15666 .compute_target_location(lsp_location, server_id, window, cx),
15667 HoverLink::Url(_) => Task::ready(Ok(None)),
15668 HoverLink::File(_) => Task::ready(Ok(None)),
15669 })
15670 .collect::<Vec<_>>();
15671 (title, location_tasks, editor.workspace().clone())
15672 })
15673 .context("location tasks preparation")?;
15674
15675 let locations: Vec<Location> = future::join_all(location_tasks)
15676 .await
15677 .into_iter()
15678 .filter_map(|location| location.transpose())
15679 .collect::<Result<_>>()
15680 .context("location tasks")?;
15681
15682 if locations.is_empty() {
15683 return Ok(Navigated::No);
15684 }
15685
15686 let Some(workspace) = workspace else {
15687 return Ok(Navigated::No);
15688 };
15689
15690 let opened = workspace
15691 .update_in(cx, |workspace, window, cx| {
15692 Self::open_locations_in_multibuffer(
15693 workspace,
15694 locations,
15695 title,
15696 split,
15697 MultibufferSelectionMode::First,
15698 window,
15699 cx,
15700 )
15701 })
15702 .ok();
15703
15704 anyhow::Ok(Navigated::from_bool(opened.is_some()))
15705 })
15706 } else {
15707 Task::ready(Ok(Navigated::No))
15708 }
15709 }
15710
15711 fn compute_target_location(
15712 &self,
15713 lsp_location: lsp::Location,
15714 server_id: LanguageServerId,
15715 window: &mut Window,
15716 cx: &mut Context<Self>,
15717 ) -> Task<anyhow::Result<Option<Location>>> {
15718 let Some(project) = self.project.clone() else {
15719 return Task::ready(Ok(None));
15720 };
15721
15722 cx.spawn_in(window, async move |editor, cx| {
15723 let location_task = editor.update(cx, |_, cx| {
15724 project.update(cx, |project, cx| {
15725 let language_server_name = project
15726 .language_server_statuses(cx)
15727 .find(|(id, _)| server_id == *id)
15728 .map(|(_, status)| LanguageServerName::from(status.name.as_str()));
15729 language_server_name.map(|language_server_name| {
15730 project.open_local_buffer_via_lsp(
15731 lsp_location.uri.clone(),
15732 server_id,
15733 language_server_name,
15734 cx,
15735 )
15736 })
15737 })
15738 })?;
15739 let location = match location_task {
15740 Some(task) => Some({
15741 let target_buffer_handle = task.await.context("open local buffer")?;
15742 let range = target_buffer_handle.read_with(cx, |target_buffer, _| {
15743 let target_start = target_buffer
15744 .clip_point_utf16(point_from_lsp(lsp_location.range.start), Bias::Left);
15745 let target_end = target_buffer
15746 .clip_point_utf16(point_from_lsp(lsp_location.range.end), Bias::Left);
15747 target_buffer.anchor_after(target_start)
15748 ..target_buffer.anchor_before(target_end)
15749 })?;
15750 Location {
15751 buffer: target_buffer_handle,
15752 range,
15753 }
15754 }),
15755 None => None,
15756 };
15757 Ok(location)
15758 })
15759 }
15760
15761 pub fn find_all_references(
15762 &mut self,
15763 _: &FindAllReferences,
15764 window: &mut Window,
15765 cx: &mut Context<Self>,
15766 ) -> Option<Task<Result<Navigated>>> {
15767 let selection = self.selections.newest::<usize>(cx);
15768 let multi_buffer = self.buffer.read(cx);
15769 let head = selection.head();
15770
15771 let multi_buffer_snapshot = multi_buffer.snapshot(cx);
15772 let head_anchor = multi_buffer_snapshot.anchor_at(
15773 head,
15774 if head < selection.tail() {
15775 Bias::Right
15776 } else {
15777 Bias::Left
15778 },
15779 );
15780
15781 match self
15782 .find_all_references_task_sources
15783 .binary_search_by(|anchor| anchor.cmp(&head_anchor, &multi_buffer_snapshot))
15784 {
15785 Ok(_) => {
15786 log::info!(
15787 "Ignoring repeated FindAllReferences invocation with the position of already running task"
15788 );
15789 return None;
15790 }
15791 Err(i) => {
15792 self.find_all_references_task_sources.insert(i, head_anchor);
15793 }
15794 }
15795
15796 let (buffer, head) = multi_buffer.text_anchor_for_position(head, cx)?;
15797 let workspace = self.workspace()?;
15798 let project = workspace.read(cx).project().clone();
15799 let references = project.update(cx, |project, cx| project.references(&buffer, head, cx));
15800 Some(cx.spawn_in(window, async move |editor, cx| {
15801 let _cleanup = cx.on_drop(&editor, move |editor, _| {
15802 if let Ok(i) = editor
15803 .find_all_references_task_sources
15804 .binary_search_by(|anchor| anchor.cmp(&head_anchor, &multi_buffer_snapshot))
15805 {
15806 editor.find_all_references_task_sources.remove(i);
15807 }
15808 });
15809
15810 let locations = references.await?;
15811 if locations.is_empty() {
15812 return anyhow::Ok(Navigated::No);
15813 }
15814
15815 workspace.update_in(cx, |workspace, window, cx| {
15816 let title = locations
15817 .first()
15818 .as_ref()
15819 .map(|location| {
15820 let buffer = location.buffer.read(cx);
15821 format!(
15822 "References to `{}`",
15823 buffer
15824 .text_for_range(location.range.clone())
15825 .collect::<String>()
15826 )
15827 })
15828 .unwrap();
15829 Self::open_locations_in_multibuffer(
15830 workspace,
15831 locations,
15832 title,
15833 false,
15834 MultibufferSelectionMode::First,
15835 window,
15836 cx,
15837 );
15838 Navigated::Yes
15839 })
15840 }))
15841 }
15842
15843 /// Opens a multibuffer with the given project locations in it
15844 pub fn open_locations_in_multibuffer(
15845 workspace: &mut Workspace,
15846 mut locations: Vec<Location>,
15847 title: String,
15848 split: bool,
15849 multibuffer_selection_mode: MultibufferSelectionMode,
15850 window: &mut Window,
15851 cx: &mut Context<Workspace>,
15852 ) {
15853 if locations.is_empty() {
15854 log::error!("bug: open_locations_in_multibuffer called with empty list of locations");
15855 return;
15856 }
15857
15858 // If there are multiple definitions, open them in a multibuffer
15859 locations.sort_by_key(|location| location.buffer.read(cx).remote_id());
15860 let mut locations = locations.into_iter().peekable();
15861 let mut ranges: Vec<Range<Anchor>> = Vec::new();
15862 let capability = workspace.project().read(cx).capability();
15863
15864 let excerpt_buffer = cx.new(|cx| {
15865 let mut multibuffer = MultiBuffer::new(capability);
15866 while let Some(location) = locations.next() {
15867 let buffer = location.buffer.read(cx);
15868 let mut ranges_for_buffer = Vec::new();
15869 let range = location.range.to_point(buffer);
15870 ranges_for_buffer.push(range.clone());
15871
15872 while let Some(next_location) = locations.peek() {
15873 if next_location.buffer == location.buffer {
15874 ranges_for_buffer.push(next_location.range.to_point(buffer));
15875 locations.next();
15876 } else {
15877 break;
15878 }
15879 }
15880
15881 ranges_for_buffer.sort_by_key(|range| (range.start, Reverse(range.end)));
15882 let (new_ranges, _) = multibuffer.set_excerpts_for_path(
15883 PathKey::for_buffer(&location.buffer, cx),
15884 location.buffer.clone(),
15885 ranges_for_buffer,
15886 DEFAULT_MULTIBUFFER_CONTEXT,
15887 cx,
15888 );
15889 ranges.extend(new_ranges)
15890 }
15891
15892 multibuffer.with_title(title)
15893 });
15894
15895 let editor = cx.new(|cx| {
15896 Editor::for_multibuffer(
15897 excerpt_buffer,
15898 Some(workspace.project().clone()),
15899 window,
15900 cx,
15901 )
15902 });
15903 editor.update(cx, |editor, cx| {
15904 match multibuffer_selection_mode {
15905 MultibufferSelectionMode::First => {
15906 if let Some(first_range) = ranges.first() {
15907 editor.change_selections(
15908 SelectionEffects::no_scroll(),
15909 window,
15910 cx,
15911 |selections| {
15912 selections.clear_disjoint();
15913 selections
15914 .select_anchor_ranges(std::iter::once(first_range.clone()));
15915 },
15916 );
15917 }
15918 editor.highlight_background::<Self>(
15919 &ranges,
15920 |theme| theme.colors().editor_highlighted_line_background,
15921 cx,
15922 );
15923 }
15924 MultibufferSelectionMode::All => {
15925 editor.change_selections(
15926 SelectionEffects::no_scroll(),
15927 window,
15928 cx,
15929 |selections| {
15930 selections.clear_disjoint();
15931 selections.select_anchor_ranges(ranges);
15932 },
15933 );
15934 }
15935 }
15936 editor.register_buffers_with_language_servers(cx);
15937 });
15938
15939 let item = Box::new(editor);
15940 let item_id = item.item_id();
15941
15942 if split {
15943 workspace.split_item(SplitDirection::Right, item.clone(), window, cx);
15944 } else {
15945 if PreviewTabsSettings::get_global(cx).enable_preview_from_code_navigation {
15946 let (preview_item_id, preview_item_idx) =
15947 workspace.active_pane().read_with(cx, |pane, _| {
15948 (pane.preview_item_id(), pane.preview_item_idx())
15949 });
15950
15951 workspace.add_item_to_active_pane(item.clone(), preview_item_idx, true, window, cx);
15952
15953 if let Some(preview_item_id) = preview_item_id {
15954 workspace.active_pane().update(cx, |pane, cx| {
15955 pane.remove_item(preview_item_id, false, false, window, cx);
15956 });
15957 }
15958 } else {
15959 workspace.add_item_to_active_pane(item.clone(), None, true, window, cx);
15960 }
15961 }
15962 workspace.active_pane().update(cx, |pane, cx| {
15963 pane.set_preview_item_id(Some(item_id), cx);
15964 });
15965 }
15966
15967 pub fn rename(
15968 &mut self,
15969 _: &Rename,
15970 window: &mut Window,
15971 cx: &mut Context<Self>,
15972 ) -> Option<Task<Result<()>>> {
15973 use language::ToOffset as _;
15974
15975 let provider = self.semantics_provider.clone()?;
15976 let selection = self.selections.newest_anchor().clone();
15977 let (cursor_buffer, cursor_buffer_position) = self
15978 .buffer
15979 .read(cx)
15980 .text_anchor_for_position(selection.head(), cx)?;
15981 let (tail_buffer, cursor_buffer_position_end) = self
15982 .buffer
15983 .read(cx)
15984 .text_anchor_for_position(selection.tail(), cx)?;
15985 if tail_buffer != cursor_buffer {
15986 return None;
15987 }
15988
15989 let snapshot = cursor_buffer.read(cx).snapshot();
15990 let cursor_buffer_offset = cursor_buffer_position.to_offset(&snapshot);
15991 let cursor_buffer_offset_end = cursor_buffer_position_end.to_offset(&snapshot);
15992 let prepare_rename = provider
15993 .range_for_rename(&cursor_buffer, cursor_buffer_position, cx)
15994 .unwrap_or_else(|| Task::ready(Ok(None)));
15995 drop(snapshot);
15996
15997 Some(cx.spawn_in(window, async move |this, cx| {
15998 let rename_range = if let Some(range) = prepare_rename.await? {
15999 Some(range)
16000 } else {
16001 this.update(cx, |this, cx| {
16002 let buffer = this.buffer.read(cx).snapshot(cx);
16003 let mut buffer_highlights = this
16004 .document_highlights_for_position(selection.head(), &buffer)
16005 .filter(|highlight| {
16006 highlight.start.excerpt_id == selection.head().excerpt_id
16007 && highlight.end.excerpt_id == selection.head().excerpt_id
16008 });
16009 buffer_highlights
16010 .next()
16011 .map(|highlight| highlight.start.text_anchor..highlight.end.text_anchor)
16012 })?
16013 };
16014 if let Some(rename_range) = rename_range {
16015 this.update_in(cx, |this, window, cx| {
16016 let snapshot = cursor_buffer.read(cx).snapshot();
16017 let rename_buffer_range = rename_range.to_offset(&snapshot);
16018 let cursor_offset_in_rename_range =
16019 cursor_buffer_offset.saturating_sub(rename_buffer_range.start);
16020 let cursor_offset_in_rename_range_end =
16021 cursor_buffer_offset_end.saturating_sub(rename_buffer_range.start);
16022
16023 this.take_rename(false, window, cx);
16024 let buffer = this.buffer.read(cx).read(cx);
16025 let cursor_offset = selection.head().to_offset(&buffer);
16026 let rename_start = cursor_offset.saturating_sub(cursor_offset_in_rename_range);
16027 let rename_end = rename_start + rename_buffer_range.len();
16028 let range = buffer.anchor_before(rename_start)..buffer.anchor_after(rename_end);
16029 let mut old_highlight_id = None;
16030 let old_name: Arc<str> = buffer
16031 .chunks(rename_start..rename_end, true)
16032 .map(|chunk| {
16033 if old_highlight_id.is_none() {
16034 old_highlight_id = chunk.syntax_highlight_id;
16035 }
16036 chunk.text
16037 })
16038 .collect::<String>()
16039 .into();
16040
16041 drop(buffer);
16042
16043 // Position the selection in the rename editor so that it matches the current selection.
16044 this.show_local_selections = false;
16045 let rename_editor = cx.new(|cx| {
16046 let mut editor = Editor::single_line(window, cx);
16047 editor.buffer.update(cx, |buffer, cx| {
16048 buffer.edit([(0..0, old_name.clone())], None, cx)
16049 });
16050 let rename_selection_range = match cursor_offset_in_rename_range
16051 .cmp(&cursor_offset_in_rename_range_end)
16052 {
16053 Ordering::Equal => {
16054 editor.select_all(&SelectAll, window, cx);
16055 return editor;
16056 }
16057 Ordering::Less => {
16058 cursor_offset_in_rename_range..cursor_offset_in_rename_range_end
16059 }
16060 Ordering::Greater => {
16061 cursor_offset_in_rename_range_end..cursor_offset_in_rename_range
16062 }
16063 };
16064 if rename_selection_range.end > old_name.len() {
16065 editor.select_all(&SelectAll, window, cx);
16066 } else {
16067 editor.change_selections(Default::default(), window, cx, |s| {
16068 s.select_ranges([rename_selection_range]);
16069 });
16070 }
16071 editor
16072 });
16073 cx.subscribe(&rename_editor, |_, _, e: &EditorEvent, cx| {
16074 if e == &EditorEvent::Focused {
16075 cx.emit(EditorEvent::FocusedIn)
16076 }
16077 })
16078 .detach();
16079
16080 let write_highlights =
16081 this.clear_background_highlights::<DocumentHighlightWrite>(cx);
16082 let read_highlights =
16083 this.clear_background_highlights::<DocumentHighlightRead>(cx);
16084 let ranges = write_highlights
16085 .iter()
16086 .flat_map(|(_, ranges)| ranges.iter())
16087 .chain(read_highlights.iter().flat_map(|(_, ranges)| ranges.iter()))
16088 .cloned()
16089 .collect();
16090
16091 this.highlight_text::<Rename>(
16092 ranges,
16093 HighlightStyle {
16094 fade_out: Some(0.6),
16095 ..Default::default()
16096 },
16097 cx,
16098 );
16099 let rename_focus_handle = rename_editor.focus_handle(cx);
16100 window.focus(&rename_focus_handle);
16101 let block_id = this.insert_blocks(
16102 [BlockProperties {
16103 style: BlockStyle::Flex,
16104 placement: BlockPlacement::Below(range.start),
16105 height: Some(1),
16106 render: Arc::new({
16107 let rename_editor = rename_editor.clone();
16108 move |cx: &mut BlockContext| {
16109 let mut text_style = cx.editor_style.text.clone();
16110 if let Some(highlight_style) = old_highlight_id
16111 .and_then(|h| h.style(&cx.editor_style.syntax))
16112 {
16113 text_style = text_style.highlight(highlight_style);
16114 }
16115 div()
16116 .block_mouse_except_scroll()
16117 .pl(cx.anchor_x)
16118 .child(EditorElement::new(
16119 &rename_editor,
16120 EditorStyle {
16121 background: cx.theme().system().transparent,
16122 local_player: cx.editor_style.local_player,
16123 text: text_style,
16124 scrollbar_width: cx.editor_style.scrollbar_width,
16125 syntax: cx.editor_style.syntax.clone(),
16126 status: cx.editor_style.status.clone(),
16127 inlay_hints_style: HighlightStyle {
16128 font_weight: Some(FontWeight::BOLD),
16129 ..make_inlay_hints_style(cx.app)
16130 },
16131 inline_completion_styles: make_suggestion_styles(
16132 cx.app,
16133 ),
16134 ..EditorStyle::default()
16135 },
16136 ))
16137 .into_any_element()
16138 }
16139 }),
16140 priority: 0,
16141 render_in_minimap: true,
16142 }],
16143 Some(Autoscroll::fit()),
16144 cx,
16145 )[0];
16146 this.pending_rename = Some(RenameState {
16147 range,
16148 old_name,
16149 editor: rename_editor,
16150 block_id,
16151 });
16152 })?;
16153 }
16154
16155 Ok(())
16156 }))
16157 }
16158
16159 pub fn confirm_rename(
16160 &mut self,
16161 _: &ConfirmRename,
16162 window: &mut Window,
16163 cx: &mut Context<Self>,
16164 ) -> Option<Task<Result<()>>> {
16165 let rename = self.take_rename(false, window, cx)?;
16166 let workspace = self.workspace()?.downgrade();
16167 let (buffer, start) = self
16168 .buffer
16169 .read(cx)
16170 .text_anchor_for_position(rename.range.start, cx)?;
16171 let (end_buffer, _) = self
16172 .buffer
16173 .read(cx)
16174 .text_anchor_for_position(rename.range.end, cx)?;
16175 if buffer != end_buffer {
16176 return None;
16177 }
16178
16179 let old_name = rename.old_name;
16180 let new_name = rename.editor.read(cx).text(cx);
16181
16182 let rename = self.semantics_provider.as_ref()?.perform_rename(
16183 &buffer,
16184 start,
16185 new_name.clone(),
16186 cx,
16187 )?;
16188
16189 Some(cx.spawn_in(window, async move |editor, cx| {
16190 let project_transaction = rename.await?;
16191 Self::open_project_transaction(
16192 &editor,
16193 workspace,
16194 project_transaction,
16195 format!("Rename: {} → {}", old_name, new_name),
16196 cx,
16197 )
16198 .await?;
16199
16200 editor.update(cx, |editor, cx| {
16201 editor.refresh_document_highlights(cx);
16202 })?;
16203 Ok(())
16204 }))
16205 }
16206
16207 fn take_rename(
16208 &mut self,
16209 moving_cursor: bool,
16210 window: &mut Window,
16211 cx: &mut Context<Self>,
16212 ) -> Option<RenameState> {
16213 let rename = self.pending_rename.take()?;
16214 if rename.editor.focus_handle(cx).is_focused(window) {
16215 window.focus(&self.focus_handle);
16216 }
16217
16218 self.remove_blocks(
16219 [rename.block_id].into_iter().collect(),
16220 Some(Autoscroll::fit()),
16221 cx,
16222 );
16223 self.clear_highlights::<Rename>(cx);
16224 self.show_local_selections = true;
16225
16226 if moving_cursor {
16227 let cursor_in_rename_editor = rename.editor.update(cx, |editor, cx| {
16228 editor.selections.newest::<usize>(cx).head()
16229 });
16230
16231 // Update the selection to match the position of the selection inside
16232 // the rename editor.
16233 let snapshot = self.buffer.read(cx).read(cx);
16234 let rename_range = rename.range.to_offset(&snapshot);
16235 let cursor_in_editor = snapshot
16236 .clip_offset(rename_range.start + cursor_in_rename_editor, Bias::Left)
16237 .min(rename_range.end);
16238 drop(snapshot);
16239
16240 self.change_selections(SelectionEffects::no_scroll(), window, cx, |s| {
16241 s.select_ranges(vec![cursor_in_editor..cursor_in_editor])
16242 });
16243 } else {
16244 self.refresh_document_highlights(cx);
16245 }
16246
16247 Some(rename)
16248 }
16249
16250 pub fn pending_rename(&self) -> Option<&RenameState> {
16251 self.pending_rename.as_ref()
16252 }
16253
16254 fn format(
16255 &mut self,
16256 _: &Format,
16257 window: &mut Window,
16258 cx: &mut Context<Self>,
16259 ) -> Option<Task<Result<()>>> {
16260 self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
16261
16262 let project = match &self.project {
16263 Some(project) => project.clone(),
16264 None => return None,
16265 };
16266
16267 Some(self.perform_format(
16268 project,
16269 FormatTrigger::Manual,
16270 FormatTarget::Buffers(self.buffer.read(cx).all_buffers()),
16271 window,
16272 cx,
16273 ))
16274 }
16275
16276 fn format_selections(
16277 &mut self,
16278 _: &FormatSelections,
16279 window: &mut Window,
16280 cx: &mut Context<Self>,
16281 ) -> Option<Task<Result<()>>> {
16282 self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
16283
16284 let project = match &self.project {
16285 Some(project) => project.clone(),
16286 None => return None,
16287 };
16288
16289 let ranges = self
16290 .selections
16291 .all_adjusted(cx)
16292 .into_iter()
16293 .map(|selection| selection.range())
16294 .collect_vec();
16295
16296 Some(self.perform_format(
16297 project,
16298 FormatTrigger::Manual,
16299 FormatTarget::Ranges(ranges),
16300 window,
16301 cx,
16302 ))
16303 }
16304
16305 fn perform_format(
16306 &mut self,
16307 project: Entity<Project>,
16308 trigger: FormatTrigger,
16309 target: FormatTarget,
16310 window: &mut Window,
16311 cx: &mut Context<Self>,
16312 ) -> Task<Result<()>> {
16313 let buffer = self.buffer.clone();
16314 let (buffers, target) = match target {
16315 FormatTarget::Buffers(buffers) => (buffers, LspFormatTarget::Buffers),
16316 FormatTarget::Ranges(selection_ranges) => {
16317 let multi_buffer = buffer.read(cx);
16318 let snapshot = multi_buffer.read(cx);
16319 let mut buffers = HashSet::default();
16320 let mut buffer_id_to_ranges: BTreeMap<BufferId, Vec<Range<text::Anchor>>> =
16321 BTreeMap::new();
16322 for selection_range in selection_ranges {
16323 for (buffer, buffer_range, _) in
16324 snapshot.range_to_buffer_ranges(selection_range)
16325 {
16326 let buffer_id = buffer.remote_id();
16327 let start = buffer.anchor_before(buffer_range.start);
16328 let end = buffer.anchor_after(buffer_range.end);
16329 buffers.insert(multi_buffer.buffer(buffer_id).unwrap());
16330 buffer_id_to_ranges
16331 .entry(buffer_id)
16332 .and_modify(|buffer_ranges| buffer_ranges.push(start..end))
16333 .or_insert_with(|| vec![start..end]);
16334 }
16335 }
16336 (buffers, LspFormatTarget::Ranges(buffer_id_to_ranges))
16337 }
16338 };
16339
16340 let transaction_id_prev = buffer.read(cx).last_transaction_id(cx);
16341 let selections_prev = transaction_id_prev
16342 .and_then(|transaction_id_prev| {
16343 // default to selections as they were after the last edit, if we have them,
16344 // instead of how they are now.
16345 // This will make it so that editing, moving somewhere else, formatting, then undoing the format
16346 // will take you back to where you made the last edit, instead of staying where you scrolled
16347 self.selection_history
16348 .transaction(transaction_id_prev)
16349 .map(|t| t.0.clone())
16350 })
16351 .unwrap_or_else(|| {
16352 log::info!("Failed to determine selections from before format. Falling back to selections when format was initiated");
16353 self.selections.disjoint_anchors()
16354 });
16355
16356 let mut timeout = cx.background_executor().timer(FORMAT_TIMEOUT).fuse();
16357 let format = project.update(cx, |project, cx| {
16358 project.format(buffers, target, true, trigger, cx)
16359 });
16360
16361 cx.spawn_in(window, async move |editor, cx| {
16362 let transaction = futures::select_biased! {
16363 transaction = format.log_err().fuse() => transaction,
16364 () = timeout => {
16365 log::warn!("timed out waiting for formatting");
16366 None
16367 }
16368 };
16369
16370 buffer
16371 .update(cx, |buffer, cx| {
16372 if let Some(transaction) = transaction {
16373 if !buffer.is_singleton() {
16374 buffer.push_transaction(&transaction.0, cx);
16375 }
16376 }
16377 cx.notify();
16378 })
16379 .ok();
16380
16381 if let Some(transaction_id_now) =
16382 buffer.read_with(cx, |b, cx| b.last_transaction_id(cx))?
16383 {
16384 let has_new_transaction = transaction_id_prev != Some(transaction_id_now);
16385 if has_new_transaction {
16386 _ = editor.update(cx, |editor, _| {
16387 editor
16388 .selection_history
16389 .insert_transaction(transaction_id_now, selections_prev);
16390 });
16391 }
16392 }
16393
16394 Ok(())
16395 })
16396 }
16397
16398 fn organize_imports(
16399 &mut self,
16400 _: &OrganizeImports,
16401 window: &mut Window,
16402 cx: &mut Context<Self>,
16403 ) -> Option<Task<Result<()>>> {
16404 self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
16405 let project = match &self.project {
16406 Some(project) => project.clone(),
16407 None => return None,
16408 };
16409 Some(self.perform_code_action_kind(
16410 project,
16411 CodeActionKind::SOURCE_ORGANIZE_IMPORTS,
16412 window,
16413 cx,
16414 ))
16415 }
16416
16417 fn perform_code_action_kind(
16418 &mut self,
16419 project: Entity<Project>,
16420 kind: CodeActionKind,
16421 window: &mut Window,
16422 cx: &mut Context<Self>,
16423 ) -> Task<Result<()>> {
16424 let buffer = self.buffer.clone();
16425 let buffers = buffer.read(cx).all_buffers();
16426 let mut timeout = cx.background_executor().timer(CODE_ACTION_TIMEOUT).fuse();
16427 let apply_action = project.update(cx, |project, cx| {
16428 project.apply_code_action_kind(buffers, kind, true, cx)
16429 });
16430 cx.spawn_in(window, async move |_, cx| {
16431 let transaction = futures::select_biased! {
16432 () = timeout => {
16433 log::warn!("timed out waiting for executing code action");
16434 None
16435 }
16436 transaction = apply_action.log_err().fuse() => transaction,
16437 };
16438 buffer
16439 .update(cx, |buffer, cx| {
16440 // check if we need this
16441 if let Some(transaction) = transaction {
16442 if !buffer.is_singleton() {
16443 buffer.push_transaction(&transaction.0, cx);
16444 }
16445 }
16446 cx.notify();
16447 })
16448 .ok();
16449 Ok(())
16450 })
16451 }
16452
16453 pub fn restart_language_server(
16454 &mut self,
16455 _: &RestartLanguageServer,
16456 _: &mut Window,
16457 cx: &mut Context<Self>,
16458 ) {
16459 if let Some(project) = self.project.clone() {
16460 self.buffer.update(cx, |multi_buffer, cx| {
16461 project.update(cx, |project, cx| {
16462 project.restart_language_servers_for_buffers(
16463 multi_buffer.all_buffers().into_iter().collect(),
16464 HashSet::default(),
16465 cx,
16466 );
16467 });
16468 })
16469 }
16470 }
16471
16472 pub fn stop_language_server(
16473 &mut self,
16474 _: &StopLanguageServer,
16475 _: &mut Window,
16476 cx: &mut Context<Self>,
16477 ) {
16478 if let Some(project) = self.project.clone() {
16479 self.buffer.update(cx, |multi_buffer, cx| {
16480 project.update(cx, |project, cx| {
16481 project.stop_language_servers_for_buffers(
16482 multi_buffer.all_buffers().into_iter().collect(),
16483 HashSet::default(),
16484 cx,
16485 );
16486 cx.emit(project::Event::RefreshInlayHints);
16487 });
16488 });
16489 }
16490 }
16491
16492 fn cancel_language_server_work(
16493 workspace: &mut Workspace,
16494 _: &actions::CancelLanguageServerWork,
16495 _: &mut Window,
16496 cx: &mut Context<Workspace>,
16497 ) {
16498 let project = workspace.project();
16499 let buffers = workspace
16500 .active_item(cx)
16501 .and_then(|item| item.act_as::<Editor>(cx))
16502 .map_or(HashSet::default(), |editor| {
16503 editor.read(cx).buffer.read(cx).all_buffers()
16504 });
16505 project.update(cx, |project, cx| {
16506 project.cancel_language_server_work_for_buffers(buffers, cx);
16507 });
16508 }
16509
16510 fn show_character_palette(
16511 &mut self,
16512 _: &ShowCharacterPalette,
16513 window: &mut Window,
16514 _: &mut Context<Self>,
16515 ) {
16516 window.show_character_palette();
16517 }
16518
16519 fn refresh_active_diagnostics(&mut self, cx: &mut Context<Editor>) {
16520 if !self.diagnostics_enabled() {
16521 return;
16522 }
16523
16524 if let ActiveDiagnostic::Group(active_diagnostics) = &mut self.active_diagnostics {
16525 let buffer = self.buffer.read(cx).snapshot(cx);
16526 let primary_range_start = active_diagnostics.active_range.start.to_offset(&buffer);
16527 let primary_range_end = active_diagnostics.active_range.end.to_offset(&buffer);
16528 let is_valid = buffer
16529 .diagnostics_in_range::<usize>(primary_range_start..primary_range_end)
16530 .any(|entry| {
16531 entry.diagnostic.is_primary
16532 && !entry.range.is_empty()
16533 && entry.range.start == primary_range_start
16534 && entry.diagnostic.message == active_diagnostics.active_message
16535 });
16536
16537 if !is_valid {
16538 self.dismiss_diagnostics(cx);
16539 }
16540 }
16541 }
16542
16543 pub fn active_diagnostic_group(&self) -> Option<&ActiveDiagnosticGroup> {
16544 match &self.active_diagnostics {
16545 ActiveDiagnostic::Group(group) => Some(group),
16546 _ => None,
16547 }
16548 }
16549
16550 pub fn set_all_diagnostics_active(&mut self, cx: &mut Context<Self>) {
16551 if !self.diagnostics_enabled() {
16552 return;
16553 }
16554 self.dismiss_diagnostics(cx);
16555 self.active_diagnostics = ActiveDiagnostic::All;
16556 }
16557
16558 fn activate_diagnostics(
16559 &mut self,
16560 buffer_id: BufferId,
16561 diagnostic: DiagnosticEntry<usize>,
16562 window: &mut Window,
16563 cx: &mut Context<Self>,
16564 ) {
16565 if !self.diagnostics_enabled() || matches!(self.active_diagnostics, ActiveDiagnostic::All) {
16566 return;
16567 }
16568 self.dismiss_diagnostics(cx);
16569 let snapshot = self.snapshot(window, cx);
16570 let buffer = self.buffer.read(cx).snapshot(cx);
16571 let Some(renderer) = GlobalDiagnosticRenderer::global(cx) else {
16572 return;
16573 };
16574
16575 let diagnostic_group = buffer
16576 .diagnostic_group(buffer_id, diagnostic.diagnostic.group_id)
16577 .collect::<Vec<_>>();
16578
16579 let blocks =
16580 renderer.render_group(diagnostic_group, buffer_id, snapshot, cx.weak_entity(), cx);
16581
16582 let blocks = self.display_map.update(cx, |display_map, cx| {
16583 display_map.insert_blocks(blocks, cx).into_iter().collect()
16584 });
16585 self.active_diagnostics = ActiveDiagnostic::Group(ActiveDiagnosticGroup {
16586 active_range: buffer.anchor_before(diagnostic.range.start)
16587 ..buffer.anchor_after(diagnostic.range.end),
16588 active_message: diagnostic.diagnostic.message.clone(),
16589 group_id: diagnostic.diagnostic.group_id,
16590 blocks,
16591 });
16592 cx.notify();
16593 }
16594
16595 fn dismiss_diagnostics(&mut self, cx: &mut Context<Self>) {
16596 if matches!(self.active_diagnostics, ActiveDiagnostic::All) {
16597 return;
16598 };
16599
16600 let prev = mem::replace(&mut self.active_diagnostics, ActiveDiagnostic::None);
16601 if let ActiveDiagnostic::Group(group) = prev {
16602 self.display_map.update(cx, |display_map, cx| {
16603 display_map.remove_blocks(group.blocks, cx);
16604 });
16605 cx.notify();
16606 }
16607 }
16608
16609 /// Disable inline diagnostics rendering for this editor.
16610 pub fn disable_inline_diagnostics(&mut self) {
16611 self.inline_diagnostics_enabled = false;
16612 self.inline_diagnostics_update = Task::ready(());
16613 self.inline_diagnostics.clear();
16614 }
16615
16616 pub fn disable_diagnostics(&mut self, cx: &mut Context<Self>) {
16617 self.diagnostics_enabled = false;
16618 self.dismiss_diagnostics(cx);
16619 self.inline_diagnostics_update = Task::ready(());
16620 self.inline_diagnostics.clear();
16621 }
16622
16623 pub fn diagnostics_enabled(&self) -> bool {
16624 self.diagnostics_enabled && self.mode.is_full()
16625 }
16626
16627 pub fn inline_diagnostics_enabled(&self) -> bool {
16628 self.inline_diagnostics_enabled && self.diagnostics_enabled()
16629 }
16630
16631 pub fn show_inline_diagnostics(&self) -> bool {
16632 self.show_inline_diagnostics
16633 }
16634
16635 pub fn toggle_inline_diagnostics(
16636 &mut self,
16637 _: &ToggleInlineDiagnostics,
16638 window: &mut Window,
16639 cx: &mut Context<Editor>,
16640 ) {
16641 self.show_inline_diagnostics = !self.show_inline_diagnostics;
16642 self.refresh_inline_diagnostics(false, window, cx);
16643 }
16644
16645 pub fn set_max_diagnostics_severity(&mut self, severity: DiagnosticSeverity, cx: &mut App) {
16646 self.diagnostics_max_severity = severity;
16647 self.display_map.update(cx, |display_map, _| {
16648 display_map.diagnostics_max_severity = self.diagnostics_max_severity;
16649 });
16650 }
16651
16652 pub fn toggle_diagnostics(
16653 &mut self,
16654 _: &ToggleDiagnostics,
16655 window: &mut Window,
16656 cx: &mut Context<Editor>,
16657 ) {
16658 if !self.diagnostics_enabled() {
16659 return;
16660 }
16661
16662 let new_severity = if self.diagnostics_max_severity == DiagnosticSeverity::Off {
16663 EditorSettings::get_global(cx)
16664 .diagnostics_max_severity
16665 .filter(|severity| severity != &DiagnosticSeverity::Off)
16666 .unwrap_or(DiagnosticSeverity::Hint)
16667 } else {
16668 DiagnosticSeverity::Off
16669 };
16670 self.set_max_diagnostics_severity(new_severity, cx);
16671 if self.diagnostics_max_severity == DiagnosticSeverity::Off {
16672 self.active_diagnostics = ActiveDiagnostic::None;
16673 self.inline_diagnostics_update = Task::ready(());
16674 self.inline_diagnostics.clear();
16675 } else {
16676 self.refresh_inline_diagnostics(false, window, cx);
16677 }
16678
16679 cx.notify();
16680 }
16681
16682 pub fn toggle_minimap(
16683 &mut self,
16684 _: &ToggleMinimap,
16685 window: &mut Window,
16686 cx: &mut Context<Editor>,
16687 ) {
16688 if self.supports_minimap(cx) {
16689 self.set_minimap_visibility(self.minimap_visibility.toggle_visibility(), window, cx);
16690 }
16691 }
16692
16693 fn refresh_inline_diagnostics(
16694 &mut self,
16695 debounce: bool,
16696 window: &mut Window,
16697 cx: &mut Context<Self>,
16698 ) {
16699 let max_severity = ProjectSettings::get_global(cx)
16700 .diagnostics
16701 .inline
16702 .max_severity
16703 .unwrap_or(self.diagnostics_max_severity);
16704
16705 if !self.inline_diagnostics_enabled()
16706 || !self.show_inline_diagnostics
16707 || max_severity == DiagnosticSeverity::Off
16708 {
16709 self.inline_diagnostics_update = Task::ready(());
16710 self.inline_diagnostics.clear();
16711 return;
16712 }
16713
16714 let debounce_ms = ProjectSettings::get_global(cx)
16715 .diagnostics
16716 .inline
16717 .update_debounce_ms;
16718 let debounce = if debounce && debounce_ms > 0 {
16719 Some(Duration::from_millis(debounce_ms))
16720 } else {
16721 None
16722 };
16723 self.inline_diagnostics_update = cx.spawn_in(window, async move |editor, cx| {
16724 if let Some(debounce) = debounce {
16725 cx.background_executor().timer(debounce).await;
16726 }
16727 let Some(snapshot) = editor.upgrade().and_then(|editor| {
16728 editor
16729 .update(cx, |editor, cx| editor.buffer().read(cx).snapshot(cx))
16730 .ok()
16731 }) else {
16732 return;
16733 };
16734
16735 let new_inline_diagnostics = cx
16736 .background_spawn(async move {
16737 let mut inline_diagnostics = Vec::<(Anchor, InlineDiagnostic)>::new();
16738 for diagnostic_entry in snapshot.diagnostics_in_range(0..snapshot.len()) {
16739 let message = diagnostic_entry
16740 .diagnostic
16741 .message
16742 .split_once('\n')
16743 .map(|(line, _)| line)
16744 .map(SharedString::new)
16745 .unwrap_or_else(|| {
16746 SharedString::from(diagnostic_entry.diagnostic.message)
16747 });
16748 let start_anchor = snapshot.anchor_before(diagnostic_entry.range.start);
16749 let (Ok(i) | Err(i)) = inline_diagnostics
16750 .binary_search_by(|(probe, _)| probe.cmp(&start_anchor, &snapshot));
16751 inline_diagnostics.insert(
16752 i,
16753 (
16754 start_anchor,
16755 InlineDiagnostic {
16756 message,
16757 group_id: diagnostic_entry.diagnostic.group_id,
16758 start: diagnostic_entry.range.start.to_point(&snapshot),
16759 is_primary: diagnostic_entry.diagnostic.is_primary,
16760 severity: diagnostic_entry.diagnostic.severity,
16761 },
16762 ),
16763 );
16764 }
16765 inline_diagnostics
16766 })
16767 .await;
16768
16769 editor
16770 .update(cx, |editor, cx| {
16771 editor.inline_diagnostics = new_inline_diagnostics;
16772 cx.notify();
16773 })
16774 .ok();
16775 });
16776 }
16777
16778 fn pull_diagnostics(
16779 &mut self,
16780 buffer_id: Option<BufferId>,
16781 window: &Window,
16782 cx: &mut Context<Self>,
16783 ) -> Option<()> {
16784 if !self.mode().is_full() {
16785 return None;
16786 }
16787 let pull_diagnostics_settings = ProjectSettings::get_global(cx)
16788 .diagnostics
16789 .lsp_pull_diagnostics;
16790 if !pull_diagnostics_settings.enabled {
16791 return None;
16792 }
16793 let project = self.project.as_ref()?.downgrade();
16794 let debounce = Duration::from_millis(pull_diagnostics_settings.debounce_ms);
16795 let mut buffers = self.buffer.read(cx).all_buffers();
16796 if let Some(buffer_id) = buffer_id {
16797 buffers.retain(|buffer| buffer.read(cx).remote_id() == buffer_id);
16798 }
16799
16800 self.pull_diagnostics_task = cx.spawn_in(window, async move |editor, cx| {
16801 cx.background_executor().timer(debounce).await;
16802
16803 let Ok(mut pull_diagnostics_tasks) = cx.update(|_, cx| {
16804 buffers
16805 .into_iter()
16806 .filter_map(|buffer| {
16807 project
16808 .update(cx, |project, cx| {
16809 project.lsp_store().update(cx, |lsp_store, cx| {
16810 lsp_store.pull_diagnostics_for_buffer(buffer, cx)
16811 })
16812 })
16813 .ok()
16814 })
16815 .collect::<FuturesUnordered<_>>()
16816 }) else {
16817 return;
16818 };
16819
16820 while let Some(pull_task) = pull_diagnostics_tasks.next().await {
16821 match pull_task {
16822 Ok(()) => {
16823 if editor
16824 .update_in(cx, |editor, window, cx| {
16825 editor.update_diagnostics_state(window, cx);
16826 })
16827 .is_err()
16828 {
16829 return;
16830 }
16831 }
16832 Err(e) => log::error!("Failed to update project diagnostics: {e:#}"),
16833 }
16834 }
16835 });
16836
16837 Some(())
16838 }
16839
16840 pub fn set_selections_from_remote(
16841 &mut self,
16842 selections: Vec<Selection<Anchor>>,
16843 pending_selection: Option<Selection<Anchor>>,
16844 window: &mut Window,
16845 cx: &mut Context<Self>,
16846 ) {
16847 let old_cursor_position = self.selections.newest_anchor().head();
16848 self.selections.change_with(cx, |s| {
16849 s.select_anchors(selections);
16850 if let Some(pending_selection) = pending_selection {
16851 s.set_pending(pending_selection, SelectMode::Character);
16852 } else {
16853 s.clear_pending();
16854 }
16855 });
16856 self.selections_did_change(
16857 false,
16858 &old_cursor_position,
16859 SelectionEffects::default(),
16860 window,
16861 cx,
16862 );
16863 }
16864
16865 pub fn transact(
16866 &mut self,
16867 window: &mut Window,
16868 cx: &mut Context<Self>,
16869 update: impl FnOnce(&mut Self, &mut Window, &mut Context<Self>),
16870 ) -> Option<TransactionId> {
16871 self.with_selection_effects_deferred(window, cx, |this, window, cx| {
16872 this.start_transaction_at(Instant::now(), window, cx);
16873 update(this, window, cx);
16874 this.end_transaction_at(Instant::now(), cx)
16875 })
16876 }
16877
16878 pub fn start_transaction_at(
16879 &mut self,
16880 now: Instant,
16881 window: &mut Window,
16882 cx: &mut Context<Self>,
16883 ) {
16884 self.end_selection(window, cx);
16885 if let Some(tx_id) = self
16886 .buffer
16887 .update(cx, |buffer, cx| buffer.start_transaction_at(now, cx))
16888 {
16889 self.selection_history
16890 .insert_transaction(tx_id, self.selections.disjoint_anchors());
16891 cx.emit(EditorEvent::TransactionBegun {
16892 transaction_id: tx_id,
16893 })
16894 }
16895 }
16896
16897 pub fn end_transaction_at(
16898 &mut self,
16899 now: Instant,
16900 cx: &mut Context<Self>,
16901 ) -> Option<TransactionId> {
16902 if let Some(transaction_id) = self
16903 .buffer
16904 .update(cx, |buffer, cx| buffer.end_transaction_at(now, cx))
16905 {
16906 if let Some((_, end_selections)) =
16907 self.selection_history.transaction_mut(transaction_id)
16908 {
16909 *end_selections = Some(self.selections.disjoint_anchors());
16910 } else {
16911 log::error!("unexpectedly ended a transaction that wasn't started by this editor");
16912 }
16913
16914 cx.emit(EditorEvent::Edited { transaction_id });
16915 Some(transaction_id)
16916 } else {
16917 None
16918 }
16919 }
16920
16921 pub fn set_mark(&mut self, _: &actions::SetMark, window: &mut Window, cx: &mut Context<Self>) {
16922 if self.selection_mark_mode {
16923 self.change_selections(SelectionEffects::no_scroll(), window, cx, |s| {
16924 s.move_with(|_, sel| {
16925 sel.collapse_to(sel.head(), SelectionGoal::None);
16926 });
16927 })
16928 }
16929 self.selection_mark_mode = true;
16930 cx.notify();
16931 }
16932
16933 pub fn swap_selection_ends(
16934 &mut self,
16935 _: &actions::SwapSelectionEnds,
16936 window: &mut Window,
16937 cx: &mut Context<Self>,
16938 ) {
16939 self.change_selections(SelectionEffects::no_scroll(), window, cx, |s| {
16940 s.move_with(|_, sel| {
16941 if sel.start != sel.end {
16942 sel.reversed = !sel.reversed
16943 }
16944 });
16945 });
16946 self.request_autoscroll(Autoscroll::newest(), cx);
16947 cx.notify();
16948 }
16949
16950 pub fn toggle_fold(
16951 &mut self,
16952 _: &actions::ToggleFold,
16953 window: &mut Window,
16954 cx: &mut Context<Self>,
16955 ) {
16956 if self.is_singleton(cx) {
16957 let selection = self.selections.newest::<Point>(cx);
16958
16959 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
16960 let range = if selection.is_empty() {
16961 let point = selection.head().to_display_point(&display_map);
16962 let start = DisplayPoint::new(point.row(), 0).to_point(&display_map);
16963 let end = DisplayPoint::new(point.row(), display_map.line_len(point.row()))
16964 .to_point(&display_map);
16965 start..end
16966 } else {
16967 selection.range()
16968 };
16969 if display_map.folds_in_range(range).next().is_some() {
16970 self.unfold_lines(&Default::default(), window, cx)
16971 } else {
16972 self.fold(&Default::default(), window, cx)
16973 }
16974 } else {
16975 let multi_buffer_snapshot = self.buffer.read(cx).snapshot(cx);
16976 let buffer_ids: HashSet<_> = self
16977 .selections
16978 .disjoint_anchor_ranges()
16979 .flat_map(|range| multi_buffer_snapshot.buffer_ids_for_range(range))
16980 .collect();
16981
16982 let should_unfold = buffer_ids
16983 .iter()
16984 .any(|buffer_id| self.is_buffer_folded(*buffer_id, cx));
16985
16986 for buffer_id in buffer_ids {
16987 if should_unfold {
16988 self.unfold_buffer(buffer_id, cx);
16989 } else {
16990 self.fold_buffer(buffer_id, cx);
16991 }
16992 }
16993 }
16994 }
16995
16996 pub fn toggle_fold_recursive(
16997 &mut self,
16998 _: &actions::ToggleFoldRecursive,
16999 window: &mut Window,
17000 cx: &mut Context<Self>,
17001 ) {
17002 let selection = self.selections.newest::<Point>(cx);
17003
17004 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
17005 let range = if selection.is_empty() {
17006 let point = selection.head().to_display_point(&display_map);
17007 let start = DisplayPoint::new(point.row(), 0).to_point(&display_map);
17008 let end = DisplayPoint::new(point.row(), display_map.line_len(point.row()))
17009 .to_point(&display_map);
17010 start..end
17011 } else {
17012 selection.range()
17013 };
17014 if display_map.folds_in_range(range).next().is_some() {
17015 self.unfold_recursive(&Default::default(), window, cx)
17016 } else {
17017 self.fold_recursive(&Default::default(), window, cx)
17018 }
17019 }
17020
17021 pub fn fold(&mut self, _: &actions::Fold, window: &mut Window, cx: &mut Context<Self>) {
17022 if self.is_singleton(cx) {
17023 let mut to_fold = Vec::new();
17024 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
17025 let selections = self.selections.all_adjusted(cx);
17026
17027 for selection in selections {
17028 let range = selection.range().sorted();
17029 let buffer_start_row = range.start.row;
17030
17031 if range.start.row != range.end.row {
17032 let mut found = false;
17033 let mut row = range.start.row;
17034 while row <= range.end.row {
17035 if let Some(crease) = display_map.crease_for_buffer_row(MultiBufferRow(row))
17036 {
17037 found = true;
17038 row = crease.range().end.row + 1;
17039 to_fold.push(crease);
17040 } else {
17041 row += 1
17042 }
17043 }
17044 if found {
17045 continue;
17046 }
17047 }
17048
17049 for row in (0..=range.start.row).rev() {
17050 if let Some(crease) = display_map.crease_for_buffer_row(MultiBufferRow(row)) {
17051 if crease.range().end.row >= buffer_start_row {
17052 to_fold.push(crease);
17053 if row <= range.start.row {
17054 break;
17055 }
17056 }
17057 }
17058 }
17059 }
17060
17061 self.fold_creases(to_fold, true, window, cx);
17062 } else {
17063 let multi_buffer_snapshot = self.buffer.read(cx).snapshot(cx);
17064 let buffer_ids = self
17065 .selections
17066 .disjoint_anchor_ranges()
17067 .flat_map(|range| multi_buffer_snapshot.buffer_ids_for_range(range))
17068 .collect::<HashSet<_>>();
17069 for buffer_id in buffer_ids {
17070 self.fold_buffer(buffer_id, cx);
17071 }
17072 }
17073 }
17074
17075 fn fold_at_level(
17076 &mut self,
17077 fold_at: &FoldAtLevel,
17078 window: &mut Window,
17079 cx: &mut Context<Self>,
17080 ) {
17081 if !self.buffer.read(cx).is_singleton() {
17082 return;
17083 }
17084
17085 let fold_at_level = fold_at.0;
17086 let snapshot = self.buffer.read(cx).snapshot(cx);
17087 let mut to_fold = Vec::new();
17088 let mut stack = vec![(0, snapshot.max_row().0, 1)];
17089
17090 while let Some((mut start_row, end_row, current_level)) = stack.pop() {
17091 while start_row < end_row {
17092 match self
17093 .snapshot(window, cx)
17094 .crease_for_buffer_row(MultiBufferRow(start_row))
17095 {
17096 Some(crease) => {
17097 let nested_start_row = crease.range().start.row + 1;
17098 let nested_end_row = crease.range().end.row;
17099
17100 if current_level < fold_at_level {
17101 stack.push((nested_start_row, nested_end_row, current_level + 1));
17102 } else if current_level == fold_at_level {
17103 to_fold.push(crease);
17104 }
17105
17106 start_row = nested_end_row + 1;
17107 }
17108 None => start_row += 1,
17109 }
17110 }
17111 }
17112
17113 self.fold_creases(to_fold, true, window, cx);
17114 }
17115
17116 pub fn fold_all(&mut self, _: &actions::FoldAll, window: &mut Window, cx: &mut Context<Self>) {
17117 if self.buffer.read(cx).is_singleton() {
17118 let mut fold_ranges = Vec::new();
17119 let snapshot = self.buffer.read(cx).snapshot(cx);
17120
17121 for row in 0..snapshot.max_row().0 {
17122 if let Some(foldable_range) = self
17123 .snapshot(window, cx)
17124 .crease_for_buffer_row(MultiBufferRow(row))
17125 {
17126 fold_ranges.push(foldable_range);
17127 }
17128 }
17129
17130 self.fold_creases(fold_ranges, true, window, cx);
17131 } else {
17132 self.toggle_fold_multiple_buffers = cx.spawn_in(window, async move |editor, cx| {
17133 editor
17134 .update_in(cx, |editor, _, cx| {
17135 for buffer_id in editor.buffer.read(cx).excerpt_buffer_ids() {
17136 editor.fold_buffer(buffer_id, cx);
17137 }
17138 })
17139 .ok();
17140 });
17141 }
17142 }
17143
17144 pub fn fold_function_bodies(
17145 &mut self,
17146 _: &actions::FoldFunctionBodies,
17147 window: &mut Window,
17148 cx: &mut Context<Self>,
17149 ) {
17150 let snapshot = self.buffer.read(cx).snapshot(cx);
17151
17152 let ranges = snapshot
17153 .text_object_ranges(0..snapshot.len(), TreeSitterOptions::default())
17154 .filter_map(|(range, obj)| (obj == TextObject::InsideFunction).then_some(range))
17155 .collect::<Vec<_>>();
17156
17157 let creases = ranges
17158 .into_iter()
17159 .map(|range| Crease::simple(range, self.display_map.read(cx).fold_placeholder.clone()))
17160 .collect();
17161
17162 self.fold_creases(creases, true, window, cx);
17163 }
17164
17165 pub fn fold_recursive(
17166 &mut self,
17167 _: &actions::FoldRecursive,
17168 window: &mut Window,
17169 cx: &mut Context<Self>,
17170 ) {
17171 let mut to_fold = Vec::new();
17172 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
17173 let selections = self.selections.all_adjusted(cx);
17174
17175 for selection in selections {
17176 let range = selection.range().sorted();
17177 let buffer_start_row = range.start.row;
17178
17179 if range.start.row != range.end.row {
17180 let mut found = false;
17181 for row in range.start.row..=range.end.row {
17182 if let Some(crease) = display_map.crease_for_buffer_row(MultiBufferRow(row)) {
17183 found = true;
17184 to_fold.push(crease);
17185 }
17186 }
17187 if found {
17188 continue;
17189 }
17190 }
17191
17192 for row in (0..=range.start.row).rev() {
17193 if let Some(crease) = display_map.crease_for_buffer_row(MultiBufferRow(row)) {
17194 if crease.range().end.row >= buffer_start_row {
17195 to_fold.push(crease);
17196 } else {
17197 break;
17198 }
17199 }
17200 }
17201 }
17202
17203 self.fold_creases(to_fold, true, window, cx);
17204 }
17205
17206 pub fn fold_at(
17207 &mut self,
17208 buffer_row: MultiBufferRow,
17209 window: &mut Window,
17210 cx: &mut Context<Self>,
17211 ) {
17212 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
17213
17214 if let Some(crease) = display_map.crease_for_buffer_row(buffer_row) {
17215 let autoscroll = self
17216 .selections
17217 .all::<Point>(cx)
17218 .iter()
17219 .any(|selection| crease.range().overlaps(&selection.range()));
17220
17221 self.fold_creases(vec![crease], autoscroll, window, cx);
17222 }
17223 }
17224
17225 pub fn unfold_lines(&mut self, _: &UnfoldLines, _window: &mut Window, cx: &mut Context<Self>) {
17226 if self.is_singleton(cx) {
17227 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
17228 let buffer = &display_map.buffer_snapshot;
17229 let selections = self.selections.all::<Point>(cx);
17230 let ranges = selections
17231 .iter()
17232 .map(|s| {
17233 let range = s.display_range(&display_map).sorted();
17234 let mut start = range.start.to_point(&display_map);
17235 let mut end = range.end.to_point(&display_map);
17236 start.column = 0;
17237 end.column = buffer.line_len(MultiBufferRow(end.row));
17238 start..end
17239 })
17240 .collect::<Vec<_>>();
17241
17242 self.unfold_ranges(&ranges, true, true, cx);
17243 } else {
17244 let multi_buffer_snapshot = self.buffer.read(cx).snapshot(cx);
17245 let buffer_ids = self
17246 .selections
17247 .disjoint_anchor_ranges()
17248 .flat_map(|range| multi_buffer_snapshot.buffer_ids_for_range(range))
17249 .collect::<HashSet<_>>();
17250 for buffer_id in buffer_ids {
17251 self.unfold_buffer(buffer_id, cx);
17252 }
17253 }
17254 }
17255
17256 pub fn unfold_recursive(
17257 &mut self,
17258 _: &UnfoldRecursive,
17259 _window: &mut Window,
17260 cx: &mut Context<Self>,
17261 ) {
17262 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
17263 let selections = self.selections.all::<Point>(cx);
17264 let ranges = selections
17265 .iter()
17266 .map(|s| {
17267 let mut range = s.display_range(&display_map).sorted();
17268 *range.start.column_mut() = 0;
17269 *range.end.column_mut() = display_map.line_len(range.end.row());
17270 let start = range.start.to_point(&display_map);
17271 let end = range.end.to_point(&display_map);
17272 start..end
17273 })
17274 .collect::<Vec<_>>();
17275
17276 self.unfold_ranges(&ranges, true, true, cx);
17277 }
17278
17279 pub fn unfold_at(
17280 &mut self,
17281 buffer_row: MultiBufferRow,
17282 _window: &mut Window,
17283 cx: &mut Context<Self>,
17284 ) {
17285 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
17286
17287 let intersection_range = Point::new(buffer_row.0, 0)
17288 ..Point::new(
17289 buffer_row.0,
17290 display_map.buffer_snapshot.line_len(buffer_row),
17291 );
17292
17293 let autoscroll = self
17294 .selections
17295 .all::<Point>(cx)
17296 .iter()
17297 .any(|selection| RangeExt::overlaps(&selection.range(), &intersection_range));
17298
17299 self.unfold_ranges(&[intersection_range], true, autoscroll, cx);
17300 }
17301
17302 pub fn unfold_all(
17303 &mut self,
17304 _: &actions::UnfoldAll,
17305 _window: &mut Window,
17306 cx: &mut Context<Self>,
17307 ) {
17308 if self.buffer.read(cx).is_singleton() {
17309 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
17310 self.unfold_ranges(&[0..display_map.buffer_snapshot.len()], true, true, cx);
17311 } else {
17312 self.toggle_fold_multiple_buffers = cx.spawn(async move |editor, cx| {
17313 editor
17314 .update(cx, |editor, cx| {
17315 for buffer_id in editor.buffer.read(cx).excerpt_buffer_ids() {
17316 editor.unfold_buffer(buffer_id, cx);
17317 }
17318 })
17319 .ok();
17320 });
17321 }
17322 }
17323
17324 pub fn fold_selected_ranges(
17325 &mut self,
17326 _: &FoldSelectedRanges,
17327 window: &mut Window,
17328 cx: &mut Context<Self>,
17329 ) {
17330 let selections = self.selections.all_adjusted(cx);
17331 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
17332 let ranges = selections
17333 .into_iter()
17334 .map(|s| Crease::simple(s.range(), display_map.fold_placeholder.clone()))
17335 .collect::<Vec<_>>();
17336 self.fold_creases(ranges, true, window, cx);
17337 }
17338
17339 pub fn fold_ranges<T: ToOffset + Clone>(
17340 &mut self,
17341 ranges: Vec<Range<T>>,
17342 auto_scroll: bool,
17343 window: &mut Window,
17344 cx: &mut Context<Self>,
17345 ) {
17346 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
17347 let ranges = ranges
17348 .into_iter()
17349 .map(|r| Crease::simple(r, display_map.fold_placeholder.clone()))
17350 .collect::<Vec<_>>();
17351 self.fold_creases(ranges, auto_scroll, window, cx);
17352 }
17353
17354 pub fn fold_creases<T: ToOffset + Clone>(
17355 &mut self,
17356 creases: Vec<Crease<T>>,
17357 auto_scroll: bool,
17358 _window: &mut Window,
17359 cx: &mut Context<Self>,
17360 ) {
17361 if creases.is_empty() {
17362 return;
17363 }
17364
17365 self.display_map.update(cx, |map, cx| map.fold(creases, cx));
17366
17367 if auto_scroll {
17368 self.request_autoscroll(Autoscroll::fit(), cx);
17369 }
17370
17371 cx.notify();
17372
17373 self.scrollbar_marker_state.dirty = true;
17374 self.folds_did_change(cx);
17375 }
17376
17377 /// Removes any folds whose ranges intersect any of the given ranges.
17378 pub fn unfold_ranges<T: ToOffset + Clone>(
17379 &mut self,
17380 ranges: &[Range<T>],
17381 inclusive: bool,
17382 auto_scroll: bool,
17383 cx: &mut Context<Self>,
17384 ) {
17385 self.remove_folds_with(ranges, auto_scroll, cx, |map, cx| {
17386 map.unfold_intersecting(ranges.iter().cloned(), inclusive, cx)
17387 });
17388 self.folds_did_change(cx);
17389 }
17390
17391 pub fn fold_buffer(&mut self, buffer_id: BufferId, cx: &mut Context<Self>) {
17392 if self.buffer().read(cx).is_singleton() || self.is_buffer_folded(buffer_id, cx) {
17393 return;
17394 }
17395 let folded_excerpts = self.buffer().read(cx).excerpts_for_buffer(buffer_id, cx);
17396 self.display_map.update(cx, |display_map, cx| {
17397 display_map.fold_buffers([buffer_id], cx)
17398 });
17399 cx.emit(EditorEvent::BufferFoldToggled {
17400 ids: folded_excerpts.iter().map(|&(id, _)| id).collect(),
17401 folded: true,
17402 });
17403 cx.notify();
17404 }
17405
17406 pub fn unfold_buffer(&mut self, buffer_id: BufferId, cx: &mut Context<Self>) {
17407 if self.buffer().read(cx).is_singleton() || !self.is_buffer_folded(buffer_id, cx) {
17408 return;
17409 }
17410 let unfolded_excerpts = self.buffer().read(cx).excerpts_for_buffer(buffer_id, cx);
17411 self.display_map.update(cx, |display_map, cx| {
17412 display_map.unfold_buffers([buffer_id], cx);
17413 });
17414 cx.emit(EditorEvent::BufferFoldToggled {
17415 ids: unfolded_excerpts.iter().map(|&(id, _)| id).collect(),
17416 folded: false,
17417 });
17418 cx.notify();
17419 }
17420
17421 pub fn is_buffer_folded(&self, buffer: BufferId, cx: &App) -> bool {
17422 self.display_map.read(cx).is_buffer_folded(buffer)
17423 }
17424
17425 pub fn folded_buffers<'a>(&self, cx: &'a App) -> &'a HashSet<BufferId> {
17426 self.display_map.read(cx).folded_buffers()
17427 }
17428
17429 pub fn disable_header_for_buffer(&mut self, buffer_id: BufferId, cx: &mut Context<Self>) {
17430 self.display_map.update(cx, |display_map, cx| {
17431 display_map.disable_header_for_buffer(buffer_id, cx);
17432 });
17433 cx.notify();
17434 }
17435
17436 /// Removes any folds with the given ranges.
17437 pub fn remove_folds_with_type<T: ToOffset + Clone>(
17438 &mut self,
17439 ranges: &[Range<T>],
17440 type_id: TypeId,
17441 auto_scroll: bool,
17442 cx: &mut Context<Self>,
17443 ) {
17444 self.remove_folds_with(ranges, auto_scroll, cx, |map, cx| {
17445 map.remove_folds_with_type(ranges.iter().cloned(), type_id, cx)
17446 });
17447 self.folds_did_change(cx);
17448 }
17449
17450 fn remove_folds_with<T: ToOffset + Clone>(
17451 &mut self,
17452 ranges: &[Range<T>],
17453 auto_scroll: bool,
17454 cx: &mut Context<Self>,
17455 update: impl FnOnce(&mut DisplayMap, &mut Context<DisplayMap>),
17456 ) {
17457 if ranges.is_empty() {
17458 return;
17459 }
17460
17461 let mut buffers_affected = HashSet::default();
17462 let multi_buffer = self.buffer().read(cx);
17463 for range in ranges {
17464 if let Some((_, buffer, _)) = multi_buffer.excerpt_containing(range.start.clone(), cx) {
17465 buffers_affected.insert(buffer.read(cx).remote_id());
17466 };
17467 }
17468
17469 self.display_map.update(cx, update);
17470
17471 if auto_scroll {
17472 self.request_autoscroll(Autoscroll::fit(), cx);
17473 }
17474
17475 cx.notify();
17476 self.scrollbar_marker_state.dirty = true;
17477 self.active_indent_guides_state.dirty = true;
17478 }
17479
17480 pub fn update_renderer_widths(
17481 &mut self,
17482 widths: impl IntoIterator<Item = (ChunkRendererId, Pixels)>,
17483 cx: &mut Context<Self>,
17484 ) -> bool {
17485 self.display_map
17486 .update(cx, |map, cx| map.update_fold_widths(widths, cx))
17487 }
17488
17489 pub fn default_fold_placeholder(&self, cx: &App) -> FoldPlaceholder {
17490 self.display_map.read(cx).fold_placeholder.clone()
17491 }
17492
17493 pub fn set_expand_all_diff_hunks(&mut self, cx: &mut App) {
17494 self.buffer.update(cx, |buffer, cx| {
17495 buffer.set_all_diff_hunks_expanded(cx);
17496 });
17497 }
17498
17499 pub fn expand_all_diff_hunks(
17500 &mut self,
17501 _: &ExpandAllDiffHunks,
17502 _window: &mut Window,
17503 cx: &mut Context<Self>,
17504 ) {
17505 self.buffer.update(cx, |buffer, cx| {
17506 buffer.expand_diff_hunks(vec![Anchor::min()..Anchor::max()], cx)
17507 });
17508 }
17509
17510 pub fn toggle_selected_diff_hunks(
17511 &mut self,
17512 _: &ToggleSelectedDiffHunks,
17513 _window: &mut Window,
17514 cx: &mut Context<Self>,
17515 ) {
17516 let ranges: Vec<_> = self.selections.disjoint.iter().map(|s| s.range()).collect();
17517 self.toggle_diff_hunks_in_ranges(ranges, cx);
17518 }
17519
17520 pub fn diff_hunks_in_ranges<'a>(
17521 &'a self,
17522 ranges: &'a [Range<Anchor>],
17523 buffer: &'a MultiBufferSnapshot,
17524 ) -> impl 'a + Iterator<Item = MultiBufferDiffHunk> {
17525 ranges.iter().flat_map(move |range| {
17526 let end_excerpt_id = range.end.excerpt_id;
17527 let range = range.to_point(buffer);
17528 let mut peek_end = range.end;
17529 if range.end.row < buffer.max_row().0 {
17530 peek_end = Point::new(range.end.row + 1, 0);
17531 }
17532 buffer
17533 .diff_hunks_in_range(range.start..peek_end)
17534 .filter(move |hunk| hunk.excerpt_id.cmp(&end_excerpt_id, buffer).is_le())
17535 })
17536 }
17537
17538 pub fn has_stageable_diff_hunks_in_ranges(
17539 &self,
17540 ranges: &[Range<Anchor>],
17541 snapshot: &MultiBufferSnapshot,
17542 ) -> bool {
17543 let mut hunks = self.diff_hunks_in_ranges(ranges, &snapshot);
17544 hunks.any(|hunk| hunk.status().has_secondary_hunk())
17545 }
17546
17547 pub fn toggle_staged_selected_diff_hunks(
17548 &mut self,
17549 _: &::git::ToggleStaged,
17550 _: &mut Window,
17551 cx: &mut Context<Self>,
17552 ) {
17553 let snapshot = self.buffer.read(cx).snapshot(cx);
17554 let ranges: Vec<_> = self.selections.disjoint.iter().map(|s| s.range()).collect();
17555 let stage = self.has_stageable_diff_hunks_in_ranges(&ranges, &snapshot);
17556 self.stage_or_unstage_diff_hunks(stage, ranges, cx);
17557 }
17558
17559 pub fn set_render_diff_hunk_controls(
17560 &mut self,
17561 render_diff_hunk_controls: RenderDiffHunkControlsFn,
17562 cx: &mut Context<Self>,
17563 ) {
17564 self.render_diff_hunk_controls = render_diff_hunk_controls;
17565 cx.notify();
17566 }
17567
17568 pub fn stage_and_next(
17569 &mut self,
17570 _: &::git::StageAndNext,
17571 window: &mut Window,
17572 cx: &mut Context<Self>,
17573 ) {
17574 self.do_stage_or_unstage_and_next(true, window, cx);
17575 }
17576
17577 pub fn unstage_and_next(
17578 &mut self,
17579 _: &::git::UnstageAndNext,
17580 window: &mut Window,
17581 cx: &mut Context<Self>,
17582 ) {
17583 self.do_stage_or_unstage_and_next(false, window, cx);
17584 }
17585
17586 pub fn stage_or_unstage_diff_hunks(
17587 &mut self,
17588 stage: bool,
17589 ranges: Vec<Range<Anchor>>,
17590 cx: &mut Context<Self>,
17591 ) {
17592 let task = self.save_buffers_for_ranges_if_needed(&ranges, cx);
17593 cx.spawn(async move |this, cx| {
17594 task.await?;
17595 this.update(cx, |this, cx| {
17596 let snapshot = this.buffer.read(cx).snapshot(cx);
17597 let chunk_by = this
17598 .diff_hunks_in_ranges(&ranges, &snapshot)
17599 .chunk_by(|hunk| hunk.buffer_id);
17600 for (buffer_id, hunks) in &chunk_by {
17601 this.do_stage_or_unstage(stage, buffer_id, hunks, cx);
17602 }
17603 })
17604 })
17605 .detach_and_log_err(cx);
17606 }
17607
17608 fn save_buffers_for_ranges_if_needed(
17609 &mut self,
17610 ranges: &[Range<Anchor>],
17611 cx: &mut Context<Editor>,
17612 ) -> Task<Result<()>> {
17613 let multibuffer = self.buffer.read(cx);
17614 let snapshot = multibuffer.read(cx);
17615 let buffer_ids: HashSet<_> = ranges
17616 .iter()
17617 .flat_map(|range| snapshot.buffer_ids_for_range(range.clone()))
17618 .collect();
17619 drop(snapshot);
17620
17621 let mut buffers = HashSet::default();
17622 for buffer_id in buffer_ids {
17623 if let Some(buffer_entity) = multibuffer.buffer(buffer_id) {
17624 let buffer = buffer_entity.read(cx);
17625 if buffer.file().is_some_and(|file| file.disk_state().exists()) && buffer.is_dirty()
17626 {
17627 buffers.insert(buffer_entity);
17628 }
17629 }
17630 }
17631
17632 if let Some(project) = &self.project {
17633 project.update(cx, |project, cx| project.save_buffers(buffers, cx))
17634 } else {
17635 Task::ready(Ok(()))
17636 }
17637 }
17638
17639 fn do_stage_or_unstage_and_next(
17640 &mut self,
17641 stage: bool,
17642 window: &mut Window,
17643 cx: &mut Context<Self>,
17644 ) {
17645 let ranges = self.selections.disjoint_anchor_ranges().collect::<Vec<_>>();
17646
17647 if ranges.iter().any(|range| range.start != range.end) {
17648 self.stage_or_unstage_diff_hunks(stage, ranges, cx);
17649 return;
17650 }
17651
17652 self.stage_or_unstage_diff_hunks(stage, ranges, cx);
17653 let snapshot = self.snapshot(window, cx);
17654 let position = self.selections.newest::<Point>(cx).head();
17655 let mut row = snapshot
17656 .buffer_snapshot
17657 .diff_hunks_in_range(position..snapshot.buffer_snapshot.max_point())
17658 .find(|hunk| hunk.row_range.start.0 > position.row)
17659 .map(|hunk| hunk.row_range.start);
17660
17661 let all_diff_hunks_expanded = self.buffer().read(cx).all_diff_hunks_expanded();
17662 // Outside of the project diff editor, wrap around to the beginning.
17663 if !all_diff_hunks_expanded {
17664 row = row.or_else(|| {
17665 snapshot
17666 .buffer_snapshot
17667 .diff_hunks_in_range(Point::zero()..position)
17668 .find(|hunk| hunk.row_range.end.0 < position.row)
17669 .map(|hunk| hunk.row_range.start)
17670 });
17671 }
17672
17673 if let Some(row) = row {
17674 let destination = Point::new(row.0, 0);
17675 let autoscroll = Autoscroll::center();
17676
17677 self.unfold_ranges(&[destination..destination], false, false, cx);
17678 self.change_selections(SelectionEffects::scroll(autoscroll), window, cx, |s| {
17679 s.select_ranges([destination..destination]);
17680 });
17681 }
17682 }
17683
17684 fn do_stage_or_unstage(
17685 &self,
17686 stage: bool,
17687 buffer_id: BufferId,
17688 hunks: impl Iterator<Item = MultiBufferDiffHunk>,
17689 cx: &mut App,
17690 ) -> Option<()> {
17691 let project = self.project.as_ref()?;
17692 let buffer = project.read(cx).buffer_for_id(buffer_id, cx)?;
17693 let diff = self.buffer.read(cx).diff_for(buffer_id)?;
17694 let buffer_snapshot = buffer.read(cx).snapshot();
17695 let file_exists = buffer_snapshot
17696 .file()
17697 .is_some_and(|file| file.disk_state().exists());
17698 diff.update(cx, |diff, cx| {
17699 diff.stage_or_unstage_hunks(
17700 stage,
17701 &hunks
17702 .map(|hunk| buffer_diff::DiffHunk {
17703 buffer_range: hunk.buffer_range,
17704 diff_base_byte_range: hunk.diff_base_byte_range,
17705 secondary_status: hunk.secondary_status,
17706 range: Point::zero()..Point::zero(), // unused
17707 })
17708 .collect::<Vec<_>>(),
17709 &buffer_snapshot,
17710 file_exists,
17711 cx,
17712 )
17713 });
17714 None
17715 }
17716
17717 pub fn expand_selected_diff_hunks(&mut self, cx: &mut Context<Self>) {
17718 let ranges: Vec<_> = self.selections.disjoint.iter().map(|s| s.range()).collect();
17719 self.buffer
17720 .update(cx, |buffer, cx| buffer.expand_diff_hunks(ranges, cx))
17721 }
17722
17723 pub fn clear_expanded_diff_hunks(&mut self, cx: &mut Context<Self>) -> bool {
17724 self.buffer.update(cx, |buffer, cx| {
17725 let ranges = vec![Anchor::min()..Anchor::max()];
17726 if !buffer.all_diff_hunks_expanded()
17727 && buffer.has_expanded_diff_hunks_in_ranges(&ranges, cx)
17728 {
17729 buffer.collapse_diff_hunks(ranges, cx);
17730 true
17731 } else {
17732 false
17733 }
17734 })
17735 }
17736
17737 fn toggle_diff_hunks_in_ranges(
17738 &mut self,
17739 ranges: Vec<Range<Anchor>>,
17740 cx: &mut Context<Editor>,
17741 ) {
17742 self.buffer.update(cx, |buffer, cx| {
17743 let expand = !buffer.has_expanded_diff_hunks_in_ranges(&ranges, cx);
17744 buffer.expand_or_collapse_diff_hunks(ranges, expand, cx);
17745 })
17746 }
17747
17748 fn toggle_single_diff_hunk(&mut self, range: Range<Anchor>, cx: &mut Context<Self>) {
17749 self.buffer.update(cx, |buffer, cx| {
17750 let snapshot = buffer.snapshot(cx);
17751 let excerpt_id = range.end.excerpt_id;
17752 let point_range = range.to_point(&snapshot);
17753 let expand = !buffer.single_hunk_is_expanded(range, cx);
17754 buffer.expand_or_collapse_diff_hunks_inner([(point_range, excerpt_id)], expand, cx);
17755 })
17756 }
17757
17758 pub(crate) fn apply_all_diff_hunks(
17759 &mut self,
17760 _: &ApplyAllDiffHunks,
17761 window: &mut Window,
17762 cx: &mut Context<Self>,
17763 ) {
17764 self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
17765
17766 let buffers = self.buffer.read(cx).all_buffers();
17767 for branch_buffer in buffers {
17768 branch_buffer.update(cx, |branch_buffer, cx| {
17769 branch_buffer.merge_into_base(Vec::new(), cx);
17770 });
17771 }
17772
17773 if let Some(project) = self.project.clone() {
17774 self.save(
17775 SaveOptions {
17776 format: true,
17777 autosave: false,
17778 },
17779 project,
17780 window,
17781 cx,
17782 )
17783 .detach_and_log_err(cx);
17784 }
17785 }
17786
17787 pub(crate) fn apply_selected_diff_hunks(
17788 &mut self,
17789 _: &ApplyDiffHunk,
17790 window: &mut Window,
17791 cx: &mut Context<Self>,
17792 ) {
17793 self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
17794 let snapshot = self.snapshot(window, cx);
17795 let hunks = snapshot.hunks_for_ranges(self.selections.ranges(cx));
17796 let mut ranges_by_buffer = HashMap::default();
17797 self.transact(window, cx, |editor, _window, cx| {
17798 for hunk in hunks {
17799 if let Some(buffer) = editor.buffer.read(cx).buffer(hunk.buffer_id) {
17800 ranges_by_buffer
17801 .entry(buffer.clone())
17802 .or_insert_with(Vec::new)
17803 .push(hunk.buffer_range.to_offset(buffer.read(cx)));
17804 }
17805 }
17806
17807 for (buffer, ranges) in ranges_by_buffer {
17808 buffer.update(cx, |buffer, cx| {
17809 buffer.merge_into_base(ranges, cx);
17810 });
17811 }
17812 });
17813
17814 if let Some(project) = self.project.clone() {
17815 self.save(
17816 SaveOptions {
17817 format: true,
17818 autosave: false,
17819 },
17820 project,
17821 window,
17822 cx,
17823 )
17824 .detach_and_log_err(cx);
17825 }
17826 }
17827
17828 pub fn set_gutter_hovered(&mut self, hovered: bool, cx: &mut Context<Self>) {
17829 if hovered != self.gutter_hovered {
17830 self.gutter_hovered = hovered;
17831 cx.notify();
17832 }
17833 }
17834
17835 pub fn insert_blocks(
17836 &mut self,
17837 blocks: impl IntoIterator<Item = BlockProperties<Anchor>>,
17838 autoscroll: Option<Autoscroll>,
17839 cx: &mut Context<Self>,
17840 ) -> Vec<CustomBlockId> {
17841 let blocks = self
17842 .display_map
17843 .update(cx, |display_map, cx| display_map.insert_blocks(blocks, cx));
17844 if let Some(autoscroll) = autoscroll {
17845 self.request_autoscroll(autoscroll, cx);
17846 }
17847 cx.notify();
17848 blocks
17849 }
17850
17851 pub fn resize_blocks(
17852 &mut self,
17853 heights: HashMap<CustomBlockId, u32>,
17854 autoscroll: Option<Autoscroll>,
17855 cx: &mut Context<Self>,
17856 ) {
17857 self.display_map
17858 .update(cx, |display_map, cx| display_map.resize_blocks(heights, cx));
17859 if let Some(autoscroll) = autoscroll {
17860 self.request_autoscroll(autoscroll, cx);
17861 }
17862 cx.notify();
17863 }
17864
17865 pub fn replace_blocks(
17866 &mut self,
17867 renderers: HashMap<CustomBlockId, RenderBlock>,
17868 autoscroll: Option<Autoscroll>,
17869 cx: &mut Context<Self>,
17870 ) {
17871 self.display_map
17872 .update(cx, |display_map, _cx| display_map.replace_blocks(renderers));
17873 if let Some(autoscroll) = autoscroll {
17874 self.request_autoscroll(autoscroll, cx);
17875 }
17876 cx.notify();
17877 }
17878
17879 pub fn remove_blocks(
17880 &mut self,
17881 block_ids: HashSet<CustomBlockId>,
17882 autoscroll: Option<Autoscroll>,
17883 cx: &mut Context<Self>,
17884 ) {
17885 self.display_map.update(cx, |display_map, cx| {
17886 display_map.remove_blocks(block_ids, cx)
17887 });
17888 if let Some(autoscroll) = autoscroll {
17889 self.request_autoscroll(autoscroll, cx);
17890 }
17891 cx.notify();
17892 }
17893
17894 pub fn row_for_block(
17895 &self,
17896 block_id: CustomBlockId,
17897 cx: &mut Context<Self>,
17898 ) -> Option<DisplayRow> {
17899 self.display_map
17900 .update(cx, |map, cx| map.row_for_block(block_id, cx))
17901 }
17902
17903 pub(crate) fn set_focused_block(&mut self, focused_block: FocusedBlock) {
17904 self.focused_block = Some(focused_block);
17905 }
17906
17907 pub(crate) fn take_focused_block(&mut self) -> Option<FocusedBlock> {
17908 self.focused_block.take()
17909 }
17910
17911 pub fn insert_creases(
17912 &mut self,
17913 creases: impl IntoIterator<Item = Crease<Anchor>>,
17914 cx: &mut Context<Self>,
17915 ) -> Vec<CreaseId> {
17916 self.display_map
17917 .update(cx, |map, cx| map.insert_creases(creases, cx))
17918 }
17919
17920 pub fn remove_creases(
17921 &mut self,
17922 ids: impl IntoIterator<Item = CreaseId>,
17923 cx: &mut Context<Self>,
17924 ) -> Vec<(CreaseId, Range<Anchor>)> {
17925 self.display_map
17926 .update(cx, |map, cx| map.remove_creases(ids, cx))
17927 }
17928
17929 pub fn longest_row(&self, cx: &mut App) -> DisplayRow {
17930 self.display_map
17931 .update(cx, |map, cx| map.snapshot(cx))
17932 .longest_row()
17933 }
17934
17935 pub fn max_point(&self, cx: &mut App) -> DisplayPoint {
17936 self.display_map
17937 .update(cx, |map, cx| map.snapshot(cx))
17938 .max_point()
17939 }
17940
17941 pub fn text(&self, cx: &App) -> String {
17942 self.buffer.read(cx).read(cx).text()
17943 }
17944
17945 pub fn is_empty(&self, cx: &App) -> bool {
17946 self.buffer.read(cx).read(cx).is_empty()
17947 }
17948
17949 pub fn text_option(&self, cx: &App) -> Option<String> {
17950 let text = self.text(cx);
17951 let text = text.trim();
17952
17953 if text.is_empty() {
17954 return None;
17955 }
17956
17957 Some(text.to_string())
17958 }
17959
17960 pub fn set_text(
17961 &mut self,
17962 text: impl Into<Arc<str>>,
17963 window: &mut Window,
17964 cx: &mut Context<Self>,
17965 ) {
17966 self.transact(window, cx, |this, _, cx| {
17967 this.buffer
17968 .read(cx)
17969 .as_singleton()
17970 .expect("you can only call set_text on editors for singleton buffers")
17971 .update(cx, |buffer, cx| buffer.set_text(text, cx));
17972 });
17973 }
17974
17975 pub fn display_text(&self, cx: &mut App) -> String {
17976 self.display_map
17977 .update(cx, |map, cx| map.snapshot(cx))
17978 .text()
17979 }
17980
17981 fn create_minimap(
17982 &self,
17983 minimap_settings: MinimapSettings,
17984 window: &mut Window,
17985 cx: &mut Context<Self>,
17986 ) -> Option<Entity<Self>> {
17987 (minimap_settings.minimap_enabled() && self.is_singleton(cx))
17988 .then(|| self.initialize_new_minimap(minimap_settings, window, cx))
17989 }
17990
17991 fn initialize_new_minimap(
17992 &self,
17993 minimap_settings: MinimapSettings,
17994 window: &mut Window,
17995 cx: &mut Context<Self>,
17996 ) -> Entity<Self> {
17997 const MINIMAP_FONT_WEIGHT: gpui::FontWeight = gpui::FontWeight::BLACK;
17998
17999 let mut minimap = Editor::new_internal(
18000 EditorMode::Minimap {
18001 parent: cx.weak_entity(),
18002 },
18003 self.buffer.clone(),
18004 self.project.clone(),
18005 Some(self.display_map.clone()),
18006 window,
18007 cx,
18008 );
18009 minimap.scroll_manager.clone_state(&self.scroll_manager);
18010 minimap.set_text_style_refinement(TextStyleRefinement {
18011 font_size: Some(MINIMAP_FONT_SIZE),
18012 font_weight: Some(MINIMAP_FONT_WEIGHT),
18013 ..Default::default()
18014 });
18015 minimap.update_minimap_configuration(minimap_settings, cx);
18016 cx.new(|_| minimap)
18017 }
18018
18019 fn update_minimap_configuration(&mut self, minimap_settings: MinimapSettings, cx: &App) {
18020 let current_line_highlight = minimap_settings
18021 .current_line_highlight
18022 .unwrap_or_else(|| EditorSettings::get_global(cx).current_line_highlight);
18023 self.set_current_line_highlight(Some(current_line_highlight));
18024 }
18025
18026 pub fn minimap(&self) -> Option<&Entity<Self>> {
18027 self.minimap
18028 .as_ref()
18029 .filter(|_| self.minimap_visibility.visible())
18030 }
18031
18032 pub fn wrap_guides(&self, cx: &App) -> SmallVec<[(usize, bool); 2]> {
18033 let mut wrap_guides = smallvec![];
18034
18035 if self.show_wrap_guides == Some(false) {
18036 return wrap_guides;
18037 }
18038
18039 let settings = self.buffer.read(cx).language_settings(cx);
18040 if settings.show_wrap_guides {
18041 match self.soft_wrap_mode(cx) {
18042 SoftWrap::Column(soft_wrap) => {
18043 wrap_guides.push((soft_wrap as usize, true));
18044 }
18045 SoftWrap::Bounded(soft_wrap) => {
18046 wrap_guides.push((soft_wrap as usize, true));
18047 }
18048 SoftWrap::GitDiff | SoftWrap::None | SoftWrap::EditorWidth => {}
18049 }
18050 wrap_guides.extend(settings.wrap_guides.iter().map(|guide| (*guide, false)))
18051 }
18052
18053 wrap_guides
18054 }
18055
18056 pub fn soft_wrap_mode(&self, cx: &App) -> SoftWrap {
18057 let settings = self.buffer.read(cx).language_settings(cx);
18058 let mode = self.soft_wrap_mode_override.unwrap_or(settings.soft_wrap);
18059 match mode {
18060 language_settings::SoftWrap::PreferLine | language_settings::SoftWrap::None => {
18061 SoftWrap::None
18062 }
18063 language_settings::SoftWrap::EditorWidth => SoftWrap::EditorWidth,
18064 language_settings::SoftWrap::PreferredLineLength => {
18065 SoftWrap::Column(settings.preferred_line_length)
18066 }
18067 language_settings::SoftWrap::Bounded => {
18068 SoftWrap::Bounded(settings.preferred_line_length)
18069 }
18070 }
18071 }
18072
18073 pub fn set_soft_wrap_mode(
18074 &mut self,
18075 mode: language_settings::SoftWrap,
18076
18077 cx: &mut Context<Self>,
18078 ) {
18079 self.soft_wrap_mode_override = Some(mode);
18080 cx.notify();
18081 }
18082
18083 pub fn set_hard_wrap(&mut self, hard_wrap: Option<usize>, cx: &mut Context<Self>) {
18084 self.hard_wrap = hard_wrap;
18085 cx.notify();
18086 }
18087
18088 pub fn set_text_style_refinement(&mut self, style: TextStyleRefinement) {
18089 self.text_style_refinement = Some(style);
18090 }
18091
18092 /// called by the Element so we know what style we were most recently rendered with.
18093 pub(crate) fn set_style(
18094 &mut self,
18095 style: EditorStyle,
18096 window: &mut Window,
18097 cx: &mut Context<Self>,
18098 ) {
18099 // We intentionally do not inform the display map about the minimap style
18100 // so that wrapping is not recalculated and stays consistent for the editor
18101 // and its linked minimap.
18102 if !self.mode.is_minimap() {
18103 let rem_size = window.rem_size();
18104 self.display_map.update(cx, |map, cx| {
18105 map.set_font(
18106 style.text.font(),
18107 style.text.font_size.to_pixels(rem_size),
18108 cx,
18109 )
18110 });
18111 }
18112 self.style = Some(style);
18113 }
18114
18115 pub fn style(&self) -> Option<&EditorStyle> {
18116 self.style.as_ref()
18117 }
18118
18119 // Called by the element. This method is not designed to be called outside of the editor
18120 // element's layout code because it does not notify when rewrapping is computed synchronously.
18121 pub(crate) fn set_wrap_width(&self, width: Option<Pixels>, cx: &mut App) -> bool {
18122 self.display_map
18123 .update(cx, |map, cx| map.set_wrap_width(width, cx))
18124 }
18125
18126 pub fn set_soft_wrap(&mut self) {
18127 self.soft_wrap_mode_override = Some(language_settings::SoftWrap::EditorWidth)
18128 }
18129
18130 pub fn toggle_soft_wrap(&mut self, _: &ToggleSoftWrap, _: &mut Window, cx: &mut Context<Self>) {
18131 if self.soft_wrap_mode_override.is_some() {
18132 self.soft_wrap_mode_override.take();
18133 } else {
18134 let soft_wrap = match self.soft_wrap_mode(cx) {
18135 SoftWrap::GitDiff => return,
18136 SoftWrap::None => language_settings::SoftWrap::EditorWidth,
18137 SoftWrap::EditorWidth | SoftWrap::Column(_) | SoftWrap::Bounded(_) => {
18138 language_settings::SoftWrap::None
18139 }
18140 };
18141 self.soft_wrap_mode_override = Some(soft_wrap);
18142 }
18143 cx.notify();
18144 }
18145
18146 pub fn toggle_tab_bar(&mut self, _: &ToggleTabBar, _: &mut Window, cx: &mut Context<Self>) {
18147 let Some(workspace) = self.workspace() else {
18148 return;
18149 };
18150 let fs = workspace.read(cx).app_state().fs.clone();
18151 let current_show = TabBarSettings::get_global(cx).show;
18152 update_settings_file::<TabBarSettings>(fs, cx, move |setting, _| {
18153 setting.show = Some(!current_show);
18154 });
18155 }
18156
18157 pub fn toggle_indent_guides(
18158 &mut self,
18159 _: &ToggleIndentGuides,
18160 _: &mut Window,
18161 cx: &mut Context<Self>,
18162 ) {
18163 let currently_enabled = self.should_show_indent_guides().unwrap_or_else(|| {
18164 self.buffer
18165 .read(cx)
18166 .language_settings(cx)
18167 .indent_guides
18168 .enabled
18169 });
18170 self.show_indent_guides = Some(!currently_enabled);
18171 cx.notify();
18172 }
18173
18174 fn should_show_indent_guides(&self) -> Option<bool> {
18175 self.show_indent_guides
18176 }
18177
18178 pub fn toggle_line_numbers(
18179 &mut self,
18180 _: &ToggleLineNumbers,
18181 _: &mut Window,
18182 cx: &mut Context<Self>,
18183 ) {
18184 let mut editor_settings = EditorSettings::get_global(cx).clone();
18185 editor_settings.gutter.line_numbers = !editor_settings.gutter.line_numbers;
18186 EditorSettings::override_global(editor_settings, cx);
18187 }
18188
18189 pub fn line_numbers_enabled(&self, cx: &App) -> bool {
18190 if let Some(show_line_numbers) = self.show_line_numbers {
18191 return show_line_numbers;
18192 }
18193 EditorSettings::get_global(cx).gutter.line_numbers
18194 }
18195
18196 pub fn should_use_relative_line_numbers(&self, cx: &mut App) -> bool {
18197 self.use_relative_line_numbers
18198 .unwrap_or(EditorSettings::get_global(cx).relative_line_numbers)
18199 }
18200
18201 pub fn toggle_relative_line_numbers(
18202 &mut self,
18203 _: &ToggleRelativeLineNumbers,
18204 _: &mut Window,
18205 cx: &mut Context<Self>,
18206 ) {
18207 let is_relative = self.should_use_relative_line_numbers(cx);
18208 self.set_relative_line_number(Some(!is_relative), cx)
18209 }
18210
18211 pub fn set_relative_line_number(&mut self, is_relative: Option<bool>, cx: &mut Context<Self>) {
18212 self.use_relative_line_numbers = is_relative;
18213 cx.notify();
18214 }
18215
18216 pub fn set_show_gutter(&mut self, show_gutter: bool, cx: &mut Context<Self>) {
18217 self.show_gutter = show_gutter;
18218 cx.notify();
18219 }
18220
18221 pub fn set_show_scrollbars(&mut self, show: bool, cx: &mut Context<Self>) {
18222 self.show_scrollbars = ScrollbarAxes {
18223 horizontal: show,
18224 vertical: show,
18225 };
18226 cx.notify();
18227 }
18228
18229 pub fn set_show_vertical_scrollbar(&mut self, show: bool, cx: &mut Context<Self>) {
18230 self.show_scrollbars.vertical = show;
18231 cx.notify();
18232 }
18233
18234 pub fn set_show_horizontal_scrollbar(&mut self, show: bool, cx: &mut Context<Self>) {
18235 self.show_scrollbars.horizontal = show;
18236 cx.notify();
18237 }
18238
18239 pub fn set_minimap_visibility(
18240 &mut self,
18241 minimap_visibility: MinimapVisibility,
18242 window: &mut Window,
18243 cx: &mut Context<Self>,
18244 ) {
18245 if self.minimap_visibility != minimap_visibility {
18246 if minimap_visibility.visible() && self.minimap.is_none() {
18247 let minimap_settings = EditorSettings::get_global(cx).minimap;
18248 self.minimap =
18249 self.create_minimap(minimap_settings.with_show_override(), window, cx);
18250 }
18251 self.minimap_visibility = minimap_visibility;
18252 cx.notify();
18253 }
18254 }
18255
18256 pub fn disable_scrollbars_and_minimap(&mut self, window: &mut Window, cx: &mut Context<Self>) {
18257 self.set_show_scrollbars(false, cx);
18258 self.set_minimap_visibility(MinimapVisibility::Disabled, window, cx);
18259 }
18260
18261 pub fn hide_minimap_by_default(&mut self, window: &mut Window, cx: &mut Context<Self>) {
18262 self.set_minimap_visibility(self.minimap_visibility.hidden(), window, cx);
18263 }
18264
18265 /// Normally the text in full mode and auto height editors is padded on the
18266 /// left side by roughly half a character width for improved hit testing.
18267 ///
18268 /// Use this method to disable this for cases where this is not wanted (e.g.
18269 /// if you want to align the editor text with some other text above or below)
18270 /// or if you want to add this padding to single-line editors.
18271 pub fn set_offset_content(&mut self, offset_content: bool, cx: &mut Context<Self>) {
18272 self.offset_content = offset_content;
18273 cx.notify();
18274 }
18275
18276 pub fn set_show_line_numbers(&mut self, show_line_numbers: bool, cx: &mut Context<Self>) {
18277 self.show_line_numbers = Some(show_line_numbers);
18278 cx.notify();
18279 }
18280
18281 pub fn disable_expand_excerpt_buttons(&mut self, cx: &mut Context<Self>) {
18282 self.disable_expand_excerpt_buttons = true;
18283 cx.notify();
18284 }
18285
18286 pub fn set_show_git_diff_gutter(&mut self, show_git_diff_gutter: bool, cx: &mut Context<Self>) {
18287 self.show_git_diff_gutter = Some(show_git_diff_gutter);
18288 cx.notify();
18289 }
18290
18291 pub fn set_show_code_actions(&mut self, show_code_actions: bool, cx: &mut Context<Self>) {
18292 self.show_code_actions = Some(show_code_actions);
18293 cx.notify();
18294 }
18295
18296 pub fn set_show_runnables(&mut self, show_runnables: bool, cx: &mut Context<Self>) {
18297 self.show_runnables = Some(show_runnables);
18298 cx.notify();
18299 }
18300
18301 pub fn set_show_breakpoints(&mut self, show_breakpoints: bool, cx: &mut Context<Self>) {
18302 self.show_breakpoints = Some(show_breakpoints);
18303 cx.notify();
18304 }
18305
18306 pub fn set_masked(&mut self, masked: bool, cx: &mut Context<Self>) {
18307 if self.display_map.read(cx).masked != masked {
18308 self.display_map.update(cx, |map, _| map.masked = masked);
18309 }
18310 cx.notify()
18311 }
18312
18313 pub fn set_show_wrap_guides(&mut self, show_wrap_guides: bool, cx: &mut Context<Self>) {
18314 self.show_wrap_guides = Some(show_wrap_guides);
18315 cx.notify();
18316 }
18317
18318 pub fn set_show_indent_guides(&mut self, show_indent_guides: bool, cx: &mut Context<Self>) {
18319 self.show_indent_guides = Some(show_indent_guides);
18320 cx.notify();
18321 }
18322
18323 pub fn working_directory(&self, cx: &App) -> Option<PathBuf> {
18324 if let Some(buffer) = self.buffer().read(cx).as_singleton() {
18325 if let Some(file) = buffer.read(cx).file().and_then(|f| f.as_local()) {
18326 if let Some(dir) = file.abs_path(cx).parent() {
18327 return Some(dir.to_owned());
18328 }
18329 }
18330
18331 if let Some(project_path) = buffer.read(cx).project_path(cx) {
18332 return Some(project_path.path.to_path_buf());
18333 }
18334 }
18335
18336 None
18337 }
18338
18339 fn target_file<'a>(&self, cx: &'a App) -> Option<&'a dyn language::LocalFile> {
18340 self.active_excerpt(cx)?
18341 .1
18342 .read(cx)
18343 .file()
18344 .and_then(|f| f.as_local())
18345 }
18346
18347 pub fn target_file_abs_path(&self, cx: &mut Context<Self>) -> Option<PathBuf> {
18348 self.active_excerpt(cx).and_then(|(_, buffer, _)| {
18349 let buffer = buffer.read(cx);
18350 if let Some(project_path) = buffer.project_path(cx) {
18351 let project = self.project.as_ref()?.read(cx);
18352 project.absolute_path(&project_path, cx)
18353 } else {
18354 buffer
18355 .file()
18356 .and_then(|file| file.as_local().map(|file| file.abs_path(cx)))
18357 }
18358 })
18359 }
18360
18361 fn target_file_path(&self, cx: &mut Context<Self>) -> Option<PathBuf> {
18362 self.active_excerpt(cx).and_then(|(_, buffer, _)| {
18363 let project_path = buffer.read(cx).project_path(cx)?;
18364 let project = self.project.as_ref()?.read(cx);
18365 let entry = project.entry_for_path(&project_path, cx)?;
18366 let path = entry.path.to_path_buf();
18367 Some(path)
18368 })
18369 }
18370
18371 pub fn reveal_in_finder(
18372 &mut self,
18373 _: &RevealInFileManager,
18374 _window: &mut Window,
18375 cx: &mut Context<Self>,
18376 ) {
18377 if let Some(target) = self.target_file(cx) {
18378 cx.reveal_path(&target.abs_path(cx));
18379 }
18380 }
18381
18382 pub fn copy_path(
18383 &mut self,
18384 _: &zed_actions::workspace::CopyPath,
18385 _window: &mut Window,
18386 cx: &mut Context<Self>,
18387 ) {
18388 if let Some(path) = self.target_file_abs_path(cx) {
18389 if let Some(path) = path.to_str() {
18390 cx.write_to_clipboard(ClipboardItem::new_string(path.to_string()));
18391 }
18392 }
18393 }
18394
18395 pub fn copy_relative_path(
18396 &mut self,
18397 _: &zed_actions::workspace::CopyRelativePath,
18398 _window: &mut Window,
18399 cx: &mut Context<Self>,
18400 ) {
18401 if let Some(path) = self.target_file_path(cx) {
18402 if let Some(path) = path.to_str() {
18403 cx.write_to_clipboard(ClipboardItem::new_string(path.to_string()));
18404 }
18405 }
18406 }
18407
18408 pub fn project_path(&self, cx: &App) -> Option<ProjectPath> {
18409 if let Some(buffer) = self.buffer.read(cx).as_singleton() {
18410 buffer.read(cx).project_path(cx)
18411 } else {
18412 None
18413 }
18414 }
18415
18416 // Returns true if the editor handled a go-to-line request
18417 pub fn go_to_active_debug_line(&mut self, window: &mut Window, cx: &mut Context<Self>) -> bool {
18418 maybe!({
18419 let breakpoint_store = self.breakpoint_store.as_ref()?;
18420
18421 let Some(active_stack_frame) = breakpoint_store.read(cx).active_position().cloned()
18422 else {
18423 self.clear_row_highlights::<ActiveDebugLine>();
18424 return None;
18425 };
18426
18427 let position = active_stack_frame.position;
18428 let buffer_id = position.buffer_id?;
18429 let snapshot = self
18430 .project
18431 .as_ref()?
18432 .read(cx)
18433 .buffer_for_id(buffer_id, cx)?
18434 .read(cx)
18435 .snapshot();
18436
18437 let mut handled = false;
18438 for (id, ExcerptRange { context, .. }) in
18439 self.buffer.read(cx).excerpts_for_buffer(buffer_id, cx)
18440 {
18441 if context.start.cmp(&position, &snapshot).is_ge()
18442 || context.end.cmp(&position, &snapshot).is_lt()
18443 {
18444 continue;
18445 }
18446 let snapshot = self.buffer.read(cx).snapshot(cx);
18447 let multibuffer_anchor = snapshot.anchor_in_excerpt(id, position)?;
18448
18449 handled = true;
18450 self.clear_row_highlights::<ActiveDebugLine>();
18451
18452 self.go_to_line::<ActiveDebugLine>(
18453 multibuffer_anchor,
18454 Some(cx.theme().colors().editor_debugger_active_line_background),
18455 window,
18456 cx,
18457 );
18458
18459 cx.notify();
18460 }
18461
18462 handled.then_some(())
18463 })
18464 .is_some()
18465 }
18466
18467 pub fn copy_file_name_without_extension(
18468 &mut self,
18469 _: &CopyFileNameWithoutExtension,
18470 _: &mut Window,
18471 cx: &mut Context<Self>,
18472 ) {
18473 if let Some(file) = self.target_file(cx) {
18474 if let Some(file_stem) = file.path().file_stem() {
18475 if let Some(name) = file_stem.to_str() {
18476 cx.write_to_clipboard(ClipboardItem::new_string(name.to_string()));
18477 }
18478 }
18479 }
18480 }
18481
18482 pub fn copy_file_name(&mut self, _: &CopyFileName, _: &mut Window, cx: &mut Context<Self>) {
18483 if let Some(file) = self.target_file(cx) {
18484 if let Some(file_name) = file.path().file_name() {
18485 if let Some(name) = file_name.to_str() {
18486 cx.write_to_clipboard(ClipboardItem::new_string(name.to_string()));
18487 }
18488 }
18489 }
18490 }
18491
18492 pub fn toggle_git_blame(
18493 &mut self,
18494 _: &::git::Blame,
18495 window: &mut Window,
18496 cx: &mut Context<Self>,
18497 ) {
18498 self.show_git_blame_gutter = !self.show_git_blame_gutter;
18499
18500 if self.show_git_blame_gutter && !self.has_blame_entries(cx) {
18501 self.start_git_blame(true, window, cx);
18502 }
18503
18504 cx.notify();
18505 }
18506
18507 pub fn toggle_git_blame_inline(
18508 &mut self,
18509 _: &ToggleGitBlameInline,
18510 window: &mut Window,
18511 cx: &mut Context<Self>,
18512 ) {
18513 self.toggle_git_blame_inline_internal(true, window, cx);
18514 cx.notify();
18515 }
18516
18517 pub fn open_git_blame_commit(
18518 &mut self,
18519 _: &OpenGitBlameCommit,
18520 window: &mut Window,
18521 cx: &mut Context<Self>,
18522 ) {
18523 self.open_git_blame_commit_internal(window, cx);
18524 }
18525
18526 fn open_git_blame_commit_internal(
18527 &mut self,
18528 window: &mut Window,
18529 cx: &mut Context<Self>,
18530 ) -> Option<()> {
18531 let blame = self.blame.as_ref()?;
18532 let snapshot = self.snapshot(window, cx);
18533 let cursor = self.selections.newest::<Point>(cx).head();
18534 let (buffer, point, _) = snapshot.buffer_snapshot.point_to_buffer_point(cursor)?;
18535 let blame_entry = blame
18536 .update(cx, |blame, cx| {
18537 blame
18538 .blame_for_rows(
18539 &[RowInfo {
18540 buffer_id: Some(buffer.remote_id()),
18541 buffer_row: Some(point.row),
18542 ..Default::default()
18543 }],
18544 cx,
18545 )
18546 .next()
18547 })
18548 .flatten()?;
18549 let renderer = cx.global::<GlobalBlameRenderer>().0.clone();
18550 let repo = blame.read(cx).repository(cx)?;
18551 let workspace = self.workspace()?.downgrade();
18552 renderer.open_blame_commit(blame_entry, repo, workspace, window, cx);
18553 None
18554 }
18555
18556 pub fn git_blame_inline_enabled(&self) -> bool {
18557 self.git_blame_inline_enabled
18558 }
18559
18560 pub fn toggle_selection_menu(
18561 &mut self,
18562 _: &ToggleSelectionMenu,
18563 _: &mut Window,
18564 cx: &mut Context<Self>,
18565 ) {
18566 self.show_selection_menu = self
18567 .show_selection_menu
18568 .map(|show_selections_menu| !show_selections_menu)
18569 .or_else(|| Some(!EditorSettings::get_global(cx).toolbar.selections_menu));
18570
18571 cx.notify();
18572 }
18573
18574 pub fn selection_menu_enabled(&self, cx: &App) -> bool {
18575 self.show_selection_menu
18576 .unwrap_or_else(|| EditorSettings::get_global(cx).toolbar.selections_menu)
18577 }
18578
18579 fn start_git_blame(
18580 &mut self,
18581 user_triggered: bool,
18582 window: &mut Window,
18583 cx: &mut Context<Self>,
18584 ) {
18585 if let Some(project) = self.project.as_ref() {
18586 let Some(buffer) = self.buffer().read(cx).as_singleton() else {
18587 return;
18588 };
18589
18590 if buffer.read(cx).file().is_none() {
18591 return;
18592 }
18593
18594 let focused = self.focus_handle(cx).contains_focused(window, cx);
18595
18596 let project = project.clone();
18597 let blame = cx.new(|cx| GitBlame::new(buffer, project, user_triggered, focused, cx));
18598 self.blame_subscription =
18599 Some(cx.observe_in(&blame, window, |_, _, _, cx| cx.notify()));
18600 self.blame = Some(blame);
18601 }
18602 }
18603
18604 fn toggle_git_blame_inline_internal(
18605 &mut self,
18606 user_triggered: bool,
18607 window: &mut Window,
18608 cx: &mut Context<Self>,
18609 ) {
18610 if self.git_blame_inline_enabled {
18611 self.git_blame_inline_enabled = false;
18612 self.show_git_blame_inline = false;
18613 self.show_git_blame_inline_delay_task.take();
18614 } else {
18615 self.git_blame_inline_enabled = true;
18616 self.start_git_blame_inline(user_triggered, window, cx);
18617 }
18618
18619 cx.notify();
18620 }
18621
18622 fn start_git_blame_inline(
18623 &mut self,
18624 user_triggered: bool,
18625 window: &mut Window,
18626 cx: &mut Context<Self>,
18627 ) {
18628 self.start_git_blame(user_triggered, window, cx);
18629
18630 if ProjectSettings::get_global(cx)
18631 .git
18632 .inline_blame_delay()
18633 .is_some()
18634 {
18635 self.start_inline_blame_timer(window, cx);
18636 } else {
18637 self.show_git_blame_inline = true
18638 }
18639 }
18640
18641 pub fn blame(&self) -> Option<&Entity<GitBlame>> {
18642 self.blame.as_ref()
18643 }
18644
18645 pub fn show_git_blame_gutter(&self) -> bool {
18646 self.show_git_blame_gutter
18647 }
18648
18649 pub fn render_git_blame_gutter(&self, cx: &App) -> bool {
18650 !self.mode().is_minimap() && self.show_git_blame_gutter && self.has_blame_entries(cx)
18651 }
18652
18653 pub fn render_git_blame_inline(&self, window: &Window, cx: &App) -> bool {
18654 self.show_git_blame_inline
18655 && (self.focus_handle.is_focused(window) || self.inline_blame_popover.is_some())
18656 && !self.newest_selection_head_on_empty_line(cx)
18657 && self.has_blame_entries(cx)
18658 }
18659
18660 fn has_blame_entries(&self, cx: &App) -> bool {
18661 self.blame()
18662 .map_or(false, |blame| blame.read(cx).has_generated_entries())
18663 }
18664
18665 fn newest_selection_head_on_empty_line(&self, cx: &App) -> bool {
18666 let cursor_anchor = self.selections.newest_anchor().head();
18667
18668 let snapshot = self.buffer.read(cx).snapshot(cx);
18669 let buffer_row = MultiBufferRow(cursor_anchor.to_point(&snapshot).row);
18670
18671 snapshot.line_len(buffer_row) == 0
18672 }
18673
18674 fn get_permalink_to_line(&self, cx: &mut Context<Self>) -> Task<Result<url::Url>> {
18675 let buffer_and_selection = maybe!({
18676 let selection = self.selections.newest::<Point>(cx);
18677 let selection_range = selection.range();
18678
18679 let multi_buffer = self.buffer().read(cx);
18680 let multi_buffer_snapshot = multi_buffer.snapshot(cx);
18681 let buffer_ranges = multi_buffer_snapshot.range_to_buffer_ranges(selection_range);
18682
18683 let (buffer, range, _) = if selection.reversed {
18684 buffer_ranges.first()
18685 } else {
18686 buffer_ranges.last()
18687 }?;
18688
18689 let selection = text::ToPoint::to_point(&range.start, &buffer).row
18690 ..text::ToPoint::to_point(&range.end, &buffer).row;
18691 Some((
18692 multi_buffer.buffer(buffer.remote_id()).unwrap().clone(),
18693 selection,
18694 ))
18695 });
18696
18697 let Some((buffer, selection)) = buffer_and_selection else {
18698 return Task::ready(Err(anyhow!("failed to determine buffer and selection")));
18699 };
18700
18701 let Some(project) = self.project.as_ref() else {
18702 return Task::ready(Err(anyhow!("editor does not have project")));
18703 };
18704
18705 project.update(cx, |project, cx| {
18706 project.get_permalink_to_line(&buffer, selection, cx)
18707 })
18708 }
18709
18710 pub fn copy_permalink_to_line(
18711 &mut self,
18712 _: &CopyPermalinkToLine,
18713 window: &mut Window,
18714 cx: &mut Context<Self>,
18715 ) {
18716 let permalink_task = self.get_permalink_to_line(cx);
18717 let workspace = self.workspace();
18718
18719 cx.spawn_in(window, async move |_, cx| match permalink_task.await {
18720 Ok(permalink) => {
18721 cx.update(|_, cx| {
18722 cx.write_to_clipboard(ClipboardItem::new_string(permalink.to_string()));
18723 })
18724 .ok();
18725 }
18726 Err(err) => {
18727 let message = format!("Failed to copy permalink: {err}");
18728
18729 anyhow::Result::<()>::Err(err).log_err();
18730
18731 if let Some(workspace) = workspace {
18732 workspace
18733 .update_in(cx, |workspace, _, cx| {
18734 struct CopyPermalinkToLine;
18735
18736 workspace.show_toast(
18737 Toast::new(
18738 NotificationId::unique::<CopyPermalinkToLine>(),
18739 message,
18740 ),
18741 cx,
18742 )
18743 })
18744 .ok();
18745 }
18746 }
18747 })
18748 .detach();
18749 }
18750
18751 pub fn copy_file_location(
18752 &mut self,
18753 _: &CopyFileLocation,
18754 _: &mut Window,
18755 cx: &mut Context<Self>,
18756 ) {
18757 let selection = self.selections.newest::<Point>(cx).start.row + 1;
18758 if let Some(file) = self.target_file(cx) {
18759 if let Some(path) = file.path().to_str() {
18760 cx.write_to_clipboard(ClipboardItem::new_string(format!("{path}:{selection}")));
18761 }
18762 }
18763 }
18764
18765 pub fn open_permalink_to_line(
18766 &mut self,
18767 _: &OpenPermalinkToLine,
18768 window: &mut Window,
18769 cx: &mut Context<Self>,
18770 ) {
18771 let permalink_task = self.get_permalink_to_line(cx);
18772 let workspace = self.workspace();
18773
18774 cx.spawn_in(window, async move |_, cx| match permalink_task.await {
18775 Ok(permalink) => {
18776 cx.update(|_, cx| {
18777 cx.open_url(permalink.as_ref());
18778 })
18779 .ok();
18780 }
18781 Err(err) => {
18782 let message = format!("Failed to open permalink: {err}");
18783
18784 anyhow::Result::<()>::Err(err).log_err();
18785
18786 if let Some(workspace) = workspace {
18787 workspace
18788 .update(cx, |workspace, cx| {
18789 struct OpenPermalinkToLine;
18790
18791 workspace.show_toast(
18792 Toast::new(
18793 NotificationId::unique::<OpenPermalinkToLine>(),
18794 message,
18795 ),
18796 cx,
18797 )
18798 })
18799 .ok();
18800 }
18801 }
18802 })
18803 .detach();
18804 }
18805
18806 pub fn insert_uuid_v4(
18807 &mut self,
18808 _: &InsertUuidV4,
18809 window: &mut Window,
18810 cx: &mut Context<Self>,
18811 ) {
18812 self.insert_uuid(UuidVersion::V4, window, cx);
18813 }
18814
18815 pub fn insert_uuid_v7(
18816 &mut self,
18817 _: &InsertUuidV7,
18818 window: &mut Window,
18819 cx: &mut Context<Self>,
18820 ) {
18821 self.insert_uuid(UuidVersion::V7, window, cx);
18822 }
18823
18824 fn insert_uuid(&mut self, version: UuidVersion, window: &mut Window, cx: &mut Context<Self>) {
18825 self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
18826 self.transact(window, cx, |this, window, cx| {
18827 let edits = this
18828 .selections
18829 .all::<Point>(cx)
18830 .into_iter()
18831 .map(|selection| {
18832 let uuid = match version {
18833 UuidVersion::V4 => uuid::Uuid::new_v4(),
18834 UuidVersion::V7 => uuid::Uuid::now_v7(),
18835 };
18836
18837 (selection.range(), uuid.to_string())
18838 });
18839 this.edit(edits, cx);
18840 this.refresh_inline_completion(true, false, window, cx);
18841 });
18842 }
18843
18844 pub fn open_selections_in_multibuffer(
18845 &mut self,
18846 _: &OpenSelectionsInMultibuffer,
18847 window: &mut Window,
18848 cx: &mut Context<Self>,
18849 ) {
18850 let multibuffer = self.buffer.read(cx);
18851
18852 let Some(buffer) = multibuffer.as_singleton() else {
18853 return;
18854 };
18855
18856 let Some(workspace) = self.workspace() else {
18857 return;
18858 };
18859
18860 let title = multibuffer.title(cx).to_string();
18861
18862 let locations = self
18863 .selections
18864 .all_anchors(cx)
18865 .into_iter()
18866 .map(|selection| Location {
18867 buffer: buffer.clone(),
18868 range: selection.start.text_anchor..selection.end.text_anchor,
18869 })
18870 .collect::<Vec<_>>();
18871
18872 cx.spawn_in(window, async move |_, cx| {
18873 workspace.update_in(cx, |workspace, window, cx| {
18874 Self::open_locations_in_multibuffer(
18875 workspace,
18876 locations,
18877 format!("Selections for '{title}'"),
18878 false,
18879 MultibufferSelectionMode::All,
18880 window,
18881 cx,
18882 );
18883 })
18884 })
18885 .detach();
18886 }
18887
18888 /// Adds a row highlight for the given range. If a row has multiple highlights, the
18889 /// last highlight added will be used.
18890 ///
18891 /// If the range ends at the beginning of a line, then that line will not be highlighted.
18892 pub fn highlight_rows<T: 'static>(
18893 &mut self,
18894 range: Range<Anchor>,
18895 color: Hsla,
18896 options: RowHighlightOptions,
18897 cx: &mut Context<Self>,
18898 ) {
18899 let snapshot = self.buffer().read(cx).snapshot(cx);
18900 let row_highlights = self.highlighted_rows.entry(TypeId::of::<T>()).or_default();
18901 let ix = row_highlights.binary_search_by(|highlight| {
18902 Ordering::Equal
18903 .then_with(|| highlight.range.start.cmp(&range.start, &snapshot))
18904 .then_with(|| highlight.range.end.cmp(&range.end, &snapshot))
18905 });
18906
18907 if let Err(mut ix) = ix {
18908 let index = post_inc(&mut self.highlight_order);
18909
18910 // If this range intersects with the preceding highlight, then merge it with
18911 // the preceding highlight. Otherwise insert a new highlight.
18912 let mut merged = false;
18913 if ix > 0 {
18914 let prev_highlight = &mut row_highlights[ix - 1];
18915 if prev_highlight
18916 .range
18917 .end
18918 .cmp(&range.start, &snapshot)
18919 .is_ge()
18920 {
18921 ix -= 1;
18922 if prev_highlight.range.end.cmp(&range.end, &snapshot).is_lt() {
18923 prev_highlight.range.end = range.end;
18924 }
18925 merged = true;
18926 prev_highlight.index = index;
18927 prev_highlight.color = color;
18928 prev_highlight.options = options;
18929 }
18930 }
18931
18932 if !merged {
18933 row_highlights.insert(
18934 ix,
18935 RowHighlight {
18936 range: range.clone(),
18937 index,
18938 color,
18939 options,
18940 type_id: TypeId::of::<T>(),
18941 },
18942 );
18943 }
18944
18945 // If any of the following highlights intersect with this one, merge them.
18946 while let Some(next_highlight) = row_highlights.get(ix + 1) {
18947 let highlight = &row_highlights[ix];
18948 if next_highlight
18949 .range
18950 .start
18951 .cmp(&highlight.range.end, &snapshot)
18952 .is_le()
18953 {
18954 if next_highlight
18955 .range
18956 .end
18957 .cmp(&highlight.range.end, &snapshot)
18958 .is_gt()
18959 {
18960 row_highlights[ix].range.end = next_highlight.range.end;
18961 }
18962 row_highlights.remove(ix + 1);
18963 } else {
18964 break;
18965 }
18966 }
18967 }
18968 }
18969
18970 /// Remove any highlighted row ranges of the given type that intersect the
18971 /// given ranges.
18972 pub fn remove_highlighted_rows<T: 'static>(
18973 &mut self,
18974 ranges_to_remove: Vec<Range<Anchor>>,
18975 cx: &mut Context<Self>,
18976 ) {
18977 let snapshot = self.buffer().read(cx).snapshot(cx);
18978 let row_highlights = self.highlighted_rows.entry(TypeId::of::<T>()).or_default();
18979 let mut ranges_to_remove = ranges_to_remove.iter().peekable();
18980 row_highlights.retain(|highlight| {
18981 while let Some(range_to_remove) = ranges_to_remove.peek() {
18982 match range_to_remove.end.cmp(&highlight.range.start, &snapshot) {
18983 Ordering::Less | Ordering::Equal => {
18984 ranges_to_remove.next();
18985 }
18986 Ordering::Greater => {
18987 match range_to_remove.start.cmp(&highlight.range.end, &snapshot) {
18988 Ordering::Less | Ordering::Equal => {
18989 return false;
18990 }
18991 Ordering::Greater => break,
18992 }
18993 }
18994 }
18995 }
18996
18997 true
18998 })
18999 }
19000
19001 /// Clear all anchor ranges for a certain highlight context type, so no corresponding rows will be highlighted.
19002 pub fn clear_row_highlights<T: 'static>(&mut self) {
19003 self.highlighted_rows.remove(&TypeId::of::<T>());
19004 }
19005
19006 /// For a highlight given context type, gets all anchor ranges that will be used for row highlighting.
19007 pub fn highlighted_rows<T: 'static>(&self) -> impl '_ + Iterator<Item = (Range<Anchor>, Hsla)> {
19008 self.highlighted_rows
19009 .get(&TypeId::of::<T>())
19010 .map_or(&[] as &[_], |vec| vec.as_slice())
19011 .iter()
19012 .map(|highlight| (highlight.range.clone(), highlight.color))
19013 }
19014
19015 /// Merges all anchor ranges for all context types ever set, picking the last highlight added in case of a row conflict.
19016 /// Returns a map of display rows that are highlighted and their corresponding highlight color.
19017 /// Allows to ignore certain kinds of highlights.
19018 pub fn highlighted_display_rows(
19019 &self,
19020 window: &mut Window,
19021 cx: &mut App,
19022 ) -> BTreeMap<DisplayRow, LineHighlight> {
19023 let snapshot = self.snapshot(window, cx);
19024 let mut used_highlight_orders = HashMap::default();
19025 self.highlighted_rows
19026 .iter()
19027 .flat_map(|(_, highlighted_rows)| highlighted_rows.iter())
19028 .fold(
19029 BTreeMap::<DisplayRow, LineHighlight>::new(),
19030 |mut unique_rows, highlight| {
19031 let start = highlight.range.start.to_display_point(&snapshot);
19032 let end = highlight.range.end.to_display_point(&snapshot);
19033 let start_row = start.row().0;
19034 let end_row = if highlight.range.end.text_anchor != text::Anchor::MAX
19035 && end.column() == 0
19036 {
19037 end.row().0.saturating_sub(1)
19038 } else {
19039 end.row().0
19040 };
19041 for row in start_row..=end_row {
19042 let used_index =
19043 used_highlight_orders.entry(row).or_insert(highlight.index);
19044 if highlight.index >= *used_index {
19045 *used_index = highlight.index;
19046 unique_rows.insert(
19047 DisplayRow(row),
19048 LineHighlight {
19049 include_gutter: highlight.options.include_gutter,
19050 border: None,
19051 background: highlight.color.into(),
19052 type_id: Some(highlight.type_id),
19053 },
19054 );
19055 }
19056 }
19057 unique_rows
19058 },
19059 )
19060 }
19061
19062 pub fn highlighted_display_row_for_autoscroll(
19063 &self,
19064 snapshot: &DisplaySnapshot,
19065 ) -> Option<DisplayRow> {
19066 self.highlighted_rows
19067 .values()
19068 .flat_map(|highlighted_rows| highlighted_rows.iter())
19069 .filter_map(|highlight| {
19070 if highlight.options.autoscroll {
19071 Some(highlight.range.start.to_display_point(snapshot).row())
19072 } else {
19073 None
19074 }
19075 })
19076 .min()
19077 }
19078
19079 pub fn set_search_within_ranges(&mut self, ranges: &[Range<Anchor>], cx: &mut Context<Self>) {
19080 self.highlight_background::<SearchWithinRange>(
19081 ranges,
19082 |colors| colors.colors().editor_document_highlight_read_background,
19083 cx,
19084 )
19085 }
19086
19087 pub fn set_breadcrumb_header(&mut self, new_header: String) {
19088 self.breadcrumb_header = Some(new_header);
19089 }
19090
19091 pub fn clear_search_within_ranges(&mut self, cx: &mut Context<Self>) {
19092 self.clear_background_highlights::<SearchWithinRange>(cx);
19093 }
19094
19095 pub fn highlight_background<T: 'static>(
19096 &mut self,
19097 ranges: &[Range<Anchor>],
19098 color_fetcher: fn(&Theme) -> Hsla,
19099 cx: &mut Context<Self>,
19100 ) {
19101 self.background_highlights.insert(
19102 HighlightKey::Type(TypeId::of::<T>()),
19103 (color_fetcher, Arc::from(ranges)),
19104 );
19105 self.scrollbar_marker_state.dirty = true;
19106 cx.notify();
19107 }
19108
19109 pub fn highlight_background_key<T: 'static>(
19110 &mut self,
19111 key: usize,
19112 ranges: &[Range<Anchor>],
19113 color_fetcher: fn(&Theme) -> Hsla,
19114 cx: &mut Context<Self>,
19115 ) {
19116 self.background_highlights.insert(
19117 HighlightKey::TypePlus(TypeId::of::<T>(), key),
19118 (color_fetcher, Arc::from(ranges)),
19119 );
19120 self.scrollbar_marker_state.dirty = true;
19121 cx.notify();
19122 }
19123
19124 pub fn clear_background_highlights<T: 'static>(
19125 &mut self,
19126 cx: &mut Context<Self>,
19127 ) -> Option<BackgroundHighlight> {
19128 let text_highlights = self
19129 .background_highlights
19130 .remove(&HighlightKey::Type(TypeId::of::<T>()))?;
19131 if !text_highlights.1.is_empty() {
19132 self.scrollbar_marker_state.dirty = true;
19133 cx.notify();
19134 }
19135 Some(text_highlights)
19136 }
19137
19138 pub fn highlight_gutter<T: 'static>(
19139 &mut self,
19140 ranges: impl Into<Vec<Range<Anchor>>>,
19141 color_fetcher: fn(&App) -> Hsla,
19142 cx: &mut Context<Self>,
19143 ) {
19144 self.gutter_highlights
19145 .insert(TypeId::of::<T>(), (color_fetcher, ranges.into()));
19146 cx.notify();
19147 }
19148
19149 pub fn clear_gutter_highlights<T: 'static>(
19150 &mut self,
19151 cx: &mut Context<Self>,
19152 ) -> Option<GutterHighlight> {
19153 cx.notify();
19154 self.gutter_highlights.remove(&TypeId::of::<T>())
19155 }
19156
19157 pub fn insert_gutter_highlight<T: 'static>(
19158 &mut self,
19159 range: Range<Anchor>,
19160 color_fetcher: fn(&App) -> Hsla,
19161 cx: &mut Context<Self>,
19162 ) {
19163 let snapshot = self.buffer().read(cx).snapshot(cx);
19164 let mut highlights = self
19165 .gutter_highlights
19166 .remove(&TypeId::of::<T>())
19167 .map(|(_, highlights)| highlights)
19168 .unwrap_or_default();
19169 let ix = highlights.binary_search_by(|highlight| {
19170 Ordering::Equal
19171 .then_with(|| highlight.start.cmp(&range.start, &snapshot))
19172 .then_with(|| highlight.end.cmp(&range.end, &snapshot))
19173 });
19174 if let Err(ix) = ix {
19175 highlights.insert(ix, range);
19176 }
19177 self.gutter_highlights
19178 .insert(TypeId::of::<T>(), (color_fetcher, highlights));
19179 }
19180
19181 pub fn remove_gutter_highlights<T: 'static>(
19182 &mut self,
19183 ranges_to_remove: Vec<Range<Anchor>>,
19184 cx: &mut Context<Self>,
19185 ) {
19186 let snapshot = self.buffer().read(cx).snapshot(cx);
19187 let Some((color_fetcher, mut gutter_highlights)) =
19188 self.gutter_highlights.remove(&TypeId::of::<T>())
19189 else {
19190 return;
19191 };
19192 let mut ranges_to_remove = ranges_to_remove.iter().peekable();
19193 gutter_highlights.retain(|highlight| {
19194 while let Some(range_to_remove) = ranges_to_remove.peek() {
19195 match range_to_remove.end.cmp(&highlight.start, &snapshot) {
19196 Ordering::Less | Ordering::Equal => {
19197 ranges_to_remove.next();
19198 }
19199 Ordering::Greater => {
19200 match range_to_remove.start.cmp(&highlight.end, &snapshot) {
19201 Ordering::Less | Ordering::Equal => {
19202 return false;
19203 }
19204 Ordering::Greater => break,
19205 }
19206 }
19207 }
19208 }
19209
19210 true
19211 });
19212 self.gutter_highlights
19213 .insert(TypeId::of::<T>(), (color_fetcher, gutter_highlights));
19214 }
19215
19216 #[cfg(feature = "test-support")]
19217 pub fn all_text_highlights(
19218 &self,
19219 window: &mut Window,
19220 cx: &mut Context<Self>,
19221 ) -> Vec<(HighlightStyle, Vec<Range<DisplayPoint>>)> {
19222 let snapshot = self.snapshot(window, cx);
19223 self.display_map.update(cx, |display_map, _| {
19224 display_map
19225 .all_text_highlights()
19226 .map(|highlight| {
19227 let (style, ranges) = highlight.as_ref();
19228 (
19229 *style,
19230 ranges
19231 .iter()
19232 .map(|range| range.clone().to_display_points(&snapshot))
19233 .collect(),
19234 )
19235 })
19236 .collect()
19237 })
19238 }
19239
19240 #[cfg(feature = "test-support")]
19241 pub fn all_text_background_highlights(
19242 &self,
19243 window: &mut Window,
19244 cx: &mut Context<Self>,
19245 ) -> Vec<(Range<DisplayPoint>, Hsla)> {
19246 let snapshot = self.snapshot(window, cx);
19247 let buffer = &snapshot.buffer_snapshot;
19248 let start = buffer.anchor_before(0);
19249 let end = buffer.anchor_after(buffer.len());
19250 self.background_highlights_in_range(start..end, &snapshot, cx.theme())
19251 }
19252
19253 #[cfg(feature = "test-support")]
19254 pub fn search_background_highlights(&mut self, cx: &mut Context<Self>) -> Vec<Range<Point>> {
19255 let snapshot = self.buffer().read(cx).snapshot(cx);
19256
19257 let highlights = self
19258 .background_highlights
19259 .get(&HighlightKey::Type(TypeId::of::<
19260 items::BufferSearchHighlights,
19261 >()));
19262
19263 if let Some((_color, ranges)) = highlights {
19264 ranges
19265 .iter()
19266 .map(|range| range.start.to_point(&snapshot)..range.end.to_point(&snapshot))
19267 .collect_vec()
19268 } else {
19269 vec![]
19270 }
19271 }
19272
19273 fn document_highlights_for_position<'a>(
19274 &'a self,
19275 position: Anchor,
19276 buffer: &'a MultiBufferSnapshot,
19277 ) -> impl 'a + Iterator<Item = &'a Range<Anchor>> {
19278 let read_highlights = self
19279 .background_highlights
19280 .get(&HighlightKey::Type(TypeId::of::<DocumentHighlightRead>()))
19281 .map(|h| &h.1);
19282 let write_highlights = self
19283 .background_highlights
19284 .get(&HighlightKey::Type(TypeId::of::<DocumentHighlightWrite>()))
19285 .map(|h| &h.1);
19286 let left_position = position.bias_left(buffer);
19287 let right_position = position.bias_right(buffer);
19288 read_highlights
19289 .into_iter()
19290 .chain(write_highlights)
19291 .flat_map(move |ranges| {
19292 let start_ix = match ranges.binary_search_by(|probe| {
19293 let cmp = probe.end.cmp(&left_position, buffer);
19294 if cmp.is_ge() {
19295 Ordering::Greater
19296 } else {
19297 Ordering::Less
19298 }
19299 }) {
19300 Ok(i) | Err(i) => i,
19301 };
19302
19303 ranges[start_ix..]
19304 .iter()
19305 .take_while(move |range| range.start.cmp(&right_position, buffer).is_le())
19306 })
19307 }
19308
19309 pub fn has_background_highlights<T: 'static>(&self) -> bool {
19310 self.background_highlights
19311 .get(&HighlightKey::Type(TypeId::of::<T>()))
19312 .map_or(false, |(_, highlights)| !highlights.is_empty())
19313 }
19314
19315 pub fn background_highlights_in_range(
19316 &self,
19317 search_range: Range<Anchor>,
19318 display_snapshot: &DisplaySnapshot,
19319 theme: &Theme,
19320 ) -> Vec<(Range<DisplayPoint>, Hsla)> {
19321 let mut results = Vec::new();
19322 for (color_fetcher, ranges) in self.background_highlights.values() {
19323 let color = color_fetcher(theme);
19324 let start_ix = match ranges.binary_search_by(|probe| {
19325 let cmp = probe
19326 .end
19327 .cmp(&search_range.start, &display_snapshot.buffer_snapshot);
19328 if cmp.is_gt() {
19329 Ordering::Greater
19330 } else {
19331 Ordering::Less
19332 }
19333 }) {
19334 Ok(i) | Err(i) => i,
19335 };
19336 for range in &ranges[start_ix..] {
19337 if range
19338 .start
19339 .cmp(&search_range.end, &display_snapshot.buffer_snapshot)
19340 .is_ge()
19341 {
19342 break;
19343 }
19344
19345 let start = range.start.to_display_point(display_snapshot);
19346 let end = range.end.to_display_point(display_snapshot);
19347 results.push((start..end, color))
19348 }
19349 }
19350 results
19351 }
19352
19353 pub fn background_highlight_row_ranges<T: 'static>(
19354 &self,
19355 search_range: Range<Anchor>,
19356 display_snapshot: &DisplaySnapshot,
19357 count: usize,
19358 ) -> Vec<RangeInclusive<DisplayPoint>> {
19359 let mut results = Vec::new();
19360 let Some((_, ranges)) = self
19361 .background_highlights
19362 .get(&HighlightKey::Type(TypeId::of::<T>()))
19363 else {
19364 return vec![];
19365 };
19366
19367 let start_ix = match ranges.binary_search_by(|probe| {
19368 let cmp = probe
19369 .end
19370 .cmp(&search_range.start, &display_snapshot.buffer_snapshot);
19371 if cmp.is_gt() {
19372 Ordering::Greater
19373 } else {
19374 Ordering::Less
19375 }
19376 }) {
19377 Ok(i) | Err(i) => i,
19378 };
19379 let mut push_region = |start: Option<Point>, end: Option<Point>| {
19380 if let (Some(start_display), Some(end_display)) = (start, end) {
19381 results.push(
19382 start_display.to_display_point(display_snapshot)
19383 ..=end_display.to_display_point(display_snapshot),
19384 );
19385 }
19386 };
19387 let mut start_row: Option<Point> = None;
19388 let mut end_row: Option<Point> = None;
19389 if ranges.len() > count {
19390 return Vec::new();
19391 }
19392 for range in &ranges[start_ix..] {
19393 if range
19394 .start
19395 .cmp(&search_range.end, &display_snapshot.buffer_snapshot)
19396 .is_ge()
19397 {
19398 break;
19399 }
19400 let end = range.end.to_point(&display_snapshot.buffer_snapshot);
19401 if let Some(current_row) = &end_row {
19402 if end.row == current_row.row {
19403 continue;
19404 }
19405 }
19406 let start = range.start.to_point(&display_snapshot.buffer_snapshot);
19407 if start_row.is_none() {
19408 assert_eq!(end_row, None);
19409 start_row = Some(start);
19410 end_row = Some(end);
19411 continue;
19412 }
19413 if let Some(current_end) = end_row.as_mut() {
19414 if start.row > current_end.row + 1 {
19415 push_region(start_row, end_row);
19416 start_row = Some(start);
19417 end_row = Some(end);
19418 } else {
19419 // Merge two hunks.
19420 *current_end = end;
19421 }
19422 } else {
19423 unreachable!();
19424 }
19425 }
19426 // We might still have a hunk that was not rendered (if there was a search hit on the last line)
19427 push_region(start_row, end_row);
19428 results
19429 }
19430
19431 pub fn gutter_highlights_in_range(
19432 &self,
19433 search_range: Range<Anchor>,
19434 display_snapshot: &DisplaySnapshot,
19435 cx: &App,
19436 ) -> Vec<(Range<DisplayPoint>, Hsla)> {
19437 let mut results = Vec::new();
19438 for (color_fetcher, ranges) in self.gutter_highlights.values() {
19439 let color = color_fetcher(cx);
19440 let start_ix = match ranges.binary_search_by(|probe| {
19441 let cmp = probe
19442 .end
19443 .cmp(&search_range.start, &display_snapshot.buffer_snapshot);
19444 if cmp.is_gt() {
19445 Ordering::Greater
19446 } else {
19447 Ordering::Less
19448 }
19449 }) {
19450 Ok(i) | Err(i) => i,
19451 };
19452 for range in &ranges[start_ix..] {
19453 if range
19454 .start
19455 .cmp(&search_range.end, &display_snapshot.buffer_snapshot)
19456 .is_ge()
19457 {
19458 break;
19459 }
19460
19461 let start = range.start.to_display_point(display_snapshot);
19462 let end = range.end.to_display_point(display_snapshot);
19463 results.push((start..end, color))
19464 }
19465 }
19466 results
19467 }
19468
19469 /// Get the text ranges corresponding to the redaction query
19470 pub fn redacted_ranges(
19471 &self,
19472 search_range: Range<Anchor>,
19473 display_snapshot: &DisplaySnapshot,
19474 cx: &App,
19475 ) -> Vec<Range<DisplayPoint>> {
19476 display_snapshot
19477 .buffer_snapshot
19478 .redacted_ranges(search_range, |file| {
19479 if let Some(file) = file {
19480 file.is_private()
19481 && EditorSettings::get(
19482 Some(SettingsLocation {
19483 worktree_id: file.worktree_id(cx),
19484 path: file.path().as_ref(),
19485 }),
19486 cx,
19487 )
19488 .redact_private_values
19489 } else {
19490 false
19491 }
19492 })
19493 .map(|range| {
19494 range.start.to_display_point(display_snapshot)
19495 ..range.end.to_display_point(display_snapshot)
19496 })
19497 .collect()
19498 }
19499
19500 pub fn highlight_text_key<T: 'static>(
19501 &mut self,
19502 key: usize,
19503 ranges: Vec<Range<Anchor>>,
19504 style: HighlightStyle,
19505 cx: &mut Context<Self>,
19506 ) {
19507 self.display_map.update(cx, |map, _| {
19508 map.highlight_text(
19509 HighlightKey::TypePlus(TypeId::of::<T>(), key),
19510 ranges,
19511 style,
19512 );
19513 });
19514 cx.notify();
19515 }
19516
19517 pub fn highlight_text<T: 'static>(
19518 &mut self,
19519 ranges: Vec<Range<Anchor>>,
19520 style: HighlightStyle,
19521 cx: &mut Context<Self>,
19522 ) {
19523 self.display_map.update(cx, |map, _| {
19524 map.highlight_text(HighlightKey::Type(TypeId::of::<T>()), ranges, style)
19525 });
19526 cx.notify();
19527 }
19528
19529 pub(crate) fn highlight_inlays<T: 'static>(
19530 &mut self,
19531 highlights: Vec<InlayHighlight>,
19532 style: HighlightStyle,
19533 cx: &mut Context<Self>,
19534 ) {
19535 self.display_map.update(cx, |map, _| {
19536 map.highlight_inlays(TypeId::of::<T>(), highlights, style)
19537 });
19538 cx.notify();
19539 }
19540
19541 pub fn text_highlights<'a, T: 'static>(
19542 &'a self,
19543 cx: &'a App,
19544 ) -> Option<(HighlightStyle, &'a [Range<Anchor>])> {
19545 self.display_map.read(cx).text_highlights(TypeId::of::<T>())
19546 }
19547
19548 pub fn clear_highlights<T: 'static>(&mut self, cx: &mut Context<Self>) {
19549 let cleared = self
19550 .display_map
19551 .update(cx, |map, _| map.clear_highlights(TypeId::of::<T>()));
19552 if cleared {
19553 cx.notify();
19554 }
19555 }
19556
19557 pub fn show_local_cursors(&self, window: &mut Window, cx: &mut App) -> bool {
19558 (self.read_only(cx) || self.blink_manager.read(cx).visible())
19559 && self.focus_handle.is_focused(window)
19560 }
19561
19562 pub fn set_show_cursor_when_unfocused(&mut self, is_enabled: bool, cx: &mut Context<Self>) {
19563 self.show_cursor_when_unfocused = is_enabled;
19564 cx.notify();
19565 }
19566
19567 fn on_buffer_changed(&mut self, _: Entity<MultiBuffer>, cx: &mut Context<Self>) {
19568 cx.notify();
19569 }
19570
19571 fn on_debug_session_event(
19572 &mut self,
19573 _session: Entity<Session>,
19574 event: &SessionEvent,
19575 cx: &mut Context<Self>,
19576 ) {
19577 match event {
19578 SessionEvent::InvalidateInlineValue => {
19579 self.refresh_inline_values(cx);
19580 }
19581 _ => {}
19582 }
19583 }
19584
19585 pub fn refresh_inline_values(&mut self, cx: &mut Context<Self>) {
19586 let Some(project) = self.project.clone() else {
19587 return;
19588 };
19589
19590 if !self.inline_value_cache.enabled {
19591 let inlays = std::mem::take(&mut self.inline_value_cache.inlays);
19592 self.splice_inlays(&inlays, Vec::new(), cx);
19593 return;
19594 }
19595
19596 let current_execution_position = self
19597 .highlighted_rows
19598 .get(&TypeId::of::<ActiveDebugLine>())
19599 .and_then(|lines| lines.last().map(|line| line.range.end));
19600
19601 self.inline_value_cache.refresh_task = cx.spawn(async move |editor, cx| {
19602 let inline_values = editor
19603 .update(cx, |editor, cx| {
19604 let Some(current_execution_position) = current_execution_position else {
19605 return Some(Task::ready(Ok(Vec::new())));
19606 };
19607
19608 let buffer = editor.buffer.read_with(cx, |buffer, cx| {
19609 let snapshot = buffer.snapshot(cx);
19610
19611 let excerpt = snapshot.excerpt_containing(
19612 current_execution_position..current_execution_position,
19613 )?;
19614
19615 editor.buffer.read(cx).buffer(excerpt.buffer_id())
19616 })?;
19617
19618 let range =
19619 buffer.read(cx).anchor_before(0)..current_execution_position.text_anchor;
19620
19621 project.inline_values(buffer, range, cx)
19622 })
19623 .ok()
19624 .flatten()?
19625 .await
19626 .context("refreshing debugger inlays")
19627 .log_err()?;
19628
19629 let mut buffer_inline_values: HashMap<BufferId, Vec<InlayHint>> = HashMap::default();
19630
19631 for (buffer_id, inline_value) in inline_values
19632 .into_iter()
19633 .filter_map(|hint| Some((hint.position.buffer_id?, hint)))
19634 {
19635 buffer_inline_values
19636 .entry(buffer_id)
19637 .or_default()
19638 .push(inline_value);
19639 }
19640
19641 editor
19642 .update(cx, |editor, cx| {
19643 let snapshot = editor.buffer.read(cx).snapshot(cx);
19644 let mut new_inlays = Vec::default();
19645
19646 for (excerpt_id, buffer_snapshot, _) in snapshot.excerpts() {
19647 let buffer_id = buffer_snapshot.remote_id();
19648 buffer_inline_values
19649 .get(&buffer_id)
19650 .into_iter()
19651 .flatten()
19652 .for_each(|hint| {
19653 let inlay = Inlay::debugger(
19654 post_inc(&mut editor.next_inlay_id),
19655 Anchor::in_buffer(excerpt_id, buffer_id, hint.position),
19656 hint.text(),
19657 );
19658
19659 new_inlays.push(inlay);
19660 });
19661 }
19662
19663 let mut inlay_ids = new_inlays.iter().map(|inlay| inlay.id).collect();
19664 std::mem::swap(&mut editor.inline_value_cache.inlays, &mut inlay_ids);
19665
19666 editor.splice_inlays(&inlay_ids, new_inlays, cx);
19667 })
19668 .ok()?;
19669 Some(())
19670 });
19671 }
19672
19673 fn on_buffer_event(
19674 &mut self,
19675 multibuffer: &Entity<MultiBuffer>,
19676 event: &multi_buffer::Event,
19677 window: &mut Window,
19678 cx: &mut Context<Self>,
19679 ) {
19680 match event {
19681 multi_buffer::Event::Edited {
19682 singleton_buffer_edited,
19683 edited_buffer,
19684 } => {
19685 self.scrollbar_marker_state.dirty = true;
19686 self.active_indent_guides_state.dirty = true;
19687 self.refresh_active_diagnostics(cx);
19688 self.refresh_code_actions(window, cx);
19689 self.refresh_selected_text_highlights(true, window, cx);
19690 self.refresh_single_line_folds(window, cx);
19691 refresh_matching_bracket_highlights(self, window, cx);
19692 if self.has_active_inline_completion() {
19693 self.update_visible_inline_completion(window, cx);
19694 }
19695 if let Some(project) = self.project.as_ref() {
19696 if let Some(edited_buffer) = edited_buffer {
19697 project.update(cx, |project, cx| {
19698 self.registered_buffers
19699 .entry(edited_buffer.read(cx).remote_id())
19700 .or_insert_with(|| {
19701 project
19702 .register_buffer_with_language_servers(&edited_buffer, cx)
19703 });
19704 });
19705 }
19706 }
19707 cx.emit(EditorEvent::BufferEdited);
19708 cx.emit(SearchEvent::MatchesInvalidated);
19709
19710 if let Some(buffer) = edited_buffer {
19711 self.update_lsp_data(false, Some(buffer.read(cx).remote_id()), window, cx);
19712 }
19713
19714 if *singleton_buffer_edited {
19715 if let Some(buffer) = edited_buffer {
19716 if buffer.read(cx).file().is_none() {
19717 cx.emit(EditorEvent::TitleChanged);
19718 }
19719 }
19720 if let Some(project) = &self.project {
19721 #[allow(clippy::mutable_key_type)]
19722 let languages_affected = multibuffer.update(cx, |multibuffer, cx| {
19723 multibuffer
19724 .all_buffers()
19725 .into_iter()
19726 .filter_map(|buffer| {
19727 buffer.update(cx, |buffer, cx| {
19728 let language = buffer.language()?;
19729 let should_discard = project.update(cx, |project, cx| {
19730 project.is_local()
19731 && !project.has_language_servers_for(buffer, cx)
19732 });
19733 should_discard.not().then_some(language.clone())
19734 })
19735 })
19736 .collect::<HashSet<_>>()
19737 });
19738 if !languages_affected.is_empty() {
19739 self.refresh_inlay_hints(
19740 InlayHintRefreshReason::BufferEdited(languages_affected),
19741 cx,
19742 );
19743 }
19744 }
19745 }
19746
19747 let Some(project) = &self.project else { return };
19748 let (telemetry, is_via_ssh) = {
19749 let project = project.read(cx);
19750 let telemetry = project.client().telemetry().clone();
19751 let is_via_ssh = project.is_via_ssh();
19752 (telemetry, is_via_ssh)
19753 };
19754 refresh_linked_ranges(self, window, cx);
19755 telemetry.log_edit_event("editor", is_via_ssh);
19756 }
19757 multi_buffer::Event::ExcerptsAdded {
19758 buffer,
19759 predecessor,
19760 excerpts,
19761 } => {
19762 self.tasks_update_task = Some(self.refresh_runnables(window, cx));
19763 let buffer_id = buffer.read(cx).remote_id();
19764 if self.buffer.read(cx).diff_for(buffer_id).is_none() {
19765 if let Some(project) = &self.project {
19766 update_uncommitted_diff_for_buffer(
19767 cx.entity(),
19768 project,
19769 [buffer.clone()],
19770 self.buffer.clone(),
19771 cx,
19772 )
19773 .detach();
19774 }
19775 }
19776 self.update_lsp_data(false, Some(buffer_id), window, cx);
19777 cx.emit(EditorEvent::ExcerptsAdded {
19778 buffer: buffer.clone(),
19779 predecessor: *predecessor,
19780 excerpts: excerpts.clone(),
19781 });
19782 self.refresh_inlay_hints(InlayHintRefreshReason::NewLinesShown, cx);
19783 }
19784 multi_buffer::Event::ExcerptsRemoved {
19785 ids,
19786 removed_buffer_ids,
19787 } => {
19788 self.refresh_inlay_hints(InlayHintRefreshReason::ExcerptsRemoved(ids.clone()), cx);
19789 let buffer = self.buffer.read(cx);
19790 self.registered_buffers
19791 .retain(|buffer_id, _| buffer.buffer(*buffer_id).is_some());
19792 jsx_tag_auto_close::refresh_enabled_in_any_buffer(self, multibuffer, cx);
19793 cx.emit(EditorEvent::ExcerptsRemoved {
19794 ids: ids.clone(),
19795 removed_buffer_ids: removed_buffer_ids.clone(),
19796 });
19797 }
19798 multi_buffer::Event::ExcerptsEdited {
19799 excerpt_ids,
19800 buffer_ids,
19801 } => {
19802 self.display_map.update(cx, |map, cx| {
19803 map.unfold_buffers(buffer_ids.iter().copied(), cx)
19804 });
19805 cx.emit(EditorEvent::ExcerptsEdited {
19806 ids: excerpt_ids.clone(),
19807 });
19808 }
19809 multi_buffer::Event::ExcerptsExpanded { ids } => {
19810 self.refresh_inlay_hints(InlayHintRefreshReason::NewLinesShown, cx);
19811 cx.emit(EditorEvent::ExcerptsExpanded { ids: ids.clone() })
19812 }
19813 multi_buffer::Event::Reparsed(buffer_id) => {
19814 self.tasks_update_task = Some(self.refresh_runnables(window, cx));
19815 jsx_tag_auto_close::refresh_enabled_in_any_buffer(self, multibuffer, cx);
19816
19817 cx.emit(EditorEvent::Reparsed(*buffer_id));
19818 }
19819 multi_buffer::Event::DiffHunksToggled => {
19820 self.tasks_update_task = Some(self.refresh_runnables(window, cx));
19821 }
19822 multi_buffer::Event::LanguageChanged(buffer_id) => {
19823 linked_editing_ranges::refresh_linked_ranges(self, window, cx);
19824 jsx_tag_auto_close::refresh_enabled_in_any_buffer(self, multibuffer, cx);
19825 cx.emit(EditorEvent::Reparsed(*buffer_id));
19826 cx.notify();
19827 }
19828 multi_buffer::Event::DirtyChanged => cx.emit(EditorEvent::DirtyChanged),
19829 multi_buffer::Event::Saved => cx.emit(EditorEvent::Saved),
19830 multi_buffer::Event::FileHandleChanged
19831 | multi_buffer::Event::Reloaded
19832 | multi_buffer::Event::BufferDiffChanged => cx.emit(EditorEvent::TitleChanged),
19833 multi_buffer::Event::Closed => cx.emit(EditorEvent::Closed),
19834 multi_buffer::Event::DiagnosticsUpdated => {
19835 self.update_diagnostics_state(window, cx);
19836 }
19837 _ => {}
19838 };
19839 }
19840
19841 fn update_diagnostics_state(&mut self, window: &mut Window, cx: &mut Context<'_, Editor>) {
19842 if !self.diagnostics_enabled() {
19843 return;
19844 }
19845 self.refresh_active_diagnostics(cx);
19846 self.refresh_inline_diagnostics(true, window, cx);
19847 self.scrollbar_marker_state.dirty = true;
19848 cx.notify();
19849 }
19850
19851 pub fn start_temporary_diff_override(&mut self) {
19852 self.load_diff_task.take();
19853 self.temporary_diff_override = true;
19854 }
19855
19856 pub fn end_temporary_diff_override(&mut self, cx: &mut Context<Self>) {
19857 self.temporary_diff_override = false;
19858 self.set_render_diff_hunk_controls(Arc::new(render_diff_hunk_controls), cx);
19859 self.buffer.update(cx, |buffer, cx| {
19860 buffer.set_all_diff_hunks_collapsed(cx);
19861 });
19862
19863 if let Some(project) = self.project.clone() {
19864 self.load_diff_task = Some(
19865 update_uncommitted_diff_for_buffer(
19866 cx.entity(),
19867 &project,
19868 self.buffer.read(cx).all_buffers(),
19869 self.buffer.clone(),
19870 cx,
19871 )
19872 .shared(),
19873 );
19874 }
19875 }
19876
19877 fn on_display_map_changed(
19878 &mut self,
19879 _: Entity<DisplayMap>,
19880 _: &mut Window,
19881 cx: &mut Context<Self>,
19882 ) {
19883 cx.notify();
19884 }
19885
19886 fn settings_changed(&mut self, window: &mut Window, cx: &mut Context<Self>) {
19887 let new_severity = if self.diagnostics_enabled() {
19888 EditorSettings::get_global(cx)
19889 .diagnostics_max_severity
19890 .unwrap_or(DiagnosticSeverity::Hint)
19891 } else {
19892 DiagnosticSeverity::Off
19893 };
19894 self.set_max_diagnostics_severity(new_severity, cx);
19895 self.tasks_update_task = Some(self.refresh_runnables(window, cx));
19896 self.update_edit_prediction_settings(cx);
19897 self.refresh_inline_completion(true, false, window, cx);
19898 self.refresh_inline_values(cx);
19899 self.refresh_inlay_hints(
19900 InlayHintRefreshReason::SettingsChange(inlay_hint_settings(
19901 self.selections.newest_anchor().head(),
19902 &self.buffer.read(cx).snapshot(cx),
19903 cx,
19904 )),
19905 cx,
19906 );
19907
19908 let old_cursor_shape = self.cursor_shape;
19909
19910 {
19911 let editor_settings = EditorSettings::get_global(cx);
19912 self.scroll_manager.vertical_scroll_margin = editor_settings.vertical_scroll_margin;
19913 self.show_breadcrumbs = editor_settings.toolbar.breadcrumbs;
19914 self.cursor_shape = editor_settings.cursor_shape.unwrap_or_default();
19915 self.hide_mouse_mode = editor_settings.hide_mouse.unwrap_or_default();
19916 }
19917
19918 if old_cursor_shape != self.cursor_shape {
19919 cx.emit(EditorEvent::CursorShapeChanged);
19920 }
19921
19922 let project_settings = ProjectSettings::get_global(cx);
19923 self.serialize_dirty_buffers =
19924 !self.mode.is_minimap() && project_settings.session.restore_unsaved_buffers;
19925
19926 if self.mode.is_full() {
19927 let show_inline_diagnostics = project_settings.diagnostics.inline.enabled;
19928 let inline_blame_enabled = project_settings.git.inline_blame_enabled();
19929 if self.show_inline_diagnostics != show_inline_diagnostics {
19930 self.show_inline_diagnostics = show_inline_diagnostics;
19931 self.refresh_inline_diagnostics(false, window, cx);
19932 }
19933
19934 if self.git_blame_inline_enabled != inline_blame_enabled {
19935 self.toggle_git_blame_inline_internal(false, window, cx);
19936 }
19937
19938 let minimap_settings = EditorSettings::get_global(cx).minimap;
19939 if self.minimap_visibility != MinimapVisibility::Disabled {
19940 if self.minimap_visibility.settings_visibility()
19941 != minimap_settings.minimap_enabled()
19942 {
19943 self.set_minimap_visibility(
19944 MinimapVisibility::for_mode(self.mode(), cx),
19945 window,
19946 cx,
19947 );
19948 } else if let Some(minimap_entity) = self.minimap.as_ref() {
19949 minimap_entity.update(cx, |minimap_editor, cx| {
19950 minimap_editor.update_minimap_configuration(minimap_settings, cx)
19951 })
19952 }
19953 }
19954 }
19955
19956 if let Some(inlay_splice) = self.colors.as_mut().and_then(|colors| {
19957 colors.render_mode_updated(EditorSettings::get_global(cx).lsp_document_colors)
19958 }) {
19959 if !inlay_splice.to_insert.is_empty() || !inlay_splice.to_remove.is_empty() {
19960 self.splice_inlays(&inlay_splice.to_remove, inlay_splice.to_insert, cx);
19961 }
19962 self.refresh_colors(false, None, window, cx);
19963 }
19964
19965 cx.notify();
19966 }
19967
19968 pub fn set_searchable(&mut self, searchable: bool) {
19969 self.searchable = searchable;
19970 }
19971
19972 pub fn searchable(&self) -> bool {
19973 self.searchable
19974 }
19975
19976 fn open_proposed_changes_editor(
19977 &mut self,
19978 _: &OpenProposedChangesEditor,
19979 window: &mut Window,
19980 cx: &mut Context<Self>,
19981 ) {
19982 let Some(workspace) = self.workspace() else {
19983 cx.propagate();
19984 return;
19985 };
19986
19987 let selections = self.selections.all::<usize>(cx);
19988 let multi_buffer = self.buffer.read(cx);
19989 let multi_buffer_snapshot = multi_buffer.snapshot(cx);
19990 let mut new_selections_by_buffer = HashMap::default();
19991 for selection in selections {
19992 for (buffer, range, _) in
19993 multi_buffer_snapshot.range_to_buffer_ranges(selection.start..selection.end)
19994 {
19995 let mut range = range.to_point(buffer);
19996 range.start.column = 0;
19997 range.end.column = buffer.line_len(range.end.row);
19998 new_selections_by_buffer
19999 .entry(multi_buffer.buffer(buffer.remote_id()).unwrap())
20000 .or_insert(Vec::new())
20001 .push(range)
20002 }
20003 }
20004
20005 let proposed_changes_buffers = new_selections_by_buffer
20006 .into_iter()
20007 .map(|(buffer, ranges)| ProposedChangeLocation { buffer, ranges })
20008 .collect::<Vec<_>>();
20009 let proposed_changes_editor = cx.new(|cx| {
20010 ProposedChangesEditor::new(
20011 "Proposed changes",
20012 proposed_changes_buffers,
20013 self.project.clone(),
20014 window,
20015 cx,
20016 )
20017 });
20018
20019 window.defer(cx, move |window, cx| {
20020 workspace.update(cx, |workspace, cx| {
20021 workspace.active_pane().update(cx, |pane, cx| {
20022 pane.add_item(
20023 Box::new(proposed_changes_editor),
20024 true,
20025 true,
20026 None,
20027 window,
20028 cx,
20029 );
20030 });
20031 });
20032 });
20033 }
20034
20035 pub fn open_excerpts_in_split(
20036 &mut self,
20037 _: &OpenExcerptsSplit,
20038 window: &mut Window,
20039 cx: &mut Context<Self>,
20040 ) {
20041 self.open_excerpts_common(None, true, window, cx)
20042 }
20043
20044 pub fn open_excerpts(&mut self, _: &OpenExcerpts, window: &mut Window, cx: &mut Context<Self>) {
20045 self.open_excerpts_common(None, false, window, cx)
20046 }
20047
20048 fn open_excerpts_common(
20049 &mut self,
20050 jump_data: Option<JumpData>,
20051 split: bool,
20052 window: &mut Window,
20053 cx: &mut Context<Self>,
20054 ) {
20055 let Some(workspace) = self.workspace() else {
20056 cx.propagate();
20057 return;
20058 };
20059
20060 if self.buffer.read(cx).is_singleton() {
20061 cx.propagate();
20062 return;
20063 }
20064
20065 let mut new_selections_by_buffer = HashMap::default();
20066 match &jump_data {
20067 Some(JumpData::MultiBufferPoint {
20068 excerpt_id,
20069 position,
20070 anchor,
20071 line_offset_from_top,
20072 }) => {
20073 let multi_buffer_snapshot = self.buffer.read(cx).snapshot(cx);
20074 if let Some(buffer) = multi_buffer_snapshot
20075 .buffer_id_for_excerpt(*excerpt_id)
20076 .and_then(|buffer_id| self.buffer.read(cx).buffer(buffer_id))
20077 {
20078 let buffer_snapshot = buffer.read(cx).snapshot();
20079 let jump_to_point = if buffer_snapshot.can_resolve(anchor) {
20080 language::ToPoint::to_point(anchor, &buffer_snapshot)
20081 } else {
20082 buffer_snapshot.clip_point(*position, Bias::Left)
20083 };
20084 let jump_to_offset = buffer_snapshot.point_to_offset(jump_to_point);
20085 new_selections_by_buffer.insert(
20086 buffer,
20087 (
20088 vec![jump_to_offset..jump_to_offset],
20089 Some(*line_offset_from_top),
20090 ),
20091 );
20092 }
20093 }
20094 Some(JumpData::MultiBufferRow {
20095 row,
20096 line_offset_from_top,
20097 }) => {
20098 let point = MultiBufferPoint::new(row.0, 0);
20099 if let Some((buffer, buffer_point, _)) =
20100 self.buffer.read(cx).point_to_buffer_point(point, cx)
20101 {
20102 let buffer_offset = buffer.read(cx).point_to_offset(buffer_point);
20103 new_selections_by_buffer
20104 .entry(buffer)
20105 .or_insert((Vec::new(), Some(*line_offset_from_top)))
20106 .0
20107 .push(buffer_offset..buffer_offset)
20108 }
20109 }
20110 None => {
20111 let selections = self.selections.all::<usize>(cx);
20112 let multi_buffer = self.buffer.read(cx);
20113 for selection in selections {
20114 for (snapshot, range, _, anchor) in multi_buffer
20115 .snapshot(cx)
20116 .range_to_buffer_ranges_with_deleted_hunks(selection.range())
20117 {
20118 if let Some(anchor) = anchor {
20119 // selection is in a deleted hunk
20120 let Some(buffer_id) = anchor.buffer_id else {
20121 continue;
20122 };
20123 let Some(buffer_handle) = multi_buffer.buffer(buffer_id) else {
20124 continue;
20125 };
20126 let offset = text::ToOffset::to_offset(
20127 &anchor.text_anchor,
20128 &buffer_handle.read(cx).snapshot(),
20129 );
20130 let range = offset..offset;
20131 new_selections_by_buffer
20132 .entry(buffer_handle)
20133 .or_insert((Vec::new(), None))
20134 .0
20135 .push(range)
20136 } else {
20137 let Some(buffer_handle) = multi_buffer.buffer(snapshot.remote_id())
20138 else {
20139 continue;
20140 };
20141 new_selections_by_buffer
20142 .entry(buffer_handle)
20143 .or_insert((Vec::new(), None))
20144 .0
20145 .push(range)
20146 }
20147 }
20148 }
20149 }
20150 }
20151
20152 new_selections_by_buffer
20153 .retain(|buffer, _| Self::can_open_excerpts_in_file(buffer.read(cx).file()));
20154
20155 if new_selections_by_buffer.is_empty() {
20156 return;
20157 }
20158
20159 // We defer the pane interaction because we ourselves are a workspace item
20160 // and activating a new item causes the pane to call a method on us reentrantly,
20161 // which panics if we're on the stack.
20162 window.defer(cx, move |window, cx| {
20163 workspace.update(cx, |workspace, cx| {
20164 let pane = if split {
20165 workspace.adjacent_pane(window, cx)
20166 } else {
20167 workspace.active_pane().clone()
20168 };
20169
20170 for (buffer, (ranges, scroll_offset)) in new_selections_by_buffer {
20171 let editor = buffer
20172 .read(cx)
20173 .file()
20174 .is_none()
20175 .then(|| {
20176 // Handle file-less buffers separately: those are not really the project items, so won't have a project path or entity id,
20177 // so `workspace.open_project_item` will never find them, always opening a new editor.
20178 // Instead, we try to activate the existing editor in the pane first.
20179 let (editor, pane_item_index) =
20180 pane.read(cx).items().enumerate().find_map(|(i, item)| {
20181 let editor = item.downcast::<Editor>()?;
20182 let singleton_buffer =
20183 editor.read(cx).buffer().read(cx).as_singleton()?;
20184 if singleton_buffer == buffer {
20185 Some((editor, i))
20186 } else {
20187 None
20188 }
20189 })?;
20190 pane.update(cx, |pane, cx| {
20191 pane.activate_item(pane_item_index, true, true, window, cx)
20192 });
20193 Some(editor)
20194 })
20195 .flatten()
20196 .unwrap_or_else(|| {
20197 workspace.open_project_item::<Self>(
20198 pane.clone(),
20199 buffer,
20200 true,
20201 true,
20202 window,
20203 cx,
20204 )
20205 });
20206
20207 editor.update(cx, |editor, cx| {
20208 let autoscroll = match scroll_offset {
20209 Some(scroll_offset) => Autoscroll::top_relative(scroll_offset as usize),
20210 None => Autoscroll::newest(),
20211 };
20212 let nav_history = editor.nav_history.take();
20213 editor.change_selections(
20214 SelectionEffects::scroll(autoscroll),
20215 window,
20216 cx,
20217 |s| {
20218 s.select_ranges(ranges);
20219 },
20220 );
20221 editor.nav_history = nav_history;
20222 });
20223 }
20224 })
20225 });
20226 }
20227
20228 // For now, don't allow opening excerpts in buffers that aren't backed by
20229 // regular project files.
20230 fn can_open_excerpts_in_file(file: Option<&Arc<dyn language::File>>) -> bool {
20231 file.map_or(true, |file| project::File::from_dyn(Some(file)).is_some())
20232 }
20233
20234 fn marked_text_ranges(&self, cx: &App) -> Option<Vec<Range<OffsetUtf16>>> {
20235 let snapshot = self.buffer.read(cx).read(cx);
20236 let (_, ranges) = self.text_highlights::<InputComposition>(cx)?;
20237 Some(
20238 ranges
20239 .iter()
20240 .map(move |range| {
20241 range.start.to_offset_utf16(&snapshot)..range.end.to_offset_utf16(&snapshot)
20242 })
20243 .collect(),
20244 )
20245 }
20246
20247 fn selection_replacement_ranges(
20248 &self,
20249 range: Range<OffsetUtf16>,
20250 cx: &mut App,
20251 ) -> Vec<Range<OffsetUtf16>> {
20252 let selections = self.selections.all::<OffsetUtf16>(cx);
20253 let newest_selection = selections
20254 .iter()
20255 .max_by_key(|selection| selection.id)
20256 .unwrap();
20257 let start_delta = range.start.0 as isize - newest_selection.start.0 as isize;
20258 let end_delta = range.end.0 as isize - newest_selection.end.0 as isize;
20259 let snapshot = self.buffer.read(cx).read(cx);
20260 selections
20261 .into_iter()
20262 .map(|mut selection| {
20263 selection.start.0 =
20264 (selection.start.0 as isize).saturating_add(start_delta) as usize;
20265 selection.end.0 = (selection.end.0 as isize).saturating_add(end_delta) as usize;
20266 snapshot.clip_offset_utf16(selection.start, Bias::Left)
20267 ..snapshot.clip_offset_utf16(selection.end, Bias::Right)
20268 })
20269 .collect()
20270 }
20271
20272 fn report_editor_event(
20273 &self,
20274 event_type: &'static str,
20275 file_extension: Option<String>,
20276 cx: &App,
20277 ) {
20278 if cfg!(any(test, feature = "test-support")) {
20279 return;
20280 }
20281
20282 let Some(project) = &self.project else { return };
20283
20284 // If None, we are in a file without an extension
20285 let file = self
20286 .buffer
20287 .read(cx)
20288 .as_singleton()
20289 .and_then(|b| b.read(cx).file());
20290 let file_extension = file_extension.or(file
20291 .as_ref()
20292 .and_then(|file| Path::new(file.file_name(cx)).extension())
20293 .and_then(|e| e.to_str())
20294 .map(|a| a.to_string()));
20295
20296 let vim_mode = vim_enabled(cx);
20297
20298 let edit_predictions_provider = all_language_settings(file, cx).edit_predictions.provider;
20299 let copilot_enabled = edit_predictions_provider
20300 == language::language_settings::EditPredictionProvider::Copilot;
20301 let copilot_enabled_for_language = self
20302 .buffer
20303 .read(cx)
20304 .language_settings(cx)
20305 .show_edit_predictions;
20306
20307 let project = project.read(cx);
20308 telemetry::event!(
20309 event_type,
20310 file_extension,
20311 vim_mode,
20312 copilot_enabled,
20313 copilot_enabled_for_language,
20314 edit_predictions_provider,
20315 is_via_ssh = project.is_via_ssh(),
20316 );
20317 }
20318
20319 /// Copy the highlighted chunks to the clipboard as JSON. The format is an array of lines,
20320 /// with each line being an array of {text, highlight} objects.
20321 fn copy_highlight_json(
20322 &mut self,
20323 _: &CopyHighlightJson,
20324 window: &mut Window,
20325 cx: &mut Context<Self>,
20326 ) {
20327 #[derive(Serialize)]
20328 struct Chunk<'a> {
20329 text: String,
20330 highlight: Option<&'a str>,
20331 }
20332
20333 let snapshot = self.buffer.read(cx).snapshot(cx);
20334 let range = self
20335 .selected_text_range(false, window, cx)
20336 .and_then(|selection| {
20337 if selection.range.is_empty() {
20338 None
20339 } else {
20340 Some(selection.range)
20341 }
20342 })
20343 .unwrap_or_else(|| 0..snapshot.len());
20344
20345 let chunks = snapshot.chunks(range, true);
20346 let mut lines = Vec::new();
20347 let mut line: VecDeque<Chunk> = VecDeque::new();
20348
20349 let Some(style) = self.style.as_ref() else {
20350 return;
20351 };
20352
20353 for chunk in chunks {
20354 let highlight = chunk
20355 .syntax_highlight_id
20356 .and_then(|id| id.name(&style.syntax));
20357 let mut chunk_lines = chunk.text.split('\n').peekable();
20358 while let Some(text) = chunk_lines.next() {
20359 let mut merged_with_last_token = false;
20360 if let Some(last_token) = line.back_mut() {
20361 if last_token.highlight == highlight {
20362 last_token.text.push_str(text);
20363 merged_with_last_token = true;
20364 }
20365 }
20366
20367 if !merged_with_last_token {
20368 line.push_back(Chunk {
20369 text: text.into(),
20370 highlight,
20371 });
20372 }
20373
20374 if chunk_lines.peek().is_some() {
20375 if line.len() > 1 && line.front().unwrap().text.is_empty() {
20376 line.pop_front();
20377 }
20378 if line.len() > 1 && line.back().unwrap().text.is_empty() {
20379 line.pop_back();
20380 }
20381
20382 lines.push(mem::take(&mut line));
20383 }
20384 }
20385 }
20386
20387 let Some(lines) = serde_json::to_string_pretty(&lines).log_err() else {
20388 return;
20389 };
20390 cx.write_to_clipboard(ClipboardItem::new_string(lines));
20391 }
20392
20393 pub fn open_context_menu(
20394 &mut self,
20395 _: &OpenContextMenu,
20396 window: &mut Window,
20397 cx: &mut Context<Self>,
20398 ) {
20399 self.request_autoscroll(Autoscroll::newest(), cx);
20400 let position = self.selections.newest_display(cx).start;
20401 mouse_context_menu::deploy_context_menu(self, None, position, window, cx);
20402 }
20403
20404 pub fn inlay_hint_cache(&self) -> &InlayHintCache {
20405 &self.inlay_hint_cache
20406 }
20407
20408 pub fn replay_insert_event(
20409 &mut self,
20410 text: &str,
20411 relative_utf16_range: Option<Range<isize>>,
20412 window: &mut Window,
20413 cx: &mut Context<Self>,
20414 ) {
20415 if !self.input_enabled {
20416 cx.emit(EditorEvent::InputIgnored { text: text.into() });
20417 return;
20418 }
20419 if let Some(relative_utf16_range) = relative_utf16_range {
20420 let selections = self.selections.all::<OffsetUtf16>(cx);
20421 self.change_selections(SelectionEffects::no_scroll(), window, cx, |s| {
20422 let new_ranges = selections.into_iter().map(|range| {
20423 let start = OffsetUtf16(
20424 range
20425 .head()
20426 .0
20427 .saturating_add_signed(relative_utf16_range.start),
20428 );
20429 let end = OffsetUtf16(
20430 range
20431 .head()
20432 .0
20433 .saturating_add_signed(relative_utf16_range.end),
20434 );
20435 start..end
20436 });
20437 s.select_ranges(new_ranges);
20438 });
20439 }
20440
20441 self.handle_input(text, window, cx);
20442 }
20443
20444 pub fn supports_inlay_hints(&self, cx: &mut App) -> bool {
20445 let Some(provider) = self.semantics_provider.as_ref() else {
20446 return false;
20447 };
20448
20449 let mut supports = false;
20450 self.buffer().update(cx, |this, cx| {
20451 this.for_each_buffer(|buffer| {
20452 supports |= provider.supports_inlay_hints(buffer, cx);
20453 });
20454 });
20455
20456 supports
20457 }
20458
20459 pub fn is_focused(&self, window: &Window) -> bool {
20460 self.focus_handle.is_focused(window)
20461 }
20462
20463 fn handle_focus(&mut self, window: &mut Window, cx: &mut Context<Self>) {
20464 cx.emit(EditorEvent::Focused);
20465
20466 if let Some(descendant) = self
20467 .last_focused_descendant
20468 .take()
20469 .and_then(|descendant| descendant.upgrade())
20470 {
20471 window.focus(&descendant);
20472 } else {
20473 if let Some(blame) = self.blame.as_ref() {
20474 blame.update(cx, GitBlame::focus)
20475 }
20476
20477 self.blink_manager.update(cx, BlinkManager::enable);
20478 self.show_cursor_names(window, cx);
20479 self.buffer.update(cx, |buffer, cx| {
20480 buffer.finalize_last_transaction(cx);
20481 if self.leader_id.is_none() {
20482 buffer.set_active_selections(
20483 &self.selections.disjoint_anchors(),
20484 self.selections.line_mode,
20485 self.cursor_shape,
20486 cx,
20487 );
20488 }
20489 });
20490 }
20491 }
20492
20493 fn handle_focus_in(&mut self, _: &mut Window, cx: &mut Context<Self>) {
20494 cx.emit(EditorEvent::FocusedIn)
20495 }
20496
20497 fn handle_focus_out(
20498 &mut self,
20499 event: FocusOutEvent,
20500 _window: &mut Window,
20501 cx: &mut Context<Self>,
20502 ) {
20503 if event.blurred != self.focus_handle {
20504 self.last_focused_descendant = Some(event.blurred);
20505 }
20506 self.refresh_inlay_hints(InlayHintRefreshReason::ModifiersChanged(false), cx);
20507 }
20508
20509 pub fn handle_blur(&mut self, window: &mut Window, cx: &mut Context<Self>) {
20510 self.blink_manager.update(cx, BlinkManager::disable);
20511 self.buffer
20512 .update(cx, |buffer, cx| buffer.remove_active_selections(cx));
20513
20514 if let Some(blame) = self.blame.as_ref() {
20515 blame.update(cx, GitBlame::blur)
20516 }
20517 if !self.hover_state.focused(window, cx) {
20518 hide_hover(self, cx);
20519 }
20520 if !self
20521 .context_menu
20522 .borrow()
20523 .as_ref()
20524 .is_some_and(|context_menu| context_menu.focused(window, cx))
20525 {
20526 self.hide_context_menu(window, cx);
20527 }
20528 self.discard_inline_completion(false, cx);
20529 cx.emit(EditorEvent::Blurred);
20530 cx.notify();
20531 }
20532
20533 pub fn observe_pending_input(&mut self, window: &mut Window, cx: &mut Context<Self>) {
20534 let mut pending: String = window
20535 .pending_input_keystrokes()
20536 .into_iter()
20537 .flatten()
20538 .filter_map(|keystroke| {
20539 if keystroke.modifiers.is_subset_of(&Modifiers::shift()) {
20540 keystroke.key_char.clone()
20541 } else {
20542 None
20543 }
20544 })
20545 .collect();
20546
20547 if !self.input_enabled || self.read_only || !self.focus_handle.is_focused(window) {
20548 pending = "".to_string();
20549 }
20550
20551 let existing_pending = self
20552 .text_highlights::<PendingInput>(cx)
20553 .map(|(_, ranges)| ranges.iter().cloned().collect::<Vec<_>>());
20554 if existing_pending.is_none() && pending.is_empty() {
20555 return;
20556 }
20557 let transaction =
20558 self.transact(window, cx, |this, window, cx| {
20559 let selections = this.selections.all::<usize>(cx);
20560 let edits = selections
20561 .iter()
20562 .map(|selection| (selection.end..selection.end, pending.clone()));
20563 this.edit(edits, cx);
20564 this.change_selections(SelectionEffects::no_scroll(), window, cx, |s| {
20565 s.select_ranges(selections.into_iter().enumerate().map(|(ix, sel)| {
20566 sel.start + ix * pending.len()..sel.end + ix * pending.len()
20567 }));
20568 });
20569 if let Some(existing_ranges) = existing_pending {
20570 let edits = existing_ranges.iter().map(|range| (range.clone(), ""));
20571 this.edit(edits, cx);
20572 }
20573 });
20574
20575 let snapshot = self.snapshot(window, cx);
20576 let ranges = self
20577 .selections
20578 .all::<usize>(cx)
20579 .into_iter()
20580 .map(|selection| {
20581 snapshot.buffer_snapshot.anchor_after(selection.end)
20582 ..snapshot
20583 .buffer_snapshot
20584 .anchor_before(selection.end + pending.len())
20585 })
20586 .collect();
20587
20588 if pending.is_empty() {
20589 self.clear_highlights::<PendingInput>(cx);
20590 } else {
20591 self.highlight_text::<PendingInput>(
20592 ranges,
20593 HighlightStyle {
20594 underline: Some(UnderlineStyle {
20595 thickness: px(1.),
20596 color: None,
20597 wavy: false,
20598 }),
20599 ..Default::default()
20600 },
20601 cx,
20602 );
20603 }
20604
20605 self.ime_transaction = self.ime_transaction.or(transaction);
20606 if let Some(transaction) = self.ime_transaction {
20607 self.buffer.update(cx, |buffer, cx| {
20608 buffer.group_until_transaction(transaction, cx);
20609 });
20610 }
20611
20612 if self.text_highlights::<PendingInput>(cx).is_none() {
20613 self.ime_transaction.take();
20614 }
20615 }
20616
20617 pub fn register_action_renderer(
20618 &mut self,
20619 listener: impl Fn(&Editor, &mut Window, &mut Context<Editor>) + 'static,
20620 ) -> Subscription {
20621 let id = self.next_editor_action_id.post_inc();
20622 self.editor_actions
20623 .borrow_mut()
20624 .insert(id, Box::new(listener));
20625
20626 let editor_actions = self.editor_actions.clone();
20627 Subscription::new(move || {
20628 editor_actions.borrow_mut().remove(&id);
20629 })
20630 }
20631
20632 pub fn register_action<A: Action>(
20633 &mut self,
20634 listener: impl Fn(&A, &mut Window, &mut App) + 'static,
20635 ) -> Subscription {
20636 let id = self.next_editor_action_id.post_inc();
20637 let listener = Arc::new(listener);
20638 self.editor_actions.borrow_mut().insert(
20639 id,
20640 Box::new(move |_, window, _| {
20641 let listener = listener.clone();
20642 window.on_action(TypeId::of::<A>(), move |action, phase, window, cx| {
20643 let action = action.downcast_ref().unwrap();
20644 if phase == DispatchPhase::Bubble {
20645 listener(action, window, cx)
20646 }
20647 })
20648 }),
20649 );
20650
20651 let editor_actions = self.editor_actions.clone();
20652 Subscription::new(move || {
20653 editor_actions.borrow_mut().remove(&id);
20654 })
20655 }
20656
20657 pub fn file_header_size(&self) -> u32 {
20658 FILE_HEADER_HEIGHT
20659 }
20660
20661 pub fn restore(
20662 &mut self,
20663 revert_changes: HashMap<BufferId, Vec<(Range<text::Anchor>, Rope)>>,
20664 window: &mut Window,
20665 cx: &mut Context<Self>,
20666 ) {
20667 let workspace = self.workspace();
20668 let project = self.project.as_ref();
20669 let save_tasks = self.buffer().update(cx, |multi_buffer, cx| {
20670 let mut tasks = Vec::new();
20671 for (buffer_id, changes) in revert_changes {
20672 if let Some(buffer) = multi_buffer.buffer(buffer_id) {
20673 buffer.update(cx, |buffer, cx| {
20674 buffer.edit(
20675 changes
20676 .into_iter()
20677 .map(|(range, text)| (range, text.to_string())),
20678 None,
20679 cx,
20680 );
20681 });
20682
20683 if let Some(project) =
20684 project.filter(|_| multi_buffer.all_diff_hunks_expanded())
20685 {
20686 project.update(cx, |project, cx| {
20687 tasks.push((buffer.clone(), project.save_buffer(buffer, cx)));
20688 })
20689 }
20690 }
20691 }
20692 tasks
20693 });
20694 cx.spawn_in(window, async move |_, cx| {
20695 for (buffer, task) in save_tasks {
20696 let result = task.await;
20697 if result.is_err() {
20698 let Some(path) = buffer
20699 .read_with(cx, |buffer, cx| buffer.project_path(cx))
20700 .ok()
20701 else {
20702 continue;
20703 };
20704 if let Some((workspace, path)) = workspace.as_ref().zip(path) {
20705 let Some(task) = cx
20706 .update_window_entity(&workspace, |workspace, window, cx| {
20707 workspace
20708 .open_path_preview(path, None, false, false, false, window, cx)
20709 })
20710 .ok()
20711 else {
20712 continue;
20713 };
20714 task.await.log_err();
20715 }
20716 }
20717 }
20718 })
20719 .detach();
20720 self.change_selections(SelectionEffects::no_scroll(), window, cx, |selections| {
20721 selections.refresh()
20722 });
20723 }
20724
20725 pub fn to_pixel_point(
20726 &self,
20727 source: multi_buffer::Anchor,
20728 editor_snapshot: &EditorSnapshot,
20729 window: &mut Window,
20730 ) -> Option<gpui::Point<Pixels>> {
20731 let source_point = source.to_display_point(editor_snapshot);
20732 self.display_to_pixel_point(source_point, editor_snapshot, window)
20733 }
20734
20735 pub fn display_to_pixel_point(
20736 &self,
20737 source: DisplayPoint,
20738 editor_snapshot: &EditorSnapshot,
20739 window: &mut Window,
20740 ) -> Option<gpui::Point<Pixels>> {
20741 let line_height = self.style()?.text.line_height_in_pixels(window.rem_size());
20742 let text_layout_details = self.text_layout_details(window);
20743 let scroll_top = text_layout_details
20744 .scroll_anchor
20745 .scroll_position(editor_snapshot)
20746 .y;
20747
20748 if source.row().as_f32() < scroll_top.floor() {
20749 return None;
20750 }
20751 let source_x = editor_snapshot.x_for_display_point(source, &text_layout_details);
20752 let source_y = line_height * (source.row().as_f32() - scroll_top);
20753 Some(gpui::Point::new(source_x, source_y))
20754 }
20755
20756 pub fn has_visible_completions_menu(&self) -> bool {
20757 !self.edit_prediction_preview_is_active()
20758 && self.context_menu.borrow().as_ref().map_or(false, |menu| {
20759 menu.visible() && matches!(menu, CodeContextMenu::Completions(_))
20760 })
20761 }
20762
20763 pub fn register_addon<T: Addon>(&mut self, instance: T) {
20764 if self.mode.is_minimap() {
20765 return;
20766 }
20767 self.addons
20768 .insert(std::any::TypeId::of::<T>(), Box::new(instance));
20769 }
20770
20771 pub fn unregister_addon<T: Addon>(&mut self) {
20772 self.addons.remove(&std::any::TypeId::of::<T>());
20773 }
20774
20775 pub fn addon<T: Addon>(&self) -> Option<&T> {
20776 let type_id = std::any::TypeId::of::<T>();
20777 self.addons
20778 .get(&type_id)
20779 .and_then(|item| item.to_any().downcast_ref::<T>())
20780 }
20781
20782 pub fn addon_mut<T: Addon>(&mut self) -> Option<&mut T> {
20783 let type_id = std::any::TypeId::of::<T>();
20784 self.addons
20785 .get_mut(&type_id)
20786 .and_then(|item| item.to_any_mut()?.downcast_mut::<T>())
20787 }
20788
20789 fn character_dimensions(&self, window: &mut Window) -> CharacterDimensions {
20790 let text_layout_details = self.text_layout_details(window);
20791 let style = &text_layout_details.editor_style;
20792 let font_id = window.text_system().resolve_font(&style.text.font());
20793 let font_size = style.text.font_size.to_pixels(window.rem_size());
20794 let line_height = style.text.line_height_in_pixels(window.rem_size());
20795 let em_width = window.text_system().em_width(font_id, font_size).unwrap();
20796 let em_advance = window.text_system().em_advance(font_id, font_size).unwrap();
20797
20798 CharacterDimensions {
20799 em_width,
20800 em_advance,
20801 line_height,
20802 }
20803 }
20804
20805 pub fn wait_for_diff_to_load(&self) -> Option<Shared<Task<()>>> {
20806 self.load_diff_task.clone()
20807 }
20808
20809 fn read_metadata_from_db(
20810 &mut self,
20811 item_id: u64,
20812 workspace_id: WorkspaceId,
20813 window: &mut Window,
20814 cx: &mut Context<Editor>,
20815 ) {
20816 if self.is_singleton(cx)
20817 && !self.mode.is_minimap()
20818 && WorkspaceSettings::get(None, cx).restore_on_startup != RestoreOnStartupBehavior::None
20819 {
20820 let buffer_snapshot = OnceCell::new();
20821
20822 if let Some(folds) = DB.get_editor_folds(item_id, workspace_id).log_err() {
20823 if !folds.is_empty() {
20824 let snapshot =
20825 buffer_snapshot.get_or_init(|| self.buffer.read(cx).snapshot(cx));
20826 self.fold_ranges(
20827 folds
20828 .into_iter()
20829 .map(|(start, end)| {
20830 snapshot.clip_offset(start, Bias::Left)
20831 ..snapshot.clip_offset(end, Bias::Right)
20832 })
20833 .collect(),
20834 false,
20835 window,
20836 cx,
20837 );
20838 }
20839 }
20840
20841 if let Some(selections) = DB.get_editor_selections(item_id, workspace_id).log_err() {
20842 if !selections.is_empty() {
20843 let snapshot =
20844 buffer_snapshot.get_or_init(|| self.buffer.read(cx).snapshot(cx));
20845 // skip adding the initial selection to selection history
20846 self.selection_history.mode = SelectionHistoryMode::Skipping;
20847 self.change_selections(SelectionEffects::no_scroll(), window, cx, |s| {
20848 s.select_ranges(selections.into_iter().map(|(start, end)| {
20849 snapshot.clip_offset(start, Bias::Left)
20850 ..snapshot.clip_offset(end, Bias::Right)
20851 }));
20852 });
20853 self.selection_history.mode = SelectionHistoryMode::Normal;
20854 }
20855 };
20856 }
20857
20858 self.read_scroll_position_from_db(item_id, workspace_id, window, cx);
20859 }
20860
20861 fn update_lsp_data(
20862 &mut self,
20863 ignore_cache: bool,
20864 for_buffer: Option<BufferId>,
20865 window: &mut Window,
20866 cx: &mut Context<'_, Self>,
20867 ) {
20868 self.pull_diagnostics(for_buffer, window, cx);
20869 self.refresh_colors(ignore_cache, for_buffer, window, cx);
20870 }
20871}
20872
20873fn vim_enabled(cx: &App) -> bool {
20874 cx.global::<SettingsStore>()
20875 .raw_user_settings()
20876 .get("vim_mode")
20877 == Some(&serde_json::Value::Bool(true))
20878}
20879
20880fn process_completion_for_edit(
20881 completion: &Completion,
20882 intent: CompletionIntent,
20883 buffer: &Entity<Buffer>,
20884 cursor_position: &text::Anchor,
20885 cx: &mut Context<Editor>,
20886) -> CompletionEdit {
20887 let buffer = buffer.read(cx);
20888 let buffer_snapshot = buffer.snapshot();
20889 let (snippet, new_text) = if completion.is_snippet() {
20890 // Workaround for typescript language server issues so that methods don't expand within
20891 // strings and functions with type expressions. The previous point is used because the query
20892 // for function identifier doesn't match when the cursor is immediately after. See PR #30312
20893 let mut snippet_source = completion.new_text.clone();
20894 let mut previous_point = text::ToPoint::to_point(cursor_position, buffer);
20895 previous_point.column = previous_point.column.saturating_sub(1);
20896 if let Some(scope) = buffer_snapshot.language_scope_at(previous_point) {
20897 if scope.prefers_label_for_snippet_in_completion() {
20898 if let Some(label) = completion.label() {
20899 if matches!(
20900 completion.kind(),
20901 Some(CompletionItemKind::FUNCTION) | Some(CompletionItemKind::METHOD)
20902 ) {
20903 snippet_source = label;
20904 }
20905 }
20906 }
20907 }
20908 match Snippet::parse(&snippet_source).log_err() {
20909 Some(parsed_snippet) => (Some(parsed_snippet.clone()), parsed_snippet.text),
20910 None => (None, completion.new_text.clone()),
20911 }
20912 } else {
20913 (None, completion.new_text.clone())
20914 };
20915
20916 let mut range_to_replace = {
20917 let replace_range = &completion.replace_range;
20918 if let CompletionSource::Lsp {
20919 insert_range: Some(insert_range),
20920 ..
20921 } = &completion.source
20922 {
20923 debug_assert_eq!(
20924 insert_range.start, replace_range.start,
20925 "insert_range and replace_range should start at the same position"
20926 );
20927 debug_assert!(
20928 insert_range
20929 .start
20930 .cmp(&cursor_position, &buffer_snapshot)
20931 .is_le(),
20932 "insert_range should start before or at cursor position"
20933 );
20934 debug_assert!(
20935 replace_range
20936 .start
20937 .cmp(&cursor_position, &buffer_snapshot)
20938 .is_le(),
20939 "replace_range should start before or at cursor position"
20940 );
20941 debug_assert!(
20942 insert_range
20943 .end
20944 .cmp(&cursor_position, &buffer_snapshot)
20945 .is_le(),
20946 "insert_range should end before or at cursor position"
20947 );
20948
20949 let should_replace = match intent {
20950 CompletionIntent::CompleteWithInsert => false,
20951 CompletionIntent::CompleteWithReplace => true,
20952 CompletionIntent::Complete | CompletionIntent::Compose => {
20953 let insert_mode =
20954 language_settings(buffer.language().map(|l| l.name()), buffer.file(), cx)
20955 .completions
20956 .lsp_insert_mode;
20957 match insert_mode {
20958 LspInsertMode::Insert => false,
20959 LspInsertMode::Replace => true,
20960 LspInsertMode::ReplaceSubsequence => {
20961 let mut text_to_replace = buffer.chars_for_range(
20962 buffer.anchor_before(replace_range.start)
20963 ..buffer.anchor_after(replace_range.end),
20964 );
20965 let mut current_needle = text_to_replace.next();
20966 for haystack_ch in completion.label.text.chars() {
20967 if let Some(needle_ch) = current_needle {
20968 if haystack_ch.eq_ignore_ascii_case(&needle_ch) {
20969 current_needle = text_to_replace.next();
20970 }
20971 }
20972 }
20973 current_needle.is_none()
20974 }
20975 LspInsertMode::ReplaceSuffix => {
20976 if replace_range
20977 .end
20978 .cmp(&cursor_position, &buffer_snapshot)
20979 .is_gt()
20980 {
20981 let range_after_cursor = *cursor_position..replace_range.end;
20982 let text_after_cursor = buffer
20983 .text_for_range(
20984 buffer.anchor_before(range_after_cursor.start)
20985 ..buffer.anchor_after(range_after_cursor.end),
20986 )
20987 .collect::<String>()
20988 .to_ascii_lowercase();
20989 completion
20990 .label
20991 .text
20992 .to_ascii_lowercase()
20993 .ends_with(&text_after_cursor)
20994 } else {
20995 true
20996 }
20997 }
20998 }
20999 }
21000 };
21001
21002 if should_replace {
21003 replace_range.clone()
21004 } else {
21005 insert_range.clone()
21006 }
21007 } else {
21008 replace_range.clone()
21009 }
21010 };
21011
21012 if range_to_replace
21013 .end
21014 .cmp(&cursor_position, &buffer_snapshot)
21015 .is_lt()
21016 {
21017 range_to_replace.end = *cursor_position;
21018 }
21019
21020 CompletionEdit {
21021 new_text,
21022 replace_range: range_to_replace.to_offset(&buffer),
21023 snippet,
21024 }
21025}
21026
21027struct CompletionEdit {
21028 new_text: String,
21029 replace_range: Range<usize>,
21030 snippet: Option<Snippet>,
21031}
21032
21033fn insert_extra_newline_brackets(
21034 buffer: &MultiBufferSnapshot,
21035 range: Range<usize>,
21036 language: &language::LanguageScope,
21037) -> bool {
21038 let leading_whitespace_len = buffer
21039 .reversed_chars_at(range.start)
21040 .take_while(|c| c.is_whitespace() && *c != '\n')
21041 .map(|c| c.len_utf8())
21042 .sum::<usize>();
21043 let trailing_whitespace_len = buffer
21044 .chars_at(range.end)
21045 .take_while(|c| c.is_whitespace() && *c != '\n')
21046 .map(|c| c.len_utf8())
21047 .sum::<usize>();
21048 let range = range.start - leading_whitespace_len..range.end + trailing_whitespace_len;
21049
21050 language.brackets().any(|(pair, enabled)| {
21051 let pair_start = pair.start.trim_end();
21052 let pair_end = pair.end.trim_start();
21053
21054 enabled
21055 && pair.newline
21056 && buffer.contains_str_at(range.end, pair_end)
21057 && buffer.contains_str_at(range.start.saturating_sub(pair_start.len()), pair_start)
21058 })
21059}
21060
21061fn insert_extra_newline_tree_sitter(buffer: &MultiBufferSnapshot, range: Range<usize>) -> bool {
21062 let (buffer, range) = match buffer.range_to_buffer_ranges(range).as_slice() {
21063 [(buffer, range, _)] => (*buffer, range.clone()),
21064 _ => return false,
21065 };
21066 let pair = {
21067 let mut result: Option<BracketMatch> = None;
21068
21069 for pair in buffer
21070 .all_bracket_ranges(range.clone())
21071 .filter(move |pair| {
21072 pair.open_range.start <= range.start && pair.close_range.end >= range.end
21073 })
21074 {
21075 let len = pair.close_range.end - pair.open_range.start;
21076
21077 if let Some(existing) = &result {
21078 let existing_len = existing.close_range.end - existing.open_range.start;
21079 if len > existing_len {
21080 continue;
21081 }
21082 }
21083
21084 result = Some(pair);
21085 }
21086
21087 result
21088 };
21089 let Some(pair) = pair else {
21090 return false;
21091 };
21092 pair.newline_only
21093 && buffer
21094 .chars_for_range(pair.open_range.end..range.start)
21095 .chain(buffer.chars_for_range(range.end..pair.close_range.start))
21096 .all(|c| c.is_whitespace() && c != '\n')
21097}
21098
21099fn update_uncommitted_diff_for_buffer(
21100 editor: Entity<Editor>,
21101 project: &Entity<Project>,
21102 buffers: impl IntoIterator<Item = Entity<Buffer>>,
21103 buffer: Entity<MultiBuffer>,
21104 cx: &mut App,
21105) -> Task<()> {
21106 let mut tasks = Vec::new();
21107 project.update(cx, |project, cx| {
21108 for buffer in buffers {
21109 if project::File::from_dyn(buffer.read(cx).file()).is_some() {
21110 tasks.push(project.open_uncommitted_diff(buffer.clone(), cx))
21111 }
21112 }
21113 });
21114 cx.spawn(async move |cx| {
21115 let diffs = future::join_all(tasks).await;
21116 if editor
21117 .read_with(cx, |editor, _cx| editor.temporary_diff_override)
21118 .unwrap_or(false)
21119 {
21120 return;
21121 }
21122
21123 buffer
21124 .update(cx, |buffer, cx| {
21125 for diff in diffs.into_iter().flatten() {
21126 buffer.add_diff(diff, cx);
21127 }
21128 })
21129 .ok();
21130 })
21131}
21132
21133fn char_len_with_expanded_tabs(offset: usize, text: &str, tab_size: NonZeroU32) -> usize {
21134 let tab_size = tab_size.get() as usize;
21135 let mut width = offset;
21136
21137 for ch in text.chars() {
21138 width += if ch == '\t' {
21139 tab_size - (width % tab_size)
21140 } else {
21141 1
21142 };
21143 }
21144
21145 width - offset
21146}
21147
21148#[cfg(test)]
21149mod tests {
21150 use super::*;
21151
21152 #[test]
21153 fn test_string_size_with_expanded_tabs() {
21154 let nz = |val| NonZeroU32::new(val).unwrap();
21155 assert_eq!(char_len_with_expanded_tabs(0, "", nz(4)), 0);
21156 assert_eq!(char_len_with_expanded_tabs(0, "hello", nz(4)), 5);
21157 assert_eq!(char_len_with_expanded_tabs(0, "\thello", nz(4)), 9);
21158 assert_eq!(char_len_with_expanded_tabs(0, "abc\tab", nz(4)), 6);
21159 assert_eq!(char_len_with_expanded_tabs(0, "hello\t", nz(4)), 8);
21160 assert_eq!(char_len_with_expanded_tabs(0, "\t\t", nz(8)), 16);
21161 assert_eq!(char_len_with_expanded_tabs(0, "x\t", nz(8)), 8);
21162 assert_eq!(char_len_with_expanded_tabs(7, "x\t", nz(8)), 9);
21163 }
21164}
21165
21166/// Tokenizes a string into runs of text that should stick together, or that is whitespace.
21167struct WordBreakingTokenizer<'a> {
21168 input: &'a str,
21169}
21170
21171impl<'a> WordBreakingTokenizer<'a> {
21172 fn new(input: &'a str) -> Self {
21173 Self { input }
21174 }
21175}
21176
21177fn is_char_ideographic(ch: char) -> bool {
21178 use unicode_script::Script::*;
21179 use unicode_script::UnicodeScript;
21180 matches!(ch.script(), Han | Tangut | Yi)
21181}
21182
21183fn is_grapheme_ideographic(text: &str) -> bool {
21184 text.chars().any(is_char_ideographic)
21185}
21186
21187fn is_grapheme_whitespace(text: &str) -> bool {
21188 text.chars().any(|x| x.is_whitespace())
21189}
21190
21191fn should_stay_with_preceding_ideograph(text: &str) -> bool {
21192 text.chars().next().map_or(false, |ch| {
21193 matches!(ch, '。' | '、' | ',' | '?' | '!' | ':' | ';' | '…')
21194 })
21195}
21196
21197#[derive(PartialEq, Eq, Debug, Clone, Copy)]
21198enum WordBreakToken<'a> {
21199 Word { token: &'a str, grapheme_len: usize },
21200 InlineWhitespace { token: &'a str, grapheme_len: usize },
21201 Newline,
21202}
21203
21204impl<'a> Iterator for WordBreakingTokenizer<'a> {
21205 /// Yields a span, the count of graphemes in the token, and whether it was
21206 /// whitespace. Note that it also breaks at word boundaries.
21207 type Item = WordBreakToken<'a>;
21208
21209 fn next(&mut self) -> Option<Self::Item> {
21210 use unicode_segmentation::UnicodeSegmentation;
21211 if self.input.is_empty() {
21212 return None;
21213 }
21214
21215 let mut iter = self.input.graphemes(true).peekable();
21216 let mut offset = 0;
21217 let mut grapheme_len = 0;
21218 if let Some(first_grapheme) = iter.next() {
21219 let is_newline = first_grapheme == "\n";
21220 let is_whitespace = is_grapheme_whitespace(first_grapheme);
21221 offset += first_grapheme.len();
21222 grapheme_len += 1;
21223 if is_grapheme_ideographic(first_grapheme) && !is_whitespace {
21224 if let Some(grapheme) = iter.peek().copied() {
21225 if should_stay_with_preceding_ideograph(grapheme) {
21226 offset += grapheme.len();
21227 grapheme_len += 1;
21228 }
21229 }
21230 } else {
21231 let mut words = self.input[offset..].split_word_bound_indices().peekable();
21232 let mut next_word_bound = words.peek().copied();
21233 if next_word_bound.map_or(false, |(i, _)| i == 0) {
21234 next_word_bound = words.next();
21235 }
21236 while let Some(grapheme) = iter.peek().copied() {
21237 if next_word_bound.map_or(false, |(i, _)| i == offset) {
21238 break;
21239 };
21240 if is_grapheme_whitespace(grapheme) != is_whitespace
21241 || (grapheme == "\n") != is_newline
21242 {
21243 break;
21244 };
21245 offset += grapheme.len();
21246 grapheme_len += 1;
21247 iter.next();
21248 }
21249 }
21250 let token = &self.input[..offset];
21251 self.input = &self.input[offset..];
21252 if token == "\n" {
21253 Some(WordBreakToken::Newline)
21254 } else if is_whitespace {
21255 Some(WordBreakToken::InlineWhitespace {
21256 token,
21257 grapheme_len,
21258 })
21259 } else {
21260 Some(WordBreakToken::Word {
21261 token,
21262 grapheme_len,
21263 })
21264 }
21265 } else {
21266 None
21267 }
21268 }
21269}
21270
21271#[test]
21272fn test_word_breaking_tokenizer() {
21273 let tests: &[(&str, &[WordBreakToken<'static>])] = &[
21274 ("", &[]),
21275 (" ", &[whitespace(" ", 2)]),
21276 ("Ʒ", &[word("Ʒ", 1)]),
21277 ("Ǽ", &[word("Ǽ", 1)]),
21278 ("⋑", &[word("⋑", 1)]),
21279 ("⋑⋑", &[word("⋑⋑", 2)]),
21280 (
21281 "原理,进而",
21282 &[word("原", 1), word("理,", 2), word("进", 1), word("而", 1)],
21283 ),
21284 (
21285 "hello world",
21286 &[word("hello", 5), whitespace(" ", 1), word("world", 5)],
21287 ),
21288 (
21289 "hello, world",
21290 &[word("hello,", 6), whitespace(" ", 1), word("world", 5)],
21291 ),
21292 (
21293 " hello world",
21294 &[
21295 whitespace(" ", 2),
21296 word("hello", 5),
21297 whitespace(" ", 1),
21298 word("world", 5),
21299 ],
21300 ),
21301 (
21302 "这是什么 \n 钢笔",
21303 &[
21304 word("这", 1),
21305 word("是", 1),
21306 word("什", 1),
21307 word("么", 1),
21308 whitespace(" ", 1),
21309 newline(),
21310 whitespace(" ", 1),
21311 word("钢", 1),
21312 word("笔", 1),
21313 ],
21314 ),
21315 (" mutton", &[whitespace(" ", 1), word("mutton", 6)]),
21316 ];
21317
21318 fn word(token: &'static str, grapheme_len: usize) -> WordBreakToken<'static> {
21319 WordBreakToken::Word {
21320 token,
21321 grapheme_len,
21322 }
21323 }
21324
21325 fn whitespace(token: &'static str, grapheme_len: usize) -> WordBreakToken<'static> {
21326 WordBreakToken::InlineWhitespace {
21327 token,
21328 grapheme_len,
21329 }
21330 }
21331
21332 fn newline() -> WordBreakToken<'static> {
21333 WordBreakToken::Newline
21334 }
21335
21336 for (input, result) in tests {
21337 assert_eq!(
21338 WordBreakingTokenizer::new(input)
21339 .collect::<Vec<_>>()
21340 .as_slice(),
21341 *result,
21342 );
21343 }
21344}
21345
21346fn wrap_with_prefix(
21347 first_line_prefix: String,
21348 subsequent_lines_prefix: String,
21349 unwrapped_text: String,
21350 wrap_column: usize,
21351 tab_size: NonZeroU32,
21352 preserve_existing_whitespace: bool,
21353) -> String {
21354 let first_line_prefix_len = char_len_with_expanded_tabs(0, &first_line_prefix, tab_size);
21355 let subsequent_lines_prefix_len =
21356 char_len_with_expanded_tabs(0, &subsequent_lines_prefix, tab_size);
21357 let mut wrapped_text = String::new();
21358 let mut current_line = first_line_prefix.clone();
21359 let mut is_first_line = true;
21360
21361 let tokenizer = WordBreakingTokenizer::new(&unwrapped_text);
21362 let mut current_line_len = first_line_prefix_len;
21363 let mut in_whitespace = false;
21364 for token in tokenizer {
21365 let have_preceding_whitespace = in_whitespace;
21366 match token {
21367 WordBreakToken::Word {
21368 token,
21369 grapheme_len,
21370 } => {
21371 in_whitespace = false;
21372 let current_prefix_len = if is_first_line {
21373 first_line_prefix_len
21374 } else {
21375 subsequent_lines_prefix_len
21376 };
21377 if current_line_len + grapheme_len > wrap_column
21378 && current_line_len != current_prefix_len
21379 {
21380 wrapped_text.push_str(current_line.trim_end());
21381 wrapped_text.push('\n');
21382 is_first_line = false;
21383 current_line = subsequent_lines_prefix.clone();
21384 current_line_len = subsequent_lines_prefix_len;
21385 }
21386 current_line.push_str(token);
21387 current_line_len += grapheme_len;
21388 }
21389 WordBreakToken::InlineWhitespace {
21390 mut token,
21391 mut grapheme_len,
21392 } => {
21393 in_whitespace = true;
21394 if have_preceding_whitespace && !preserve_existing_whitespace {
21395 continue;
21396 }
21397 if !preserve_existing_whitespace {
21398 token = " ";
21399 grapheme_len = 1;
21400 }
21401 let current_prefix_len = if is_first_line {
21402 first_line_prefix_len
21403 } else {
21404 subsequent_lines_prefix_len
21405 };
21406 if current_line_len + grapheme_len > wrap_column {
21407 wrapped_text.push_str(current_line.trim_end());
21408 wrapped_text.push('\n');
21409 is_first_line = false;
21410 current_line = subsequent_lines_prefix.clone();
21411 current_line_len = subsequent_lines_prefix_len;
21412 } else if current_line_len != current_prefix_len || preserve_existing_whitespace {
21413 current_line.push_str(token);
21414 current_line_len += grapheme_len;
21415 }
21416 }
21417 WordBreakToken::Newline => {
21418 in_whitespace = true;
21419 let current_prefix_len = if is_first_line {
21420 first_line_prefix_len
21421 } else {
21422 subsequent_lines_prefix_len
21423 };
21424 if preserve_existing_whitespace {
21425 wrapped_text.push_str(current_line.trim_end());
21426 wrapped_text.push('\n');
21427 is_first_line = false;
21428 current_line = subsequent_lines_prefix.clone();
21429 current_line_len = subsequent_lines_prefix_len;
21430 } else if have_preceding_whitespace {
21431 continue;
21432 } else if current_line_len + 1 > wrap_column
21433 && current_line_len != current_prefix_len
21434 {
21435 wrapped_text.push_str(current_line.trim_end());
21436 wrapped_text.push('\n');
21437 is_first_line = false;
21438 current_line = subsequent_lines_prefix.clone();
21439 current_line_len = subsequent_lines_prefix_len;
21440 } else if current_line_len != current_prefix_len {
21441 current_line.push(' ');
21442 current_line_len += 1;
21443 }
21444 }
21445 }
21446 }
21447
21448 if !current_line.is_empty() {
21449 wrapped_text.push_str(¤t_line);
21450 }
21451 wrapped_text
21452}
21453
21454#[test]
21455fn test_wrap_with_prefix() {
21456 assert_eq!(
21457 wrap_with_prefix(
21458 "# ".to_string(),
21459 "# ".to_string(),
21460 "abcdefg".to_string(),
21461 4,
21462 NonZeroU32::new(4).unwrap(),
21463 false,
21464 ),
21465 "# abcdefg"
21466 );
21467 assert_eq!(
21468 wrap_with_prefix(
21469 "".to_string(),
21470 "".to_string(),
21471 "\thello world".to_string(),
21472 8,
21473 NonZeroU32::new(4).unwrap(),
21474 false,
21475 ),
21476 "hello\nworld"
21477 );
21478 assert_eq!(
21479 wrap_with_prefix(
21480 "// ".to_string(),
21481 "// ".to_string(),
21482 "xx \nyy zz aa bb cc".to_string(),
21483 12,
21484 NonZeroU32::new(4).unwrap(),
21485 false,
21486 ),
21487 "// xx yy zz\n// aa bb cc"
21488 );
21489 assert_eq!(
21490 wrap_with_prefix(
21491 String::new(),
21492 String::new(),
21493 "这是什么 \n 钢笔".to_string(),
21494 3,
21495 NonZeroU32::new(4).unwrap(),
21496 false,
21497 ),
21498 "这是什\n么 钢\n笔"
21499 );
21500}
21501
21502pub trait CollaborationHub {
21503 fn collaborators<'a>(&self, cx: &'a App) -> &'a HashMap<PeerId, Collaborator>;
21504 fn user_participant_indices<'a>(&self, cx: &'a App) -> &'a HashMap<u64, ParticipantIndex>;
21505 fn user_names(&self, cx: &App) -> HashMap<u64, SharedString>;
21506}
21507
21508impl CollaborationHub for Entity<Project> {
21509 fn collaborators<'a>(&self, cx: &'a App) -> &'a HashMap<PeerId, Collaborator> {
21510 self.read(cx).collaborators()
21511 }
21512
21513 fn user_participant_indices<'a>(&self, cx: &'a App) -> &'a HashMap<u64, ParticipantIndex> {
21514 self.read(cx).user_store().read(cx).participant_indices()
21515 }
21516
21517 fn user_names(&self, cx: &App) -> HashMap<u64, SharedString> {
21518 let this = self.read(cx);
21519 let user_ids = this.collaborators().values().map(|c| c.user_id);
21520 this.user_store().read(cx).participant_names(user_ids, cx)
21521 }
21522}
21523
21524pub trait SemanticsProvider {
21525 fn hover(
21526 &self,
21527 buffer: &Entity<Buffer>,
21528 position: text::Anchor,
21529 cx: &mut App,
21530 ) -> Option<Task<Vec<project::Hover>>>;
21531
21532 fn inline_values(
21533 &self,
21534 buffer_handle: Entity<Buffer>,
21535 range: Range<text::Anchor>,
21536 cx: &mut App,
21537 ) -> Option<Task<anyhow::Result<Vec<InlayHint>>>>;
21538
21539 fn inlay_hints(
21540 &self,
21541 buffer_handle: Entity<Buffer>,
21542 range: Range<text::Anchor>,
21543 cx: &mut App,
21544 ) -> Option<Task<anyhow::Result<Vec<InlayHint>>>>;
21545
21546 fn resolve_inlay_hint(
21547 &self,
21548 hint: InlayHint,
21549 buffer_handle: Entity<Buffer>,
21550 server_id: LanguageServerId,
21551 cx: &mut App,
21552 ) -> Option<Task<anyhow::Result<InlayHint>>>;
21553
21554 fn supports_inlay_hints(&self, buffer: &Entity<Buffer>, cx: &mut App) -> bool;
21555
21556 fn document_highlights(
21557 &self,
21558 buffer: &Entity<Buffer>,
21559 position: text::Anchor,
21560 cx: &mut App,
21561 ) -> Option<Task<Result<Vec<DocumentHighlight>>>>;
21562
21563 fn definitions(
21564 &self,
21565 buffer: &Entity<Buffer>,
21566 position: text::Anchor,
21567 kind: GotoDefinitionKind,
21568 cx: &mut App,
21569 ) -> Option<Task<Result<Vec<LocationLink>>>>;
21570
21571 fn range_for_rename(
21572 &self,
21573 buffer: &Entity<Buffer>,
21574 position: text::Anchor,
21575 cx: &mut App,
21576 ) -> Option<Task<Result<Option<Range<text::Anchor>>>>>;
21577
21578 fn perform_rename(
21579 &self,
21580 buffer: &Entity<Buffer>,
21581 position: text::Anchor,
21582 new_name: String,
21583 cx: &mut App,
21584 ) -> Option<Task<Result<ProjectTransaction>>>;
21585}
21586
21587pub trait CompletionProvider {
21588 fn completions(
21589 &self,
21590 excerpt_id: ExcerptId,
21591 buffer: &Entity<Buffer>,
21592 buffer_position: text::Anchor,
21593 trigger: CompletionContext,
21594 window: &mut Window,
21595 cx: &mut Context<Editor>,
21596 ) -> Task<Result<Vec<CompletionResponse>>>;
21597
21598 fn resolve_completions(
21599 &self,
21600 _buffer: Entity<Buffer>,
21601 _completion_indices: Vec<usize>,
21602 _completions: Rc<RefCell<Box<[Completion]>>>,
21603 _cx: &mut Context<Editor>,
21604 ) -> Task<Result<bool>> {
21605 Task::ready(Ok(false))
21606 }
21607
21608 fn apply_additional_edits_for_completion(
21609 &self,
21610 _buffer: Entity<Buffer>,
21611 _completions: Rc<RefCell<Box<[Completion]>>>,
21612 _completion_index: usize,
21613 _push_to_history: bool,
21614 _cx: &mut Context<Editor>,
21615 ) -> Task<Result<Option<language::Transaction>>> {
21616 Task::ready(Ok(None))
21617 }
21618
21619 fn is_completion_trigger(
21620 &self,
21621 buffer: &Entity<Buffer>,
21622 position: language::Anchor,
21623 text: &str,
21624 trigger_in_words: bool,
21625 menu_is_open: bool,
21626 cx: &mut Context<Editor>,
21627 ) -> bool;
21628
21629 fn selection_changed(&self, _mat: Option<&StringMatch>, _window: &mut Window, _cx: &mut App) {}
21630
21631 fn sort_completions(&self) -> bool {
21632 true
21633 }
21634
21635 fn filter_completions(&self) -> bool {
21636 true
21637 }
21638}
21639
21640pub trait CodeActionProvider {
21641 fn id(&self) -> Arc<str>;
21642
21643 fn code_actions(
21644 &self,
21645 buffer: &Entity<Buffer>,
21646 range: Range<text::Anchor>,
21647 window: &mut Window,
21648 cx: &mut App,
21649 ) -> Task<Result<Vec<CodeAction>>>;
21650
21651 fn apply_code_action(
21652 &self,
21653 buffer_handle: Entity<Buffer>,
21654 action: CodeAction,
21655 excerpt_id: ExcerptId,
21656 push_to_history: bool,
21657 window: &mut Window,
21658 cx: &mut App,
21659 ) -> Task<Result<ProjectTransaction>>;
21660}
21661
21662impl CodeActionProvider for Entity<Project> {
21663 fn id(&self) -> Arc<str> {
21664 "project".into()
21665 }
21666
21667 fn code_actions(
21668 &self,
21669 buffer: &Entity<Buffer>,
21670 range: Range<text::Anchor>,
21671 _window: &mut Window,
21672 cx: &mut App,
21673 ) -> Task<Result<Vec<CodeAction>>> {
21674 self.update(cx, |project, cx| {
21675 let code_lens = project.code_lens(buffer, range.clone(), cx);
21676 let code_actions = project.code_actions(buffer, range, None, cx);
21677 cx.background_spawn(async move {
21678 let (code_lens, code_actions) = join(code_lens, code_actions).await;
21679 Ok(code_lens
21680 .context("code lens fetch")?
21681 .into_iter()
21682 .chain(code_actions.context("code action fetch")?)
21683 .collect())
21684 })
21685 })
21686 }
21687
21688 fn apply_code_action(
21689 &self,
21690 buffer_handle: Entity<Buffer>,
21691 action: CodeAction,
21692 _excerpt_id: ExcerptId,
21693 push_to_history: bool,
21694 _window: &mut Window,
21695 cx: &mut App,
21696 ) -> Task<Result<ProjectTransaction>> {
21697 self.update(cx, |project, cx| {
21698 project.apply_code_action(buffer_handle, action, push_to_history, cx)
21699 })
21700 }
21701}
21702
21703fn snippet_completions(
21704 project: &Project,
21705 buffer: &Entity<Buffer>,
21706 buffer_position: text::Anchor,
21707 cx: &mut App,
21708) -> Task<Result<CompletionResponse>> {
21709 let languages = buffer.read(cx).languages_at(buffer_position);
21710 let snippet_store = project.snippets().read(cx);
21711
21712 let scopes: Vec<_> = languages
21713 .iter()
21714 .filter_map(|language| {
21715 let language_name = language.lsp_id();
21716 let snippets = snippet_store.snippets_for(Some(language_name), cx);
21717
21718 if snippets.is_empty() {
21719 None
21720 } else {
21721 Some((language.default_scope(), snippets))
21722 }
21723 })
21724 .collect();
21725
21726 if scopes.is_empty() {
21727 return Task::ready(Ok(CompletionResponse {
21728 completions: vec![],
21729 is_incomplete: false,
21730 }));
21731 }
21732
21733 let snapshot = buffer.read(cx).text_snapshot();
21734 let chars: String = snapshot
21735 .reversed_chars_for_range(text::Anchor::MIN..buffer_position)
21736 .collect();
21737 let executor = cx.background_executor().clone();
21738
21739 cx.background_spawn(async move {
21740 let mut is_incomplete = false;
21741 let mut completions: Vec<Completion> = Vec::new();
21742 for (scope, snippets) in scopes.into_iter() {
21743 let classifier = CharClassifier::new(Some(scope)).for_completion(true);
21744 let mut last_word = chars
21745 .chars()
21746 .take_while(|c| classifier.is_word(*c))
21747 .collect::<String>();
21748 last_word = last_word.chars().rev().collect();
21749
21750 if last_word.is_empty() {
21751 return Ok(CompletionResponse {
21752 completions: vec![],
21753 is_incomplete: true,
21754 });
21755 }
21756
21757 let as_offset = text::ToOffset::to_offset(&buffer_position, &snapshot);
21758 let to_lsp = |point: &text::Anchor| {
21759 let end = text::ToPointUtf16::to_point_utf16(point, &snapshot);
21760 point_to_lsp(end)
21761 };
21762 let lsp_end = to_lsp(&buffer_position);
21763
21764 let candidates = snippets
21765 .iter()
21766 .enumerate()
21767 .flat_map(|(ix, snippet)| {
21768 snippet
21769 .prefix
21770 .iter()
21771 .map(move |prefix| StringMatchCandidate::new(ix, &prefix))
21772 })
21773 .collect::<Vec<StringMatchCandidate>>();
21774
21775 const MAX_RESULTS: usize = 100;
21776 let mut matches = fuzzy::match_strings(
21777 &candidates,
21778 &last_word,
21779 last_word.chars().any(|c| c.is_uppercase()),
21780 true,
21781 MAX_RESULTS,
21782 &Default::default(),
21783 executor.clone(),
21784 )
21785 .await;
21786
21787 if matches.len() >= MAX_RESULTS {
21788 is_incomplete = true;
21789 }
21790
21791 // Remove all candidates where the query's start does not match the start of any word in the candidate
21792 if let Some(query_start) = last_word.chars().next() {
21793 matches.retain(|string_match| {
21794 split_words(&string_match.string).any(|word| {
21795 // Check that the first codepoint of the word as lowercase matches the first
21796 // codepoint of the query as lowercase
21797 word.chars()
21798 .flat_map(|codepoint| codepoint.to_lowercase())
21799 .zip(query_start.to_lowercase())
21800 .all(|(word_cp, query_cp)| word_cp == query_cp)
21801 })
21802 });
21803 }
21804
21805 let matched_strings = matches
21806 .into_iter()
21807 .map(|m| m.string)
21808 .collect::<HashSet<_>>();
21809
21810 completions.extend(snippets.iter().filter_map(|snippet| {
21811 let matching_prefix = snippet
21812 .prefix
21813 .iter()
21814 .find(|prefix| matched_strings.contains(*prefix))?;
21815 let start = as_offset - last_word.len();
21816 let start = snapshot.anchor_before(start);
21817 let range = start..buffer_position;
21818 let lsp_start = to_lsp(&start);
21819 let lsp_range = lsp::Range {
21820 start: lsp_start,
21821 end: lsp_end,
21822 };
21823 Some(Completion {
21824 replace_range: range,
21825 new_text: snippet.body.clone(),
21826 source: CompletionSource::Lsp {
21827 insert_range: None,
21828 server_id: LanguageServerId(usize::MAX),
21829 resolved: true,
21830 lsp_completion: Box::new(lsp::CompletionItem {
21831 label: snippet.prefix.first().unwrap().clone(),
21832 kind: Some(CompletionItemKind::SNIPPET),
21833 label_details: snippet.description.as_ref().map(|description| {
21834 lsp::CompletionItemLabelDetails {
21835 detail: Some(description.clone()),
21836 description: None,
21837 }
21838 }),
21839 insert_text_format: Some(InsertTextFormat::SNIPPET),
21840 text_edit: Some(lsp::CompletionTextEdit::InsertAndReplace(
21841 lsp::InsertReplaceEdit {
21842 new_text: snippet.body.clone(),
21843 insert: lsp_range,
21844 replace: lsp_range,
21845 },
21846 )),
21847 filter_text: Some(snippet.body.clone()),
21848 sort_text: Some(char::MAX.to_string()),
21849 ..lsp::CompletionItem::default()
21850 }),
21851 lsp_defaults: None,
21852 },
21853 label: CodeLabel {
21854 text: matching_prefix.clone(),
21855 runs: Vec::new(),
21856 filter_range: 0..matching_prefix.len(),
21857 },
21858 icon_path: None,
21859 documentation: Some(CompletionDocumentation::SingleLineAndMultiLinePlainText {
21860 single_line: snippet.name.clone().into(),
21861 plain_text: snippet
21862 .description
21863 .clone()
21864 .map(|description| description.into()),
21865 }),
21866 insert_text_mode: None,
21867 confirm: None,
21868 })
21869 }))
21870 }
21871
21872 Ok(CompletionResponse {
21873 completions,
21874 is_incomplete,
21875 })
21876 })
21877}
21878
21879impl CompletionProvider for Entity<Project> {
21880 fn completions(
21881 &self,
21882 _excerpt_id: ExcerptId,
21883 buffer: &Entity<Buffer>,
21884 buffer_position: text::Anchor,
21885 options: CompletionContext,
21886 _window: &mut Window,
21887 cx: &mut Context<Editor>,
21888 ) -> Task<Result<Vec<CompletionResponse>>> {
21889 self.update(cx, |project, cx| {
21890 let snippets = snippet_completions(project, buffer, buffer_position, cx);
21891 let project_completions = project.completions(buffer, buffer_position, options, cx);
21892 cx.background_spawn(async move {
21893 let mut responses = project_completions.await?;
21894 let snippets = snippets.await?;
21895 if !snippets.completions.is_empty() {
21896 responses.push(snippets);
21897 }
21898 Ok(responses)
21899 })
21900 })
21901 }
21902
21903 fn resolve_completions(
21904 &self,
21905 buffer: Entity<Buffer>,
21906 completion_indices: Vec<usize>,
21907 completions: Rc<RefCell<Box<[Completion]>>>,
21908 cx: &mut Context<Editor>,
21909 ) -> Task<Result<bool>> {
21910 self.update(cx, |project, cx| {
21911 project.lsp_store().update(cx, |lsp_store, cx| {
21912 lsp_store.resolve_completions(buffer, completion_indices, completions, cx)
21913 })
21914 })
21915 }
21916
21917 fn apply_additional_edits_for_completion(
21918 &self,
21919 buffer: Entity<Buffer>,
21920 completions: Rc<RefCell<Box<[Completion]>>>,
21921 completion_index: usize,
21922 push_to_history: bool,
21923 cx: &mut Context<Editor>,
21924 ) -> Task<Result<Option<language::Transaction>>> {
21925 self.update(cx, |project, cx| {
21926 project.lsp_store().update(cx, |lsp_store, cx| {
21927 lsp_store.apply_additional_edits_for_completion(
21928 buffer,
21929 completions,
21930 completion_index,
21931 push_to_history,
21932 cx,
21933 )
21934 })
21935 })
21936 }
21937
21938 fn is_completion_trigger(
21939 &self,
21940 buffer: &Entity<Buffer>,
21941 position: language::Anchor,
21942 text: &str,
21943 trigger_in_words: bool,
21944 menu_is_open: bool,
21945 cx: &mut Context<Editor>,
21946 ) -> bool {
21947 let mut chars = text.chars();
21948 let char = if let Some(char) = chars.next() {
21949 char
21950 } else {
21951 return false;
21952 };
21953 if chars.next().is_some() {
21954 return false;
21955 }
21956
21957 let buffer = buffer.read(cx);
21958 let snapshot = buffer.snapshot();
21959 if !menu_is_open && !snapshot.settings_at(position, cx).show_completions_on_input {
21960 return false;
21961 }
21962 let classifier = snapshot.char_classifier_at(position).for_completion(true);
21963 if trigger_in_words && classifier.is_word(char) {
21964 return true;
21965 }
21966
21967 buffer.completion_triggers().contains(text)
21968 }
21969}
21970
21971impl SemanticsProvider for Entity<Project> {
21972 fn hover(
21973 &self,
21974 buffer: &Entity<Buffer>,
21975 position: text::Anchor,
21976 cx: &mut App,
21977 ) -> Option<Task<Vec<project::Hover>>> {
21978 Some(self.update(cx, |project, cx| project.hover(buffer, position, cx)))
21979 }
21980
21981 fn document_highlights(
21982 &self,
21983 buffer: &Entity<Buffer>,
21984 position: text::Anchor,
21985 cx: &mut App,
21986 ) -> Option<Task<Result<Vec<DocumentHighlight>>>> {
21987 Some(self.update(cx, |project, cx| {
21988 project.document_highlights(buffer, position, cx)
21989 }))
21990 }
21991
21992 fn definitions(
21993 &self,
21994 buffer: &Entity<Buffer>,
21995 position: text::Anchor,
21996 kind: GotoDefinitionKind,
21997 cx: &mut App,
21998 ) -> Option<Task<Result<Vec<LocationLink>>>> {
21999 Some(self.update(cx, |project, cx| match kind {
22000 GotoDefinitionKind::Symbol => project.definitions(&buffer, position, cx),
22001 GotoDefinitionKind::Declaration => project.declarations(&buffer, position, cx),
22002 GotoDefinitionKind::Type => project.type_definitions(&buffer, position, cx),
22003 GotoDefinitionKind::Implementation => project.implementations(&buffer, position, cx),
22004 }))
22005 }
22006
22007 fn supports_inlay_hints(&self, buffer: &Entity<Buffer>, cx: &mut App) -> bool {
22008 // TODO: make this work for remote projects
22009 self.update(cx, |project, cx| {
22010 if project
22011 .active_debug_session(cx)
22012 .is_some_and(|(session, _)| session.read(cx).any_stopped_thread())
22013 {
22014 return true;
22015 }
22016
22017 buffer.update(cx, |buffer, cx| {
22018 project.any_language_server_supports_inlay_hints(buffer, cx)
22019 })
22020 })
22021 }
22022
22023 fn inline_values(
22024 &self,
22025 buffer_handle: Entity<Buffer>,
22026 range: Range<text::Anchor>,
22027 cx: &mut App,
22028 ) -> Option<Task<anyhow::Result<Vec<InlayHint>>>> {
22029 self.update(cx, |project, cx| {
22030 let (session, active_stack_frame) = project.active_debug_session(cx)?;
22031
22032 Some(project.inline_values(session, active_stack_frame, buffer_handle, range, cx))
22033 })
22034 }
22035
22036 fn inlay_hints(
22037 &self,
22038 buffer_handle: Entity<Buffer>,
22039 range: Range<text::Anchor>,
22040 cx: &mut App,
22041 ) -> Option<Task<anyhow::Result<Vec<InlayHint>>>> {
22042 Some(self.update(cx, |project, cx| {
22043 project.inlay_hints(buffer_handle, range, cx)
22044 }))
22045 }
22046
22047 fn resolve_inlay_hint(
22048 &self,
22049 hint: InlayHint,
22050 buffer_handle: Entity<Buffer>,
22051 server_id: LanguageServerId,
22052 cx: &mut App,
22053 ) -> Option<Task<anyhow::Result<InlayHint>>> {
22054 Some(self.update(cx, |project, cx| {
22055 project.resolve_inlay_hint(hint, buffer_handle, server_id, cx)
22056 }))
22057 }
22058
22059 fn range_for_rename(
22060 &self,
22061 buffer: &Entity<Buffer>,
22062 position: text::Anchor,
22063 cx: &mut App,
22064 ) -> Option<Task<Result<Option<Range<text::Anchor>>>>> {
22065 Some(self.update(cx, |project, cx| {
22066 let buffer = buffer.clone();
22067 let task = project.prepare_rename(buffer.clone(), position, cx);
22068 cx.spawn(async move |_, cx| {
22069 Ok(match task.await? {
22070 PrepareRenameResponse::Success(range) => Some(range),
22071 PrepareRenameResponse::InvalidPosition => None,
22072 PrepareRenameResponse::OnlyUnpreparedRenameSupported => {
22073 // Fallback on using TreeSitter info to determine identifier range
22074 buffer.read_with(cx, |buffer, _| {
22075 let snapshot = buffer.snapshot();
22076 let (range, kind) = snapshot.surrounding_word(position);
22077 if kind != Some(CharKind::Word) {
22078 return None;
22079 }
22080 Some(
22081 snapshot.anchor_before(range.start)
22082 ..snapshot.anchor_after(range.end),
22083 )
22084 })?
22085 }
22086 })
22087 })
22088 }))
22089 }
22090
22091 fn perform_rename(
22092 &self,
22093 buffer: &Entity<Buffer>,
22094 position: text::Anchor,
22095 new_name: String,
22096 cx: &mut App,
22097 ) -> Option<Task<Result<ProjectTransaction>>> {
22098 Some(self.update(cx, |project, cx| {
22099 project.perform_rename(buffer.clone(), position, new_name, cx)
22100 }))
22101 }
22102}
22103
22104fn inlay_hint_settings(
22105 location: Anchor,
22106 snapshot: &MultiBufferSnapshot,
22107 cx: &mut Context<Editor>,
22108) -> InlayHintSettings {
22109 let file = snapshot.file_at(location);
22110 let language = snapshot.language_at(location).map(|l| l.name());
22111 language_settings(language, file, cx).inlay_hints
22112}
22113
22114fn consume_contiguous_rows(
22115 contiguous_row_selections: &mut Vec<Selection<Point>>,
22116 selection: &Selection<Point>,
22117 display_map: &DisplaySnapshot,
22118 selections: &mut Peekable<std::slice::Iter<Selection<Point>>>,
22119) -> (MultiBufferRow, MultiBufferRow) {
22120 contiguous_row_selections.push(selection.clone());
22121 let start_row = MultiBufferRow(selection.start.row);
22122 let mut end_row = ending_row(selection, display_map);
22123
22124 while let Some(next_selection) = selections.peek() {
22125 if next_selection.start.row <= end_row.0 {
22126 end_row = ending_row(next_selection, display_map);
22127 contiguous_row_selections.push(selections.next().unwrap().clone());
22128 } else {
22129 break;
22130 }
22131 }
22132 (start_row, end_row)
22133}
22134
22135fn ending_row(next_selection: &Selection<Point>, display_map: &DisplaySnapshot) -> MultiBufferRow {
22136 if next_selection.end.column > 0 || next_selection.is_empty() {
22137 MultiBufferRow(display_map.next_line_boundary(next_selection.end).0.row + 1)
22138 } else {
22139 MultiBufferRow(next_selection.end.row)
22140 }
22141}
22142
22143impl EditorSnapshot {
22144 pub fn remote_selections_in_range<'a>(
22145 &'a self,
22146 range: &'a Range<Anchor>,
22147 collaboration_hub: &dyn CollaborationHub,
22148 cx: &'a App,
22149 ) -> impl 'a + Iterator<Item = RemoteSelection> {
22150 let participant_names = collaboration_hub.user_names(cx);
22151 let participant_indices = collaboration_hub.user_participant_indices(cx);
22152 let collaborators_by_peer_id = collaboration_hub.collaborators(cx);
22153 let collaborators_by_replica_id = collaborators_by_peer_id
22154 .values()
22155 .map(|collaborator| (collaborator.replica_id, collaborator))
22156 .collect::<HashMap<_, _>>();
22157 self.buffer_snapshot
22158 .selections_in_range(range, false)
22159 .filter_map(move |(replica_id, line_mode, cursor_shape, selection)| {
22160 if replica_id == AGENT_REPLICA_ID {
22161 Some(RemoteSelection {
22162 replica_id,
22163 selection,
22164 cursor_shape,
22165 line_mode,
22166 collaborator_id: CollaboratorId::Agent,
22167 user_name: Some("Agent".into()),
22168 color: cx.theme().players().agent(),
22169 })
22170 } else {
22171 let collaborator = collaborators_by_replica_id.get(&replica_id)?;
22172 let participant_index = participant_indices.get(&collaborator.user_id).copied();
22173 let user_name = participant_names.get(&collaborator.user_id).cloned();
22174 Some(RemoteSelection {
22175 replica_id,
22176 selection,
22177 cursor_shape,
22178 line_mode,
22179 collaborator_id: CollaboratorId::PeerId(collaborator.peer_id),
22180 user_name,
22181 color: if let Some(index) = participant_index {
22182 cx.theme().players().color_for_participant(index.0)
22183 } else {
22184 cx.theme().players().absent()
22185 },
22186 })
22187 }
22188 })
22189 }
22190
22191 pub fn hunks_for_ranges(
22192 &self,
22193 ranges: impl IntoIterator<Item = Range<Point>>,
22194 ) -> Vec<MultiBufferDiffHunk> {
22195 let mut hunks = Vec::new();
22196 let mut processed_buffer_rows: HashMap<BufferId, HashSet<Range<text::Anchor>>> =
22197 HashMap::default();
22198 for query_range in ranges {
22199 let query_rows =
22200 MultiBufferRow(query_range.start.row)..MultiBufferRow(query_range.end.row + 1);
22201 for hunk in self.buffer_snapshot.diff_hunks_in_range(
22202 Point::new(query_rows.start.0, 0)..Point::new(query_rows.end.0, 0),
22203 ) {
22204 // Include deleted hunks that are adjacent to the query range, because
22205 // otherwise they would be missed.
22206 let mut intersects_range = hunk.row_range.overlaps(&query_rows);
22207 if hunk.status().is_deleted() {
22208 intersects_range |= hunk.row_range.start == query_rows.end;
22209 intersects_range |= hunk.row_range.end == query_rows.start;
22210 }
22211 if intersects_range {
22212 if !processed_buffer_rows
22213 .entry(hunk.buffer_id)
22214 .or_default()
22215 .insert(hunk.buffer_range.start..hunk.buffer_range.end)
22216 {
22217 continue;
22218 }
22219 hunks.push(hunk);
22220 }
22221 }
22222 }
22223
22224 hunks
22225 }
22226
22227 fn display_diff_hunks_for_rows<'a>(
22228 &'a self,
22229 display_rows: Range<DisplayRow>,
22230 folded_buffers: &'a HashSet<BufferId>,
22231 ) -> impl 'a + Iterator<Item = DisplayDiffHunk> {
22232 let buffer_start = DisplayPoint::new(display_rows.start, 0).to_point(self);
22233 let buffer_end = DisplayPoint::new(display_rows.end, 0).to_point(self);
22234
22235 self.buffer_snapshot
22236 .diff_hunks_in_range(buffer_start..buffer_end)
22237 .filter_map(|hunk| {
22238 if folded_buffers.contains(&hunk.buffer_id) {
22239 return None;
22240 }
22241
22242 let hunk_start_point = Point::new(hunk.row_range.start.0, 0);
22243 let hunk_end_point = Point::new(hunk.row_range.end.0, 0);
22244
22245 let hunk_display_start = self.point_to_display_point(hunk_start_point, Bias::Left);
22246 let hunk_display_end = self.point_to_display_point(hunk_end_point, Bias::Right);
22247
22248 let display_hunk = if hunk_display_start.column() != 0 {
22249 DisplayDiffHunk::Folded {
22250 display_row: hunk_display_start.row(),
22251 }
22252 } else {
22253 let mut end_row = hunk_display_end.row();
22254 if hunk_display_end.column() > 0 {
22255 end_row.0 += 1;
22256 }
22257 let is_created_file = hunk.is_created_file();
22258 DisplayDiffHunk::Unfolded {
22259 status: hunk.status(),
22260 diff_base_byte_range: hunk.diff_base_byte_range,
22261 display_row_range: hunk_display_start.row()..end_row,
22262 multi_buffer_range: Anchor::range_in_buffer(
22263 hunk.excerpt_id,
22264 hunk.buffer_id,
22265 hunk.buffer_range,
22266 ),
22267 is_created_file,
22268 }
22269 };
22270
22271 Some(display_hunk)
22272 })
22273 }
22274
22275 pub fn language_at<T: ToOffset>(&self, position: T) -> Option<&Arc<Language>> {
22276 self.display_snapshot.buffer_snapshot.language_at(position)
22277 }
22278
22279 pub fn is_focused(&self) -> bool {
22280 self.is_focused
22281 }
22282
22283 pub fn placeholder_text(&self) -> Option<&Arc<str>> {
22284 self.placeholder_text.as_ref()
22285 }
22286
22287 pub fn scroll_position(&self) -> gpui::Point<f32> {
22288 self.scroll_anchor.scroll_position(&self.display_snapshot)
22289 }
22290
22291 fn gutter_dimensions(
22292 &self,
22293 font_id: FontId,
22294 font_size: Pixels,
22295 max_line_number_width: Pixels,
22296 cx: &App,
22297 ) -> Option<GutterDimensions> {
22298 if !self.show_gutter {
22299 return None;
22300 }
22301
22302 let ch_width = cx.text_system().ch_width(font_id, font_size).log_err()?;
22303 let ch_advance = cx.text_system().ch_advance(font_id, font_size).log_err()?;
22304
22305 let show_git_gutter = self.show_git_diff_gutter.unwrap_or_else(|| {
22306 matches!(
22307 ProjectSettings::get_global(cx).git.git_gutter,
22308 Some(GitGutterSetting::TrackedFiles)
22309 )
22310 });
22311 let gutter_settings = EditorSettings::get_global(cx).gutter;
22312 let show_line_numbers = self
22313 .show_line_numbers
22314 .unwrap_or(gutter_settings.line_numbers);
22315 let line_gutter_width = if show_line_numbers {
22316 // Avoid flicker-like gutter resizes when the line number gains another digit by
22317 // only resizing the gutter on files with > 10**min_line_number_digits lines.
22318 let min_width_for_number_on_gutter =
22319 ch_advance * gutter_settings.min_line_number_digits as f32;
22320 max_line_number_width.max(min_width_for_number_on_gutter)
22321 } else {
22322 0.0.into()
22323 };
22324
22325 let show_runnables = self.show_runnables.unwrap_or(gutter_settings.runnables);
22326 let show_breakpoints = self.show_breakpoints.unwrap_or(gutter_settings.breakpoints);
22327
22328 let git_blame_entries_width =
22329 self.git_blame_gutter_max_author_length
22330 .map(|max_author_length| {
22331 let renderer = cx.global::<GlobalBlameRenderer>().0.clone();
22332 const MAX_RELATIVE_TIMESTAMP: &str = "60 minutes ago";
22333
22334 /// The number of characters to dedicate to gaps and margins.
22335 const SPACING_WIDTH: usize = 4;
22336
22337 let max_char_count = max_author_length.min(renderer.max_author_length())
22338 + ::git::SHORT_SHA_LENGTH
22339 + MAX_RELATIVE_TIMESTAMP.len()
22340 + SPACING_WIDTH;
22341
22342 ch_advance * max_char_count
22343 });
22344
22345 let is_singleton = self.buffer_snapshot.is_singleton();
22346
22347 let mut left_padding = git_blame_entries_width.unwrap_or(Pixels::ZERO);
22348 left_padding += if !is_singleton {
22349 ch_width * 4.0
22350 } else if show_runnables || show_breakpoints {
22351 ch_width * 3.0
22352 } else if show_git_gutter && show_line_numbers {
22353 ch_width * 2.0
22354 } else if show_git_gutter || show_line_numbers {
22355 ch_width
22356 } else {
22357 px(0.)
22358 };
22359
22360 let shows_folds = is_singleton && gutter_settings.folds;
22361
22362 let right_padding = if shows_folds && show_line_numbers {
22363 ch_width * 4.0
22364 } else if shows_folds || (!is_singleton && show_line_numbers) {
22365 ch_width * 3.0
22366 } else if show_line_numbers {
22367 ch_width
22368 } else {
22369 px(0.)
22370 };
22371
22372 Some(GutterDimensions {
22373 left_padding,
22374 right_padding,
22375 width: line_gutter_width + left_padding + right_padding,
22376 margin: GutterDimensions::default_gutter_margin(font_id, font_size, cx),
22377 git_blame_entries_width,
22378 })
22379 }
22380
22381 pub fn render_crease_toggle(
22382 &self,
22383 buffer_row: MultiBufferRow,
22384 row_contains_cursor: bool,
22385 editor: Entity<Editor>,
22386 window: &mut Window,
22387 cx: &mut App,
22388 ) -> Option<AnyElement> {
22389 let folded = self.is_line_folded(buffer_row);
22390 let mut is_foldable = false;
22391
22392 if let Some(crease) = self
22393 .crease_snapshot
22394 .query_row(buffer_row, &self.buffer_snapshot)
22395 {
22396 is_foldable = true;
22397 match crease {
22398 Crease::Inline { render_toggle, .. } | Crease::Block { render_toggle, .. } => {
22399 if let Some(render_toggle) = render_toggle {
22400 let toggle_callback =
22401 Arc::new(move |folded, window: &mut Window, cx: &mut App| {
22402 if folded {
22403 editor.update(cx, |editor, cx| {
22404 editor.fold_at(buffer_row, window, cx)
22405 });
22406 } else {
22407 editor.update(cx, |editor, cx| {
22408 editor.unfold_at(buffer_row, window, cx)
22409 });
22410 }
22411 });
22412 return Some((render_toggle)(
22413 buffer_row,
22414 folded,
22415 toggle_callback,
22416 window,
22417 cx,
22418 ));
22419 }
22420 }
22421 }
22422 }
22423
22424 is_foldable |= self.starts_indent(buffer_row);
22425
22426 if folded || (is_foldable && (row_contains_cursor || self.gutter_hovered)) {
22427 Some(
22428 Disclosure::new(("gutter_crease", buffer_row.0), !folded)
22429 .toggle_state(folded)
22430 .on_click(window.listener_for(&editor, move |this, _e, window, cx| {
22431 if folded {
22432 this.unfold_at(buffer_row, window, cx);
22433 } else {
22434 this.fold_at(buffer_row, window, cx);
22435 }
22436 }))
22437 .into_any_element(),
22438 )
22439 } else {
22440 None
22441 }
22442 }
22443
22444 pub fn render_crease_trailer(
22445 &self,
22446 buffer_row: MultiBufferRow,
22447 window: &mut Window,
22448 cx: &mut App,
22449 ) -> Option<AnyElement> {
22450 let folded = self.is_line_folded(buffer_row);
22451 if let Crease::Inline { render_trailer, .. } = self
22452 .crease_snapshot
22453 .query_row(buffer_row, &self.buffer_snapshot)?
22454 {
22455 let render_trailer = render_trailer.as_ref()?;
22456 Some(render_trailer(buffer_row, folded, window, cx))
22457 } else {
22458 None
22459 }
22460 }
22461}
22462
22463impl Deref for EditorSnapshot {
22464 type Target = DisplaySnapshot;
22465
22466 fn deref(&self) -> &Self::Target {
22467 &self.display_snapshot
22468 }
22469}
22470
22471#[derive(Clone, Debug, PartialEq, Eq)]
22472pub enum EditorEvent {
22473 InputIgnored {
22474 text: Arc<str>,
22475 },
22476 InputHandled {
22477 utf16_range_to_replace: Option<Range<isize>>,
22478 text: Arc<str>,
22479 },
22480 ExcerptsAdded {
22481 buffer: Entity<Buffer>,
22482 predecessor: ExcerptId,
22483 excerpts: Vec<(ExcerptId, ExcerptRange<language::Anchor>)>,
22484 },
22485 ExcerptsRemoved {
22486 ids: Vec<ExcerptId>,
22487 removed_buffer_ids: Vec<BufferId>,
22488 },
22489 BufferFoldToggled {
22490 ids: Vec<ExcerptId>,
22491 folded: bool,
22492 },
22493 ExcerptsEdited {
22494 ids: Vec<ExcerptId>,
22495 },
22496 ExcerptsExpanded {
22497 ids: Vec<ExcerptId>,
22498 },
22499 BufferEdited,
22500 Edited {
22501 transaction_id: clock::Lamport,
22502 },
22503 Reparsed(BufferId),
22504 Focused,
22505 FocusedIn,
22506 Blurred,
22507 DirtyChanged,
22508 Saved,
22509 TitleChanged,
22510 DiffBaseChanged,
22511 SelectionsChanged {
22512 local: bool,
22513 },
22514 ScrollPositionChanged {
22515 local: bool,
22516 autoscroll: bool,
22517 },
22518 Closed,
22519 TransactionUndone {
22520 transaction_id: clock::Lamport,
22521 },
22522 TransactionBegun {
22523 transaction_id: clock::Lamport,
22524 },
22525 Reloaded,
22526 CursorShapeChanged,
22527 PushedToNavHistory {
22528 anchor: Anchor,
22529 is_deactivate: bool,
22530 },
22531}
22532
22533impl EventEmitter<EditorEvent> for Editor {}
22534
22535impl Focusable for Editor {
22536 fn focus_handle(&self, _cx: &App) -> FocusHandle {
22537 self.focus_handle.clone()
22538 }
22539}
22540
22541impl Render for Editor {
22542 fn render(&mut self, _: &mut Window, cx: &mut Context<Self>) -> impl IntoElement {
22543 let settings = ThemeSettings::get_global(cx);
22544
22545 let mut text_style = match self.mode {
22546 EditorMode::SingleLine { .. } | EditorMode::AutoHeight { .. } => TextStyle {
22547 color: cx.theme().colors().editor_foreground,
22548 font_family: settings.ui_font.family.clone(),
22549 font_features: settings.ui_font.features.clone(),
22550 font_fallbacks: settings.ui_font.fallbacks.clone(),
22551 font_size: rems(0.875).into(),
22552 font_weight: settings.ui_font.weight,
22553 line_height: relative(settings.buffer_line_height.value()),
22554 ..Default::default()
22555 },
22556 EditorMode::Full { .. } | EditorMode::Minimap { .. } => TextStyle {
22557 color: cx.theme().colors().editor_foreground,
22558 font_family: settings.buffer_font.family.clone(),
22559 font_features: settings.buffer_font.features.clone(),
22560 font_fallbacks: settings.buffer_font.fallbacks.clone(),
22561 font_size: settings.buffer_font_size(cx).into(),
22562 font_weight: settings.buffer_font.weight,
22563 line_height: relative(settings.buffer_line_height.value()),
22564 ..Default::default()
22565 },
22566 };
22567 if let Some(text_style_refinement) = &self.text_style_refinement {
22568 text_style.refine(text_style_refinement)
22569 }
22570
22571 let background = match self.mode {
22572 EditorMode::SingleLine { .. } => cx.theme().system().transparent,
22573 EditorMode::AutoHeight { .. } => cx.theme().system().transparent,
22574 EditorMode::Full { .. } => cx.theme().colors().editor_background,
22575 EditorMode::Minimap { .. } => cx.theme().colors().editor_background.opacity(0.7),
22576 };
22577
22578 EditorElement::new(
22579 &cx.entity(),
22580 EditorStyle {
22581 background,
22582 border: cx.theme().colors().border,
22583 local_player: cx.theme().players().local(),
22584 text: text_style,
22585 scrollbar_width: EditorElement::SCROLLBAR_WIDTH,
22586 syntax: cx.theme().syntax().clone(),
22587 status: cx.theme().status().clone(),
22588 inlay_hints_style: make_inlay_hints_style(cx),
22589 inline_completion_styles: make_suggestion_styles(cx),
22590 unnecessary_code_fade: ThemeSettings::get_global(cx).unnecessary_code_fade,
22591 show_underlines: self.diagnostics_enabled(),
22592 },
22593 )
22594 }
22595}
22596
22597impl EntityInputHandler for Editor {
22598 fn text_for_range(
22599 &mut self,
22600 range_utf16: Range<usize>,
22601 adjusted_range: &mut Option<Range<usize>>,
22602 _: &mut Window,
22603 cx: &mut Context<Self>,
22604 ) -> Option<String> {
22605 let snapshot = self.buffer.read(cx).read(cx);
22606 let start = snapshot.clip_offset_utf16(OffsetUtf16(range_utf16.start), Bias::Left);
22607 let end = snapshot.clip_offset_utf16(OffsetUtf16(range_utf16.end), Bias::Right);
22608 if (start.0..end.0) != range_utf16 {
22609 adjusted_range.replace(start.0..end.0);
22610 }
22611 Some(snapshot.text_for_range(start..end).collect())
22612 }
22613
22614 fn selected_text_range(
22615 &mut self,
22616 ignore_disabled_input: bool,
22617 _: &mut Window,
22618 cx: &mut Context<Self>,
22619 ) -> Option<UTF16Selection> {
22620 // Prevent the IME menu from appearing when holding down an alphabetic key
22621 // while input is disabled.
22622 if !ignore_disabled_input && !self.input_enabled {
22623 return None;
22624 }
22625
22626 let selection = self.selections.newest::<OffsetUtf16>(cx);
22627 let range = selection.range();
22628
22629 Some(UTF16Selection {
22630 range: range.start.0..range.end.0,
22631 reversed: selection.reversed,
22632 })
22633 }
22634
22635 fn marked_text_range(&self, _: &mut Window, cx: &mut Context<Self>) -> Option<Range<usize>> {
22636 let snapshot = self.buffer.read(cx).read(cx);
22637 let range = self.text_highlights::<InputComposition>(cx)?.1.first()?;
22638 Some(range.start.to_offset_utf16(&snapshot).0..range.end.to_offset_utf16(&snapshot).0)
22639 }
22640
22641 fn unmark_text(&mut self, _: &mut Window, cx: &mut Context<Self>) {
22642 self.clear_highlights::<InputComposition>(cx);
22643 self.ime_transaction.take();
22644 }
22645
22646 fn replace_text_in_range(
22647 &mut self,
22648 range_utf16: Option<Range<usize>>,
22649 text: &str,
22650 window: &mut Window,
22651 cx: &mut Context<Self>,
22652 ) {
22653 if !self.input_enabled {
22654 cx.emit(EditorEvent::InputIgnored { text: text.into() });
22655 return;
22656 }
22657
22658 self.transact(window, cx, |this, window, cx| {
22659 let new_selected_ranges = if let Some(range_utf16) = range_utf16 {
22660 let range_utf16 = OffsetUtf16(range_utf16.start)..OffsetUtf16(range_utf16.end);
22661 Some(this.selection_replacement_ranges(range_utf16, cx))
22662 } else {
22663 this.marked_text_ranges(cx)
22664 };
22665
22666 let range_to_replace = new_selected_ranges.as_ref().and_then(|ranges_to_replace| {
22667 let newest_selection_id = this.selections.newest_anchor().id;
22668 this.selections
22669 .all::<OffsetUtf16>(cx)
22670 .iter()
22671 .zip(ranges_to_replace.iter())
22672 .find_map(|(selection, range)| {
22673 if selection.id == newest_selection_id {
22674 Some(
22675 (range.start.0 as isize - selection.head().0 as isize)
22676 ..(range.end.0 as isize - selection.head().0 as isize),
22677 )
22678 } else {
22679 None
22680 }
22681 })
22682 });
22683
22684 cx.emit(EditorEvent::InputHandled {
22685 utf16_range_to_replace: range_to_replace,
22686 text: text.into(),
22687 });
22688
22689 if let Some(new_selected_ranges) = new_selected_ranges {
22690 this.change_selections(SelectionEffects::no_scroll(), window, cx, |selections| {
22691 selections.select_ranges(new_selected_ranges)
22692 });
22693 this.backspace(&Default::default(), window, cx);
22694 }
22695
22696 this.handle_input(text, window, cx);
22697 });
22698
22699 if let Some(transaction) = self.ime_transaction {
22700 self.buffer.update(cx, |buffer, cx| {
22701 buffer.group_until_transaction(transaction, cx);
22702 });
22703 }
22704
22705 self.unmark_text(window, cx);
22706 }
22707
22708 fn replace_and_mark_text_in_range(
22709 &mut self,
22710 range_utf16: Option<Range<usize>>,
22711 text: &str,
22712 new_selected_range_utf16: Option<Range<usize>>,
22713 window: &mut Window,
22714 cx: &mut Context<Self>,
22715 ) {
22716 if !self.input_enabled {
22717 return;
22718 }
22719
22720 let transaction = self.transact(window, cx, |this, window, cx| {
22721 let ranges_to_replace = if let Some(mut marked_ranges) = this.marked_text_ranges(cx) {
22722 let snapshot = this.buffer.read(cx).read(cx);
22723 if let Some(relative_range_utf16) = range_utf16.as_ref() {
22724 for marked_range in &mut marked_ranges {
22725 marked_range.end.0 = marked_range.start.0 + relative_range_utf16.end;
22726 marked_range.start.0 += relative_range_utf16.start;
22727 marked_range.start =
22728 snapshot.clip_offset_utf16(marked_range.start, Bias::Left);
22729 marked_range.end =
22730 snapshot.clip_offset_utf16(marked_range.end, Bias::Right);
22731 }
22732 }
22733 Some(marked_ranges)
22734 } else if let Some(range_utf16) = range_utf16 {
22735 let range_utf16 = OffsetUtf16(range_utf16.start)..OffsetUtf16(range_utf16.end);
22736 Some(this.selection_replacement_ranges(range_utf16, cx))
22737 } else {
22738 None
22739 };
22740
22741 let range_to_replace = ranges_to_replace.as_ref().and_then(|ranges_to_replace| {
22742 let newest_selection_id = this.selections.newest_anchor().id;
22743 this.selections
22744 .all::<OffsetUtf16>(cx)
22745 .iter()
22746 .zip(ranges_to_replace.iter())
22747 .find_map(|(selection, range)| {
22748 if selection.id == newest_selection_id {
22749 Some(
22750 (range.start.0 as isize - selection.head().0 as isize)
22751 ..(range.end.0 as isize - selection.head().0 as isize),
22752 )
22753 } else {
22754 None
22755 }
22756 })
22757 });
22758
22759 cx.emit(EditorEvent::InputHandled {
22760 utf16_range_to_replace: range_to_replace,
22761 text: text.into(),
22762 });
22763
22764 if let Some(ranges) = ranges_to_replace {
22765 this.change_selections(SelectionEffects::no_scroll(), window, cx, |s| {
22766 s.select_ranges(ranges)
22767 });
22768 }
22769
22770 let marked_ranges = {
22771 let snapshot = this.buffer.read(cx).read(cx);
22772 this.selections
22773 .disjoint_anchors()
22774 .iter()
22775 .map(|selection| {
22776 selection.start.bias_left(&snapshot)..selection.end.bias_right(&snapshot)
22777 })
22778 .collect::<Vec<_>>()
22779 };
22780
22781 if text.is_empty() {
22782 this.unmark_text(window, cx);
22783 } else {
22784 this.highlight_text::<InputComposition>(
22785 marked_ranges.clone(),
22786 HighlightStyle {
22787 underline: Some(UnderlineStyle {
22788 thickness: px(1.),
22789 color: None,
22790 wavy: false,
22791 }),
22792 ..Default::default()
22793 },
22794 cx,
22795 );
22796 }
22797
22798 // Disable auto-closing when composing text (i.e. typing a `"` on a Brazilian keyboard)
22799 let use_autoclose = this.use_autoclose;
22800 let use_auto_surround = this.use_auto_surround;
22801 this.set_use_autoclose(false);
22802 this.set_use_auto_surround(false);
22803 this.handle_input(text, window, cx);
22804 this.set_use_autoclose(use_autoclose);
22805 this.set_use_auto_surround(use_auto_surround);
22806
22807 if let Some(new_selected_range) = new_selected_range_utf16 {
22808 let snapshot = this.buffer.read(cx).read(cx);
22809 let new_selected_ranges = marked_ranges
22810 .into_iter()
22811 .map(|marked_range| {
22812 let insertion_start = marked_range.start.to_offset_utf16(&snapshot).0;
22813 let new_start = OffsetUtf16(new_selected_range.start + insertion_start);
22814 let new_end = OffsetUtf16(new_selected_range.end + insertion_start);
22815 snapshot.clip_offset_utf16(new_start, Bias::Left)
22816 ..snapshot.clip_offset_utf16(new_end, Bias::Right)
22817 })
22818 .collect::<Vec<_>>();
22819
22820 drop(snapshot);
22821 this.change_selections(SelectionEffects::no_scroll(), window, cx, |selections| {
22822 selections.select_ranges(new_selected_ranges)
22823 });
22824 }
22825 });
22826
22827 self.ime_transaction = self.ime_transaction.or(transaction);
22828 if let Some(transaction) = self.ime_transaction {
22829 self.buffer.update(cx, |buffer, cx| {
22830 buffer.group_until_transaction(transaction, cx);
22831 });
22832 }
22833
22834 if self.text_highlights::<InputComposition>(cx).is_none() {
22835 self.ime_transaction.take();
22836 }
22837 }
22838
22839 fn bounds_for_range(
22840 &mut self,
22841 range_utf16: Range<usize>,
22842 element_bounds: gpui::Bounds<Pixels>,
22843 window: &mut Window,
22844 cx: &mut Context<Self>,
22845 ) -> Option<gpui::Bounds<Pixels>> {
22846 let text_layout_details = self.text_layout_details(window);
22847 let CharacterDimensions {
22848 em_width,
22849 em_advance,
22850 line_height,
22851 } = self.character_dimensions(window);
22852
22853 let snapshot = self.snapshot(window, cx);
22854 let scroll_position = snapshot.scroll_position();
22855 let scroll_left = scroll_position.x * em_advance;
22856
22857 let start = OffsetUtf16(range_utf16.start).to_display_point(&snapshot);
22858 let x = snapshot.x_for_display_point(start, &text_layout_details) - scroll_left
22859 + self.gutter_dimensions.full_width();
22860 let y = line_height * (start.row().as_f32() - scroll_position.y);
22861
22862 Some(Bounds {
22863 origin: element_bounds.origin + point(x, y),
22864 size: size(em_width, line_height),
22865 })
22866 }
22867
22868 fn character_index_for_point(
22869 &mut self,
22870 point: gpui::Point<Pixels>,
22871 _window: &mut Window,
22872 _cx: &mut Context<Self>,
22873 ) -> Option<usize> {
22874 let position_map = self.last_position_map.as_ref()?;
22875 if !position_map.text_hitbox.contains(&point) {
22876 return None;
22877 }
22878 let display_point = position_map.point_for_position(point).previous_valid;
22879 let anchor = position_map
22880 .snapshot
22881 .display_point_to_anchor(display_point, Bias::Left);
22882 let utf16_offset = anchor.to_offset_utf16(&position_map.snapshot.buffer_snapshot);
22883 Some(utf16_offset.0)
22884 }
22885}
22886
22887trait SelectionExt {
22888 fn display_range(&self, map: &DisplaySnapshot) -> Range<DisplayPoint>;
22889 fn spanned_rows(
22890 &self,
22891 include_end_if_at_line_start: bool,
22892 map: &DisplaySnapshot,
22893 ) -> Range<MultiBufferRow>;
22894}
22895
22896impl<T: ToPoint + ToOffset> SelectionExt for Selection<T> {
22897 fn display_range(&self, map: &DisplaySnapshot) -> Range<DisplayPoint> {
22898 let start = self
22899 .start
22900 .to_point(&map.buffer_snapshot)
22901 .to_display_point(map);
22902 let end = self
22903 .end
22904 .to_point(&map.buffer_snapshot)
22905 .to_display_point(map);
22906 if self.reversed {
22907 end..start
22908 } else {
22909 start..end
22910 }
22911 }
22912
22913 fn spanned_rows(
22914 &self,
22915 include_end_if_at_line_start: bool,
22916 map: &DisplaySnapshot,
22917 ) -> Range<MultiBufferRow> {
22918 let start = self.start.to_point(&map.buffer_snapshot);
22919 let mut end = self.end.to_point(&map.buffer_snapshot);
22920 if !include_end_if_at_line_start && start.row != end.row && end.column == 0 {
22921 end.row -= 1;
22922 }
22923
22924 let buffer_start = map.prev_line_boundary(start).0;
22925 let buffer_end = map.next_line_boundary(end).0;
22926 MultiBufferRow(buffer_start.row)..MultiBufferRow(buffer_end.row + 1)
22927 }
22928}
22929
22930impl<T: InvalidationRegion> InvalidationStack<T> {
22931 fn invalidate<S>(&mut self, selections: &[Selection<S>], buffer: &MultiBufferSnapshot)
22932 where
22933 S: Clone + ToOffset,
22934 {
22935 while let Some(region) = self.last() {
22936 let all_selections_inside_invalidation_ranges =
22937 if selections.len() == region.ranges().len() {
22938 selections
22939 .iter()
22940 .zip(region.ranges().iter().map(|r| r.to_offset(buffer)))
22941 .all(|(selection, invalidation_range)| {
22942 let head = selection.head().to_offset(buffer);
22943 invalidation_range.start <= head && invalidation_range.end >= head
22944 })
22945 } else {
22946 false
22947 };
22948
22949 if all_selections_inside_invalidation_ranges {
22950 break;
22951 } else {
22952 self.pop();
22953 }
22954 }
22955 }
22956}
22957
22958impl<T> Default for InvalidationStack<T> {
22959 fn default() -> Self {
22960 Self(Default::default())
22961 }
22962}
22963
22964impl<T> Deref for InvalidationStack<T> {
22965 type Target = Vec<T>;
22966
22967 fn deref(&self) -> &Self::Target {
22968 &self.0
22969 }
22970}
22971
22972impl<T> DerefMut for InvalidationStack<T> {
22973 fn deref_mut(&mut self) -> &mut Self::Target {
22974 &mut self.0
22975 }
22976}
22977
22978impl InvalidationRegion for SnippetState {
22979 fn ranges(&self) -> &[Range<Anchor>] {
22980 &self.ranges[self.active_index]
22981 }
22982}
22983
22984fn inline_completion_edit_text(
22985 current_snapshot: &BufferSnapshot,
22986 edits: &[(Range<Anchor>, String)],
22987 edit_preview: &EditPreview,
22988 include_deletions: bool,
22989 cx: &App,
22990) -> HighlightedText {
22991 let edits = edits
22992 .iter()
22993 .map(|(anchor, text)| {
22994 (
22995 anchor.start.text_anchor..anchor.end.text_anchor,
22996 text.clone(),
22997 )
22998 })
22999 .collect::<Vec<_>>();
23000
23001 edit_preview.highlight_edits(current_snapshot, &edits, include_deletions, cx)
23002}
23003
23004pub fn diagnostic_style(severity: lsp::DiagnosticSeverity, colors: &StatusColors) -> Hsla {
23005 match severity {
23006 lsp::DiagnosticSeverity::ERROR => colors.error,
23007 lsp::DiagnosticSeverity::WARNING => colors.warning,
23008 lsp::DiagnosticSeverity::INFORMATION => colors.info,
23009 lsp::DiagnosticSeverity::HINT => colors.info,
23010 _ => colors.ignored,
23011 }
23012}
23013
23014pub fn styled_runs_for_code_label<'a>(
23015 label: &'a CodeLabel,
23016 syntax_theme: &'a theme::SyntaxTheme,
23017) -> impl 'a + Iterator<Item = (Range<usize>, HighlightStyle)> {
23018 let fade_out = HighlightStyle {
23019 fade_out: Some(0.35),
23020 ..Default::default()
23021 };
23022
23023 let mut prev_end = label.filter_range.end;
23024 label
23025 .runs
23026 .iter()
23027 .enumerate()
23028 .flat_map(move |(ix, (range, highlight_id))| {
23029 let style = if let Some(style) = highlight_id.style(syntax_theme) {
23030 style
23031 } else {
23032 return Default::default();
23033 };
23034 let mut muted_style = style;
23035 muted_style.highlight(fade_out);
23036
23037 let mut runs = SmallVec::<[(Range<usize>, HighlightStyle); 3]>::new();
23038 if range.start >= label.filter_range.end {
23039 if range.start > prev_end {
23040 runs.push((prev_end..range.start, fade_out));
23041 }
23042 runs.push((range.clone(), muted_style));
23043 } else if range.end <= label.filter_range.end {
23044 runs.push((range.clone(), style));
23045 } else {
23046 runs.push((range.start..label.filter_range.end, style));
23047 runs.push((label.filter_range.end..range.end, muted_style));
23048 }
23049 prev_end = cmp::max(prev_end, range.end);
23050
23051 if ix + 1 == label.runs.len() && label.text.len() > prev_end {
23052 runs.push((prev_end..label.text.len(), fade_out));
23053 }
23054
23055 runs
23056 })
23057}
23058
23059pub(crate) fn split_words(text: &str) -> impl std::iter::Iterator<Item = &str> + '_ {
23060 let mut prev_index = 0;
23061 let mut prev_codepoint: Option<char> = None;
23062 text.char_indices()
23063 .chain([(text.len(), '\0')])
23064 .filter_map(move |(index, codepoint)| {
23065 let prev_codepoint = prev_codepoint.replace(codepoint)?;
23066 let is_boundary = index == text.len()
23067 || !prev_codepoint.is_uppercase() && codepoint.is_uppercase()
23068 || !prev_codepoint.is_alphanumeric() && codepoint.is_alphanumeric();
23069 if is_boundary {
23070 let chunk = &text[prev_index..index];
23071 prev_index = index;
23072 Some(chunk)
23073 } else {
23074 None
23075 }
23076 })
23077}
23078
23079pub trait RangeToAnchorExt: Sized {
23080 fn to_anchors(self, snapshot: &MultiBufferSnapshot) -> Range<Anchor>;
23081
23082 fn to_display_points(self, snapshot: &EditorSnapshot) -> Range<DisplayPoint> {
23083 let anchor_range = self.to_anchors(&snapshot.buffer_snapshot);
23084 anchor_range.start.to_display_point(snapshot)..anchor_range.end.to_display_point(snapshot)
23085 }
23086}
23087
23088impl<T: ToOffset> RangeToAnchorExt for Range<T> {
23089 fn to_anchors(self, snapshot: &MultiBufferSnapshot) -> Range<Anchor> {
23090 let start_offset = self.start.to_offset(snapshot);
23091 let end_offset = self.end.to_offset(snapshot);
23092 if start_offset == end_offset {
23093 snapshot.anchor_before(start_offset)..snapshot.anchor_before(end_offset)
23094 } else {
23095 snapshot.anchor_after(self.start)..snapshot.anchor_before(self.end)
23096 }
23097 }
23098}
23099
23100pub trait RowExt {
23101 fn as_f32(&self) -> f32;
23102
23103 fn next_row(&self) -> Self;
23104
23105 fn previous_row(&self) -> Self;
23106
23107 fn minus(&self, other: Self) -> u32;
23108}
23109
23110impl RowExt for DisplayRow {
23111 fn as_f32(&self) -> f32 {
23112 self.0 as f32
23113 }
23114
23115 fn next_row(&self) -> Self {
23116 Self(self.0 + 1)
23117 }
23118
23119 fn previous_row(&self) -> Self {
23120 Self(self.0.saturating_sub(1))
23121 }
23122
23123 fn minus(&self, other: Self) -> u32 {
23124 self.0 - other.0
23125 }
23126}
23127
23128impl RowExt for MultiBufferRow {
23129 fn as_f32(&self) -> f32 {
23130 self.0 as f32
23131 }
23132
23133 fn next_row(&self) -> Self {
23134 Self(self.0 + 1)
23135 }
23136
23137 fn previous_row(&self) -> Self {
23138 Self(self.0.saturating_sub(1))
23139 }
23140
23141 fn minus(&self, other: Self) -> u32 {
23142 self.0 - other.0
23143 }
23144}
23145
23146trait RowRangeExt {
23147 type Row;
23148
23149 fn len(&self) -> usize;
23150
23151 fn iter_rows(&self) -> impl DoubleEndedIterator<Item = Self::Row>;
23152}
23153
23154impl RowRangeExt for Range<MultiBufferRow> {
23155 type Row = MultiBufferRow;
23156
23157 fn len(&self) -> usize {
23158 (self.end.0 - self.start.0) as usize
23159 }
23160
23161 fn iter_rows(&self) -> impl DoubleEndedIterator<Item = MultiBufferRow> {
23162 (self.start.0..self.end.0).map(MultiBufferRow)
23163 }
23164}
23165
23166impl RowRangeExt for Range<DisplayRow> {
23167 type Row = DisplayRow;
23168
23169 fn len(&self) -> usize {
23170 (self.end.0 - self.start.0) as usize
23171 }
23172
23173 fn iter_rows(&self) -> impl DoubleEndedIterator<Item = DisplayRow> {
23174 (self.start.0..self.end.0).map(DisplayRow)
23175 }
23176}
23177
23178/// If select range has more than one line, we
23179/// just point the cursor to range.start.
23180fn collapse_multiline_range(range: Range<Point>) -> Range<Point> {
23181 if range.start.row == range.end.row {
23182 range
23183 } else {
23184 range.start..range.start
23185 }
23186}
23187pub struct KillRing(ClipboardItem);
23188impl Global for KillRing {}
23189
23190const UPDATE_DEBOUNCE: Duration = Duration::from_millis(50);
23191
23192enum BreakpointPromptEditAction {
23193 Log,
23194 Condition,
23195 HitCondition,
23196}
23197
23198struct BreakpointPromptEditor {
23199 pub(crate) prompt: Entity<Editor>,
23200 editor: WeakEntity<Editor>,
23201 breakpoint_anchor: Anchor,
23202 breakpoint: Breakpoint,
23203 edit_action: BreakpointPromptEditAction,
23204 block_ids: HashSet<CustomBlockId>,
23205 editor_margins: Arc<Mutex<EditorMargins>>,
23206 _subscriptions: Vec<Subscription>,
23207}
23208
23209impl BreakpointPromptEditor {
23210 const MAX_LINES: u8 = 4;
23211
23212 fn new(
23213 editor: WeakEntity<Editor>,
23214 breakpoint_anchor: Anchor,
23215 breakpoint: Breakpoint,
23216 edit_action: BreakpointPromptEditAction,
23217 window: &mut Window,
23218 cx: &mut Context<Self>,
23219 ) -> Self {
23220 let base_text = match edit_action {
23221 BreakpointPromptEditAction::Log => breakpoint.message.as_ref(),
23222 BreakpointPromptEditAction::Condition => breakpoint.condition.as_ref(),
23223 BreakpointPromptEditAction::HitCondition => breakpoint.hit_condition.as_ref(),
23224 }
23225 .map(|msg| msg.to_string())
23226 .unwrap_or_default();
23227
23228 let buffer = cx.new(|cx| Buffer::local(base_text, cx));
23229 let buffer = cx.new(|cx| MultiBuffer::singleton(buffer, cx));
23230
23231 let prompt = cx.new(|cx| {
23232 let mut prompt = Editor::new(
23233 EditorMode::AutoHeight {
23234 min_lines: 1,
23235 max_lines: Some(Self::MAX_LINES as usize),
23236 },
23237 buffer,
23238 None,
23239 window,
23240 cx,
23241 );
23242 prompt.set_soft_wrap_mode(language::language_settings::SoftWrap::EditorWidth, cx);
23243 prompt.set_show_cursor_when_unfocused(false, cx);
23244 prompt.set_placeholder_text(
23245 match edit_action {
23246 BreakpointPromptEditAction::Log => "Message to log when a breakpoint is hit. Expressions within {} are interpolated.",
23247 BreakpointPromptEditAction::Condition => "Condition when a breakpoint is hit. Expressions within {} are interpolated.",
23248 BreakpointPromptEditAction::HitCondition => "How many breakpoint hits to ignore",
23249 },
23250 cx,
23251 );
23252
23253 prompt
23254 });
23255
23256 Self {
23257 prompt,
23258 editor,
23259 breakpoint_anchor,
23260 breakpoint,
23261 edit_action,
23262 editor_margins: Arc::new(Mutex::new(EditorMargins::default())),
23263 block_ids: Default::default(),
23264 _subscriptions: vec![],
23265 }
23266 }
23267
23268 pub(crate) fn add_block_ids(&mut self, block_ids: Vec<CustomBlockId>) {
23269 self.block_ids.extend(block_ids)
23270 }
23271
23272 fn confirm(&mut self, _: &menu::Confirm, window: &mut Window, cx: &mut Context<Self>) {
23273 if let Some(editor) = self.editor.upgrade() {
23274 let message = self
23275 .prompt
23276 .read(cx)
23277 .buffer
23278 .read(cx)
23279 .as_singleton()
23280 .expect("A multi buffer in breakpoint prompt isn't possible")
23281 .read(cx)
23282 .as_rope()
23283 .to_string();
23284
23285 editor.update(cx, |editor, cx| {
23286 editor.edit_breakpoint_at_anchor(
23287 self.breakpoint_anchor,
23288 self.breakpoint.clone(),
23289 match self.edit_action {
23290 BreakpointPromptEditAction::Log => {
23291 BreakpointEditAction::EditLogMessage(message.into())
23292 }
23293 BreakpointPromptEditAction::Condition => {
23294 BreakpointEditAction::EditCondition(message.into())
23295 }
23296 BreakpointPromptEditAction::HitCondition => {
23297 BreakpointEditAction::EditHitCondition(message.into())
23298 }
23299 },
23300 cx,
23301 );
23302
23303 editor.remove_blocks(self.block_ids.clone(), None, cx);
23304 cx.focus_self(window);
23305 });
23306 }
23307 }
23308
23309 fn cancel(&mut self, _: &menu::Cancel, window: &mut Window, cx: &mut Context<Self>) {
23310 self.editor
23311 .update(cx, |editor, cx| {
23312 editor.remove_blocks(self.block_ids.clone(), None, cx);
23313 window.focus(&editor.focus_handle);
23314 })
23315 .log_err();
23316 }
23317
23318 fn render_prompt_editor(&self, cx: &mut Context<Self>) -> impl IntoElement {
23319 let settings = ThemeSettings::get_global(cx);
23320 let text_style = TextStyle {
23321 color: if self.prompt.read(cx).read_only(cx) {
23322 cx.theme().colors().text_disabled
23323 } else {
23324 cx.theme().colors().text
23325 },
23326 font_family: settings.buffer_font.family.clone(),
23327 font_fallbacks: settings.buffer_font.fallbacks.clone(),
23328 font_size: settings.buffer_font_size(cx).into(),
23329 font_weight: settings.buffer_font.weight,
23330 line_height: relative(settings.buffer_line_height.value()),
23331 ..Default::default()
23332 };
23333 EditorElement::new(
23334 &self.prompt,
23335 EditorStyle {
23336 background: cx.theme().colors().editor_background,
23337 local_player: cx.theme().players().local(),
23338 text: text_style,
23339 ..Default::default()
23340 },
23341 )
23342 }
23343}
23344
23345impl Render for BreakpointPromptEditor {
23346 fn render(&mut self, window: &mut Window, cx: &mut Context<Self>) -> impl IntoElement {
23347 let editor_margins = *self.editor_margins.lock();
23348 let gutter_dimensions = editor_margins.gutter;
23349 h_flex()
23350 .key_context("Editor")
23351 .bg(cx.theme().colors().editor_background)
23352 .border_y_1()
23353 .border_color(cx.theme().status().info_border)
23354 .size_full()
23355 .py(window.line_height() / 2.5)
23356 .on_action(cx.listener(Self::confirm))
23357 .on_action(cx.listener(Self::cancel))
23358 .child(h_flex().w(gutter_dimensions.full_width() + (gutter_dimensions.margin / 2.0)))
23359 .child(div().flex_1().child(self.render_prompt_editor(cx)))
23360 }
23361}
23362
23363impl Focusable for BreakpointPromptEditor {
23364 fn focus_handle(&self, cx: &App) -> FocusHandle {
23365 self.prompt.focus_handle(cx)
23366 }
23367}
23368
23369fn all_edits_insertions_or_deletions(
23370 edits: &Vec<(Range<Anchor>, String)>,
23371 snapshot: &MultiBufferSnapshot,
23372) -> bool {
23373 let mut all_insertions = true;
23374 let mut all_deletions = true;
23375
23376 for (range, new_text) in edits.iter() {
23377 let range_is_empty = range.to_offset(&snapshot).is_empty();
23378 let text_is_empty = new_text.is_empty();
23379
23380 if range_is_empty != text_is_empty {
23381 if range_is_empty {
23382 all_deletions = false;
23383 } else {
23384 all_insertions = false;
23385 }
23386 } else {
23387 return false;
23388 }
23389
23390 if !all_insertions && !all_deletions {
23391 return false;
23392 }
23393 }
23394 all_insertions || all_deletions
23395}
23396
23397struct MissingEditPredictionKeybindingTooltip;
23398
23399impl Render for MissingEditPredictionKeybindingTooltip {
23400 fn render(&mut self, window: &mut Window, cx: &mut Context<Self>) -> impl IntoElement {
23401 ui::tooltip_container(window, cx, |container, _, cx| {
23402 container
23403 .flex_shrink_0()
23404 .max_w_80()
23405 .min_h(rems_from_px(124.))
23406 .justify_between()
23407 .child(
23408 v_flex()
23409 .flex_1()
23410 .text_ui_sm(cx)
23411 .child(Label::new("Conflict with Accept Keybinding"))
23412 .child("Your keymap currently overrides the default accept keybinding. To continue, assign one keybinding for the `editor::AcceptEditPrediction` action.")
23413 )
23414 .child(
23415 h_flex()
23416 .pb_1()
23417 .gap_1()
23418 .items_end()
23419 .w_full()
23420 .child(Button::new("open-keymap", "Assign Keybinding").size(ButtonSize::Compact).on_click(|_ev, window, cx| {
23421 window.dispatch_action(zed_actions::OpenKeymap.boxed_clone(), cx)
23422 }))
23423 .child(Button::new("see-docs", "See Docs").size(ButtonSize::Compact).on_click(|_ev, _window, cx| {
23424 cx.open_url("https://zed.dev/docs/completions#edit-predictions-missing-keybinding");
23425 })),
23426 )
23427 })
23428 }
23429}
23430
23431#[derive(Debug, Clone, Copy, PartialEq)]
23432pub struct LineHighlight {
23433 pub background: Background,
23434 pub border: Option<gpui::Hsla>,
23435 pub include_gutter: bool,
23436 pub type_id: Option<TypeId>,
23437}
23438
23439struct LineManipulationResult {
23440 pub new_text: String,
23441 pub line_count_before: usize,
23442 pub line_count_after: usize,
23443}
23444
23445fn render_diff_hunk_controls(
23446 row: u32,
23447 status: &DiffHunkStatus,
23448 hunk_range: Range<Anchor>,
23449 is_created_file: bool,
23450 line_height: Pixels,
23451 editor: &Entity<Editor>,
23452 _window: &mut Window,
23453 cx: &mut App,
23454) -> AnyElement {
23455 h_flex()
23456 .h(line_height)
23457 .mr_1()
23458 .gap_1()
23459 .px_0p5()
23460 .pb_1()
23461 .border_x_1()
23462 .border_b_1()
23463 .border_color(cx.theme().colors().border_variant)
23464 .rounded_b_lg()
23465 .bg(cx.theme().colors().editor_background)
23466 .gap_1()
23467 .block_mouse_except_scroll()
23468 .shadow_md()
23469 .child(if status.has_secondary_hunk() {
23470 Button::new(("stage", row as u64), "Stage")
23471 .alpha(if status.is_pending() { 0.66 } else { 1.0 })
23472 .tooltip({
23473 let focus_handle = editor.focus_handle(cx);
23474 move |window, cx| {
23475 Tooltip::for_action_in(
23476 "Stage Hunk",
23477 &::git::ToggleStaged,
23478 &focus_handle,
23479 window,
23480 cx,
23481 )
23482 }
23483 })
23484 .on_click({
23485 let editor = editor.clone();
23486 move |_event, _window, cx| {
23487 editor.update(cx, |editor, cx| {
23488 editor.stage_or_unstage_diff_hunks(
23489 true,
23490 vec![hunk_range.start..hunk_range.start],
23491 cx,
23492 );
23493 });
23494 }
23495 })
23496 } else {
23497 Button::new(("unstage", row as u64), "Unstage")
23498 .alpha(if status.is_pending() { 0.66 } else { 1.0 })
23499 .tooltip({
23500 let focus_handle = editor.focus_handle(cx);
23501 move |window, cx| {
23502 Tooltip::for_action_in(
23503 "Unstage Hunk",
23504 &::git::ToggleStaged,
23505 &focus_handle,
23506 window,
23507 cx,
23508 )
23509 }
23510 })
23511 .on_click({
23512 let editor = editor.clone();
23513 move |_event, _window, cx| {
23514 editor.update(cx, |editor, cx| {
23515 editor.stage_or_unstage_diff_hunks(
23516 false,
23517 vec![hunk_range.start..hunk_range.start],
23518 cx,
23519 );
23520 });
23521 }
23522 })
23523 })
23524 .child(
23525 Button::new(("restore", row as u64), "Restore")
23526 .tooltip({
23527 let focus_handle = editor.focus_handle(cx);
23528 move |window, cx| {
23529 Tooltip::for_action_in(
23530 "Restore Hunk",
23531 &::git::Restore,
23532 &focus_handle,
23533 window,
23534 cx,
23535 )
23536 }
23537 })
23538 .on_click({
23539 let editor = editor.clone();
23540 move |_event, window, cx| {
23541 editor.update(cx, |editor, cx| {
23542 let snapshot = editor.snapshot(window, cx);
23543 let point = hunk_range.start.to_point(&snapshot.buffer_snapshot);
23544 editor.restore_hunks_in_ranges(vec![point..point], window, cx);
23545 });
23546 }
23547 })
23548 .disabled(is_created_file),
23549 )
23550 .when(
23551 !editor.read(cx).buffer().read(cx).all_diff_hunks_expanded(),
23552 |el| {
23553 el.child(
23554 IconButton::new(("next-hunk", row as u64), IconName::ArrowDown)
23555 .shape(IconButtonShape::Square)
23556 .icon_size(IconSize::Small)
23557 // .disabled(!has_multiple_hunks)
23558 .tooltip({
23559 let focus_handle = editor.focus_handle(cx);
23560 move |window, cx| {
23561 Tooltip::for_action_in(
23562 "Next Hunk",
23563 &GoToHunk,
23564 &focus_handle,
23565 window,
23566 cx,
23567 )
23568 }
23569 })
23570 .on_click({
23571 let editor = editor.clone();
23572 move |_event, window, cx| {
23573 editor.update(cx, |editor, cx| {
23574 let snapshot = editor.snapshot(window, cx);
23575 let position =
23576 hunk_range.end.to_point(&snapshot.buffer_snapshot);
23577 editor.go_to_hunk_before_or_after_position(
23578 &snapshot,
23579 position,
23580 Direction::Next,
23581 window,
23582 cx,
23583 );
23584 editor.expand_selected_diff_hunks(cx);
23585 });
23586 }
23587 }),
23588 )
23589 .child(
23590 IconButton::new(("prev-hunk", row as u64), IconName::ArrowUp)
23591 .shape(IconButtonShape::Square)
23592 .icon_size(IconSize::Small)
23593 // .disabled(!has_multiple_hunks)
23594 .tooltip({
23595 let focus_handle = editor.focus_handle(cx);
23596 move |window, cx| {
23597 Tooltip::for_action_in(
23598 "Previous Hunk",
23599 &GoToPreviousHunk,
23600 &focus_handle,
23601 window,
23602 cx,
23603 )
23604 }
23605 })
23606 .on_click({
23607 let editor = editor.clone();
23608 move |_event, window, cx| {
23609 editor.update(cx, |editor, cx| {
23610 let snapshot = editor.snapshot(window, cx);
23611 let point =
23612 hunk_range.start.to_point(&snapshot.buffer_snapshot);
23613 editor.go_to_hunk_before_or_after_position(
23614 &snapshot,
23615 point,
23616 Direction::Prev,
23617 window,
23618 cx,
23619 );
23620 editor.expand_selected_diff_hunks(cx);
23621 });
23622 }
23623 }),
23624 )
23625 },
23626 )
23627 .into_any_element()
23628}