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;
18mod code_context_menus;
19pub mod commit_tooltip;
20pub mod display_map;
21mod editor_settings;
22mod editor_settings_controls;
23mod element;
24mod git;
25mod highlight_matching_bracket;
26mod hover_links;
27mod hover_popover;
28mod indent_guides;
29mod inlay_hint_cache;
30pub mod items;
31mod jsx_tag_auto_close;
32mod linked_editing_ranges;
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 editor_tests;
45#[cfg(test)]
46mod inline_completion_tests;
47mod signature_help;
48#[cfg(any(test, feature = "test-support"))]
49pub mod test;
50
51pub(crate) use actions::*;
52pub use actions::{AcceptEditPrediction, OpenExcerpts, OpenExcerptsSplit};
53use aho_corasick::AhoCorasick;
54use anyhow::{anyhow, Context as _, Result};
55use blink_manager::BlinkManager;
56use buffer_diff::DiffHunkStatus;
57use client::{Collaborator, ParticipantIndex};
58use clock::ReplicaId;
59use collections::{BTreeMap, HashMap, HashSet, VecDeque};
60use convert_case::{Case, Casing};
61use display_map::*;
62pub use display_map::{DisplayPoint, FoldPlaceholder};
63pub use editor_settings::{
64 CurrentLineHighlight, EditorSettings, ScrollBeyondLastLine, SearchSettings, ShowScrollbar,
65};
66pub use editor_settings_controls::*;
67use element::{layout_line, AcceptEditPredictionBinding, LineWithInvisibles, PositionMap};
68pub use element::{
69 CursorLayout, EditorElement, HighlightedRange, HighlightedRangeLine, PointForPosition,
70};
71use futures::{
72 future::{self, Shared},
73 FutureExt,
74};
75use fuzzy::StringMatchCandidate;
76
77use ::git::Restore;
78use code_context_menus::{
79 AvailableCodeAction, CodeActionContents, CodeActionsItem, CodeActionsMenu, CodeContextMenu,
80 CompletionsMenu, ContextMenuOrigin,
81};
82use git::blame::GitBlame;
83use gpui::{
84 div, impl_actions, point, prelude::*, pulsating_between, px, relative, size, Action, Animation,
85 AnimationExt, AnyElement, App, AsyncWindowContext, AvailableSpace, Background, Bounds,
86 ClipboardEntry, ClipboardItem, Context, DispatchPhase, Edges, Entity, EntityInputHandler,
87 EventEmitter, FocusHandle, FocusOutEvent, Focusable, FontId, FontWeight, Global,
88 HighlightStyle, Hsla, KeyContext, Modifiers, MouseButton, MouseDownEvent, PaintQuad,
89 ParentElement, Pixels, Render, SharedString, Size, Stateful, Styled, StyledText, Subscription,
90 Task, TextStyle, TextStyleRefinement, UTF16Selection, UnderlineStyle, UniformListScrollHandle,
91 WeakEntity, WeakFocusHandle, Window,
92};
93use highlight_matching_bracket::refresh_matching_bracket_highlights;
94use hover_popover::{hide_hover, HoverState};
95use indent_guides::ActiveIndentGuidesState;
96use inlay_hint_cache::{InlayHintCache, InlaySplice, InvalidationStrategy};
97pub use inline_completion::Direction;
98use inline_completion::{EditPredictionProvider, InlineCompletionProviderHandle};
99pub use items::MAX_TAB_TITLE_LEN;
100use itertools::Itertools;
101use language::{
102 language_settings::{
103 self, all_language_settings, language_settings, InlayHintSettings, RewrapBehavior,
104 WordsCompletionMode,
105 },
106 point_from_lsp, text_diff_with_options, AutoindentMode, BracketMatch, BracketPair, Buffer,
107 Capability, CharKind, CodeLabel, CursorShape, Diagnostic, DiffOptions, EditPredictionsMode,
108 EditPreview, HighlightedText, IndentKind, IndentSize, Language, OffsetRangeExt, Point,
109 Selection, SelectionGoal, TextObject, TransactionId, TreeSitterOptions, WordsQuery,
110};
111use language::{point_to_lsp, BufferRow, CharClassifier, Runnable, RunnableRange};
112use linked_editing_ranges::refresh_linked_ranges;
113use mouse_context_menu::MouseContextMenu;
114use persistence::DB;
115pub use proposed_changes_editor::{
116 ProposedChangeLocation, ProposedChangesEditor, ProposedChangesEditorToolbar,
117};
118use smallvec::smallvec;
119use std::iter::Peekable;
120use task::{ResolvedTask, TaskTemplate, TaskVariables};
121
122use hover_links::{find_file, HoverLink, HoveredLinkState, InlayHighlight};
123pub use lsp::CompletionContext;
124use lsp::{
125 CodeActionKind, CompletionItemKind, CompletionTriggerKind, DiagnosticSeverity,
126 InsertTextFormat, LanguageServerId, LanguageServerName,
127};
128
129use language::BufferSnapshot;
130use movement::TextLayoutDetails;
131pub use multi_buffer::{
132 Anchor, AnchorRangeExt, ExcerptId, ExcerptRange, MultiBuffer, MultiBufferSnapshot, RowInfo,
133 ToOffset, ToPoint,
134};
135use multi_buffer::{
136 ExcerptInfo, ExpandExcerptDirection, MultiBufferDiffHunk, MultiBufferPoint, MultiBufferRow,
137 MultiOrSingleBufferOffsetRange, ToOffsetUtf16,
138};
139use project::{
140 lsp_store::{CompletionDocumentation, FormatTrigger, LspFormatTarget, OpenLspBufferHandle},
141 project_settings::{GitGutterSetting, ProjectSettings},
142 CodeAction, Completion, CompletionIntent, CompletionSource, DocumentHighlight, InlayHint,
143 Location, LocationLink, PrepareRenameResponse, Project, ProjectItem, ProjectTransaction,
144 TaskSourceKind,
145};
146use rand::prelude::*;
147use rpc::{proto::*, ErrorExt};
148use scroll::{Autoscroll, OngoingScroll, ScrollAnchor, ScrollManager, ScrollbarAutoHide};
149use selections_collection::{
150 resolve_selections, MutableSelectionsCollection, SelectionsCollection,
151};
152use serde::{Deserialize, Serialize};
153use settings::{update_settings_file, Settings, SettingsLocation, SettingsStore};
154use smallvec::SmallVec;
155use snippet::Snippet;
156use std::{
157 any::TypeId,
158 borrow::Cow,
159 cell::RefCell,
160 cmp::{self, Ordering, Reverse},
161 mem,
162 num::NonZeroU32,
163 ops::{ControlFlow, Deref, DerefMut, Not as _, Range, RangeInclusive},
164 path::{Path, PathBuf},
165 rc::Rc,
166 sync::Arc,
167 time::{Duration, Instant},
168};
169pub use sum_tree::Bias;
170use sum_tree::TreeMap;
171use text::{BufferId, OffsetUtf16, Rope};
172use theme::{
173 observe_buffer_font_size_adjustment, ActiveTheme, PlayerColor, StatusColors, SyntaxTheme,
174 ThemeColors, ThemeSettings,
175};
176use ui::{
177 h_flex, prelude::*, ButtonSize, ButtonStyle, Disclosure, IconButton, IconName, IconSize, Key,
178 Tooltip,
179};
180use util::{defer, maybe, post_inc, RangeExt, ResultExt, TryFutureExt};
181use workspace::{
182 item::{ItemHandle, PreviewTabsSettings},
183 ItemId, RestoreOnStartupBehavior,
184};
185use workspace::{
186 notifications::{DetachAndPromptErr, NotificationId, NotifyTaskExt},
187 WorkspaceSettings,
188};
189use workspace::{
190 searchable::SearchEvent, ItemNavHistory, SplitDirection, ViewId, Workspace, WorkspaceId,
191};
192use workspace::{Item as WorkspaceItem, OpenInTerminal, OpenTerminal, TabBarSettings, Toast};
193
194use crate::hover_links::{find_url, find_url_from_range};
195use crate::signature_help::{SignatureHelpHiddenBy, SignatureHelpState};
196
197pub const FILE_HEADER_HEIGHT: u32 = 2;
198pub const MULTI_BUFFER_EXCERPT_HEADER_HEIGHT: u32 = 1;
199pub const DEFAULT_MULTIBUFFER_CONTEXT: u32 = 2;
200const CURSOR_BLINK_INTERVAL: Duration = Duration::from_millis(500);
201const MAX_LINE_LEN: usize = 1024;
202const MIN_NAVIGATION_HISTORY_ROW_DELTA: i64 = 10;
203const MAX_SELECTION_HISTORY_LEN: usize = 1024;
204pub(crate) const CURSORS_VISIBLE_FOR: Duration = Duration::from_millis(2000);
205#[doc(hidden)]
206pub const CODE_ACTIONS_DEBOUNCE_TIMEOUT: Duration = Duration::from_millis(250);
207
208pub(crate) const CODE_ACTION_TIMEOUT: Duration = Duration::from_secs(5);
209pub(crate) const FORMAT_TIMEOUT: Duration = Duration::from_secs(5);
210pub(crate) const SCROLL_CENTER_TOP_BOTTOM_DEBOUNCE_TIMEOUT: Duration = Duration::from_secs(1);
211
212pub(crate) const EDIT_PREDICTION_KEY_CONTEXT: &str = "edit_prediction";
213pub(crate) const EDIT_PREDICTION_CONFLICT_KEY_CONTEXT: &str = "edit_prediction_conflict";
214
215const COLUMNAR_SELECTION_MODIFIERS: Modifiers = Modifiers {
216 alt: true,
217 shift: true,
218 control: false,
219 platform: false,
220 function: false,
221};
222
223#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
224pub enum InlayId {
225 InlineCompletion(usize),
226 Hint(usize),
227}
228
229impl InlayId {
230 fn id(&self) -> usize {
231 match self {
232 Self::InlineCompletion(id) => *id,
233 Self::Hint(id) => *id,
234 }
235 }
236}
237
238enum DocumentHighlightRead {}
239enum DocumentHighlightWrite {}
240enum InputComposition {}
241enum SelectedTextHighlight {}
242
243#[derive(Debug, Copy, Clone, PartialEq, Eq)]
244pub enum Navigated {
245 Yes,
246 No,
247}
248
249impl Navigated {
250 pub fn from_bool(yes: bool) -> Navigated {
251 if yes {
252 Navigated::Yes
253 } else {
254 Navigated::No
255 }
256 }
257}
258
259#[derive(Debug, Clone, PartialEq, Eq)]
260enum DisplayDiffHunk {
261 Folded {
262 display_row: DisplayRow,
263 },
264 Unfolded {
265 is_created_file: bool,
266 diff_base_byte_range: Range<usize>,
267 display_row_range: Range<DisplayRow>,
268 multi_buffer_range: Range<Anchor>,
269 status: DiffHunkStatus,
270 },
271}
272
273pub fn init_settings(cx: &mut App) {
274 EditorSettings::register(cx);
275}
276
277pub fn init(cx: &mut App) {
278 init_settings(cx);
279
280 workspace::register_project_item::<Editor>(cx);
281 workspace::FollowableViewRegistry::register::<Editor>(cx);
282 workspace::register_serializable_item::<Editor>(cx);
283
284 cx.observe_new(
285 |workspace: &mut Workspace, _: Option<&mut Window>, _cx: &mut Context<Workspace>| {
286 workspace.register_action(Editor::new_file);
287 workspace.register_action(Editor::new_file_vertical);
288 workspace.register_action(Editor::new_file_horizontal);
289 workspace.register_action(Editor::cancel_language_server_work);
290 },
291 )
292 .detach();
293
294 cx.on_action(move |_: &workspace::NewFile, cx| {
295 let app_state = workspace::AppState::global(cx);
296 if let Some(app_state) = app_state.upgrade() {
297 workspace::open_new(
298 Default::default(),
299 app_state,
300 cx,
301 |workspace, window, cx| {
302 Editor::new_file(workspace, &Default::default(), window, cx)
303 },
304 )
305 .detach();
306 }
307 });
308 cx.on_action(move |_: &workspace::NewWindow, cx| {
309 let app_state = workspace::AppState::global(cx);
310 if let Some(app_state) = app_state.upgrade() {
311 workspace::open_new(
312 Default::default(),
313 app_state,
314 cx,
315 |workspace, window, cx| {
316 cx.activate(true);
317 Editor::new_file(workspace, &Default::default(), window, cx)
318 },
319 )
320 .detach();
321 }
322 });
323}
324
325pub struct SearchWithinRange;
326
327trait InvalidationRegion {
328 fn ranges(&self) -> &[Range<Anchor>];
329}
330
331#[derive(Clone, Debug, PartialEq)]
332pub enum SelectPhase {
333 Begin {
334 position: DisplayPoint,
335 add: bool,
336 click_count: usize,
337 },
338 BeginColumnar {
339 position: DisplayPoint,
340 reset: bool,
341 goal_column: u32,
342 },
343 Extend {
344 position: DisplayPoint,
345 click_count: usize,
346 },
347 Update {
348 position: DisplayPoint,
349 goal_column: u32,
350 scroll_delta: gpui::Point<f32>,
351 },
352 End,
353}
354
355#[derive(Clone, Debug)]
356pub enum SelectMode {
357 Character,
358 Word(Range<Anchor>),
359 Line(Range<Anchor>),
360 All,
361}
362
363#[derive(Copy, Clone, PartialEq, Eq, Debug)]
364pub enum EditorMode {
365 SingleLine { auto_width: bool },
366 AutoHeight { max_lines: usize },
367 Full,
368}
369
370#[derive(Copy, Clone, Debug)]
371pub enum SoftWrap {
372 /// Prefer not to wrap at all.
373 ///
374 /// Note: this is currently internal, as actually limited by [`crate::MAX_LINE_LEN`] until it wraps.
375 /// The mode is used inside git diff hunks, where it's seems currently more useful to not wrap as much as possible.
376 GitDiff,
377 /// Prefer a single line generally, unless an overly long line is encountered.
378 None,
379 /// Soft wrap lines that exceed the editor width.
380 EditorWidth,
381 /// Soft wrap lines at the preferred line length.
382 Column(u32),
383 /// Soft wrap line at the preferred line length or the editor width (whichever is smaller).
384 Bounded(u32),
385}
386
387#[derive(Clone)]
388pub struct EditorStyle {
389 pub background: Hsla,
390 pub local_player: PlayerColor,
391 pub text: TextStyle,
392 pub scrollbar_width: Pixels,
393 pub syntax: Arc<SyntaxTheme>,
394 pub status: StatusColors,
395 pub inlay_hints_style: HighlightStyle,
396 pub inline_completion_styles: InlineCompletionStyles,
397 pub unnecessary_code_fade: f32,
398}
399
400impl Default for EditorStyle {
401 fn default() -> Self {
402 Self {
403 background: Hsla::default(),
404 local_player: PlayerColor::default(),
405 text: TextStyle::default(),
406 scrollbar_width: Pixels::default(),
407 syntax: Default::default(),
408 // HACK: Status colors don't have a real default.
409 // We should look into removing the status colors from the editor
410 // style and retrieve them directly from the theme.
411 status: StatusColors::dark(),
412 inlay_hints_style: HighlightStyle::default(),
413 inline_completion_styles: InlineCompletionStyles {
414 insertion: HighlightStyle::default(),
415 whitespace: HighlightStyle::default(),
416 },
417 unnecessary_code_fade: Default::default(),
418 }
419 }
420}
421
422pub fn make_inlay_hints_style(cx: &mut App) -> HighlightStyle {
423 let show_background = language_settings::language_settings(None, None, cx)
424 .inlay_hints
425 .show_background;
426
427 HighlightStyle {
428 color: Some(cx.theme().status().hint),
429 background_color: show_background.then(|| cx.theme().status().hint_background),
430 ..HighlightStyle::default()
431 }
432}
433
434pub fn make_suggestion_styles(cx: &mut App) -> InlineCompletionStyles {
435 InlineCompletionStyles {
436 insertion: HighlightStyle {
437 color: Some(cx.theme().status().predictive),
438 ..HighlightStyle::default()
439 },
440 whitespace: HighlightStyle {
441 background_color: Some(cx.theme().status().created_background),
442 ..HighlightStyle::default()
443 },
444 }
445}
446
447type CompletionId = usize;
448
449pub(crate) enum EditDisplayMode {
450 TabAccept,
451 DiffPopover,
452 Inline,
453}
454
455enum InlineCompletion {
456 Edit {
457 edits: Vec<(Range<Anchor>, String)>,
458 edit_preview: Option<EditPreview>,
459 display_mode: EditDisplayMode,
460 snapshot: BufferSnapshot,
461 },
462 Move {
463 target: Anchor,
464 snapshot: BufferSnapshot,
465 },
466}
467
468struct InlineCompletionState {
469 inlay_ids: Vec<InlayId>,
470 completion: InlineCompletion,
471 completion_id: Option<SharedString>,
472 invalidation_range: Range<Anchor>,
473}
474
475enum EditPredictionSettings {
476 Disabled,
477 Enabled {
478 show_in_menu: bool,
479 preview_requires_modifier: bool,
480 },
481}
482
483enum InlineCompletionHighlight {}
484
485#[derive(Debug, Clone)]
486struct InlineDiagnostic {
487 message: SharedString,
488 group_id: usize,
489 is_primary: bool,
490 start: Point,
491 severity: DiagnosticSeverity,
492}
493
494pub enum MenuInlineCompletionsPolicy {
495 Never,
496 ByProvider,
497}
498
499pub enum EditPredictionPreview {
500 /// Modifier is not pressed
501 Inactive { released_too_fast: bool },
502 /// Modifier pressed
503 Active {
504 since: Instant,
505 previous_scroll_position: Option<ScrollAnchor>,
506 },
507}
508
509impl EditPredictionPreview {
510 pub fn released_too_fast(&self) -> bool {
511 match self {
512 EditPredictionPreview::Inactive { released_too_fast } => *released_too_fast,
513 EditPredictionPreview::Active { .. } => false,
514 }
515 }
516
517 pub fn set_previous_scroll_position(&mut self, scroll_position: Option<ScrollAnchor>) {
518 if let EditPredictionPreview::Active {
519 previous_scroll_position,
520 ..
521 } = self
522 {
523 *previous_scroll_position = scroll_position;
524 }
525 }
526}
527
528#[derive(Copy, Clone, Eq, PartialEq, PartialOrd, Ord, Debug, Default)]
529struct EditorActionId(usize);
530
531impl EditorActionId {
532 pub fn post_inc(&mut self) -> Self {
533 let answer = self.0;
534
535 *self = Self(answer + 1);
536
537 Self(answer)
538 }
539}
540
541// type GetFieldEditorTheme = dyn Fn(&theme::Theme) -> theme::FieldEditor;
542// type OverrideTextStyle = dyn Fn(&EditorStyle) -> Option<HighlightStyle>;
543
544type BackgroundHighlight = (fn(&ThemeColors) -> Hsla, Arc<[Range<Anchor>]>);
545type GutterHighlight = (fn(&App) -> Hsla, Arc<[Range<Anchor>]>);
546
547#[derive(Default)]
548struct ScrollbarMarkerState {
549 scrollbar_size: Size<Pixels>,
550 dirty: bool,
551 markers: Arc<[PaintQuad]>,
552 pending_refresh: Option<Task<Result<()>>>,
553}
554
555impl ScrollbarMarkerState {
556 fn should_refresh(&self, scrollbar_size: Size<Pixels>) -> bool {
557 self.pending_refresh.is_none() && (self.scrollbar_size != scrollbar_size || self.dirty)
558 }
559}
560
561#[derive(Clone, Debug)]
562struct RunnableTasks {
563 templates: Vec<(TaskSourceKind, TaskTemplate)>,
564 offset: multi_buffer::Anchor,
565 // We need the column at which the task context evaluation should take place (when we're spawning it via gutter).
566 column: u32,
567 // Values of all named captures, including those starting with '_'
568 extra_variables: HashMap<String, String>,
569 // Full range of the tagged region. We use it to determine which `extra_variables` to grab for context resolution in e.g. a modal.
570 context_range: Range<BufferOffset>,
571}
572
573impl RunnableTasks {
574 fn resolve<'a>(
575 &'a self,
576 cx: &'a task::TaskContext,
577 ) -> impl Iterator<Item = (TaskSourceKind, ResolvedTask)> + 'a {
578 self.templates.iter().filter_map(|(kind, template)| {
579 template
580 .resolve_task(&kind.to_id_base(), cx)
581 .map(|task| (kind.clone(), task))
582 })
583 }
584}
585
586#[derive(Clone)]
587struct ResolvedTasks {
588 templates: SmallVec<[(TaskSourceKind, ResolvedTask); 1]>,
589 position: Anchor,
590}
591#[derive(Copy, Clone, Debug, PartialEq, PartialOrd)]
592struct BufferOffset(usize);
593
594// Addons allow storing per-editor state in other crates (e.g. Vim)
595pub trait Addon: 'static {
596 fn extend_key_context(&self, _: &mut KeyContext, _: &App) {}
597
598 fn render_buffer_header_controls(
599 &self,
600 _: &ExcerptInfo,
601 _: &Window,
602 _: &App,
603 ) -> Option<AnyElement> {
604 None
605 }
606
607 fn to_any(&self) -> &dyn std::any::Any;
608}
609
610/// Zed's primary implementation of text input, allowing users to edit a [`MultiBuffer`].
611///
612/// See the [module level documentation](self) for more information.
613pub struct Editor {
614 focus_handle: FocusHandle,
615 last_focused_descendant: Option<WeakFocusHandle>,
616 /// The text buffer being edited
617 buffer: Entity<MultiBuffer>,
618 /// Map of how text in the buffer should be displayed.
619 /// Handles soft wraps, folds, fake inlay text insertions, etc.
620 pub display_map: Entity<DisplayMap>,
621 pub selections: SelectionsCollection,
622 pub scroll_manager: ScrollManager,
623 /// When inline assist editors are linked, they all render cursors because
624 /// typing enters text into each of them, even the ones that aren't focused.
625 pub(crate) show_cursor_when_unfocused: bool,
626 columnar_selection_tail: Option<Anchor>,
627 add_selections_state: Option<AddSelectionsState>,
628 select_next_state: Option<SelectNextState>,
629 select_prev_state: Option<SelectNextState>,
630 selection_history: SelectionHistory,
631 autoclose_regions: Vec<AutocloseRegion>,
632 snippet_stack: InvalidationStack<SnippetState>,
633 select_larger_syntax_node_stack: Vec<Box<[Selection<usize>]>>,
634 ime_transaction: Option<TransactionId>,
635 active_diagnostics: Option<ActiveDiagnosticGroup>,
636 show_inline_diagnostics: bool,
637 inline_diagnostics_update: Task<()>,
638 inline_diagnostics_enabled: bool,
639 inline_diagnostics: Vec<(Anchor, InlineDiagnostic)>,
640 soft_wrap_mode_override: Option<language_settings::SoftWrap>,
641 hard_wrap: Option<usize>,
642
643 // TODO: make this a access method
644 pub project: Option<Entity<Project>>,
645 semantics_provider: Option<Rc<dyn SemanticsProvider>>,
646 completion_provider: Option<Box<dyn CompletionProvider>>,
647 collaboration_hub: Option<Box<dyn CollaborationHub>>,
648 blink_manager: Entity<BlinkManager>,
649 show_cursor_names: bool,
650 hovered_cursors: HashMap<HoveredCursor, Task<()>>,
651 pub show_local_selections: bool,
652 mode: EditorMode,
653 show_breadcrumbs: bool,
654 show_gutter: bool,
655 show_scrollbars: bool,
656 show_line_numbers: Option<bool>,
657 use_relative_line_numbers: Option<bool>,
658 show_git_diff_gutter: Option<bool>,
659 show_code_actions: Option<bool>,
660 show_runnables: Option<bool>,
661 show_wrap_guides: Option<bool>,
662 show_indent_guides: Option<bool>,
663 placeholder_text: Option<Arc<str>>,
664 highlight_order: usize,
665 highlighted_rows: HashMap<TypeId, Vec<RowHighlight>>,
666 background_highlights: TreeMap<TypeId, BackgroundHighlight>,
667 gutter_highlights: TreeMap<TypeId, GutterHighlight>,
668 scrollbar_marker_state: ScrollbarMarkerState,
669 active_indent_guides_state: ActiveIndentGuidesState,
670 nav_history: Option<ItemNavHistory>,
671 context_menu: RefCell<Option<CodeContextMenu>>,
672 mouse_context_menu: Option<MouseContextMenu>,
673 completion_tasks: Vec<(CompletionId, Task<Option<()>>)>,
674 signature_help_state: SignatureHelpState,
675 auto_signature_help: Option<bool>,
676 find_all_references_task_sources: Vec<Anchor>,
677 next_completion_id: CompletionId,
678 available_code_actions: Option<(Location, Rc<[AvailableCodeAction]>)>,
679 code_actions_task: Option<Task<Result<()>>>,
680 selection_highlight_task: Option<Task<()>>,
681 document_highlights_task: Option<Task<()>>,
682 linked_editing_range_task: Option<Task<Option<()>>>,
683 linked_edit_ranges: linked_editing_ranges::LinkedEditingRanges,
684 pending_rename: Option<RenameState>,
685 searchable: bool,
686 cursor_shape: CursorShape,
687 current_line_highlight: Option<CurrentLineHighlight>,
688 collapse_matches: bool,
689 autoindent_mode: Option<AutoindentMode>,
690 workspace: Option<(WeakEntity<Workspace>, Option<WorkspaceId>)>,
691 input_enabled: bool,
692 use_modal_editing: bool,
693 read_only: bool,
694 leader_peer_id: Option<PeerId>,
695 remote_id: Option<ViewId>,
696 hover_state: HoverState,
697 pending_mouse_down: Option<Rc<RefCell<Option<MouseDownEvent>>>>,
698 gutter_hovered: bool,
699 hovered_link_state: Option<HoveredLinkState>,
700 edit_prediction_provider: Option<RegisteredInlineCompletionProvider>,
701 code_action_providers: Vec<Rc<dyn CodeActionProvider>>,
702 active_inline_completion: Option<InlineCompletionState>,
703 /// Used to prevent flickering as the user types while the menu is open
704 stale_inline_completion_in_menu: Option<InlineCompletionState>,
705 edit_prediction_settings: EditPredictionSettings,
706 inline_completions_hidden_for_vim_mode: bool,
707 show_inline_completions_override: Option<bool>,
708 menu_inline_completions_policy: MenuInlineCompletionsPolicy,
709 edit_prediction_preview: EditPredictionPreview,
710 edit_prediction_indent_conflict: bool,
711 edit_prediction_requires_modifier_in_indent_conflict: bool,
712 inlay_hint_cache: InlayHintCache,
713 next_inlay_id: usize,
714 _subscriptions: Vec<Subscription>,
715 pixel_position_of_newest_cursor: Option<gpui::Point<Pixels>>,
716 gutter_dimensions: GutterDimensions,
717 style: Option<EditorStyle>,
718 text_style_refinement: Option<TextStyleRefinement>,
719 next_editor_action_id: EditorActionId,
720 editor_actions:
721 Rc<RefCell<BTreeMap<EditorActionId, Box<dyn Fn(&mut Window, &mut Context<Self>)>>>>,
722 use_autoclose: bool,
723 use_auto_surround: bool,
724 auto_replace_emoji_shortcode: bool,
725 jsx_tag_auto_close_enabled_in_any_buffer: bool,
726 show_git_blame_gutter: bool,
727 show_git_blame_inline: bool,
728 show_git_blame_inline_delay_task: Option<Task<()>>,
729 git_blame_inline_tooltip: Option<WeakEntity<crate::commit_tooltip::CommitTooltip>>,
730 git_blame_inline_enabled: bool,
731 serialize_dirty_buffers: bool,
732 show_selection_menu: Option<bool>,
733 blame: Option<Entity<GitBlame>>,
734 blame_subscription: Option<Subscription>,
735 custom_context_menu: Option<
736 Box<
737 dyn 'static
738 + Fn(
739 &mut Self,
740 DisplayPoint,
741 &mut Window,
742 &mut Context<Self>,
743 ) -> Option<Entity<ui::ContextMenu>>,
744 >,
745 >,
746 last_bounds: Option<Bounds<Pixels>>,
747 last_position_map: Option<Rc<PositionMap>>,
748 expect_bounds_change: Option<Bounds<Pixels>>,
749 tasks: BTreeMap<(BufferId, BufferRow), RunnableTasks>,
750 tasks_update_task: Option<Task<()>>,
751 in_project_search: bool,
752 previous_search_ranges: Option<Arc<[Range<Anchor>]>>,
753 breadcrumb_header: Option<String>,
754 focused_block: Option<FocusedBlock>,
755 next_scroll_position: NextScrollCursorCenterTopBottom,
756 addons: HashMap<TypeId, Box<dyn Addon>>,
757 registered_buffers: HashMap<BufferId, OpenLspBufferHandle>,
758 load_diff_task: Option<Shared<Task<()>>>,
759 selection_mark_mode: bool,
760 toggle_fold_multiple_buffers: Task<()>,
761 _scroll_cursor_center_top_bottom_task: Task<()>,
762 serialize_selections: Task<()>,
763}
764
765#[derive(Copy, Clone, Debug, PartialEq, Eq, Default)]
766enum NextScrollCursorCenterTopBottom {
767 #[default]
768 Center,
769 Top,
770 Bottom,
771}
772
773impl NextScrollCursorCenterTopBottom {
774 fn next(&self) -> Self {
775 match self {
776 Self::Center => Self::Top,
777 Self::Top => Self::Bottom,
778 Self::Bottom => Self::Center,
779 }
780 }
781}
782
783#[derive(Clone)]
784pub struct EditorSnapshot {
785 pub mode: EditorMode,
786 show_gutter: bool,
787 show_line_numbers: Option<bool>,
788 show_git_diff_gutter: Option<bool>,
789 show_code_actions: Option<bool>,
790 show_runnables: Option<bool>,
791 git_blame_gutter_max_author_length: Option<usize>,
792 pub display_snapshot: DisplaySnapshot,
793 pub placeholder_text: Option<Arc<str>>,
794 is_focused: bool,
795 scroll_anchor: ScrollAnchor,
796 ongoing_scroll: OngoingScroll,
797 current_line_highlight: CurrentLineHighlight,
798 gutter_hovered: bool,
799}
800
801const GIT_BLAME_MAX_AUTHOR_CHARS_DISPLAYED: usize = 20;
802
803#[derive(Default, Debug, Clone, Copy)]
804pub struct GutterDimensions {
805 pub left_padding: Pixels,
806 pub right_padding: Pixels,
807 pub width: Pixels,
808 pub margin: Pixels,
809 pub git_blame_entries_width: Option<Pixels>,
810}
811
812impl GutterDimensions {
813 /// The full width of the space taken up by the gutter.
814 pub fn full_width(&self) -> Pixels {
815 self.margin + self.width
816 }
817
818 /// The width of the space reserved for the fold indicators,
819 /// use alongside 'justify_end' and `gutter_width` to
820 /// right align content with the line numbers
821 pub fn fold_area_width(&self) -> Pixels {
822 self.margin + self.right_padding
823 }
824}
825
826#[derive(Debug)]
827pub struct RemoteSelection {
828 pub replica_id: ReplicaId,
829 pub selection: Selection<Anchor>,
830 pub cursor_shape: CursorShape,
831 pub peer_id: PeerId,
832 pub line_mode: bool,
833 pub participant_index: Option<ParticipantIndex>,
834 pub user_name: Option<SharedString>,
835}
836
837#[derive(Clone, Debug)]
838struct SelectionHistoryEntry {
839 selections: Arc<[Selection<Anchor>]>,
840 select_next_state: Option<SelectNextState>,
841 select_prev_state: Option<SelectNextState>,
842 add_selections_state: Option<AddSelectionsState>,
843}
844
845enum SelectionHistoryMode {
846 Normal,
847 Undoing,
848 Redoing,
849}
850
851#[derive(Clone, PartialEq, Eq, Hash)]
852struct HoveredCursor {
853 replica_id: u16,
854 selection_id: usize,
855}
856
857impl Default for SelectionHistoryMode {
858 fn default() -> Self {
859 Self::Normal
860 }
861}
862
863#[derive(Default)]
864struct SelectionHistory {
865 #[allow(clippy::type_complexity)]
866 selections_by_transaction:
867 HashMap<TransactionId, (Arc<[Selection<Anchor>]>, Option<Arc<[Selection<Anchor>]>>)>,
868 mode: SelectionHistoryMode,
869 undo_stack: VecDeque<SelectionHistoryEntry>,
870 redo_stack: VecDeque<SelectionHistoryEntry>,
871}
872
873impl SelectionHistory {
874 fn insert_transaction(
875 &mut self,
876 transaction_id: TransactionId,
877 selections: Arc<[Selection<Anchor>]>,
878 ) {
879 self.selections_by_transaction
880 .insert(transaction_id, (selections, None));
881 }
882
883 #[allow(clippy::type_complexity)]
884 fn transaction(
885 &self,
886 transaction_id: TransactionId,
887 ) -> Option<&(Arc<[Selection<Anchor>]>, Option<Arc<[Selection<Anchor>]>>)> {
888 self.selections_by_transaction.get(&transaction_id)
889 }
890
891 #[allow(clippy::type_complexity)]
892 fn transaction_mut(
893 &mut self,
894 transaction_id: TransactionId,
895 ) -> Option<&mut (Arc<[Selection<Anchor>]>, Option<Arc<[Selection<Anchor>]>>)> {
896 self.selections_by_transaction.get_mut(&transaction_id)
897 }
898
899 fn push(&mut self, entry: SelectionHistoryEntry) {
900 if !entry.selections.is_empty() {
901 match self.mode {
902 SelectionHistoryMode::Normal => {
903 self.push_undo(entry);
904 self.redo_stack.clear();
905 }
906 SelectionHistoryMode::Undoing => self.push_redo(entry),
907 SelectionHistoryMode::Redoing => self.push_undo(entry),
908 }
909 }
910 }
911
912 fn push_undo(&mut self, entry: SelectionHistoryEntry) {
913 if self
914 .undo_stack
915 .back()
916 .map_or(true, |e| e.selections != entry.selections)
917 {
918 self.undo_stack.push_back(entry);
919 if self.undo_stack.len() > MAX_SELECTION_HISTORY_LEN {
920 self.undo_stack.pop_front();
921 }
922 }
923 }
924
925 fn push_redo(&mut self, entry: SelectionHistoryEntry) {
926 if self
927 .redo_stack
928 .back()
929 .map_or(true, |e| e.selections != entry.selections)
930 {
931 self.redo_stack.push_back(entry);
932 if self.redo_stack.len() > MAX_SELECTION_HISTORY_LEN {
933 self.redo_stack.pop_front();
934 }
935 }
936 }
937}
938
939struct RowHighlight {
940 index: usize,
941 range: Range<Anchor>,
942 color: Hsla,
943 should_autoscroll: bool,
944}
945
946#[derive(Clone, Debug)]
947struct AddSelectionsState {
948 above: bool,
949 stack: Vec<usize>,
950}
951
952#[derive(Clone)]
953struct SelectNextState {
954 query: AhoCorasick,
955 wordwise: bool,
956 done: bool,
957}
958
959impl std::fmt::Debug for SelectNextState {
960 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
961 f.debug_struct(std::any::type_name::<Self>())
962 .field("wordwise", &self.wordwise)
963 .field("done", &self.done)
964 .finish()
965 }
966}
967
968#[derive(Debug)]
969struct AutocloseRegion {
970 selection_id: usize,
971 range: Range<Anchor>,
972 pair: BracketPair,
973}
974
975#[derive(Debug)]
976struct SnippetState {
977 ranges: Vec<Vec<Range<Anchor>>>,
978 active_index: usize,
979 choices: Vec<Option<Vec<String>>>,
980}
981
982#[doc(hidden)]
983pub struct RenameState {
984 pub range: Range<Anchor>,
985 pub old_name: Arc<str>,
986 pub editor: Entity<Editor>,
987 block_id: CustomBlockId,
988}
989
990struct InvalidationStack<T>(Vec<T>);
991
992struct RegisteredInlineCompletionProvider {
993 provider: Arc<dyn InlineCompletionProviderHandle>,
994 _subscription: Subscription,
995}
996
997#[derive(Debug, PartialEq, Eq)]
998struct ActiveDiagnosticGroup {
999 primary_range: Range<Anchor>,
1000 primary_message: String,
1001 group_id: usize,
1002 blocks: HashMap<CustomBlockId, Diagnostic>,
1003 is_valid: bool,
1004}
1005
1006#[derive(Serialize, Deserialize, Clone, Debug)]
1007pub struct ClipboardSelection {
1008 /// The number of bytes in this selection.
1009 pub len: usize,
1010 /// Whether this was a full-line selection.
1011 pub is_entire_line: bool,
1012 /// The indentation of the first line when this content was originally copied.
1013 pub first_line_indent: u32,
1014}
1015
1016#[derive(Debug)]
1017pub(crate) struct NavigationData {
1018 cursor_anchor: Anchor,
1019 cursor_position: Point,
1020 scroll_anchor: ScrollAnchor,
1021 scroll_top_row: u32,
1022}
1023
1024#[derive(Debug, Clone, Copy, PartialEq, Eq)]
1025pub enum GotoDefinitionKind {
1026 Symbol,
1027 Declaration,
1028 Type,
1029 Implementation,
1030}
1031
1032#[derive(Debug, Clone)]
1033enum InlayHintRefreshReason {
1034 ModifiersChanged(bool),
1035 Toggle(bool),
1036 SettingsChange(InlayHintSettings),
1037 NewLinesShown,
1038 BufferEdited(HashSet<Arc<Language>>),
1039 RefreshRequested,
1040 ExcerptsRemoved(Vec<ExcerptId>),
1041}
1042
1043impl InlayHintRefreshReason {
1044 fn description(&self) -> &'static str {
1045 match self {
1046 Self::ModifiersChanged(_) => "modifiers changed",
1047 Self::Toggle(_) => "toggle",
1048 Self::SettingsChange(_) => "settings change",
1049 Self::NewLinesShown => "new lines shown",
1050 Self::BufferEdited(_) => "buffer edited",
1051 Self::RefreshRequested => "refresh requested",
1052 Self::ExcerptsRemoved(_) => "excerpts removed",
1053 }
1054 }
1055}
1056
1057pub enum FormatTarget {
1058 Buffers,
1059 Ranges(Vec<Range<MultiBufferPoint>>),
1060}
1061
1062pub(crate) struct FocusedBlock {
1063 id: BlockId,
1064 focus_handle: WeakFocusHandle,
1065}
1066
1067#[derive(Clone)]
1068enum JumpData {
1069 MultiBufferRow {
1070 row: MultiBufferRow,
1071 line_offset_from_top: u32,
1072 },
1073 MultiBufferPoint {
1074 excerpt_id: ExcerptId,
1075 position: Point,
1076 anchor: text::Anchor,
1077 line_offset_from_top: u32,
1078 },
1079}
1080
1081pub enum MultibufferSelectionMode {
1082 First,
1083 All,
1084}
1085
1086impl Editor {
1087 pub fn single_line(window: &mut Window, cx: &mut Context<Self>) -> Self {
1088 let buffer = cx.new(|cx| Buffer::local("", cx));
1089 let buffer = cx.new(|cx| MultiBuffer::singleton(buffer, cx));
1090 Self::new(
1091 EditorMode::SingleLine { auto_width: false },
1092 buffer,
1093 None,
1094 window,
1095 cx,
1096 )
1097 }
1098
1099 pub fn multi_line(window: &mut Window, cx: &mut Context<Self>) -> Self {
1100 let buffer = cx.new(|cx| Buffer::local("", cx));
1101 let buffer = cx.new(|cx| MultiBuffer::singleton(buffer, cx));
1102 Self::new(EditorMode::Full, buffer, None, window, cx)
1103 }
1104
1105 pub fn auto_width(window: &mut Window, cx: &mut Context<Self>) -> Self {
1106 let buffer = cx.new(|cx| Buffer::local("", cx));
1107 let buffer = cx.new(|cx| MultiBuffer::singleton(buffer, cx));
1108 Self::new(
1109 EditorMode::SingleLine { auto_width: true },
1110 buffer,
1111 None,
1112 window,
1113 cx,
1114 )
1115 }
1116
1117 pub fn auto_height(max_lines: usize, window: &mut Window, cx: &mut Context<Self>) -> Self {
1118 let buffer = cx.new(|cx| Buffer::local("", cx));
1119 let buffer = cx.new(|cx| MultiBuffer::singleton(buffer, cx));
1120 Self::new(
1121 EditorMode::AutoHeight { max_lines },
1122 buffer,
1123 None,
1124 window,
1125 cx,
1126 )
1127 }
1128
1129 pub fn for_buffer(
1130 buffer: Entity<Buffer>,
1131 project: Option<Entity<Project>>,
1132 window: &mut Window,
1133 cx: &mut Context<Self>,
1134 ) -> Self {
1135 let buffer = cx.new(|cx| MultiBuffer::singleton(buffer, cx));
1136 Self::new(EditorMode::Full, buffer, project, window, cx)
1137 }
1138
1139 pub fn for_multibuffer(
1140 buffer: Entity<MultiBuffer>,
1141 project: Option<Entity<Project>>,
1142 window: &mut Window,
1143 cx: &mut Context<Self>,
1144 ) -> Self {
1145 Self::new(EditorMode::Full, buffer, project, window, cx)
1146 }
1147
1148 pub fn clone(&self, window: &mut Window, cx: &mut Context<Self>) -> Self {
1149 let mut clone = Self::new(
1150 self.mode,
1151 self.buffer.clone(),
1152 self.project.clone(),
1153 window,
1154 cx,
1155 );
1156 self.display_map.update(cx, |display_map, cx| {
1157 let snapshot = display_map.snapshot(cx);
1158 clone.display_map.update(cx, |display_map, cx| {
1159 display_map.set_state(&snapshot, cx);
1160 });
1161 });
1162 clone.selections.clone_state(&self.selections);
1163 clone.scroll_manager.clone_state(&self.scroll_manager);
1164 clone.searchable = self.searchable;
1165 clone
1166 }
1167
1168 pub fn new(
1169 mode: EditorMode,
1170 buffer: Entity<MultiBuffer>,
1171 project: Option<Entity<Project>>,
1172 window: &mut Window,
1173 cx: &mut Context<Self>,
1174 ) -> Self {
1175 let style = window.text_style();
1176 let font_size = style.font_size.to_pixels(window.rem_size());
1177 let editor = cx.entity().downgrade();
1178 let fold_placeholder = FoldPlaceholder {
1179 constrain_width: true,
1180 render: Arc::new(move |fold_id, fold_range, cx| {
1181 let editor = editor.clone();
1182 div()
1183 .id(fold_id)
1184 .bg(cx.theme().colors().ghost_element_background)
1185 .hover(|style| style.bg(cx.theme().colors().ghost_element_hover))
1186 .active(|style| style.bg(cx.theme().colors().ghost_element_active))
1187 .rounded_xs()
1188 .size_full()
1189 .cursor_pointer()
1190 .child("⋯")
1191 .on_mouse_down(MouseButton::Left, |_, _, cx| cx.stop_propagation())
1192 .on_click(move |_, _window, cx| {
1193 editor
1194 .update(cx, |editor, cx| {
1195 editor.unfold_ranges(
1196 &[fold_range.start..fold_range.end],
1197 true,
1198 false,
1199 cx,
1200 );
1201 cx.stop_propagation();
1202 })
1203 .ok();
1204 })
1205 .into_any()
1206 }),
1207 merge_adjacent: true,
1208 ..Default::default()
1209 };
1210 let display_map = cx.new(|cx| {
1211 DisplayMap::new(
1212 buffer.clone(),
1213 style.font(),
1214 font_size,
1215 None,
1216 FILE_HEADER_HEIGHT,
1217 MULTI_BUFFER_EXCERPT_HEADER_HEIGHT,
1218 fold_placeholder,
1219 cx,
1220 )
1221 });
1222
1223 let selections = SelectionsCollection::new(display_map.clone(), buffer.clone());
1224
1225 let blink_manager = cx.new(|cx| BlinkManager::new(CURSOR_BLINK_INTERVAL, cx));
1226
1227 let soft_wrap_mode_override = matches!(mode, EditorMode::SingleLine { .. })
1228 .then(|| language_settings::SoftWrap::None);
1229
1230 let mut project_subscriptions = Vec::new();
1231 if mode == EditorMode::Full {
1232 if let Some(project) = project.as_ref() {
1233 project_subscriptions.push(cx.subscribe_in(
1234 project,
1235 window,
1236 |editor, _, event, window, cx| {
1237 if let project::Event::RefreshInlayHints = event {
1238 editor
1239 .refresh_inlay_hints(InlayHintRefreshReason::RefreshRequested, cx);
1240 } else if let project::Event::SnippetEdit(id, snippet_edits) = event {
1241 if let Some(buffer) = editor.buffer.read(cx).buffer(*id) {
1242 let focus_handle = editor.focus_handle(cx);
1243 if focus_handle.is_focused(window) {
1244 let snapshot = buffer.read(cx).snapshot();
1245 for (range, snippet) in snippet_edits {
1246 let editor_range =
1247 language::range_from_lsp(*range).to_offset(&snapshot);
1248 editor
1249 .insert_snippet(
1250 &[editor_range],
1251 snippet.clone(),
1252 window,
1253 cx,
1254 )
1255 .ok();
1256 }
1257 }
1258 }
1259 }
1260 },
1261 ));
1262 if let Some(task_inventory) = project
1263 .read(cx)
1264 .task_store()
1265 .read(cx)
1266 .task_inventory()
1267 .cloned()
1268 {
1269 project_subscriptions.push(cx.observe_in(
1270 &task_inventory,
1271 window,
1272 |editor, _, window, cx| {
1273 editor.tasks_update_task = Some(editor.refresh_runnables(window, cx));
1274 },
1275 ));
1276 }
1277 }
1278 }
1279
1280 let buffer_snapshot = buffer.read(cx).snapshot(cx);
1281
1282 let inlay_hint_settings =
1283 inlay_hint_settings(selections.newest_anchor().head(), &buffer_snapshot, cx);
1284 let focus_handle = cx.focus_handle();
1285 cx.on_focus(&focus_handle, window, Self::handle_focus)
1286 .detach();
1287 cx.on_focus_in(&focus_handle, window, Self::handle_focus_in)
1288 .detach();
1289 cx.on_focus_out(&focus_handle, window, Self::handle_focus_out)
1290 .detach();
1291 cx.on_blur(&focus_handle, window, Self::handle_blur)
1292 .detach();
1293
1294 let show_indent_guides = if matches!(mode, EditorMode::SingleLine { .. }) {
1295 Some(false)
1296 } else {
1297 None
1298 };
1299
1300 let mut code_action_providers = Vec::new();
1301 let mut load_uncommitted_diff = None;
1302 if let Some(project) = project.clone() {
1303 load_uncommitted_diff = Some(
1304 get_uncommitted_diff_for_buffer(
1305 &project,
1306 buffer.read(cx).all_buffers(),
1307 buffer.clone(),
1308 cx,
1309 )
1310 .shared(),
1311 );
1312 code_action_providers.push(Rc::new(project) as Rc<_>);
1313 }
1314
1315 let mut this = Self {
1316 focus_handle,
1317 show_cursor_when_unfocused: false,
1318 last_focused_descendant: None,
1319 buffer: buffer.clone(),
1320 display_map: display_map.clone(),
1321 selections,
1322 scroll_manager: ScrollManager::new(cx),
1323 columnar_selection_tail: None,
1324 add_selections_state: None,
1325 select_next_state: None,
1326 select_prev_state: None,
1327 selection_history: Default::default(),
1328 autoclose_regions: Default::default(),
1329 snippet_stack: Default::default(),
1330 select_larger_syntax_node_stack: Vec::new(),
1331 ime_transaction: Default::default(),
1332 active_diagnostics: None,
1333 show_inline_diagnostics: ProjectSettings::get_global(cx).diagnostics.inline.enabled,
1334 inline_diagnostics_update: Task::ready(()),
1335 inline_diagnostics: Vec::new(),
1336 soft_wrap_mode_override,
1337 hard_wrap: None,
1338 completion_provider: project.clone().map(|project| Box::new(project) as _),
1339 semantics_provider: project.clone().map(|project| Rc::new(project) as _),
1340 collaboration_hub: project.clone().map(|project| Box::new(project) as _),
1341 project,
1342 blink_manager: blink_manager.clone(),
1343 show_local_selections: true,
1344 show_scrollbars: true,
1345 mode,
1346 show_breadcrumbs: EditorSettings::get_global(cx).toolbar.breadcrumbs,
1347 show_gutter: mode == EditorMode::Full,
1348 show_line_numbers: None,
1349 use_relative_line_numbers: None,
1350 show_git_diff_gutter: None,
1351 show_code_actions: None,
1352 show_runnables: None,
1353 show_wrap_guides: None,
1354 show_indent_guides,
1355 placeholder_text: None,
1356 highlight_order: 0,
1357 highlighted_rows: HashMap::default(),
1358 background_highlights: Default::default(),
1359 gutter_highlights: TreeMap::default(),
1360 scrollbar_marker_state: ScrollbarMarkerState::default(),
1361 active_indent_guides_state: ActiveIndentGuidesState::default(),
1362 nav_history: None,
1363 context_menu: RefCell::new(None),
1364 mouse_context_menu: None,
1365 completion_tasks: Default::default(),
1366 signature_help_state: SignatureHelpState::default(),
1367 auto_signature_help: None,
1368 find_all_references_task_sources: Vec::new(),
1369 next_completion_id: 0,
1370 next_inlay_id: 0,
1371 code_action_providers,
1372 available_code_actions: Default::default(),
1373 code_actions_task: Default::default(),
1374 selection_highlight_task: Default::default(),
1375 document_highlights_task: Default::default(),
1376 linked_editing_range_task: Default::default(),
1377 pending_rename: Default::default(),
1378 searchable: true,
1379 cursor_shape: EditorSettings::get_global(cx)
1380 .cursor_shape
1381 .unwrap_or_default(),
1382 current_line_highlight: None,
1383 autoindent_mode: Some(AutoindentMode::EachLine),
1384 collapse_matches: false,
1385 workspace: None,
1386 input_enabled: true,
1387 use_modal_editing: mode == EditorMode::Full,
1388 read_only: false,
1389 use_autoclose: true,
1390 use_auto_surround: true,
1391 auto_replace_emoji_shortcode: false,
1392 jsx_tag_auto_close_enabled_in_any_buffer: false,
1393 leader_peer_id: None,
1394 remote_id: None,
1395 hover_state: Default::default(),
1396 pending_mouse_down: None,
1397 hovered_link_state: Default::default(),
1398 edit_prediction_provider: None,
1399 active_inline_completion: None,
1400 stale_inline_completion_in_menu: None,
1401 edit_prediction_preview: EditPredictionPreview::Inactive {
1402 released_too_fast: false,
1403 },
1404 inline_diagnostics_enabled: mode == EditorMode::Full,
1405 inlay_hint_cache: InlayHintCache::new(inlay_hint_settings),
1406
1407 gutter_hovered: false,
1408 pixel_position_of_newest_cursor: None,
1409 last_bounds: None,
1410 last_position_map: None,
1411 expect_bounds_change: None,
1412 gutter_dimensions: GutterDimensions::default(),
1413 style: None,
1414 show_cursor_names: false,
1415 hovered_cursors: Default::default(),
1416 next_editor_action_id: EditorActionId::default(),
1417 editor_actions: Rc::default(),
1418 inline_completions_hidden_for_vim_mode: false,
1419 show_inline_completions_override: None,
1420 menu_inline_completions_policy: MenuInlineCompletionsPolicy::ByProvider,
1421 edit_prediction_settings: EditPredictionSettings::Disabled,
1422 edit_prediction_indent_conflict: false,
1423 edit_prediction_requires_modifier_in_indent_conflict: true,
1424 custom_context_menu: None,
1425 show_git_blame_gutter: false,
1426 show_git_blame_inline: false,
1427 show_selection_menu: None,
1428 show_git_blame_inline_delay_task: None,
1429 git_blame_inline_tooltip: None,
1430 git_blame_inline_enabled: ProjectSettings::get_global(cx).git.inline_blame_enabled(),
1431 serialize_dirty_buffers: ProjectSettings::get_global(cx)
1432 .session
1433 .restore_unsaved_buffers,
1434 blame: None,
1435 blame_subscription: None,
1436 tasks: Default::default(),
1437 _subscriptions: vec![
1438 cx.observe(&buffer, Self::on_buffer_changed),
1439 cx.subscribe_in(&buffer, window, Self::on_buffer_event),
1440 cx.observe_in(&display_map, window, Self::on_display_map_changed),
1441 cx.observe(&blink_manager, |_, _, cx| cx.notify()),
1442 cx.observe_global_in::<SettingsStore>(window, Self::settings_changed),
1443 observe_buffer_font_size_adjustment(cx, |_, cx| cx.notify()),
1444 cx.observe_window_activation(window, |editor, window, cx| {
1445 let active = window.is_window_active();
1446 editor.blink_manager.update(cx, |blink_manager, cx| {
1447 if active {
1448 blink_manager.enable(cx);
1449 } else {
1450 blink_manager.disable(cx);
1451 }
1452 });
1453 }),
1454 ],
1455 tasks_update_task: None,
1456 linked_edit_ranges: Default::default(),
1457 in_project_search: false,
1458 previous_search_ranges: None,
1459 breadcrumb_header: None,
1460 focused_block: None,
1461 next_scroll_position: NextScrollCursorCenterTopBottom::default(),
1462 addons: HashMap::default(),
1463 registered_buffers: HashMap::default(),
1464 _scroll_cursor_center_top_bottom_task: Task::ready(()),
1465 selection_mark_mode: false,
1466 toggle_fold_multiple_buffers: Task::ready(()),
1467 serialize_selections: Task::ready(()),
1468 text_style_refinement: None,
1469 load_diff_task: load_uncommitted_diff,
1470 };
1471 this.tasks_update_task = Some(this.refresh_runnables(window, cx));
1472 this._subscriptions.extend(project_subscriptions);
1473
1474 this.end_selection(window, cx);
1475 this.scroll_manager.show_scrollbar(window, cx);
1476 jsx_tag_auto_close::refresh_enabled_in_any_buffer(&mut this, &buffer, cx);
1477
1478 if mode == EditorMode::Full {
1479 let should_auto_hide_scrollbars = cx.should_auto_hide_scrollbars();
1480 cx.set_global(ScrollbarAutoHide(should_auto_hide_scrollbars));
1481
1482 if this.git_blame_inline_enabled {
1483 this.git_blame_inline_enabled = true;
1484 this.start_git_blame_inline(false, window, cx);
1485 }
1486
1487 if let Some(buffer) = buffer.read(cx).as_singleton() {
1488 if let Some(project) = this.project.as_ref() {
1489 let handle = project.update(cx, |project, cx| {
1490 project.register_buffer_with_language_servers(&buffer, cx)
1491 });
1492 this.registered_buffers
1493 .insert(buffer.read(cx).remote_id(), handle);
1494 }
1495 }
1496 }
1497
1498 this.report_editor_event("Editor Opened", None, cx);
1499 this
1500 }
1501
1502 pub fn mouse_menu_is_focused(&self, window: &Window, cx: &App) -> bool {
1503 self.mouse_context_menu
1504 .as_ref()
1505 .is_some_and(|menu| menu.context_menu.focus_handle(cx).is_focused(window))
1506 }
1507
1508 fn key_context(&self, window: &Window, cx: &App) -> KeyContext {
1509 self.key_context_internal(self.has_active_inline_completion(), window, cx)
1510 }
1511
1512 fn key_context_internal(
1513 &self,
1514 has_active_edit_prediction: bool,
1515 window: &Window,
1516 cx: &App,
1517 ) -> KeyContext {
1518 let mut key_context = KeyContext::new_with_defaults();
1519 key_context.add("Editor");
1520 let mode = match self.mode {
1521 EditorMode::SingleLine { .. } => "single_line",
1522 EditorMode::AutoHeight { .. } => "auto_height",
1523 EditorMode::Full => "full",
1524 };
1525
1526 if EditorSettings::jupyter_enabled(cx) {
1527 key_context.add("jupyter");
1528 }
1529
1530 key_context.set("mode", mode);
1531 if self.pending_rename.is_some() {
1532 key_context.add("renaming");
1533 }
1534
1535 match self.context_menu.borrow().as_ref() {
1536 Some(CodeContextMenu::Completions(_)) => {
1537 key_context.add("menu");
1538 key_context.add("showing_completions");
1539 }
1540 Some(CodeContextMenu::CodeActions(_)) => {
1541 key_context.add("menu");
1542 key_context.add("showing_code_actions")
1543 }
1544 None => {}
1545 }
1546
1547 // Disable vim contexts when a sub-editor (e.g. rename/inline assistant) is focused.
1548 if !self.focus_handle(cx).contains_focused(window, cx)
1549 || (self.is_focused(window) || self.mouse_menu_is_focused(window, cx))
1550 {
1551 for addon in self.addons.values() {
1552 addon.extend_key_context(&mut key_context, cx)
1553 }
1554 }
1555
1556 if let Some(singleton_buffer) = self.buffer.read(cx).as_singleton() {
1557 if let Some(extension) = singleton_buffer
1558 .read(cx)
1559 .file()
1560 .and_then(|file| file.path().extension()?.to_str())
1561 {
1562 key_context.set("extension", extension.to_string());
1563 }
1564 } else {
1565 key_context.add("multibuffer");
1566 }
1567
1568 if has_active_edit_prediction {
1569 if self.edit_prediction_in_conflict() {
1570 key_context.add(EDIT_PREDICTION_CONFLICT_KEY_CONTEXT);
1571 } else {
1572 key_context.add(EDIT_PREDICTION_KEY_CONTEXT);
1573 key_context.add("copilot_suggestion");
1574 }
1575 }
1576
1577 if self.selection_mark_mode {
1578 key_context.add("selection_mode");
1579 }
1580
1581 key_context
1582 }
1583
1584 pub fn edit_prediction_in_conflict(&self) -> bool {
1585 if !self.show_edit_predictions_in_menu() {
1586 return false;
1587 }
1588
1589 let showing_completions = self
1590 .context_menu
1591 .borrow()
1592 .as_ref()
1593 .map_or(false, |context| {
1594 matches!(context, CodeContextMenu::Completions(_))
1595 });
1596
1597 showing_completions
1598 || self.edit_prediction_requires_modifier()
1599 // Require modifier key when the cursor is on leading whitespace, to allow `tab`
1600 // bindings to insert tab characters.
1601 || (self.edit_prediction_requires_modifier_in_indent_conflict && self.edit_prediction_indent_conflict)
1602 }
1603
1604 pub fn accept_edit_prediction_keybind(
1605 &self,
1606 window: &Window,
1607 cx: &App,
1608 ) -> AcceptEditPredictionBinding {
1609 let key_context = self.key_context_internal(true, window, cx);
1610 let in_conflict = self.edit_prediction_in_conflict();
1611
1612 AcceptEditPredictionBinding(
1613 window
1614 .bindings_for_action_in_context(&AcceptEditPrediction, key_context)
1615 .into_iter()
1616 .filter(|binding| {
1617 !in_conflict
1618 || binding
1619 .keystrokes()
1620 .first()
1621 .map_or(false, |keystroke| keystroke.modifiers.modified())
1622 })
1623 .rev()
1624 .min_by_key(|binding| {
1625 binding
1626 .keystrokes()
1627 .first()
1628 .map_or(u8::MAX, |k| k.modifiers.number_of_modifiers())
1629 }),
1630 )
1631 }
1632
1633 pub fn new_file(
1634 workspace: &mut Workspace,
1635 _: &workspace::NewFile,
1636 window: &mut Window,
1637 cx: &mut Context<Workspace>,
1638 ) {
1639 Self::new_in_workspace(workspace, window, cx).detach_and_prompt_err(
1640 "Failed to create buffer",
1641 window,
1642 cx,
1643 |e, _, _| match e.error_code() {
1644 ErrorCode::RemoteUpgradeRequired => Some(format!(
1645 "The remote instance of Zed does not support this yet. It must be upgraded to {}",
1646 e.error_tag("required").unwrap_or("the latest version")
1647 )),
1648 _ => None,
1649 },
1650 );
1651 }
1652
1653 pub fn new_in_workspace(
1654 workspace: &mut Workspace,
1655 window: &mut Window,
1656 cx: &mut Context<Workspace>,
1657 ) -> Task<Result<Entity<Editor>>> {
1658 let project = workspace.project().clone();
1659 let create = project.update(cx, |project, cx| project.create_buffer(cx));
1660
1661 cx.spawn_in(window, |workspace, mut cx| async move {
1662 let buffer = create.await?;
1663 workspace.update_in(&mut cx, |workspace, window, cx| {
1664 let editor =
1665 cx.new(|cx| Editor::for_buffer(buffer, Some(project.clone()), window, cx));
1666 workspace.add_item_to_active_pane(Box::new(editor.clone()), None, true, window, cx);
1667 editor
1668 })
1669 })
1670 }
1671
1672 fn new_file_vertical(
1673 workspace: &mut Workspace,
1674 _: &workspace::NewFileSplitVertical,
1675 window: &mut Window,
1676 cx: &mut Context<Workspace>,
1677 ) {
1678 Self::new_file_in_direction(workspace, SplitDirection::vertical(cx), window, cx)
1679 }
1680
1681 fn new_file_horizontal(
1682 workspace: &mut Workspace,
1683 _: &workspace::NewFileSplitHorizontal,
1684 window: &mut Window,
1685 cx: &mut Context<Workspace>,
1686 ) {
1687 Self::new_file_in_direction(workspace, SplitDirection::horizontal(cx), window, cx)
1688 }
1689
1690 fn new_file_in_direction(
1691 workspace: &mut Workspace,
1692 direction: SplitDirection,
1693 window: &mut Window,
1694 cx: &mut Context<Workspace>,
1695 ) {
1696 let project = workspace.project().clone();
1697 let create = project.update(cx, |project, cx| project.create_buffer(cx));
1698
1699 cx.spawn_in(window, |workspace, mut cx| async move {
1700 let buffer = create.await?;
1701 workspace.update_in(&mut cx, move |workspace, window, cx| {
1702 workspace.split_item(
1703 direction,
1704 Box::new(
1705 cx.new(|cx| Editor::for_buffer(buffer, Some(project.clone()), window, cx)),
1706 ),
1707 window,
1708 cx,
1709 )
1710 })?;
1711 anyhow::Ok(())
1712 })
1713 .detach_and_prompt_err("Failed to create buffer", window, cx, |e, _, _| {
1714 match e.error_code() {
1715 ErrorCode::RemoteUpgradeRequired => Some(format!(
1716 "The remote instance of Zed does not support this yet. It must be upgraded to {}",
1717 e.error_tag("required").unwrap_or("the latest version")
1718 )),
1719 _ => None,
1720 }
1721 });
1722 }
1723
1724 pub fn leader_peer_id(&self) -> Option<PeerId> {
1725 self.leader_peer_id
1726 }
1727
1728 pub fn buffer(&self) -> &Entity<MultiBuffer> {
1729 &self.buffer
1730 }
1731
1732 pub fn workspace(&self) -> Option<Entity<Workspace>> {
1733 self.workspace.as_ref()?.0.upgrade()
1734 }
1735
1736 pub fn title<'a>(&self, cx: &'a App) -> Cow<'a, str> {
1737 self.buffer().read(cx).title(cx)
1738 }
1739
1740 pub fn snapshot(&self, window: &mut Window, cx: &mut App) -> EditorSnapshot {
1741 let git_blame_gutter_max_author_length = self
1742 .render_git_blame_gutter(cx)
1743 .then(|| {
1744 if let Some(blame) = self.blame.as_ref() {
1745 let max_author_length =
1746 blame.update(cx, |blame, cx| blame.max_author_length(cx));
1747 Some(max_author_length)
1748 } else {
1749 None
1750 }
1751 })
1752 .flatten();
1753
1754 EditorSnapshot {
1755 mode: self.mode,
1756 show_gutter: self.show_gutter,
1757 show_line_numbers: self.show_line_numbers,
1758 show_git_diff_gutter: self.show_git_diff_gutter,
1759 show_code_actions: self.show_code_actions,
1760 show_runnables: self.show_runnables,
1761 git_blame_gutter_max_author_length,
1762 display_snapshot: self.display_map.update(cx, |map, cx| map.snapshot(cx)),
1763 scroll_anchor: self.scroll_manager.anchor(),
1764 ongoing_scroll: self.scroll_manager.ongoing_scroll(),
1765 placeholder_text: self.placeholder_text.clone(),
1766 is_focused: self.focus_handle.is_focused(window),
1767 current_line_highlight: self
1768 .current_line_highlight
1769 .unwrap_or_else(|| EditorSettings::get_global(cx).current_line_highlight),
1770 gutter_hovered: self.gutter_hovered,
1771 }
1772 }
1773
1774 pub fn language_at<T: ToOffset>(&self, point: T, cx: &App) -> Option<Arc<Language>> {
1775 self.buffer.read(cx).language_at(point, cx)
1776 }
1777
1778 pub fn file_at<T: ToOffset>(&self, point: T, cx: &App) -> Option<Arc<dyn language::File>> {
1779 self.buffer.read(cx).read(cx).file_at(point).cloned()
1780 }
1781
1782 pub fn active_excerpt(
1783 &self,
1784 cx: &App,
1785 ) -> Option<(ExcerptId, Entity<Buffer>, Range<text::Anchor>)> {
1786 self.buffer
1787 .read(cx)
1788 .excerpt_containing(self.selections.newest_anchor().head(), cx)
1789 }
1790
1791 pub fn mode(&self) -> EditorMode {
1792 self.mode
1793 }
1794
1795 pub fn collaboration_hub(&self) -> Option<&dyn CollaborationHub> {
1796 self.collaboration_hub.as_deref()
1797 }
1798
1799 pub fn set_collaboration_hub(&mut self, hub: Box<dyn CollaborationHub>) {
1800 self.collaboration_hub = Some(hub);
1801 }
1802
1803 pub fn set_in_project_search(&mut self, in_project_search: bool) {
1804 self.in_project_search = in_project_search;
1805 }
1806
1807 pub fn set_custom_context_menu(
1808 &mut self,
1809 f: impl 'static
1810 + Fn(
1811 &mut Self,
1812 DisplayPoint,
1813 &mut Window,
1814 &mut Context<Self>,
1815 ) -> Option<Entity<ui::ContextMenu>>,
1816 ) {
1817 self.custom_context_menu = Some(Box::new(f))
1818 }
1819
1820 pub fn set_completion_provider(&mut self, provider: Option<Box<dyn CompletionProvider>>) {
1821 self.completion_provider = provider;
1822 }
1823
1824 pub fn semantics_provider(&self) -> Option<Rc<dyn SemanticsProvider>> {
1825 self.semantics_provider.clone()
1826 }
1827
1828 pub fn set_semantics_provider(&mut self, provider: Option<Rc<dyn SemanticsProvider>>) {
1829 self.semantics_provider = provider;
1830 }
1831
1832 pub fn set_edit_prediction_provider<T>(
1833 &mut self,
1834 provider: Option<Entity<T>>,
1835 window: &mut Window,
1836 cx: &mut Context<Self>,
1837 ) where
1838 T: EditPredictionProvider,
1839 {
1840 self.edit_prediction_provider =
1841 provider.map(|provider| RegisteredInlineCompletionProvider {
1842 _subscription: cx.observe_in(&provider, window, |this, _, window, cx| {
1843 if this.focus_handle.is_focused(window) {
1844 this.update_visible_inline_completion(window, cx);
1845 }
1846 }),
1847 provider: Arc::new(provider),
1848 });
1849 self.update_edit_prediction_settings(cx);
1850 self.refresh_inline_completion(false, false, window, cx);
1851 }
1852
1853 pub fn placeholder_text(&self) -> Option<&str> {
1854 self.placeholder_text.as_deref()
1855 }
1856
1857 pub fn set_placeholder_text(
1858 &mut self,
1859 placeholder_text: impl Into<Arc<str>>,
1860 cx: &mut Context<Self>,
1861 ) {
1862 let placeholder_text = Some(placeholder_text.into());
1863 if self.placeholder_text != placeholder_text {
1864 self.placeholder_text = placeholder_text;
1865 cx.notify();
1866 }
1867 }
1868
1869 pub fn set_cursor_shape(&mut self, cursor_shape: CursorShape, cx: &mut Context<Self>) {
1870 self.cursor_shape = cursor_shape;
1871
1872 // Disrupt blink for immediate user feedback that the cursor shape has changed
1873 self.blink_manager.update(cx, BlinkManager::show_cursor);
1874
1875 cx.notify();
1876 }
1877
1878 pub fn set_current_line_highlight(
1879 &mut self,
1880 current_line_highlight: Option<CurrentLineHighlight>,
1881 ) {
1882 self.current_line_highlight = current_line_highlight;
1883 }
1884
1885 pub fn set_collapse_matches(&mut self, collapse_matches: bool) {
1886 self.collapse_matches = collapse_matches;
1887 }
1888
1889 fn register_buffers_with_language_servers(&mut self, cx: &mut Context<Self>) {
1890 let buffers = self.buffer.read(cx).all_buffers();
1891 let Some(project) = self.project.as_ref() else {
1892 return;
1893 };
1894 project.update(cx, |project, cx| {
1895 for buffer in buffers {
1896 self.registered_buffers
1897 .entry(buffer.read(cx).remote_id())
1898 .or_insert_with(|| project.register_buffer_with_language_servers(&buffer, cx));
1899 }
1900 })
1901 }
1902
1903 pub fn range_for_match<T: std::marker::Copy>(&self, range: &Range<T>) -> Range<T> {
1904 if self.collapse_matches {
1905 return range.start..range.start;
1906 }
1907 range.clone()
1908 }
1909
1910 pub fn set_clip_at_line_ends(&mut self, clip: bool, cx: &mut Context<Self>) {
1911 if self.display_map.read(cx).clip_at_line_ends != clip {
1912 self.display_map
1913 .update(cx, |map, _| map.clip_at_line_ends = clip);
1914 }
1915 }
1916
1917 pub fn set_input_enabled(&mut self, input_enabled: bool) {
1918 self.input_enabled = input_enabled;
1919 }
1920
1921 pub fn set_inline_completions_hidden_for_vim_mode(
1922 &mut self,
1923 hidden: bool,
1924 window: &mut Window,
1925 cx: &mut Context<Self>,
1926 ) {
1927 if hidden != self.inline_completions_hidden_for_vim_mode {
1928 self.inline_completions_hidden_for_vim_mode = hidden;
1929 if hidden {
1930 self.update_visible_inline_completion(window, cx);
1931 } else {
1932 self.refresh_inline_completion(true, false, window, cx);
1933 }
1934 }
1935 }
1936
1937 pub fn set_menu_inline_completions_policy(&mut self, value: MenuInlineCompletionsPolicy) {
1938 self.menu_inline_completions_policy = value;
1939 }
1940
1941 pub fn set_autoindent(&mut self, autoindent: bool) {
1942 if autoindent {
1943 self.autoindent_mode = Some(AutoindentMode::EachLine);
1944 } else {
1945 self.autoindent_mode = None;
1946 }
1947 }
1948
1949 pub fn read_only(&self, cx: &App) -> bool {
1950 self.read_only || self.buffer.read(cx).read_only()
1951 }
1952
1953 pub fn set_read_only(&mut self, read_only: bool) {
1954 self.read_only = read_only;
1955 }
1956
1957 pub fn set_use_autoclose(&mut self, autoclose: bool) {
1958 self.use_autoclose = autoclose;
1959 }
1960
1961 pub fn set_use_auto_surround(&mut self, auto_surround: bool) {
1962 self.use_auto_surround = auto_surround;
1963 }
1964
1965 pub fn set_auto_replace_emoji_shortcode(&mut self, auto_replace: bool) {
1966 self.auto_replace_emoji_shortcode = auto_replace;
1967 }
1968
1969 pub fn toggle_edit_predictions(
1970 &mut self,
1971 _: &ToggleEditPrediction,
1972 window: &mut Window,
1973 cx: &mut Context<Self>,
1974 ) {
1975 if self.show_inline_completions_override.is_some() {
1976 self.set_show_edit_predictions(None, window, cx);
1977 } else {
1978 let show_edit_predictions = !self.edit_predictions_enabled();
1979 self.set_show_edit_predictions(Some(show_edit_predictions), window, cx);
1980 }
1981 }
1982
1983 pub fn set_show_edit_predictions(
1984 &mut self,
1985 show_edit_predictions: Option<bool>,
1986 window: &mut Window,
1987 cx: &mut Context<Self>,
1988 ) {
1989 self.show_inline_completions_override = show_edit_predictions;
1990 self.update_edit_prediction_settings(cx);
1991
1992 if let Some(false) = show_edit_predictions {
1993 self.discard_inline_completion(false, cx);
1994 } else {
1995 self.refresh_inline_completion(false, true, window, cx);
1996 }
1997 }
1998
1999 fn inline_completions_disabled_in_scope(
2000 &self,
2001 buffer: &Entity<Buffer>,
2002 buffer_position: language::Anchor,
2003 cx: &App,
2004 ) -> bool {
2005 let snapshot = buffer.read(cx).snapshot();
2006 let settings = snapshot.settings_at(buffer_position, cx);
2007
2008 let Some(scope) = snapshot.language_scope_at(buffer_position) else {
2009 return false;
2010 };
2011
2012 scope.override_name().map_or(false, |scope_name| {
2013 settings
2014 .edit_predictions_disabled_in
2015 .iter()
2016 .any(|s| s == scope_name)
2017 })
2018 }
2019
2020 pub fn set_use_modal_editing(&mut self, to: bool) {
2021 self.use_modal_editing = to;
2022 }
2023
2024 pub fn use_modal_editing(&self) -> bool {
2025 self.use_modal_editing
2026 }
2027
2028 fn selections_did_change(
2029 &mut self,
2030 local: bool,
2031 old_cursor_position: &Anchor,
2032 show_completions: bool,
2033 window: &mut Window,
2034 cx: &mut Context<Self>,
2035 ) {
2036 window.invalidate_character_coordinates();
2037
2038 // Copy selections to primary selection buffer
2039 #[cfg(any(target_os = "linux", target_os = "freebsd"))]
2040 if local {
2041 let selections = self.selections.all::<usize>(cx);
2042 let buffer_handle = self.buffer.read(cx).read(cx);
2043
2044 let mut text = String::new();
2045 for (index, selection) in selections.iter().enumerate() {
2046 let text_for_selection = buffer_handle
2047 .text_for_range(selection.start..selection.end)
2048 .collect::<String>();
2049
2050 text.push_str(&text_for_selection);
2051 if index != selections.len() - 1 {
2052 text.push('\n');
2053 }
2054 }
2055
2056 if !text.is_empty() {
2057 cx.write_to_primary(ClipboardItem::new_string(text));
2058 }
2059 }
2060
2061 if self.focus_handle.is_focused(window) && self.leader_peer_id.is_none() {
2062 self.buffer.update(cx, |buffer, cx| {
2063 buffer.set_active_selections(
2064 &self.selections.disjoint_anchors(),
2065 self.selections.line_mode,
2066 self.cursor_shape,
2067 cx,
2068 )
2069 });
2070 }
2071 let display_map = self
2072 .display_map
2073 .update(cx, |display_map, cx| display_map.snapshot(cx));
2074 let buffer = &display_map.buffer_snapshot;
2075 self.add_selections_state = None;
2076 self.select_next_state = None;
2077 self.select_prev_state = None;
2078 self.select_larger_syntax_node_stack.clear();
2079 self.invalidate_autoclose_regions(&self.selections.disjoint_anchors(), buffer);
2080 self.snippet_stack
2081 .invalidate(&self.selections.disjoint_anchors(), buffer);
2082 self.take_rename(false, window, cx);
2083
2084 let new_cursor_position = self.selections.newest_anchor().head();
2085
2086 self.push_to_nav_history(
2087 *old_cursor_position,
2088 Some(new_cursor_position.to_point(buffer)),
2089 cx,
2090 );
2091
2092 if local {
2093 let new_cursor_position = self.selections.newest_anchor().head();
2094 let mut context_menu = self.context_menu.borrow_mut();
2095 let completion_menu = match context_menu.as_ref() {
2096 Some(CodeContextMenu::Completions(menu)) => Some(menu),
2097 _ => {
2098 *context_menu = None;
2099 None
2100 }
2101 };
2102 if let Some(buffer_id) = new_cursor_position.buffer_id {
2103 if !self.registered_buffers.contains_key(&buffer_id) {
2104 if let Some(project) = self.project.as_ref() {
2105 project.update(cx, |project, cx| {
2106 let Some(buffer) = self.buffer.read(cx).buffer(buffer_id) else {
2107 return;
2108 };
2109 self.registered_buffers.insert(
2110 buffer_id,
2111 project.register_buffer_with_language_servers(&buffer, cx),
2112 );
2113 })
2114 }
2115 }
2116 }
2117
2118 if let Some(completion_menu) = completion_menu {
2119 let cursor_position = new_cursor_position.to_offset(buffer);
2120 let (word_range, kind) =
2121 buffer.surrounding_word(completion_menu.initial_position, true);
2122 if kind == Some(CharKind::Word)
2123 && word_range.to_inclusive().contains(&cursor_position)
2124 {
2125 let mut completion_menu = completion_menu.clone();
2126 drop(context_menu);
2127
2128 let query = Self::completion_query(buffer, cursor_position);
2129 cx.spawn(move |this, mut cx| async move {
2130 completion_menu
2131 .filter(query.as_deref(), cx.background_executor().clone())
2132 .await;
2133
2134 this.update(&mut cx, |this, cx| {
2135 let mut context_menu = this.context_menu.borrow_mut();
2136 let Some(CodeContextMenu::Completions(menu)) = context_menu.as_ref()
2137 else {
2138 return;
2139 };
2140
2141 if menu.id > completion_menu.id {
2142 return;
2143 }
2144
2145 *context_menu = Some(CodeContextMenu::Completions(completion_menu));
2146 drop(context_menu);
2147 cx.notify();
2148 })
2149 })
2150 .detach();
2151
2152 if show_completions {
2153 self.show_completions(&ShowCompletions { trigger: None }, window, cx);
2154 }
2155 } else {
2156 drop(context_menu);
2157 self.hide_context_menu(window, cx);
2158 }
2159 } else {
2160 drop(context_menu);
2161 }
2162
2163 hide_hover(self, cx);
2164
2165 if old_cursor_position.to_display_point(&display_map).row()
2166 != new_cursor_position.to_display_point(&display_map).row()
2167 {
2168 self.available_code_actions.take();
2169 }
2170 self.refresh_code_actions(window, cx);
2171 self.refresh_document_highlights(cx);
2172 self.refresh_selected_text_highlights(window, cx);
2173 refresh_matching_bracket_highlights(self, window, cx);
2174 self.update_visible_inline_completion(window, cx);
2175 self.edit_prediction_requires_modifier_in_indent_conflict = true;
2176 linked_editing_ranges::refresh_linked_ranges(self, window, cx);
2177 if self.git_blame_inline_enabled {
2178 self.start_inline_blame_timer(window, cx);
2179 }
2180 }
2181
2182 self.blink_manager.update(cx, BlinkManager::pause_blinking);
2183 cx.emit(EditorEvent::SelectionsChanged { local });
2184
2185 let selections = &self.selections.disjoint;
2186 if selections.len() == 1 {
2187 cx.emit(SearchEvent::ActiveMatchChanged)
2188 }
2189 if local
2190 && self.is_singleton(cx)
2191 && WorkspaceSettings::get(None, cx).restore_on_startup != RestoreOnStartupBehavior::None
2192 {
2193 if let Some(workspace_id) = self.workspace.as_ref().and_then(|workspace| workspace.1) {
2194 let background_executor = cx.background_executor().clone();
2195 let editor_id = cx.entity().entity_id().as_u64() as ItemId;
2196 let snapshot = self.buffer().read(cx).snapshot(cx);
2197 let selections = selections.clone();
2198 self.serialize_selections = cx.background_spawn(async move {
2199 background_executor.timer(Duration::from_millis(100)).await;
2200 let selections = selections
2201 .iter()
2202 .map(|selection| {
2203 (
2204 selection.start.to_offset(&snapshot),
2205 selection.end.to_offset(&snapshot),
2206 )
2207 })
2208 .collect();
2209 DB.save_editor_selections(editor_id, workspace_id, selections)
2210 .await
2211 .with_context(|| format!("persisting editor selections for editor {editor_id}, workspace {workspace_id:?}"))
2212 .log_err();
2213 });
2214 }
2215 }
2216
2217 cx.notify();
2218 }
2219
2220 pub fn sync_selections(
2221 &mut self,
2222 other: Entity<Editor>,
2223 cx: &mut Context<Self>,
2224 ) -> gpui::Subscription {
2225 let other_selections = other.read(cx).selections.disjoint.to_vec();
2226 self.selections.change_with(cx, |selections| {
2227 selections.select_anchors(other_selections);
2228 });
2229
2230 let other_subscription =
2231 cx.subscribe(&other, |this, other, other_evt, cx| match other_evt {
2232 EditorEvent::SelectionsChanged { local: true } => {
2233 let other_selections = other.read(cx).selections.disjoint.to_vec();
2234 if other_selections.is_empty() {
2235 return;
2236 }
2237 this.selections.change_with(cx, |selections| {
2238 selections.select_anchors(other_selections);
2239 });
2240 }
2241 _ => {}
2242 });
2243
2244 let this_subscription =
2245 cx.subscribe_self::<EditorEvent>(move |this, this_evt, cx| match this_evt {
2246 EditorEvent::SelectionsChanged { local: true } => {
2247 let these_selections = this.selections.disjoint.to_vec();
2248 if these_selections.is_empty() {
2249 return;
2250 }
2251 other.update(cx, |other_editor, cx| {
2252 other_editor.selections.change_with(cx, |selections| {
2253 selections.select_anchors(these_selections);
2254 })
2255 });
2256 }
2257 _ => {}
2258 });
2259
2260 Subscription::join(other_subscription, this_subscription)
2261 }
2262
2263 pub fn change_selections<R>(
2264 &mut self,
2265 autoscroll: Option<Autoscroll>,
2266 window: &mut Window,
2267 cx: &mut Context<Self>,
2268 change: impl FnOnce(&mut MutableSelectionsCollection<'_>) -> R,
2269 ) -> R {
2270 self.change_selections_inner(autoscroll, true, window, cx, change)
2271 }
2272
2273 fn change_selections_inner<R>(
2274 &mut self,
2275 autoscroll: Option<Autoscroll>,
2276 request_completions: bool,
2277 window: &mut Window,
2278 cx: &mut Context<Self>,
2279 change: impl FnOnce(&mut MutableSelectionsCollection<'_>) -> R,
2280 ) -> R {
2281 let old_cursor_position = self.selections.newest_anchor().head();
2282 self.push_to_selection_history();
2283
2284 let (changed, result) = self.selections.change_with(cx, change);
2285
2286 if changed {
2287 if let Some(autoscroll) = autoscroll {
2288 self.request_autoscroll(autoscroll, cx);
2289 }
2290 self.selections_did_change(true, &old_cursor_position, request_completions, window, cx);
2291
2292 if self.should_open_signature_help_automatically(
2293 &old_cursor_position,
2294 self.signature_help_state.backspace_pressed(),
2295 cx,
2296 ) {
2297 self.show_signature_help(&ShowSignatureHelp, window, cx);
2298 }
2299 self.signature_help_state.set_backspace_pressed(false);
2300 }
2301
2302 result
2303 }
2304
2305 pub fn edit<I, S, T>(&mut self, edits: I, cx: &mut Context<Self>)
2306 where
2307 I: IntoIterator<Item = (Range<S>, T)>,
2308 S: ToOffset,
2309 T: Into<Arc<str>>,
2310 {
2311 if self.read_only(cx) {
2312 return;
2313 }
2314
2315 self.buffer
2316 .update(cx, |buffer, cx| buffer.edit(edits, None, cx));
2317 }
2318
2319 pub fn edit_with_autoindent<I, S, T>(&mut self, edits: I, cx: &mut Context<Self>)
2320 where
2321 I: IntoIterator<Item = (Range<S>, T)>,
2322 S: ToOffset,
2323 T: Into<Arc<str>>,
2324 {
2325 if self.read_only(cx) {
2326 return;
2327 }
2328
2329 self.buffer.update(cx, |buffer, cx| {
2330 buffer.edit(edits, self.autoindent_mode.clone(), cx)
2331 });
2332 }
2333
2334 pub fn edit_with_block_indent<I, S, T>(
2335 &mut self,
2336 edits: I,
2337 original_indent_columns: Vec<Option<u32>>,
2338 cx: &mut Context<Self>,
2339 ) where
2340 I: IntoIterator<Item = (Range<S>, T)>,
2341 S: ToOffset,
2342 T: Into<Arc<str>>,
2343 {
2344 if self.read_only(cx) {
2345 return;
2346 }
2347
2348 self.buffer.update(cx, |buffer, cx| {
2349 buffer.edit(
2350 edits,
2351 Some(AutoindentMode::Block {
2352 original_indent_columns,
2353 }),
2354 cx,
2355 )
2356 });
2357 }
2358
2359 fn select(&mut self, phase: SelectPhase, window: &mut Window, cx: &mut Context<Self>) {
2360 self.hide_context_menu(window, cx);
2361
2362 match phase {
2363 SelectPhase::Begin {
2364 position,
2365 add,
2366 click_count,
2367 } => self.begin_selection(position, add, click_count, window, cx),
2368 SelectPhase::BeginColumnar {
2369 position,
2370 goal_column,
2371 reset,
2372 } => self.begin_columnar_selection(position, goal_column, reset, window, cx),
2373 SelectPhase::Extend {
2374 position,
2375 click_count,
2376 } => self.extend_selection(position, click_count, window, cx),
2377 SelectPhase::Update {
2378 position,
2379 goal_column,
2380 scroll_delta,
2381 } => self.update_selection(position, goal_column, scroll_delta, window, cx),
2382 SelectPhase::End => self.end_selection(window, cx),
2383 }
2384 }
2385
2386 fn extend_selection(
2387 &mut self,
2388 position: DisplayPoint,
2389 click_count: usize,
2390 window: &mut Window,
2391 cx: &mut Context<Self>,
2392 ) {
2393 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
2394 let tail = self.selections.newest::<usize>(cx).tail();
2395 self.begin_selection(position, false, click_count, window, cx);
2396
2397 let position = position.to_offset(&display_map, Bias::Left);
2398 let tail_anchor = display_map.buffer_snapshot.anchor_before(tail);
2399
2400 let mut pending_selection = self
2401 .selections
2402 .pending_anchor()
2403 .expect("extend_selection not called with pending selection");
2404 if position >= tail {
2405 pending_selection.start = tail_anchor;
2406 } else {
2407 pending_selection.end = tail_anchor;
2408 pending_selection.reversed = true;
2409 }
2410
2411 let mut pending_mode = self.selections.pending_mode().unwrap();
2412 match &mut pending_mode {
2413 SelectMode::Word(range) | SelectMode::Line(range) => *range = tail_anchor..tail_anchor,
2414 _ => {}
2415 }
2416
2417 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
2418 s.set_pending(pending_selection, pending_mode)
2419 });
2420 }
2421
2422 fn begin_selection(
2423 &mut self,
2424 position: DisplayPoint,
2425 add: bool,
2426 click_count: usize,
2427 window: &mut Window,
2428 cx: &mut Context<Self>,
2429 ) {
2430 if !self.focus_handle.is_focused(window) {
2431 self.last_focused_descendant = None;
2432 window.focus(&self.focus_handle);
2433 }
2434
2435 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
2436 let buffer = &display_map.buffer_snapshot;
2437 let newest_selection = self.selections.newest_anchor().clone();
2438 let position = display_map.clip_point(position, Bias::Left);
2439
2440 let start;
2441 let end;
2442 let mode;
2443 let mut auto_scroll;
2444 match click_count {
2445 1 => {
2446 start = buffer.anchor_before(position.to_point(&display_map));
2447 end = start;
2448 mode = SelectMode::Character;
2449 auto_scroll = true;
2450 }
2451 2 => {
2452 let range = movement::surrounding_word(&display_map, position);
2453 start = buffer.anchor_before(range.start.to_point(&display_map));
2454 end = buffer.anchor_before(range.end.to_point(&display_map));
2455 mode = SelectMode::Word(start..end);
2456 auto_scroll = true;
2457 }
2458 3 => {
2459 let position = display_map
2460 .clip_point(position, Bias::Left)
2461 .to_point(&display_map);
2462 let line_start = display_map.prev_line_boundary(position).0;
2463 let next_line_start = buffer.clip_point(
2464 display_map.next_line_boundary(position).0 + Point::new(1, 0),
2465 Bias::Left,
2466 );
2467 start = buffer.anchor_before(line_start);
2468 end = buffer.anchor_before(next_line_start);
2469 mode = SelectMode::Line(start..end);
2470 auto_scroll = true;
2471 }
2472 _ => {
2473 start = buffer.anchor_before(0);
2474 end = buffer.anchor_before(buffer.len());
2475 mode = SelectMode::All;
2476 auto_scroll = false;
2477 }
2478 }
2479 auto_scroll &= EditorSettings::get_global(cx).autoscroll_on_clicks;
2480
2481 let point_to_delete: Option<usize> = {
2482 let selected_points: Vec<Selection<Point>> =
2483 self.selections.disjoint_in_range(start..end, cx);
2484
2485 if !add || click_count > 1 {
2486 None
2487 } else if !selected_points.is_empty() {
2488 Some(selected_points[0].id)
2489 } else {
2490 let clicked_point_already_selected =
2491 self.selections.disjoint.iter().find(|selection| {
2492 selection.start.to_point(buffer) == start.to_point(buffer)
2493 || selection.end.to_point(buffer) == end.to_point(buffer)
2494 });
2495
2496 clicked_point_already_selected.map(|selection| selection.id)
2497 }
2498 };
2499
2500 let selections_count = self.selections.count();
2501
2502 self.change_selections(auto_scroll.then(Autoscroll::newest), window, cx, |s| {
2503 if let Some(point_to_delete) = point_to_delete {
2504 s.delete(point_to_delete);
2505
2506 if selections_count == 1 {
2507 s.set_pending_anchor_range(start..end, mode);
2508 }
2509 } else {
2510 if !add {
2511 s.clear_disjoint();
2512 } else if click_count > 1 {
2513 s.delete(newest_selection.id)
2514 }
2515
2516 s.set_pending_anchor_range(start..end, mode);
2517 }
2518 });
2519 }
2520
2521 fn begin_columnar_selection(
2522 &mut self,
2523 position: DisplayPoint,
2524 goal_column: u32,
2525 reset: bool,
2526 window: &mut Window,
2527 cx: &mut Context<Self>,
2528 ) {
2529 if !self.focus_handle.is_focused(window) {
2530 self.last_focused_descendant = None;
2531 window.focus(&self.focus_handle);
2532 }
2533
2534 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
2535
2536 if reset {
2537 let pointer_position = display_map
2538 .buffer_snapshot
2539 .anchor_before(position.to_point(&display_map));
2540
2541 self.change_selections(Some(Autoscroll::newest()), window, cx, |s| {
2542 s.clear_disjoint();
2543 s.set_pending_anchor_range(
2544 pointer_position..pointer_position,
2545 SelectMode::Character,
2546 );
2547 });
2548 }
2549
2550 let tail = self.selections.newest::<Point>(cx).tail();
2551 self.columnar_selection_tail = Some(display_map.buffer_snapshot.anchor_before(tail));
2552
2553 if !reset {
2554 self.select_columns(
2555 tail.to_display_point(&display_map),
2556 position,
2557 goal_column,
2558 &display_map,
2559 window,
2560 cx,
2561 );
2562 }
2563 }
2564
2565 fn update_selection(
2566 &mut self,
2567 position: DisplayPoint,
2568 goal_column: u32,
2569 scroll_delta: gpui::Point<f32>,
2570 window: &mut Window,
2571 cx: &mut Context<Self>,
2572 ) {
2573 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
2574
2575 if let Some(tail) = self.columnar_selection_tail.as_ref() {
2576 let tail = tail.to_display_point(&display_map);
2577 self.select_columns(tail, position, goal_column, &display_map, window, cx);
2578 } else if let Some(mut pending) = self.selections.pending_anchor() {
2579 let buffer = self.buffer.read(cx).snapshot(cx);
2580 let head;
2581 let tail;
2582 let mode = self.selections.pending_mode().unwrap();
2583 match &mode {
2584 SelectMode::Character => {
2585 head = position.to_point(&display_map);
2586 tail = pending.tail().to_point(&buffer);
2587 }
2588 SelectMode::Word(original_range) => {
2589 let original_display_range = original_range.start.to_display_point(&display_map)
2590 ..original_range.end.to_display_point(&display_map);
2591 let original_buffer_range = original_display_range.start.to_point(&display_map)
2592 ..original_display_range.end.to_point(&display_map);
2593 if movement::is_inside_word(&display_map, position)
2594 || original_display_range.contains(&position)
2595 {
2596 let word_range = movement::surrounding_word(&display_map, position);
2597 if word_range.start < original_display_range.start {
2598 head = word_range.start.to_point(&display_map);
2599 } else {
2600 head = word_range.end.to_point(&display_map);
2601 }
2602 } else {
2603 head = position.to_point(&display_map);
2604 }
2605
2606 if head <= original_buffer_range.start {
2607 tail = original_buffer_range.end;
2608 } else {
2609 tail = original_buffer_range.start;
2610 }
2611 }
2612 SelectMode::Line(original_range) => {
2613 let original_range = original_range.to_point(&display_map.buffer_snapshot);
2614
2615 let position = display_map
2616 .clip_point(position, Bias::Left)
2617 .to_point(&display_map);
2618 let line_start = display_map.prev_line_boundary(position).0;
2619 let next_line_start = buffer.clip_point(
2620 display_map.next_line_boundary(position).0 + Point::new(1, 0),
2621 Bias::Left,
2622 );
2623
2624 if line_start < original_range.start {
2625 head = line_start
2626 } else {
2627 head = next_line_start
2628 }
2629
2630 if head <= original_range.start {
2631 tail = original_range.end;
2632 } else {
2633 tail = original_range.start;
2634 }
2635 }
2636 SelectMode::All => {
2637 return;
2638 }
2639 };
2640
2641 if head < tail {
2642 pending.start = buffer.anchor_before(head);
2643 pending.end = buffer.anchor_before(tail);
2644 pending.reversed = true;
2645 } else {
2646 pending.start = buffer.anchor_before(tail);
2647 pending.end = buffer.anchor_before(head);
2648 pending.reversed = false;
2649 }
2650
2651 self.change_selections(None, window, cx, |s| {
2652 s.set_pending(pending, mode);
2653 });
2654 } else {
2655 log::error!("update_selection dispatched with no pending selection");
2656 return;
2657 }
2658
2659 self.apply_scroll_delta(scroll_delta, window, cx);
2660 cx.notify();
2661 }
2662
2663 fn end_selection(&mut self, window: &mut Window, cx: &mut Context<Self>) {
2664 self.columnar_selection_tail.take();
2665 if self.selections.pending_anchor().is_some() {
2666 let selections = self.selections.all::<usize>(cx);
2667 self.change_selections(None, window, cx, |s| {
2668 s.select(selections);
2669 s.clear_pending();
2670 });
2671 }
2672 }
2673
2674 fn select_columns(
2675 &mut self,
2676 tail: DisplayPoint,
2677 head: DisplayPoint,
2678 goal_column: u32,
2679 display_map: &DisplaySnapshot,
2680 window: &mut Window,
2681 cx: &mut Context<Self>,
2682 ) {
2683 let start_row = cmp::min(tail.row(), head.row());
2684 let end_row = cmp::max(tail.row(), head.row());
2685 let start_column = cmp::min(tail.column(), goal_column);
2686 let end_column = cmp::max(tail.column(), goal_column);
2687 let reversed = start_column < tail.column();
2688
2689 let selection_ranges = (start_row.0..=end_row.0)
2690 .map(DisplayRow)
2691 .filter_map(|row| {
2692 if start_column <= display_map.line_len(row) && !display_map.is_block_line(row) {
2693 let start = display_map
2694 .clip_point(DisplayPoint::new(row, start_column), Bias::Left)
2695 .to_point(display_map);
2696 let end = display_map
2697 .clip_point(DisplayPoint::new(row, end_column), Bias::Right)
2698 .to_point(display_map);
2699 if reversed {
2700 Some(end..start)
2701 } else {
2702 Some(start..end)
2703 }
2704 } else {
2705 None
2706 }
2707 })
2708 .collect::<Vec<_>>();
2709
2710 self.change_selections(None, window, cx, |s| {
2711 s.select_ranges(selection_ranges);
2712 });
2713 cx.notify();
2714 }
2715
2716 pub fn has_pending_nonempty_selection(&self) -> bool {
2717 let pending_nonempty_selection = match self.selections.pending_anchor() {
2718 Some(Selection { start, end, .. }) => start != end,
2719 None => false,
2720 };
2721
2722 pending_nonempty_selection
2723 || (self.columnar_selection_tail.is_some() && self.selections.disjoint.len() > 1)
2724 }
2725
2726 pub fn has_pending_selection(&self) -> bool {
2727 self.selections.pending_anchor().is_some() || self.columnar_selection_tail.is_some()
2728 }
2729
2730 pub fn cancel(&mut self, _: &Cancel, window: &mut Window, cx: &mut Context<Self>) {
2731 self.selection_mark_mode = false;
2732
2733 if self.clear_expanded_diff_hunks(cx) {
2734 cx.notify();
2735 return;
2736 }
2737 if self.dismiss_menus_and_popups(true, window, cx) {
2738 return;
2739 }
2740
2741 if self.mode == EditorMode::Full
2742 && self.change_selections(Some(Autoscroll::fit()), window, cx, |s| s.try_cancel())
2743 {
2744 return;
2745 }
2746
2747 cx.propagate();
2748 }
2749
2750 pub fn dismiss_menus_and_popups(
2751 &mut self,
2752 is_user_requested: bool,
2753 window: &mut Window,
2754 cx: &mut Context<Self>,
2755 ) -> bool {
2756 if self.take_rename(false, window, cx).is_some() {
2757 return true;
2758 }
2759
2760 if hide_hover(self, cx) {
2761 return true;
2762 }
2763
2764 if self.hide_signature_help(cx, SignatureHelpHiddenBy::Escape) {
2765 return true;
2766 }
2767
2768 if self.hide_context_menu(window, cx).is_some() {
2769 return true;
2770 }
2771
2772 if self.mouse_context_menu.take().is_some() {
2773 return true;
2774 }
2775
2776 if is_user_requested && self.discard_inline_completion(true, cx) {
2777 return true;
2778 }
2779
2780 if self.snippet_stack.pop().is_some() {
2781 return true;
2782 }
2783
2784 if self.mode == EditorMode::Full && self.active_diagnostics.is_some() {
2785 self.dismiss_diagnostics(cx);
2786 return true;
2787 }
2788
2789 false
2790 }
2791
2792 fn linked_editing_ranges_for(
2793 &self,
2794 selection: Range<text::Anchor>,
2795 cx: &App,
2796 ) -> Option<HashMap<Entity<Buffer>, Vec<Range<text::Anchor>>>> {
2797 if self.linked_edit_ranges.is_empty() {
2798 return None;
2799 }
2800 let ((base_range, linked_ranges), buffer_snapshot, buffer) =
2801 selection.end.buffer_id.and_then(|end_buffer_id| {
2802 if selection.start.buffer_id != Some(end_buffer_id) {
2803 return None;
2804 }
2805 let buffer = self.buffer.read(cx).buffer(end_buffer_id)?;
2806 let snapshot = buffer.read(cx).snapshot();
2807 self.linked_edit_ranges
2808 .get(end_buffer_id, selection.start..selection.end, &snapshot)
2809 .map(|ranges| (ranges, snapshot, buffer))
2810 })?;
2811 use text::ToOffset as TO;
2812 // find offset from the start of current range to current cursor position
2813 let start_byte_offset = TO::to_offset(&base_range.start, &buffer_snapshot);
2814
2815 let start_offset = TO::to_offset(&selection.start, &buffer_snapshot);
2816 let start_difference = start_offset - start_byte_offset;
2817 let end_offset = TO::to_offset(&selection.end, &buffer_snapshot);
2818 let end_difference = end_offset - start_byte_offset;
2819 // Current range has associated linked ranges.
2820 let mut linked_edits = HashMap::<_, Vec<_>>::default();
2821 for range in linked_ranges.iter() {
2822 let start_offset = TO::to_offset(&range.start, &buffer_snapshot);
2823 let end_offset = start_offset + end_difference;
2824 let start_offset = start_offset + start_difference;
2825 if start_offset > buffer_snapshot.len() || end_offset > buffer_snapshot.len() {
2826 continue;
2827 }
2828 if self.selections.disjoint_anchor_ranges().any(|s| {
2829 if s.start.buffer_id != selection.start.buffer_id
2830 || s.end.buffer_id != selection.end.buffer_id
2831 {
2832 return false;
2833 }
2834 TO::to_offset(&s.start.text_anchor, &buffer_snapshot) <= end_offset
2835 && TO::to_offset(&s.end.text_anchor, &buffer_snapshot) >= start_offset
2836 }) {
2837 continue;
2838 }
2839 let start = buffer_snapshot.anchor_after(start_offset);
2840 let end = buffer_snapshot.anchor_after(end_offset);
2841 linked_edits
2842 .entry(buffer.clone())
2843 .or_default()
2844 .push(start..end);
2845 }
2846 Some(linked_edits)
2847 }
2848
2849 pub fn handle_input(&mut self, text: &str, window: &mut Window, cx: &mut Context<Self>) {
2850 let text: Arc<str> = text.into();
2851
2852 if self.read_only(cx) {
2853 return;
2854 }
2855
2856 let selections = self.selections.all_adjusted(cx);
2857 let mut bracket_inserted = false;
2858 let mut edits = Vec::new();
2859 let mut linked_edits = HashMap::<_, Vec<_>>::default();
2860 let mut new_selections = Vec::with_capacity(selections.len());
2861 let mut new_autoclose_regions = Vec::new();
2862 let snapshot = self.buffer.read(cx).read(cx);
2863
2864 for (selection, autoclose_region) in
2865 self.selections_with_autoclose_regions(selections, &snapshot)
2866 {
2867 if let Some(scope) = snapshot.language_scope_at(selection.head()) {
2868 // Determine if the inserted text matches the opening or closing
2869 // bracket of any of this language's bracket pairs.
2870 let mut bracket_pair = None;
2871 let mut is_bracket_pair_start = false;
2872 let mut is_bracket_pair_end = false;
2873 if !text.is_empty() {
2874 // `text` can be empty when a user is using IME (e.g. Chinese Wubi Simplified)
2875 // and they are removing the character that triggered IME popup.
2876 for (pair, enabled) in scope.brackets() {
2877 if !pair.close && !pair.surround {
2878 continue;
2879 }
2880
2881 if enabled && pair.start.ends_with(text.as_ref()) {
2882 let prefix_len = pair.start.len() - text.len();
2883 let preceding_text_matches_prefix = prefix_len == 0
2884 || (selection.start.column >= (prefix_len as u32)
2885 && snapshot.contains_str_at(
2886 Point::new(
2887 selection.start.row,
2888 selection.start.column - (prefix_len as u32),
2889 ),
2890 &pair.start[..prefix_len],
2891 ));
2892 if preceding_text_matches_prefix {
2893 bracket_pair = Some(pair.clone());
2894 is_bracket_pair_start = true;
2895 break;
2896 }
2897 }
2898 if pair.end.as_str() == text.as_ref() {
2899 bracket_pair = Some(pair.clone());
2900 is_bracket_pair_end = true;
2901 break;
2902 }
2903 }
2904 }
2905
2906 if let Some(bracket_pair) = bracket_pair {
2907 let snapshot_settings = snapshot.language_settings_at(selection.start, cx);
2908 let autoclose = self.use_autoclose && snapshot_settings.use_autoclose;
2909 let auto_surround =
2910 self.use_auto_surround && snapshot_settings.use_auto_surround;
2911 if selection.is_empty() {
2912 if is_bracket_pair_start {
2913 // If the inserted text is a suffix of an opening bracket and the
2914 // selection is preceded by the rest of the opening bracket, then
2915 // insert the closing bracket.
2916 let following_text_allows_autoclose = snapshot
2917 .chars_at(selection.start)
2918 .next()
2919 .map_or(true, |c| scope.should_autoclose_before(c));
2920
2921 let preceding_text_allows_autoclose = selection.start.column == 0
2922 || snapshot.reversed_chars_at(selection.start).next().map_or(
2923 true,
2924 |c| {
2925 bracket_pair.start != bracket_pair.end
2926 || !snapshot
2927 .char_classifier_at(selection.start)
2928 .is_word(c)
2929 },
2930 );
2931
2932 let is_closing_quote = if bracket_pair.end == bracket_pair.start
2933 && bracket_pair.start.len() == 1
2934 {
2935 let target = bracket_pair.start.chars().next().unwrap();
2936 let current_line_count = snapshot
2937 .reversed_chars_at(selection.start)
2938 .take_while(|&c| c != '\n')
2939 .filter(|&c| c == target)
2940 .count();
2941 current_line_count % 2 == 1
2942 } else {
2943 false
2944 };
2945
2946 if autoclose
2947 && bracket_pair.close
2948 && following_text_allows_autoclose
2949 && preceding_text_allows_autoclose
2950 && !is_closing_quote
2951 {
2952 let anchor = snapshot.anchor_before(selection.end);
2953 new_selections.push((selection.map(|_| anchor), text.len()));
2954 new_autoclose_regions.push((
2955 anchor,
2956 text.len(),
2957 selection.id,
2958 bracket_pair.clone(),
2959 ));
2960 edits.push((
2961 selection.range(),
2962 format!("{}{}", text, bracket_pair.end).into(),
2963 ));
2964 bracket_inserted = true;
2965 continue;
2966 }
2967 }
2968
2969 if let Some(region) = autoclose_region {
2970 // If the selection is followed by an auto-inserted closing bracket,
2971 // then don't insert that closing bracket again; just move the selection
2972 // past the closing bracket.
2973 let should_skip = selection.end == region.range.end.to_point(&snapshot)
2974 && text.as_ref() == region.pair.end.as_str();
2975 if should_skip {
2976 let anchor = snapshot.anchor_after(selection.end);
2977 new_selections
2978 .push((selection.map(|_| anchor), region.pair.end.len()));
2979 continue;
2980 }
2981 }
2982
2983 let always_treat_brackets_as_autoclosed = snapshot
2984 .language_settings_at(selection.start, cx)
2985 .always_treat_brackets_as_autoclosed;
2986 if always_treat_brackets_as_autoclosed
2987 && is_bracket_pair_end
2988 && snapshot.contains_str_at(selection.end, text.as_ref())
2989 {
2990 // Otherwise, when `always_treat_brackets_as_autoclosed` is set to `true
2991 // and the inserted text is a closing bracket and the selection is followed
2992 // by the closing bracket then move the selection past the closing bracket.
2993 let anchor = snapshot.anchor_after(selection.end);
2994 new_selections.push((selection.map(|_| anchor), text.len()));
2995 continue;
2996 }
2997 }
2998 // If an opening bracket is 1 character long and is typed while
2999 // text is selected, then surround that text with the bracket pair.
3000 else if auto_surround
3001 && bracket_pair.surround
3002 && is_bracket_pair_start
3003 && bracket_pair.start.chars().count() == 1
3004 {
3005 edits.push((selection.start..selection.start, text.clone()));
3006 edits.push((
3007 selection.end..selection.end,
3008 bracket_pair.end.as_str().into(),
3009 ));
3010 bracket_inserted = true;
3011 new_selections.push((
3012 Selection {
3013 id: selection.id,
3014 start: snapshot.anchor_after(selection.start),
3015 end: snapshot.anchor_before(selection.end),
3016 reversed: selection.reversed,
3017 goal: selection.goal,
3018 },
3019 0,
3020 ));
3021 continue;
3022 }
3023 }
3024 }
3025
3026 if self.auto_replace_emoji_shortcode
3027 && selection.is_empty()
3028 && text.as_ref().ends_with(':')
3029 {
3030 if let Some(possible_emoji_short_code) =
3031 Self::find_possible_emoji_shortcode_at_position(&snapshot, selection.start)
3032 {
3033 if !possible_emoji_short_code.is_empty() {
3034 if let Some(emoji) = emojis::get_by_shortcode(&possible_emoji_short_code) {
3035 let emoji_shortcode_start = Point::new(
3036 selection.start.row,
3037 selection.start.column - possible_emoji_short_code.len() as u32 - 1,
3038 );
3039
3040 // Remove shortcode from buffer
3041 edits.push((
3042 emoji_shortcode_start..selection.start,
3043 "".to_string().into(),
3044 ));
3045 new_selections.push((
3046 Selection {
3047 id: selection.id,
3048 start: snapshot.anchor_after(emoji_shortcode_start),
3049 end: snapshot.anchor_before(selection.start),
3050 reversed: selection.reversed,
3051 goal: selection.goal,
3052 },
3053 0,
3054 ));
3055
3056 // Insert emoji
3057 let selection_start_anchor = snapshot.anchor_after(selection.start);
3058 new_selections.push((selection.map(|_| selection_start_anchor), 0));
3059 edits.push((selection.start..selection.end, emoji.to_string().into()));
3060
3061 continue;
3062 }
3063 }
3064 }
3065 }
3066
3067 // If not handling any auto-close operation, then just replace the selected
3068 // text with the given input and move the selection to the end of the
3069 // newly inserted text.
3070 let anchor = snapshot.anchor_after(selection.end);
3071 if !self.linked_edit_ranges.is_empty() {
3072 let start_anchor = snapshot.anchor_before(selection.start);
3073
3074 let is_word_char = text.chars().next().map_or(true, |char| {
3075 let classifier = snapshot.char_classifier_at(start_anchor.to_offset(&snapshot));
3076 classifier.is_word(char)
3077 });
3078
3079 if is_word_char {
3080 if let Some(ranges) = self
3081 .linked_editing_ranges_for(start_anchor.text_anchor..anchor.text_anchor, cx)
3082 {
3083 for (buffer, edits) in ranges {
3084 linked_edits
3085 .entry(buffer.clone())
3086 .or_default()
3087 .extend(edits.into_iter().map(|range| (range, text.clone())));
3088 }
3089 }
3090 }
3091 }
3092
3093 new_selections.push((selection.map(|_| anchor), 0));
3094 edits.push((selection.start..selection.end, text.clone()));
3095 }
3096
3097 drop(snapshot);
3098
3099 self.transact(window, cx, |this, window, cx| {
3100 let initial_buffer_versions =
3101 jsx_tag_auto_close::construct_initial_buffer_versions_map(this, &edits, cx);
3102
3103 this.buffer.update(cx, |buffer, cx| {
3104 buffer.edit(edits, this.autoindent_mode.clone(), cx);
3105 });
3106 for (buffer, edits) in linked_edits {
3107 buffer.update(cx, |buffer, cx| {
3108 let snapshot = buffer.snapshot();
3109 let edits = edits
3110 .into_iter()
3111 .map(|(range, text)| {
3112 use text::ToPoint as TP;
3113 let end_point = TP::to_point(&range.end, &snapshot);
3114 let start_point = TP::to_point(&range.start, &snapshot);
3115 (start_point..end_point, text)
3116 })
3117 .sorted_by_key(|(range, _)| range.start)
3118 .collect::<Vec<_>>();
3119 buffer.edit(edits, None, cx);
3120 })
3121 }
3122 let new_anchor_selections = new_selections.iter().map(|e| &e.0);
3123 let new_selection_deltas = new_selections.iter().map(|e| e.1);
3124 let map = this.display_map.update(cx, |map, cx| map.snapshot(cx));
3125 let new_selections = resolve_selections::<usize, _>(new_anchor_selections, &map)
3126 .zip(new_selection_deltas)
3127 .map(|(selection, delta)| Selection {
3128 id: selection.id,
3129 start: selection.start + delta,
3130 end: selection.end + delta,
3131 reversed: selection.reversed,
3132 goal: SelectionGoal::None,
3133 })
3134 .collect::<Vec<_>>();
3135
3136 let mut i = 0;
3137 for (position, delta, selection_id, pair) in new_autoclose_regions {
3138 let position = position.to_offset(&map.buffer_snapshot) + delta;
3139 let start = map.buffer_snapshot.anchor_before(position);
3140 let end = map.buffer_snapshot.anchor_after(position);
3141 while let Some(existing_state) = this.autoclose_regions.get(i) {
3142 match existing_state.range.start.cmp(&start, &map.buffer_snapshot) {
3143 Ordering::Less => i += 1,
3144 Ordering::Greater => break,
3145 Ordering::Equal => {
3146 match end.cmp(&existing_state.range.end, &map.buffer_snapshot) {
3147 Ordering::Less => i += 1,
3148 Ordering::Equal => break,
3149 Ordering::Greater => break,
3150 }
3151 }
3152 }
3153 }
3154 this.autoclose_regions.insert(
3155 i,
3156 AutocloseRegion {
3157 selection_id,
3158 range: start..end,
3159 pair,
3160 },
3161 );
3162 }
3163
3164 let had_active_inline_completion = this.has_active_inline_completion();
3165 this.change_selections_inner(Some(Autoscroll::fit()), false, window, cx, |s| {
3166 s.select(new_selections)
3167 });
3168
3169 if !bracket_inserted {
3170 if let Some(on_type_format_task) =
3171 this.trigger_on_type_formatting(text.to_string(), window, cx)
3172 {
3173 on_type_format_task.detach_and_log_err(cx);
3174 }
3175 }
3176
3177 let editor_settings = EditorSettings::get_global(cx);
3178 if bracket_inserted
3179 && (editor_settings.auto_signature_help
3180 || editor_settings.show_signature_help_after_edits)
3181 {
3182 this.show_signature_help(&ShowSignatureHelp, window, cx);
3183 }
3184
3185 let trigger_in_words =
3186 this.show_edit_predictions_in_menu() || !had_active_inline_completion;
3187 if this.hard_wrap.is_some() {
3188 let latest: Range<Point> = this.selections.newest(cx).range();
3189 if latest.is_empty()
3190 && this
3191 .buffer()
3192 .read(cx)
3193 .snapshot(cx)
3194 .line_len(MultiBufferRow(latest.start.row))
3195 == latest.start.column
3196 {
3197 this.rewrap_impl(true, cx)
3198 }
3199 }
3200 this.trigger_completion_on_input(&text, trigger_in_words, window, cx);
3201 linked_editing_ranges::refresh_linked_ranges(this, window, cx);
3202 this.refresh_inline_completion(true, false, window, cx);
3203 jsx_tag_auto_close::handle_from(this, initial_buffer_versions, window, cx);
3204 });
3205 }
3206
3207 fn find_possible_emoji_shortcode_at_position(
3208 snapshot: &MultiBufferSnapshot,
3209 position: Point,
3210 ) -> Option<String> {
3211 let mut chars = Vec::new();
3212 let mut found_colon = false;
3213 for char in snapshot.reversed_chars_at(position).take(100) {
3214 // Found a possible emoji shortcode in the middle of the buffer
3215 if found_colon {
3216 if char.is_whitespace() {
3217 chars.reverse();
3218 return Some(chars.iter().collect());
3219 }
3220 // If the previous character is not a whitespace, we are in the middle of a word
3221 // and we only want to complete the shortcode if the word is made up of other emojis
3222 let mut containing_word = String::new();
3223 for ch in snapshot
3224 .reversed_chars_at(position)
3225 .skip(chars.len() + 1)
3226 .take(100)
3227 {
3228 if ch.is_whitespace() {
3229 break;
3230 }
3231 containing_word.push(ch);
3232 }
3233 let containing_word = containing_word.chars().rev().collect::<String>();
3234 if util::word_consists_of_emojis(containing_word.as_str()) {
3235 chars.reverse();
3236 return Some(chars.iter().collect());
3237 }
3238 }
3239
3240 if char.is_whitespace() || !char.is_ascii() {
3241 return None;
3242 }
3243 if char == ':' {
3244 found_colon = true;
3245 } else {
3246 chars.push(char);
3247 }
3248 }
3249 // Found a possible emoji shortcode at the beginning of the buffer
3250 chars.reverse();
3251 Some(chars.iter().collect())
3252 }
3253
3254 pub fn newline(&mut self, _: &Newline, window: &mut Window, cx: &mut Context<Self>) {
3255 self.transact(window, cx, |this, window, cx| {
3256 let (edits, selection_fixup_info): (Vec<_>, Vec<_>) = {
3257 let selections = this.selections.all::<usize>(cx);
3258 let multi_buffer = this.buffer.read(cx);
3259 let buffer = multi_buffer.snapshot(cx);
3260 selections
3261 .iter()
3262 .map(|selection| {
3263 let start_point = selection.start.to_point(&buffer);
3264 let mut indent =
3265 buffer.indent_size_for_line(MultiBufferRow(start_point.row));
3266 indent.len = cmp::min(indent.len, start_point.column);
3267 let start = selection.start;
3268 let end = selection.end;
3269 let selection_is_empty = start == end;
3270 let language_scope = buffer.language_scope_at(start);
3271 let (comment_delimiter, insert_extra_newline) = if let Some(language) =
3272 &language_scope
3273 {
3274 let insert_extra_newline =
3275 insert_extra_newline_brackets(&buffer, start..end, language)
3276 || insert_extra_newline_tree_sitter(&buffer, start..end);
3277
3278 // Comment extension on newline is allowed only for cursor selections
3279 let comment_delimiter = maybe!({
3280 if !selection_is_empty {
3281 return None;
3282 }
3283
3284 if !multi_buffer.language_settings(cx).extend_comment_on_newline {
3285 return None;
3286 }
3287
3288 let delimiters = language.line_comment_prefixes();
3289 let max_len_of_delimiter =
3290 delimiters.iter().map(|delimiter| delimiter.len()).max()?;
3291 let (snapshot, range) =
3292 buffer.buffer_line_for_row(MultiBufferRow(start_point.row))?;
3293
3294 let mut index_of_first_non_whitespace = 0;
3295 let comment_candidate = snapshot
3296 .chars_for_range(range)
3297 .skip_while(|c| {
3298 let should_skip = c.is_whitespace();
3299 if should_skip {
3300 index_of_first_non_whitespace += 1;
3301 }
3302 should_skip
3303 })
3304 .take(max_len_of_delimiter)
3305 .collect::<String>();
3306 let comment_prefix = delimiters.iter().find(|comment_prefix| {
3307 comment_candidate.starts_with(comment_prefix.as_ref())
3308 })?;
3309 let cursor_is_placed_after_comment_marker =
3310 index_of_first_non_whitespace + comment_prefix.len()
3311 <= start_point.column as usize;
3312 if cursor_is_placed_after_comment_marker {
3313 Some(comment_prefix.clone())
3314 } else {
3315 None
3316 }
3317 });
3318 (comment_delimiter, insert_extra_newline)
3319 } else {
3320 (None, false)
3321 };
3322
3323 let capacity_for_delimiter = comment_delimiter
3324 .as_deref()
3325 .map(str::len)
3326 .unwrap_or_default();
3327 let mut new_text =
3328 String::with_capacity(1 + capacity_for_delimiter + indent.len as usize);
3329 new_text.push('\n');
3330 new_text.extend(indent.chars());
3331 if let Some(delimiter) = &comment_delimiter {
3332 new_text.push_str(delimiter);
3333 }
3334 if insert_extra_newline {
3335 new_text = new_text.repeat(2);
3336 }
3337
3338 let anchor = buffer.anchor_after(end);
3339 let new_selection = selection.map(|_| anchor);
3340 (
3341 (start..end, new_text),
3342 (insert_extra_newline, new_selection),
3343 )
3344 })
3345 .unzip()
3346 };
3347
3348 this.edit_with_autoindent(edits, cx);
3349 let buffer = this.buffer.read(cx).snapshot(cx);
3350 let new_selections = selection_fixup_info
3351 .into_iter()
3352 .map(|(extra_newline_inserted, new_selection)| {
3353 let mut cursor = new_selection.end.to_point(&buffer);
3354 if extra_newline_inserted {
3355 cursor.row -= 1;
3356 cursor.column = buffer.line_len(MultiBufferRow(cursor.row));
3357 }
3358 new_selection.map(|_| cursor)
3359 })
3360 .collect();
3361
3362 this.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
3363 s.select(new_selections)
3364 });
3365 this.refresh_inline_completion(true, false, window, cx);
3366 });
3367 }
3368
3369 pub fn newline_above(&mut self, _: &NewlineAbove, window: &mut Window, cx: &mut Context<Self>) {
3370 let buffer = self.buffer.read(cx);
3371 let snapshot = buffer.snapshot(cx);
3372
3373 let mut edits = Vec::new();
3374 let mut rows = Vec::new();
3375
3376 for (rows_inserted, selection) in self.selections.all_adjusted(cx).into_iter().enumerate() {
3377 let cursor = selection.head();
3378 let row = cursor.row;
3379
3380 let start_of_line = snapshot.clip_point(Point::new(row, 0), Bias::Left);
3381
3382 let newline = "\n".to_string();
3383 edits.push((start_of_line..start_of_line, newline));
3384
3385 rows.push(row + rows_inserted as u32);
3386 }
3387
3388 self.transact(window, cx, |editor, window, cx| {
3389 editor.edit(edits, cx);
3390
3391 editor.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
3392 let mut index = 0;
3393 s.move_cursors_with(|map, _, _| {
3394 let row = rows[index];
3395 index += 1;
3396
3397 let point = Point::new(row, 0);
3398 let boundary = map.next_line_boundary(point).1;
3399 let clipped = map.clip_point(boundary, Bias::Left);
3400
3401 (clipped, SelectionGoal::None)
3402 });
3403 });
3404
3405 let mut indent_edits = Vec::new();
3406 let multibuffer_snapshot = editor.buffer.read(cx).snapshot(cx);
3407 for row in rows {
3408 let indents = multibuffer_snapshot.suggested_indents(row..row + 1, cx);
3409 for (row, indent) in indents {
3410 if indent.len == 0 {
3411 continue;
3412 }
3413
3414 let text = match indent.kind {
3415 IndentKind::Space => " ".repeat(indent.len as usize),
3416 IndentKind::Tab => "\t".repeat(indent.len as usize),
3417 };
3418 let point = Point::new(row.0, 0);
3419 indent_edits.push((point..point, text));
3420 }
3421 }
3422 editor.edit(indent_edits, cx);
3423 });
3424 }
3425
3426 pub fn newline_below(&mut self, _: &NewlineBelow, window: &mut Window, cx: &mut Context<Self>) {
3427 let buffer = self.buffer.read(cx);
3428 let snapshot = buffer.snapshot(cx);
3429
3430 let mut edits = Vec::new();
3431 let mut rows = Vec::new();
3432 let mut rows_inserted = 0;
3433
3434 for selection in self.selections.all_adjusted(cx) {
3435 let cursor = selection.head();
3436 let row = cursor.row;
3437
3438 let point = Point::new(row + 1, 0);
3439 let start_of_line = snapshot.clip_point(point, Bias::Left);
3440
3441 let newline = "\n".to_string();
3442 edits.push((start_of_line..start_of_line, newline));
3443
3444 rows_inserted += 1;
3445 rows.push(row + rows_inserted);
3446 }
3447
3448 self.transact(window, cx, |editor, window, cx| {
3449 editor.edit(edits, cx);
3450
3451 editor.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
3452 let mut index = 0;
3453 s.move_cursors_with(|map, _, _| {
3454 let row = rows[index];
3455 index += 1;
3456
3457 let point = Point::new(row, 0);
3458 let boundary = map.next_line_boundary(point).1;
3459 let clipped = map.clip_point(boundary, Bias::Left);
3460
3461 (clipped, SelectionGoal::None)
3462 });
3463 });
3464
3465 let mut indent_edits = Vec::new();
3466 let multibuffer_snapshot = editor.buffer.read(cx).snapshot(cx);
3467 for row in rows {
3468 let indents = multibuffer_snapshot.suggested_indents(row..row + 1, cx);
3469 for (row, indent) in indents {
3470 if indent.len == 0 {
3471 continue;
3472 }
3473
3474 let text = match indent.kind {
3475 IndentKind::Space => " ".repeat(indent.len as usize),
3476 IndentKind::Tab => "\t".repeat(indent.len as usize),
3477 };
3478 let point = Point::new(row.0, 0);
3479 indent_edits.push((point..point, text));
3480 }
3481 }
3482 editor.edit(indent_edits, cx);
3483 });
3484 }
3485
3486 pub fn insert(&mut self, text: &str, window: &mut Window, cx: &mut Context<Self>) {
3487 let autoindent = text.is_empty().not().then(|| AutoindentMode::Block {
3488 original_indent_columns: Vec::new(),
3489 });
3490 self.insert_with_autoindent_mode(text, autoindent, window, cx);
3491 }
3492
3493 fn insert_with_autoindent_mode(
3494 &mut self,
3495 text: &str,
3496 autoindent_mode: Option<AutoindentMode>,
3497 window: &mut Window,
3498 cx: &mut Context<Self>,
3499 ) {
3500 if self.read_only(cx) {
3501 return;
3502 }
3503
3504 let text: Arc<str> = text.into();
3505 self.transact(window, cx, |this, window, cx| {
3506 let old_selections = this.selections.all_adjusted(cx);
3507 let selection_anchors = this.buffer.update(cx, |buffer, cx| {
3508 let anchors = {
3509 let snapshot = buffer.read(cx);
3510 old_selections
3511 .iter()
3512 .map(|s| {
3513 let anchor = snapshot.anchor_after(s.head());
3514 s.map(|_| anchor)
3515 })
3516 .collect::<Vec<_>>()
3517 };
3518 buffer.edit(
3519 old_selections
3520 .iter()
3521 .map(|s| (s.start..s.end, text.clone())),
3522 autoindent_mode,
3523 cx,
3524 );
3525 anchors
3526 });
3527
3528 this.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
3529 s.select_anchors(selection_anchors);
3530 });
3531
3532 cx.notify();
3533 });
3534 }
3535
3536 fn trigger_completion_on_input(
3537 &mut self,
3538 text: &str,
3539 trigger_in_words: bool,
3540 window: &mut Window,
3541 cx: &mut Context<Self>,
3542 ) {
3543 if self.is_completion_trigger(text, trigger_in_words, cx) {
3544 self.show_completions(
3545 &ShowCompletions {
3546 trigger: Some(text.to_owned()).filter(|x| !x.is_empty()),
3547 },
3548 window,
3549 cx,
3550 );
3551 } else {
3552 self.hide_context_menu(window, cx);
3553 }
3554 }
3555
3556 fn is_completion_trigger(
3557 &self,
3558 text: &str,
3559 trigger_in_words: bool,
3560 cx: &mut Context<Self>,
3561 ) -> bool {
3562 let position = self.selections.newest_anchor().head();
3563 let multibuffer = self.buffer.read(cx);
3564 let Some(buffer) = position
3565 .buffer_id
3566 .and_then(|buffer_id| multibuffer.buffer(buffer_id).clone())
3567 else {
3568 return false;
3569 };
3570
3571 if let Some(completion_provider) = &self.completion_provider {
3572 completion_provider.is_completion_trigger(
3573 &buffer,
3574 position.text_anchor,
3575 text,
3576 trigger_in_words,
3577 cx,
3578 )
3579 } else {
3580 false
3581 }
3582 }
3583
3584 /// If any empty selections is touching the start of its innermost containing autoclose
3585 /// region, expand it to select the brackets.
3586 fn select_autoclose_pair(&mut self, window: &mut Window, cx: &mut Context<Self>) {
3587 let selections = self.selections.all::<usize>(cx);
3588 let buffer = self.buffer.read(cx).read(cx);
3589 let new_selections = self
3590 .selections_with_autoclose_regions(selections, &buffer)
3591 .map(|(mut selection, region)| {
3592 if !selection.is_empty() {
3593 return selection;
3594 }
3595
3596 if let Some(region) = region {
3597 let mut range = region.range.to_offset(&buffer);
3598 if selection.start == range.start && range.start >= region.pair.start.len() {
3599 range.start -= region.pair.start.len();
3600 if buffer.contains_str_at(range.start, ®ion.pair.start)
3601 && buffer.contains_str_at(range.end, ®ion.pair.end)
3602 {
3603 range.end += region.pair.end.len();
3604 selection.start = range.start;
3605 selection.end = range.end;
3606
3607 return selection;
3608 }
3609 }
3610 }
3611
3612 let always_treat_brackets_as_autoclosed = buffer
3613 .language_settings_at(selection.start, cx)
3614 .always_treat_brackets_as_autoclosed;
3615
3616 if !always_treat_brackets_as_autoclosed {
3617 return selection;
3618 }
3619
3620 if let Some(scope) = buffer.language_scope_at(selection.start) {
3621 for (pair, enabled) in scope.brackets() {
3622 if !enabled || !pair.close {
3623 continue;
3624 }
3625
3626 if buffer.contains_str_at(selection.start, &pair.end) {
3627 let pair_start_len = pair.start.len();
3628 if buffer.contains_str_at(
3629 selection.start.saturating_sub(pair_start_len),
3630 &pair.start,
3631 ) {
3632 selection.start -= pair_start_len;
3633 selection.end += pair.end.len();
3634
3635 return selection;
3636 }
3637 }
3638 }
3639 }
3640
3641 selection
3642 })
3643 .collect();
3644
3645 drop(buffer);
3646 self.change_selections(None, window, cx, |selections| {
3647 selections.select(new_selections)
3648 });
3649 }
3650
3651 /// Iterate the given selections, and for each one, find the smallest surrounding
3652 /// autoclose region. This uses the ordering of the selections and the autoclose
3653 /// regions to avoid repeated comparisons.
3654 fn selections_with_autoclose_regions<'a, D: ToOffset + Clone>(
3655 &'a self,
3656 selections: impl IntoIterator<Item = Selection<D>>,
3657 buffer: &'a MultiBufferSnapshot,
3658 ) -> impl Iterator<Item = (Selection<D>, Option<&'a AutocloseRegion>)> {
3659 let mut i = 0;
3660 let mut regions = self.autoclose_regions.as_slice();
3661 selections.into_iter().map(move |selection| {
3662 let range = selection.start.to_offset(buffer)..selection.end.to_offset(buffer);
3663
3664 let mut enclosing = None;
3665 while let Some(pair_state) = regions.get(i) {
3666 if pair_state.range.end.to_offset(buffer) < range.start {
3667 regions = ®ions[i + 1..];
3668 i = 0;
3669 } else if pair_state.range.start.to_offset(buffer) > range.end {
3670 break;
3671 } else {
3672 if pair_state.selection_id == selection.id {
3673 enclosing = Some(pair_state);
3674 }
3675 i += 1;
3676 }
3677 }
3678
3679 (selection, enclosing)
3680 })
3681 }
3682
3683 /// Remove any autoclose regions that no longer contain their selection.
3684 fn invalidate_autoclose_regions(
3685 &mut self,
3686 mut selections: &[Selection<Anchor>],
3687 buffer: &MultiBufferSnapshot,
3688 ) {
3689 self.autoclose_regions.retain(|state| {
3690 let mut i = 0;
3691 while let Some(selection) = selections.get(i) {
3692 if selection.end.cmp(&state.range.start, buffer).is_lt() {
3693 selections = &selections[1..];
3694 continue;
3695 }
3696 if selection.start.cmp(&state.range.end, buffer).is_gt() {
3697 break;
3698 }
3699 if selection.id == state.selection_id {
3700 return true;
3701 } else {
3702 i += 1;
3703 }
3704 }
3705 false
3706 });
3707 }
3708
3709 fn completion_query(buffer: &MultiBufferSnapshot, position: impl ToOffset) -> Option<String> {
3710 let offset = position.to_offset(buffer);
3711 let (word_range, kind) = buffer.surrounding_word(offset, true);
3712 if offset > word_range.start && kind == Some(CharKind::Word) {
3713 Some(
3714 buffer
3715 .text_for_range(word_range.start..offset)
3716 .collect::<String>(),
3717 )
3718 } else {
3719 None
3720 }
3721 }
3722
3723 pub fn toggle_inlay_hints(
3724 &mut self,
3725 _: &ToggleInlayHints,
3726 _: &mut Window,
3727 cx: &mut Context<Self>,
3728 ) {
3729 self.refresh_inlay_hints(
3730 InlayHintRefreshReason::Toggle(!self.inlay_hints_enabled()),
3731 cx,
3732 );
3733 }
3734
3735 pub fn inlay_hints_enabled(&self) -> bool {
3736 self.inlay_hint_cache.enabled
3737 }
3738
3739 fn refresh_inlay_hints(&mut self, reason: InlayHintRefreshReason, cx: &mut Context<Self>) {
3740 if self.semantics_provider.is_none() || self.mode != EditorMode::Full {
3741 return;
3742 }
3743
3744 let reason_description = reason.description();
3745 let ignore_debounce = matches!(
3746 reason,
3747 InlayHintRefreshReason::SettingsChange(_)
3748 | InlayHintRefreshReason::Toggle(_)
3749 | InlayHintRefreshReason::ExcerptsRemoved(_)
3750 | InlayHintRefreshReason::ModifiersChanged(_)
3751 );
3752 let (invalidate_cache, required_languages) = match reason {
3753 InlayHintRefreshReason::ModifiersChanged(enabled) => {
3754 match self.inlay_hint_cache.modifiers_override(enabled) {
3755 Some(enabled) => {
3756 if enabled {
3757 (InvalidationStrategy::RefreshRequested, None)
3758 } else {
3759 self.splice_inlays(
3760 &self
3761 .visible_inlay_hints(cx)
3762 .iter()
3763 .map(|inlay| inlay.id)
3764 .collect::<Vec<InlayId>>(),
3765 Vec::new(),
3766 cx,
3767 );
3768 return;
3769 }
3770 }
3771 None => return,
3772 }
3773 }
3774 InlayHintRefreshReason::Toggle(enabled) => {
3775 if self.inlay_hint_cache.toggle(enabled) {
3776 if enabled {
3777 (InvalidationStrategy::RefreshRequested, None)
3778 } else {
3779 self.splice_inlays(
3780 &self
3781 .visible_inlay_hints(cx)
3782 .iter()
3783 .map(|inlay| inlay.id)
3784 .collect::<Vec<InlayId>>(),
3785 Vec::new(),
3786 cx,
3787 );
3788 return;
3789 }
3790 } else {
3791 return;
3792 }
3793 }
3794 InlayHintRefreshReason::SettingsChange(new_settings) => {
3795 match self.inlay_hint_cache.update_settings(
3796 &self.buffer,
3797 new_settings,
3798 self.visible_inlay_hints(cx),
3799 cx,
3800 ) {
3801 ControlFlow::Break(Some(InlaySplice {
3802 to_remove,
3803 to_insert,
3804 })) => {
3805 self.splice_inlays(&to_remove, to_insert, cx);
3806 return;
3807 }
3808 ControlFlow::Break(None) => return,
3809 ControlFlow::Continue(()) => (InvalidationStrategy::RefreshRequested, None),
3810 }
3811 }
3812 InlayHintRefreshReason::ExcerptsRemoved(excerpts_removed) => {
3813 if let Some(InlaySplice {
3814 to_remove,
3815 to_insert,
3816 }) = self.inlay_hint_cache.remove_excerpts(excerpts_removed)
3817 {
3818 self.splice_inlays(&to_remove, to_insert, cx);
3819 }
3820 return;
3821 }
3822 InlayHintRefreshReason::NewLinesShown => (InvalidationStrategy::None, None),
3823 InlayHintRefreshReason::BufferEdited(buffer_languages) => {
3824 (InvalidationStrategy::BufferEdited, Some(buffer_languages))
3825 }
3826 InlayHintRefreshReason::RefreshRequested => {
3827 (InvalidationStrategy::RefreshRequested, None)
3828 }
3829 };
3830
3831 if let Some(InlaySplice {
3832 to_remove,
3833 to_insert,
3834 }) = self.inlay_hint_cache.spawn_hint_refresh(
3835 reason_description,
3836 self.excerpts_for_inlay_hints_query(required_languages.as_ref(), cx),
3837 invalidate_cache,
3838 ignore_debounce,
3839 cx,
3840 ) {
3841 self.splice_inlays(&to_remove, to_insert, cx);
3842 }
3843 }
3844
3845 fn visible_inlay_hints(&self, cx: &Context<Editor>) -> Vec<Inlay> {
3846 self.display_map
3847 .read(cx)
3848 .current_inlays()
3849 .filter(move |inlay| matches!(inlay.id, InlayId::Hint(_)))
3850 .cloned()
3851 .collect()
3852 }
3853
3854 pub fn excerpts_for_inlay_hints_query(
3855 &self,
3856 restrict_to_languages: Option<&HashSet<Arc<Language>>>,
3857 cx: &mut Context<Editor>,
3858 ) -> HashMap<ExcerptId, (Entity<Buffer>, clock::Global, Range<usize>)> {
3859 let Some(project) = self.project.as_ref() else {
3860 return HashMap::default();
3861 };
3862 let project = project.read(cx);
3863 let multi_buffer = self.buffer().read(cx);
3864 let multi_buffer_snapshot = multi_buffer.snapshot(cx);
3865 let multi_buffer_visible_start = self
3866 .scroll_manager
3867 .anchor()
3868 .anchor
3869 .to_point(&multi_buffer_snapshot);
3870 let multi_buffer_visible_end = multi_buffer_snapshot.clip_point(
3871 multi_buffer_visible_start
3872 + Point::new(self.visible_line_count().unwrap_or(0.).ceil() as u32, 0),
3873 Bias::Left,
3874 );
3875 let multi_buffer_visible_range = multi_buffer_visible_start..multi_buffer_visible_end;
3876 multi_buffer_snapshot
3877 .range_to_buffer_ranges(multi_buffer_visible_range)
3878 .into_iter()
3879 .filter(|(_, excerpt_visible_range, _)| !excerpt_visible_range.is_empty())
3880 .filter_map(|(buffer, excerpt_visible_range, excerpt_id)| {
3881 let buffer_file = project::File::from_dyn(buffer.file())?;
3882 let buffer_worktree = project.worktree_for_id(buffer_file.worktree_id(cx), cx)?;
3883 let worktree_entry = buffer_worktree
3884 .read(cx)
3885 .entry_for_id(buffer_file.project_entry_id(cx)?)?;
3886 if worktree_entry.is_ignored {
3887 return None;
3888 }
3889
3890 let language = buffer.language()?;
3891 if let Some(restrict_to_languages) = restrict_to_languages {
3892 if !restrict_to_languages.contains(language) {
3893 return None;
3894 }
3895 }
3896 Some((
3897 excerpt_id,
3898 (
3899 multi_buffer.buffer(buffer.remote_id()).unwrap(),
3900 buffer.version().clone(),
3901 excerpt_visible_range,
3902 ),
3903 ))
3904 })
3905 .collect()
3906 }
3907
3908 pub fn text_layout_details(&self, window: &mut Window) -> TextLayoutDetails {
3909 TextLayoutDetails {
3910 text_system: window.text_system().clone(),
3911 editor_style: self.style.clone().unwrap(),
3912 rem_size: window.rem_size(),
3913 scroll_anchor: self.scroll_manager.anchor(),
3914 visible_rows: self.visible_line_count(),
3915 vertical_scroll_margin: self.scroll_manager.vertical_scroll_margin,
3916 }
3917 }
3918
3919 pub fn splice_inlays(
3920 &self,
3921 to_remove: &[InlayId],
3922 to_insert: Vec<Inlay>,
3923 cx: &mut Context<Self>,
3924 ) {
3925 self.display_map.update(cx, |display_map, cx| {
3926 display_map.splice_inlays(to_remove, to_insert, cx)
3927 });
3928 cx.notify();
3929 }
3930
3931 fn trigger_on_type_formatting(
3932 &self,
3933 input: String,
3934 window: &mut Window,
3935 cx: &mut Context<Self>,
3936 ) -> Option<Task<Result<()>>> {
3937 if input.len() != 1 {
3938 return None;
3939 }
3940
3941 let project = self.project.as_ref()?;
3942 let position = self.selections.newest_anchor().head();
3943 let (buffer, buffer_position) = self
3944 .buffer
3945 .read(cx)
3946 .text_anchor_for_position(position, cx)?;
3947
3948 let settings = language_settings::language_settings(
3949 buffer
3950 .read(cx)
3951 .language_at(buffer_position)
3952 .map(|l| l.name()),
3953 buffer.read(cx).file(),
3954 cx,
3955 );
3956 if !settings.use_on_type_format {
3957 return None;
3958 }
3959
3960 // OnTypeFormatting returns a list of edits, no need to pass them between Zed instances,
3961 // hence we do LSP request & edit on host side only — add formats to host's history.
3962 let push_to_lsp_host_history = true;
3963 // If this is not the host, append its history with new edits.
3964 let push_to_client_history = project.read(cx).is_via_collab();
3965
3966 let on_type_formatting = project.update(cx, |project, cx| {
3967 project.on_type_format(
3968 buffer.clone(),
3969 buffer_position,
3970 input,
3971 push_to_lsp_host_history,
3972 cx,
3973 )
3974 });
3975 Some(cx.spawn_in(window, |editor, mut cx| async move {
3976 if let Some(transaction) = on_type_formatting.await? {
3977 if push_to_client_history {
3978 buffer
3979 .update(&mut cx, |buffer, _| {
3980 buffer.push_transaction(transaction, Instant::now());
3981 })
3982 .ok();
3983 }
3984 editor.update(&mut cx, |editor, cx| {
3985 editor.refresh_document_highlights(cx);
3986 })?;
3987 }
3988 Ok(())
3989 }))
3990 }
3991
3992 pub fn show_word_completions(
3993 &mut self,
3994 _: &ShowWordCompletions,
3995 window: &mut Window,
3996 cx: &mut Context<Self>,
3997 ) {
3998 self.open_completions_menu(true, None, window, cx);
3999 }
4000
4001 pub fn show_completions(
4002 &mut self,
4003 options: &ShowCompletions,
4004 window: &mut Window,
4005 cx: &mut Context<Self>,
4006 ) {
4007 self.open_completions_menu(false, options.trigger.as_deref(), window, cx);
4008 }
4009
4010 fn open_completions_menu(
4011 &mut self,
4012 ignore_completion_provider: bool,
4013 trigger: Option<&str>,
4014 window: &mut Window,
4015 cx: &mut Context<Self>,
4016 ) {
4017 if self.pending_rename.is_some() {
4018 return;
4019 }
4020 if !self.snippet_stack.is_empty() && self.context_menu.borrow().as_ref().is_some() {
4021 return;
4022 }
4023
4024 let position = self.selections.newest_anchor().head();
4025 if position.diff_base_anchor.is_some() {
4026 return;
4027 }
4028 let (buffer, buffer_position) =
4029 if let Some(output) = self.buffer.read(cx).text_anchor_for_position(position, cx) {
4030 output
4031 } else {
4032 return;
4033 };
4034 let buffer_snapshot = buffer.read(cx).snapshot();
4035 let show_completion_documentation = buffer_snapshot
4036 .settings_at(buffer_position, cx)
4037 .show_completion_documentation;
4038
4039 let query = Self::completion_query(&self.buffer.read(cx).read(cx), position);
4040
4041 let trigger_kind = match trigger {
4042 Some(trigger) if buffer.read(cx).completion_triggers().contains(trigger) => {
4043 CompletionTriggerKind::TRIGGER_CHARACTER
4044 }
4045 _ => CompletionTriggerKind::INVOKED,
4046 };
4047 let completion_context = CompletionContext {
4048 trigger_character: trigger.and_then(|trigger| {
4049 if trigger_kind == CompletionTriggerKind::TRIGGER_CHARACTER {
4050 Some(String::from(trigger))
4051 } else {
4052 None
4053 }
4054 }),
4055 trigger_kind,
4056 };
4057
4058 let (old_range, word_kind) = buffer_snapshot.surrounding_word(buffer_position);
4059 let (old_range, word_to_exclude) = if word_kind == Some(CharKind::Word) {
4060 let word_to_exclude = buffer_snapshot
4061 .text_for_range(old_range.clone())
4062 .collect::<String>();
4063 (
4064 buffer_snapshot.anchor_before(old_range.start)
4065 ..buffer_snapshot.anchor_after(old_range.end),
4066 Some(word_to_exclude),
4067 )
4068 } else {
4069 (buffer_position..buffer_position, None)
4070 };
4071
4072 let completion_settings = language_settings(
4073 buffer_snapshot
4074 .language_at(buffer_position)
4075 .map(|language| language.name()),
4076 buffer_snapshot.file(),
4077 cx,
4078 )
4079 .completions;
4080
4081 // The document can be large, so stay in reasonable bounds when searching for words,
4082 // otherwise completion pop-up might be slow to appear.
4083 const WORD_LOOKUP_ROWS: u32 = 5_000;
4084 let buffer_row = text::ToPoint::to_point(&buffer_position, &buffer_snapshot).row;
4085 let min_word_search = buffer_snapshot.clip_point(
4086 Point::new(buffer_row.saturating_sub(WORD_LOOKUP_ROWS), 0),
4087 Bias::Left,
4088 );
4089 let max_word_search = buffer_snapshot.clip_point(
4090 Point::new(buffer_row + WORD_LOOKUP_ROWS, 0).min(buffer_snapshot.max_point()),
4091 Bias::Right,
4092 );
4093 let word_search_range = buffer_snapshot.point_to_offset(min_word_search)
4094 ..buffer_snapshot.point_to_offset(max_word_search);
4095
4096 let provider = self
4097 .completion_provider
4098 .as_ref()
4099 .filter(|_| !ignore_completion_provider);
4100 let skip_digits = query
4101 .as_ref()
4102 .map_or(true, |query| !query.chars().any(|c| c.is_digit(10)));
4103
4104 let (mut words, provided_completions) = match provider {
4105 Some(provider) => {
4106 let completions =
4107 provider.completions(&buffer, buffer_position, completion_context, window, cx);
4108
4109 let words = match completion_settings.words {
4110 WordsCompletionMode::Disabled => Task::ready(HashMap::default()),
4111 WordsCompletionMode::Enabled | WordsCompletionMode::Fallback => cx
4112 .background_spawn(async move {
4113 buffer_snapshot.words_in_range(WordsQuery {
4114 fuzzy_contents: None,
4115 range: word_search_range,
4116 skip_digits,
4117 })
4118 }),
4119 };
4120
4121 (words, completions)
4122 }
4123 None => (
4124 cx.background_spawn(async move {
4125 buffer_snapshot.words_in_range(WordsQuery {
4126 fuzzy_contents: None,
4127 range: word_search_range,
4128 skip_digits,
4129 })
4130 }),
4131 Task::ready(Ok(None)),
4132 ),
4133 };
4134
4135 let sort_completions = provider
4136 .as_ref()
4137 .map_or(true, |provider| provider.sort_completions());
4138
4139 let id = post_inc(&mut self.next_completion_id);
4140 let task = cx.spawn_in(window, |editor, mut cx| {
4141 async move {
4142 editor.update(&mut cx, |this, _| {
4143 this.completion_tasks.retain(|(task_id, _)| *task_id >= id);
4144 })?;
4145
4146 let mut completions = Vec::new();
4147 if let Some(provided_completions) = provided_completions.await.log_err().flatten() {
4148 completions.extend(provided_completions);
4149 if completion_settings.words == WordsCompletionMode::Fallback {
4150 words = Task::ready(HashMap::default());
4151 }
4152 }
4153
4154 let mut words = words.await;
4155 if let Some(word_to_exclude) = &word_to_exclude {
4156 words.remove(word_to_exclude);
4157 }
4158 for lsp_completion in &completions {
4159 words.remove(&lsp_completion.new_text);
4160 }
4161 completions.extend(words.into_iter().map(|(word, word_range)| Completion {
4162 old_range: old_range.clone(),
4163 new_text: word.clone(),
4164 label: CodeLabel::plain(word, None),
4165 documentation: None,
4166 source: CompletionSource::BufferWord {
4167 word_range,
4168 resolved: false,
4169 },
4170 confirm: None,
4171 }));
4172
4173 let menu = if completions.is_empty() {
4174 None
4175 } else {
4176 let mut menu = CompletionsMenu::new(
4177 id,
4178 sort_completions,
4179 show_completion_documentation,
4180 position,
4181 buffer.clone(),
4182 completions.into(),
4183 );
4184
4185 menu.filter(query.as_deref(), cx.background_executor().clone())
4186 .await;
4187
4188 menu.visible().then_some(menu)
4189 };
4190
4191 editor.update_in(&mut cx, |editor, window, cx| {
4192 match editor.context_menu.borrow().as_ref() {
4193 None => {}
4194 Some(CodeContextMenu::Completions(prev_menu)) => {
4195 if prev_menu.id > id {
4196 return;
4197 }
4198 }
4199 _ => return,
4200 }
4201
4202 if editor.focus_handle.is_focused(window) && menu.is_some() {
4203 let mut menu = menu.unwrap();
4204 menu.resolve_visible_completions(editor.completion_provider.as_deref(), cx);
4205
4206 *editor.context_menu.borrow_mut() =
4207 Some(CodeContextMenu::Completions(menu));
4208
4209 if editor.show_edit_predictions_in_menu() {
4210 editor.update_visible_inline_completion(window, cx);
4211 } else {
4212 editor.discard_inline_completion(false, cx);
4213 }
4214
4215 cx.notify();
4216 } else if editor.completion_tasks.len() <= 1 {
4217 // If there are no more completion tasks and the last menu was
4218 // empty, we should hide it.
4219 let was_hidden = editor.hide_context_menu(window, cx).is_none();
4220 // If it was already hidden and we don't show inline
4221 // completions in the menu, we should also show the
4222 // inline-completion when available.
4223 if was_hidden && editor.show_edit_predictions_in_menu() {
4224 editor.update_visible_inline_completion(window, cx);
4225 }
4226 }
4227 })?;
4228
4229 anyhow::Ok(())
4230 }
4231 .log_err()
4232 });
4233
4234 self.completion_tasks.push((id, task));
4235 }
4236
4237 pub fn confirm_completion(
4238 &mut self,
4239 action: &ConfirmCompletion,
4240 window: &mut Window,
4241 cx: &mut Context<Self>,
4242 ) -> Option<Task<Result<()>>> {
4243 self.do_completion(action.item_ix, CompletionIntent::Complete, window, cx)
4244 }
4245
4246 pub fn compose_completion(
4247 &mut self,
4248 action: &ComposeCompletion,
4249 window: &mut Window,
4250 cx: &mut Context<Self>,
4251 ) -> Option<Task<Result<()>>> {
4252 self.do_completion(action.item_ix, CompletionIntent::Compose, window, cx)
4253 }
4254
4255 fn do_completion(
4256 &mut self,
4257 item_ix: Option<usize>,
4258 intent: CompletionIntent,
4259 window: &mut Window,
4260 cx: &mut Context<Editor>,
4261 ) -> Option<Task<std::result::Result<(), anyhow::Error>>> {
4262 use language::ToOffset as _;
4263
4264 let completions_menu =
4265 if let CodeContextMenu::Completions(menu) = self.hide_context_menu(window, cx)? {
4266 menu
4267 } else {
4268 return None;
4269 };
4270
4271 let entries = completions_menu.entries.borrow();
4272 let mat = entries.get(item_ix.unwrap_or(completions_menu.selected_item))?;
4273 if self.show_edit_predictions_in_menu() {
4274 self.discard_inline_completion(true, cx);
4275 }
4276 let candidate_id = mat.candidate_id;
4277 drop(entries);
4278
4279 let buffer_handle = completions_menu.buffer;
4280 let completion = completions_menu
4281 .completions
4282 .borrow()
4283 .get(candidate_id)?
4284 .clone();
4285 cx.stop_propagation();
4286
4287 let snippet;
4288 let text;
4289
4290 if completion.is_snippet() {
4291 snippet = Some(Snippet::parse(&completion.new_text).log_err()?);
4292 text = snippet.as_ref().unwrap().text.clone();
4293 } else {
4294 snippet = None;
4295 text = completion.new_text.clone();
4296 };
4297 let selections = self.selections.all::<usize>(cx);
4298 let buffer = buffer_handle.read(cx);
4299 let old_range = completion.old_range.to_offset(buffer);
4300 let old_text = buffer.text_for_range(old_range.clone()).collect::<String>();
4301
4302 let newest_selection = self.selections.newest_anchor();
4303 if newest_selection.start.buffer_id != Some(buffer_handle.read(cx).remote_id()) {
4304 return None;
4305 }
4306
4307 let lookbehind = newest_selection
4308 .start
4309 .text_anchor
4310 .to_offset(buffer)
4311 .saturating_sub(old_range.start);
4312 let lookahead = old_range
4313 .end
4314 .saturating_sub(newest_selection.end.text_anchor.to_offset(buffer));
4315 let mut common_prefix_len = old_text
4316 .bytes()
4317 .zip(text.bytes())
4318 .take_while(|(a, b)| a == b)
4319 .count();
4320
4321 let snapshot = self.buffer.read(cx).snapshot(cx);
4322 let mut range_to_replace: Option<Range<isize>> = None;
4323 let mut ranges = Vec::new();
4324 let mut linked_edits = HashMap::<_, Vec<_>>::default();
4325 for selection in &selections {
4326 if snapshot.contains_str_at(selection.start.saturating_sub(lookbehind), &old_text) {
4327 let start = selection.start.saturating_sub(lookbehind);
4328 let end = selection.end + lookahead;
4329 if selection.id == newest_selection.id {
4330 range_to_replace = Some(
4331 ((start + common_prefix_len) as isize - selection.start as isize)
4332 ..(end as isize - selection.start as isize),
4333 );
4334 }
4335 ranges.push(start + common_prefix_len..end);
4336 } else {
4337 common_prefix_len = 0;
4338 ranges.clear();
4339 ranges.extend(selections.iter().map(|s| {
4340 if s.id == newest_selection.id {
4341 range_to_replace = Some(
4342 old_range.start.to_offset_utf16(&snapshot).0 as isize
4343 - selection.start as isize
4344 ..old_range.end.to_offset_utf16(&snapshot).0 as isize
4345 - selection.start as isize,
4346 );
4347 old_range.clone()
4348 } else {
4349 s.start..s.end
4350 }
4351 }));
4352 break;
4353 }
4354 if !self.linked_edit_ranges.is_empty() {
4355 let start_anchor = snapshot.anchor_before(selection.head());
4356 let end_anchor = snapshot.anchor_after(selection.tail());
4357 if let Some(ranges) = self
4358 .linked_editing_ranges_for(start_anchor.text_anchor..end_anchor.text_anchor, cx)
4359 {
4360 for (buffer, edits) in ranges {
4361 linked_edits.entry(buffer.clone()).or_default().extend(
4362 edits
4363 .into_iter()
4364 .map(|range| (range, text[common_prefix_len..].to_owned())),
4365 );
4366 }
4367 }
4368 }
4369 }
4370 let text = &text[common_prefix_len..];
4371
4372 cx.emit(EditorEvent::InputHandled {
4373 utf16_range_to_replace: range_to_replace,
4374 text: text.into(),
4375 });
4376
4377 self.transact(window, cx, |this, window, cx| {
4378 if let Some(mut snippet) = snippet {
4379 snippet.text = text.to_string();
4380 for tabstop in snippet
4381 .tabstops
4382 .iter_mut()
4383 .flat_map(|tabstop| tabstop.ranges.iter_mut())
4384 {
4385 tabstop.start -= common_prefix_len as isize;
4386 tabstop.end -= common_prefix_len as isize;
4387 }
4388
4389 this.insert_snippet(&ranges, snippet, window, cx).log_err();
4390 } else {
4391 this.buffer.update(cx, |buffer, cx| {
4392 buffer.edit(
4393 ranges.iter().map(|range| (range.clone(), text)),
4394 this.autoindent_mode.clone(),
4395 cx,
4396 );
4397 });
4398 }
4399 for (buffer, edits) in linked_edits {
4400 buffer.update(cx, |buffer, cx| {
4401 let snapshot = buffer.snapshot();
4402 let edits = edits
4403 .into_iter()
4404 .map(|(range, text)| {
4405 use text::ToPoint as TP;
4406 let end_point = TP::to_point(&range.end, &snapshot);
4407 let start_point = TP::to_point(&range.start, &snapshot);
4408 (start_point..end_point, text)
4409 })
4410 .sorted_by_key(|(range, _)| range.start)
4411 .collect::<Vec<_>>();
4412 buffer.edit(edits, None, cx);
4413 })
4414 }
4415
4416 this.refresh_inline_completion(true, false, window, cx);
4417 });
4418
4419 let show_new_completions_on_confirm = completion
4420 .confirm
4421 .as_ref()
4422 .map_or(false, |confirm| confirm(intent, window, cx));
4423 if show_new_completions_on_confirm {
4424 self.show_completions(&ShowCompletions { trigger: None }, window, cx);
4425 }
4426
4427 let provider = self.completion_provider.as_ref()?;
4428 drop(completion);
4429 let apply_edits = provider.apply_additional_edits_for_completion(
4430 buffer_handle,
4431 completions_menu.completions.clone(),
4432 candidate_id,
4433 true,
4434 cx,
4435 );
4436
4437 let editor_settings = EditorSettings::get_global(cx);
4438 if editor_settings.show_signature_help_after_edits || editor_settings.auto_signature_help {
4439 // After the code completion is finished, users often want to know what signatures are needed.
4440 // so we should automatically call signature_help
4441 self.show_signature_help(&ShowSignatureHelp, window, cx);
4442 }
4443
4444 Some(cx.foreground_executor().spawn(async move {
4445 apply_edits.await?;
4446 Ok(())
4447 }))
4448 }
4449
4450 pub fn toggle_code_actions(
4451 &mut self,
4452 action: &ToggleCodeActions,
4453 window: &mut Window,
4454 cx: &mut Context<Self>,
4455 ) {
4456 let mut context_menu = self.context_menu.borrow_mut();
4457 if let Some(CodeContextMenu::CodeActions(code_actions)) = context_menu.as_ref() {
4458 if code_actions.deployed_from_indicator == action.deployed_from_indicator {
4459 // Toggle if we're selecting the same one
4460 *context_menu = None;
4461 cx.notify();
4462 return;
4463 } else {
4464 // Otherwise, clear it and start a new one
4465 *context_menu = None;
4466 cx.notify();
4467 }
4468 }
4469 drop(context_menu);
4470 let snapshot = self.snapshot(window, cx);
4471 let deployed_from_indicator = action.deployed_from_indicator;
4472 let mut task = self.code_actions_task.take();
4473 let action = action.clone();
4474 cx.spawn_in(window, |editor, mut cx| async move {
4475 while let Some(prev_task) = task {
4476 prev_task.await.log_err();
4477 task = editor.update(&mut cx, |this, _| this.code_actions_task.take())?;
4478 }
4479
4480 let spawned_test_task = editor.update_in(&mut cx, |editor, window, cx| {
4481 if editor.focus_handle.is_focused(window) {
4482 let multibuffer_point = action
4483 .deployed_from_indicator
4484 .map(|row| DisplayPoint::new(row, 0).to_point(&snapshot))
4485 .unwrap_or_else(|| editor.selections.newest::<Point>(cx).head());
4486 let (buffer, buffer_row) = snapshot
4487 .buffer_snapshot
4488 .buffer_line_for_row(MultiBufferRow(multibuffer_point.row))
4489 .and_then(|(buffer_snapshot, range)| {
4490 editor
4491 .buffer
4492 .read(cx)
4493 .buffer(buffer_snapshot.remote_id())
4494 .map(|buffer| (buffer, range.start.row))
4495 })?;
4496 let (_, code_actions) = editor
4497 .available_code_actions
4498 .clone()
4499 .and_then(|(location, code_actions)| {
4500 let snapshot = location.buffer.read(cx).snapshot();
4501 let point_range = location.range.to_point(&snapshot);
4502 let point_range = point_range.start.row..=point_range.end.row;
4503 if point_range.contains(&buffer_row) {
4504 Some((location, code_actions))
4505 } else {
4506 None
4507 }
4508 })
4509 .unzip();
4510 let buffer_id = buffer.read(cx).remote_id();
4511 let tasks = editor
4512 .tasks
4513 .get(&(buffer_id, buffer_row))
4514 .map(|t| Arc::new(t.to_owned()));
4515 if tasks.is_none() && code_actions.is_none() {
4516 return None;
4517 }
4518
4519 editor.completion_tasks.clear();
4520 editor.discard_inline_completion(false, cx);
4521 let task_context =
4522 tasks
4523 .as_ref()
4524 .zip(editor.project.clone())
4525 .map(|(tasks, project)| {
4526 Self::build_tasks_context(&project, &buffer, buffer_row, tasks, cx)
4527 });
4528
4529 Some(cx.spawn_in(window, |editor, mut cx| async move {
4530 let task_context = match task_context {
4531 Some(task_context) => task_context.await,
4532 None => None,
4533 };
4534 let resolved_tasks =
4535 tasks.zip(task_context).map(|(tasks, task_context)| {
4536 Rc::new(ResolvedTasks {
4537 templates: tasks.resolve(&task_context).collect(),
4538 position: snapshot.buffer_snapshot.anchor_before(Point::new(
4539 multibuffer_point.row,
4540 tasks.column,
4541 )),
4542 })
4543 });
4544 let spawn_straight_away = resolved_tasks
4545 .as_ref()
4546 .map_or(false, |tasks| tasks.templates.len() == 1)
4547 && code_actions
4548 .as_ref()
4549 .map_or(true, |actions| actions.is_empty());
4550 if let Ok(task) = editor.update_in(&mut cx, |editor, window, cx| {
4551 *editor.context_menu.borrow_mut() =
4552 Some(CodeContextMenu::CodeActions(CodeActionsMenu {
4553 buffer,
4554 actions: CodeActionContents {
4555 tasks: resolved_tasks,
4556 actions: code_actions,
4557 },
4558 selected_item: Default::default(),
4559 scroll_handle: UniformListScrollHandle::default(),
4560 deployed_from_indicator,
4561 }));
4562 if spawn_straight_away {
4563 if let Some(task) = editor.confirm_code_action(
4564 &ConfirmCodeAction { item_ix: Some(0) },
4565 window,
4566 cx,
4567 ) {
4568 cx.notify();
4569 return task;
4570 }
4571 }
4572 cx.notify();
4573 Task::ready(Ok(()))
4574 }) {
4575 task.await
4576 } else {
4577 Ok(())
4578 }
4579 }))
4580 } else {
4581 Some(Task::ready(Ok(())))
4582 }
4583 })?;
4584 if let Some(task) = spawned_test_task {
4585 task.await?;
4586 }
4587
4588 Ok::<_, anyhow::Error>(())
4589 })
4590 .detach_and_log_err(cx);
4591 }
4592
4593 pub fn confirm_code_action(
4594 &mut self,
4595 action: &ConfirmCodeAction,
4596 window: &mut Window,
4597 cx: &mut Context<Self>,
4598 ) -> Option<Task<Result<()>>> {
4599 let actions_menu =
4600 if let CodeContextMenu::CodeActions(menu) = self.hide_context_menu(window, cx)? {
4601 menu
4602 } else {
4603 return None;
4604 };
4605 let action_ix = action.item_ix.unwrap_or(actions_menu.selected_item);
4606 let action = actions_menu.actions.get(action_ix)?;
4607 let title = action.label();
4608 let buffer = actions_menu.buffer;
4609 let workspace = self.workspace()?;
4610
4611 match action {
4612 CodeActionsItem::Task(task_source_kind, resolved_task) => {
4613 workspace.update(cx, |workspace, cx| {
4614 workspace::tasks::schedule_resolved_task(
4615 workspace,
4616 task_source_kind,
4617 resolved_task,
4618 false,
4619 cx,
4620 );
4621
4622 Some(Task::ready(Ok(())))
4623 })
4624 }
4625 CodeActionsItem::CodeAction {
4626 excerpt_id,
4627 action,
4628 provider,
4629 } => {
4630 let apply_code_action =
4631 provider.apply_code_action(buffer, action, excerpt_id, true, window, cx);
4632 let workspace = workspace.downgrade();
4633 Some(cx.spawn_in(window, |editor, cx| async move {
4634 let project_transaction = apply_code_action.await?;
4635 Self::open_project_transaction(
4636 &editor,
4637 workspace,
4638 project_transaction,
4639 title,
4640 cx,
4641 )
4642 .await
4643 }))
4644 }
4645 }
4646 }
4647
4648 pub async fn open_project_transaction(
4649 this: &WeakEntity<Editor>,
4650 workspace: WeakEntity<Workspace>,
4651 transaction: ProjectTransaction,
4652 title: String,
4653 mut cx: AsyncWindowContext,
4654 ) -> Result<()> {
4655 let mut entries = transaction.0.into_iter().collect::<Vec<_>>();
4656 cx.update(|_, cx| {
4657 entries.sort_unstable_by_key(|(buffer, _)| {
4658 buffer.read(cx).file().map(|f| f.path().clone())
4659 });
4660 })?;
4661
4662 // If the project transaction's edits are all contained within this editor, then
4663 // avoid opening a new editor to display them.
4664
4665 if let Some((buffer, transaction)) = entries.first() {
4666 if entries.len() == 1 {
4667 let excerpt = this.update(&mut cx, |editor, cx| {
4668 editor
4669 .buffer()
4670 .read(cx)
4671 .excerpt_containing(editor.selections.newest_anchor().head(), cx)
4672 })?;
4673 if let Some((_, excerpted_buffer, excerpt_range)) = excerpt {
4674 if excerpted_buffer == *buffer {
4675 let all_edits_within_excerpt = buffer.read_with(&cx, |buffer, _| {
4676 let excerpt_range = excerpt_range.to_offset(buffer);
4677 buffer
4678 .edited_ranges_for_transaction::<usize>(transaction)
4679 .all(|range| {
4680 excerpt_range.start <= range.start
4681 && excerpt_range.end >= range.end
4682 })
4683 })?;
4684
4685 if all_edits_within_excerpt {
4686 return Ok(());
4687 }
4688 }
4689 }
4690 }
4691 } else {
4692 return Ok(());
4693 }
4694
4695 let mut ranges_to_highlight = Vec::new();
4696 let excerpt_buffer = cx.new(|cx| {
4697 let mut multibuffer = MultiBuffer::new(Capability::ReadWrite).with_title(title);
4698 for (buffer_handle, transaction) in &entries {
4699 let buffer = buffer_handle.read(cx);
4700 ranges_to_highlight.extend(
4701 multibuffer.push_excerpts_with_context_lines(
4702 buffer_handle.clone(),
4703 buffer
4704 .edited_ranges_for_transaction::<usize>(transaction)
4705 .collect(),
4706 DEFAULT_MULTIBUFFER_CONTEXT,
4707 cx,
4708 ),
4709 );
4710 }
4711 multibuffer.push_transaction(entries.iter().map(|(b, t)| (b, t)), cx);
4712 multibuffer
4713 })?;
4714
4715 workspace.update_in(&mut cx, |workspace, window, cx| {
4716 let project = workspace.project().clone();
4717 let editor =
4718 cx.new(|cx| Editor::for_multibuffer(excerpt_buffer, Some(project), window, cx));
4719 workspace.add_item_to_active_pane(Box::new(editor.clone()), None, true, window, cx);
4720 editor.update(cx, |editor, cx| {
4721 editor.highlight_background::<Self>(
4722 &ranges_to_highlight,
4723 |theme| theme.editor_highlighted_line_background,
4724 cx,
4725 );
4726 });
4727 })?;
4728
4729 Ok(())
4730 }
4731
4732 pub fn clear_code_action_providers(&mut self) {
4733 self.code_action_providers.clear();
4734 self.available_code_actions.take();
4735 }
4736
4737 pub fn add_code_action_provider(
4738 &mut self,
4739 provider: Rc<dyn CodeActionProvider>,
4740 window: &mut Window,
4741 cx: &mut Context<Self>,
4742 ) {
4743 if self
4744 .code_action_providers
4745 .iter()
4746 .any(|existing_provider| existing_provider.id() == provider.id())
4747 {
4748 return;
4749 }
4750
4751 self.code_action_providers.push(provider);
4752 self.refresh_code_actions(window, cx);
4753 }
4754
4755 pub fn remove_code_action_provider(
4756 &mut self,
4757 id: Arc<str>,
4758 window: &mut Window,
4759 cx: &mut Context<Self>,
4760 ) {
4761 self.code_action_providers
4762 .retain(|provider| provider.id() != id);
4763 self.refresh_code_actions(window, cx);
4764 }
4765
4766 fn refresh_code_actions(&mut self, window: &mut Window, cx: &mut Context<Self>) -> Option<()> {
4767 let buffer = self.buffer.read(cx);
4768 let newest_selection = self.selections.newest_anchor().clone();
4769 if newest_selection.head().diff_base_anchor.is_some() {
4770 return None;
4771 }
4772 let (start_buffer, start) = buffer.text_anchor_for_position(newest_selection.start, cx)?;
4773 let (end_buffer, end) = buffer.text_anchor_for_position(newest_selection.end, cx)?;
4774 if start_buffer != end_buffer {
4775 return None;
4776 }
4777
4778 self.code_actions_task = Some(cx.spawn_in(window, |this, mut cx| async move {
4779 cx.background_executor()
4780 .timer(CODE_ACTIONS_DEBOUNCE_TIMEOUT)
4781 .await;
4782
4783 let (providers, tasks) = this.update_in(&mut cx, |this, window, cx| {
4784 let providers = this.code_action_providers.clone();
4785 let tasks = this
4786 .code_action_providers
4787 .iter()
4788 .map(|provider| provider.code_actions(&start_buffer, start..end, window, cx))
4789 .collect::<Vec<_>>();
4790 (providers, tasks)
4791 })?;
4792
4793 let mut actions = Vec::new();
4794 for (provider, provider_actions) in
4795 providers.into_iter().zip(future::join_all(tasks).await)
4796 {
4797 if let Some(provider_actions) = provider_actions.log_err() {
4798 actions.extend(provider_actions.into_iter().map(|action| {
4799 AvailableCodeAction {
4800 excerpt_id: newest_selection.start.excerpt_id,
4801 action,
4802 provider: provider.clone(),
4803 }
4804 }));
4805 }
4806 }
4807
4808 this.update(&mut cx, |this, cx| {
4809 this.available_code_actions = if actions.is_empty() {
4810 None
4811 } else {
4812 Some((
4813 Location {
4814 buffer: start_buffer,
4815 range: start..end,
4816 },
4817 actions.into(),
4818 ))
4819 };
4820 cx.notify();
4821 })
4822 }));
4823 None
4824 }
4825
4826 fn start_inline_blame_timer(&mut self, window: &mut Window, cx: &mut Context<Self>) {
4827 if let Some(delay) = ProjectSettings::get_global(cx).git.inline_blame_delay() {
4828 self.show_git_blame_inline = false;
4829
4830 self.show_git_blame_inline_delay_task =
4831 Some(cx.spawn_in(window, |this, mut cx| async move {
4832 cx.background_executor().timer(delay).await;
4833
4834 this.update(&mut cx, |this, cx| {
4835 this.show_git_blame_inline = true;
4836 cx.notify();
4837 })
4838 .log_err();
4839 }));
4840 }
4841 }
4842
4843 fn refresh_document_highlights(&mut self, cx: &mut Context<Self>) -> Option<()> {
4844 if self.pending_rename.is_some() {
4845 return None;
4846 }
4847
4848 let provider = self.semantics_provider.clone()?;
4849 let buffer = self.buffer.read(cx);
4850 let newest_selection = self.selections.newest_anchor().clone();
4851 let cursor_position = newest_selection.head();
4852 let (cursor_buffer, cursor_buffer_position) =
4853 buffer.text_anchor_for_position(cursor_position, cx)?;
4854 let (tail_buffer, _) = buffer.text_anchor_for_position(newest_selection.tail(), cx)?;
4855 if cursor_buffer != tail_buffer {
4856 return None;
4857 }
4858 let debounce = EditorSettings::get_global(cx).lsp_highlight_debounce;
4859 self.document_highlights_task = Some(cx.spawn(|this, mut cx| async move {
4860 cx.background_executor()
4861 .timer(Duration::from_millis(debounce))
4862 .await;
4863
4864 let highlights = if let Some(highlights) = cx
4865 .update(|cx| {
4866 provider.document_highlights(&cursor_buffer, cursor_buffer_position, cx)
4867 })
4868 .ok()
4869 .flatten()
4870 {
4871 highlights.await.log_err()
4872 } else {
4873 None
4874 };
4875
4876 if let Some(highlights) = highlights {
4877 this.update(&mut cx, |this, cx| {
4878 if this.pending_rename.is_some() {
4879 return;
4880 }
4881
4882 let buffer_id = cursor_position.buffer_id;
4883 let buffer = this.buffer.read(cx);
4884 if !buffer
4885 .text_anchor_for_position(cursor_position, cx)
4886 .map_or(false, |(buffer, _)| buffer == cursor_buffer)
4887 {
4888 return;
4889 }
4890
4891 let cursor_buffer_snapshot = cursor_buffer.read(cx);
4892 let mut write_ranges = Vec::new();
4893 let mut read_ranges = Vec::new();
4894 for highlight in highlights {
4895 for (excerpt_id, excerpt_range) in
4896 buffer.excerpts_for_buffer(cursor_buffer.read(cx).remote_id(), cx)
4897 {
4898 let start = highlight
4899 .range
4900 .start
4901 .max(&excerpt_range.context.start, cursor_buffer_snapshot);
4902 let end = highlight
4903 .range
4904 .end
4905 .min(&excerpt_range.context.end, cursor_buffer_snapshot);
4906 if start.cmp(&end, cursor_buffer_snapshot).is_ge() {
4907 continue;
4908 }
4909
4910 let range = Anchor {
4911 buffer_id,
4912 excerpt_id,
4913 text_anchor: start,
4914 diff_base_anchor: None,
4915 }..Anchor {
4916 buffer_id,
4917 excerpt_id,
4918 text_anchor: end,
4919 diff_base_anchor: None,
4920 };
4921 if highlight.kind == lsp::DocumentHighlightKind::WRITE {
4922 write_ranges.push(range);
4923 } else {
4924 read_ranges.push(range);
4925 }
4926 }
4927 }
4928
4929 this.highlight_background::<DocumentHighlightRead>(
4930 &read_ranges,
4931 |theme| theme.editor_document_highlight_read_background,
4932 cx,
4933 );
4934 this.highlight_background::<DocumentHighlightWrite>(
4935 &write_ranges,
4936 |theme| theme.editor_document_highlight_write_background,
4937 cx,
4938 );
4939 cx.notify();
4940 })
4941 .log_err();
4942 }
4943 }));
4944 None
4945 }
4946
4947 pub fn refresh_selected_text_highlights(
4948 &mut self,
4949 window: &mut Window,
4950 cx: &mut Context<Editor>,
4951 ) {
4952 if matches!(self.mode, EditorMode::SingleLine { .. }) {
4953 return;
4954 }
4955 self.selection_highlight_task.take();
4956 if !EditorSettings::get_global(cx).selection_highlight {
4957 self.clear_background_highlights::<SelectedTextHighlight>(cx);
4958 return;
4959 }
4960 if self.selections.count() != 1 || self.selections.line_mode {
4961 self.clear_background_highlights::<SelectedTextHighlight>(cx);
4962 return;
4963 }
4964 let selection = self.selections.newest::<Point>(cx);
4965 if selection.is_empty() || selection.start.row != selection.end.row {
4966 self.clear_background_highlights::<SelectedTextHighlight>(cx);
4967 return;
4968 }
4969 let debounce = EditorSettings::get_global(cx).selection_highlight_debounce;
4970 self.selection_highlight_task = Some(cx.spawn_in(window, |editor, mut cx| async move {
4971 cx.background_executor()
4972 .timer(Duration::from_millis(debounce))
4973 .await;
4974 let Some(Some(matches_task)) = editor
4975 .update_in(&mut cx, |editor, _, cx| {
4976 if editor.selections.count() != 1 || editor.selections.line_mode {
4977 editor.clear_background_highlights::<SelectedTextHighlight>(cx);
4978 return None;
4979 }
4980 let selection = editor.selections.newest::<Point>(cx);
4981 if selection.is_empty() || selection.start.row != selection.end.row {
4982 editor.clear_background_highlights::<SelectedTextHighlight>(cx);
4983 return None;
4984 }
4985 let buffer = editor.buffer().read(cx).snapshot(cx);
4986 let query = buffer.text_for_range(selection.range()).collect::<String>();
4987 if query.trim().is_empty() {
4988 editor.clear_background_highlights::<SelectedTextHighlight>(cx);
4989 return None;
4990 }
4991 Some(cx.background_spawn(async move {
4992 let mut ranges = Vec::new();
4993 let selection_anchors = selection.range().to_anchors(&buffer);
4994 for range in [buffer.anchor_before(0)..buffer.anchor_after(buffer.len())] {
4995 for (search_buffer, search_range, excerpt_id) in
4996 buffer.range_to_buffer_ranges(range)
4997 {
4998 ranges.extend(
4999 project::search::SearchQuery::text(
5000 query.clone(),
5001 false,
5002 false,
5003 false,
5004 Default::default(),
5005 Default::default(),
5006 None,
5007 )
5008 .unwrap()
5009 .search(search_buffer, Some(search_range.clone()))
5010 .await
5011 .into_iter()
5012 .filter_map(
5013 |match_range| {
5014 let start = search_buffer.anchor_after(
5015 search_range.start + match_range.start,
5016 );
5017 let end = search_buffer.anchor_before(
5018 search_range.start + match_range.end,
5019 );
5020 let range = Anchor::range_in_buffer(
5021 excerpt_id,
5022 search_buffer.remote_id(),
5023 start..end,
5024 );
5025 (range != selection_anchors).then_some(range)
5026 },
5027 ),
5028 );
5029 }
5030 }
5031 ranges
5032 }))
5033 })
5034 .log_err()
5035 else {
5036 return;
5037 };
5038 let matches = matches_task.await;
5039 editor
5040 .update_in(&mut cx, |editor, _, cx| {
5041 editor.clear_background_highlights::<SelectedTextHighlight>(cx);
5042 if !matches.is_empty() {
5043 editor.highlight_background::<SelectedTextHighlight>(
5044 &matches,
5045 |theme| theme.editor_document_highlight_bracket_background,
5046 cx,
5047 )
5048 }
5049 })
5050 .log_err();
5051 }));
5052 }
5053
5054 pub fn refresh_inline_completion(
5055 &mut self,
5056 debounce: bool,
5057 user_requested: bool,
5058 window: &mut Window,
5059 cx: &mut Context<Self>,
5060 ) -> Option<()> {
5061 let provider = self.edit_prediction_provider()?;
5062 let cursor = self.selections.newest_anchor().head();
5063 let (buffer, cursor_buffer_position) =
5064 self.buffer.read(cx).text_anchor_for_position(cursor, cx)?;
5065
5066 if !self.edit_predictions_enabled_in_buffer(&buffer, cursor_buffer_position, cx) {
5067 self.discard_inline_completion(false, cx);
5068 return None;
5069 }
5070
5071 if !user_requested
5072 && (!self.should_show_edit_predictions()
5073 || !self.is_focused(window)
5074 || buffer.read(cx).is_empty())
5075 {
5076 self.discard_inline_completion(false, cx);
5077 return None;
5078 }
5079
5080 self.update_visible_inline_completion(window, cx);
5081 provider.refresh(
5082 self.project.clone(),
5083 buffer,
5084 cursor_buffer_position,
5085 debounce,
5086 cx,
5087 );
5088 Some(())
5089 }
5090
5091 fn show_edit_predictions_in_menu(&self) -> bool {
5092 match self.edit_prediction_settings {
5093 EditPredictionSettings::Disabled => false,
5094 EditPredictionSettings::Enabled { show_in_menu, .. } => show_in_menu,
5095 }
5096 }
5097
5098 pub fn edit_predictions_enabled(&self) -> bool {
5099 match self.edit_prediction_settings {
5100 EditPredictionSettings::Disabled => false,
5101 EditPredictionSettings::Enabled { .. } => true,
5102 }
5103 }
5104
5105 fn edit_prediction_requires_modifier(&self) -> bool {
5106 match self.edit_prediction_settings {
5107 EditPredictionSettings::Disabled => false,
5108 EditPredictionSettings::Enabled {
5109 preview_requires_modifier,
5110 ..
5111 } => preview_requires_modifier,
5112 }
5113 }
5114
5115 pub fn update_edit_prediction_settings(&mut self, cx: &mut Context<Self>) {
5116 if self.edit_prediction_provider.is_none() {
5117 self.edit_prediction_settings = EditPredictionSettings::Disabled;
5118 } else {
5119 let selection = self.selections.newest_anchor();
5120 let cursor = selection.head();
5121
5122 if let Some((buffer, cursor_buffer_position)) =
5123 self.buffer.read(cx).text_anchor_for_position(cursor, cx)
5124 {
5125 self.edit_prediction_settings =
5126 self.edit_prediction_settings_at_position(&buffer, cursor_buffer_position, cx);
5127 }
5128 }
5129 }
5130
5131 fn edit_prediction_settings_at_position(
5132 &self,
5133 buffer: &Entity<Buffer>,
5134 buffer_position: language::Anchor,
5135 cx: &App,
5136 ) -> EditPredictionSettings {
5137 if self.mode != EditorMode::Full
5138 || !self.show_inline_completions_override.unwrap_or(true)
5139 || self.inline_completions_disabled_in_scope(buffer, buffer_position, cx)
5140 {
5141 return EditPredictionSettings::Disabled;
5142 }
5143
5144 let buffer = buffer.read(cx);
5145
5146 let file = buffer.file();
5147
5148 if !language_settings(buffer.language().map(|l| l.name()), file, cx).show_edit_predictions {
5149 return EditPredictionSettings::Disabled;
5150 };
5151
5152 let by_provider = matches!(
5153 self.menu_inline_completions_policy,
5154 MenuInlineCompletionsPolicy::ByProvider
5155 );
5156
5157 let show_in_menu = by_provider
5158 && self
5159 .edit_prediction_provider
5160 .as_ref()
5161 .map_or(false, |provider| {
5162 provider.provider.show_completions_in_menu()
5163 });
5164
5165 let preview_requires_modifier =
5166 all_language_settings(file, cx).edit_predictions_mode() == EditPredictionsMode::Subtle;
5167
5168 EditPredictionSettings::Enabled {
5169 show_in_menu,
5170 preview_requires_modifier,
5171 }
5172 }
5173
5174 fn should_show_edit_predictions(&self) -> bool {
5175 self.snippet_stack.is_empty() && self.edit_predictions_enabled()
5176 }
5177
5178 pub fn edit_prediction_preview_is_active(&self) -> bool {
5179 matches!(
5180 self.edit_prediction_preview,
5181 EditPredictionPreview::Active { .. }
5182 )
5183 }
5184
5185 pub fn edit_predictions_enabled_at_cursor(&self, cx: &App) -> bool {
5186 let cursor = self.selections.newest_anchor().head();
5187 if let Some((buffer, cursor_position)) =
5188 self.buffer.read(cx).text_anchor_for_position(cursor, cx)
5189 {
5190 self.edit_predictions_enabled_in_buffer(&buffer, cursor_position, cx)
5191 } else {
5192 false
5193 }
5194 }
5195
5196 fn edit_predictions_enabled_in_buffer(
5197 &self,
5198 buffer: &Entity<Buffer>,
5199 buffer_position: language::Anchor,
5200 cx: &App,
5201 ) -> bool {
5202 maybe!({
5203 let provider = self.edit_prediction_provider()?;
5204 if !provider.is_enabled(&buffer, buffer_position, cx) {
5205 return Some(false);
5206 }
5207 let buffer = buffer.read(cx);
5208 let Some(file) = buffer.file() else {
5209 return Some(true);
5210 };
5211 let settings = all_language_settings(Some(file), cx);
5212 Some(settings.edit_predictions_enabled_for_file(file, cx))
5213 })
5214 .unwrap_or(false)
5215 }
5216
5217 fn cycle_inline_completion(
5218 &mut self,
5219 direction: Direction,
5220 window: &mut Window,
5221 cx: &mut Context<Self>,
5222 ) -> Option<()> {
5223 let provider = self.edit_prediction_provider()?;
5224 let cursor = self.selections.newest_anchor().head();
5225 let (buffer, cursor_buffer_position) =
5226 self.buffer.read(cx).text_anchor_for_position(cursor, cx)?;
5227 if self.inline_completions_hidden_for_vim_mode || !self.should_show_edit_predictions() {
5228 return None;
5229 }
5230
5231 provider.cycle(buffer, cursor_buffer_position, direction, cx);
5232 self.update_visible_inline_completion(window, cx);
5233
5234 Some(())
5235 }
5236
5237 pub fn show_inline_completion(
5238 &mut self,
5239 _: &ShowEditPrediction,
5240 window: &mut Window,
5241 cx: &mut Context<Self>,
5242 ) {
5243 if !self.has_active_inline_completion() {
5244 self.refresh_inline_completion(false, true, window, cx);
5245 return;
5246 }
5247
5248 self.update_visible_inline_completion(window, cx);
5249 }
5250
5251 pub fn display_cursor_names(
5252 &mut self,
5253 _: &DisplayCursorNames,
5254 window: &mut Window,
5255 cx: &mut Context<Self>,
5256 ) {
5257 self.show_cursor_names(window, cx);
5258 }
5259
5260 fn show_cursor_names(&mut self, window: &mut Window, cx: &mut Context<Self>) {
5261 self.show_cursor_names = true;
5262 cx.notify();
5263 cx.spawn_in(window, |this, mut cx| async move {
5264 cx.background_executor().timer(CURSORS_VISIBLE_FOR).await;
5265 this.update(&mut cx, |this, cx| {
5266 this.show_cursor_names = false;
5267 cx.notify()
5268 })
5269 .ok()
5270 })
5271 .detach();
5272 }
5273
5274 pub fn next_edit_prediction(
5275 &mut self,
5276 _: &NextEditPrediction,
5277 window: &mut Window,
5278 cx: &mut Context<Self>,
5279 ) {
5280 if self.has_active_inline_completion() {
5281 self.cycle_inline_completion(Direction::Next, window, cx);
5282 } else {
5283 let is_copilot_disabled = self
5284 .refresh_inline_completion(false, true, window, cx)
5285 .is_none();
5286 if is_copilot_disabled {
5287 cx.propagate();
5288 }
5289 }
5290 }
5291
5292 pub fn previous_edit_prediction(
5293 &mut self,
5294 _: &PreviousEditPrediction,
5295 window: &mut Window,
5296 cx: &mut Context<Self>,
5297 ) {
5298 if self.has_active_inline_completion() {
5299 self.cycle_inline_completion(Direction::Prev, window, cx);
5300 } else {
5301 let is_copilot_disabled = self
5302 .refresh_inline_completion(false, true, window, cx)
5303 .is_none();
5304 if is_copilot_disabled {
5305 cx.propagate();
5306 }
5307 }
5308 }
5309
5310 pub fn accept_edit_prediction(
5311 &mut self,
5312 _: &AcceptEditPrediction,
5313 window: &mut Window,
5314 cx: &mut Context<Self>,
5315 ) {
5316 if self.show_edit_predictions_in_menu() {
5317 self.hide_context_menu(window, cx);
5318 }
5319
5320 let Some(active_inline_completion) = self.active_inline_completion.as_ref() else {
5321 return;
5322 };
5323
5324 self.report_inline_completion_event(
5325 active_inline_completion.completion_id.clone(),
5326 true,
5327 cx,
5328 );
5329
5330 match &active_inline_completion.completion {
5331 InlineCompletion::Move { target, .. } => {
5332 let target = *target;
5333
5334 if let Some(position_map) = &self.last_position_map {
5335 if position_map
5336 .visible_row_range
5337 .contains(&target.to_display_point(&position_map.snapshot).row())
5338 || !self.edit_prediction_requires_modifier()
5339 {
5340 self.unfold_ranges(&[target..target], true, false, cx);
5341 // Note that this is also done in vim's handler of the Tab action.
5342 self.change_selections(
5343 Some(Autoscroll::newest()),
5344 window,
5345 cx,
5346 |selections| {
5347 selections.select_anchor_ranges([target..target]);
5348 },
5349 );
5350 self.clear_row_highlights::<EditPredictionPreview>();
5351
5352 self.edit_prediction_preview
5353 .set_previous_scroll_position(None);
5354 } else {
5355 self.edit_prediction_preview
5356 .set_previous_scroll_position(Some(
5357 position_map.snapshot.scroll_anchor,
5358 ));
5359
5360 self.highlight_rows::<EditPredictionPreview>(
5361 target..target,
5362 cx.theme().colors().editor_highlighted_line_background,
5363 true,
5364 cx,
5365 );
5366 self.request_autoscroll(Autoscroll::fit(), cx);
5367 }
5368 }
5369 }
5370 InlineCompletion::Edit { edits, .. } => {
5371 if let Some(provider) = self.edit_prediction_provider() {
5372 provider.accept(cx);
5373 }
5374
5375 let snapshot = self.buffer.read(cx).snapshot(cx);
5376 let last_edit_end = edits.last().unwrap().0.end.bias_right(&snapshot);
5377
5378 self.buffer.update(cx, |buffer, cx| {
5379 buffer.edit(edits.iter().cloned(), None, cx)
5380 });
5381
5382 self.change_selections(None, window, cx, |s| {
5383 s.select_anchor_ranges([last_edit_end..last_edit_end])
5384 });
5385
5386 self.update_visible_inline_completion(window, cx);
5387 if self.active_inline_completion.is_none() {
5388 self.refresh_inline_completion(true, true, window, cx);
5389 }
5390
5391 cx.notify();
5392 }
5393 }
5394
5395 self.edit_prediction_requires_modifier_in_indent_conflict = false;
5396 }
5397
5398 pub fn accept_partial_inline_completion(
5399 &mut self,
5400 _: &AcceptPartialEditPrediction,
5401 window: &mut Window,
5402 cx: &mut Context<Self>,
5403 ) {
5404 let Some(active_inline_completion) = self.active_inline_completion.as_ref() else {
5405 return;
5406 };
5407 if self.selections.count() != 1 {
5408 return;
5409 }
5410
5411 self.report_inline_completion_event(
5412 active_inline_completion.completion_id.clone(),
5413 true,
5414 cx,
5415 );
5416
5417 match &active_inline_completion.completion {
5418 InlineCompletion::Move { target, .. } => {
5419 let target = *target;
5420 self.change_selections(Some(Autoscroll::newest()), window, cx, |selections| {
5421 selections.select_anchor_ranges([target..target]);
5422 });
5423 }
5424 InlineCompletion::Edit { edits, .. } => {
5425 // Find an insertion that starts at the cursor position.
5426 let snapshot = self.buffer.read(cx).snapshot(cx);
5427 let cursor_offset = self.selections.newest::<usize>(cx).head();
5428 let insertion = edits.iter().find_map(|(range, text)| {
5429 let range = range.to_offset(&snapshot);
5430 if range.is_empty() && range.start == cursor_offset {
5431 Some(text)
5432 } else {
5433 None
5434 }
5435 });
5436
5437 if let Some(text) = insertion {
5438 let mut partial_completion = text
5439 .chars()
5440 .by_ref()
5441 .take_while(|c| c.is_alphabetic())
5442 .collect::<String>();
5443 if partial_completion.is_empty() {
5444 partial_completion = text
5445 .chars()
5446 .by_ref()
5447 .take_while(|c| c.is_whitespace() || !c.is_alphabetic())
5448 .collect::<String>();
5449 }
5450
5451 cx.emit(EditorEvent::InputHandled {
5452 utf16_range_to_replace: None,
5453 text: partial_completion.clone().into(),
5454 });
5455
5456 self.insert_with_autoindent_mode(&partial_completion, None, window, cx);
5457
5458 self.refresh_inline_completion(true, true, window, cx);
5459 cx.notify();
5460 } else {
5461 self.accept_edit_prediction(&Default::default(), window, cx);
5462 }
5463 }
5464 }
5465 }
5466
5467 fn discard_inline_completion(
5468 &mut self,
5469 should_report_inline_completion_event: bool,
5470 cx: &mut Context<Self>,
5471 ) -> bool {
5472 if should_report_inline_completion_event {
5473 let completion_id = self
5474 .active_inline_completion
5475 .as_ref()
5476 .and_then(|active_completion| active_completion.completion_id.clone());
5477
5478 self.report_inline_completion_event(completion_id, false, cx);
5479 }
5480
5481 if let Some(provider) = self.edit_prediction_provider() {
5482 provider.discard(cx);
5483 }
5484
5485 self.take_active_inline_completion(cx)
5486 }
5487
5488 fn report_inline_completion_event(&self, id: Option<SharedString>, accepted: bool, cx: &App) {
5489 let Some(provider) = self.edit_prediction_provider() else {
5490 return;
5491 };
5492
5493 let Some((_, buffer, _)) = self
5494 .buffer
5495 .read(cx)
5496 .excerpt_containing(self.selections.newest_anchor().head(), cx)
5497 else {
5498 return;
5499 };
5500
5501 let extension = buffer
5502 .read(cx)
5503 .file()
5504 .and_then(|file| Some(file.path().extension()?.to_string_lossy().to_string()));
5505
5506 let event_type = match accepted {
5507 true => "Edit Prediction Accepted",
5508 false => "Edit Prediction Discarded",
5509 };
5510 telemetry::event!(
5511 event_type,
5512 provider = provider.name(),
5513 prediction_id = id,
5514 suggestion_accepted = accepted,
5515 file_extension = extension,
5516 );
5517 }
5518
5519 pub fn has_active_inline_completion(&self) -> bool {
5520 self.active_inline_completion.is_some()
5521 }
5522
5523 fn take_active_inline_completion(&mut self, cx: &mut Context<Self>) -> bool {
5524 let Some(active_inline_completion) = self.active_inline_completion.take() else {
5525 return false;
5526 };
5527
5528 self.splice_inlays(&active_inline_completion.inlay_ids, Default::default(), cx);
5529 self.clear_highlights::<InlineCompletionHighlight>(cx);
5530 self.stale_inline_completion_in_menu = Some(active_inline_completion);
5531 true
5532 }
5533
5534 /// Returns true when we're displaying the edit prediction popover below the cursor
5535 /// like we are not previewing and the LSP autocomplete menu is visible
5536 /// or we are in `when_holding_modifier` mode.
5537 pub fn edit_prediction_visible_in_cursor_popover(&self, has_completion: bool) -> bool {
5538 if self.edit_prediction_preview_is_active()
5539 || !self.show_edit_predictions_in_menu()
5540 || !self.edit_predictions_enabled()
5541 {
5542 return false;
5543 }
5544
5545 if self.has_visible_completions_menu() {
5546 return true;
5547 }
5548
5549 has_completion && self.edit_prediction_requires_modifier()
5550 }
5551
5552 fn handle_modifiers_changed(
5553 &mut self,
5554 modifiers: Modifiers,
5555 position_map: &PositionMap,
5556 window: &mut Window,
5557 cx: &mut Context<Self>,
5558 ) {
5559 if self.show_edit_predictions_in_menu() {
5560 self.update_edit_prediction_preview(&modifiers, window, cx);
5561 }
5562
5563 self.update_selection_mode(&modifiers, position_map, window, cx);
5564
5565 let mouse_position = window.mouse_position();
5566 if !position_map.text_hitbox.is_hovered(window) {
5567 return;
5568 }
5569
5570 self.update_hovered_link(
5571 position_map.point_for_position(mouse_position),
5572 &position_map.snapshot,
5573 modifiers,
5574 window,
5575 cx,
5576 )
5577 }
5578
5579 fn update_selection_mode(
5580 &mut self,
5581 modifiers: &Modifiers,
5582 position_map: &PositionMap,
5583 window: &mut Window,
5584 cx: &mut Context<Self>,
5585 ) {
5586 if modifiers != &COLUMNAR_SELECTION_MODIFIERS || self.selections.pending.is_none() {
5587 return;
5588 }
5589
5590 let mouse_position = window.mouse_position();
5591 let point_for_position = position_map.point_for_position(mouse_position);
5592 let position = point_for_position.previous_valid;
5593
5594 self.select(
5595 SelectPhase::BeginColumnar {
5596 position,
5597 reset: false,
5598 goal_column: point_for_position.exact_unclipped.column(),
5599 },
5600 window,
5601 cx,
5602 );
5603 }
5604
5605 fn update_edit_prediction_preview(
5606 &mut self,
5607 modifiers: &Modifiers,
5608 window: &mut Window,
5609 cx: &mut Context<Self>,
5610 ) {
5611 let accept_keybind = self.accept_edit_prediction_keybind(window, cx);
5612 let Some(accept_keystroke) = accept_keybind.keystroke() else {
5613 return;
5614 };
5615
5616 if &accept_keystroke.modifiers == modifiers && accept_keystroke.modifiers.modified() {
5617 if matches!(
5618 self.edit_prediction_preview,
5619 EditPredictionPreview::Inactive { .. }
5620 ) {
5621 self.edit_prediction_preview = EditPredictionPreview::Active {
5622 previous_scroll_position: None,
5623 since: Instant::now(),
5624 };
5625
5626 self.update_visible_inline_completion(window, cx);
5627 cx.notify();
5628 }
5629 } else if let EditPredictionPreview::Active {
5630 previous_scroll_position,
5631 since,
5632 } = self.edit_prediction_preview
5633 {
5634 if let (Some(previous_scroll_position), Some(position_map)) =
5635 (previous_scroll_position, self.last_position_map.as_ref())
5636 {
5637 self.set_scroll_position(
5638 previous_scroll_position
5639 .scroll_position(&position_map.snapshot.display_snapshot),
5640 window,
5641 cx,
5642 );
5643 }
5644
5645 self.edit_prediction_preview = EditPredictionPreview::Inactive {
5646 released_too_fast: since.elapsed() < Duration::from_millis(200),
5647 };
5648 self.clear_row_highlights::<EditPredictionPreview>();
5649 self.update_visible_inline_completion(window, cx);
5650 cx.notify();
5651 }
5652 }
5653
5654 fn update_visible_inline_completion(
5655 &mut self,
5656 _window: &mut Window,
5657 cx: &mut Context<Self>,
5658 ) -> Option<()> {
5659 let selection = self.selections.newest_anchor();
5660 let cursor = selection.head();
5661 let multibuffer = self.buffer.read(cx).snapshot(cx);
5662 let offset_selection = selection.map(|endpoint| endpoint.to_offset(&multibuffer));
5663 let excerpt_id = cursor.excerpt_id;
5664
5665 let show_in_menu = self.show_edit_predictions_in_menu();
5666 let completions_menu_has_precedence = !show_in_menu
5667 && (self.context_menu.borrow().is_some()
5668 || (!self.completion_tasks.is_empty() && !self.has_active_inline_completion()));
5669
5670 if completions_menu_has_precedence
5671 || !offset_selection.is_empty()
5672 || self
5673 .active_inline_completion
5674 .as_ref()
5675 .map_or(false, |completion| {
5676 let invalidation_range = completion.invalidation_range.to_offset(&multibuffer);
5677 let invalidation_range = invalidation_range.start..=invalidation_range.end;
5678 !invalidation_range.contains(&offset_selection.head())
5679 })
5680 {
5681 self.discard_inline_completion(false, cx);
5682 return None;
5683 }
5684
5685 self.take_active_inline_completion(cx);
5686 let Some(provider) = self.edit_prediction_provider() else {
5687 self.edit_prediction_settings = EditPredictionSettings::Disabled;
5688 return None;
5689 };
5690
5691 let (buffer, cursor_buffer_position) =
5692 self.buffer.read(cx).text_anchor_for_position(cursor, cx)?;
5693
5694 self.edit_prediction_settings =
5695 self.edit_prediction_settings_at_position(&buffer, cursor_buffer_position, cx);
5696
5697 self.edit_prediction_indent_conflict = multibuffer.is_line_whitespace_upto(cursor);
5698
5699 if self.edit_prediction_indent_conflict {
5700 let cursor_point = cursor.to_point(&multibuffer);
5701
5702 let indents = multibuffer.suggested_indents(cursor_point.row..cursor_point.row + 1, cx);
5703
5704 if let Some((_, indent)) = indents.iter().next() {
5705 if indent.len == cursor_point.column {
5706 self.edit_prediction_indent_conflict = false;
5707 }
5708 }
5709 }
5710
5711 let inline_completion = provider.suggest(&buffer, cursor_buffer_position, cx)?;
5712 let edits = inline_completion
5713 .edits
5714 .into_iter()
5715 .flat_map(|(range, new_text)| {
5716 let start = multibuffer.anchor_in_excerpt(excerpt_id, range.start)?;
5717 let end = multibuffer.anchor_in_excerpt(excerpt_id, range.end)?;
5718 Some((start..end, new_text))
5719 })
5720 .collect::<Vec<_>>();
5721 if edits.is_empty() {
5722 return None;
5723 }
5724
5725 let first_edit_start = edits.first().unwrap().0.start;
5726 let first_edit_start_point = first_edit_start.to_point(&multibuffer);
5727 let edit_start_row = first_edit_start_point.row.saturating_sub(2);
5728
5729 let last_edit_end = edits.last().unwrap().0.end;
5730 let last_edit_end_point = last_edit_end.to_point(&multibuffer);
5731 let edit_end_row = cmp::min(multibuffer.max_point().row, last_edit_end_point.row + 2);
5732
5733 let cursor_row = cursor.to_point(&multibuffer).row;
5734
5735 let snapshot = multibuffer.buffer_for_excerpt(excerpt_id).cloned()?;
5736
5737 let mut inlay_ids = Vec::new();
5738 let invalidation_row_range;
5739 let move_invalidation_row_range = if cursor_row < edit_start_row {
5740 Some(cursor_row..edit_end_row)
5741 } else if cursor_row > edit_end_row {
5742 Some(edit_start_row..cursor_row)
5743 } else {
5744 None
5745 };
5746 let is_move =
5747 move_invalidation_row_range.is_some() || self.inline_completions_hidden_for_vim_mode;
5748 let completion = if is_move {
5749 invalidation_row_range =
5750 move_invalidation_row_range.unwrap_or(edit_start_row..edit_end_row);
5751 let target = first_edit_start;
5752 InlineCompletion::Move { target, snapshot }
5753 } else {
5754 let show_completions_in_buffer = !self.edit_prediction_visible_in_cursor_popover(true)
5755 && !self.inline_completions_hidden_for_vim_mode;
5756
5757 if show_completions_in_buffer {
5758 if edits
5759 .iter()
5760 .all(|(range, _)| range.to_offset(&multibuffer).is_empty())
5761 {
5762 let mut inlays = Vec::new();
5763 for (range, new_text) in &edits {
5764 let inlay = Inlay::inline_completion(
5765 post_inc(&mut self.next_inlay_id),
5766 range.start,
5767 new_text.as_str(),
5768 );
5769 inlay_ids.push(inlay.id);
5770 inlays.push(inlay);
5771 }
5772
5773 self.splice_inlays(&[], inlays, cx);
5774 } else {
5775 let background_color = cx.theme().status().deleted_background;
5776 self.highlight_text::<InlineCompletionHighlight>(
5777 edits.iter().map(|(range, _)| range.clone()).collect(),
5778 HighlightStyle {
5779 background_color: Some(background_color),
5780 ..Default::default()
5781 },
5782 cx,
5783 );
5784 }
5785 }
5786
5787 invalidation_row_range = edit_start_row..edit_end_row;
5788
5789 let display_mode = if all_edits_insertions_or_deletions(&edits, &multibuffer) {
5790 if provider.show_tab_accept_marker() {
5791 EditDisplayMode::TabAccept
5792 } else {
5793 EditDisplayMode::Inline
5794 }
5795 } else {
5796 EditDisplayMode::DiffPopover
5797 };
5798
5799 InlineCompletion::Edit {
5800 edits,
5801 edit_preview: inline_completion.edit_preview,
5802 display_mode,
5803 snapshot,
5804 }
5805 };
5806
5807 let invalidation_range = multibuffer
5808 .anchor_before(Point::new(invalidation_row_range.start, 0))
5809 ..multibuffer.anchor_after(Point::new(
5810 invalidation_row_range.end,
5811 multibuffer.line_len(MultiBufferRow(invalidation_row_range.end)),
5812 ));
5813
5814 self.stale_inline_completion_in_menu = None;
5815 self.active_inline_completion = Some(InlineCompletionState {
5816 inlay_ids,
5817 completion,
5818 completion_id: inline_completion.id,
5819 invalidation_range,
5820 });
5821
5822 cx.notify();
5823
5824 Some(())
5825 }
5826
5827 pub fn edit_prediction_provider(&self) -> Option<Arc<dyn InlineCompletionProviderHandle>> {
5828 Some(self.edit_prediction_provider.as_ref()?.provider.clone())
5829 }
5830
5831 fn render_code_actions_indicator(
5832 &self,
5833 _style: &EditorStyle,
5834 row: DisplayRow,
5835 is_active: bool,
5836 cx: &mut Context<Self>,
5837 ) -> Option<IconButton> {
5838 if self.available_code_actions.is_some() {
5839 Some(
5840 IconButton::new("code_actions_indicator", ui::IconName::Bolt)
5841 .shape(ui::IconButtonShape::Square)
5842 .icon_size(IconSize::XSmall)
5843 .icon_color(Color::Muted)
5844 .toggle_state(is_active)
5845 .tooltip({
5846 let focus_handle = self.focus_handle.clone();
5847 move |window, cx| {
5848 Tooltip::for_action_in(
5849 "Toggle Code Actions",
5850 &ToggleCodeActions {
5851 deployed_from_indicator: None,
5852 },
5853 &focus_handle,
5854 window,
5855 cx,
5856 )
5857 }
5858 })
5859 .on_click(cx.listener(move |editor, _e, window, cx| {
5860 window.focus(&editor.focus_handle(cx));
5861 editor.toggle_code_actions(
5862 &ToggleCodeActions {
5863 deployed_from_indicator: Some(row),
5864 },
5865 window,
5866 cx,
5867 );
5868 })),
5869 )
5870 } else {
5871 None
5872 }
5873 }
5874
5875 fn clear_tasks(&mut self) {
5876 self.tasks.clear()
5877 }
5878
5879 fn insert_tasks(&mut self, key: (BufferId, BufferRow), value: RunnableTasks) {
5880 if self.tasks.insert(key, value).is_some() {
5881 // This case should hopefully be rare, but just in case...
5882 log::error!("multiple different run targets found on a single line, only the last target will be rendered")
5883 }
5884 }
5885
5886 fn build_tasks_context(
5887 project: &Entity<Project>,
5888 buffer: &Entity<Buffer>,
5889 buffer_row: u32,
5890 tasks: &Arc<RunnableTasks>,
5891 cx: &mut Context<Self>,
5892 ) -> Task<Option<task::TaskContext>> {
5893 let position = Point::new(buffer_row, tasks.column);
5894 let range_start = buffer.read(cx).anchor_at(position, Bias::Right);
5895 let location = Location {
5896 buffer: buffer.clone(),
5897 range: range_start..range_start,
5898 };
5899 // Fill in the environmental variables from the tree-sitter captures
5900 let mut captured_task_variables = TaskVariables::default();
5901 for (capture_name, value) in tasks.extra_variables.clone() {
5902 captured_task_variables.insert(
5903 task::VariableName::Custom(capture_name.into()),
5904 value.clone(),
5905 );
5906 }
5907 project.update(cx, |project, cx| {
5908 project.task_store().update(cx, |task_store, cx| {
5909 task_store.task_context_for_location(captured_task_variables, location, cx)
5910 })
5911 })
5912 }
5913
5914 pub fn spawn_nearest_task(
5915 &mut self,
5916 action: &SpawnNearestTask,
5917 window: &mut Window,
5918 cx: &mut Context<Self>,
5919 ) {
5920 let Some((workspace, _)) = self.workspace.clone() else {
5921 return;
5922 };
5923 let Some(project) = self.project.clone() else {
5924 return;
5925 };
5926
5927 // Try to find a closest, enclosing node using tree-sitter that has a
5928 // task
5929 let Some((buffer, buffer_row, tasks)) = self
5930 .find_enclosing_node_task(cx)
5931 // Or find the task that's closest in row-distance.
5932 .or_else(|| self.find_closest_task(cx))
5933 else {
5934 return;
5935 };
5936
5937 let reveal_strategy = action.reveal;
5938 let task_context = Self::build_tasks_context(&project, &buffer, buffer_row, &tasks, cx);
5939 cx.spawn_in(window, |_, mut cx| async move {
5940 let context = task_context.await?;
5941 let (task_source_kind, mut resolved_task) = tasks.resolve(&context).next()?;
5942
5943 let resolved = resolved_task.resolved.as_mut()?;
5944 resolved.reveal = reveal_strategy;
5945
5946 workspace
5947 .update(&mut cx, |workspace, cx| {
5948 workspace::tasks::schedule_resolved_task(
5949 workspace,
5950 task_source_kind,
5951 resolved_task,
5952 false,
5953 cx,
5954 );
5955 })
5956 .ok()
5957 })
5958 .detach();
5959 }
5960
5961 fn find_closest_task(
5962 &mut self,
5963 cx: &mut Context<Self>,
5964 ) -> Option<(Entity<Buffer>, u32, Arc<RunnableTasks>)> {
5965 let cursor_row = self.selections.newest_adjusted(cx).head().row;
5966
5967 let ((buffer_id, row), tasks) = self
5968 .tasks
5969 .iter()
5970 .min_by_key(|((_, row), _)| cursor_row.abs_diff(*row))?;
5971
5972 let buffer = self.buffer.read(cx).buffer(*buffer_id)?;
5973 let tasks = Arc::new(tasks.to_owned());
5974 Some((buffer, *row, tasks))
5975 }
5976
5977 fn find_enclosing_node_task(
5978 &mut self,
5979 cx: &mut Context<Self>,
5980 ) -> Option<(Entity<Buffer>, u32, Arc<RunnableTasks>)> {
5981 let snapshot = self.buffer.read(cx).snapshot(cx);
5982 let offset = self.selections.newest::<usize>(cx).head();
5983 let excerpt = snapshot.excerpt_containing(offset..offset)?;
5984 let buffer_id = excerpt.buffer().remote_id();
5985
5986 let layer = excerpt.buffer().syntax_layer_at(offset)?;
5987 let mut cursor = layer.node().walk();
5988
5989 while cursor.goto_first_child_for_byte(offset).is_some() {
5990 if cursor.node().end_byte() == offset {
5991 cursor.goto_next_sibling();
5992 }
5993 }
5994
5995 // Ascend to the smallest ancestor that contains the range and has a task.
5996 loop {
5997 let node = cursor.node();
5998 let node_range = node.byte_range();
5999 let symbol_start_row = excerpt.buffer().offset_to_point(node.start_byte()).row;
6000
6001 // Check if this node contains our offset
6002 if node_range.start <= offset && node_range.end >= offset {
6003 // If it contains offset, check for task
6004 if let Some(tasks) = self.tasks.get(&(buffer_id, symbol_start_row)) {
6005 let buffer = self.buffer.read(cx).buffer(buffer_id)?;
6006 return Some((buffer, symbol_start_row, Arc::new(tasks.to_owned())));
6007 }
6008 }
6009
6010 if !cursor.goto_parent() {
6011 break;
6012 }
6013 }
6014 None
6015 }
6016
6017 fn render_run_indicator(
6018 &self,
6019 _style: &EditorStyle,
6020 is_active: bool,
6021 row: DisplayRow,
6022 cx: &mut Context<Self>,
6023 ) -> IconButton {
6024 IconButton::new(("run_indicator", row.0 as usize), ui::IconName::Play)
6025 .shape(ui::IconButtonShape::Square)
6026 .icon_size(IconSize::XSmall)
6027 .icon_color(Color::Muted)
6028 .toggle_state(is_active)
6029 .on_click(cx.listener(move |editor, _e, window, cx| {
6030 window.focus(&editor.focus_handle(cx));
6031 editor.toggle_code_actions(
6032 &ToggleCodeActions {
6033 deployed_from_indicator: Some(row),
6034 },
6035 window,
6036 cx,
6037 );
6038 }))
6039 }
6040
6041 pub fn context_menu_visible(&self) -> bool {
6042 !self.edit_prediction_preview_is_active()
6043 && self
6044 .context_menu
6045 .borrow()
6046 .as_ref()
6047 .map_or(false, |menu| menu.visible())
6048 }
6049
6050 fn context_menu_origin(&self) -> Option<ContextMenuOrigin> {
6051 self.context_menu
6052 .borrow()
6053 .as_ref()
6054 .map(|menu| menu.origin())
6055 }
6056
6057 const EDIT_PREDICTION_POPOVER_PADDING_X: Pixels = Pixels(24.);
6058 const EDIT_PREDICTION_POPOVER_PADDING_Y: Pixels = Pixels(2.);
6059
6060 fn render_edit_prediction_popover(
6061 &mut self,
6062 text_bounds: &Bounds<Pixels>,
6063 content_origin: gpui::Point<Pixels>,
6064 editor_snapshot: &EditorSnapshot,
6065 visible_row_range: Range<DisplayRow>,
6066 scroll_top: f32,
6067 scroll_bottom: f32,
6068 line_layouts: &[LineWithInvisibles],
6069 line_height: Pixels,
6070 scroll_pixel_position: gpui::Point<Pixels>,
6071 newest_selection_head: Option<DisplayPoint>,
6072 editor_width: Pixels,
6073 style: &EditorStyle,
6074 window: &mut Window,
6075 cx: &mut App,
6076 ) -> Option<(AnyElement, gpui::Point<Pixels>)> {
6077 let active_inline_completion = self.active_inline_completion.as_ref()?;
6078
6079 if self.edit_prediction_visible_in_cursor_popover(true) {
6080 return None;
6081 }
6082
6083 match &active_inline_completion.completion {
6084 InlineCompletion::Move { target, .. } => {
6085 let target_display_point = target.to_display_point(editor_snapshot);
6086
6087 if self.edit_prediction_requires_modifier() {
6088 if !self.edit_prediction_preview_is_active() {
6089 return None;
6090 }
6091
6092 self.render_edit_prediction_modifier_jump_popover(
6093 text_bounds,
6094 content_origin,
6095 visible_row_range,
6096 line_layouts,
6097 line_height,
6098 scroll_pixel_position,
6099 newest_selection_head,
6100 target_display_point,
6101 window,
6102 cx,
6103 )
6104 } else {
6105 self.render_edit_prediction_eager_jump_popover(
6106 text_bounds,
6107 content_origin,
6108 editor_snapshot,
6109 visible_row_range,
6110 scroll_top,
6111 scroll_bottom,
6112 line_height,
6113 scroll_pixel_position,
6114 target_display_point,
6115 editor_width,
6116 window,
6117 cx,
6118 )
6119 }
6120 }
6121 InlineCompletion::Edit {
6122 display_mode: EditDisplayMode::Inline,
6123 ..
6124 } => None,
6125 InlineCompletion::Edit {
6126 display_mode: EditDisplayMode::TabAccept,
6127 edits,
6128 ..
6129 } => {
6130 let range = &edits.first()?.0;
6131 let target_display_point = range.end.to_display_point(editor_snapshot);
6132
6133 self.render_edit_prediction_end_of_line_popover(
6134 "Accept",
6135 editor_snapshot,
6136 visible_row_range,
6137 target_display_point,
6138 line_height,
6139 scroll_pixel_position,
6140 content_origin,
6141 editor_width,
6142 window,
6143 cx,
6144 )
6145 }
6146 InlineCompletion::Edit {
6147 edits,
6148 edit_preview,
6149 display_mode: EditDisplayMode::DiffPopover,
6150 snapshot,
6151 } => self.render_edit_prediction_diff_popover(
6152 text_bounds,
6153 content_origin,
6154 editor_snapshot,
6155 visible_row_range,
6156 line_layouts,
6157 line_height,
6158 scroll_pixel_position,
6159 newest_selection_head,
6160 editor_width,
6161 style,
6162 edits,
6163 edit_preview,
6164 snapshot,
6165 window,
6166 cx,
6167 ),
6168 }
6169 }
6170
6171 fn render_edit_prediction_modifier_jump_popover(
6172 &mut self,
6173 text_bounds: &Bounds<Pixels>,
6174 content_origin: gpui::Point<Pixels>,
6175 visible_row_range: Range<DisplayRow>,
6176 line_layouts: &[LineWithInvisibles],
6177 line_height: Pixels,
6178 scroll_pixel_position: gpui::Point<Pixels>,
6179 newest_selection_head: Option<DisplayPoint>,
6180 target_display_point: DisplayPoint,
6181 window: &mut Window,
6182 cx: &mut App,
6183 ) -> Option<(AnyElement, gpui::Point<Pixels>)> {
6184 let scrolled_content_origin =
6185 content_origin - gpui::Point::new(scroll_pixel_position.x, Pixels(0.0));
6186
6187 const SCROLL_PADDING_Y: Pixels = px(12.);
6188
6189 if target_display_point.row() < visible_row_range.start {
6190 return self.render_edit_prediction_scroll_popover(
6191 |_| SCROLL_PADDING_Y,
6192 IconName::ArrowUp,
6193 visible_row_range,
6194 line_layouts,
6195 newest_selection_head,
6196 scrolled_content_origin,
6197 window,
6198 cx,
6199 );
6200 } else if target_display_point.row() >= visible_row_range.end {
6201 return self.render_edit_prediction_scroll_popover(
6202 |size| text_bounds.size.height - size.height - SCROLL_PADDING_Y,
6203 IconName::ArrowDown,
6204 visible_row_range,
6205 line_layouts,
6206 newest_selection_head,
6207 scrolled_content_origin,
6208 window,
6209 cx,
6210 );
6211 }
6212
6213 const POLE_WIDTH: Pixels = px(2.);
6214
6215 let line_layout =
6216 line_layouts.get(target_display_point.row().minus(visible_row_range.start) as usize)?;
6217 let target_column = target_display_point.column() as usize;
6218
6219 let target_x = line_layout.x_for_index(target_column);
6220 let target_y =
6221 (target_display_point.row().as_f32() * line_height) - scroll_pixel_position.y;
6222
6223 let flag_on_right = target_x < text_bounds.size.width / 2.;
6224
6225 let mut border_color = Self::edit_prediction_callout_popover_border_color(cx);
6226 border_color.l += 0.001;
6227
6228 let mut element = v_flex()
6229 .items_end()
6230 .when(flag_on_right, |el| el.items_start())
6231 .child(if flag_on_right {
6232 self.render_edit_prediction_line_popover("Jump", None, window, cx)?
6233 .rounded_bl(px(0.))
6234 .rounded_tl(px(0.))
6235 .border_l_2()
6236 .border_color(border_color)
6237 } else {
6238 self.render_edit_prediction_line_popover("Jump", None, window, cx)?
6239 .rounded_br(px(0.))
6240 .rounded_tr(px(0.))
6241 .border_r_2()
6242 .border_color(border_color)
6243 })
6244 .child(div().w(POLE_WIDTH).bg(border_color).h(line_height))
6245 .into_any();
6246
6247 let size = element.layout_as_root(AvailableSpace::min_size(), window, cx);
6248
6249 let mut origin = scrolled_content_origin + point(target_x, target_y)
6250 - point(
6251 if flag_on_right {
6252 POLE_WIDTH
6253 } else {
6254 size.width - POLE_WIDTH
6255 },
6256 size.height - line_height,
6257 );
6258
6259 origin.x = origin.x.max(content_origin.x);
6260
6261 element.prepaint_at(origin, window, cx);
6262
6263 Some((element, origin))
6264 }
6265
6266 fn render_edit_prediction_scroll_popover(
6267 &mut self,
6268 to_y: impl Fn(Size<Pixels>) -> Pixels,
6269 scroll_icon: IconName,
6270 visible_row_range: Range<DisplayRow>,
6271 line_layouts: &[LineWithInvisibles],
6272 newest_selection_head: Option<DisplayPoint>,
6273 scrolled_content_origin: gpui::Point<Pixels>,
6274 window: &mut Window,
6275 cx: &mut App,
6276 ) -> Option<(AnyElement, gpui::Point<Pixels>)> {
6277 let mut element = self
6278 .render_edit_prediction_line_popover("Scroll", Some(scroll_icon), window, cx)?
6279 .into_any();
6280
6281 let size = element.layout_as_root(AvailableSpace::min_size(), window, cx);
6282
6283 let cursor = newest_selection_head?;
6284 let cursor_row_layout =
6285 line_layouts.get(cursor.row().minus(visible_row_range.start) as usize)?;
6286 let cursor_column = cursor.column() as usize;
6287
6288 let cursor_character_x = cursor_row_layout.x_for_index(cursor_column);
6289
6290 let origin = scrolled_content_origin + point(cursor_character_x, to_y(size));
6291
6292 element.prepaint_at(origin, window, cx);
6293 Some((element, origin))
6294 }
6295
6296 fn render_edit_prediction_eager_jump_popover(
6297 &mut self,
6298 text_bounds: &Bounds<Pixels>,
6299 content_origin: gpui::Point<Pixels>,
6300 editor_snapshot: &EditorSnapshot,
6301 visible_row_range: Range<DisplayRow>,
6302 scroll_top: f32,
6303 scroll_bottom: f32,
6304 line_height: Pixels,
6305 scroll_pixel_position: gpui::Point<Pixels>,
6306 target_display_point: DisplayPoint,
6307 editor_width: Pixels,
6308 window: &mut Window,
6309 cx: &mut App,
6310 ) -> Option<(AnyElement, gpui::Point<Pixels>)> {
6311 if target_display_point.row().as_f32() < scroll_top {
6312 let mut element = self
6313 .render_edit_prediction_line_popover(
6314 "Jump to Edit",
6315 Some(IconName::ArrowUp),
6316 window,
6317 cx,
6318 )?
6319 .into_any();
6320
6321 let size = element.layout_as_root(AvailableSpace::min_size(), window, cx);
6322 let offset = point(
6323 (text_bounds.size.width - size.width) / 2.,
6324 Self::EDIT_PREDICTION_POPOVER_PADDING_Y,
6325 );
6326
6327 let origin = text_bounds.origin + offset;
6328 element.prepaint_at(origin, window, cx);
6329 Some((element, origin))
6330 } else if (target_display_point.row().as_f32() + 1.) > scroll_bottom {
6331 let mut element = self
6332 .render_edit_prediction_line_popover(
6333 "Jump to Edit",
6334 Some(IconName::ArrowDown),
6335 window,
6336 cx,
6337 )?
6338 .into_any();
6339
6340 let size = element.layout_as_root(AvailableSpace::min_size(), window, cx);
6341 let offset = point(
6342 (text_bounds.size.width - size.width) / 2.,
6343 text_bounds.size.height - size.height - Self::EDIT_PREDICTION_POPOVER_PADDING_Y,
6344 );
6345
6346 let origin = text_bounds.origin + offset;
6347 element.prepaint_at(origin, window, cx);
6348 Some((element, origin))
6349 } else {
6350 self.render_edit_prediction_end_of_line_popover(
6351 "Jump to Edit",
6352 editor_snapshot,
6353 visible_row_range,
6354 target_display_point,
6355 line_height,
6356 scroll_pixel_position,
6357 content_origin,
6358 editor_width,
6359 window,
6360 cx,
6361 )
6362 }
6363 }
6364
6365 fn render_edit_prediction_end_of_line_popover(
6366 self: &mut Editor,
6367 label: &'static str,
6368 editor_snapshot: &EditorSnapshot,
6369 visible_row_range: Range<DisplayRow>,
6370 target_display_point: DisplayPoint,
6371 line_height: Pixels,
6372 scroll_pixel_position: gpui::Point<Pixels>,
6373 content_origin: gpui::Point<Pixels>,
6374 editor_width: Pixels,
6375 window: &mut Window,
6376 cx: &mut App,
6377 ) -> Option<(AnyElement, gpui::Point<Pixels>)> {
6378 let target_line_end = DisplayPoint::new(
6379 target_display_point.row(),
6380 editor_snapshot.line_len(target_display_point.row()),
6381 );
6382
6383 let mut element = self
6384 .render_edit_prediction_line_popover(label, None, window, cx)?
6385 .into_any();
6386
6387 let size = element.layout_as_root(AvailableSpace::min_size(), window, cx);
6388
6389 let line_origin = self.display_to_pixel_point(target_line_end, editor_snapshot, window)?;
6390
6391 let start_point = content_origin - point(scroll_pixel_position.x, Pixels::ZERO);
6392 let mut origin = start_point
6393 + line_origin
6394 + point(Self::EDIT_PREDICTION_POPOVER_PADDING_X, Pixels::ZERO);
6395 origin.x = origin.x.max(content_origin.x);
6396
6397 let max_x = content_origin.x + editor_width - size.width;
6398
6399 if origin.x > max_x {
6400 let offset = line_height + Self::EDIT_PREDICTION_POPOVER_PADDING_Y;
6401
6402 let icon = if visible_row_range.contains(&(target_display_point.row() + 2)) {
6403 origin.y += offset;
6404 IconName::ArrowUp
6405 } else {
6406 origin.y -= offset;
6407 IconName::ArrowDown
6408 };
6409
6410 element = self
6411 .render_edit_prediction_line_popover(label, Some(icon), window, cx)?
6412 .into_any();
6413
6414 let size = element.layout_as_root(AvailableSpace::min_size(), window, cx);
6415
6416 origin.x = content_origin.x + editor_width - size.width - px(2.);
6417 }
6418
6419 element.prepaint_at(origin, window, cx);
6420 Some((element, origin))
6421 }
6422
6423 fn render_edit_prediction_diff_popover(
6424 self: &Editor,
6425 text_bounds: &Bounds<Pixels>,
6426 content_origin: gpui::Point<Pixels>,
6427 editor_snapshot: &EditorSnapshot,
6428 visible_row_range: Range<DisplayRow>,
6429 line_layouts: &[LineWithInvisibles],
6430 line_height: Pixels,
6431 scroll_pixel_position: gpui::Point<Pixels>,
6432 newest_selection_head: Option<DisplayPoint>,
6433 editor_width: Pixels,
6434 style: &EditorStyle,
6435 edits: &Vec<(Range<Anchor>, String)>,
6436 edit_preview: &Option<language::EditPreview>,
6437 snapshot: &language::BufferSnapshot,
6438 window: &mut Window,
6439 cx: &mut App,
6440 ) -> Option<(AnyElement, gpui::Point<Pixels>)> {
6441 let edit_start = edits
6442 .first()
6443 .unwrap()
6444 .0
6445 .start
6446 .to_display_point(editor_snapshot);
6447 let edit_end = edits
6448 .last()
6449 .unwrap()
6450 .0
6451 .end
6452 .to_display_point(editor_snapshot);
6453
6454 let is_visible = visible_row_range.contains(&edit_start.row())
6455 || visible_row_range.contains(&edit_end.row());
6456 if !is_visible {
6457 return None;
6458 }
6459
6460 let highlighted_edits =
6461 crate::inline_completion_edit_text(&snapshot, edits, edit_preview.as_ref()?, false, cx);
6462
6463 let styled_text = highlighted_edits.to_styled_text(&style.text);
6464 let line_count = highlighted_edits.text.lines().count();
6465
6466 const BORDER_WIDTH: Pixels = px(1.);
6467
6468 let keybind = self.render_edit_prediction_accept_keybind(window, cx);
6469 let has_keybind = keybind.is_some();
6470
6471 let mut element = h_flex()
6472 .items_start()
6473 .child(
6474 h_flex()
6475 .bg(cx.theme().colors().editor_background)
6476 .border(BORDER_WIDTH)
6477 .shadow_sm()
6478 .border_color(cx.theme().colors().border)
6479 .rounded_l_lg()
6480 .when(line_count > 1, |el| el.rounded_br_lg())
6481 .pr_1()
6482 .child(styled_text),
6483 )
6484 .child(
6485 h_flex()
6486 .h(line_height + BORDER_WIDTH * px(2.))
6487 .px_1p5()
6488 .gap_1()
6489 // Workaround: For some reason, there's a gap if we don't do this
6490 .ml(-BORDER_WIDTH)
6491 .shadow(smallvec![gpui::BoxShadow {
6492 color: gpui::black().opacity(0.05),
6493 offset: point(px(1.), px(1.)),
6494 blur_radius: px(2.),
6495 spread_radius: px(0.),
6496 }])
6497 .bg(Editor::edit_prediction_line_popover_bg_color(cx))
6498 .border(BORDER_WIDTH)
6499 .border_color(cx.theme().colors().border)
6500 .rounded_r_lg()
6501 .id("edit_prediction_diff_popover_keybind")
6502 .when(!has_keybind, |el| {
6503 let status_colors = cx.theme().status();
6504
6505 el.bg(status_colors.error_background)
6506 .border_color(status_colors.error.opacity(0.6))
6507 .child(Icon::new(IconName::Info).color(Color::Error))
6508 .cursor_default()
6509 .hoverable_tooltip(move |_window, cx| {
6510 cx.new(|_| MissingEditPredictionKeybindingTooltip).into()
6511 })
6512 })
6513 .children(keybind),
6514 )
6515 .into_any();
6516
6517 let longest_row =
6518 editor_snapshot.longest_row_in_range(edit_start.row()..edit_end.row() + 1);
6519 let longest_line_width = if visible_row_range.contains(&longest_row) {
6520 line_layouts[(longest_row.0 - visible_row_range.start.0) as usize].width
6521 } else {
6522 layout_line(
6523 longest_row,
6524 editor_snapshot,
6525 style,
6526 editor_width,
6527 |_| false,
6528 window,
6529 cx,
6530 )
6531 .width
6532 };
6533
6534 let viewport_bounds =
6535 Bounds::new(Default::default(), window.viewport_size()).extend(Edges {
6536 right: -EditorElement::SCROLLBAR_WIDTH,
6537 ..Default::default()
6538 });
6539
6540 let x_after_longest =
6541 text_bounds.origin.x + longest_line_width + Self::EDIT_PREDICTION_POPOVER_PADDING_X
6542 - scroll_pixel_position.x;
6543
6544 let element_bounds = element.layout_as_root(AvailableSpace::min_size(), window, cx);
6545
6546 // Fully visible if it can be displayed within the window (allow overlapping other
6547 // panes). However, this is only allowed if the popover starts within text_bounds.
6548 let can_position_to_the_right = x_after_longest < text_bounds.right()
6549 && x_after_longest + element_bounds.width < viewport_bounds.right();
6550
6551 let mut origin = if can_position_to_the_right {
6552 point(
6553 x_after_longest,
6554 text_bounds.origin.y + edit_start.row().as_f32() * line_height
6555 - scroll_pixel_position.y,
6556 )
6557 } else {
6558 let cursor_row = newest_selection_head.map(|head| head.row());
6559 let above_edit = edit_start
6560 .row()
6561 .0
6562 .checked_sub(line_count as u32)
6563 .map(DisplayRow);
6564 let below_edit = Some(edit_end.row() + 1);
6565 let above_cursor =
6566 cursor_row.and_then(|row| row.0.checked_sub(line_count as u32).map(DisplayRow));
6567 let below_cursor = cursor_row.map(|cursor_row| cursor_row + 1);
6568
6569 // Place the edit popover adjacent to the edit if there is a location
6570 // available that is onscreen and does not obscure the cursor. Otherwise,
6571 // place it adjacent to the cursor.
6572 let row_target = [above_edit, below_edit, above_cursor, below_cursor]
6573 .into_iter()
6574 .flatten()
6575 .find(|&start_row| {
6576 let end_row = start_row + line_count as u32;
6577 visible_row_range.contains(&start_row)
6578 && visible_row_range.contains(&end_row)
6579 && cursor_row.map_or(true, |cursor_row| {
6580 !((start_row..end_row).contains(&cursor_row))
6581 })
6582 })?;
6583
6584 content_origin
6585 + point(
6586 -scroll_pixel_position.x,
6587 row_target.as_f32() * line_height - scroll_pixel_position.y,
6588 )
6589 };
6590
6591 origin.x -= BORDER_WIDTH;
6592
6593 window.defer_draw(element, origin, 1);
6594
6595 // Do not return an element, since it will already be drawn due to defer_draw.
6596 None
6597 }
6598
6599 fn edit_prediction_cursor_popover_height(&self) -> Pixels {
6600 px(30.)
6601 }
6602
6603 fn current_user_player_color(&self, cx: &mut App) -> PlayerColor {
6604 if self.read_only(cx) {
6605 cx.theme().players().read_only()
6606 } else {
6607 self.style.as_ref().unwrap().local_player
6608 }
6609 }
6610
6611 fn render_edit_prediction_accept_keybind(
6612 &self,
6613 window: &mut Window,
6614 cx: &App,
6615 ) -> Option<AnyElement> {
6616 let accept_binding = self.accept_edit_prediction_keybind(window, cx);
6617 let accept_keystroke = accept_binding.keystroke()?;
6618
6619 let is_platform_style_mac = PlatformStyle::platform() == PlatformStyle::Mac;
6620
6621 let modifiers_color = if accept_keystroke.modifiers == window.modifiers() {
6622 Color::Accent
6623 } else {
6624 Color::Muted
6625 };
6626
6627 h_flex()
6628 .px_0p5()
6629 .when(is_platform_style_mac, |parent| parent.gap_0p5())
6630 .font(theme::ThemeSettings::get_global(cx).buffer_font.clone())
6631 .text_size(TextSize::XSmall.rems(cx))
6632 .child(h_flex().children(ui::render_modifiers(
6633 &accept_keystroke.modifiers,
6634 PlatformStyle::platform(),
6635 Some(modifiers_color),
6636 Some(IconSize::XSmall.rems().into()),
6637 true,
6638 )))
6639 .when(is_platform_style_mac, |parent| {
6640 parent.child(accept_keystroke.key.clone())
6641 })
6642 .when(!is_platform_style_mac, |parent| {
6643 parent.child(
6644 Key::new(
6645 util::capitalize(&accept_keystroke.key),
6646 Some(Color::Default),
6647 )
6648 .size(Some(IconSize::XSmall.rems().into())),
6649 )
6650 })
6651 .into_any()
6652 .into()
6653 }
6654
6655 fn render_edit_prediction_line_popover(
6656 &self,
6657 label: impl Into<SharedString>,
6658 icon: Option<IconName>,
6659 window: &mut Window,
6660 cx: &App,
6661 ) -> Option<Stateful<Div>> {
6662 let padding_right = if icon.is_some() { px(4.) } else { px(8.) };
6663
6664 let keybind = self.render_edit_prediction_accept_keybind(window, cx);
6665 let has_keybind = keybind.is_some();
6666
6667 let result = h_flex()
6668 .id("ep-line-popover")
6669 .py_0p5()
6670 .pl_1()
6671 .pr(padding_right)
6672 .gap_1()
6673 .rounded_md()
6674 .border_1()
6675 .bg(Self::edit_prediction_line_popover_bg_color(cx))
6676 .border_color(Self::edit_prediction_callout_popover_border_color(cx))
6677 .shadow_sm()
6678 .when(!has_keybind, |el| {
6679 let status_colors = cx.theme().status();
6680
6681 el.bg(status_colors.error_background)
6682 .border_color(status_colors.error.opacity(0.6))
6683 .pl_2()
6684 .child(Icon::new(IconName::ZedPredictError).color(Color::Error))
6685 .cursor_default()
6686 .hoverable_tooltip(move |_window, cx| {
6687 cx.new(|_| MissingEditPredictionKeybindingTooltip).into()
6688 })
6689 })
6690 .children(keybind)
6691 .child(
6692 Label::new(label)
6693 .size(LabelSize::Small)
6694 .when(!has_keybind, |el| {
6695 el.color(cx.theme().status().error.into()).strikethrough()
6696 }),
6697 )
6698 .when(!has_keybind, |el| {
6699 el.child(
6700 h_flex().ml_1().child(
6701 Icon::new(IconName::Info)
6702 .size(IconSize::Small)
6703 .color(cx.theme().status().error.into()),
6704 ),
6705 )
6706 })
6707 .when_some(icon, |element, icon| {
6708 element.child(
6709 div()
6710 .mt(px(1.5))
6711 .child(Icon::new(icon).size(IconSize::Small)),
6712 )
6713 });
6714
6715 Some(result)
6716 }
6717
6718 fn edit_prediction_line_popover_bg_color(cx: &App) -> Hsla {
6719 let accent_color = cx.theme().colors().text_accent;
6720 let editor_bg_color = cx.theme().colors().editor_background;
6721 editor_bg_color.blend(accent_color.opacity(0.1))
6722 }
6723
6724 fn edit_prediction_callout_popover_border_color(cx: &App) -> Hsla {
6725 let accent_color = cx.theme().colors().text_accent;
6726 let editor_bg_color = cx.theme().colors().editor_background;
6727 editor_bg_color.blend(accent_color.opacity(0.6))
6728 }
6729
6730 fn render_edit_prediction_cursor_popover(
6731 &self,
6732 min_width: Pixels,
6733 max_width: Pixels,
6734 cursor_point: Point,
6735 style: &EditorStyle,
6736 accept_keystroke: Option<&gpui::Keystroke>,
6737 _window: &Window,
6738 cx: &mut Context<Editor>,
6739 ) -> Option<AnyElement> {
6740 let provider = self.edit_prediction_provider.as_ref()?;
6741
6742 if provider.provider.needs_terms_acceptance(cx) {
6743 return Some(
6744 h_flex()
6745 .min_w(min_width)
6746 .flex_1()
6747 .px_2()
6748 .py_1()
6749 .gap_3()
6750 .elevation_2(cx)
6751 .hover(|style| style.bg(cx.theme().colors().element_hover))
6752 .id("accept-terms")
6753 .cursor_pointer()
6754 .on_mouse_down(MouseButton::Left, |_, window, _| window.prevent_default())
6755 .on_click(cx.listener(|this, _event, window, cx| {
6756 cx.stop_propagation();
6757 this.report_editor_event("Edit Prediction Provider ToS Clicked", None, cx);
6758 window.dispatch_action(
6759 zed_actions::OpenZedPredictOnboarding.boxed_clone(),
6760 cx,
6761 );
6762 }))
6763 .child(
6764 h_flex()
6765 .flex_1()
6766 .gap_2()
6767 .child(Icon::new(IconName::ZedPredict))
6768 .child(Label::new("Accept Terms of Service"))
6769 .child(div().w_full())
6770 .child(
6771 Icon::new(IconName::ArrowUpRight)
6772 .color(Color::Muted)
6773 .size(IconSize::Small),
6774 )
6775 .into_any_element(),
6776 )
6777 .into_any(),
6778 );
6779 }
6780
6781 let is_refreshing = provider.provider.is_refreshing(cx);
6782
6783 fn pending_completion_container() -> Div {
6784 h_flex()
6785 .h_full()
6786 .flex_1()
6787 .gap_2()
6788 .child(Icon::new(IconName::ZedPredict))
6789 }
6790
6791 let completion = match &self.active_inline_completion {
6792 Some(prediction) => {
6793 if !self.has_visible_completions_menu() {
6794 const RADIUS: Pixels = px(6.);
6795 const BORDER_WIDTH: Pixels = px(1.);
6796
6797 return Some(
6798 h_flex()
6799 .elevation_2(cx)
6800 .border(BORDER_WIDTH)
6801 .border_color(cx.theme().colors().border)
6802 .when(accept_keystroke.is_none(), |el| {
6803 el.border_color(cx.theme().status().error)
6804 })
6805 .rounded(RADIUS)
6806 .rounded_tl(px(0.))
6807 .overflow_hidden()
6808 .child(div().px_1p5().child(match &prediction.completion {
6809 InlineCompletion::Move { target, snapshot } => {
6810 use text::ToPoint as _;
6811 if target.text_anchor.to_point(&snapshot).row > cursor_point.row
6812 {
6813 Icon::new(IconName::ZedPredictDown)
6814 } else {
6815 Icon::new(IconName::ZedPredictUp)
6816 }
6817 }
6818 InlineCompletion::Edit { .. } => Icon::new(IconName::ZedPredict),
6819 }))
6820 .child(
6821 h_flex()
6822 .gap_1()
6823 .py_1()
6824 .px_2()
6825 .rounded_r(RADIUS - BORDER_WIDTH)
6826 .border_l_1()
6827 .border_color(cx.theme().colors().border)
6828 .bg(Self::edit_prediction_line_popover_bg_color(cx))
6829 .when(self.edit_prediction_preview.released_too_fast(), |el| {
6830 el.child(
6831 Label::new("Hold")
6832 .size(LabelSize::Small)
6833 .when(accept_keystroke.is_none(), |el| {
6834 el.strikethrough()
6835 })
6836 .line_height_style(LineHeightStyle::UiLabel),
6837 )
6838 })
6839 .id("edit_prediction_cursor_popover_keybind")
6840 .when(accept_keystroke.is_none(), |el| {
6841 let status_colors = cx.theme().status();
6842
6843 el.bg(status_colors.error_background)
6844 .border_color(status_colors.error.opacity(0.6))
6845 .child(Icon::new(IconName::Info).color(Color::Error))
6846 .cursor_default()
6847 .hoverable_tooltip(move |_window, cx| {
6848 cx.new(|_| MissingEditPredictionKeybindingTooltip)
6849 .into()
6850 })
6851 })
6852 .when_some(
6853 accept_keystroke.as_ref(),
6854 |el, accept_keystroke| {
6855 el.child(h_flex().children(ui::render_modifiers(
6856 &accept_keystroke.modifiers,
6857 PlatformStyle::platform(),
6858 Some(Color::Default),
6859 Some(IconSize::XSmall.rems().into()),
6860 false,
6861 )))
6862 },
6863 ),
6864 )
6865 .into_any(),
6866 );
6867 }
6868
6869 self.render_edit_prediction_cursor_popover_preview(
6870 prediction,
6871 cursor_point,
6872 style,
6873 cx,
6874 )?
6875 }
6876
6877 None if is_refreshing => match &self.stale_inline_completion_in_menu {
6878 Some(stale_completion) => self.render_edit_prediction_cursor_popover_preview(
6879 stale_completion,
6880 cursor_point,
6881 style,
6882 cx,
6883 )?,
6884
6885 None => {
6886 pending_completion_container().child(Label::new("...").size(LabelSize::Small))
6887 }
6888 },
6889
6890 None => pending_completion_container().child(Label::new("No Prediction")),
6891 };
6892
6893 let completion = if is_refreshing {
6894 completion
6895 .with_animation(
6896 "loading-completion",
6897 Animation::new(Duration::from_secs(2))
6898 .repeat()
6899 .with_easing(pulsating_between(0.4, 0.8)),
6900 |label, delta| label.opacity(delta),
6901 )
6902 .into_any_element()
6903 } else {
6904 completion.into_any_element()
6905 };
6906
6907 let has_completion = self.active_inline_completion.is_some();
6908
6909 let is_platform_style_mac = PlatformStyle::platform() == PlatformStyle::Mac;
6910 Some(
6911 h_flex()
6912 .min_w(min_width)
6913 .max_w(max_width)
6914 .flex_1()
6915 .elevation_2(cx)
6916 .border_color(cx.theme().colors().border)
6917 .child(
6918 div()
6919 .flex_1()
6920 .py_1()
6921 .px_2()
6922 .overflow_hidden()
6923 .child(completion),
6924 )
6925 .when_some(accept_keystroke, |el, accept_keystroke| {
6926 if !accept_keystroke.modifiers.modified() {
6927 return el;
6928 }
6929
6930 el.child(
6931 h_flex()
6932 .h_full()
6933 .border_l_1()
6934 .rounded_r_lg()
6935 .border_color(cx.theme().colors().border)
6936 .bg(Self::edit_prediction_line_popover_bg_color(cx))
6937 .gap_1()
6938 .py_1()
6939 .px_2()
6940 .child(
6941 h_flex()
6942 .font(theme::ThemeSettings::get_global(cx).buffer_font.clone())
6943 .when(is_platform_style_mac, |parent| parent.gap_1())
6944 .child(h_flex().children(ui::render_modifiers(
6945 &accept_keystroke.modifiers,
6946 PlatformStyle::platform(),
6947 Some(if !has_completion {
6948 Color::Muted
6949 } else {
6950 Color::Default
6951 }),
6952 None,
6953 false,
6954 ))),
6955 )
6956 .child(Label::new("Preview").into_any_element())
6957 .opacity(if has_completion { 1.0 } else { 0.4 }),
6958 )
6959 })
6960 .into_any(),
6961 )
6962 }
6963
6964 fn render_edit_prediction_cursor_popover_preview(
6965 &self,
6966 completion: &InlineCompletionState,
6967 cursor_point: Point,
6968 style: &EditorStyle,
6969 cx: &mut Context<Editor>,
6970 ) -> Option<Div> {
6971 use text::ToPoint as _;
6972
6973 fn render_relative_row_jump(
6974 prefix: impl Into<String>,
6975 current_row: u32,
6976 target_row: u32,
6977 ) -> Div {
6978 let (row_diff, arrow) = if target_row < current_row {
6979 (current_row - target_row, IconName::ArrowUp)
6980 } else {
6981 (target_row - current_row, IconName::ArrowDown)
6982 };
6983
6984 h_flex()
6985 .child(
6986 Label::new(format!("{}{}", prefix.into(), row_diff))
6987 .color(Color::Muted)
6988 .size(LabelSize::Small),
6989 )
6990 .child(Icon::new(arrow).color(Color::Muted).size(IconSize::Small))
6991 }
6992
6993 match &completion.completion {
6994 InlineCompletion::Move {
6995 target, snapshot, ..
6996 } => Some(
6997 h_flex()
6998 .px_2()
6999 .gap_2()
7000 .flex_1()
7001 .child(
7002 if target.text_anchor.to_point(&snapshot).row > cursor_point.row {
7003 Icon::new(IconName::ZedPredictDown)
7004 } else {
7005 Icon::new(IconName::ZedPredictUp)
7006 },
7007 )
7008 .child(Label::new("Jump to Edit")),
7009 ),
7010
7011 InlineCompletion::Edit {
7012 edits,
7013 edit_preview,
7014 snapshot,
7015 display_mode: _,
7016 } => {
7017 let first_edit_row = edits.first()?.0.start.text_anchor.to_point(&snapshot).row;
7018
7019 let (highlighted_edits, has_more_lines) = crate::inline_completion_edit_text(
7020 &snapshot,
7021 &edits,
7022 edit_preview.as_ref()?,
7023 true,
7024 cx,
7025 )
7026 .first_line_preview();
7027
7028 let styled_text = gpui::StyledText::new(highlighted_edits.text)
7029 .with_default_highlights(&style.text, highlighted_edits.highlights);
7030
7031 let preview = h_flex()
7032 .gap_1()
7033 .min_w_16()
7034 .child(styled_text)
7035 .when(has_more_lines, |parent| parent.child("…"));
7036
7037 let left = if first_edit_row != cursor_point.row {
7038 render_relative_row_jump("", cursor_point.row, first_edit_row)
7039 .into_any_element()
7040 } else {
7041 Icon::new(IconName::ZedPredict).into_any_element()
7042 };
7043
7044 Some(
7045 h_flex()
7046 .h_full()
7047 .flex_1()
7048 .gap_2()
7049 .pr_1()
7050 .overflow_x_hidden()
7051 .font(theme::ThemeSettings::get_global(cx).buffer_font.clone())
7052 .child(left)
7053 .child(preview),
7054 )
7055 }
7056 }
7057 }
7058
7059 fn render_context_menu(
7060 &self,
7061 style: &EditorStyle,
7062 max_height_in_lines: u32,
7063 y_flipped: bool,
7064 window: &mut Window,
7065 cx: &mut Context<Editor>,
7066 ) -> Option<AnyElement> {
7067 let menu = self.context_menu.borrow();
7068 let menu = menu.as_ref()?;
7069 if !menu.visible() {
7070 return None;
7071 };
7072 Some(menu.render(style, max_height_in_lines, y_flipped, window, cx))
7073 }
7074
7075 fn render_context_menu_aside(
7076 &mut self,
7077 max_size: Size<Pixels>,
7078 window: &mut Window,
7079 cx: &mut Context<Editor>,
7080 ) -> Option<AnyElement> {
7081 self.context_menu.borrow_mut().as_mut().and_then(|menu| {
7082 if menu.visible() {
7083 menu.render_aside(self, max_size, window, cx)
7084 } else {
7085 None
7086 }
7087 })
7088 }
7089
7090 fn hide_context_menu(
7091 &mut self,
7092 window: &mut Window,
7093 cx: &mut Context<Self>,
7094 ) -> Option<CodeContextMenu> {
7095 cx.notify();
7096 self.completion_tasks.clear();
7097 let context_menu = self.context_menu.borrow_mut().take();
7098 self.stale_inline_completion_in_menu.take();
7099 self.update_visible_inline_completion(window, cx);
7100 context_menu
7101 }
7102
7103 fn show_snippet_choices(
7104 &mut self,
7105 choices: &Vec<String>,
7106 selection: Range<Anchor>,
7107 cx: &mut Context<Self>,
7108 ) {
7109 if selection.start.buffer_id.is_none() {
7110 return;
7111 }
7112 let buffer_id = selection.start.buffer_id.unwrap();
7113 let buffer = self.buffer().read(cx).buffer(buffer_id);
7114 let id = post_inc(&mut self.next_completion_id);
7115
7116 if let Some(buffer) = buffer {
7117 *self.context_menu.borrow_mut() = Some(CodeContextMenu::Completions(
7118 CompletionsMenu::new_snippet_choices(id, true, choices, selection, buffer),
7119 ));
7120 }
7121 }
7122
7123 pub fn insert_snippet(
7124 &mut self,
7125 insertion_ranges: &[Range<usize>],
7126 snippet: Snippet,
7127 window: &mut Window,
7128 cx: &mut Context<Self>,
7129 ) -> Result<()> {
7130 struct Tabstop<T> {
7131 is_end_tabstop: bool,
7132 ranges: Vec<Range<T>>,
7133 choices: Option<Vec<String>>,
7134 }
7135
7136 let tabstops = self.buffer.update(cx, |buffer, cx| {
7137 let snippet_text: Arc<str> = snippet.text.clone().into();
7138 buffer.edit(
7139 insertion_ranges
7140 .iter()
7141 .cloned()
7142 .map(|range| (range, snippet_text.clone())),
7143 Some(AutoindentMode::EachLine),
7144 cx,
7145 );
7146
7147 let snapshot = &*buffer.read(cx);
7148 let snippet = &snippet;
7149 snippet
7150 .tabstops
7151 .iter()
7152 .map(|tabstop| {
7153 let is_end_tabstop = tabstop.ranges.first().map_or(false, |tabstop| {
7154 tabstop.is_empty() && tabstop.start == snippet.text.len() as isize
7155 });
7156 let mut tabstop_ranges = tabstop
7157 .ranges
7158 .iter()
7159 .flat_map(|tabstop_range| {
7160 let mut delta = 0_isize;
7161 insertion_ranges.iter().map(move |insertion_range| {
7162 let insertion_start = insertion_range.start as isize + delta;
7163 delta +=
7164 snippet.text.len() as isize - insertion_range.len() as isize;
7165
7166 let start = ((insertion_start + tabstop_range.start) as usize)
7167 .min(snapshot.len());
7168 let end = ((insertion_start + tabstop_range.end) as usize)
7169 .min(snapshot.len());
7170 snapshot.anchor_before(start)..snapshot.anchor_after(end)
7171 })
7172 })
7173 .collect::<Vec<_>>();
7174 tabstop_ranges.sort_unstable_by(|a, b| a.start.cmp(&b.start, snapshot));
7175
7176 Tabstop {
7177 is_end_tabstop,
7178 ranges: tabstop_ranges,
7179 choices: tabstop.choices.clone(),
7180 }
7181 })
7182 .collect::<Vec<_>>()
7183 });
7184 if let Some(tabstop) = tabstops.first() {
7185 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
7186 s.select_ranges(tabstop.ranges.iter().cloned());
7187 });
7188
7189 if let Some(choices) = &tabstop.choices {
7190 if let Some(selection) = tabstop.ranges.first() {
7191 self.show_snippet_choices(choices, selection.clone(), cx)
7192 }
7193 }
7194
7195 // If we're already at the last tabstop and it's at the end of the snippet,
7196 // we're done, we don't need to keep the state around.
7197 if !tabstop.is_end_tabstop {
7198 let choices = tabstops
7199 .iter()
7200 .map(|tabstop| tabstop.choices.clone())
7201 .collect();
7202
7203 let ranges = tabstops
7204 .into_iter()
7205 .map(|tabstop| tabstop.ranges)
7206 .collect::<Vec<_>>();
7207
7208 self.snippet_stack.push(SnippetState {
7209 active_index: 0,
7210 ranges,
7211 choices,
7212 });
7213 }
7214
7215 // Check whether the just-entered snippet ends with an auto-closable bracket.
7216 if self.autoclose_regions.is_empty() {
7217 let snapshot = self.buffer.read(cx).snapshot(cx);
7218 for selection in &mut self.selections.all::<Point>(cx) {
7219 let selection_head = selection.head();
7220 let Some(scope) = snapshot.language_scope_at(selection_head) else {
7221 continue;
7222 };
7223
7224 let mut bracket_pair = None;
7225 let next_chars = snapshot.chars_at(selection_head).collect::<String>();
7226 let prev_chars = snapshot
7227 .reversed_chars_at(selection_head)
7228 .collect::<String>();
7229 for (pair, enabled) in scope.brackets() {
7230 if enabled
7231 && pair.close
7232 && prev_chars.starts_with(pair.start.as_str())
7233 && next_chars.starts_with(pair.end.as_str())
7234 {
7235 bracket_pair = Some(pair.clone());
7236 break;
7237 }
7238 }
7239 if let Some(pair) = bracket_pair {
7240 let start = snapshot.anchor_after(selection_head);
7241 let end = snapshot.anchor_after(selection_head);
7242 self.autoclose_regions.push(AutocloseRegion {
7243 selection_id: selection.id,
7244 range: start..end,
7245 pair,
7246 });
7247 }
7248 }
7249 }
7250 }
7251 Ok(())
7252 }
7253
7254 pub fn move_to_next_snippet_tabstop(
7255 &mut self,
7256 window: &mut Window,
7257 cx: &mut Context<Self>,
7258 ) -> bool {
7259 self.move_to_snippet_tabstop(Bias::Right, window, cx)
7260 }
7261
7262 pub fn move_to_prev_snippet_tabstop(
7263 &mut self,
7264 window: &mut Window,
7265 cx: &mut Context<Self>,
7266 ) -> bool {
7267 self.move_to_snippet_tabstop(Bias::Left, window, cx)
7268 }
7269
7270 pub fn move_to_snippet_tabstop(
7271 &mut self,
7272 bias: Bias,
7273 window: &mut Window,
7274 cx: &mut Context<Self>,
7275 ) -> bool {
7276 if let Some(mut snippet) = self.snippet_stack.pop() {
7277 match bias {
7278 Bias::Left => {
7279 if snippet.active_index > 0 {
7280 snippet.active_index -= 1;
7281 } else {
7282 self.snippet_stack.push(snippet);
7283 return false;
7284 }
7285 }
7286 Bias::Right => {
7287 if snippet.active_index + 1 < snippet.ranges.len() {
7288 snippet.active_index += 1;
7289 } else {
7290 self.snippet_stack.push(snippet);
7291 return false;
7292 }
7293 }
7294 }
7295 if let Some(current_ranges) = snippet.ranges.get(snippet.active_index) {
7296 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
7297 s.select_anchor_ranges(current_ranges.iter().cloned())
7298 });
7299
7300 if let Some(choices) = &snippet.choices[snippet.active_index] {
7301 if let Some(selection) = current_ranges.first() {
7302 self.show_snippet_choices(&choices, selection.clone(), cx);
7303 }
7304 }
7305
7306 // If snippet state is not at the last tabstop, push it back on the stack
7307 if snippet.active_index + 1 < snippet.ranges.len() {
7308 self.snippet_stack.push(snippet);
7309 }
7310 return true;
7311 }
7312 }
7313
7314 false
7315 }
7316
7317 pub fn clear(&mut self, window: &mut Window, cx: &mut Context<Self>) {
7318 self.transact(window, cx, |this, window, cx| {
7319 this.select_all(&SelectAll, window, cx);
7320 this.insert("", window, cx);
7321 });
7322 }
7323
7324 pub fn backspace(&mut self, _: &Backspace, window: &mut Window, cx: &mut Context<Self>) {
7325 self.transact(window, cx, |this, window, cx| {
7326 this.select_autoclose_pair(window, cx);
7327 let mut linked_ranges = HashMap::<_, Vec<_>>::default();
7328 if !this.linked_edit_ranges.is_empty() {
7329 let selections = this.selections.all::<MultiBufferPoint>(cx);
7330 let snapshot = this.buffer.read(cx).snapshot(cx);
7331
7332 for selection in selections.iter() {
7333 let selection_start = snapshot.anchor_before(selection.start).text_anchor;
7334 let selection_end = snapshot.anchor_after(selection.end).text_anchor;
7335 if selection_start.buffer_id != selection_end.buffer_id {
7336 continue;
7337 }
7338 if let Some(ranges) =
7339 this.linked_editing_ranges_for(selection_start..selection_end, cx)
7340 {
7341 for (buffer, entries) in ranges {
7342 linked_ranges.entry(buffer).or_default().extend(entries);
7343 }
7344 }
7345 }
7346 }
7347
7348 let mut selections = this.selections.all::<MultiBufferPoint>(cx);
7349 if !this.selections.line_mode {
7350 let display_map = this.display_map.update(cx, |map, cx| map.snapshot(cx));
7351 for selection in &mut selections {
7352 if selection.is_empty() {
7353 let old_head = selection.head();
7354 let mut new_head =
7355 movement::left(&display_map, old_head.to_display_point(&display_map))
7356 .to_point(&display_map);
7357 if let Some((buffer, line_buffer_range)) = display_map
7358 .buffer_snapshot
7359 .buffer_line_for_row(MultiBufferRow(old_head.row))
7360 {
7361 let indent_size =
7362 buffer.indent_size_for_line(line_buffer_range.start.row);
7363 let indent_len = match indent_size.kind {
7364 IndentKind::Space => {
7365 buffer.settings_at(line_buffer_range.start, cx).tab_size
7366 }
7367 IndentKind::Tab => NonZeroU32::new(1).unwrap(),
7368 };
7369 if old_head.column <= indent_size.len && old_head.column > 0 {
7370 let indent_len = indent_len.get();
7371 new_head = cmp::min(
7372 new_head,
7373 MultiBufferPoint::new(
7374 old_head.row,
7375 ((old_head.column - 1) / indent_len) * indent_len,
7376 ),
7377 );
7378 }
7379 }
7380
7381 selection.set_head(new_head, SelectionGoal::None);
7382 }
7383 }
7384 }
7385
7386 this.signature_help_state.set_backspace_pressed(true);
7387 this.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
7388 s.select(selections)
7389 });
7390 this.insert("", window, cx);
7391 let empty_str: Arc<str> = Arc::from("");
7392 for (buffer, edits) in linked_ranges {
7393 let snapshot = buffer.read(cx).snapshot();
7394 use text::ToPoint as TP;
7395
7396 let edits = edits
7397 .into_iter()
7398 .map(|range| {
7399 let end_point = TP::to_point(&range.end, &snapshot);
7400 let mut start_point = TP::to_point(&range.start, &snapshot);
7401
7402 if end_point == start_point {
7403 let offset = text::ToOffset::to_offset(&range.start, &snapshot)
7404 .saturating_sub(1);
7405 start_point =
7406 snapshot.clip_point(TP::to_point(&offset, &snapshot), Bias::Left);
7407 };
7408
7409 (start_point..end_point, empty_str.clone())
7410 })
7411 .sorted_by_key(|(range, _)| range.start)
7412 .collect::<Vec<_>>();
7413 buffer.update(cx, |this, cx| {
7414 this.edit(edits, None, cx);
7415 })
7416 }
7417 this.refresh_inline_completion(true, false, window, cx);
7418 linked_editing_ranges::refresh_linked_ranges(this, window, cx);
7419 });
7420 }
7421
7422 pub fn delete(&mut self, _: &Delete, window: &mut Window, cx: &mut Context<Self>) {
7423 self.transact(window, cx, |this, window, cx| {
7424 this.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
7425 let line_mode = s.line_mode;
7426 s.move_with(|map, selection| {
7427 if selection.is_empty() && !line_mode {
7428 let cursor = movement::right(map, selection.head());
7429 selection.end = cursor;
7430 selection.reversed = true;
7431 selection.goal = SelectionGoal::None;
7432 }
7433 })
7434 });
7435 this.insert("", window, cx);
7436 this.refresh_inline_completion(true, false, window, cx);
7437 });
7438 }
7439
7440 pub fn backtab(&mut self, _: &Backtab, window: &mut Window, cx: &mut Context<Self>) {
7441 if self.move_to_prev_snippet_tabstop(window, cx) {
7442 return;
7443 }
7444
7445 self.outdent(&Outdent, window, cx);
7446 }
7447
7448 pub fn tab(&mut self, _: &Tab, window: &mut Window, cx: &mut Context<Self>) {
7449 if self.move_to_next_snippet_tabstop(window, cx) || self.read_only(cx) {
7450 return;
7451 }
7452
7453 let mut selections = self.selections.all_adjusted(cx);
7454 let buffer = self.buffer.read(cx);
7455 let snapshot = buffer.snapshot(cx);
7456 let rows_iter = selections.iter().map(|s| s.head().row);
7457 let suggested_indents = snapshot.suggested_indents(rows_iter, cx);
7458
7459 let mut edits = Vec::new();
7460 let mut prev_edited_row = 0;
7461 let mut row_delta = 0;
7462 for selection in &mut selections {
7463 if selection.start.row != prev_edited_row {
7464 row_delta = 0;
7465 }
7466 prev_edited_row = selection.end.row;
7467
7468 // If the selection is non-empty, then increase the indentation of the selected lines.
7469 if !selection.is_empty() {
7470 row_delta =
7471 Self::indent_selection(buffer, &snapshot, selection, &mut edits, row_delta, cx);
7472 continue;
7473 }
7474
7475 // If the selection is empty and the cursor is in the leading whitespace before the
7476 // suggested indentation, then auto-indent the line.
7477 let cursor = selection.head();
7478 let current_indent = snapshot.indent_size_for_line(MultiBufferRow(cursor.row));
7479 if let Some(suggested_indent) =
7480 suggested_indents.get(&MultiBufferRow(cursor.row)).copied()
7481 {
7482 if cursor.column < suggested_indent.len
7483 && cursor.column <= current_indent.len
7484 && current_indent.len <= suggested_indent.len
7485 {
7486 selection.start = Point::new(cursor.row, suggested_indent.len);
7487 selection.end = selection.start;
7488 if row_delta == 0 {
7489 edits.extend(Buffer::edit_for_indent_size_adjustment(
7490 cursor.row,
7491 current_indent,
7492 suggested_indent,
7493 ));
7494 row_delta = suggested_indent.len - current_indent.len;
7495 }
7496 continue;
7497 }
7498 }
7499
7500 // Otherwise, insert a hard or soft tab.
7501 let settings = buffer.language_settings_at(cursor, cx);
7502 let tab_size = if settings.hard_tabs {
7503 IndentSize::tab()
7504 } else {
7505 let tab_size = settings.tab_size.get();
7506 let char_column = snapshot
7507 .text_for_range(Point::new(cursor.row, 0)..cursor)
7508 .flat_map(str::chars)
7509 .count()
7510 + row_delta as usize;
7511 let chars_to_next_tab_stop = tab_size - (char_column as u32 % tab_size);
7512 IndentSize::spaces(chars_to_next_tab_stop)
7513 };
7514 selection.start = Point::new(cursor.row, cursor.column + row_delta + tab_size.len);
7515 selection.end = selection.start;
7516 edits.push((cursor..cursor, tab_size.chars().collect::<String>()));
7517 row_delta += tab_size.len;
7518 }
7519
7520 self.transact(window, cx, |this, window, cx| {
7521 this.buffer.update(cx, |b, cx| b.edit(edits, None, cx));
7522 this.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
7523 s.select(selections)
7524 });
7525 this.refresh_inline_completion(true, false, window, cx);
7526 });
7527 }
7528
7529 pub fn indent(&mut self, _: &Indent, window: &mut Window, cx: &mut Context<Self>) {
7530 if self.read_only(cx) {
7531 return;
7532 }
7533 let mut selections = self.selections.all::<Point>(cx);
7534 let mut prev_edited_row = 0;
7535 let mut row_delta = 0;
7536 let mut edits = Vec::new();
7537 let buffer = self.buffer.read(cx);
7538 let snapshot = buffer.snapshot(cx);
7539 for selection in &mut selections {
7540 if selection.start.row != prev_edited_row {
7541 row_delta = 0;
7542 }
7543 prev_edited_row = selection.end.row;
7544
7545 row_delta =
7546 Self::indent_selection(buffer, &snapshot, selection, &mut edits, row_delta, cx);
7547 }
7548
7549 self.transact(window, cx, |this, window, cx| {
7550 this.buffer.update(cx, |b, cx| b.edit(edits, None, cx));
7551 this.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
7552 s.select(selections)
7553 });
7554 });
7555 }
7556
7557 fn indent_selection(
7558 buffer: &MultiBuffer,
7559 snapshot: &MultiBufferSnapshot,
7560 selection: &mut Selection<Point>,
7561 edits: &mut Vec<(Range<Point>, String)>,
7562 delta_for_start_row: u32,
7563 cx: &App,
7564 ) -> u32 {
7565 let settings = buffer.language_settings_at(selection.start, cx);
7566 let tab_size = settings.tab_size.get();
7567 let indent_kind = if settings.hard_tabs {
7568 IndentKind::Tab
7569 } else {
7570 IndentKind::Space
7571 };
7572 let mut start_row = selection.start.row;
7573 let mut end_row = selection.end.row + 1;
7574
7575 // If a selection ends at the beginning of a line, don't indent
7576 // that last line.
7577 if selection.end.column == 0 && selection.end.row > selection.start.row {
7578 end_row -= 1;
7579 }
7580
7581 // Avoid re-indenting a row that has already been indented by a
7582 // previous selection, but still update this selection's column
7583 // to reflect that indentation.
7584 if delta_for_start_row > 0 {
7585 start_row += 1;
7586 selection.start.column += delta_for_start_row;
7587 if selection.end.row == selection.start.row {
7588 selection.end.column += delta_for_start_row;
7589 }
7590 }
7591
7592 let mut delta_for_end_row = 0;
7593 let has_multiple_rows = start_row + 1 != end_row;
7594 for row in start_row..end_row {
7595 let current_indent = snapshot.indent_size_for_line(MultiBufferRow(row));
7596 let indent_delta = match (current_indent.kind, indent_kind) {
7597 (IndentKind::Space, IndentKind::Space) => {
7598 let columns_to_next_tab_stop = tab_size - (current_indent.len % tab_size);
7599 IndentSize::spaces(columns_to_next_tab_stop)
7600 }
7601 (IndentKind::Tab, IndentKind::Space) => IndentSize::spaces(tab_size),
7602 (_, IndentKind::Tab) => IndentSize::tab(),
7603 };
7604
7605 let start = if has_multiple_rows || current_indent.len < selection.start.column {
7606 0
7607 } else {
7608 selection.start.column
7609 };
7610 let row_start = Point::new(row, start);
7611 edits.push((
7612 row_start..row_start,
7613 indent_delta.chars().collect::<String>(),
7614 ));
7615
7616 // Update this selection's endpoints to reflect the indentation.
7617 if row == selection.start.row {
7618 selection.start.column += indent_delta.len;
7619 }
7620 if row == selection.end.row {
7621 selection.end.column += indent_delta.len;
7622 delta_for_end_row = indent_delta.len;
7623 }
7624 }
7625
7626 if selection.start.row == selection.end.row {
7627 delta_for_start_row + delta_for_end_row
7628 } else {
7629 delta_for_end_row
7630 }
7631 }
7632
7633 pub fn outdent(&mut self, _: &Outdent, window: &mut Window, cx: &mut Context<Self>) {
7634 if self.read_only(cx) {
7635 return;
7636 }
7637 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
7638 let selections = self.selections.all::<Point>(cx);
7639 let mut deletion_ranges = Vec::new();
7640 let mut last_outdent = None;
7641 {
7642 let buffer = self.buffer.read(cx);
7643 let snapshot = buffer.snapshot(cx);
7644 for selection in &selections {
7645 let settings = buffer.language_settings_at(selection.start, cx);
7646 let tab_size = settings.tab_size.get();
7647 let mut rows = selection.spanned_rows(false, &display_map);
7648
7649 // Avoid re-outdenting a row that has already been outdented by a
7650 // previous selection.
7651 if let Some(last_row) = last_outdent {
7652 if last_row == rows.start {
7653 rows.start = rows.start.next_row();
7654 }
7655 }
7656 let has_multiple_rows = rows.len() > 1;
7657 for row in rows.iter_rows() {
7658 let indent_size = snapshot.indent_size_for_line(row);
7659 if indent_size.len > 0 {
7660 let deletion_len = match indent_size.kind {
7661 IndentKind::Space => {
7662 let columns_to_prev_tab_stop = indent_size.len % tab_size;
7663 if columns_to_prev_tab_stop == 0 {
7664 tab_size
7665 } else {
7666 columns_to_prev_tab_stop
7667 }
7668 }
7669 IndentKind::Tab => 1,
7670 };
7671 let start = if has_multiple_rows
7672 || deletion_len > selection.start.column
7673 || indent_size.len < selection.start.column
7674 {
7675 0
7676 } else {
7677 selection.start.column - deletion_len
7678 };
7679 deletion_ranges.push(
7680 Point::new(row.0, start)..Point::new(row.0, start + deletion_len),
7681 );
7682 last_outdent = Some(row);
7683 }
7684 }
7685 }
7686 }
7687
7688 self.transact(window, cx, |this, window, cx| {
7689 this.buffer.update(cx, |buffer, cx| {
7690 let empty_str: Arc<str> = Arc::default();
7691 buffer.edit(
7692 deletion_ranges
7693 .into_iter()
7694 .map(|range| (range, empty_str.clone())),
7695 None,
7696 cx,
7697 );
7698 });
7699 let selections = this.selections.all::<usize>(cx);
7700 this.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
7701 s.select(selections)
7702 });
7703 });
7704 }
7705
7706 pub fn autoindent(&mut self, _: &AutoIndent, window: &mut Window, cx: &mut Context<Self>) {
7707 if self.read_only(cx) {
7708 return;
7709 }
7710 let selections = self
7711 .selections
7712 .all::<usize>(cx)
7713 .into_iter()
7714 .map(|s| s.range());
7715
7716 self.transact(window, cx, |this, window, cx| {
7717 this.buffer.update(cx, |buffer, cx| {
7718 buffer.autoindent_ranges(selections, cx);
7719 });
7720 let selections = this.selections.all::<usize>(cx);
7721 this.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
7722 s.select(selections)
7723 });
7724 });
7725 }
7726
7727 pub fn delete_line(&mut self, _: &DeleteLine, window: &mut Window, cx: &mut Context<Self>) {
7728 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
7729 let selections = self.selections.all::<Point>(cx);
7730
7731 let mut new_cursors = Vec::new();
7732 let mut edit_ranges = Vec::new();
7733 let mut selections = selections.iter().peekable();
7734 while let Some(selection) = selections.next() {
7735 let mut rows = selection.spanned_rows(false, &display_map);
7736 let goal_display_column = selection.head().to_display_point(&display_map).column();
7737
7738 // Accumulate contiguous regions of rows that we want to delete.
7739 while let Some(next_selection) = selections.peek() {
7740 let next_rows = next_selection.spanned_rows(false, &display_map);
7741 if next_rows.start <= rows.end {
7742 rows.end = next_rows.end;
7743 selections.next().unwrap();
7744 } else {
7745 break;
7746 }
7747 }
7748
7749 let buffer = &display_map.buffer_snapshot;
7750 let mut edit_start = Point::new(rows.start.0, 0).to_offset(buffer);
7751 let edit_end;
7752 let cursor_buffer_row;
7753 if buffer.max_point().row >= rows.end.0 {
7754 // If there's a line after the range, delete the \n from the end of the row range
7755 // and position the cursor on the next line.
7756 edit_end = Point::new(rows.end.0, 0).to_offset(buffer);
7757 cursor_buffer_row = rows.end;
7758 } else {
7759 // If there isn't a line after the range, delete the \n from the line before the
7760 // start of the row range and position the cursor there.
7761 edit_start = edit_start.saturating_sub(1);
7762 edit_end = buffer.len();
7763 cursor_buffer_row = rows.start.previous_row();
7764 }
7765
7766 let mut cursor = Point::new(cursor_buffer_row.0, 0).to_display_point(&display_map);
7767 *cursor.column_mut() =
7768 cmp::min(goal_display_column, display_map.line_len(cursor.row()));
7769
7770 new_cursors.push((
7771 selection.id,
7772 buffer.anchor_after(cursor.to_point(&display_map)),
7773 ));
7774 edit_ranges.push(edit_start..edit_end);
7775 }
7776
7777 self.transact(window, cx, |this, window, cx| {
7778 let buffer = this.buffer.update(cx, |buffer, cx| {
7779 let empty_str: Arc<str> = Arc::default();
7780 buffer.edit(
7781 edit_ranges
7782 .into_iter()
7783 .map(|range| (range, empty_str.clone())),
7784 None,
7785 cx,
7786 );
7787 buffer.snapshot(cx)
7788 });
7789 let new_selections = new_cursors
7790 .into_iter()
7791 .map(|(id, cursor)| {
7792 let cursor = cursor.to_point(&buffer);
7793 Selection {
7794 id,
7795 start: cursor,
7796 end: cursor,
7797 reversed: false,
7798 goal: SelectionGoal::None,
7799 }
7800 })
7801 .collect();
7802
7803 this.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
7804 s.select(new_selections);
7805 });
7806 });
7807 }
7808
7809 pub fn join_lines_impl(
7810 &mut self,
7811 insert_whitespace: bool,
7812 window: &mut Window,
7813 cx: &mut Context<Self>,
7814 ) {
7815 if self.read_only(cx) {
7816 return;
7817 }
7818 let mut row_ranges = Vec::<Range<MultiBufferRow>>::new();
7819 for selection in self.selections.all::<Point>(cx) {
7820 let start = MultiBufferRow(selection.start.row);
7821 // Treat single line selections as if they include the next line. Otherwise this action
7822 // would do nothing for single line selections individual cursors.
7823 let end = if selection.start.row == selection.end.row {
7824 MultiBufferRow(selection.start.row + 1)
7825 } else {
7826 MultiBufferRow(selection.end.row)
7827 };
7828
7829 if let Some(last_row_range) = row_ranges.last_mut() {
7830 if start <= last_row_range.end {
7831 last_row_range.end = end;
7832 continue;
7833 }
7834 }
7835 row_ranges.push(start..end);
7836 }
7837
7838 let snapshot = self.buffer.read(cx).snapshot(cx);
7839 let mut cursor_positions = Vec::new();
7840 for row_range in &row_ranges {
7841 let anchor = snapshot.anchor_before(Point::new(
7842 row_range.end.previous_row().0,
7843 snapshot.line_len(row_range.end.previous_row()),
7844 ));
7845 cursor_positions.push(anchor..anchor);
7846 }
7847
7848 self.transact(window, cx, |this, window, cx| {
7849 for row_range in row_ranges.into_iter().rev() {
7850 for row in row_range.iter_rows().rev() {
7851 let end_of_line = Point::new(row.0, snapshot.line_len(row));
7852 let next_line_row = row.next_row();
7853 let indent = snapshot.indent_size_for_line(next_line_row);
7854 let start_of_next_line = Point::new(next_line_row.0, indent.len);
7855
7856 let replace =
7857 if snapshot.line_len(next_line_row) > indent.len && insert_whitespace {
7858 " "
7859 } else {
7860 ""
7861 };
7862
7863 this.buffer.update(cx, |buffer, cx| {
7864 buffer.edit([(end_of_line..start_of_next_line, replace)], None, cx)
7865 });
7866 }
7867 }
7868
7869 this.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
7870 s.select_anchor_ranges(cursor_positions)
7871 });
7872 });
7873 }
7874
7875 pub fn join_lines(&mut self, _: &JoinLines, window: &mut Window, cx: &mut Context<Self>) {
7876 self.join_lines_impl(true, window, cx);
7877 }
7878
7879 pub fn sort_lines_case_sensitive(
7880 &mut self,
7881 _: &SortLinesCaseSensitive,
7882 window: &mut Window,
7883 cx: &mut Context<Self>,
7884 ) {
7885 self.manipulate_lines(window, cx, |lines| lines.sort())
7886 }
7887
7888 pub fn sort_lines_case_insensitive(
7889 &mut self,
7890 _: &SortLinesCaseInsensitive,
7891 window: &mut Window,
7892 cx: &mut Context<Self>,
7893 ) {
7894 self.manipulate_lines(window, cx, |lines| {
7895 lines.sort_by_key(|line| line.to_lowercase())
7896 })
7897 }
7898
7899 pub fn unique_lines_case_insensitive(
7900 &mut self,
7901 _: &UniqueLinesCaseInsensitive,
7902 window: &mut Window,
7903 cx: &mut Context<Self>,
7904 ) {
7905 self.manipulate_lines(window, cx, |lines| {
7906 let mut seen = HashSet::default();
7907 lines.retain(|line| seen.insert(line.to_lowercase()));
7908 })
7909 }
7910
7911 pub fn unique_lines_case_sensitive(
7912 &mut self,
7913 _: &UniqueLinesCaseSensitive,
7914 window: &mut Window,
7915 cx: &mut Context<Self>,
7916 ) {
7917 self.manipulate_lines(window, cx, |lines| {
7918 let mut seen = HashSet::default();
7919 lines.retain(|line| seen.insert(*line));
7920 })
7921 }
7922
7923 pub fn reload_file(&mut self, _: &ReloadFile, window: &mut Window, cx: &mut Context<Self>) {
7924 let Some(project) = self.project.clone() else {
7925 return;
7926 };
7927 self.reload(project, window, cx)
7928 .detach_and_notify_err(window, cx);
7929 }
7930
7931 pub fn restore_file(
7932 &mut self,
7933 _: &::git::RestoreFile,
7934 window: &mut Window,
7935 cx: &mut Context<Self>,
7936 ) {
7937 let mut buffer_ids = HashSet::default();
7938 let snapshot = self.buffer().read(cx).snapshot(cx);
7939 for selection in self.selections.all::<usize>(cx) {
7940 buffer_ids.extend(snapshot.buffer_ids_for_range(selection.range()))
7941 }
7942
7943 let buffer = self.buffer().read(cx);
7944 let ranges = buffer_ids
7945 .into_iter()
7946 .flat_map(|buffer_id| buffer.excerpt_ranges_for_buffer(buffer_id, cx))
7947 .collect::<Vec<_>>();
7948
7949 self.restore_hunks_in_ranges(ranges, window, cx);
7950 }
7951
7952 pub fn git_restore(&mut self, _: &Restore, window: &mut Window, cx: &mut Context<Self>) {
7953 let selections = self
7954 .selections
7955 .all(cx)
7956 .into_iter()
7957 .map(|s| s.range())
7958 .collect();
7959 self.restore_hunks_in_ranges(selections, window, cx);
7960 }
7961
7962 fn restore_hunks_in_ranges(
7963 &mut self,
7964 ranges: Vec<Range<Point>>,
7965 window: &mut Window,
7966 cx: &mut Context<Editor>,
7967 ) {
7968 let mut revert_changes = HashMap::default();
7969 let chunk_by = self
7970 .snapshot(window, cx)
7971 .hunks_for_ranges(ranges)
7972 .into_iter()
7973 .chunk_by(|hunk| hunk.buffer_id);
7974 for (buffer_id, hunks) in &chunk_by {
7975 let hunks = hunks.collect::<Vec<_>>();
7976 for hunk in &hunks {
7977 self.prepare_restore_change(&mut revert_changes, hunk, cx);
7978 }
7979 self.do_stage_or_unstage(false, buffer_id, hunks.into_iter(), cx);
7980 }
7981 drop(chunk_by);
7982 if !revert_changes.is_empty() {
7983 self.transact(window, cx, |editor, window, cx| {
7984 editor.restore(revert_changes, window, cx);
7985 });
7986 }
7987 }
7988
7989 pub fn open_active_item_in_terminal(
7990 &mut self,
7991 _: &OpenInTerminal,
7992 window: &mut Window,
7993 cx: &mut Context<Self>,
7994 ) {
7995 if let Some(working_directory) = self.active_excerpt(cx).and_then(|(_, buffer, _)| {
7996 let project_path = buffer.read(cx).project_path(cx)?;
7997 let project = self.project.as_ref()?.read(cx);
7998 let entry = project.entry_for_path(&project_path, cx)?;
7999 let parent = match &entry.canonical_path {
8000 Some(canonical_path) => canonical_path.to_path_buf(),
8001 None => project.absolute_path(&project_path, cx)?,
8002 }
8003 .parent()?
8004 .to_path_buf();
8005 Some(parent)
8006 }) {
8007 window.dispatch_action(OpenTerminal { working_directory }.boxed_clone(), cx);
8008 }
8009 }
8010
8011 pub fn prepare_restore_change(
8012 &self,
8013 revert_changes: &mut HashMap<BufferId, Vec<(Range<text::Anchor>, Rope)>>,
8014 hunk: &MultiBufferDiffHunk,
8015 cx: &mut App,
8016 ) -> Option<()> {
8017 if hunk.is_created_file() {
8018 return None;
8019 }
8020 let buffer = self.buffer.read(cx);
8021 let diff = buffer.diff_for(hunk.buffer_id)?;
8022 let buffer = buffer.buffer(hunk.buffer_id)?;
8023 let buffer = buffer.read(cx);
8024 let original_text = diff
8025 .read(cx)
8026 .base_text()
8027 .as_rope()
8028 .slice(hunk.diff_base_byte_range.clone());
8029 let buffer_snapshot = buffer.snapshot();
8030 let buffer_revert_changes = revert_changes.entry(buffer.remote_id()).or_default();
8031 if let Err(i) = buffer_revert_changes.binary_search_by(|probe| {
8032 probe
8033 .0
8034 .start
8035 .cmp(&hunk.buffer_range.start, &buffer_snapshot)
8036 .then(probe.0.end.cmp(&hunk.buffer_range.end, &buffer_snapshot))
8037 }) {
8038 buffer_revert_changes.insert(i, (hunk.buffer_range.clone(), original_text));
8039 Some(())
8040 } else {
8041 None
8042 }
8043 }
8044
8045 pub fn reverse_lines(&mut self, _: &ReverseLines, window: &mut Window, cx: &mut Context<Self>) {
8046 self.manipulate_lines(window, cx, |lines| lines.reverse())
8047 }
8048
8049 pub fn shuffle_lines(&mut self, _: &ShuffleLines, window: &mut Window, cx: &mut Context<Self>) {
8050 self.manipulate_lines(window, cx, |lines| lines.shuffle(&mut thread_rng()))
8051 }
8052
8053 fn manipulate_lines<Fn>(
8054 &mut self,
8055 window: &mut Window,
8056 cx: &mut Context<Self>,
8057 mut callback: Fn,
8058 ) where
8059 Fn: FnMut(&mut Vec<&str>),
8060 {
8061 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
8062 let buffer = self.buffer.read(cx).snapshot(cx);
8063
8064 let mut edits = Vec::new();
8065
8066 let selections = self.selections.all::<Point>(cx);
8067 let mut selections = selections.iter().peekable();
8068 let mut contiguous_row_selections = Vec::new();
8069 let mut new_selections = Vec::new();
8070 let mut added_lines = 0;
8071 let mut removed_lines = 0;
8072
8073 while let Some(selection) = selections.next() {
8074 let (start_row, end_row) = consume_contiguous_rows(
8075 &mut contiguous_row_selections,
8076 selection,
8077 &display_map,
8078 &mut selections,
8079 );
8080
8081 let start_point = Point::new(start_row.0, 0);
8082 let end_point = Point::new(
8083 end_row.previous_row().0,
8084 buffer.line_len(end_row.previous_row()),
8085 );
8086 let text = buffer
8087 .text_for_range(start_point..end_point)
8088 .collect::<String>();
8089
8090 let mut lines = text.split('\n').collect_vec();
8091
8092 let lines_before = lines.len();
8093 callback(&mut lines);
8094 let lines_after = lines.len();
8095
8096 edits.push((start_point..end_point, lines.join("\n")));
8097
8098 // Selections must change based on added and removed line count
8099 let start_row =
8100 MultiBufferRow(start_point.row + added_lines as u32 - removed_lines as u32);
8101 let end_row = MultiBufferRow(start_row.0 + lines_after.saturating_sub(1) as u32);
8102 new_selections.push(Selection {
8103 id: selection.id,
8104 start: start_row,
8105 end: end_row,
8106 goal: SelectionGoal::None,
8107 reversed: selection.reversed,
8108 });
8109
8110 if lines_after > lines_before {
8111 added_lines += lines_after - lines_before;
8112 } else if lines_before > lines_after {
8113 removed_lines += lines_before - lines_after;
8114 }
8115 }
8116
8117 self.transact(window, cx, |this, window, cx| {
8118 let buffer = this.buffer.update(cx, |buffer, cx| {
8119 buffer.edit(edits, None, cx);
8120 buffer.snapshot(cx)
8121 });
8122
8123 // Recalculate offsets on newly edited buffer
8124 let new_selections = new_selections
8125 .iter()
8126 .map(|s| {
8127 let start_point = Point::new(s.start.0, 0);
8128 let end_point = Point::new(s.end.0, buffer.line_len(s.end));
8129 Selection {
8130 id: s.id,
8131 start: buffer.point_to_offset(start_point),
8132 end: buffer.point_to_offset(end_point),
8133 goal: s.goal,
8134 reversed: s.reversed,
8135 }
8136 })
8137 .collect();
8138
8139 this.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
8140 s.select(new_selections);
8141 });
8142
8143 this.request_autoscroll(Autoscroll::fit(), cx);
8144 });
8145 }
8146
8147 pub fn convert_to_upper_case(
8148 &mut self,
8149 _: &ConvertToUpperCase,
8150 window: &mut Window,
8151 cx: &mut Context<Self>,
8152 ) {
8153 self.manipulate_text(window, cx, |text| text.to_uppercase())
8154 }
8155
8156 pub fn convert_to_lower_case(
8157 &mut self,
8158 _: &ConvertToLowerCase,
8159 window: &mut Window,
8160 cx: &mut Context<Self>,
8161 ) {
8162 self.manipulate_text(window, cx, |text| text.to_lowercase())
8163 }
8164
8165 pub fn convert_to_title_case(
8166 &mut self,
8167 _: &ConvertToTitleCase,
8168 window: &mut Window,
8169 cx: &mut Context<Self>,
8170 ) {
8171 self.manipulate_text(window, cx, |text| {
8172 text.split('\n')
8173 .map(|line| line.to_case(Case::Title))
8174 .join("\n")
8175 })
8176 }
8177
8178 pub fn convert_to_snake_case(
8179 &mut self,
8180 _: &ConvertToSnakeCase,
8181 window: &mut Window,
8182 cx: &mut Context<Self>,
8183 ) {
8184 self.manipulate_text(window, cx, |text| text.to_case(Case::Snake))
8185 }
8186
8187 pub fn convert_to_kebab_case(
8188 &mut self,
8189 _: &ConvertToKebabCase,
8190 window: &mut Window,
8191 cx: &mut Context<Self>,
8192 ) {
8193 self.manipulate_text(window, cx, |text| text.to_case(Case::Kebab))
8194 }
8195
8196 pub fn convert_to_upper_camel_case(
8197 &mut self,
8198 _: &ConvertToUpperCamelCase,
8199 window: &mut Window,
8200 cx: &mut Context<Self>,
8201 ) {
8202 self.manipulate_text(window, cx, |text| {
8203 text.split('\n')
8204 .map(|line| line.to_case(Case::UpperCamel))
8205 .join("\n")
8206 })
8207 }
8208
8209 pub fn convert_to_lower_camel_case(
8210 &mut self,
8211 _: &ConvertToLowerCamelCase,
8212 window: &mut Window,
8213 cx: &mut Context<Self>,
8214 ) {
8215 self.manipulate_text(window, cx, |text| text.to_case(Case::Camel))
8216 }
8217
8218 pub fn convert_to_opposite_case(
8219 &mut self,
8220 _: &ConvertToOppositeCase,
8221 window: &mut Window,
8222 cx: &mut Context<Self>,
8223 ) {
8224 self.manipulate_text(window, cx, |text| {
8225 text.chars()
8226 .fold(String::with_capacity(text.len()), |mut t, c| {
8227 if c.is_uppercase() {
8228 t.extend(c.to_lowercase());
8229 } else {
8230 t.extend(c.to_uppercase());
8231 }
8232 t
8233 })
8234 })
8235 }
8236
8237 fn manipulate_text<Fn>(&mut self, window: &mut Window, cx: &mut Context<Self>, mut callback: Fn)
8238 where
8239 Fn: FnMut(&str) -> String,
8240 {
8241 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
8242 let buffer = self.buffer.read(cx).snapshot(cx);
8243
8244 let mut new_selections = Vec::new();
8245 let mut edits = Vec::new();
8246 let mut selection_adjustment = 0i32;
8247
8248 for selection in self.selections.all::<usize>(cx) {
8249 let selection_is_empty = selection.is_empty();
8250
8251 let (start, end) = if selection_is_empty {
8252 let word_range = movement::surrounding_word(
8253 &display_map,
8254 selection.start.to_display_point(&display_map),
8255 );
8256 let start = word_range.start.to_offset(&display_map, Bias::Left);
8257 let end = word_range.end.to_offset(&display_map, Bias::Left);
8258 (start, end)
8259 } else {
8260 (selection.start, selection.end)
8261 };
8262
8263 let text = buffer.text_for_range(start..end).collect::<String>();
8264 let old_length = text.len() as i32;
8265 let text = callback(&text);
8266
8267 new_selections.push(Selection {
8268 start: (start as i32 - selection_adjustment) as usize,
8269 end: ((start + text.len()) as i32 - selection_adjustment) as usize,
8270 goal: SelectionGoal::None,
8271 ..selection
8272 });
8273
8274 selection_adjustment += old_length - text.len() as i32;
8275
8276 edits.push((start..end, text));
8277 }
8278
8279 self.transact(window, cx, |this, window, cx| {
8280 this.buffer.update(cx, |buffer, cx| {
8281 buffer.edit(edits, None, cx);
8282 });
8283
8284 this.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
8285 s.select(new_selections);
8286 });
8287
8288 this.request_autoscroll(Autoscroll::fit(), cx);
8289 });
8290 }
8291
8292 pub fn duplicate(
8293 &mut self,
8294 upwards: bool,
8295 whole_lines: bool,
8296 window: &mut Window,
8297 cx: &mut Context<Self>,
8298 ) {
8299 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
8300 let buffer = &display_map.buffer_snapshot;
8301 let selections = self.selections.all::<Point>(cx);
8302
8303 let mut edits = Vec::new();
8304 let mut selections_iter = selections.iter().peekable();
8305 while let Some(selection) = selections_iter.next() {
8306 let mut rows = selection.spanned_rows(false, &display_map);
8307 // duplicate line-wise
8308 if whole_lines || selection.start == selection.end {
8309 // Avoid duplicating the same lines twice.
8310 while let Some(next_selection) = selections_iter.peek() {
8311 let next_rows = next_selection.spanned_rows(false, &display_map);
8312 if next_rows.start < rows.end {
8313 rows.end = next_rows.end;
8314 selections_iter.next().unwrap();
8315 } else {
8316 break;
8317 }
8318 }
8319
8320 // Copy the text from the selected row region and splice it either at the start
8321 // or end of the region.
8322 let start = Point::new(rows.start.0, 0);
8323 let end = Point::new(
8324 rows.end.previous_row().0,
8325 buffer.line_len(rows.end.previous_row()),
8326 );
8327 let text = buffer
8328 .text_for_range(start..end)
8329 .chain(Some("\n"))
8330 .collect::<String>();
8331 let insert_location = if upwards {
8332 Point::new(rows.end.0, 0)
8333 } else {
8334 start
8335 };
8336 edits.push((insert_location..insert_location, text));
8337 } else {
8338 // duplicate character-wise
8339 let start = selection.start;
8340 let end = selection.end;
8341 let text = buffer.text_for_range(start..end).collect::<String>();
8342 edits.push((selection.end..selection.end, text));
8343 }
8344 }
8345
8346 self.transact(window, cx, |this, _, cx| {
8347 this.buffer.update(cx, |buffer, cx| {
8348 buffer.edit(edits, None, cx);
8349 });
8350
8351 this.request_autoscroll(Autoscroll::fit(), cx);
8352 });
8353 }
8354
8355 pub fn duplicate_line_up(
8356 &mut self,
8357 _: &DuplicateLineUp,
8358 window: &mut Window,
8359 cx: &mut Context<Self>,
8360 ) {
8361 self.duplicate(true, true, window, cx);
8362 }
8363
8364 pub fn duplicate_line_down(
8365 &mut self,
8366 _: &DuplicateLineDown,
8367 window: &mut Window,
8368 cx: &mut Context<Self>,
8369 ) {
8370 self.duplicate(false, true, window, cx);
8371 }
8372
8373 pub fn duplicate_selection(
8374 &mut self,
8375 _: &DuplicateSelection,
8376 window: &mut Window,
8377 cx: &mut Context<Self>,
8378 ) {
8379 self.duplicate(false, false, window, cx);
8380 }
8381
8382 pub fn move_line_up(&mut self, _: &MoveLineUp, window: &mut Window, cx: &mut Context<Self>) {
8383 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
8384 let buffer = self.buffer.read(cx).snapshot(cx);
8385
8386 let mut edits = Vec::new();
8387 let mut unfold_ranges = Vec::new();
8388 let mut refold_creases = Vec::new();
8389
8390 let selections = self.selections.all::<Point>(cx);
8391 let mut selections = selections.iter().peekable();
8392 let mut contiguous_row_selections = Vec::new();
8393 let mut new_selections = Vec::new();
8394
8395 while let Some(selection) = selections.next() {
8396 // Find all the selections that span a contiguous row range
8397 let (start_row, end_row) = consume_contiguous_rows(
8398 &mut contiguous_row_selections,
8399 selection,
8400 &display_map,
8401 &mut selections,
8402 );
8403
8404 // Move the text spanned by the row range to be before the line preceding the row range
8405 if start_row.0 > 0 {
8406 let range_to_move = Point::new(
8407 start_row.previous_row().0,
8408 buffer.line_len(start_row.previous_row()),
8409 )
8410 ..Point::new(
8411 end_row.previous_row().0,
8412 buffer.line_len(end_row.previous_row()),
8413 );
8414 let insertion_point = display_map
8415 .prev_line_boundary(Point::new(start_row.previous_row().0, 0))
8416 .0;
8417
8418 // Don't move lines across excerpts
8419 if buffer
8420 .excerpt_containing(insertion_point..range_to_move.end)
8421 .is_some()
8422 {
8423 let text = buffer
8424 .text_for_range(range_to_move.clone())
8425 .flat_map(|s| s.chars())
8426 .skip(1)
8427 .chain(['\n'])
8428 .collect::<String>();
8429
8430 edits.push((
8431 buffer.anchor_after(range_to_move.start)
8432 ..buffer.anchor_before(range_to_move.end),
8433 String::new(),
8434 ));
8435 let insertion_anchor = buffer.anchor_after(insertion_point);
8436 edits.push((insertion_anchor..insertion_anchor, text));
8437
8438 let row_delta = range_to_move.start.row - insertion_point.row + 1;
8439
8440 // Move selections up
8441 new_selections.extend(contiguous_row_selections.drain(..).map(
8442 |mut selection| {
8443 selection.start.row -= row_delta;
8444 selection.end.row -= row_delta;
8445 selection
8446 },
8447 ));
8448
8449 // Move folds up
8450 unfold_ranges.push(range_to_move.clone());
8451 for fold in display_map.folds_in_range(
8452 buffer.anchor_before(range_to_move.start)
8453 ..buffer.anchor_after(range_to_move.end),
8454 ) {
8455 let mut start = fold.range.start.to_point(&buffer);
8456 let mut end = fold.range.end.to_point(&buffer);
8457 start.row -= row_delta;
8458 end.row -= row_delta;
8459 refold_creases.push(Crease::simple(start..end, fold.placeholder.clone()));
8460 }
8461 }
8462 }
8463
8464 // If we didn't move line(s), preserve the existing selections
8465 new_selections.append(&mut contiguous_row_selections);
8466 }
8467
8468 self.transact(window, cx, |this, window, cx| {
8469 this.unfold_ranges(&unfold_ranges, true, true, cx);
8470 this.buffer.update(cx, |buffer, cx| {
8471 for (range, text) in edits {
8472 buffer.edit([(range, text)], None, cx);
8473 }
8474 });
8475 this.fold_creases(refold_creases, true, window, cx);
8476 this.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
8477 s.select(new_selections);
8478 })
8479 });
8480 }
8481
8482 pub fn move_line_down(
8483 &mut self,
8484 _: &MoveLineDown,
8485 window: &mut Window,
8486 cx: &mut Context<Self>,
8487 ) {
8488 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
8489 let buffer = self.buffer.read(cx).snapshot(cx);
8490
8491 let mut edits = Vec::new();
8492 let mut unfold_ranges = Vec::new();
8493 let mut refold_creases = Vec::new();
8494
8495 let selections = self.selections.all::<Point>(cx);
8496 let mut selections = selections.iter().peekable();
8497 let mut contiguous_row_selections = Vec::new();
8498 let mut new_selections = Vec::new();
8499
8500 while let Some(selection) = selections.next() {
8501 // Find all the selections that span a contiguous row range
8502 let (start_row, end_row) = consume_contiguous_rows(
8503 &mut contiguous_row_selections,
8504 selection,
8505 &display_map,
8506 &mut selections,
8507 );
8508
8509 // Move the text spanned by the row range to be after the last line of the row range
8510 if end_row.0 <= buffer.max_point().row {
8511 let range_to_move =
8512 MultiBufferPoint::new(start_row.0, 0)..MultiBufferPoint::new(end_row.0, 0);
8513 let insertion_point = display_map
8514 .next_line_boundary(MultiBufferPoint::new(end_row.0, 0))
8515 .0;
8516
8517 // Don't move lines across excerpt boundaries
8518 if buffer
8519 .excerpt_containing(range_to_move.start..insertion_point)
8520 .is_some()
8521 {
8522 let mut text = String::from("\n");
8523 text.extend(buffer.text_for_range(range_to_move.clone()));
8524 text.pop(); // Drop trailing newline
8525 edits.push((
8526 buffer.anchor_after(range_to_move.start)
8527 ..buffer.anchor_before(range_to_move.end),
8528 String::new(),
8529 ));
8530 let insertion_anchor = buffer.anchor_after(insertion_point);
8531 edits.push((insertion_anchor..insertion_anchor, text));
8532
8533 let row_delta = insertion_point.row - range_to_move.end.row + 1;
8534
8535 // Move selections down
8536 new_selections.extend(contiguous_row_selections.drain(..).map(
8537 |mut selection| {
8538 selection.start.row += row_delta;
8539 selection.end.row += row_delta;
8540 selection
8541 },
8542 ));
8543
8544 // Move folds down
8545 unfold_ranges.push(range_to_move.clone());
8546 for fold in display_map.folds_in_range(
8547 buffer.anchor_before(range_to_move.start)
8548 ..buffer.anchor_after(range_to_move.end),
8549 ) {
8550 let mut start = fold.range.start.to_point(&buffer);
8551 let mut end = fold.range.end.to_point(&buffer);
8552 start.row += row_delta;
8553 end.row += row_delta;
8554 refold_creases.push(Crease::simple(start..end, fold.placeholder.clone()));
8555 }
8556 }
8557 }
8558
8559 // If we didn't move line(s), preserve the existing selections
8560 new_selections.append(&mut contiguous_row_selections);
8561 }
8562
8563 self.transact(window, cx, |this, window, cx| {
8564 this.unfold_ranges(&unfold_ranges, true, true, cx);
8565 this.buffer.update(cx, |buffer, cx| {
8566 for (range, text) in edits {
8567 buffer.edit([(range, text)], None, cx);
8568 }
8569 });
8570 this.fold_creases(refold_creases, true, window, cx);
8571 this.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
8572 s.select(new_selections)
8573 });
8574 });
8575 }
8576
8577 pub fn transpose(&mut self, _: &Transpose, window: &mut Window, cx: &mut Context<Self>) {
8578 let text_layout_details = &self.text_layout_details(window);
8579 self.transact(window, cx, |this, window, cx| {
8580 let edits = this.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
8581 let mut edits: Vec<(Range<usize>, String)> = Default::default();
8582 let line_mode = s.line_mode;
8583 s.move_with(|display_map, selection| {
8584 if !selection.is_empty() || line_mode {
8585 return;
8586 }
8587
8588 let mut head = selection.head();
8589 let mut transpose_offset = head.to_offset(display_map, Bias::Right);
8590 if head.column() == display_map.line_len(head.row()) {
8591 transpose_offset = display_map
8592 .buffer_snapshot
8593 .clip_offset(transpose_offset.saturating_sub(1), Bias::Left);
8594 }
8595
8596 if transpose_offset == 0 {
8597 return;
8598 }
8599
8600 *head.column_mut() += 1;
8601 head = display_map.clip_point(head, Bias::Right);
8602 let goal = SelectionGoal::HorizontalPosition(
8603 display_map
8604 .x_for_display_point(head, text_layout_details)
8605 .into(),
8606 );
8607 selection.collapse_to(head, goal);
8608
8609 let transpose_start = display_map
8610 .buffer_snapshot
8611 .clip_offset(transpose_offset.saturating_sub(1), Bias::Left);
8612 if edits.last().map_or(true, |e| e.0.end <= transpose_start) {
8613 let transpose_end = display_map
8614 .buffer_snapshot
8615 .clip_offset(transpose_offset + 1, Bias::Right);
8616 if let Some(ch) =
8617 display_map.buffer_snapshot.chars_at(transpose_start).next()
8618 {
8619 edits.push((transpose_start..transpose_offset, String::new()));
8620 edits.push((transpose_end..transpose_end, ch.to_string()));
8621 }
8622 }
8623 });
8624 edits
8625 });
8626 this.buffer
8627 .update(cx, |buffer, cx| buffer.edit(edits, None, cx));
8628 let selections = this.selections.all::<usize>(cx);
8629 this.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
8630 s.select(selections);
8631 });
8632 });
8633 }
8634
8635 pub fn rewrap(&mut self, _: &Rewrap, _: &mut Window, cx: &mut Context<Self>) {
8636 self.rewrap_impl(false, cx)
8637 }
8638
8639 pub fn rewrap_impl(&mut self, override_language_settings: bool, cx: &mut Context<Self>) {
8640 let buffer = self.buffer.read(cx).snapshot(cx);
8641 let selections = self.selections.all::<Point>(cx);
8642 let mut selections = selections.iter().peekable();
8643
8644 let mut edits = Vec::new();
8645 let mut rewrapped_row_ranges = Vec::<RangeInclusive<u32>>::new();
8646
8647 while let Some(selection) = selections.next() {
8648 let mut start_row = selection.start.row;
8649 let mut end_row = selection.end.row;
8650
8651 // Skip selections that overlap with a range that has already been rewrapped.
8652 let selection_range = start_row..end_row;
8653 if rewrapped_row_ranges
8654 .iter()
8655 .any(|range| range.overlaps(&selection_range))
8656 {
8657 continue;
8658 }
8659
8660 let tab_size = buffer.language_settings_at(selection.head(), cx).tab_size;
8661
8662 // Since not all lines in the selection may be at the same indent
8663 // level, choose the indent size that is the most common between all
8664 // of the lines.
8665 //
8666 // If there is a tie, we use the deepest indent.
8667 let (indent_size, indent_end) = {
8668 let mut indent_size_occurrences = HashMap::default();
8669 let mut rows_by_indent_size = HashMap::<IndentSize, Vec<u32>>::default();
8670
8671 for row in start_row..=end_row {
8672 let indent = buffer.indent_size_for_line(MultiBufferRow(row));
8673 rows_by_indent_size.entry(indent).or_default().push(row);
8674 *indent_size_occurrences.entry(indent).or_insert(0) += 1;
8675 }
8676
8677 let indent_size = indent_size_occurrences
8678 .into_iter()
8679 .max_by_key(|(indent, count)| (*count, indent.len_with_expanded_tabs(tab_size)))
8680 .map(|(indent, _)| indent)
8681 .unwrap_or_default();
8682 let row = rows_by_indent_size[&indent_size][0];
8683 let indent_end = Point::new(row, indent_size.len);
8684
8685 (indent_size, indent_end)
8686 };
8687
8688 let mut line_prefix = indent_size.chars().collect::<String>();
8689
8690 let mut inside_comment = false;
8691 if let Some(comment_prefix) =
8692 buffer
8693 .language_scope_at(selection.head())
8694 .and_then(|language| {
8695 language
8696 .line_comment_prefixes()
8697 .iter()
8698 .find(|prefix| buffer.contains_str_at(indent_end, prefix))
8699 .cloned()
8700 })
8701 {
8702 line_prefix.push_str(&comment_prefix);
8703 inside_comment = true;
8704 }
8705
8706 let language_settings = buffer.language_settings_at(selection.head(), cx);
8707 let allow_rewrap_based_on_language = match language_settings.allow_rewrap {
8708 RewrapBehavior::InComments => inside_comment,
8709 RewrapBehavior::InSelections => !selection.is_empty(),
8710 RewrapBehavior::Anywhere => true,
8711 };
8712
8713 let should_rewrap = override_language_settings
8714 || allow_rewrap_based_on_language
8715 || self.hard_wrap.is_some();
8716 if !should_rewrap {
8717 continue;
8718 }
8719
8720 if selection.is_empty() {
8721 'expand_upwards: while start_row > 0 {
8722 let prev_row = start_row - 1;
8723 if buffer.contains_str_at(Point::new(prev_row, 0), &line_prefix)
8724 && buffer.line_len(MultiBufferRow(prev_row)) as usize > line_prefix.len()
8725 {
8726 start_row = prev_row;
8727 } else {
8728 break 'expand_upwards;
8729 }
8730 }
8731
8732 'expand_downwards: while end_row < buffer.max_point().row {
8733 let next_row = end_row + 1;
8734 if buffer.contains_str_at(Point::new(next_row, 0), &line_prefix)
8735 && buffer.line_len(MultiBufferRow(next_row)) as usize > line_prefix.len()
8736 {
8737 end_row = next_row;
8738 } else {
8739 break 'expand_downwards;
8740 }
8741 }
8742 }
8743
8744 let start = Point::new(start_row, 0);
8745 let start_offset = start.to_offset(&buffer);
8746 let end = Point::new(end_row, buffer.line_len(MultiBufferRow(end_row)));
8747 let selection_text = buffer.text_for_range(start..end).collect::<String>();
8748 let Some(lines_without_prefixes) = selection_text
8749 .lines()
8750 .map(|line| {
8751 line.strip_prefix(&line_prefix)
8752 .or_else(|| line.trim_start().strip_prefix(&line_prefix.trim_start()))
8753 .ok_or_else(|| {
8754 anyhow!("line did not start with prefix {line_prefix:?}: {line:?}")
8755 })
8756 })
8757 .collect::<Result<Vec<_>, _>>()
8758 .log_err()
8759 else {
8760 continue;
8761 };
8762
8763 let wrap_column = self.hard_wrap.unwrap_or_else(|| {
8764 buffer
8765 .language_settings_at(Point::new(start_row, 0), cx)
8766 .preferred_line_length as usize
8767 });
8768 let wrapped_text = wrap_with_prefix(
8769 line_prefix,
8770 lines_without_prefixes.join(" "),
8771 wrap_column,
8772 tab_size,
8773 );
8774
8775 // TODO: should always use char-based diff while still supporting cursor behavior that
8776 // matches vim.
8777 let mut diff_options = DiffOptions::default();
8778 if override_language_settings {
8779 diff_options.max_word_diff_len = 0;
8780 diff_options.max_word_diff_line_count = 0;
8781 } else {
8782 diff_options.max_word_diff_len = usize::MAX;
8783 diff_options.max_word_diff_line_count = usize::MAX;
8784 }
8785
8786 for (old_range, new_text) in
8787 text_diff_with_options(&selection_text, &wrapped_text, diff_options)
8788 {
8789 let edit_start = buffer.anchor_after(start_offset + old_range.start);
8790 let edit_end = buffer.anchor_after(start_offset + old_range.end);
8791 edits.push((edit_start..edit_end, new_text));
8792 }
8793
8794 rewrapped_row_ranges.push(start_row..=end_row);
8795 }
8796
8797 self.buffer
8798 .update(cx, |buffer, cx| buffer.edit(edits, None, cx));
8799 }
8800
8801 pub fn cut_common(&mut self, window: &mut Window, cx: &mut Context<Self>) -> ClipboardItem {
8802 let mut text = String::new();
8803 let buffer = self.buffer.read(cx).snapshot(cx);
8804 let mut selections = self.selections.all::<Point>(cx);
8805 let mut clipboard_selections = Vec::with_capacity(selections.len());
8806 {
8807 let max_point = buffer.max_point();
8808 let mut is_first = true;
8809 for selection in &mut selections {
8810 let is_entire_line = selection.is_empty() || self.selections.line_mode;
8811 if is_entire_line {
8812 selection.start = Point::new(selection.start.row, 0);
8813 if !selection.is_empty() && selection.end.column == 0 {
8814 selection.end = cmp::min(max_point, selection.end);
8815 } else {
8816 selection.end = cmp::min(max_point, Point::new(selection.end.row + 1, 0));
8817 }
8818 selection.goal = SelectionGoal::None;
8819 }
8820 if is_first {
8821 is_first = false;
8822 } else {
8823 text += "\n";
8824 }
8825 let mut len = 0;
8826 for chunk in buffer.text_for_range(selection.start..selection.end) {
8827 text.push_str(chunk);
8828 len += chunk.len();
8829 }
8830 clipboard_selections.push(ClipboardSelection {
8831 len,
8832 is_entire_line,
8833 first_line_indent: buffer
8834 .indent_size_for_line(MultiBufferRow(selection.start.row))
8835 .len,
8836 });
8837 }
8838 }
8839
8840 self.transact(window, cx, |this, window, cx| {
8841 this.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
8842 s.select(selections);
8843 });
8844 this.insert("", window, cx);
8845 });
8846 ClipboardItem::new_string_with_json_metadata(text, clipboard_selections)
8847 }
8848
8849 pub fn cut(&mut self, _: &Cut, window: &mut Window, cx: &mut Context<Self>) {
8850 let item = self.cut_common(window, cx);
8851 cx.write_to_clipboard(item);
8852 }
8853
8854 pub fn kill_ring_cut(&mut self, _: &KillRingCut, window: &mut Window, cx: &mut Context<Self>) {
8855 self.change_selections(None, window, cx, |s| {
8856 s.move_with(|snapshot, sel| {
8857 if sel.is_empty() {
8858 sel.end = DisplayPoint::new(sel.end.row(), snapshot.line_len(sel.end.row()))
8859 }
8860 });
8861 });
8862 let item = self.cut_common(window, cx);
8863 cx.set_global(KillRing(item))
8864 }
8865
8866 pub fn kill_ring_yank(
8867 &mut self,
8868 _: &KillRingYank,
8869 window: &mut Window,
8870 cx: &mut Context<Self>,
8871 ) {
8872 let (text, metadata) = if let Some(KillRing(item)) = cx.try_global() {
8873 if let Some(ClipboardEntry::String(kill_ring)) = item.entries().first() {
8874 (kill_ring.text().to_string(), kill_ring.metadata_json())
8875 } else {
8876 return;
8877 }
8878 } else {
8879 return;
8880 };
8881 self.do_paste(&text, metadata, false, window, cx);
8882 }
8883
8884 pub fn copy(&mut self, _: &Copy, _: &mut Window, cx: &mut Context<Self>) {
8885 let selections = self.selections.all::<Point>(cx);
8886 let buffer = self.buffer.read(cx).read(cx);
8887 let mut text = String::new();
8888
8889 let mut clipboard_selections = Vec::with_capacity(selections.len());
8890 {
8891 let max_point = buffer.max_point();
8892 let mut is_first = true;
8893 for selection in selections.iter() {
8894 let mut start = selection.start;
8895 let mut end = selection.end;
8896 let is_entire_line = selection.is_empty() || self.selections.line_mode;
8897 if is_entire_line {
8898 start = Point::new(start.row, 0);
8899 end = cmp::min(max_point, Point::new(end.row + 1, 0));
8900 }
8901 if is_first {
8902 is_first = false;
8903 } else {
8904 text += "\n";
8905 }
8906 let mut len = 0;
8907 for chunk in buffer.text_for_range(start..end) {
8908 text.push_str(chunk);
8909 len += chunk.len();
8910 }
8911 clipboard_selections.push(ClipboardSelection {
8912 len,
8913 is_entire_line,
8914 first_line_indent: buffer.indent_size_for_line(MultiBufferRow(start.row)).len,
8915 });
8916 }
8917 }
8918
8919 cx.write_to_clipboard(ClipboardItem::new_string_with_json_metadata(
8920 text,
8921 clipboard_selections,
8922 ));
8923 }
8924
8925 pub fn do_paste(
8926 &mut self,
8927 text: &String,
8928 clipboard_selections: Option<Vec<ClipboardSelection>>,
8929 handle_entire_lines: bool,
8930 window: &mut Window,
8931 cx: &mut Context<Self>,
8932 ) {
8933 if self.read_only(cx) {
8934 return;
8935 }
8936
8937 let clipboard_text = Cow::Borrowed(text);
8938
8939 self.transact(window, cx, |this, window, cx| {
8940 if let Some(mut clipboard_selections) = clipboard_selections {
8941 let old_selections = this.selections.all::<usize>(cx);
8942 let all_selections_were_entire_line =
8943 clipboard_selections.iter().all(|s| s.is_entire_line);
8944 let first_selection_indent_column =
8945 clipboard_selections.first().map(|s| s.first_line_indent);
8946 if clipboard_selections.len() != old_selections.len() {
8947 clipboard_selections.drain(..);
8948 }
8949 let cursor_offset = this.selections.last::<usize>(cx).head();
8950 let mut auto_indent_on_paste = true;
8951
8952 this.buffer.update(cx, |buffer, cx| {
8953 let snapshot = buffer.read(cx);
8954 auto_indent_on_paste = snapshot
8955 .language_settings_at(cursor_offset, cx)
8956 .auto_indent_on_paste;
8957
8958 let mut start_offset = 0;
8959 let mut edits = Vec::new();
8960 let mut original_indent_columns = Vec::new();
8961 for (ix, selection) in old_selections.iter().enumerate() {
8962 let to_insert;
8963 let entire_line;
8964 let original_indent_column;
8965 if let Some(clipboard_selection) = clipboard_selections.get(ix) {
8966 let end_offset = start_offset + clipboard_selection.len;
8967 to_insert = &clipboard_text[start_offset..end_offset];
8968 entire_line = clipboard_selection.is_entire_line;
8969 start_offset = end_offset + 1;
8970 original_indent_column = Some(clipboard_selection.first_line_indent);
8971 } else {
8972 to_insert = clipboard_text.as_str();
8973 entire_line = all_selections_were_entire_line;
8974 original_indent_column = first_selection_indent_column
8975 }
8976
8977 // If the corresponding selection was empty when this slice of the
8978 // clipboard text was written, then the entire line containing the
8979 // selection was copied. If this selection is also currently empty,
8980 // then paste the line before the current line of the buffer.
8981 let range = if selection.is_empty() && handle_entire_lines && entire_line {
8982 let column = selection.start.to_point(&snapshot).column as usize;
8983 let line_start = selection.start - column;
8984 line_start..line_start
8985 } else {
8986 selection.range()
8987 };
8988
8989 edits.push((range, to_insert));
8990 original_indent_columns.push(original_indent_column);
8991 }
8992 drop(snapshot);
8993
8994 buffer.edit(
8995 edits,
8996 if auto_indent_on_paste {
8997 Some(AutoindentMode::Block {
8998 original_indent_columns,
8999 })
9000 } else {
9001 None
9002 },
9003 cx,
9004 );
9005 });
9006
9007 let selections = this.selections.all::<usize>(cx);
9008 this.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
9009 s.select(selections)
9010 });
9011 } else {
9012 this.insert(&clipboard_text, window, cx);
9013 }
9014 });
9015 }
9016
9017 pub fn paste(&mut self, _: &Paste, window: &mut Window, cx: &mut Context<Self>) {
9018 if let Some(item) = cx.read_from_clipboard() {
9019 let entries = item.entries();
9020
9021 match entries.first() {
9022 // For now, we only support applying metadata if there's one string. In the future, we can incorporate all the selections
9023 // of all the pasted entries.
9024 Some(ClipboardEntry::String(clipboard_string)) if entries.len() == 1 => self
9025 .do_paste(
9026 clipboard_string.text(),
9027 clipboard_string.metadata_json::<Vec<ClipboardSelection>>(),
9028 true,
9029 window,
9030 cx,
9031 ),
9032 _ => self.do_paste(&item.text().unwrap_or_default(), None, true, window, cx),
9033 }
9034 }
9035 }
9036
9037 pub fn undo(&mut self, _: &Undo, window: &mut Window, cx: &mut Context<Self>) {
9038 if self.read_only(cx) {
9039 return;
9040 }
9041
9042 if let Some(transaction_id) = self.buffer.update(cx, |buffer, cx| buffer.undo(cx)) {
9043 if let Some((selections, _)) =
9044 self.selection_history.transaction(transaction_id).cloned()
9045 {
9046 self.change_selections(None, window, cx, |s| {
9047 s.select_anchors(selections.to_vec());
9048 });
9049 } else {
9050 log::error!(
9051 "No entry in selection_history found for undo. \
9052 This may correspond to a bug where undo does not update the selection. \
9053 If this is occurring, please add details to \
9054 https://github.com/zed-industries/zed/issues/22692"
9055 );
9056 }
9057 self.request_autoscroll(Autoscroll::fit(), cx);
9058 self.unmark_text(window, cx);
9059 self.refresh_inline_completion(true, false, window, cx);
9060 cx.emit(EditorEvent::Edited { transaction_id });
9061 cx.emit(EditorEvent::TransactionUndone { transaction_id });
9062 }
9063 }
9064
9065 pub fn redo(&mut self, _: &Redo, window: &mut Window, cx: &mut Context<Self>) {
9066 if self.read_only(cx) {
9067 return;
9068 }
9069
9070 if let Some(transaction_id) = self.buffer.update(cx, |buffer, cx| buffer.redo(cx)) {
9071 if let Some((_, Some(selections))) =
9072 self.selection_history.transaction(transaction_id).cloned()
9073 {
9074 self.change_selections(None, window, cx, |s| {
9075 s.select_anchors(selections.to_vec());
9076 });
9077 } else {
9078 log::error!(
9079 "No entry in selection_history found for redo. \
9080 This may correspond to a bug where undo does not update the selection. \
9081 If this is occurring, please add details to \
9082 https://github.com/zed-industries/zed/issues/22692"
9083 );
9084 }
9085 self.request_autoscroll(Autoscroll::fit(), cx);
9086 self.unmark_text(window, cx);
9087 self.refresh_inline_completion(true, false, window, cx);
9088 cx.emit(EditorEvent::Edited { transaction_id });
9089 }
9090 }
9091
9092 pub fn finalize_last_transaction(&mut self, cx: &mut Context<Self>) {
9093 self.buffer
9094 .update(cx, |buffer, cx| buffer.finalize_last_transaction(cx));
9095 }
9096
9097 pub fn group_until_transaction(&mut self, tx_id: TransactionId, cx: &mut Context<Self>) {
9098 self.buffer
9099 .update(cx, |buffer, cx| buffer.group_until_transaction(tx_id, cx));
9100 }
9101
9102 pub fn move_left(&mut self, _: &MoveLeft, window: &mut Window, cx: &mut Context<Self>) {
9103 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
9104 let line_mode = s.line_mode;
9105 s.move_with(|map, selection| {
9106 let cursor = if selection.is_empty() && !line_mode {
9107 movement::left(map, selection.start)
9108 } else {
9109 selection.start
9110 };
9111 selection.collapse_to(cursor, SelectionGoal::None);
9112 });
9113 })
9114 }
9115
9116 pub fn select_left(&mut self, _: &SelectLeft, window: &mut Window, cx: &mut Context<Self>) {
9117 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
9118 s.move_heads_with(|map, head, _| (movement::left(map, head), SelectionGoal::None));
9119 })
9120 }
9121
9122 pub fn move_right(&mut self, _: &MoveRight, window: &mut Window, cx: &mut Context<Self>) {
9123 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
9124 let line_mode = s.line_mode;
9125 s.move_with(|map, selection| {
9126 let cursor = if selection.is_empty() && !line_mode {
9127 movement::right(map, selection.end)
9128 } else {
9129 selection.end
9130 };
9131 selection.collapse_to(cursor, SelectionGoal::None)
9132 });
9133 })
9134 }
9135
9136 pub fn select_right(&mut self, _: &SelectRight, window: &mut Window, cx: &mut Context<Self>) {
9137 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
9138 s.move_heads_with(|map, head, _| (movement::right(map, head), SelectionGoal::None));
9139 })
9140 }
9141
9142 pub fn move_up(&mut self, _: &MoveUp, window: &mut Window, cx: &mut Context<Self>) {
9143 if self.take_rename(true, window, cx).is_some() {
9144 return;
9145 }
9146
9147 if matches!(self.mode, EditorMode::SingleLine { .. }) {
9148 cx.propagate();
9149 return;
9150 }
9151
9152 let text_layout_details = &self.text_layout_details(window);
9153 let selection_count = self.selections.count();
9154 let first_selection = self.selections.first_anchor();
9155
9156 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
9157 let line_mode = s.line_mode;
9158 s.move_with(|map, selection| {
9159 if !selection.is_empty() && !line_mode {
9160 selection.goal = SelectionGoal::None;
9161 }
9162 let (cursor, goal) = movement::up(
9163 map,
9164 selection.start,
9165 selection.goal,
9166 false,
9167 text_layout_details,
9168 );
9169 selection.collapse_to(cursor, goal);
9170 });
9171 });
9172
9173 if selection_count == 1 && first_selection.range() == self.selections.first_anchor().range()
9174 {
9175 cx.propagate();
9176 }
9177 }
9178
9179 pub fn move_up_by_lines(
9180 &mut self,
9181 action: &MoveUpByLines,
9182 window: &mut Window,
9183 cx: &mut Context<Self>,
9184 ) {
9185 if self.take_rename(true, window, cx).is_some() {
9186 return;
9187 }
9188
9189 if matches!(self.mode, EditorMode::SingleLine { .. }) {
9190 cx.propagate();
9191 return;
9192 }
9193
9194 let text_layout_details = &self.text_layout_details(window);
9195
9196 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
9197 let line_mode = s.line_mode;
9198 s.move_with(|map, selection| {
9199 if !selection.is_empty() && !line_mode {
9200 selection.goal = SelectionGoal::None;
9201 }
9202 let (cursor, goal) = movement::up_by_rows(
9203 map,
9204 selection.start,
9205 action.lines,
9206 selection.goal,
9207 false,
9208 text_layout_details,
9209 );
9210 selection.collapse_to(cursor, goal);
9211 });
9212 })
9213 }
9214
9215 pub fn move_down_by_lines(
9216 &mut self,
9217 action: &MoveDownByLines,
9218 window: &mut Window,
9219 cx: &mut Context<Self>,
9220 ) {
9221 if self.take_rename(true, window, cx).is_some() {
9222 return;
9223 }
9224
9225 if matches!(self.mode, EditorMode::SingleLine { .. }) {
9226 cx.propagate();
9227 return;
9228 }
9229
9230 let text_layout_details = &self.text_layout_details(window);
9231
9232 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
9233 let line_mode = s.line_mode;
9234 s.move_with(|map, selection| {
9235 if !selection.is_empty() && !line_mode {
9236 selection.goal = SelectionGoal::None;
9237 }
9238 let (cursor, goal) = movement::down_by_rows(
9239 map,
9240 selection.start,
9241 action.lines,
9242 selection.goal,
9243 false,
9244 text_layout_details,
9245 );
9246 selection.collapse_to(cursor, goal);
9247 });
9248 })
9249 }
9250
9251 pub fn select_down_by_lines(
9252 &mut self,
9253 action: &SelectDownByLines,
9254 window: &mut Window,
9255 cx: &mut Context<Self>,
9256 ) {
9257 let text_layout_details = &self.text_layout_details(window);
9258 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
9259 s.move_heads_with(|map, head, goal| {
9260 movement::down_by_rows(map, head, action.lines, goal, false, text_layout_details)
9261 })
9262 })
9263 }
9264
9265 pub fn select_up_by_lines(
9266 &mut self,
9267 action: &SelectUpByLines,
9268 window: &mut Window,
9269 cx: &mut Context<Self>,
9270 ) {
9271 let text_layout_details = &self.text_layout_details(window);
9272 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
9273 s.move_heads_with(|map, head, goal| {
9274 movement::up_by_rows(map, head, action.lines, goal, false, text_layout_details)
9275 })
9276 })
9277 }
9278
9279 pub fn select_page_up(
9280 &mut self,
9281 _: &SelectPageUp,
9282 window: &mut Window,
9283 cx: &mut Context<Self>,
9284 ) {
9285 let Some(row_count) = self.visible_row_count() else {
9286 return;
9287 };
9288
9289 let text_layout_details = &self.text_layout_details(window);
9290
9291 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
9292 s.move_heads_with(|map, head, goal| {
9293 movement::up_by_rows(map, head, row_count, goal, false, text_layout_details)
9294 })
9295 })
9296 }
9297
9298 pub fn move_page_up(
9299 &mut self,
9300 action: &MovePageUp,
9301 window: &mut Window,
9302 cx: &mut Context<Self>,
9303 ) {
9304 if self.take_rename(true, window, cx).is_some() {
9305 return;
9306 }
9307
9308 if self
9309 .context_menu
9310 .borrow_mut()
9311 .as_mut()
9312 .map(|menu| menu.select_first(self.completion_provider.as_deref(), cx))
9313 .unwrap_or(false)
9314 {
9315 return;
9316 }
9317
9318 if matches!(self.mode, EditorMode::SingleLine { .. }) {
9319 cx.propagate();
9320 return;
9321 }
9322
9323 let Some(row_count) = self.visible_row_count() else {
9324 return;
9325 };
9326
9327 let autoscroll = if action.center_cursor {
9328 Autoscroll::center()
9329 } else {
9330 Autoscroll::fit()
9331 };
9332
9333 let text_layout_details = &self.text_layout_details(window);
9334
9335 self.change_selections(Some(autoscroll), window, cx, |s| {
9336 let line_mode = s.line_mode;
9337 s.move_with(|map, selection| {
9338 if !selection.is_empty() && !line_mode {
9339 selection.goal = SelectionGoal::None;
9340 }
9341 let (cursor, goal) = movement::up_by_rows(
9342 map,
9343 selection.end,
9344 row_count,
9345 selection.goal,
9346 false,
9347 text_layout_details,
9348 );
9349 selection.collapse_to(cursor, goal);
9350 });
9351 });
9352 }
9353
9354 pub fn select_up(&mut self, _: &SelectUp, window: &mut Window, cx: &mut Context<Self>) {
9355 let text_layout_details = &self.text_layout_details(window);
9356 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
9357 s.move_heads_with(|map, head, goal| {
9358 movement::up(map, head, goal, false, text_layout_details)
9359 })
9360 })
9361 }
9362
9363 pub fn move_down(&mut self, _: &MoveDown, window: &mut Window, cx: &mut Context<Self>) {
9364 self.take_rename(true, window, cx);
9365
9366 if matches!(self.mode, EditorMode::SingleLine { .. }) {
9367 cx.propagate();
9368 return;
9369 }
9370
9371 let text_layout_details = &self.text_layout_details(window);
9372 let selection_count = self.selections.count();
9373 let first_selection = self.selections.first_anchor();
9374
9375 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
9376 let line_mode = s.line_mode;
9377 s.move_with(|map, selection| {
9378 if !selection.is_empty() && !line_mode {
9379 selection.goal = SelectionGoal::None;
9380 }
9381 let (cursor, goal) = movement::down(
9382 map,
9383 selection.end,
9384 selection.goal,
9385 false,
9386 text_layout_details,
9387 );
9388 selection.collapse_to(cursor, goal);
9389 });
9390 });
9391
9392 if selection_count == 1 && first_selection.range() == self.selections.first_anchor().range()
9393 {
9394 cx.propagate();
9395 }
9396 }
9397
9398 pub fn select_page_down(
9399 &mut self,
9400 _: &SelectPageDown,
9401 window: &mut Window,
9402 cx: &mut Context<Self>,
9403 ) {
9404 let Some(row_count) = self.visible_row_count() else {
9405 return;
9406 };
9407
9408 let text_layout_details = &self.text_layout_details(window);
9409
9410 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
9411 s.move_heads_with(|map, head, goal| {
9412 movement::down_by_rows(map, head, row_count, goal, false, text_layout_details)
9413 })
9414 })
9415 }
9416
9417 pub fn move_page_down(
9418 &mut self,
9419 action: &MovePageDown,
9420 window: &mut Window,
9421 cx: &mut Context<Self>,
9422 ) {
9423 if self.take_rename(true, window, cx).is_some() {
9424 return;
9425 }
9426
9427 if self
9428 .context_menu
9429 .borrow_mut()
9430 .as_mut()
9431 .map(|menu| menu.select_last(self.completion_provider.as_deref(), cx))
9432 .unwrap_or(false)
9433 {
9434 return;
9435 }
9436
9437 if matches!(self.mode, EditorMode::SingleLine { .. }) {
9438 cx.propagate();
9439 return;
9440 }
9441
9442 let Some(row_count) = self.visible_row_count() else {
9443 return;
9444 };
9445
9446 let autoscroll = if action.center_cursor {
9447 Autoscroll::center()
9448 } else {
9449 Autoscroll::fit()
9450 };
9451
9452 let text_layout_details = &self.text_layout_details(window);
9453 self.change_selections(Some(autoscroll), window, cx, |s| {
9454 let line_mode = s.line_mode;
9455 s.move_with(|map, selection| {
9456 if !selection.is_empty() && !line_mode {
9457 selection.goal = SelectionGoal::None;
9458 }
9459 let (cursor, goal) = movement::down_by_rows(
9460 map,
9461 selection.end,
9462 row_count,
9463 selection.goal,
9464 false,
9465 text_layout_details,
9466 );
9467 selection.collapse_to(cursor, goal);
9468 });
9469 });
9470 }
9471
9472 pub fn select_down(&mut self, _: &SelectDown, window: &mut Window, cx: &mut Context<Self>) {
9473 let text_layout_details = &self.text_layout_details(window);
9474 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
9475 s.move_heads_with(|map, head, goal| {
9476 movement::down(map, head, goal, false, text_layout_details)
9477 })
9478 });
9479 }
9480
9481 pub fn context_menu_first(
9482 &mut self,
9483 _: &ContextMenuFirst,
9484 _window: &mut Window,
9485 cx: &mut Context<Self>,
9486 ) {
9487 if let Some(context_menu) = self.context_menu.borrow_mut().as_mut() {
9488 context_menu.select_first(self.completion_provider.as_deref(), cx);
9489 }
9490 }
9491
9492 pub fn context_menu_prev(
9493 &mut self,
9494 _: &ContextMenuPrevious,
9495 _window: &mut Window,
9496 cx: &mut Context<Self>,
9497 ) {
9498 if let Some(context_menu) = self.context_menu.borrow_mut().as_mut() {
9499 context_menu.select_prev(self.completion_provider.as_deref(), cx);
9500 }
9501 }
9502
9503 pub fn context_menu_next(
9504 &mut self,
9505 _: &ContextMenuNext,
9506 _window: &mut Window,
9507 cx: &mut Context<Self>,
9508 ) {
9509 if let Some(context_menu) = self.context_menu.borrow_mut().as_mut() {
9510 context_menu.select_next(self.completion_provider.as_deref(), cx);
9511 }
9512 }
9513
9514 pub fn context_menu_last(
9515 &mut self,
9516 _: &ContextMenuLast,
9517 _window: &mut Window,
9518 cx: &mut Context<Self>,
9519 ) {
9520 if let Some(context_menu) = self.context_menu.borrow_mut().as_mut() {
9521 context_menu.select_last(self.completion_provider.as_deref(), cx);
9522 }
9523 }
9524
9525 pub fn move_to_previous_word_start(
9526 &mut self,
9527 _: &MoveToPreviousWordStart,
9528 window: &mut Window,
9529 cx: &mut Context<Self>,
9530 ) {
9531 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
9532 s.move_cursors_with(|map, head, _| {
9533 (
9534 movement::previous_word_start(map, head),
9535 SelectionGoal::None,
9536 )
9537 });
9538 })
9539 }
9540
9541 pub fn move_to_previous_subword_start(
9542 &mut self,
9543 _: &MoveToPreviousSubwordStart,
9544 window: &mut Window,
9545 cx: &mut Context<Self>,
9546 ) {
9547 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
9548 s.move_cursors_with(|map, head, _| {
9549 (
9550 movement::previous_subword_start(map, head),
9551 SelectionGoal::None,
9552 )
9553 });
9554 })
9555 }
9556
9557 pub fn select_to_previous_word_start(
9558 &mut self,
9559 _: &SelectToPreviousWordStart,
9560 window: &mut Window,
9561 cx: &mut Context<Self>,
9562 ) {
9563 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
9564 s.move_heads_with(|map, head, _| {
9565 (
9566 movement::previous_word_start(map, head),
9567 SelectionGoal::None,
9568 )
9569 });
9570 })
9571 }
9572
9573 pub fn select_to_previous_subword_start(
9574 &mut self,
9575 _: &SelectToPreviousSubwordStart,
9576 window: &mut Window,
9577 cx: &mut Context<Self>,
9578 ) {
9579 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
9580 s.move_heads_with(|map, head, _| {
9581 (
9582 movement::previous_subword_start(map, head),
9583 SelectionGoal::None,
9584 )
9585 });
9586 })
9587 }
9588
9589 pub fn delete_to_previous_word_start(
9590 &mut self,
9591 action: &DeleteToPreviousWordStart,
9592 window: &mut Window,
9593 cx: &mut Context<Self>,
9594 ) {
9595 self.transact(window, cx, |this, window, cx| {
9596 this.select_autoclose_pair(window, cx);
9597 this.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
9598 let line_mode = s.line_mode;
9599 s.move_with(|map, selection| {
9600 if selection.is_empty() && !line_mode {
9601 let cursor = if action.ignore_newlines {
9602 movement::previous_word_start(map, selection.head())
9603 } else {
9604 movement::previous_word_start_or_newline(map, selection.head())
9605 };
9606 selection.set_head(cursor, SelectionGoal::None);
9607 }
9608 });
9609 });
9610 this.insert("", window, cx);
9611 });
9612 }
9613
9614 pub fn delete_to_previous_subword_start(
9615 &mut self,
9616 _: &DeleteToPreviousSubwordStart,
9617 window: &mut Window,
9618 cx: &mut Context<Self>,
9619 ) {
9620 self.transact(window, cx, |this, window, cx| {
9621 this.select_autoclose_pair(window, cx);
9622 this.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
9623 let line_mode = s.line_mode;
9624 s.move_with(|map, selection| {
9625 if selection.is_empty() && !line_mode {
9626 let cursor = movement::previous_subword_start(map, selection.head());
9627 selection.set_head(cursor, SelectionGoal::None);
9628 }
9629 });
9630 });
9631 this.insert("", window, cx);
9632 });
9633 }
9634
9635 pub fn move_to_next_word_end(
9636 &mut self,
9637 _: &MoveToNextWordEnd,
9638 window: &mut Window,
9639 cx: &mut Context<Self>,
9640 ) {
9641 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
9642 s.move_cursors_with(|map, head, _| {
9643 (movement::next_word_end(map, head), SelectionGoal::None)
9644 });
9645 })
9646 }
9647
9648 pub fn move_to_next_subword_end(
9649 &mut self,
9650 _: &MoveToNextSubwordEnd,
9651 window: &mut Window,
9652 cx: &mut Context<Self>,
9653 ) {
9654 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
9655 s.move_cursors_with(|map, head, _| {
9656 (movement::next_subword_end(map, head), SelectionGoal::None)
9657 });
9658 })
9659 }
9660
9661 pub fn select_to_next_word_end(
9662 &mut self,
9663 _: &SelectToNextWordEnd,
9664 window: &mut Window,
9665 cx: &mut Context<Self>,
9666 ) {
9667 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
9668 s.move_heads_with(|map, head, _| {
9669 (movement::next_word_end(map, head), SelectionGoal::None)
9670 });
9671 })
9672 }
9673
9674 pub fn select_to_next_subword_end(
9675 &mut self,
9676 _: &SelectToNextSubwordEnd,
9677 window: &mut Window,
9678 cx: &mut Context<Self>,
9679 ) {
9680 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
9681 s.move_heads_with(|map, head, _| {
9682 (movement::next_subword_end(map, head), SelectionGoal::None)
9683 });
9684 })
9685 }
9686
9687 pub fn delete_to_next_word_end(
9688 &mut self,
9689 action: &DeleteToNextWordEnd,
9690 window: &mut Window,
9691 cx: &mut Context<Self>,
9692 ) {
9693 self.transact(window, cx, |this, window, cx| {
9694 this.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
9695 let line_mode = s.line_mode;
9696 s.move_with(|map, selection| {
9697 if selection.is_empty() && !line_mode {
9698 let cursor = if action.ignore_newlines {
9699 movement::next_word_end(map, selection.head())
9700 } else {
9701 movement::next_word_end_or_newline(map, selection.head())
9702 };
9703 selection.set_head(cursor, SelectionGoal::None);
9704 }
9705 });
9706 });
9707 this.insert("", window, cx);
9708 });
9709 }
9710
9711 pub fn delete_to_next_subword_end(
9712 &mut self,
9713 _: &DeleteToNextSubwordEnd,
9714 window: &mut Window,
9715 cx: &mut Context<Self>,
9716 ) {
9717 self.transact(window, cx, |this, window, cx| {
9718 this.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
9719 s.move_with(|map, selection| {
9720 if selection.is_empty() {
9721 let cursor = movement::next_subword_end(map, selection.head());
9722 selection.set_head(cursor, SelectionGoal::None);
9723 }
9724 });
9725 });
9726 this.insert("", window, cx);
9727 });
9728 }
9729
9730 pub fn move_to_beginning_of_line(
9731 &mut self,
9732 action: &MoveToBeginningOfLine,
9733 window: &mut Window,
9734 cx: &mut Context<Self>,
9735 ) {
9736 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
9737 s.move_cursors_with(|map, head, _| {
9738 (
9739 movement::indented_line_beginning(
9740 map,
9741 head,
9742 action.stop_at_soft_wraps,
9743 action.stop_at_indent,
9744 ),
9745 SelectionGoal::None,
9746 )
9747 });
9748 })
9749 }
9750
9751 pub fn select_to_beginning_of_line(
9752 &mut self,
9753 action: &SelectToBeginningOfLine,
9754 window: &mut Window,
9755 cx: &mut Context<Self>,
9756 ) {
9757 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
9758 s.move_heads_with(|map, head, _| {
9759 (
9760 movement::indented_line_beginning(
9761 map,
9762 head,
9763 action.stop_at_soft_wraps,
9764 action.stop_at_indent,
9765 ),
9766 SelectionGoal::None,
9767 )
9768 });
9769 });
9770 }
9771
9772 pub fn delete_to_beginning_of_line(
9773 &mut self,
9774 action: &DeleteToBeginningOfLine,
9775 window: &mut Window,
9776 cx: &mut Context<Self>,
9777 ) {
9778 self.transact(window, cx, |this, window, cx| {
9779 this.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
9780 s.move_with(|_, selection| {
9781 selection.reversed = true;
9782 });
9783 });
9784
9785 this.select_to_beginning_of_line(
9786 &SelectToBeginningOfLine {
9787 stop_at_soft_wraps: false,
9788 stop_at_indent: action.stop_at_indent,
9789 },
9790 window,
9791 cx,
9792 );
9793 this.backspace(&Backspace, window, cx);
9794 });
9795 }
9796
9797 pub fn move_to_end_of_line(
9798 &mut self,
9799 action: &MoveToEndOfLine,
9800 window: &mut Window,
9801 cx: &mut Context<Self>,
9802 ) {
9803 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
9804 s.move_cursors_with(|map, head, _| {
9805 (
9806 movement::line_end(map, head, action.stop_at_soft_wraps),
9807 SelectionGoal::None,
9808 )
9809 });
9810 })
9811 }
9812
9813 pub fn select_to_end_of_line(
9814 &mut self,
9815 action: &SelectToEndOfLine,
9816 window: &mut Window,
9817 cx: &mut Context<Self>,
9818 ) {
9819 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
9820 s.move_heads_with(|map, head, _| {
9821 (
9822 movement::line_end(map, head, action.stop_at_soft_wraps),
9823 SelectionGoal::None,
9824 )
9825 });
9826 })
9827 }
9828
9829 pub fn delete_to_end_of_line(
9830 &mut self,
9831 _: &DeleteToEndOfLine,
9832 window: &mut Window,
9833 cx: &mut Context<Self>,
9834 ) {
9835 self.transact(window, cx, |this, window, cx| {
9836 this.select_to_end_of_line(
9837 &SelectToEndOfLine {
9838 stop_at_soft_wraps: false,
9839 },
9840 window,
9841 cx,
9842 );
9843 this.delete(&Delete, window, cx);
9844 });
9845 }
9846
9847 pub fn cut_to_end_of_line(
9848 &mut self,
9849 _: &CutToEndOfLine,
9850 window: &mut Window,
9851 cx: &mut Context<Self>,
9852 ) {
9853 self.transact(window, cx, |this, window, cx| {
9854 this.select_to_end_of_line(
9855 &SelectToEndOfLine {
9856 stop_at_soft_wraps: false,
9857 },
9858 window,
9859 cx,
9860 );
9861 this.cut(&Cut, window, cx);
9862 });
9863 }
9864
9865 pub fn move_to_start_of_paragraph(
9866 &mut self,
9867 _: &MoveToStartOfParagraph,
9868 window: &mut Window,
9869 cx: &mut Context<Self>,
9870 ) {
9871 if matches!(self.mode, EditorMode::SingleLine { .. }) {
9872 cx.propagate();
9873 return;
9874 }
9875
9876 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
9877 s.move_with(|map, selection| {
9878 selection.collapse_to(
9879 movement::start_of_paragraph(map, selection.head(), 1),
9880 SelectionGoal::None,
9881 )
9882 });
9883 })
9884 }
9885
9886 pub fn move_to_end_of_paragraph(
9887 &mut self,
9888 _: &MoveToEndOfParagraph,
9889 window: &mut Window,
9890 cx: &mut Context<Self>,
9891 ) {
9892 if matches!(self.mode, EditorMode::SingleLine { .. }) {
9893 cx.propagate();
9894 return;
9895 }
9896
9897 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
9898 s.move_with(|map, selection| {
9899 selection.collapse_to(
9900 movement::end_of_paragraph(map, selection.head(), 1),
9901 SelectionGoal::None,
9902 )
9903 });
9904 })
9905 }
9906
9907 pub fn select_to_start_of_paragraph(
9908 &mut self,
9909 _: &SelectToStartOfParagraph,
9910 window: &mut Window,
9911 cx: &mut Context<Self>,
9912 ) {
9913 if matches!(self.mode, EditorMode::SingleLine { .. }) {
9914 cx.propagate();
9915 return;
9916 }
9917
9918 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
9919 s.move_heads_with(|map, head, _| {
9920 (
9921 movement::start_of_paragraph(map, head, 1),
9922 SelectionGoal::None,
9923 )
9924 });
9925 })
9926 }
9927
9928 pub fn select_to_end_of_paragraph(
9929 &mut self,
9930 _: &SelectToEndOfParagraph,
9931 window: &mut Window,
9932 cx: &mut Context<Self>,
9933 ) {
9934 if matches!(self.mode, EditorMode::SingleLine { .. }) {
9935 cx.propagate();
9936 return;
9937 }
9938
9939 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
9940 s.move_heads_with(|map, head, _| {
9941 (
9942 movement::end_of_paragraph(map, head, 1),
9943 SelectionGoal::None,
9944 )
9945 });
9946 })
9947 }
9948
9949 pub fn move_to_start_of_excerpt(
9950 &mut self,
9951 _: &MoveToStartOfExcerpt,
9952 window: &mut Window,
9953 cx: &mut Context<Self>,
9954 ) {
9955 if matches!(self.mode, EditorMode::SingleLine { .. }) {
9956 cx.propagate();
9957 return;
9958 }
9959
9960 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
9961 s.move_with(|map, selection| {
9962 selection.collapse_to(
9963 movement::start_of_excerpt(
9964 map,
9965 selection.head(),
9966 workspace::searchable::Direction::Prev,
9967 ),
9968 SelectionGoal::None,
9969 )
9970 });
9971 })
9972 }
9973
9974 pub fn move_to_start_of_next_excerpt(
9975 &mut self,
9976 _: &MoveToStartOfNextExcerpt,
9977 window: &mut Window,
9978 cx: &mut Context<Self>,
9979 ) {
9980 if matches!(self.mode, EditorMode::SingleLine { .. }) {
9981 cx.propagate();
9982 return;
9983 }
9984
9985 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
9986 s.move_with(|map, selection| {
9987 selection.collapse_to(
9988 movement::start_of_excerpt(
9989 map,
9990 selection.head(),
9991 workspace::searchable::Direction::Next,
9992 ),
9993 SelectionGoal::None,
9994 )
9995 });
9996 })
9997 }
9998
9999 pub fn move_to_end_of_excerpt(
10000 &mut self,
10001 _: &MoveToEndOfExcerpt,
10002 window: &mut Window,
10003 cx: &mut Context<Self>,
10004 ) {
10005 if matches!(self.mode, EditorMode::SingleLine { .. }) {
10006 cx.propagate();
10007 return;
10008 }
10009
10010 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
10011 s.move_with(|map, selection| {
10012 selection.collapse_to(
10013 movement::end_of_excerpt(
10014 map,
10015 selection.head(),
10016 workspace::searchable::Direction::Next,
10017 ),
10018 SelectionGoal::None,
10019 )
10020 });
10021 })
10022 }
10023
10024 pub fn move_to_end_of_previous_excerpt(
10025 &mut self,
10026 _: &MoveToEndOfPreviousExcerpt,
10027 window: &mut Window,
10028 cx: &mut Context<Self>,
10029 ) {
10030 if matches!(self.mode, EditorMode::SingleLine { .. }) {
10031 cx.propagate();
10032 return;
10033 }
10034
10035 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
10036 s.move_with(|map, selection| {
10037 selection.collapse_to(
10038 movement::end_of_excerpt(
10039 map,
10040 selection.head(),
10041 workspace::searchable::Direction::Prev,
10042 ),
10043 SelectionGoal::None,
10044 )
10045 });
10046 })
10047 }
10048
10049 pub fn select_to_start_of_excerpt(
10050 &mut self,
10051 _: &SelectToStartOfExcerpt,
10052 window: &mut Window,
10053 cx: &mut Context<Self>,
10054 ) {
10055 if matches!(self.mode, EditorMode::SingleLine { .. }) {
10056 cx.propagate();
10057 return;
10058 }
10059
10060 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
10061 s.move_heads_with(|map, head, _| {
10062 (
10063 movement::start_of_excerpt(map, head, workspace::searchable::Direction::Prev),
10064 SelectionGoal::None,
10065 )
10066 });
10067 })
10068 }
10069
10070 pub fn select_to_start_of_next_excerpt(
10071 &mut self,
10072 _: &SelectToStartOfNextExcerpt,
10073 window: &mut Window,
10074 cx: &mut Context<Self>,
10075 ) {
10076 if matches!(self.mode, EditorMode::SingleLine { .. }) {
10077 cx.propagate();
10078 return;
10079 }
10080
10081 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
10082 s.move_heads_with(|map, head, _| {
10083 (
10084 movement::start_of_excerpt(map, head, workspace::searchable::Direction::Next),
10085 SelectionGoal::None,
10086 )
10087 });
10088 })
10089 }
10090
10091 pub fn select_to_end_of_excerpt(
10092 &mut self,
10093 _: &SelectToEndOfExcerpt,
10094 window: &mut Window,
10095 cx: &mut Context<Self>,
10096 ) {
10097 if matches!(self.mode, EditorMode::SingleLine { .. }) {
10098 cx.propagate();
10099 return;
10100 }
10101
10102 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
10103 s.move_heads_with(|map, head, _| {
10104 (
10105 movement::end_of_excerpt(map, head, workspace::searchable::Direction::Next),
10106 SelectionGoal::None,
10107 )
10108 });
10109 })
10110 }
10111
10112 pub fn select_to_end_of_previous_excerpt(
10113 &mut self,
10114 _: &SelectToEndOfPreviousExcerpt,
10115 window: &mut Window,
10116 cx: &mut Context<Self>,
10117 ) {
10118 if matches!(self.mode, EditorMode::SingleLine { .. }) {
10119 cx.propagate();
10120 return;
10121 }
10122
10123 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
10124 s.move_heads_with(|map, head, _| {
10125 (
10126 movement::end_of_excerpt(map, head, workspace::searchable::Direction::Prev),
10127 SelectionGoal::None,
10128 )
10129 });
10130 })
10131 }
10132
10133 pub fn move_to_beginning(
10134 &mut self,
10135 _: &MoveToBeginning,
10136 window: &mut Window,
10137 cx: &mut Context<Self>,
10138 ) {
10139 if matches!(self.mode, EditorMode::SingleLine { .. }) {
10140 cx.propagate();
10141 return;
10142 }
10143
10144 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
10145 s.select_ranges(vec![0..0]);
10146 });
10147 }
10148
10149 pub fn select_to_beginning(
10150 &mut self,
10151 _: &SelectToBeginning,
10152 window: &mut Window,
10153 cx: &mut Context<Self>,
10154 ) {
10155 let mut selection = self.selections.last::<Point>(cx);
10156 selection.set_head(Point::zero(), SelectionGoal::None);
10157
10158 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
10159 s.select(vec![selection]);
10160 });
10161 }
10162
10163 pub fn move_to_end(&mut self, _: &MoveToEnd, window: &mut Window, cx: &mut Context<Self>) {
10164 if matches!(self.mode, EditorMode::SingleLine { .. }) {
10165 cx.propagate();
10166 return;
10167 }
10168
10169 let cursor = self.buffer.read(cx).read(cx).len();
10170 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
10171 s.select_ranges(vec![cursor..cursor])
10172 });
10173 }
10174
10175 pub fn set_nav_history(&mut self, nav_history: Option<ItemNavHistory>) {
10176 self.nav_history = nav_history;
10177 }
10178
10179 pub fn nav_history(&self) -> Option<&ItemNavHistory> {
10180 self.nav_history.as_ref()
10181 }
10182
10183 fn push_to_nav_history(
10184 &mut self,
10185 cursor_anchor: Anchor,
10186 new_position: Option<Point>,
10187 cx: &mut Context<Self>,
10188 ) {
10189 if let Some(nav_history) = self.nav_history.as_mut() {
10190 let buffer = self.buffer.read(cx).read(cx);
10191 let cursor_position = cursor_anchor.to_point(&buffer);
10192 let scroll_state = self.scroll_manager.anchor();
10193 let scroll_top_row = scroll_state.top_row(&buffer);
10194 drop(buffer);
10195
10196 if let Some(new_position) = new_position {
10197 let row_delta = (new_position.row as i64 - cursor_position.row as i64).abs();
10198 if row_delta < MIN_NAVIGATION_HISTORY_ROW_DELTA {
10199 return;
10200 }
10201 }
10202
10203 nav_history.push(
10204 Some(NavigationData {
10205 cursor_anchor,
10206 cursor_position,
10207 scroll_anchor: scroll_state,
10208 scroll_top_row,
10209 }),
10210 cx,
10211 );
10212 }
10213 }
10214
10215 pub fn select_to_end(&mut self, _: &SelectToEnd, window: &mut Window, cx: &mut Context<Self>) {
10216 let buffer = self.buffer.read(cx).snapshot(cx);
10217 let mut selection = self.selections.first::<usize>(cx);
10218 selection.set_head(buffer.len(), SelectionGoal::None);
10219 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
10220 s.select(vec![selection]);
10221 });
10222 }
10223
10224 pub fn select_all(&mut self, _: &SelectAll, window: &mut Window, cx: &mut Context<Self>) {
10225 let end = self.buffer.read(cx).read(cx).len();
10226 self.change_selections(None, window, cx, |s| {
10227 s.select_ranges(vec![0..end]);
10228 });
10229 }
10230
10231 pub fn select_line(&mut self, _: &SelectLine, window: &mut Window, cx: &mut Context<Self>) {
10232 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
10233 let mut selections = self.selections.all::<Point>(cx);
10234 let max_point = display_map.buffer_snapshot.max_point();
10235 for selection in &mut selections {
10236 let rows = selection.spanned_rows(true, &display_map);
10237 selection.start = Point::new(rows.start.0, 0);
10238 selection.end = cmp::min(max_point, Point::new(rows.end.0, 0));
10239 selection.reversed = false;
10240 }
10241 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
10242 s.select(selections);
10243 });
10244 }
10245
10246 pub fn split_selection_into_lines(
10247 &mut self,
10248 _: &SplitSelectionIntoLines,
10249 window: &mut Window,
10250 cx: &mut Context<Self>,
10251 ) {
10252 let selections = self
10253 .selections
10254 .all::<Point>(cx)
10255 .into_iter()
10256 .map(|selection| selection.start..selection.end)
10257 .collect::<Vec<_>>();
10258 self.unfold_ranges(&selections, true, true, cx);
10259
10260 let mut new_selection_ranges = Vec::new();
10261 {
10262 let buffer = self.buffer.read(cx).read(cx);
10263 for selection in selections {
10264 for row in selection.start.row..selection.end.row {
10265 let cursor = Point::new(row, buffer.line_len(MultiBufferRow(row)));
10266 new_selection_ranges.push(cursor..cursor);
10267 }
10268
10269 let is_multiline_selection = selection.start.row != selection.end.row;
10270 // Don't insert last one if it's a multi-line selection ending at the start of a line,
10271 // so this action feels more ergonomic when paired with other selection operations
10272 let should_skip_last = is_multiline_selection && selection.end.column == 0;
10273 if !should_skip_last {
10274 new_selection_ranges.push(selection.end..selection.end);
10275 }
10276 }
10277 }
10278 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
10279 s.select_ranges(new_selection_ranges);
10280 });
10281 }
10282
10283 pub fn add_selection_above(
10284 &mut self,
10285 _: &AddSelectionAbove,
10286 window: &mut Window,
10287 cx: &mut Context<Self>,
10288 ) {
10289 self.add_selection(true, window, cx);
10290 }
10291
10292 pub fn add_selection_below(
10293 &mut self,
10294 _: &AddSelectionBelow,
10295 window: &mut Window,
10296 cx: &mut Context<Self>,
10297 ) {
10298 self.add_selection(false, window, cx);
10299 }
10300
10301 fn add_selection(&mut self, above: bool, window: &mut Window, cx: &mut Context<Self>) {
10302 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
10303 let mut selections = self.selections.all::<Point>(cx);
10304 let text_layout_details = self.text_layout_details(window);
10305 let mut state = self.add_selections_state.take().unwrap_or_else(|| {
10306 let oldest_selection = selections.iter().min_by_key(|s| s.id).unwrap().clone();
10307 let range = oldest_selection.display_range(&display_map).sorted();
10308
10309 let start_x = display_map.x_for_display_point(range.start, &text_layout_details);
10310 let end_x = display_map.x_for_display_point(range.end, &text_layout_details);
10311 let positions = start_x.min(end_x)..start_x.max(end_x);
10312
10313 selections.clear();
10314 let mut stack = Vec::new();
10315 for row in range.start.row().0..=range.end.row().0 {
10316 if let Some(selection) = self.selections.build_columnar_selection(
10317 &display_map,
10318 DisplayRow(row),
10319 &positions,
10320 oldest_selection.reversed,
10321 &text_layout_details,
10322 ) {
10323 stack.push(selection.id);
10324 selections.push(selection);
10325 }
10326 }
10327
10328 if above {
10329 stack.reverse();
10330 }
10331
10332 AddSelectionsState { above, stack }
10333 });
10334
10335 let last_added_selection = *state.stack.last().unwrap();
10336 let mut new_selections = Vec::new();
10337 if above == state.above {
10338 let end_row = if above {
10339 DisplayRow(0)
10340 } else {
10341 display_map.max_point().row()
10342 };
10343
10344 'outer: for selection in selections {
10345 if selection.id == last_added_selection {
10346 let range = selection.display_range(&display_map).sorted();
10347 debug_assert_eq!(range.start.row(), range.end.row());
10348 let mut row = range.start.row();
10349 let positions =
10350 if let SelectionGoal::HorizontalRange { start, end } = selection.goal {
10351 px(start)..px(end)
10352 } else {
10353 let start_x =
10354 display_map.x_for_display_point(range.start, &text_layout_details);
10355 let end_x =
10356 display_map.x_for_display_point(range.end, &text_layout_details);
10357 start_x.min(end_x)..start_x.max(end_x)
10358 };
10359
10360 while row != end_row {
10361 if above {
10362 row.0 -= 1;
10363 } else {
10364 row.0 += 1;
10365 }
10366
10367 if let Some(new_selection) = self.selections.build_columnar_selection(
10368 &display_map,
10369 row,
10370 &positions,
10371 selection.reversed,
10372 &text_layout_details,
10373 ) {
10374 state.stack.push(new_selection.id);
10375 if above {
10376 new_selections.push(new_selection);
10377 new_selections.push(selection);
10378 } else {
10379 new_selections.push(selection);
10380 new_selections.push(new_selection);
10381 }
10382
10383 continue 'outer;
10384 }
10385 }
10386 }
10387
10388 new_selections.push(selection);
10389 }
10390 } else {
10391 new_selections = selections;
10392 new_selections.retain(|s| s.id != last_added_selection);
10393 state.stack.pop();
10394 }
10395
10396 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
10397 s.select(new_selections);
10398 });
10399 if state.stack.len() > 1 {
10400 self.add_selections_state = Some(state);
10401 }
10402 }
10403
10404 pub fn select_next_match_internal(
10405 &mut self,
10406 display_map: &DisplaySnapshot,
10407 replace_newest: bool,
10408 autoscroll: Option<Autoscroll>,
10409 window: &mut Window,
10410 cx: &mut Context<Self>,
10411 ) -> Result<()> {
10412 fn select_next_match_ranges(
10413 this: &mut Editor,
10414 range: Range<usize>,
10415 replace_newest: bool,
10416 auto_scroll: Option<Autoscroll>,
10417 window: &mut Window,
10418 cx: &mut Context<Editor>,
10419 ) {
10420 this.unfold_ranges(&[range.clone()], false, true, cx);
10421 this.change_selections(auto_scroll, window, cx, |s| {
10422 if replace_newest {
10423 s.delete(s.newest_anchor().id);
10424 }
10425 s.insert_range(range.clone());
10426 });
10427 }
10428
10429 let buffer = &display_map.buffer_snapshot;
10430 let mut selections = self.selections.all::<usize>(cx);
10431 if let Some(mut select_next_state) = self.select_next_state.take() {
10432 let query = &select_next_state.query;
10433 if !select_next_state.done {
10434 let first_selection = selections.iter().min_by_key(|s| s.id).unwrap();
10435 let last_selection = selections.iter().max_by_key(|s| s.id).unwrap();
10436 let mut next_selected_range = None;
10437
10438 let bytes_after_last_selection =
10439 buffer.bytes_in_range(last_selection.end..buffer.len());
10440 let bytes_before_first_selection = buffer.bytes_in_range(0..first_selection.start);
10441 let query_matches = query
10442 .stream_find_iter(bytes_after_last_selection)
10443 .map(|result| (last_selection.end, result))
10444 .chain(
10445 query
10446 .stream_find_iter(bytes_before_first_selection)
10447 .map(|result| (0, result)),
10448 );
10449
10450 for (start_offset, query_match) in query_matches {
10451 let query_match = query_match.unwrap(); // can only fail due to I/O
10452 let offset_range =
10453 start_offset + query_match.start()..start_offset + query_match.end();
10454 let display_range = offset_range.start.to_display_point(display_map)
10455 ..offset_range.end.to_display_point(display_map);
10456
10457 if !select_next_state.wordwise
10458 || (!movement::is_inside_word(display_map, display_range.start)
10459 && !movement::is_inside_word(display_map, display_range.end))
10460 {
10461 // TODO: This is n^2, because we might check all the selections
10462 if !selections
10463 .iter()
10464 .any(|selection| selection.range().overlaps(&offset_range))
10465 {
10466 next_selected_range = Some(offset_range);
10467 break;
10468 }
10469 }
10470 }
10471
10472 if let Some(next_selected_range) = next_selected_range {
10473 select_next_match_ranges(
10474 self,
10475 next_selected_range,
10476 replace_newest,
10477 autoscroll,
10478 window,
10479 cx,
10480 );
10481 } else {
10482 select_next_state.done = true;
10483 }
10484 }
10485
10486 self.select_next_state = Some(select_next_state);
10487 } else {
10488 let mut only_carets = true;
10489 let mut same_text_selected = true;
10490 let mut selected_text = None;
10491
10492 let mut selections_iter = selections.iter().peekable();
10493 while let Some(selection) = selections_iter.next() {
10494 if selection.start != selection.end {
10495 only_carets = false;
10496 }
10497
10498 if same_text_selected {
10499 if selected_text.is_none() {
10500 selected_text =
10501 Some(buffer.text_for_range(selection.range()).collect::<String>());
10502 }
10503
10504 if let Some(next_selection) = selections_iter.peek() {
10505 if next_selection.range().len() == selection.range().len() {
10506 let next_selected_text = buffer
10507 .text_for_range(next_selection.range())
10508 .collect::<String>();
10509 if Some(next_selected_text) != selected_text {
10510 same_text_selected = false;
10511 selected_text = None;
10512 }
10513 } else {
10514 same_text_selected = false;
10515 selected_text = None;
10516 }
10517 }
10518 }
10519 }
10520
10521 if only_carets {
10522 for selection in &mut selections {
10523 let word_range = movement::surrounding_word(
10524 display_map,
10525 selection.start.to_display_point(display_map),
10526 );
10527 selection.start = word_range.start.to_offset(display_map, Bias::Left);
10528 selection.end = word_range.end.to_offset(display_map, Bias::Left);
10529 selection.goal = SelectionGoal::None;
10530 selection.reversed = false;
10531 select_next_match_ranges(
10532 self,
10533 selection.start..selection.end,
10534 replace_newest,
10535 autoscroll,
10536 window,
10537 cx,
10538 );
10539 }
10540
10541 if selections.len() == 1 {
10542 let selection = selections
10543 .last()
10544 .expect("ensured that there's only one selection");
10545 let query = buffer
10546 .text_for_range(selection.start..selection.end)
10547 .collect::<String>();
10548 let is_empty = query.is_empty();
10549 let select_state = SelectNextState {
10550 query: AhoCorasick::new(&[query])?,
10551 wordwise: true,
10552 done: is_empty,
10553 };
10554 self.select_next_state = Some(select_state);
10555 } else {
10556 self.select_next_state = None;
10557 }
10558 } else if let Some(selected_text) = selected_text {
10559 self.select_next_state = Some(SelectNextState {
10560 query: AhoCorasick::new(&[selected_text])?,
10561 wordwise: false,
10562 done: false,
10563 });
10564 self.select_next_match_internal(
10565 display_map,
10566 replace_newest,
10567 autoscroll,
10568 window,
10569 cx,
10570 )?;
10571 }
10572 }
10573 Ok(())
10574 }
10575
10576 pub fn select_all_matches(
10577 &mut self,
10578 _action: &SelectAllMatches,
10579 window: &mut Window,
10580 cx: &mut Context<Self>,
10581 ) -> Result<()> {
10582 self.push_to_selection_history();
10583 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
10584
10585 self.select_next_match_internal(&display_map, false, None, window, cx)?;
10586 let Some(select_next_state) = self.select_next_state.as_mut() else {
10587 return Ok(());
10588 };
10589 if select_next_state.done {
10590 return Ok(());
10591 }
10592
10593 let mut new_selections = self.selections.all::<usize>(cx);
10594
10595 let buffer = &display_map.buffer_snapshot;
10596 let query_matches = select_next_state
10597 .query
10598 .stream_find_iter(buffer.bytes_in_range(0..buffer.len()));
10599
10600 for query_match in query_matches {
10601 let query_match = query_match.unwrap(); // can only fail due to I/O
10602 let offset_range = query_match.start()..query_match.end();
10603 let display_range = offset_range.start.to_display_point(&display_map)
10604 ..offset_range.end.to_display_point(&display_map);
10605
10606 if !select_next_state.wordwise
10607 || (!movement::is_inside_word(&display_map, display_range.start)
10608 && !movement::is_inside_word(&display_map, display_range.end))
10609 {
10610 self.selections.change_with(cx, |selections| {
10611 new_selections.push(Selection {
10612 id: selections.new_selection_id(),
10613 start: offset_range.start,
10614 end: offset_range.end,
10615 reversed: false,
10616 goal: SelectionGoal::None,
10617 });
10618 });
10619 }
10620 }
10621
10622 new_selections.sort_by_key(|selection| selection.start);
10623 let mut ix = 0;
10624 while ix + 1 < new_selections.len() {
10625 let current_selection = &new_selections[ix];
10626 let next_selection = &new_selections[ix + 1];
10627 if current_selection.range().overlaps(&next_selection.range()) {
10628 if current_selection.id < next_selection.id {
10629 new_selections.remove(ix + 1);
10630 } else {
10631 new_selections.remove(ix);
10632 }
10633 } else {
10634 ix += 1;
10635 }
10636 }
10637
10638 let reversed = self.selections.oldest::<usize>(cx).reversed;
10639
10640 for selection in new_selections.iter_mut() {
10641 selection.reversed = reversed;
10642 }
10643
10644 select_next_state.done = true;
10645 self.unfold_ranges(
10646 &new_selections
10647 .iter()
10648 .map(|selection| selection.range())
10649 .collect::<Vec<_>>(),
10650 false,
10651 false,
10652 cx,
10653 );
10654 self.change_selections(Some(Autoscroll::fit()), window, cx, |selections| {
10655 selections.select(new_selections)
10656 });
10657
10658 Ok(())
10659 }
10660
10661 pub fn select_next(
10662 &mut self,
10663 action: &SelectNext,
10664 window: &mut Window,
10665 cx: &mut Context<Self>,
10666 ) -> Result<()> {
10667 self.push_to_selection_history();
10668 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
10669 self.select_next_match_internal(
10670 &display_map,
10671 action.replace_newest,
10672 Some(Autoscroll::newest()),
10673 window,
10674 cx,
10675 )?;
10676 Ok(())
10677 }
10678
10679 pub fn select_previous(
10680 &mut self,
10681 action: &SelectPrevious,
10682 window: &mut Window,
10683 cx: &mut Context<Self>,
10684 ) -> Result<()> {
10685 self.push_to_selection_history();
10686 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
10687 let buffer = &display_map.buffer_snapshot;
10688 let mut selections = self.selections.all::<usize>(cx);
10689 if let Some(mut select_prev_state) = self.select_prev_state.take() {
10690 let query = &select_prev_state.query;
10691 if !select_prev_state.done {
10692 let first_selection = selections.iter().min_by_key(|s| s.id).unwrap();
10693 let last_selection = selections.iter().max_by_key(|s| s.id).unwrap();
10694 let mut next_selected_range = None;
10695 // When we're iterating matches backwards, the oldest match will actually be the furthest one in the buffer.
10696 let bytes_before_last_selection =
10697 buffer.reversed_bytes_in_range(0..last_selection.start);
10698 let bytes_after_first_selection =
10699 buffer.reversed_bytes_in_range(first_selection.end..buffer.len());
10700 let query_matches = query
10701 .stream_find_iter(bytes_before_last_selection)
10702 .map(|result| (last_selection.start, result))
10703 .chain(
10704 query
10705 .stream_find_iter(bytes_after_first_selection)
10706 .map(|result| (buffer.len(), result)),
10707 );
10708 for (end_offset, query_match) in query_matches {
10709 let query_match = query_match.unwrap(); // can only fail due to I/O
10710 let offset_range =
10711 end_offset - query_match.end()..end_offset - query_match.start();
10712 let display_range = offset_range.start.to_display_point(&display_map)
10713 ..offset_range.end.to_display_point(&display_map);
10714
10715 if !select_prev_state.wordwise
10716 || (!movement::is_inside_word(&display_map, display_range.start)
10717 && !movement::is_inside_word(&display_map, display_range.end))
10718 {
10719 next_selected_range = Some(offset_range);
10720 break;
10721 }
10722 }
10723
10724 if let Some(next_selected_range) = next_selected_range {
10725 self.unfold_ranges(&[next_selected_range.clone()], false, true, cx);
10726 self.change_selections(Some(Autoscroll::newest()), window, cx, |s| {
10727 if action.replace_newest {
10728 s.delete(s.newest_anchor().id);
10729 }
10730 s.insert_range(next_selected_range);
10731 });
10732 } else {
10733 select_prev_state.done = true;
10734 }
10735 }
10736
10737 self.select_prev_state = Some(select_prev_state);
10738 } else {
10739 let mut only_carets = true;
10740 let mut same_text_selected = true;
10741 let mut selected_text = None;
10742
10743 let mut selections_iter = selections.iter().peekable();
10744 while let Some(selection) = selections_iter.next() {
10745 if selection.start != selection.end {
10746 only_carets = false;
10747 }
10748
10749 if same_text_selected {
10750 if selected_text.is_none() {
10751 selected_text =
10752 Some(buffer.text_for_range(selection.range()).collect::<String>());
10753 }
10754
10755 if let Some(next_selection) = selections_iter.peek() {
10756 if next_selection.range().len() == selection.range().len() {
10757 let next_selected_text = buffer
10758 .text_for_range(next_selection.range())
10759 .collect::<String>();
10760 if Some(next_selected_text) != selected_text {
10761 same_text_selected = false;
10762 selected_text = None;
10763 }
10764 } else {
10765 same_text_selected = false;
10766 selected_text = None;
10767 }
10768 }
10769 }
10770 }
10771
10772 if only_carets {
10773 for selection in &mut selections {
10774 let word_range = movement::surrounding_word(
10775 &display_map,
10776 selection.start.to_display_point(&display_map),
10777 );
10778 selection.start = word_range.start.to_offset(&display_map, Bias::Left);
10779 selection.end = word_range.end.to_offset(&display_map, Bias::Left);
10780 selection.goal = SelectionGoal::None;
10781 selection.reversed = false;
10782 }
10783 if selections.len() == 1 {
10784 let selection = selections
10785 .last()
10786 .expect("ensured that there's only one selection");
10787 let query = buffer
10788 .text_for_range(selection.start..selection.end)
10789 .collect::<String>();
10790 let is_empty = query.is_empty();
10791 let select_state = SelectNextState {
10792 query: AhoCorasick::new(&[query.chars().rev().collect::<String>()])?,
10793 wordwise: true,
10794 done: is_empty,
10795 };
10796 self.select_prev_state = Some(select_state);
10797 } else {
10798 self.select_prev_state = None;
10799 }
10800
10801 self.unfold_ranges(
10802 &selections.iter().map(|s| s.range()).collect::<Vec<_>>(),
10803 false,
10804 true,
10805 cx,
10806 );
10807 self.change_selections(Some(Autoscroll::newest()), window, cx, |s| {
10808 s.select(selections);
10809 });
10810 } else if let Some(selected_text) = selected_text {
10811 self.select_prev_state = Some(SelectNextState {
10812 query: AhoCorasick::new(&[selected_text.chars().rev().collect::<String>()])?,
10813 wordwise: false,
10814 done: false,
10815 });
10816 self.select_previous(action, window, cx)?;
10817 }
10818 }
10819 Ok(())
10820 }
10821
10822 pub fn toggle_comments(
10823 &mut self,
10824 action: &ToggleComments,
10825 window: &mut Window,
10826 cx: &mut Context<Self>,
10827 ) {
10828 if self.read_only(cx) {
10829 return;
10830 }
10831 let text_layout_details = &self.text_layout_details(window);
10832 self.transact(window, cx, |this, window, cx| {
10833 let mut selections = this.selections.all::<MultiBufferPoint>(cx);
10834 let mut edits = Vec::new();
10835 let mut selection_edit_ranges = Vec::new();
10836 let mut last_toggled_row = None;
10837 let snapshot = this.buffer.read(cx).read(cx);
10838 let empty_str: Arc<str> = Arc::default();
10839 let mut suffixes_inserted = Vec::new();
10840 let ignore_indent = action.ignore_indent;
10841
10842 fn comment_prefix_range(
10843 snapshot: &MultiBufferSnapshot,
10844 row: MultiBufferRow,
10845 comment_prefix: &str,
10846 comment_prefix_whitespace: &str,
10847 ignore_indent: bool,
10848 ) -> Range<Point> {
10849 let indent_size = if ignore_indent {
10850 0
10851 } else {
10852 snapshot.indent_size_for_line(row).len
10853 };
10854
10855 let start = Point::new(row.0, indent_size);
10856
10857 let mut line_bytes = snapshot
10858 .bytes_in_range(start..snapshot.max_point())
10859 .flatten()
10860 .copied();
10861
10862 // If this line currently begins with the line comment prefix, then record
10863 // the range containing the prefix.
10864 if line_bytes
10865 .by_ref()
10866 .take(comment_prefix.len())
10867 .eq(comment_prefix.bytes())
10868 {
10869 // Include any whitespace that matches the comment prefix.
10870 let matching_whitespace_len = line_bytes
10871 .zip(comment_prefix_whitespace.bytes())
10872 .take_while(|(a, b)| a == b)
10873 .count() as u32;
10874 let end = Point::new(
10875 start.row,
10876 start.column + comment_prefix.len() as u32 + matching_whitespace_len,
10877 );
10878 start..end
10879 } else {
10880 start..start
10881 }
10882 }
10883
10884 fn comment_suffix_range(
10885 snapshot: &MultiBufferSnapshot,
10886 row: MultiBufferRow,
10887 comment_suffix: &str,
10888 comment_suffix_has_leading_space: bool,
10889 ) -> Range<Point> {
10890 let end = Point::new(row.0, snapshot.line_len(row));
10891 let suffix_start_column = end.column.saturating_sub(comment_suffix.len() as u32);
10892
10893 let mut line_end_bytes = snapshot
10894 .bytes_in_range(Point::new(end.row, suffix_start_column.saturating_sub(1))..end)
10895 .flatten()
10896 .copied();
10897
10898 let leading_space_len = if suffix_start_column > 0
10899 && line_end_bytes.next() == Some(b' ')
10900 && comment_suffix_has_leading_space
10901 {
10902 1
10903 } else {
10904 0
10905 };
10906
10907 // If this line currently begins with the line comment prefix, then record
10908 // the range containing the prefix.
10909 if line_end_bytes.by_ref().eq(comment_suffix.bytes()) {
10910 let start = Point::new(end.row, suffix_start_column - leading_space_len);
10911 start..end
10912 } else {
10913 end..end
10914 }
10915 }
10916
10917 // TODO: Handle selections that cross excerpts
10918 for selection in &mut selections {
10919 let start_column = snapshot
10920 .indent_size_for_line(MultiBufferRow(selection.start.row))
10921 .len;
10922 let language = if let Some(language) =
10923 snapshot.language_scope_at(Point::new(selection.start.row, start_column))
10924 {
10925 language
10926 } else {
10927 continue;
10928 };
10929
10930 selection_edit_ranges.clear();
10931
10932 // If multiple selections contain a given row, avoid processing that
10933 // row more than once.
10934 let mut start_row = MultiBufferRow(selection.start.row);
10935 if last_toggled_row == Some(start_row) {
10936 start_row = start_row.next_row();
10937 }
10938 let end_row =
10939 if selection.end.row > selection.start.row && selection.end.column == 0 {
10940 MultiBufferRow(selection.end.row - 1)
10941 } else {
10942 MultiBufferRow(selection.end.row)
10943 };
10944 last_toggled_row = Some(end_row);
10945
10946 if start_row > end_row {
10947 continue;
10948 }
10949
10950 // If the language has line comments, toggle those.
10951 let mut full_comment_prefixes = language.line_comment_prefixes().to_vec();
10952
10953 // If ignore_indent is set, trim spaces from the right side of all full_comment_prefixes
10954 if ignore_indent {
10955 full_comment_prefixes = full_comment_prefixes
10956 .into_iter()
10957 .map(|s| Arc::from(s.trim_end()))
10958 .collect();
10959 }
10960
10961 if !full_comment_prefixes.is_empty() {
10962 let first_prefix = full_comment_prefixes
10963 .first()
10964 .expect("prefixes is non-empty");
10965 let prefix_trimmed_lengths = full_comment_prefixes
10966 .iter()
10967 .map(|p| p.trim_end_matches(' ').len())
10968 .collect::<SmallVec<[usize; 4]>>();
10969
10970 let mut all_selection_lines_are_comments = true;
10971
10972 for row in start_row.0..=end_row.0 {
10973 let row = MultiBufferRow(row);
10974 if start_row < end_row && snapshot.is_line_blank(row) {
10975 continue;
10976 }
10977
10978 let prefix_range = full_comment_prefixes
10979 .iter()
10980 .zip(prefix_trimmed_lengths.iter().copied())
10981 .map(|(prefix, trimmed_prefix_len)| {
10982 comment_prefix_range(
10983 snapshot.deref(),
10984 row,
10985 &prefix[..trimmed_prefix_len],
10986 &prefix[trimmed_prefix_len..],
10987 ignore_indent,
10988 )
10989 })
10990 .max_by_key(|range| range.end.column - range.start.column)
10991 .expect("prefixes is non-empty");
10992
10993 if prefix_range.is_empty() {
10994 all_selection_lines_are_comments = false;
10995 }
10996
10997 selection_edit_ranges.push(prefix_range);
10998 }
10999
11000 if all_selection_lines_are_comments {
11001 edits.extend(
11002 selection_edit_ranges
11003 .iter()
11004 .cloned()
11005 .map(|range| (range, empty_str.clone())),
11006 );
11007 } else {
11008 let min_column = selection_edit_ranges
11009 .iter()
11010 .map(|range| range.start.column)
11011 .min()
11012 .unwrap_or(0);
11013 edits.extend(selection_edit_ranges.iter().map(|range| {
11014 let position = Point::new(range.start.row, min_column);
11015 (position..position, first_prefix.clone())
11016 }));
11017 }
11018 } else if let Some((full_comment_prefix, comment_suffix)) =
11019 language.block_comment_delimiters()
11020 {
11021 let comment_prefix = full_comment_prefix.trim_end_matches(' ');
11022 let comment_prefix_whitespace = &full_comment_prefix[comment_prefix.len()..];
11023 let prefix_range = comment_prefix_range(
11024 snapshot.deref(),
11025 start_row,
11026 comment_prefix,
11027 comment_prefix_whitespace,
11028 ignore_indent,
11029 );
11030 let suffix_range = comment_suffix_range(
11031 snapshot.deref(),
11032 end_row,
11033 comment_suffix.trim_start_matches(' '),
11034 comment_suffix.starts_with(' '),
11035 );
11036
11037 if prefix_range.is_empty() || suffix_range.is_empty() {
11038 edits.push((
11039 prefix_range.start..prefix_range.start,
11040 full_comment_prefix.clone(),
11041 ));
11042 edits.push((suffix_range.end..suffix_range.end, comment_suffix.clone()));
11043 suffixes_inserted.push((end_row, comment_suffix.len()));
11044 } else {
11045 edits.push((prefix_range, empty_str.clone()));
11046 edits.push((suffix_range, empty_str.clone()));
11047 }
11048 } else {
11049 continue;
11050 }
11051 }
11052
11053 drop(snapshot);
11054 this.buffer.update(cx, |buffer, cx| {
11055 buffer.edit(edits, None, cx);
11056 });
11057
11058 // Adjust selections so that they end before any comment suffixes that
11059 // were inserted.
11060 let mut suffixes_inserted = suffixes_inserted.into_iter().peekable();
11061 let mut selections = this.selections.all::<Point>(cx);
11062 let snapshot = this.buffer.read(cx).read(cx);
11063 for selection in &mut selections {
11064 while let Some((row, suffix_len)) = suffixes_inserted.peek().copied() {
11065 match row.cmp(&MultiBufferRow(selection.end.row)) {
11066 Ordering::Less => {
11067 suffixes_inserted.next();
11068 continue;
11069 }
11070 Ordering::Greater => break,
11071 Ordering::Equal => {
11072 if selection.end.column == snapshot.line_len(row) {
11073 if selection.is_empty() {
11074 selection.start.column -= suffix_len as u32;
11075 }
11076 selection.end.column -= suffix_len as u32;
11077 }
11078 break;
11079 }
11080 }
11081 }
11082 }
11083
11084 drop(snapshot);
11085 this.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
11086 s.select(selections)
11087 });
11088
11089 let selections = this.selections.all::<Point>(cx);
11090 let selections_on_single_row = selections.windows(2).all(|selections| {
11091 selections[0].start.row == selections[1].start.row
11092 && selections[0].end.row == selections[1].end.row
11093 && selections[0].start.row == selections[0].end.row
11094 });
11095 let selections_selecting = selections
11096 .iter()
11097 .any(|selection| selection.start != selection.end);
11098 let advance_downwards = action.advance_downwards
11099 && selections_on_single_row
11100 && !selections_selecting
11101 && !matches!(this.mode, EditorMode::SingleLine { .. });
11102
11103 if advance_downwards {
11104 let snapshot = this.buffer.read(cx).snapshot(cx);
11105
11106 this.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
11107 s.move_cursors_with(|display_snapshot, display_point, _| {
11108 let mut point = display_point.to_point(display_snapshot);
11109 point.row += 1;
11110 point = snapshot.clip_point(point, Bias::Left);
11111 let display_point = point.to_display_point(display_snapshot);
11112 let goal = SelectionGoal::HorizontalPosition(
11113 display_snapshot
11114 .x_for_display_point(display_point, text_layout_details)
11115 .into(),
11116 );
11117 (display_point, goal)
11118 })
11119 });
11120 }
11121 });
11122 }
11123
11124 pub fn select_enclosing_symbol(
11125 &mut self,
11126 _: &SelectEnclosingSymbol,
11127 window: &mut Window,
11128 cx: &mut Context<Self>,
11129 ) {
11130 let buffer = self.buffer.read(cx).snapshot(cx);
11131 let old_selections = self.selections.all::<usize>(cx).into_boxed_slice();
11132
11133 fn update_selection(
11134 selection: &Selection<usize>,
11135 buffer_snap: &MultiBufferSnapshot,
11136 ) -> Option<Selection<usize>> {
11137 let cursor = selection.head();
11138 let (_buffer_id, symbols) = buffer_snap.symbols_containing(cursor, None)?;
11139 for symbol in symbols.iter().rev() {
11140 let start = symbol.range.start.to_offset(buffer_snap);
11141 let end = symbol.range.end.to_offset(buffer_snap);
11142 let new_range = start..end;
11143 if start < selection.start || end > selection.end {
11144 return Some(Selection {
11145 id: selection.id,
11146 start: new_range.start,
11147 end: new_range.end,
11148 goal: SelectionGoal::None,
11149 reversed: selection.reversed,
11150 });
11151 }
11152 }
11153 None
11154 }
11155
11156 let mut selected_larger_symbol = false;
11157 let new_selections = old_selections
11158 .iter()
11159 .map(|selection| match update_selection(selection, &buffer) {
11160 Some(new_selection) => {
11161 if new_selection.range() != selection.range() {
11162 selected_larger_symbol = true;
11163 }
11164 new_selection
11165 }
11166 None => selection.clone(),
11167 })
11168 .collect::<Vec<_>>();
11169
11170 if selected_larger_symbol {
11171 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
11172 s.select(new_selections);
11173 });
11174 }
11175 }
11176
11177 pub fn select_larger_syntax_node(
11178 &mut self,
11179 _: &SelectLargerSyntaxNode,
11180 window: &mut Window,
11181 cx: &mut Context<Self>,
11182 ) {
11183 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
11184 let buffer = self.buffer.read(cx).snapshot(cx);
11185 let old_selections = self.selections.all::<usize>(cx).into_boxed_slice();
11186
11187 let mut stack = mem::take(&mut self.select_larger_syntax_node_stack);
11188 let mut selected_larger_node = false;
11189 let new_selections = old_selections
11190 .iter()
11191 .map(|selection| {
11192 let old_range = selection.start..selection.end;
11193 let mut new_range = old_range.clone();
11194 let mut new_node = None;
11195 while let Some((node, containing_range)) = buffer.syntax_ancestor(new_range.clone())
11196 {
11197 new_node = Some(node);
11198 new_range = match containing_range {
11199 MultiOrSingleBufferOffsetRange::Single(_) => break,
11200 MultiOrSingleBufferOffsetRange::Multi(range) => range,
11201 };
11202 if !display_map.intersects_fold(new_range.start)
11203 && !display_map.intersects_fold(new_range.end)
11204 {
11205 break;
11206 }
11207 }
11208
11209 if let Some(node) = new_node {
11210 // Log the ancestor, to support using this action as a way to explore TreeSitter
11211 // nodes. Parent and grandparent are also logged because this operation will not
11212 // visit nodes that have the same range as their parent.
11213 log::info!("Node: {node:?}");
11214 let parent = node.parent();
11215 log::info!("Parent: {parent:?}");
11216 let grandparent = parent.and_then(|x| x.parent());
11217 log::info!("Grandparent: {grandparent:?}");
11218 }
11219
11220 selected_larger_node |= new_range != old_range;
11221 Selection {
11222 id: selection.id,
11223 start: new_range.start,
11224 end: new_range.end,
11225 goal: SelectionGoal::None,
11226 reversed: selection.reversed,
11227 }
11228 })
11229 .collect::<Vec<_>>();
11230
11231 if selected_larger_node {
11232 stack.push(old_selections);
11233 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
11234 s.select(new_selections);
11235 });
11236 }
11237 self.select_larger_syntax_node_stack = stack;
11238 }
11239
11240 pub fn select_smaller_syntax_node(
11241 &mut self,
11242 _: &SelectSmallerSyntaxNode,
11243 window: &mut Window,
11244 cx: &mut Context<Self>,
11245 ) {
11246 let mut stack = mem::take(&mut self.select_larger_syntax_node_stack);
11247 if let Some(selections) = stack.pop() {
11248 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
11249 s.select(selections.to_vec());
11250 });
11251 }
11252 self.select_larger_syntax_node_stack = stack;
11253 }
11254
11255 fn refresh_runnables(&mut self, window: &mut Window, cx: &mut Context<Self>) -> Task<()> {
11256 if !EditorSettings::get_global(cx).gutter.runnables {
11257 self.clear_tasks();
11258 return Task::ready(());
11259 }
11260 let project = self.project.as_ref().map(Entity::downgrade);
11261 cx.spawn_in(window, |this, mut cx| async move {
11262 cx.background_executor().timer(UPDATE_DEBOUNCE).await;
11263 let Some(project) = project.and_then(|p| p.upgrade()) else {
11264 return;
11265 };
11266 let Ok(display_snapshot) = this.update(&mut cx, |this, cx| {
11267 this.display_map.update(cx, |map, cx| map.snapshot(cx))
11268 }) else {
11269 return;
11270 };
11271
11272 let hide_runnables = project
11273 .update(&mut cx, |project, cx| {
11274 // Do not display any test indicators in non-dev server remote projects.
11275 project.is_via_collab() && project.ssh_connection_string(cx).is_none()
11276 })
11277 .unwrap_or(true);
11278 if hide_runnables {
11279 return;
11280 }
11281 let new_rows =
11282 cx.background_spawn({
11283 let snapshot = display_snapshot.clone();
11284 async move {
11285 Self::fetch_runnable_ranges(&snapshot, Anchor::min()..Anchor::max())
11286 }
11287 })
11288 .await;
11289
11290 let rows = Self::runnable_rows(project, display_snapshot, new_rows, cx.clone());
11291 this.update(&mut cx, |this, _| {
11292 this.clear_tasks();
11293 for (key, value) in rows {
11294 this.insert_tasks(key, value);
11295 }
11296 })
11297 .ok();
11298 })
11299 }
11300 fn fetch_runnable_ranges(
11301 snapshot: &DisplaySnapshot,
11302 range: Range<Anchor>,
11303 ) -> Vec<language::RunnableRange> {
11304 snapshot.buffer_snapshot.runnable_ranges(range).collect()
11305 }
11306
11307 fn runnable_rows(
11308 project: Entity<Project>,
11309 snapshot: DisplaySnapshot,
11310 runnable_ranges: Vec<RunnableRange>,
11311 mut cx: AsyncWindowContext,
11312 ) -> Vec<((BufferId, u32), RunnableTasks)> {
11313 runnable_ranges
11314 .into_iter()
11315 .filter_map(|mut runnable| {
11316 let tasks = cx
11317 .update(|_, cx| Self::templates_with_tags(&project, &mut runnable.runnable, cx))
11318 .ok()?;
11319 if tasks.is_empty() {
11320 return None;
11321 }
11322
11323 let point = runnable.run_range.start.to_point(&snapshot.buffer_snapshot);
11324
11325 let row = snapshot
11326 .buffer_snapshot
11327 .buffer_line_for_row(MultiBufferRow(point.row))?
11328 .1
11329 .start
11330 .row;
11331
11332 let context_range =
11333 BufferOffset(runnable.full_range.start)..BufferOffset(runnable.full_range.end);
11334 Some((
11335 (runnable.buffer_id, row),
11336 RunnableTasks {
11337 templates: tasks,
11338 offset: snapshot
11339 .buffer_snapshot
11340 .anchor_before(runnable.run_range.start),
11341 context_range,
11342 column: point.column,
11343 extra_variables: runnable.extra_captures,
11344 },
11345 ))
11346 })
11347 .collect()
11348 }
11349
11350 fn templates_with_tags(
11351 project: &Entity<Project>,
11352 runnable: &mut Runnable,
11353 cx: &mut App,
11354 ) -> Vec<(TaskSourceKind, TaskTemplate)> {
11355 let (inventory, worktree_id, file) = project.read_with(cx, |project, cx| {
11356 let (worktree_id, file) = project
11357 .buffer_for_id(runnable.buffer, cx)
11358 .and_then(|buffer| buffer.read(cx).file())
11359 .map(|file| (file.worktree_id(cx), file.clone()))
11360 .unzip();
11361
11362 (
11363 project.task_store().read(cx).task_inventory().cloned(),
11364 worktree_id,
11365 file,
11366 )
11367 });
11368
11369 let tags = mem::take(&mut runnable.tags);
11370 let mut tags: Vec<_> = tags
11371 .into_iter()
11372 .flat_map(|tag| {
11373 let tag = tag.0.clone();
11374 inventory
11375 .as_ref()
11376 .into_iter()
11377 .flat_map(|inventory| {
11378 inventory.read(cx).list_tasks(
11379 file.clone(),
11380 Some(runnable.language.clone()),
11381 worktree_id,
11382 cx,
11383 )
11384 })
11385 .filter(move |(_, template)| {
11386 template.tags.iter().any(|source_tag| source_tag == &tag)
11387 })
11388 })
11389 .sorted_by_key(|(kind, _)| kind.to_owned())
11390 .collect();
11391 if let Some((leading_tag_source, _)) = tags.first() {
11392 // Strongest source wins; if we have worktree tag binding, prefer that to
11393 // global and language bindings;
11394 // if we have a global binding, prefer that to language binding.
11395 let first_mismatch = tags
11396 .iter()
11397 .position(|(tag_source, _)| tag_source != leading_tag_source);
11398 if let Some(index) = first_mismatch {
11399 tags.truncate(index);
11400 }
11401 }
11402
11403 tags
11404 }
11405
11406 pub fn move_to_enclosing_bracket(
11407 &mut self,
11408 _: &MoveToEnclosingBracket,
11409 window: &mut Window,
11410 cx: &mut Context<Self>,
11411 ) {
11412 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
11413 s.move_offsets_with(|snapshot, selection| {
11414 let Some(enclosing_bracket_ranges) =
11415 snapshot.enclosing_bracket_ranges(selection.start..selection.end)
11416 else {
11417 return;
11418 };
11419
11420 let mut best_length = usize::MAX;
11421 let mut best_inside = false;
11422 let mut best_in_bracket_range = false;
11423 let mut best_destination = None;
11424 for (open, close) in enclosing_bracket_ranges {
11425 let close = close.to_inclusive();
11426 let length = close.end() - open.start;
11427 let inside = selection.start >= open.end && selection.end <= *close.start();
11428 let in_bracket_range = open.to_inclusive().contains(&selection.head())
11429 || close.contains(&selection.head());
11430
11431 // If best is next to a bracket and current isn't, skip
11432 if !in_bracket_range && best_in_bracket_range {
11433 continue;
11434 }
11435
11436 // Prefer smaller lengths unless best is inside and current isn't
11437 if length > best_length && (best_inside || !inside) {
11438 continue;
11439 }
11440
11441 best_length = length;
11442 best_inside = inside;
11443 best_in_bracket_range = in_bracket_range;
11444 best_destination = Some(
11445 if close.contains(&selection.start) && close.contains(&selection.end) {
11446 if inside {
11447 open.end
11448 } else {
11449 open.start
11450 }
11451 } else if inside {
11452 *close.start()
11453 } else {
11454 *close.end()
11455 },
11456 );
11457 }
11458
11459 if let Some(destination) = best_destination {
11460 selection.collapse_to(destination, SelectionGoal::None);
11461 }
11462 })
11463 });
11464 }
11465
11466 pub fn undo_selection(
11467 &mut self,
11468 _: &UndoSelection,
11469 window: &mut Window,
11470 cx: &mut Context<Self>,
11471 ) {
11472 self.end_selection(window, cx);
11473 self.selection_history.mode = SelectionHistoryMode::Undoing;
11474 if let Some(entry) = self.selection_history.undo_stack.pop_back() {
11475 self.change_selections(None, window, cx, |s| {
11476 s.select_anchors(entry.selections.to_vec())
11477 });
11478 self.select_next_state = entry.select_next_state;
11479 self.select_prev_state = entry.select_prev_state;
11480 self.add_selections_state = entry.add_selections_state;
11481 self.request_autoscroll(Autoscroll::newest(), cx);
11482 }
11483 self.selection_history.mode = SelectionHistoryMode::Normal;
11484 }
11485
11486 pub fn redo_selection(
11487 &mut self,
11488 _: &RedoSelection,
11489 window: &mut Window,
11490 cx: &mut Context<Self>,
11491 ) {
11492 self.end_selection(window, cx);
11493 self.selection_history.mode = SelectionHistoryMode::Redoing;
11494 if let Some(entry) = self.selection_history.redo_stack.pop_back() {
11495 self.change_selections(None, window, cx, |s| {
11496 s.select_anchors(entry.selections.to_vec())
11497 });
11498 self.select_next_state = entry.select_next_state;
11499 self.select_prev_state = entry.select_prev_state;
11500 self.add_selections_state = entry.add_selections_state;
11501 self.request_autoscroll(Autoscroll::newest(), cx);
11502 }
11503 self.selection_history.mode = SelectionHistoryMode::Normal;
11504 }
11505
11506 pub fn expand_excerpts(
11507 &mut self,
11508 action: &ExpandExcerpts,
11509 _: &mut Window,
11510 cx: &mut Context<Self>,
11511 ) {
11512 self.expand_excerpts_for_direction(action.lines, ExpandExcerptDirection::UpAndDown, cx)
11513 }
11514
11515 pub fn expand_excerpts_down(
11516 &mut self,
11517 action: &ExpandExcerptsDown,
11518 _: &mut Window,
11519 cx: &mut Context<Self>,
11520 ) {
11521 self.expand_excerpts_for_direction(action.lines, ExpandExcerptDirection::Down, cx)
11522 }
11523
11524 pub fn expand_excerpts_up(
11525 &mut self,
11526 action: &ExpandExcerptsUp,
11527 _: &mut Window,
11528 cx: &mut Context<Self>,
11529 ) {
11530 self.expand_excerpts_for_direction(action.lines, ExpandExcerptDirection::Up, cx)
11531 }
11532
11533 pub fn expand_excerpts_for_direction(
11534 &mut self,
11535 lines: u32,
11536 direction: ExpandExcerptDirection,
11537
11538 cx: &mut Context<Self>,
11539 ) {
11540 let selections = self.selections.disjoint_anchors();
11541
11542 let lines = if lines == 0 {
11543 EditorSettings::get_global(cx).expand_excerpt_lines
11544 } else {
11545 lines
11546 };
11547
11548 self.buffer.update(cx, |buffer, cx| {
11549 let snapshot = buffer.snapshot(cx);
11550 let mut excerpt_ids = selections
11551 .iter()
11552 .flat_map(|selection| snapshot.excerpt_ids_for_range(selection.range()))
11553 .collect::<Vec<_>>();
11554 excerpt_ids.sort();
11555 excerpt_ids.dedup();
11556 buffer.expand_excerpts(excerpt_ids, lines, direction, cx)
11557 })
11558 }
11559
11560 pub fn expand_excerpt(
11561 &mut self,
11562 excerpt: ExcerptId,
11563 direction: ExpandExcerptDirection,
11564 cx: &mut Context<Self>,
11565 ) {
11566 let lines = EditorSettings::get_global(cx).expand_excerpt_lines;
11567 self.buffer.update(cx, |buffer, cx| {
11568 buffer.expand_excerpts([excerpt], lines, direction, cx)
11569 })
11570 }
11571
11572 pub fn go_to_singleton_buffer_point(
11573 &mut self,
11574 point: Point,
11575 window: &mut Window,
11576 cx: &mut Context<Self>,
11577 ) {
11578 self.go_to_singleton_buffer_range(point..point, window, cx);
11579 }
11580
11581 pub fn go_to_singleton_buffer_range(
11582 &mut self,
11583 range: Range<Point>,
11584 window: &mut Window,
11585 cx: &mut Context<Self>,
11586 ) {
11587 let multibuffer = self.buffer().read(cx);
11588 let Some(buffer) = multibuffer.as_singleton() else {
11589 return;
11590 };
11591 let Some(start) = multibuffer.buffer_point_to_anchor(&buffer, range.start, cx) else {
11592 return;
11593 };
11594 let Some(end) = multibuffer.buffer_point_to_anchor(&buffer, range.end, cx) else {
11595 return;
11596 };
11597 self.change_selections(Some(Autoscroll::center()), window, cx, |s| {
11598 s.select_anchor_ranges([start..end])
11599 });
11600 }
11601
11602 fn go_to_diagnostic(
11603 &mut self,
11604 _: &GoToDiagnostic,
11605 window: &mut Window,
11606 cx: &mut Context<Self>,
11607 ) {
11608 self.go_to_diagnostic_impl(Direction::Next, window, cx)
11609 }
11610
11611 fn go_to_prev_diagnostic(
11612 &mut self,
11613 _: &GoToPreviousDiagnostic,
11614 window: &mut Window,
11615 cx: &mut Context<Self>,
11616 ) {
11617 self.go_to_diagnostic_impl(Direction::Prev, window, cx)
11618 }
11619
11620 pub fn go_to_diagnostic_impl(
11621 &mut self,
11622 direction: Direction,
11623 window: &mut Window,
11624 cx: &mut Context<Self>,
11625 ) {
11626 let buffer = self.buffer.read(cx).snapshot(cx);
11627 let selection = self.selections.newest::<usize>(cx);
11628
11629 // If there is an active Diagnostic Popover jump to its diagnostic instead.
11630 if direction == Direction::Next {
11631 if let Some(popover) = self.hover_state.diagnostic_popover.as_ref() {
11632 let Some(buffer_id) = popover.local_diagnostic.range.start.buffer_id else {
11633 return;
11634 };
11635 self.activate_diagnostics(
11636 buffer_id,
11637 popover.local_diagnostic.diagnostic.group_id,
11638 window,
11639 cx,
11640 );
11641 if let Some(active_diagnostics) = self.active_diagnostics.as_ref() {
11642 let primary_range_start = active_diagnostics.primary_range.start;
11643 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
11644 let mut new_selection = s.newest_anchor().clone();
11645 new_selection.collapse_to(primary_range_start, SelectionGoal::None);
11646 s.select_anchors(vec![new_selection.clone()]);
11647 });
11648 self.refresh_inline_completion(false, true, window, cx);
11649 }
11650 return;
11651 }
11652 }
11653
11654 let active_group_id = self
11655 .active_diagnostics
11656 .as_ref()
11657 .map(|active_group| active_group.group_id);
11658 let active_primary_range = self.active_diagnostics.as_ref().map(|active_diagnostics| {
11659 active_diagnostics
11660 .primary_range
11661 .to_offset(&buffer)
11662 .to_inclusive()
11663 });
11664 let search_start = if let Some(active_primary_range) = active_primary_range.as_ref() {
11665 if active_primary_range.contains(&selection.head()) {
11666 *active_primary_range.start()
11667 } else {
11668 selection.head()
11669 }
11670 } else {
11671 selection.head()
11672 };
11673
11674 let snapshot = self.snapshot(window, cx);
11675 let primary_diagnostics_before = buffer
11676 .diagnostics_in_range::<usize>(0..search_start)
11677 .filter(|entry| entry.diagnostic.is_primary)
11678 .filter(|entry| entry.range.start != entry.range.end)
11679 .filter(|entry| entry.diagnostic.severity <= DiagnosticSeverity::WARNING)
11680 .filter(|entry| !snapshot.intersects_fold(entry.range.start))
11681 .collect::<Vec<_>>();
11682 let last_same_group_diagnostic_before = active_group_id.and_then(|active_group_id| {
11683 primary_diagnostics_before
11684 .iter()
11685 .position(|entry| entry.diagnostic.group_id == active_group_id)
11686 });
11687
11688 let primary_diagnostics_after = buffer
11689 .diagnostics_in_range::<usize>(search_start..buffer.len())
11690 .filter(|entry| entry.diagnostic.is_primary)
11691 .filter(|entry| entry.range.start != entry.range.end)
11692 .filter(|entry| entry.diagnostic.severity <= DiagnosticSeverity::WARNING)
11693 .filter(|diagnostic| !snapshot.intersects_fold(diagnostic.range.start))
11694 .collect::<Vec<_>>();
11695 let last_same_group_diagnostic_after = active_group_id.and_then(|active_group_id| {
11696 primary_diagnostics_after
11697 .iter()
11698 .enumerate()
11699 .rev()
11700 .find_map(|(i, entry)| {
11701 if entry.diagnostic.group_id == active_group_id {
11702 Some(i)
11703 } else {
11704 None
11705 }
11706 })
11707 });
11708
11709 let next_primary_diagnostic = match direction {
11710 Direction::Prev => primary_diagnostics_before
11711 .iter()
11712 .take(last_same_group_diagnostic_before.unwrap_or(usize::MAX))
11713 .rev()
11714 .next(),
11715 Direction::Next => primary_diagnostics_after
11716 .iter()
11717 .skip(
11718 last_same_group_diagnostic_after
11719 .map(|index| index + 1)
11720 .unwrap_or(0),
11721 )
11722 .next(),
11723 };
11724
11725 // Cycle around to the start of the buffer, potentially moving back to the start of
11726 // the currently active diagnostic.
11727 let cycle_around = || match direction {
11728 Direction::Prev => primary_diagnostics_after
11729 .iter()
11730 .rev()
11731 .chain(primary_diagnostics_before.iter().rev())
11732 .next(),
11733 Direction::Next => primary_diagnostics_before
11734 .iter()
11735 .chain(primary_diagnostics_after.iter())
11736 .next(),
11737 };
11738
11739 if let Some((primary_range, group_id)) = next_primary_diagnostic
11740 .or_else(cycle_around)
11741 .map(|entry| (&entry.range, entry.diagnostic.group_id))
11742 {
11743 let Some(buffer_id) = buffer.anchor_after(primary_range.start).buffer_id else {
11744 return;
11745 };
11746 self.activate_diagnostics(buffer_id, group_id, window, cx);
11747 if self.active_diagnostics.is_some() {
11748 self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
11749 s.select(vec![Selection {
11750 id: selection.id,
11751 start: primary_range.start,
11752 end: primary_range.start,
11753 reversed: false,
11754 goal: SelectionGoal::None,
11755 }]);
11756 });
11757 self.refresh_inline_completion(false, true, window, cx);
11758 }
11759 }
11760 }
11761
11762 fn go_to_next_hunk(&mut self, _: &GoToHunk, window: &mut Window, cx: &mut Context<Self>) {
11763 let snapshot = self.snapshot(window, cx);
11764 let selection = self.selections.newest::<Point>(cx);
11765 self.go_to_hunk_before_or_after_position(
11766 &snapshot,
11767 selection.head(),
11768 Direction::Next,
11769 window,
11770 cx,
11771 );
11772 }
11773
11774 fn go_to_hunk_before_or_after_position(
11775 &mut self,
11776 snapshot: &EditorSnapshot,
11777 position: Point,
11778 direction: Direction,
11779 window: &mut Window,
11780 cx: &mut Context<Editor>,
11781 ) {
11782 let row = if direction == Direction::Next {
11783 self.hunk_after_position(snapshot, position)
11784 .map(|hunk| hunk.row_range.start)
11785 } else {
11786 self.hunk_before_position(snapshot, position)
11787 };
11788
11789 if let Some(row) = row {
11790 let destination = Point::new(row.0, 0);
11791 let autoscroll = Autoscroll::center();
11792
11793 self.unfold_ranges(&[destination..destination], false, false, cx);
11794 self.change_selections(Some(autoscroll), window, cx, |s| {
11795 s.select_ranges([destination..destination]);
11796 });
11797 }
11798 }
11799
11800 fn hunk_after_position(
11801 &mut self,
11802 snapshot: &EditorSnapshot,
11803 position: Point,
11804 ) -> Option<MultiBufferDiffHunk> {
11805 snapshot
11806 .buffer_snapshot
11807 .diff_hunks_in_range(position..snapshot.buffer_snapshot.max_point())
11808 .find(|hunk| hunk.row_range.start.0 > position.row)
11809 .or_else(|| {
11810 snapshot
11811 .buffer_snapshot
11812 .diff_hunks_in_range(Point::zero()..position)
11813 .find(|hunk| hunk.row_range.end.0 < position.row)
11814 })
11815 }
11816
11817 fn go_to_prev_hunk(
11818 &mut self,
11819 _: &GoToPreviousHunk,
11820 window: &mut Window,
11821 cx: &mut Context<Self>,
11822 ) {
11823 let snapshot = self.snapshot(window, cx);
11824 let selection = self.selections.newest::<Point>(cx);
11825 self.go_to_hunk_before_or_after_position(
11826 &snapshot,
11827 selection.head(),
11828 Direction::Prev,
11829 window,
11830 cx,
11831 );
11832 }
11833
11834 fn hunk_before_position(
11835 &mut self,
11836 snapshot: &EditorSnapshot,
11837 position: Point,
11838 ) -> Option<MultiBufferRow> {
11839 snapshot
11840 .buffer_snapshot
11841 .diff_hunk_before(position)
11842 .or_else(|| snapshot.buffer_snapshot.diff_hunk_before(Point::MAX))
11843 }
11844
11845 pub fn go_to_definition(
11846 &mut self,
11847 _: &GoToDefinition,
11848 window: &mut Window,
11849 cx: &mut Context<Self>,
11850 ) -> Task<Result<Navigated>> {
11851 let definition =
11852 self.go_to_definition_of_kind(GotoDefinitionKind::Symbol, false, window, cx);
11853 cx.spawn_in(window, |editor, mut cx| async move {
11854 if definition.await? == Navigated::Yes {
11855 return Ok(Navigated::Yes);
11856 }
11857 match editor.update_in(&mut cx, |editor, window, cx| {
11858 editor.find_all_references(&FindAllReferences, window, cx)
11859 })? {
11860 Some(references) => references.await,
11861 None => Ok(Navigated::No),
11862 }
11863 })
11864 }
11865
11866 pub fn go_to_declaration(
11867 &mut self,
11868 _: &GoToDeclaration,
11869 window: &mut Window,
11870 cx: &mut Context<Self>,
11871 ) -> Task<Result<Navigated>> {
11872 self.go_to_definition_of_kind(GotoDefinitionKind::Declaration, false, window, cx)
11873 }
11874
11875 pub fn go_to_declaration_split(
11876 &mut self,
11877 _: &GoToDeclaration,
11878 window: &mut Window,
11879 cx: &mut Context<Self>,
11880 ) -> Task<Result<Navigated>> {
11881 self.go_to_definition_of_kind(GotoDefinitionKind::Declaration, true, window, cx)
11882 }
11883
11884 pub fn go_to_implementation(
11885 &mut self,
11886 _: &GoToImplementation,
11887 window: &mut Window,
11888 cx: &mut Context<Self>,
11889 ) -> Task<Result<Navigated>> {
11890 self.go_to_definition_of_kind(GotoDefinitionKind::Implementation, false, window, cx)
11891 }
11892
11893 pub fn go_to_implementation_split(
11894 &mut self,
11895 _: &GoToImplementationSplit,
11896 window: &mut Window,
11897 cx: &mut Context<Self>,
11898 ) -> Task<Result<Navigated>> {
11899 self.go_to_definition_of_kind(GotoDefinitionKind::Implementation, true, window, cx)
11900 }
11901
11902 pub fn go_to_type_definition(
11903 &mut self,
11904 _: &GoToTypeDefinition,
11905 window: &mut Window,
11906 cx: &mut Context<Self>,
11907 ) -> Task<Result<Navigated>> {
11908 self.go_to_definition_of_kind(GotoDefinitionKind::Type, false, window, cx)
11909 }
11910
11911 pub fn go_to_definition_split(
11912 &mut self,
11913 _: &GoToDefinitionSplit,
11914 window: &mut Window,
11915 cx: &mut Context<Self>,
11916 ) -> Task<Result<Navigated>> {
11917 self.go_to_definition_of_kind(GotoDefinitionKind::Symbol, true, window, cx)
11918 }
11919
11920 pub fn go_to_type_definition_split(
11921 &mut self,
11922 _: &GoToTypeDefinitionSplit,
11923 window: &mut Window,
11924 cx: &mut Context<Self>,
11925 ) -> Task<Result<Navigated>> {
11926 self.go_to_definition_of_kind(GotoDefinitionKind::Type, true, window, cx)
11927 }
11928
11929 fn go_to_definition_of_kind(
11930 &mut self,
11931 kind: GotoDefinitionKind,
11932 split: bool,
11933 window: &mut Window,
11934 cx: &mut Context<Self>,
11935 ) -> Task<Result<Navigated>> {
11936 let Some(provider) = self.semantics_provider.clone() else {
11937 return Task::ready(Ok(Navigated::No));
11938 };
11939 let head = self.selections.newest::<usize>(cx).head();
11940 let buffer = self.buffer.read(cx);
11941 let (buffer, head) = if let Some(text_anchor) = buffer.text_anchor_for_position(head, cx) {
11942 text_anchor
11943 } else {
11944 return Task::ready(Ok(Navigated::No));
11945 };
11946
11947 let Some(definitions) = provider.definitions(&buffer, head, kind, cx) else {
11948 return Task::ready(Ok(Navigated::No));
11949 };
11950
11951 cx.spawn_in(window, |editor, mut cx| async move {
11952 let definitions = definitions.await?;
11953 let navigated = editor
11954 .update_in(&mut cx, |editor, window, cx| {
11955 editor.navigate_to_hover_links(
11956 Some(kind),
11957 definitions
11958 .into_iter()
11959 .filter(|location| {
11960 hover_links::exclude_link_to_position(&buffer, &head, location, cx)
11961 })
11962 .map(HoverLink::Text)
11963 .collect::<Vec<_>>(),
11964 split,
11965 window,
11966 cx,
11967 )
11968 })?
11969 .await?;
11970 anyhow::Ok(navigated)
11971 })
11972 }
11973
11974 pub fn open_url(&mut self, _: &OpenUrl, window: &mut Window, cx: &mut Context<Self>) {
11975 let selection = self.selections.newest_anchor();
11976 let head = selection.head();
11977 let tail = selection.tail();
11978
11979 let Some((buffer, start_position)) =
11980 self.buffer.read(cx).text_anchor_for_position(head, cx)
11981 else {
11982 return;
11983 };
11984
11985 let end_position = if head != tail {
11986 let Some((_, pos)) = self.buffer.read(cx).text_anchor_for_position(tail, cx) else {
11987 return;
11988 };
11989 Some(pos)
11990 } else {
11991 None
11992 };
11993
11994 let url_finder = cx.spawn_in(window, |editor, mut cx| async move {
11995 let url = if let Some(end_pos) = end_position {
11996 find_url_from_range(&buffer, start_position..end_pos, cx.clone())
11997 } else {
11998 find_url(&buffer, start_position, cx.clone()).map(|(_, url)| url)
11999 };
12000
12001 if let Some(url) = url {
12002 editor.update(&mut cx, |_, cx| {
12003 cx.open_url(&url);
12004 })
12005 } else {
12006 Ok(())
12007 }
12008 });
12009
12010 url_finder.detach();
12011 }
12012
12013 pub fn open_selected_filename(
12014 &mut self,
12015 _: &OpenSelectedFilename,
12016 window: &mut Window,
12017 cx: &mut Context<Self>,
12018 ) {
12019 let Some(workspace) = self.workspace() else {
12020 return;
12021 };
12022
12023 let position = self.selections.newest_anchor().head();
12024
12025 let Some((buffer, buffer_position)) =
12026 self.buffer.read(cx).text_anchor_for_position(position, cx)
12027 else {
12028 return;
12029 };
12030
12031 let project = self.project.clone();
12032
12033 cx.spawn_in(window, |_, mut cx| async move {
12034 let result = find_file(&buffer, project, buffer_position, &mut cx).await;
12035
12036 if let Some((_, path)) = result {
12037 workspace
12038 .update_in(&mut cx, |workspace, window, cx| {
12039 workspace.open_resolved_path(path, window, cx)
12040 })?
12041 .await?;
12042 }
12043 anyhow::Ok(())
12044 })
12045 .detach();
12046 }
12047
12048 pub(crate) fn navigate_to_hover_links(
12049 &mut self,
12050 kind: Option<GotoDefinitionKind>,
12051 mut definitions: Vec<HoverLink>,
12052 split: bool,
12053 window: &mut Window,
12054 cx: &mut Context<Editor>,
12055 ) -> Task<Result<Navigated>> {
12056 // If there is one definition, just open it directly
12057 if definitions.len() == 1 {
12058 let definition = definitions.pop().unwrap();
12059
12060 enum TargetTaskResult {
12061 Location(Option<Location>),
12062 AlreadyNavigated,
12063 }
12064
12065 let target_task = match definition {
12066 HoverLink::Text(link) => {
12067 Task::ready(anyhow::Ok(TargetTaskResult::Location(Some(link.target))))
12068 }
12069 HoverLink::InlayHint(lsp_location, server_id) => {
12070 let computation =
12071 self.compute_target_location(lsp_location, server_id, window, cx);
12072 cx.background_spawn(async move {
12073 let location = computation.await?;
12074 Ok(TargetTaskResult::Location(location))
12075 })
12076 }
12077 HoverLink::Url(url) => {
12078 cx.open_url(&url);
12079 Task::ready(Ok(TargetTaskResult::AlreadyNavigated))
12080 }
12081 HoverLink::File(path) => {
12082 if let Some(workspace) = self.workspace() {
12083 cx.spawn_in(window, |_, mut cx| async move {
12084 workspace
12085 .update_in(&mut cx, |workspace, window, cx| {
12086 workspace.open_resolved_path(path, window, cx)
12087 })?
12088 .await
12089 .map(|_| TargetTaskResult::AlreadyNavigated)
12090 })
12091 } else {
12092 Task::ready(Ok(TargetTaskResult::Location(None)))
12093 }
12094 }
12095 };
12096 cx.spawn_in(window, |editor, mut cx| async move {
12097 let target = match target_task.await.context("target resolution task")? {
12098 TargetTaskResult::AlreadyNavigated => return Ok(Navigated::Yes),
12099 TargetTaskResult::Location(None) => return Ok(Navigated::No),
12100 TargetTaskResult::Location(Some(target)) => target,
12101 };
12102
12103 editor.update_in(&mut cx, |editor, window, cx| {
12104 let Some(workspace) = editor.workspace() else {
12105 return Navigated::No;
12106 };
12107 let pane = workspace.read(cx).active_pane().clone();
12108
12109 let range = target.range.to_point(target.buffer.read(cx));
12110 let range = editor.range_for_match(&range);
12111 let range = collapse_multiline_range(range);
12112
12113 if !split
12114 && Some(&target.buffer) == editor.buffer.read(cx).as_singleton().as_ref()
12115 {
12116 editor.go_to_singleton_buffer_range(range.clone(), window, cx);
12117 } else {
12118 window.defer(cx, move |window, cx| {
12119 let target_editor: Entity<Self> =
12120 workspace.update(cx, |workspace, cx| {
12121 let pane = if split {
12122 workspace.adjacent_pane(window, cx)
12123 } else {
12124 workspace.active_pane().clone()
12125 };
12126
12127 workspace.open_project_item(
12128 pane,
12129 target.buffer.clone(),
12130 true,
12131 true,
12132 window,
12133 cx,
12134 )
12135 });
12136 target_editor.update(cx, |target_editor, cx| {
12137 // When selecting a definition in a different buffer, disable the nav history
12138 // to avoid creating a history entry at the previous cursor location.
12139 pane.update(cx, |pane, _| pane.disable_history());
12140 target_editor.go_to_singleton_buffer_range(range, window, cx);
12141 pane.update(cx, |pane, _| pane.enable_history());
12142 });
12143 });
12144 }
12145 Navigated::Yes
12146 })
12147 })
12148 } else if !definitions.is_empty() {
12149 cx.spawn_in(window, |editor, mut cx| async move {
12150 let (title, location_tasks, workspace) = editor
12151 .update_in(&mut cx, |editor, window, cx| {
12152 let tab_kind = match kind {
12153 Some(GotoDefinitionKind::Implementation) => "Implementations",
12154 _ => "Definitions",
12155 };
12156 let title = definitions
12157 .iter()
12158 .find_map(|definition| match definition {
12159 HoverLink::Text(link) => link.origin.as_ref().map(|origin| {
12160 let buffer = origin.buffer.read(cx);
12161 format!(
12162 "{} for {}",
12163 tab_kind,
12164 buffer
12165 .text_for_range(origin.range.clone())
12166 .collect::<String>()
12167 )
12168 }),
12169 HoverLink::InlayHint(_, _) => None,
12170 HoverLink::Url(_) => None,
12171 HoverLink::File(_) => None,
12172 })
12173 .unwrap_or(tab_kind.to_string());
12174 let location_tasks = definitions
12175 .into_iter()
12176 .map(|definition| match definition {
12177 HoverLink::Text(link) => Task::ready(Ok(Some(link.target))),
12178 HoverLink::InlayHint(lsp_location, server_id) => editor
12179 .compute_target_location(lsp_location, server_id, window, cx),
12180 HoverLink::Url(_) => Task::ready(Ok(None)),
12181 HoverLink::File(_) => Task::ready(Ok(None)),
12182 })
12183 .collect::<Vec<_>>();
12184 (title, location_tasks, editor.workspace().clone())
12185 })
12186 .context("location tasks preparation")?;
12187
12188 let locations = future::join_all(location_tasks)
12189 .await
12190 .into_iter()
12191 .filter_map(|location| location.transpose())
12192 .collect::<Result<_>>()
12193 .context("location tasks")?;
12194
12195 let Some(workspace) = workspace else {
12196 return Ok(Navigated::No);
12197 };
12198 let opened = workspace
12199 .update_in(&mut cx, |workspace, window, cx| {
12200 Self::open_locations_in_multibuffer(
12201 workspace,
12202 locations,
12203 title,
12204 split,
12205 MultibufferSelectionMode::First,
12206 window,
12207 cx,
12208 )
12209 })
12210 .ok();
12211
12212 anyhow::Ok(Navigated::from_bool(opened.is_some()))
12213 })
12214 } else {
12215 Task::ready(Ok(Navigated::No))
12216 }
12217 }
12218
12219 fn compute_target_location(
12220 &self,
12221 lsp_location: lsp::Location,
12222 server_id: LanguageServerId,
12223 window: &mut Window,
12224 cx: &mut Context<Self>,
12225 ) -> Task<anyhow::Result<Option<Location>>> {
12226 let Some(project) = self.project.clone() else {
12227 return Task::ready(Ok(None));
12228 };
12229
12230 cx.spawn_in(window, move |editor, mut cx| async move {
12231 let location_task = editor.update(&mut cx, |_, cx| {
12232 project.update(cx, |project, cx| {
12233 let language_server_name = project
12234 .language_server_statuses(cx)
12235 .find(|(id, _)| server_id == *id)
12236 .map(|(_, status)| LanguageServerName::from(status.name.as_str()));
12237 language_server_name.map(|language_server_name| {
12238 project.open_local_buffer_via_lsp(
12239 lsp_location.uri.clone(),
12240 server_id,
12241 language_server_name,
12242 cx,
12243 )
12244 })
12245 })
12246 })?;
12247 let location = match location_task {
12248 Some(task) => Some({
12249 let target_buffer_handle = task.await.context("open local buffer")?;
12250 let range = target_buffer_handle.update(&mut cx, |target_buffer, _| {
12251 let target_start = target_buffer
12252 .clip_point_utf16(point_from_lsp(lsp_location.range.start), Bias::Left);
12253 let target_end = target_buffer
12254 .clip_point_utf16(point_from_lsp(lsp_location.range.end), Bias::Left);
12255 target_buffer.anchor_after(target_start)
12256 ..target_buffer.anchor_before(target_end)
12257 })?;
12258 Location {
12259 buffer: target_buffer_handle,
12260 range,
12261 }
12262 }),
12263 None => None,
12264 };
12265 Ok(location)
12266 })
12267 }
12268
12269 pub fn find_all_references(
12270 &mut self,
12271 _: &FindAllReferences,
12272 window: &mut Window,
12273 cx: &mut Context<Self>,
12274 ) -> Option<Task<Result<Navigated>>> {
12275 let selection = self.selections.newest::<usize>(cx);
12276 let multi_buffer = self.buffer.read(cx);
12277 let head = selection.head();
12278
12279 let multi_buffer_snapshot = multi_buffer.snapshot(cx);
12280 let head_anchor = multi_buffer_snapshot.anchor_at(
12281 head,
12282 if head < selection.tail() {
12283 Bias::Right
12284 } else {
12285 Bias::Left
12286 },
12287 );
12288
12289 match self
12290 .find_all_references_task_sources
12291 .binary_search_by(|anchor| anchor.cmp(&head_anchor, &multi_buffer_snapshot))
12292 {
12293 Ok(_) => {
12294 log::info!(
12295 "Ignoring repeated FindAllReferences invocation with the position of already running task"
12296 );
12297 return None;
12298 }
12299 Err(i) => {
12300 self.find_all_references_task_sources.insert(i, head_anchor);
12301 }
12302 }
12303
12304 let (buffer, head) = multi_buffer.text_anchor_for_position(head, cx)?;
12305 let workspace = self.workspace()?;
12306 let project = workspace.read(cx).project().clone();
12307 let references = project.update(cx, |project, cx| project.references(&buffer, head, cx));
12308 Some(cx.spawn_in(window, |editor, mut cx| async move {
12309 let _cleanup = defer({
12310 let mut cx = cx.clone();
12311 move || {
12312 let _ = editor.update(&mut cx, |editor, _| {
12313 if let Ok(i) =
12314 editor
12315 .find_all_references_task_sources
12316 .binary_search_by(|anchor| {
12317 anchor.cmp(&head_anchor, &multi_buffer_snapshot)
12318 })
12319 {
12320 editor.find_all_references_task_sources.remove(i);
12321 }
12322 });
12323 }
12324 });
12325
12326 let locations = references.await?;
12327 if locations.is_empty() {
12328 return anyhow::Ok(Navigated::No);
12329 }
12330
12331 workspace.update_in(&mut cx, |workspace, window, cx| {
12332 let title = locations
12333 .first()
12334 .as_ref()
12335 .map(|location| {
12336 let buffer = location.buffer.read(cx);
12337 format!(
12338 "References to `{}`",
12339 buffer
12340 .text_for_range(location.range.clone())
12341 .collect::<String>()
12342 )
12343 })
12344 .unwrap();
12345 Self::open_locations_in_multibuffer(
12346 workspace,
12347 locations,
12348 title,
12349 false,
12350 MultibufferSelectionMode::First,
12351 window,
12352 cx,
12353 );
12354 Navigated::Yes
12355 })
12356 }))
12357 }
12358
12359 /// Opens a multibuffer with the given project locations in it
12360 pub fn open_locations_in_multibuffer(
12361 workspace: &mut Workspace,
12362 mut locations: Vec<Location>,
12363 title: String,
12364 split: bool,
12365 multibuffer_selection_mode: MultibufferSelectionMode,
12366 window: &mut Window,
12367 cx: &mut Context<Workspace>,
12368 ) {
12369 // If there are multiple definitions, open them in a multibuffer
12370 locations.sort_by_key(|location| location.buffer.read(cx).remote_id());
12371 let mut locations = locations.into_iter().peekable();
12372 let mut ranges = Vec::new();
12373 let capability = workspace.project().read(cx).capability();
12374
12375 let excerpt_buffer = cx.new(|cx| {
12376 let mut multibuffer = MultiBuffer::new(capability);
12377 while let Some(location) = locations.next() {
12378 let buffer = location.buffer.read(cx);
12379 let mut ranges_for_buffer = Vec::new();
12380 let range = location.range.to_offset(buffer);
12381 ranges_for_buffer.push(range.clone());
12382
12383 while let Some(next_location) = locations.peek() {
12384 if next_location.buffer == location.buffer {
12385 ranges_for_buffer.push(next_location.range.to_offset(buffer));
12386 locations.next();
12387 } else {
12388 break;
12389 }
12390 }
12391
12392 ranges_for_buffer.sort_by_key(|range| (range.start, Reverse(range.end)));
12393 ranges.extend(multibuffer.push_excerpts_with_context_lines(
12394 location.buffer.clone(),
12395 ranges_for_buffer,
12396 DEFAULT_MULTIBUFFER_CONTEXT,
12397 cx,
12398 ))
12399 }
12400
12401 multibuffer.with_title(title)
12402 });
12403
12404 let editor = cx.new(|cx| {
12405 Editor::for_multibuffer(
12406 excerpt_buffer,
12407 Some(workspace.project().clone()),
12408 window,
12409 cx,
12410 )
12411 });
12412 editor.update(cx, |editor, cx| {
12413 match multibuffer_selection_mode {
12414 MultibufferSelectionMode::First => {
12415 if let Some(first_range) = ranges.first() {
12416 editor.change_selections(None, window, cx, |selections| {
12417 selections.clear_disjoint();
12418 selections.select_anchor_ranges(std::iter::once(first_range.clone()));
12419 });
12420 }
12421 editor.highlight_background::<Self>(
12422 &ranges,
12423 |theme| theme.editor_highlighted_line_background,
12424 cx,
12425 );
12426 }
12427 MultibufferSelectionMode::All => {
12428 editor.change_selections(None, window, cx, |selections| {
12429 selections.clear_disjoint();
12430 selections.select_anchor_ranges(ranges);
12431 });
12432 }
12433 }
12434 editor.register_buffers_with_language_servers(cx);
12435 });
12436
12437 let item = Box::new(editor);
12438 let item_id = item.item_id();
12439
12440 if split {
12441 workspace.split_item(SplitDirection::Right, item.clone(), window, cx);
12442 } else {
12443 let destination_index = workspace.active_pane().update(cx, |pane, cx| {
12444 if PreviewTabsSettings::get_global(cx).enable_preview_from_code_navigation {
12445 pane.close_current_preview_item(window, cx)
12446 } else {
12447 None
12448 }
12449 });
12450 workspace.add_item_to_active_pane(item.clone(), destination_index, true, window, cx);
12451 }
12452 workspace.active_pane().update(cx, |pane, cx| {
12453 pane.set_preview_item_id(Some(item_id), cx);
12454 });
12455 }
12456
12457 pub fn rename(
12458 &mut self,
12459 _: &Rename,
12460 window: &mut Window,
12461 cx: &mut Context<Self>,
12462 ) -> Option<Task<Result<()>>> {
12463 use language::ToOffset as _;
12464
12465 let provider = self.semantics_provider.clone()?;
12466 let selection = self.selections.newest_anchor().clone();
12467 let (cursor_buffer, cursor_buffer_position) = self
12468 .buffer
12469 .read(cx)
12470 .text_anchor_for_position(selection.head(), cx)?;
12471 let (tail_buffer, cursor_buffer_position_end) = self
12472 .buffer
12473 .read(cx)
12474 .text_anchor_for_position(selection.tail(), cx)?;
12475 if tail_buffer != cursor_buffer {
12476 return None;
12477 }
12478
12479 let snapshot = cursor_buffer.read(cx).snapshot();
12480 let cursor_buffer_offset = cursor_buffer_position.to_offset(&snapshot);
12481 let cursor_buffer_offset_end = cursor_buffer_position_end.to_offset(&snapshot);
12482 let prepare_rename = provider
12483 .range_for_rename(&cursor_buffer, cursor_buffer_position, cx)
12484 .unwrap_or_else(|| Task::ready(Ok(None)));
12485 drop(snapshot);
12486
12487 Some(cx.spawn_in(window, |this, mut cx| async move {
12488 let rename_range = if let Some(range) = prepare_rename.await? {
12489 Some(range)
12490 } else {
12491 this.update(&mut cx, |this, cx| {
12492 let buffer = this.buffer.read(cx).snapshot(cx);
12493 let mut buffer_highlights = this
12494 .document_highlights_for_position(selection.head(), &buffer)
12495 .filter(|highlight| {
12496 highlight.start.excerpt_id == selection.head().excerpt_id
12497 && highlight.end.excerpt_id == selection.head().excerpt_id
12498 });
12499 buffer_highlights
12500 .next()
12501 .map(|highlight| highlight.start.text_anchor..highlight.end.text_anchor)
12502 })?
12503 };
12504 if let Some(rename_range) = rename_range {
12505 this.update_in(&mut cx, |this, window, cx| {
12506 let snapshot = cursor_buffer.read(cx).snapshot();
12507 let rename_buffer_range = rename_range.to_offset(&snapshot);
12508 let cursor_offset_in_rename_range =
12509 cursor_buffer_offset.saturating_sub(rename_buffer_range.start);
12510 let cursor_offset_in_rename_range_end =
12511 cursor_buffer_offset_end.saturating_sub(rename_buffer_range.start);
12512
12513 this.take_rename(false, window, cx);
12514 let buffer = this.buffer.read(cx).read(cx);
12515 let cursor_offset = selection.head().to_offset(&buffer);
12516 let rename_start = cursor_offset.saturating_sub(cursor_offset_in_rename_range);
12517 let rename_end = rename_start + rename_buffer_range.len();
12518 let range = buffer.anchor_before(rename_start)..buffer.anchor_after(rename_end);
12519 let mut old_highlight_id = None;
12520 let old_name: Arc<str> = buffer
12521 .chunks(rename_start..rename_end, true)
12522 .map(|chunk| {
12523 if old_highlight_id.is_none() {
12524 old_highlight_id = chunk.syntax_highlight_id;
12525 }
12526 chunk.text
12527 })
12528 .collect::<String>()
12529 .into();
12530
12531 drop(buffer);
12532
12533 // Position the selection in the rename editor so that it matches the current selection.
12534 this.show_local_selections = false;
12535 let rename_editor = cx.new(|cx| {
12536 let mut editor = Editor::single_line(window, cx);
12537 editor.buffer.update(cx, |buffer, cx| {
12538 buffer.edit([(0..0, old_name.clone())], None, cx)
12539 });
12540 let rename_selection_range = match cursor_offset_in_rename_range
12541 .cmp(&cursor_offset_in_rename_range_end)
12542 {
12543 Ordering::Equal => {
12544 editor.select_all(&SelectAll, window, cx);
12545 return editor;
12546 }
12547 Ordering::Less => {
12548 cursor_offset_in_rename_range..cursor_offset_in_rename_range_end
12549 }
12550 Ordering::Greater => {
12551 cursor_offset_in_rename_range_end..cursor_offset_in_rename_range
12552 }
12553 };
12554 if rename_selection_range.end > old_name.len() {
12555 editor.select_all(&SelectAll, window, cx);
12556 } else {
12557 editor.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
12558 s.select_ranges([rename_selection_range]);
12559 });
12560 }
12561 editor
12562 });
12563 cx.subscribe(&rename_editor, |_, _, e: &EditorEvent, cx| {
12564 if e == &EditorEvent::Focused {
12565 cx.emit(EditorEvent::FocusedIn)
12566 }
12567 })
12568 .detach();
12569
12570 let write_highlights =
12571 this.clear_background_highlights::<DocumentHighlightWrite>(cx);
12572 let read_highlights =
12573 this.clear_background_highlights::<DocumentHighlightRead>(cx);
12574 let ranges = write_highlights
12575 .iter()
12576 .flat_map(|(_, ranges)| ranges.iter())
12577 .chain(read_highlights.iter().flat_map(|(_, ranges)| ranges.iter()))
12578 .cloned()
12579 .collect();
12580
12581 this.highlight_text::<Rename>(
12582 ranges,
12583 HighlightStyle {
12584 fade_out: Some(0.6),
12585 ..Default::default()
12586 },
12587 cx,
12588 );
12589 let rename_focus_handle = rename_editor.focus_handle(cx);
12590 window.focus(&rename_focus_handle);
12591 let block_id = this.insert_blocks(
12592 [BlockProperties {
12593 style: BlockStyle::Flex,
12594 placement: BlockPlacement::Below(range.start),
12595 height: 1,
12596 render: Arc::new({
12597 let rename_editor = rename_editor.clone();
12598 move |cx: &mut BlockContext| {
12599 let mut text_style = cx.editor_style.text.clone();
12600 if let Some(highlight_style) = old_highlight_id
12601 .and_then(|h| h.style(&cx.editor_style.syntax))
12602 {
12603 text_style = text_style.highlight(highlight_style);
12604 }
12605 div()
12606 .block_mouse_down()
12607 .pl(cx.anchor_x)
12608 .child(EditorElement::new(
12609 &rename_editor,
12610 EditorStyle {
12611 background: cx.theme().system().transparent,
12612 local_player: cx.editor_style.local_player,
12613 text: text_style,
12614 scrollbar_width: cx.editor_style.scrollbar_width,
12615 syntax: cx.editor_style.syntax.clone(),
12616 status: cx.editor_style.status.clone(),
12617 inlay_hints_style: HighlightStyle {
12618 font_weight: Some(FontWeight::BOLD),
12619 ..make_inlay_hints_style(cx.app)
12620 },
12621 inline_completion_styles: make_suggestion_styles(
12622 cx.app,
12623 ),
12624 ..EditorStyle::default()
12625 },
12626 ))
12627 .into_any_element()
12628 }
12629 }),
12630 priority: 0,
12631 }],
12632 Some(Autoscroll::fit()),
12633 cx,
12634 )[0];
12635 this.pending_rename = Some(RenameState {
12636 range,
12637 old_name,
12638 editor: rename_editor,
12639 block_id,
12640 });
12641 })?;
12642 }
12643
12644 Ok(())
12645 }))
12646 }
12647
12648 pub fn confirm_rename(
12649 &mut self,
12650 _: &ConfirmRename,
12651 window: &mut Window,
12652 cx: &mut Context<Self>,
12653 ) -> Option<Task<Result<()>>> {
12654 let rename = self.take_rename(false, window, cx)?;
12655 let workspace = self.workspace()?.downgrade();
12656 let (buffer, start) = self
12657 .buffer
12658 .read(cx)
12659 .text_anchor_for_position(rename.range.start, cx)?;
12660 let (end_buffer, _) = self
12661 .buffer
12662 .read(cx)
12663 .text_anchor_for_position(rename.range.end, cx)?;
12664 if buffer != end_buffer {
12665 return None;
12666 }
12667
12668 let old_name = rename.old_name;
12669 let new_name = rename.editor.read(cx).text(cx);
12670
12671 let rename = self.semantics_provider.as_ref()?.perform_rename(
12672 &buffer,
12673 start,
12674 new_name.clone(),
12675 cx,
12676 )?;
12677
12678 Some(cx.spawn_in(window, |editor, mut cx| async move {
12679 let project_transaction = rename.await?;
12680 Self::open_project_transaction(
12681 &editor,
12682 workspace,
12683 project_transaction,
12684 format!("Rename: {} → {}", old_name, new_name),
12685 cx.clone(),
12686 )
12687 .await?;
12688
12689 editor.update(&mut cx, |editor, cx| {
12690 editor.refresh_document_highlights(cx);
12691 })?;
12692 Ok(())
12693 }))
12694 }
12695
12696 fn take_rename(
12697 &mut self,
12698 moving_cursor: bool,
12699 window: &mut Window,
12700 cx: &mut Context<Self>,
12701 ) -> Option<RenameState> {
12702 let rename = self.pending_rename.take()?;
12703 if rename.editor.focus_handle(cx).is_focused(window) {
12704 window.focus(&self.focus_handle);
12705 }
12706
12707 self.remove_blocks(
12708 [rename.block_id].into_iter().collect(),
12709 Some(Autoscroll::fit()),
12710 cx,
12711 );
12712 self.clear_highlights::<Rename>(cx);
12713 self.show_local_selections = true;
12714
12715 if moving_cursor {
12716 let cursor_in_rename_editor = rename.editor.update(cx, |editor, cx| {
12717 editor.selections.newest::<usize>(cx).head()
12718 });
12719
12720 // Update the selection to match the position of the selection inside
12721 // the rename editor.
12722 let snapshot = self.buffer.read(cx).read(cx);
12723 let rename_range = rename.range.to_offset(&snapshot);
12724 let cursor_in_editor = snapshot
12725 .clip_offset(rename_range.start + cursor_in_rename_editor, Bias::Left)
12726 .min(rename_range.end);
12727 drop(snapshot);
12728
12729 self.change_selections(None, window, cx, |s| {
12730 s.select_ranges(vec![cursor_in_editor..cursor_in_editor])
12731 });
12732 } else {
12733 self.refresh_document_highlights(cx);
12734 }
12735
12736 Some(rename)
12737 }
12738
12739 pub fn pending_rename(&self) -> Option<&RenameState> {
12740 self.pending_rename.as_ref()
12741 }
12742
12743 fn format(
12744 &mut self,
12745 _: &Format,
12746 window: &mut Window,
12747 cx: &mut Context<Self>,
12748 ) -> Option<Task<Result<()>>> {
12749 let project = match &self.project {
12750 Some(project) => project.clone(),
12751 None => return None,
12752 };
12753
12754 Some(self.perform_format(
12755 project,
12756 FormatTrigger::Manual,
12757 FormatTarget::Buffers,
12758 window,
12759 cx,
12760 ))
12761 }
12762
12763 fn format_selections(
12764 &mut self,
12765 _: &FormatSelections,
12766 window: &mut Window,
12767 cx: &mut Context<Self>,
12768 ) -> Option<Task<Result<()>>> {
12769 let project = match &self.project {
12770 Some(project) => project.clone(),
12771 None => return None,
12772 };
12773
12774 let ranges = self
12775 .selections
12776 .all_adjusted(cx)
12777 .into_iter()
12778 .map(|selection| selection.range())
12779 .collect_vec();
12780
12781 Some(self.perform_format(
12782 project,
12783 FormatTrigger::Manual,
12784 FormatTarget::Ranges(ranges),
12785 window,
12786 cx,
12787 ))
12788 }
12789
12790 fn perform_format(
12791 &mut self,
12792 project: Entity<Project>,
12793 trigger: FormatTrigger,
12794 target: FormatTarget,
12795 window: &mut Window,
12796 cx: &mut Context<Self>,
12797 ) -> Task<Result<()>> {
12798 let buffer = self.buffer.clone();
12799 let (buffers, target) = match target {
12800 FormatTarget::Buffers => {
12801 let mut buffers = buffer.read(cx).all_buffers();
12802 if trigger == FormatTrigger::Save {
12803 buffers.retain(|buffer| buffer.read(cx).is_dirty());
12804 }
12805 (buffers, LspFormatTarget::Buffers)
12806 }
12807 FormatTarget::Ranges(selection_ranges) => {
12808 let multi_buffer = buffer.read(cx);
12809 let snapshot = multi_buffer.read(cx);
12810 let mut buffers = HashSet::default();
12811 let mut buffer_id_to_ranges: BTreeMap<BufferId, Vec<Range<text::Anchor>>> =
12812 BTreeMap::new();
12813 for selection_range in selection_ranges {
12814 for (buffer, buffer_range, _) in
12815 snapshot.range_to_buffer_ranges(selection_range)
12816 {
12817 let buffer_id = buffer.remote_id();
12818 let start = buffer.anchor_before(buffer_range.start);
12819 let end = buffer.anchor_after(buffer_range.end);
12820 buffers.insert(multi_buffer.buffer(buffer_id).unwrap());
12821 buffer_id_to_ranges
12822 .entry(buffer_id)
12823 .and_modify(|buffer_ranges| buffer_ranges.push(start..end))
12824 .or_insert_with(|| vec![start..end]);
12825 }
12826 }
12827 (buffers, LspFormatTarget::Ranges(buffer_id_to_ranges))
12828 }
12829 };
12830
12831 let mut timeout = cx.background_executor().timer(FORMAT_TIMEOUT).fuse();
12832 let format = project.update(cx, |project, cx| {
12833 project.format(buffers, target, true, trigger, cx)
12834 });
12835
12836 cx.spawn_in(window, |_, mut cx| async move {
12837 let transaction = futures::select_biased! {
12838 transaction = format.log_err().fuse() => transaction,
12839 () = timeout => {
12840 log::warn!("timed out waiting for formatting");
12841 None
12842 }
12843 };
12844
12845 buffer
12846 .update(&mut cx, |buffer, cx| {
12847 if let Some(transaction) = transaction {
12848 if !buffer.is_singleton() {
12849 buffer.push_transaction(&transaction.0, cx);
12850 }
12851 }
12852 cx.notify();
12853 })
12854 .ok();
12855
12856 Ok(())
12857 })
12858 }
12859
12860 fn organize_imports(
12861 &mut self,
12862 _: &OrganizeImports,
12863 window: &mut Window,
12864 cx: &mut Context<Self>,
12865 ) -> Option<Task<Result<()>>> {
12866 let project = match &self.project {
12867 Some(project) => project.clone(),
12868 None => return None,
12869 };
12870 Some(self.perform_code_action_kind(
12871 project,
12872 CodeActionKind::SOURCE_ORGANIZE_IMPORTS,
12873 window,
12874 cx,
12875 ))
12876 }
12877
12878 fn perform_code_action_kind(
12879 &mut self,
12880 project: Entity<Project>,
12881 kind: CodeActionKind,
12882 window: &mut Window,
12883 cx: &mut Context<Self>,
12884 ) -> Task<Result<()>> {
12885 let buffer = self.buffer.clone();
12886 let buffers = buffer.read(cx).all_buffers();
12887 let mut timeout = cx.background_executor().timer(CODE_ACTION_TIMEOUT).fuse();
12888 let apply_action = project.update(cx, |project, cx| {
12889 project.apply_code_action_kind(buffers, kind, true, cx)
12890 });
12891 cx.spawn_in(window, |_, mut cx| async move {
12892 let transaction = futures::select_biased! {
12893 () = timeout => {
12894 log::warn!("timed out waiting for executing code action");
12895 None
12896 }
12897 transaction = apply_action.log_err().fuse() => transaction,
12898 };
12899 buffer
12900 .update(&mut cx, |buffer, cx| {
12901 // check if we need this
12902 if let Some(transaction) = transaction {
12903 if !buffer.is_singleton() {
12904 buffer.push_transaction(&transaction.0, cx);
12905 }
12906 }
12907 cx.notify();
12908 })
12909 .ok();
12910 Ok(())
12911 })
12912 }
12913
12914 fn restart_language_server(
12915 &mut self,
12916 _: &RestartLanguageServer,
12917 _: &mut Window,
12918 cx: &mut Context<Self>,
12919 ) {
12920 if let Some(project) = self.project.clone() {
12921 self.buffer.update(cx, |multi_buffer, cx| {
12922 project.update(cx, |project, cx| {
12923 project.restart_language_servers_for_buffers(
12924 multi_buffer.all_buffers().into_iter().collect(),
12925 cx,
12926 );
12927 });
12928 })
12929 }
12930 }
12931
12932 fn cancel_language_server_work(
12933 workspace: &mut Workspace,
12934 _: &actions::CancelLanguageServerWork,
12935 _: &mut Window,
12936 cx: &mut Context<Workspace>,
12937 ) {
12938 let project = workspace.project();
12939 let buffers = workspace
12940 .active_item(cx)
12941 .and_then(|item| item.act_as::<Editor>(cx))
12942 .map_or(HashSet::default(), |editor| {
12943 editor.read(cx).buffer.read(cx).all_buffers()
12944 });
12945 project.update(cx, |project, cx| {
12946 project.cancel_language_server_work_for_buffers(buffers, cx);
12947 });
12948 }
12949
12950 fn show_character_palette(
12951 &mut self,
12952 _: &ShowCharacterPalette,
12953 window: &mut Window,
12954 _: &mut Context<Self>,
12955 ) {
12956 window.show_character_palette();
12957 }
12958
12959 fn refresh_active_diagnostics(&mut self, cx: &mut Context<Editor>) {
12960 if let Some(active_diagnostics) = self.active_diagnostics.as_mut() {
12961 let buffer = self.buffer.read(cx).snapshot(cx);
12962 let primary_range_start = active_diagnostics.primary_range.start.to_offset(&buffer);
12963 let primary_range_end = active_diagnostics.primary_range.end.to_offset(&buffer);
12964 let is_valid = buffer
12965 .diagnostics_in_range::<usize>(primary_range_start..primary_range_end)
12966 .any(|entry| {
12967 entry.diagnostic.is_primary
12968 && !entry.range.is_empty()
12969 && entry.range.start == primary_range_start
12970 && entry.diagnostic.message == active_diagnostics.primary_message
12971 });
12972
12973 if is_valid != active_diagnostics.is_valid {
12974 active_diagnostics.is_valid = is_valid;
12975 if is_valid {
12976 let mut new_styles = HashMap::default();
12977 for (block_id, diagnostic) in &active_diagnostics.blocks {
12978 new_styles.insert(
12979 *block_id,
12980 diagnostic_block_renderer(diagnostic.clone(), None, true),
12981 );
12982 }
12983 self.display_map.update(cx, |display_map, _cx| {
12984 display_map.replace_blocks(new_styles);
12985 });
12986 } else {
12987 self.dismiss_diagnostics(cx);
12988 }
12989 }
12990 }
12991 }
12992
12993 fn activate_diagnostics(
12994 &mut self,
12995 buffer_id: BufferId,
12996 group_id: usize,
12997 window: &mut Window,
12998 cx: &mut Context<Self>,
12999 ) {
13000 self.dismiss_diagnostics(cx);
13001 let snapshot = self.snapshot(window, cx);
13002 self.active_diagnostics = self.display_map.update(cx, |display_map, cx| {
13003 let buffer = self.buffer.read(cx).snapshot(cx);
13004
13005 let mut primary_range = None;
13006 let mut primary_message = None;
13007 let diagnostic_group = buffer
13008 .diagnostic_group(buffer_id, group_id)
13009 .filter_map(|entry| {
13010 let start = entry.range.start;
13011 let end = entry.range.end;
13012 if snapshot.is_line_folded(MultiBufferRow(start.row))
13013 && (start.row == end.row
13014 || snapshot.is_line_folded(MultiBufferRow(end.row)))
13015 {
13016 return None;
13017 }
13018 if entry.diagnostic.is_primary {
13019 primary_range = Some(entry.range.clone());
13020 primary_message = Some(entry.diagnostic.message.clone());
13021 }
13022 Some(entry)
13023 })
13024 .collect::<Vec<_>>();
13025 let primary_range = primary_range?;
13026 let primary_message = primary_message?;
13027
13028 let blocks = display_map
13029 .insert_blocks(
13030 diagnostic_group.iter().map(|entry| {
13031 let diagnostic = entry.diagnostic.clone();
13032 let message_height = diagnostic.message.matches('\n').count() as u32 + 1;
13033 BlockProperties {
13034 style: BlockStyle::Fixed,
13035 placement: BlockPlacement::Below(
13036 buffer.anchor_after(entry.range.start),
13037 ),
13038 height: message_height,
13039 render: diagnostic_block_renderer(diagnostic, None, true),
13040 priority: 0,
13041 }
13042 }),
13043 cx,
13044 )
13045 .into_iter()
13046 .zip(diagnostic_group.into_iter().map(|entry| entry.diagnostic))
13047 .collect();
13048
13049 Some(ActiveDiagnosticGroup {
13050 primary_range: buffer.anchor_before(primary_range.start)
13051 ..buffer.anchor_after(primary_range.end),
13052 primary_message,
13053 group_id,
13054 blocks,
13055 is_valid: true,
13056 })
13057 });
13058 }
13059
13060 fn dismiss_diagnostics(&mut self, cx: &mut Context<Self>) {
13061 if let Some(active_diagnostic_group) = self.active_diagnostics.take() {
13062 self.display_map.update(cx, |display_map, cx| {
13063 display_map.remove_blocks(active_diagnostic_group.blocks.into_keys().collect(), cx);
13064 });
13065 cx.notify();
13066 }
13067 }
13068
13069 /// Disable inline diagnostics rendering for this editor.
13070 pub fn disable_inline_diagnostics(&mut self) {
13071 self.inline_diagnostics_enabled = false;
13072 self.inline_diagnostics_update = Task::ready(());
13073 self.inline_diagnostics.clear();
13074 }
13075
13076 pub fn inline_diagnostics_enabled(&self) -> bool {
13077 self.inline_diagnostics_enabled
13078 }
13079
13080 pub fn show_inline_diagnostics(&self) -> bool {
13081 self.show_inline_diagnostics
13082 }
13083
13084 pub fn toggle_inline_diagnostics(
13085 &mut self,
13086 _: &ToggleInlineDiagnostics,
13087 window: &mut Window,
13088 cx: &mut Context<'_, Editor>,
13089 ) {
13090 self.show_inline_diagnostics = !self.show_inline_diagnostics;
13091 self.refresh_inline_diagnostics(false, window, cx);
13092 }
13093
13094 fn refresh_inline_diagnostics(
13095 &mut self,
13096 debounce: bool,
13097 window: &mut Window,
13098 cx: &mut Context<Self>,
13099 ) {
13100 if !self.inline_diagnostics_enabled || !self.show_inline_diagnostics {
13101 self.inline_diagnostics_update = Task::ready(());
13102 self.inline_diagnostics.clear();
13103 return;
13104 }
13105
13106 let debounce_ms = ProjectSettings::get_global(cx)
13107 .diagnostics
13108 .inline
13109 .update_debounce_ms;
13110 let debounce = if debounce && debounce_ms > 0 {
13111 Some(Duration::from_millis(debounce_ms))
13112 } else {
13113 None
13114 };
13115 self.inline_diagnostics_update = cx.spawn_in(window, |editor, mut cx| async move {
13116 if let Some(debounce) = debounce {
13117 cx.background_executor().timer(debounce).await;
13118 }
13119 let Some(snapshot) = editor
13120 .update(&mut cx, |editor, cx| editor.buffer().read(cx).snapshot(cx))
13121 .ok()
13122 else {
13123 return;
13124 };
13125
13126 let new_inline_diagnostics = cx
13127 .background_spawn(async move {
13128 let mut inline_diagnostics = Vec::<(Anchor, InlineDiagnostic)>::new();
13129 for diagnostic_entry in snapshot.diagnostics_in_range(0..snapshot.len()) {
13130 let message = diagnostic_entry
13131 .diagnostic
13132 .message
13133 .split_once('\n')
13134 .map(|(line, _)| line)
13135 .map(SharedString::new)
13136 .unwrap_or_else(|| {
13137 SharedString::from(diagnostic_entry.diagnostic.message)
13138 });
13139 let start_anchor = snapshot.anchor_before(diagnostic_entry.range.start);
13140 let (Ok(i) | Err(i)) = inline_diagnostics
13141 .binary_search_by(|(probe, _)| probe.cmp(&start_anchor, &snapshot));
13142 inline_diagnostics.insert(
13143 i,
13144 (
13145 start_anchor,
13146 InlineDiagnostic {
13147 message,
13148 group_id: diagnostic_entry.diagnostic.group_id,
13149 start: diagnostic_entry.range.start.to_point(&snapshot),
13150 is_primary: diagnostic_entry.diagnostic.is_primary,
13151 severity: diagnostic_entry.diagnostic.severity,
13152 },
13153 ),
13154 );
13155 }
13156 inline_diagnostics
13157 })
13158 .await;
13159
13160 editor
13161 .update(&mut cx, |editor, cx| {
13162 editor.inline_diagnostics = new_inline_diagnostics;
13163 cx.notify();
13164 })
13165 .ok();
13166 });
13167 }
13168
13169 pub fn set_selections_from_remote(
13170 &mut self,
13171 selections: Vec<Selection<Anchor>>,
13172 pending_selection: Option<Selection<Anchor>>,
13173 window: &mut Window,
13174 cx: &mut Context<Self>,
13175 ) {
13176 let old_cursor_position = self.selections.newest_anchor().head();
13177 self.selections.change_with(cx, |s| {
13178 s.select_anchors(selections);
13179 if let Some(pending_selection) = pending_selection {
13180 s.set_pending(pending_selection, SelectMode::Character);
13181 } else {
13182 s.clear_pending();
13183 }
13184 });
13185 self.selections_did_change(false, &old_cursor_position, true, window, cx);
13186 }
13187
13188 fn push_to_selection_history(&mut self) {
13189 self.selection_history.push(SelectionHistoryEntry {
13190 selections: self.selections.disjoint_anchors(),
13191 select_next_state: self.select_next_state.clone(),
13192 select_prev_state: self.select_prev_state.clone(),
13193 add_selections_state: self.add_selections_state.clone(),
13194 });
13195 }
13196
13197 pub fn transact(
13198 &mut self,
13199 window: &mut Window,
13200 cx: &mut Context<Self>,
13201 update: impl FnOnce(&mut Self, &mut Window, &mut Context<Self>),
13202 ) -> Option<TransactionId> {
13203 self.start_transaction_at(Instant::now(), window, cx);
13204 update(self, window, cx);
13205 self.end_transaction_at(Instant::now(), cx)
13206 }
13207
13208 pub fn start_transaction_at(
13209 &mut self,
13210 now: Instant,
13211 window: &mut Window,
13212 cx: &mut Context<Self>,
13213 ) {
13214 self.end_selection(window, cx);
13215 if let Some(tx_id) = self
13216 .buffer
13217 .update(cx, |buffer, cx| buffer.start_transaction_at(now, cx))
13218 {
13219 self.selection_history
13220 .insert_transaction(tx_id, self.selections.disjoint_anchors());
13221 cx.emit(EditorEvent::TransactionBegun {
13222 transaction_id: tx_id,
13223 })
13224 }
13225 }
13226
13227 pub fn end_transaction_at(
13228 &mut self,
13229 now: Instant,
13230 cx: &mut Context<Self>,
13231 ) -> Option<TransactionId> {
13232 if let Some(transaction_id) = self
13233 .buffer
13234 .update(cx, |buffer, cx| buffer.end_transaction_at(now, cx))
13235 {
13236 if let Some((_, end_selections)) =
13237 self.selection_history.transaction_mut(transaction_id)
13238 {
13239 *end_selections = Some(self.selections.disjoint_anchors());
13240 } else {
13241 log::error!("unexpectedly ended a transaction that wasn't started by this editor");
13242 }
13243
13244 cx.emit(EditorEvent::Edited { transaction_id });
13245 Some(transaction_id)
13246 } else {
13247 None
13248 }
13249 }
13250
13251 pub fn set_mark(&mut self, _: &actions::SetMark, window: &mut Window, cx: &mut Context<Self>) {
13252 if self.selection_mark_mode {
13253 self.change_selections(None, window, cx, |s| {
13254 s.move_with(|_, sel| {
13255 sel.collapse_to(sel.head(), SelectionGoal::None);
13256 });
13257 })
13258 }
13259 self.selection_mark_mode = true;
13260 cx.notify();
13261 }
13262
13263 pub fn swap_selection_ends(
13264 &mut self,
13265 _: &actions::SwapSelectionEnds,
13266 window: &mut Window,
13267 cx: &mut Context<Self>,
13268 ) {
13269 self.change_selections(None, window, cx, |s| {
13270 s.move_with(|_, sel| {
13271 if sel.start != sel.end {
13272 sel.reversed = !sel.reversed
13273 }
13274 });
13275 });
13276 self.request_autoscroll(Autoscroll::newest(), cx);
13277 cx.notify();
13278 }
13279
13280 pub fn toggle_fold(
13281 &mut self,
13282 _: &actions::ToggleFold,
13283 window: &mut Window,
13284 cx: &mut Context<Self>,
13285 ) {
13286 if self.is_singleton(cx) {
13287 let selection = self.selections.newest::<Point>(cx);
13288
13289 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
13290 let range = if selection.is_empty() {
13291 let point = selection.head().to_display_point(&display_map);
13292 let start = DisplayPoint::new(point.row(), 0).to_point(&display_map);
13293 let end = DisplayPoint::new(point.row(), display_map.line_len(point.row()))
13294 .to_point(&display_map);
13295 start..end
13296 } else {
13297 selection.range()
13298 };
13299 if display_map.folds_in_range(range).next().is_some() {
13300 self.unfold_lines(&Default::default(), window, cx)
13301 } else {
13302 self.fold(&Default::default(), window, cx)
13303 }
13304 } else {
13305 let multi_buffer_snapshot = self.buffer.read(cx).snapshot(cx);
13306 let buffer_ids: HashSet<_> = self
13307 .selections
13308 .disjoint_anchor_ranges()
13309 .flat_map(|range| multi_buffer_snapshot.buffer_ids_for_range(range))
13310 .collect();
13311
13312 let should_unfold = buffer_ids
13313 .iter()
13314 .any(|buffer_id| self.is_buffer_folded(*buffer_id, cx));
13315
13316 for buffer_id in buffer_ids {
13317 if should_unfold {
13318 self.unfold_buffer(buffer_id, cx);
13319 } else {
13320 self.fold_buffer(buffer_id, cx);
13321 }
13322 }
13323 }
13324 }
13325
13326 pub fn toggle_fold_recursive(
13327 &mut self,
13328 _: &actions::ToggleFoldRecursive,
13329 window: &mut Window,
13330 cx: &mut Context<Self>,
13331 ) {
13332 let selection = self.selections.newest::<Point>(cx);
13333
13334 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
13335 let range = if selection.is_empty() {
13336 let point = selection.head().to_display_point(&display_map);
13337 let start = DisplayPoint::new(point.row(), 0).to_point(&display_map);
13338 let end = DisplayPoint::new(point.row(), display_map.line_len(point.row()))
13339 .to_point(&display_map);
13340 start..end
13341 } else {
13342 selection.range()
13343 };
13344 if display_map.folds_in_range(range).next().is_some() {
13345 self.unfold_recursive(&Default::default(), window, cx)
13346 } else {
13347 self.fold_recursive(&Default::default(), window, cx)
13348 }
13349 }
13350
13351 pub fn fold(&mut self, _: &actions::Fold, window: &mut Window, cx: &mut Context<Self>) {
13352 if self.is_singleton(cx) {
13353 let mut to_fold = Vec::new();
13354 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
13355 let selections = self.selections.all_adjusted(cx);
13356
13357 for selection in selections {
13358 let range = selection.range().sorted();
13359 let buffer_start_row = range.start.row;
13360
13361 if range.start.row != range.end.row {
13362 let mut found = false;
13363 let mut row = range.start.row;
13364 while row <= range.end.row {
13365 if let Some(crease) = display_map.crease_for_buffer_row(MultiBufferRow(row))
13366 {
13367 found = true;
13368 row = crease.range().end.row + 1;
13369 to_fold.push(crease);
13370 } else {
13371 row += 1
13372 }
13373 }
13374 if found {
13375 continue;
13376 }
13377 }
13378
13379 for row in (0..=range.start.row).rev() {
13380 if let Some(crease) = display_map.crease_for_buffer_row(MultiBufferRow(row)) {
13381 if crease.range().end.row >= buffer_start_row {
13382 to_fold.push(crease);
13383 if row <= range.start.row {
13384 break;
13385 }
13386 }
13387 }
13388 }
13389 }
13390
13391 self.fold_creases(to_fold, true, window, cx);
13392 } else {
13393 let multi_buffer_snapshot = self.buffer.read(cx).snapshot(cx);
13394 let buffer_ids = self
13395 .selections
13396 .disjoint_anchor_ranges()
13397 .flat_map(|range| multi_buffer_snapshot.buffer_ids_for_range(range))
13398 .collect::<HashSet<_>>();
13399 for buffer_id in buffer_ids {
13400 self.fold_buffer(buffer_id, cx);
13401 }
13402 }
13403 }
13404
13405 fn fold_at_level(
13406 &mut self,
13407 fold_at: &FoldAtLevel,
13408 window: &mut Window,
13409 cx: &mut Context<Self>,
13410 ) {
13411 if !self.buffer.read(cx).is_singleton() {
13412 return;
13413 }
13414
13415 let fold_at_level = fold_at.0;
13416 let snapshot = self.buffer.read(cx).snapshot(cx);
13417 let mut to_fold = Vec::new();
13418 let mut stack = vec![(0, snapshot.max_row().0, 1)];
13419
13420 while let Some((mut start_row, end_row, current_level)) = stack.pop() {
13421 while start_row < end_row {
13422 match self
13423 .snapshot(window, cx)
13424 .crease_for_buffer_row(MultiBufferRow(start_row))
13425 {
13426 Some(crease) => {
13427 let nested_start_row = crease.range().start.row + 1;
13428 let nested_end_row = crease.range().end.row;
13429
13430 if current_level < fold_at_level {
13431 stack.push((nested_start_row, nested_end_row, current_level + 1));
13432 } else if current_level == fold_at_level {
13433 to_fold.push(crease);
13434 }
13435
13436 start_row = nested_end_row + 1;
13437 }
13438 None => start_row += 1,
13439 }
13440 }
13441 }
13442
13443 self.fold_creases(to_fold, true, window, cx);
13444 }
13445
13446 pub fn fold_all(&mut self, _: &actions::FoldAll, window: &mut Window, cx: &mut Context<Self>) {
13447 if self.buffer.read(cx).is_singleton() {
13448 let mut fold_ranges = Vec::new();
13449 let snapshot = self.buffer.read(cx).snapshot(cx);
13450
13451 for row in 0..snapshot.max_row().0 {
13452 if let Some(foldable_range) = self
13453 .snapshot(window, cx)
13454 .crease_for_buffer_row(MultiBufferRow(row))
13455 {
13456 fold_ranges.push(foldable_range);
13457 }
13458 }
13459
13460 self.fold_creases(fold_ranges, true, window, cx);
13461 } else {
13462 self.toggle_fold_multiple_buffers = cx.spawn_in(window, |editor, mut cx| async move {
13463 editor
13464 .update_in(&mut cx, |editor, _, cx| {
13465 for buffer_id in editor.buffer.read(cx).excerpt_buffer_ids() {
13466 editor.fold_buffer(buffer_id, cx);
13467 }
13468 })
13469 .ok();
13470 });
13471 }
13472 }
13473
13474 pub fn fold_function_bodies(
13475 &mut self,
13476 _: &actions::FoldFunctionBodies,
13477 window: &mut Window,
13478 cx: &mut Context<Self>,
13479 ) {
13480 let snapshot = self.buffer.read(cx).snapshot(cx);
13481
13482 let ranges = snapshot
13483 .text_object_ranges(0..snapshot.len(), TreeSitterOptions::default())
13484 .filter_map(|(range, obj)| (obj == TextObject::InsideFunction).then_some(range))
13485 .collect::<Vec<_>>();
13486
13487 let creases = ranges
13488 .into_iter()
13489 .map(|range| Crease::simple(range, self.display_map.read(cx).fold_placeholder.clone()))
13490 .collect();
13491
13492 self.fold_creases(creases, true, window, cx);
13493 }
13494
13495 pub fn fold_recursive(
13496 &mut self,
13497 _: &actions::FoldRecursive,
13498 window: &mut Window,
13499 cx: &mut Context<Self>,
13500 ) {
13501 let mut to_fold = Vec::new();
13502 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
13503 let selections = self.selections.all_adjusted(cx);
13504
13505 for selection in selections {
13506 let range = selection.range().sorted();
13507 let buffer_start_row = range.start.row;
13508
13509 if range.start.row != range.end.row {
13510 let mut found = false;
13511 for row in range.start.row..=range.end.row {
13512 if let Some(crease) = display_map.crease_for_buffer_row(MultiBufferRow(row)) {
13513 found = true;
13514 to_fold.push(crease);
13515 }
13516 }
13517 if found {
13518 continue;
13519 }
13520 }
13521
13522 for row in (0..=range.start.row).rev() {
13523 if let Some(crease) = display_map.crease_for_buffer_row(MultiBufferRow(row)) {
13524 if crease.range().end.row >= buffer_start_row {
13525 to_fold.push(crease);
13526 } else {
13527 break;
13528 }
13529 }
13530 }
13531 }
13532
13533 self.fold_creases(to_fold, true, window, cx);
13534 }
13535
13536 pub fn fold_at(&mut self, fold_at: &FoldAt, window: &mut Window, cx: &mut Context<Self>) {
13537 let buffer_row = fold_at.buffer_row;
13538 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
13539
13540 if let Some(crease) = display_map.crease_for_buffer_row(buffer_row) {
13541 let autoscroll = self
13542 .selections
13543 .all::<Point>(cx)
13544 .iter()
13545 .any(|selection| crease.range().overlaps(&selection.range()));
13546
13547 self.fold_creases(vec![crease], autoscroll, window, cx);
13548 }
13549 }
13550
13551 pub fn unfold_lines(&mut self, _: &UnfoldLines, _window: &mut Window, cx: &mut Context<Self>) {
13552 if self.is_singleton(cx) {
13553 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
13554 let buffer = &display_map.buffer_snapshot;
13555 let selections = self.selections.all::<Point>(cx);
13556 let ranges = selections
13557 .iter()
13558 .map(|s| {
13559 let range = s.display_range(&display_map).sorted();
13560 let mut start = range.start.to_point(&display_map);
13561 let mut end = range.end.to_point(&display_map);
13562 start.column = 0;
13563 end.column = buffer.line_len(MultiBufferRow(end.row));
13564 start..end
13565 })
13566 .collect::<Vec<_>>();
13567
13568 self.unfold_ranges(&ranges, true, true, cx);
13569 } else {
13570 let multi_buffer_snapshot = self.buffer.read(cx).snapshot(cx);
13571 let buffer_ids = self
13572 .selections
13573 .disjoint_anchor_ranges()
13574 .flat_map(|range| multi_buffer_snapshot.buffer_ids_for_range(range))
13575 .collect::<HashSet<_>>();
13576 for buffer_id in buffer_ids {
13577 self.unfold_buffer(buffer_id, cx);
13578 }
13579 }
13580 }
13581
13582 pub fn unfold_recursive(
13583 &mut self,
13584 _: &UnfoldRecursive,
13585 _window: &mut Window,
13586 cx: &mut Context<Self>,
13587 ) {
13588 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
13589 let selections = self.selections.all::<Point>(cx);
13590 let ranges = selections
13591 .iter()
13592 .map(|s| {
13593 let mut range = s.display_range(&display_map).sorted();
13594 *range.start.column_mut() = 0;
13595 *range.end.column_mut() = display_map.line_len(range.end.row());
13596 let start = range.start.to_point(&display_map);
13597 let end = range.end.to_point(&display_map);
13598 start..end
13599 })
13600 .collect::<Vec<_>>();
13601
13602 self.unfold_ranges(&ranges, true, true, cx);
13603 }
13604
13605 pub fn unfold_at(
13606 &mut self,
13607 unfold_at: &UnfoldAt,
13608 _window: &mut Window,
13609 cx: &mut Context<Self>,
13610 ) {
13611 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
13612
13613 let intersection_range = Point::new(unfold_at.buffer_row.0, 0)
13614 ..Point::new(
13615 unfold_at.buffer_row.0,
13616 display_map.buffer_snapshot.line_len(unfold_at.buffer_row),
13617 );
13618
13619 let autoscroll = self
13620 .selections
13621 .all::<Point>(cx)
13622 .iter()
13623 .any(|selection| RangeExt::overlaps(&selection.range(), &intersection_range));
13624
13625 self.unfold_ranges(&[intersection_range], true, autoscroll, cx);
13626 }
13627
13628 pub fn unfold_all(
13629 &mut self,
13630 _: &actions::UnfoldAll,
13631 _window: &mut Window,
13632 cx: &mut Context<Self>,
13633 ) {
13634 if self.buffer.read(cx).is_singleton() {
13635 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
13636 self.unfold_ranges(&[0..display_map.buffer_snapshot.len()], true, true, cx);
13637 } else {
13638 self.toggle_fold_multiple_buffers = cx.spawn(|editor, mut cx| async move {
13639 editor
13640 .update(&mut cx, |editor, cx| {
13641 for buffer_id in editor.buffer.read(cx).excerpt_buffer_ids() {
13642 editor.unfold_buffer(buffer_id, cx);
13643 }
13644 })
13645 .ok();
13646 });
13647 }
13648 }
13649
13650 pub fn fold_selected_ranges(
13651 &mut self,
13652 _: &FoldSelectedRanges,
13653 window: &mut Window,
13654 cx: &mut Context<Self>,
13655 ) {
13656 let selections = self.selections.all::<Point>(cx);
13657 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
13658 let line_mode = self.selections.line_mode;
13659 let ranges = selections
13660 .into_iter()
13661 .map(|s| {
13662 if line_mode {
13663 let start = Point::new(s.start.row, 0);
13664 let end = Point::new(
13665 s.end.row,
13666 display_map
13667 .buffer_snapshot
13668 .line_len(MultiBufferRow(s.end.row)),
13669 );
13670 Crease::simple(start..end, display_map.fold_placeholder.clone())
13671 } else {
13672 Crease::simple(s.start..s.end, display_map.fold_placeholder.clone())
13673 }
13674 })
13675 .collect::<Vec<_>>();
13676 self.fold_creases(ranges, true, window, cx);
13677 }
13678
13679 pub fn fold_ranges<T: ToOffset + Clone>(
13680 &mut self,
13681 ranges: Vec<Range<T>>,
13682 auto_scroll: bool,
13683 window: &mut Window,
13684 cx: &mut Context<Self>,
13685 ) {
13686 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
13687 let ranges = ranges
13688 .into_iter()
13689 .map(|r| Crease::simple(r, display_map.fold_placeholder.clone()))
13690 .collect::<Vec<_>>();
13691 self.fold_creases(ranges, auto_scroll, window, cx);
13692 }
13693
13694 pub fn fold_creases<T: ToOffset + Clone>(
13695 &mut self,
13696 creases: Vec<Crease<T>>,
13697 auto_scroll: bool,
13698 window: &mut Window,
13699 cx: &mut Context<Self>,
13700 ) {
13701 if creases.is_empty() {
13702 return;
13703 }
13704
13705 let mut buffers_affected = HashSet::default();
13706 let multi_buffer = self.buffer().read(cx);
13707 for crease in &creases {
13708 if let Some((_, buffer, _)) =
13709 multi_buffer.excerpt_containing(crease.range().start.clone(), cx)
13710 {
13711 buffers_affected.insert(buffer.read(cx).remote_id());
13712 };
13713 }
13714
13715 self.display_map.update(cx, |map, cx| map.fold(creases, cx));
13716
13717 if auto_scroll {
13718 self.request_autoscroll(Autoscroll::fit(), cx);
13719 }
13720
13721 cx.notify();
13722
13723 if let Some(active_diagnostics) = self.active_diagnostics.take() {
13724 // Clear diagnostics block when folding a range that contains it.
13725 let snapshot = self.snapshot(window, cx);
13726 if snapshot.intersects_fold(active_diagnostics.primary_range.start) {
13727 drop(snapshot);
13728 self.active_diagnostics = Some(active_diagnostics);
13729 self.dismiss_diagnostics(cx);
13730 } else {
13731 self.active_diagnostics = Some(active_diagnostics);
13732 }
13733 }
13734
13735 self.scrollbar_marker_state.dirty = true;
13736 }
13737
13738 /// Removes any folds whose ranges intersect any of the given ranges.
13739 pub fn unfold_ranges<T: ToOffset + Clone>(
13740 &mut self,
13741 ranges: &[Range<T>],
13742 inclusive: bool,
13743 auto_scroll: bool,
13744 cx: &mut Context<Self>,
13745 ) {
13746 self.remove_folds_with(ranges, auto_scroll, cx, |map, cx| {
13747 map.unfold_intersecting(ranges.iter().cloned(), inclusive, cx)
13748 });
13749 }
13750
13751 pub fn fold_buffer(&mut self, buffer_id: BufferId, cx: &mut Context<Self>) {
13752 if self.buffer().read(cx).is_singleton() || self.is_buffer_folded(buffer_id, cx) {
13753 return;
13754 }
13755 let folded_excerpts = self.buffer().read(cx).excerpts_for_buffer(buffer_id, cx);
13756 self.display_map.update(cx, |display_map, cx| {
13757 display_map.fold_buffers([buffer_id], cx)
13758 });
13759 cx.emit(EditorEvent::BufferFoldToggled {
13760 ids: folded_excerpts.iter().map(|&(id, _)| id).collect(),
13761 folded: true,
13762 });
13763 cx.notify();
13764 }
13765
13766 pub fn unfold_buffer(&mut self, buffer_id: BufferId, cx: &mut Context<Self>) {
13767 if self.buffer().read(cx).is_singleton() || !self.is_buffer_folded(buffer_id, cx) {
13768 return;
13769 }
13770 let unfolded_excerpts = self.buffer().read(cx).excerpts_for_buffer(buffer_id, cx);
13771 self.display_map.update(cx, |display_map, cx| {
13772 display_map.unfold_buffers([buffer_id], cx);
13773 });
13774 cx.emit(EditorEvent::BufferFoldToggled {
13775 ids: unfolded_excerpts.iter().map(|&(id, _)| id).collect(),
13776 folded: false,
13777 });
13778 cx.notify();
13779 }
13780
13781 pub fn is_buffer_folded(&self, buffer: BufferId, cx: &App) -> bool {
13782 self.display_map.read(cx).is_buffer_folded(buffer)
13783 }
13784
13785 pub fn folded_buffers<'a>(&self, cx: &'a App) -> &'a HashSet<BufferId> {
13786 self.display_map.read(cx).folded_buffers()
13787 }
13788
13789 /// Removes any folds with the given ranges.
13790 pub fn remove_folds_with_type<T: ToOffset + Clone>(
13791 &mut self,
13792 ranges: &[Range<T>],
13793 type_id: TypeId,
13794 auto_scroll: bool,
13795 cx: &mut Context<Self>,
13796 ) {
13797 self.remove_folds_with(ranges, auto_scroll, cx, |map, cx| {
13798 map.remove_folds_with_type(ranges.iter().cloned(), type_id, cx)
13799 });
13800 }
13801
13802 fn remove_folds_with<T: ToOffset + Clone>(
13803 &mut self,
13804 ranges: &[Range<T>],
13805 auto_scroll: bool,
13806 cx: &mut Context<Self>,
13807 update: impl FnOnce(&mut DisplayMap, &mut Context<DisplayMap>),
13808 ) {
13809 if ranges.is_empty() {
13810 return;
13811 }
13812
13813 let mut buffers_affected = HashSet::default();
13814 let multi_buffer = self.buffer().read(cx);
13815 for range in ranges {
13816 if let Some((_, buffer, _)) = multi_buffer.excerpt_containing(range.start.clone(), cx) {
13817 buffers_affected.insert(buffer.read(cx).remote_id());
13818 };
13819 }
13820
13821 self.display_map.update(cx, update);
13822
13823 if auto_scroll {
13824 self.request_autoscroll(Autoscroll::fit(), cx);
13825 }
13826
13827 cx.notify();
13828 self.scrollbar_marker_state.dirty = true;
13829 self.active_indent_guides_state.dirty = true;
13830 }
13831
13832 pub fn default_fold_placeholder(&self, cx: &App) -> FoldPlaceholder {
13833 self.display_map.read(cx).fold_placeholder.clone()
13834 }
13835
13836 pub fn set_expand_all_diff_hunks(&mut self, cx: &mut App) {
13837 self.buffer.update(cx, |buffer, cx| {
13838 buffer.set_all_diff_hunks_expanded(cx);
13839 });
13840 }
13841
13842 pub fn expand_all_diff_hunks(
13843 &mut self,
13844 _: &ExpandAllDiffHunks,
13845 _window: &mut Window,
13846 cx: &mut Context<Self>,
13847 ) {
13848 self.buffer.update(cx, |buffer, cx| {
13849 buffer.expand_diff_hunks(vec![Anchor::min()..Anchor::max()], cx)
13850 });
13851 }
13852
13853 pub fn toggle_selected_diff_hunks(
13854 &mut self,
13855 _: &ToggleSelectedDiffHunks,
13856 _window: &mut Window,
13857 cx: &mut Context<Self>,
13858 ) {
13859 let ranges: Vec<_> = self.selections.disjoint.iter().map(|s| s.range()).collect();
13860 self.toggle_diff_hunks_in_ranges(ranges, cx);
13861 }
13862
13863 pub fn diff_hunks_in_ranges<'a>(
13864 &'a self,
13865 ranges: &'a [Range<Anchor>],
13866 buffer: &'a MultiBufferSnapshot,
13867 ) -> impl 'a + Iterator<Item = MultiBufferDiffHunk> {
13868 ranges.iter().flat_map(move |range| {
13869 let end_excerpt_id = range.end.excerpt_id;
13870 let range = range.to_point(buffer);
13871 let mut peek_end = range.end;
13872 if range.end.row < buffer.max_row().0 {
13873 peek_end = Point::new(range.end.row + 1, 0);
13874 }
13875 buffer
13876 .diff_hunks_in_range(range.start..peek_end)
13877 .filter(move |hunk| hunk.excerpt_id.cmp(&end_excerpt_id, buffer).is_le())
13878 })
13879 }
13880
13881 pub fn has_stageable_diff_hunks_in_ranges(
13882 &self,
13883 ranges: &[Range<Anchor>],
13884 snapshot: &MultiBufferSnapshot,
13885 ) -> bool {
13886 let mut hunks = self.diff_hunks_in_ranges(ranges, &snapshot);
13887 hunks.any(|hunk| hunk.status().has_secondary_hunk())
13888 }
13889
13890 pub fn toggle_staged_selected_diff_hunks(
13891 &mut self,
13892 _: &::git::ToggleStaged,
13893 _: &mut Window,
13894 cx: &mut Context<Self>,
13895 ) {
13896 let snapshot = self.buffer.read(cx).snapshot(cx);
13897 let ranges: Vec<_> = self.selections.disjoint.iter().map(|s| s.range()).collect();
13898 let stage = self.has_stageable_diff_hunks_in_ranges(&ranges, &snapshot);
13899 self.stage_or_unstage_diff_hunks(stage, ranges, cx);
13900 }
13901
13902 pub fn stage_and_next(
13903 &mut self,
13904 _: &::git::StageAndNext,
13905 window: &mut Window,
13906 cx: &mut Context<Self>,
13907 ) {
13908 self.do_stage_or_unstage_and_next(true, window, cx);
13909 }
13910
13911 pub fn unstage_and_next(
13912 &mut self,
13913 _: &::git::UnstageAndNext,
13914 window: &mut Window,
13915 cx: &mut Context<Self>,
13916 ) {
13917 self.do_stage_or_unstage_and_next(false, window, cx);
13918 }
13919
13920 pub fn stage_or_unstage_diff_hunks(
13921 &mut self,
13922 stage: bool,
13923 ranges: Vec<Range<Anchor>>,
13924 cx: &mut Context<Self>,
13925 ) {
13926 let task = self.save_buffers_for_ranges_if_needed(&ranges, cx);
13927 cx.spawn(|this, mut cx| async move {
13928 task.await?;
13929 this.update(&mut cx, |this, cx| {
13930 let snapshot = this.buffer.read(cx).snapshot(cx);
13931 let chunk_by = this
13932 .diff_hunks_in_ranges(&ranges, &snapshot)
13933 .chunk_by(|hunk| hunk.buffer_id);
13934 for (buffer_id, hunks) in &chunk_by {
13935 this.do_stage_or_unstage(stage, buffer_id, hunks, cx);
13936 }
13937 })
13938 })
13939 .detach_and_log_err(cx);
13940 }
13941
13942 fn save_buffers_for_ranges_if_needed(
13943 &mut self,
13944 ranges: &[Range<Anchor>],
13945 cx: &mut Context<'_, Editor>,
13946 ) -> Task<Result<()>> {
13947 let multibuffer = self.buffer.read(cx);
13948 let snapshot = multibuffer.read(cx);
13949 let buffer_ids: HashSet<_> = ranges
13950 .iter()
13951 .flat_map(|range| snapshot.buffer_ids_for_range(range.clone()))
13952 .collect();
13953 drop(snapshot);
13954
13955 let mut buffers = HashSet::default();
13956 for buffer_id in buffer_ids {
13957 if let Some(buffer_entity) = multibuffer.buffer(buffer_id) {
13958 let buffer = buffer_entity.read(cx);
13959 if buffer.file().is_some_and(|file| file.disk_state().exists()) && buffer.is_dirty()
13960 {
13961 buffers.insert(buffer_entity);
13962 }
13963 }
13964 }
13965
13966 if let Some(project) = &self.project {
13967 project.update(cx, |project, cx| project.save_buffers(buffers, cx))
13968 } else {
13969 Task::ready(Ok(()))
13970 }
13971 }
13972
13973 fn do_stage_or_unstage_and_next(
13974 &mut self,
13975 stage: bool,
13976 window: &mut Window,
13977 cx: &mut Context<Self>,
13978 ) {
13979 let ranges = self.selections.disjoint_anchor_ranges().collect::<Vec<_>>();
13980
13981 if ranges.iter().any(|range| range.start != range.end) {
13982 self.stage_or_unstage_diff_hunks(stage, ranges, cx);
13983 return;
13984 }
13985
13986 self.stage_or_unstage_diff_hunks(stage, ranges, cx);
13987 let snapshot = self.snapshot(window, cx);
13988 let position = self.selections.newest::<Point>(cx).head();
13989 let mut row = snapshot
13990 .buffer_snapshot
13991 .diff_hunks_in_range(position..snapshot.buffer_snapshot.max_point())
13992 .find(|hunk| hunk.row_range.start.0 > position.row)
13993 .map(|hunk| hunk.row_range.start);
13994
13995 let all_diff_hunks_expanded = self.buffer().read(cx).all_diff_hunks_expanded();
13996 // Outside of the project diff editor, wrap around to the beginning.
13997 if !all_diff_hunks_expanded {
13998 row = row.or_else(|| {
13999 snapshot
14000 .buffer_snapshot
14001 .diff_hunks_in_range(Point::zero()..position)
14002 .find(|hunk| hunk.row_range.end.0 < position.row)
14003 .map(|hunk| hunk.row_range.start)
14004 });
14005 }
14006
14007 if let Some(row) = row {
14008 let destination = Point::new(row.0, 0);
14009 let autoscroll = Autoscroll::center();
14010
14011 self.unfold_ranges(&[destination..destination], false, false, cx);
14012 self.change_selections(Some(autoscroll), window, cx, |s| {
14013 s.select_ranges([destination..destination]);
14014 });
14015 } else if all_diff_hunks_expanded {
14016 window.dispatch_action(::git::ExpandCommitEditor.boxed_clone(), cx);
14017 }
14018 }
14019
14020 fn do_stage_or_unstage(
14021 &self,
14022 stage: bool,
14023 buffer_id: BufferId,
14024 hunks: impl Iterator<Item = MultiBufferDiffHunk>,
14025 cx: &mut App,
14026 ) -> Option<()> {
14027 let project = self.project.as_ref()?;
14028 let buffer = project.read(cx).buffer_for_id(buffer_id, cx)?;
14029 let diff = self.buffer.read(cx).diff_for(buffer_id)?;
14030 let buffer_snapshot = buffer.read(cx).snapshot();
14031 let file_exists = buffer_snapshot
14032 .file()
14033 .is_some_and(|file| file.disk_state().exists());
14034 diff.update(cx, |diff, cx| {
14035 diff.stage_or_unstage_hunks(
14036 stage,
14037 &hunks
14038 .map(|hunk| buffer_diff::DiffHunk {
14039 buffer_range: hunk.buffer_range,
14040 diff_base_byte_range: hunk.diff_base_byte_range,
14041 secondary_status: hunk.secondary_status,
14042 range: Point::zero()..Point::zero(), // unused
14043 })
14044 .collect::<Vec<_>>(),
14045 &buffer_snapshot,
14046 file_exists,
14047 cx,
14048 )
14049 });
14050 None
14051 }
14052
14053 pub fn expand_selected_diff_hunks(&mut self, cx: &mut Context<Self>) {
14054 let ranges: Vec<_> = self.selections.disjoint.iter().map(|s| s.range()).collect();
14055 self.buffer
14056 .update(cx, |buffer, cx| buffer.expand_diff_hunks(ranges, cx))
14057 }
14058
14059 pub fn clear_expanded_diff_hunks(&mut self, cx: &mut Context<Self>) -> bool {
14060 self.buffer.update(cx, |buffer, cx| {
14061 let ranges = vec![Anchor::min()..Anchor::max()];
14062 if !buffer.all_diff_hunks_expanded()
14063 && buffer.has_expanded_diff_hunks_in_ranges(&ranges, cx)
14064 {
14065 buffer.collapse_diff_hunks(ranges, cx);
14066 true
14067 } else {
14068 false
14069 }
14070 })
14071 }
14072
14073 fn toggle_diff_hunks_in_ranges(
14074 &mut self,
14075 ranges: Vec<Range<Anchor>>,
14076 cx: &mut Context<'_, Editor>,
14077 ) {
14078 self.buffer.update(cx, |buffer, cx| {
14079 let expand = !buffer.has_expanded_diff_hunks_in_ranges(&ranges, cx);
14080 buffer.expand_or_collapse_diff_hunks(ranges, expand, cx);
14081 })
14082 }
14083
14084 fn toggle_single_diff_hunk(&mut self, range: Range<Anchor>, cx: &mut Context<Self>) {
14085 self.buffer.update(cx, |buffer, cx| {
14086 let snapshot = buffer.snapshot(cx);
14087 let excerpt_id = range.end.excerpt_id;
14088 let point_range = range.to_point(&snapshot);
14089 let expand = !buffer.single_hunk_is_expanded(range, cx);
14090 buffer.expand_or_collapse_diff_hunks_inner([(point_range, excerpt_id)], expand, cx);
14091 })
14092 }
14093
14094 pub(crate) fn apply_all_diff_hunks(
14095 &mut self,
14096 _: &ApplyAllDiffHunks,
14097 window: &mut Window,
14098 cx: &mut Context<Self>,
14099 ) {
14100 let buffers = self.buffer.read(cx).all_buffers();
14101 for branch_buffer in buffers {
14102 branch_buffer.update(cx, |branch_buffer, cx| {
14103 branch_buffer.merge_into_base(Vec::new(), cx);
14104 });
14105 }
14106
14107 if let Some(project) = self.project.clone() {
14108 self.save(true, project, window, cx).detach_and_log_err(cx);
14109 }
14110 }
14111
14112 pub(crate) fn apply_selected_diff_hunks(
14113 &mut self,
14114 _: &ApplyDiffHunk,
14115 window: &mut Window,
14116 cx: &mut Context<Self>,
14117 ) {
14118 let snapshot = self.snapshot(window, cx);
14119 let hunks = snapshot.hunks_for_ranges(self.selections.ranges(cx));
14120 let mut ranges_by_buffer = HashMap::default();
14121 self.transact(window, cx, |editor, _window, cx| {
14122 for hunk in hunks {
14123 if let Some(buffer) = editor.buffer.read(cx).buffer(hunk.buffer_id) {
14124 ranges_by_buffer
14125 .entry(buffer.clone())
14126 .or_insert_with(Vec::new)
14127 .push(hunk.buffer_range.to_offset(buffer.read(cx)));
14128 }
14129 }
14130
14131 for (buffer, ranges) in ranges_by_buffer {
14132 buffer.update(cx, |buffer, cx| {
14133 buffer.merge_into_base(ranges, cx);
14134 });
14135 }
14136 });
14137
14138 if let Some(project) = self.project.clone() {
14139 self.save(true, project, window, cx).detach_and_log_err(cx);
14140 }
14141 }
14142
14143 pub fn set_gutter_hovered(&mut self, hovered: bool, cx: &mut Context<Self>) {
14144 if hovered != self.gutter_hovered {
14145 self.gutter_hovered = hovered;
14146 cx.notify();
14147 }
14148 }
14149
14150 pub fn insert_blocks(
14151 &mut self,
14152 blocks: impl IntoIterator<Item = BlockProperties<Anchor>>,
14153 autoscroll: Option<Autoscroll>,
14154 cx: &mut Context<Self>,
14155 ) -> Vec<CustomBlockId> {
14156 let blocks = self
14157 .display_map
14158 .update(cx, |display_map, cx| display_map.insert_blocks(blocks, cx));
14159 if let Some(autoscroll) = autoscroll {
14160 self.request_autoscroll(autoscroll, cx);
14161 }
14162 cx.notify();
14163 blocks
14164 }
14165
14166 pub fn resize_blocks(
14167 &mut self,
14168 heights: HashMap<CustomBlockId, u32>,
14169 autoscroll: Option<Autoscroll>,
14170 cx: &mut Context<Self>,
14171 ) {
14172 self.display_map
14173 .update(cx, |display_map, cx| display_map.resize_blocks(heights, cx));
14174 if let Some(autoscroll) = autoscroll {
14175 self.request_autoscroll(autoscroll, cx);
14176 }
14177 cx.notify();
14178 }
14179
14180 pub fn replace_blocks(
14181 &mut self,
14182 renderers: HashMap<CustomBlockId, RenderBlock>,
14183 autoscroll: Option<Autoscroll>,
14184 cx: &mut Context<Self>,
14185 ) {
14186 self.display_map
14187 .update(cx, |display_map, _cx| display_map.replace_blocks(renderers));
14188 if let Some(autoscroll) = autoscroll {
14189 self.request_autoscroll(autoscroll, cx);
14190 }
14191 cx.notify();
14192 }
14193
14194 pub fn remove_blocks(
14195 &mut self,
14196 block_ids: HashSet<CustomBlockId>,
14197 autoscroll: Option<Autoscroll>,
14198 cx: &mut Context<Self>,
14199 ) {
14200 self.display_map.update(cx, |display_map, cx| {
14201 display_map.remove_blocks(block_ids, cx)
14202 });
14203 if let Some(autoscroll) = autoscroll {
14204 self.request_autoscroll(autoscroll, cx);
14205 }
14206 cx.notify();
14207 }
14208
14209 pub fn row_for_block(
14210 &self,
14211 block_id: CustomBlockId,
14212 cx: &mut Context<Self>,
14213 ) -> Option<DisplayRow> {
14214 self.display_map
14215 .update(cx, |map, cx| map.row_for_block(block_id, cx))
14216 }
14217
14218 pub(crate) fn set_focused_block(&mut self, focused_block: FocusedBlock) {
14219 self.focused_block = Some(focused_block);
14220 }
14221
14222 pub(crate) fn take_focused_block(&mut self) -> Option<FocusedBlock> {
14223 self.focused_block.take()
14224 }
14225
14226 pub fn insert_creases(
14227 &mut self,
14228 creases: impl IntoIterator<Item = Crease<Anchor>>,
14229 cx: &mut Context<Self>,
14230 ) -> Vec<CreaseId> {
14231 self.display_map
14232 .update(cx, |map, cx| map.insert_creases(creases, cx))
14233 }
14234
14235 pub fn remove_creases(
14236 &mut self,
14237 ids: impl IntoIterator<Item = CreaseId>,
14238 cx: &mut Context<Self>,
14239 ) {
14240 self.display_map
14241 .update(cx, |map, cx| map.remove_creases(ids, cx));
14242 }
14243
14244 pub fn longest_row(&self, cx: &mut App) -> DisplayRow {
14245 self.display_map
14246 .update(cx, |map, cx| map.snapshot(cx))
14247 .longest_row()
14248 }
14249
14250 pub fn max_point(&self, cx: &mut App) -> DisplayPoint {
14251 self.display_map
14252 .update(cx, |map, cx| map.snapshot(cx))
14253 .max_point()
14254 }
14255
14256 pub fn text(&self, cx: &App) -> String {
14257 self.buffer.read(cx).read(cx).text()
14258 }
14259
14260 pub fn is_empty(&self, cx: &App) -> bool {
14261 self.buffer.read(cx).read(cx).is_empty()
14262 }
14263
14264 pub fn text_option(&self, cx: &App) -> Option<String> {
14265 let text = self.text(cx);
14266 let text = text.trim();
14267
14268 if text.is_empty() {
14269 return None;
14270 }
14271
14272 Some(text.to_string())
14273 }
14274
14275 pub fn set_text(
14276 &mut self,
14277 text: impl Into<Arc<str>>,
14278 window: &mut Window,
14279 cx: &mut Context<Self>,
14280 ) {
14281 self.transact(window, cx, |this, _, cx| {
14282 this.buffer
14283 .read(cx)
14284 .as_singleton()
14285 .expect("you can only call set_text on editors for singleton buffers")
14286 .update(cx, |buffer, cx| buffer.set_text(text, cx));
14287 });
14288 }
14289
14290 pub fn display_text(&self, cx: &mut App) -> String {
14291 self.display_map
14292 .update(cx, |map, cx| map.snapshot(cx))
14293 .text()
14294 }
14295
14296 pub fn wrap_guides(&self, cx: &App) -> SmallVec<[(usize, bool); 2]> {
14297 let mut wrap_guides = smallvec::smallvec![];
14298
14299 if self.show_wrap_guides == Some(false) {
14300 return wrap_guides;
14301 }
14302
14303 let settings = self.buffer.read(cx).language_settings(cx);
14304 if settings.show_wrap_guides {
14305 match self.soft_wrap_mode(cx) {
14306 SoftWrap::Column(soft_wrap) => {
14307 wrap_guides.push((soft_wrap as usize, true));
14308 }
14309 SoftWrap::Bounded(soft_wrap) => {
14310 wrap_guides.push((soft_wrap as usize, true));
14311 }
14312 SoftWrap::GitDiff | SoftWrap::None | SoftWrap::EditorWidth => {}
14313 }
14314 wrap_guides.extend(settings.wrap_guides.iter().map(|guide| (*guide, false)))
14315 }
14316
14317 wrap_guides
14318 }
14319
14320 pub fn soft_wrap_mode(&self, cx: &App) -> SoftWrap {
14321 let settings = self.buffer.read(cx).language_settings(cx);
14322 let mode = self.soft_wrap_mode_override.unwrap_or(settings.soft_wrap);
14323 match mode {
14324 language_settings::SoftWrap::PreferLine | language_settings::SoftWrap::None => {
14325 SoftWrap::None
14326 }
14327 language_settings::SoftWrap::EditorWidth => SoftWrap::EditorWidth,
14328 language_settings::SoftWrap::PreferredLineLength => {
14329 SoftWrap::Column(settings.preferred_line_length)
14330 }
14331 language_settings::SoftWrap::Bounded => {
14332 SoftWrap::Bounded(settings.preferred_line_length)
14333 }
14334 }
14335 }
14336
14337 pub fn set_soft_wrap_mode(
14338 &mut self,
14339 mode: language_settings::SoftWrap,
14340
14341 cx: &mut Context<Self>,
14342 ) {
14343 self.soft_wrap_mode_override = Some(mode);
14344 cx.notify();
14345 }
14346
14347 pub fn set_hard_wrap(&mut self, hard_wrap: Option<usize>, cx: &mut Context<Self>) {
14348 self.hard_wrap = hard_wrap;
14349 cx.notify();
14350 }
14351
14352 pub fn set_text_style_refinement(&mut self, style: TextStyleRefinement) {
14353 self.text_style_refinement = Some(style);
14354 }
14355
14356 /// called by the Element so we know what style we were most recently rendered with.
14357 pub(crate) fn set_style(
14358 &mut self,
14359 style: EditorStyle,
14360 window: &mut Window,
14361 cx: &mut Context<Self>,
14362 ) {
14363 let rem_size = window.rem_size();
14364 self.display_map.update(cx, |map, cx| {
14365 map.set_font(
14366 style.text.font(),
14367 style.text.font_size.to_pixels(rem_size),
14368 cx,
14369 )
14370 });
14371 self.style = Some(style);
14372 }
14373
14374 pub fn style(&self) -> Option<&EditorStyle> {
14375 self.style.as_ref()
14376 }
14377
14378 // Called by the element. This method is not designed to be called outside of the editor
14379 // element's layout code because it does not notify when rewrapping is computed synchronously.
14380 pub(crate) fn set_wrap_width(&self, width: Option<Pixels>, cx: &mut App) -> bool {
14381 self.display_map
14382 .update(cx, |map, cx| map.set_wrap_width(width, cx))
14383 }
14384
14385 pub fn set_soft_wrap(&mut self) {
14386 self.soft_wrap_mode_override = Some(language_settings::SoftWrap::EditorWidth)
14387 }
14388
14389 pub fn toggle_soft_wrap(&mut self, _: &ToggleSoftWrap, _: &mut Window, cx: &mut Context<Self>) {
14390 if self.soft_wrap_mode_override.is_some() {
14391 self.soft_wrap_mode_override.take();
14392 } else {
14393 let soft_wrap = match self.soft_wrap_mode(cx) {
14394 SoftWrap::GitDiff => return,
14395 SoftWrap::None => language_settings::SoftWrap::EditorWidth,
14396 SoftWrap::EditorWidth | SoftWrap::Column(_) | SoftWrap::Bounded(_) => {
14397 language_settings::SoftWrap::None
14398 }
14399 };
14400 self.soft_wrap_mode_override = Some(soft_wrap);
14401 }
14402 cx.notify();
14403 }
14404
14405 pub fn toggle_tab_bar(&mut self, _: &ToggleTabBar, _: &mut Window, cx: &mut Context<Self>) {
14406 let Some(workspace) = self.workspace() else {
14407 return;
14408 };
14409 let fs = workspace.read(cx).app_state().fs.clone();
14410 let current_show = TabBarSettings::get_global(cx).show;
14411 update_settings_file::<TabBarSettings>(fs, cx, move |setting, _| {
14412 setting.show = Some(!current_show);
14413 });
14414 }
14415
14416 pub fn toggle_indent_guides(
14417 &mut self,
14418 _: &ToggleIndentGuides,
14419 _: &mut Window,
14420 cx: &mut Context<Self>,
14421 ) {
14422 let currently_enabled = self.should_show_indent_guides().unwrap_or_else(|| {
14423 self.buffer
14424 .read(cx)
14425 .language_settings(cx)
14426 .indent_guides
14427 .enabled
14428 });
14429 self.show_indent_guides = Some(!currently_enabled);
14430 cx.notify();
14431 }
14432
14433 fn should_show_indent_guides(&self) -> Option<bool> {
14434 self.show_indent_guides
14435 }
14436
14437 pub fn toggle_line_numbers(
14438 &mut self,
14439 _: &ToggleLineNumbers,
14440 _: &mut Window,
14441 cx: &mut Context<Self>,
14442 ) {
14443 let mut editor_settings = EditorSettings::get_global(cx).clone();
14444 editor_settings.gutter.line_numbers = !editor_settings.gutter.line_numbers;
14445 EditorSettings::override_global(editor_settings, cx);
14446 }
14447
14448 pub fn line_numbers_enabled(&self, cx: &App) -> bool {
14449 if let Some(show_line_numbers) = self.show_line_numbers {
14450 return show_line_numbers;
14451 }
14452 EditorSettings::get_global(cx).gutter.line_numbers
14453 }
14454
14455 pub fn should_use_relative_line_numbers(&self, cx: &mut App) -> bool {
14456 self.use_relative_line_numbers
14457 .unwrap_or(EditorSettings::get_global(cx).relative_line_numbers)
14458 }
14459
14460 pub fn toggle_relative_line_numbers(
14461 &mut self,
14462 _: &ToggleRelativeLineNumbers,
14463 _: &mut Window,
14464 cx: &mut Context<Self>,
14465 ) {
14466 let is_relative = self.should_use_relative_line_numbers(cx);
14467 self.set_relative_line_number(Some(!is_relative), cx)
14468 }
14469
14470 pub fn set_relative_line_number(&mut self, is_relative: Option<bool>, cx: &mut Context<Self>) {
14471 self.use_relative_line_numbers = is_relative;
14472 cx.notify();
14473 }
14474
14475 pub fn set_show_gutter(&mut self, show_gutter: bool, cx: &mut Context<Self>) {
14476 self.show_gutter = show_gutter;
14477 cx.notify();
14478 }
14479
14480 pub fn set_show_scrollbars(&mut self, show_scrollbars: bool, cx: &mut Context<Self>) {
14481 self.show_scrollbars = show_scrollbars;
14482 cx.notify();
14483 }
14484
14485 pub fn set_show_line_numbers(&mut self, show_line_numbers: bool, cx: &mut Context<Self>) {
14486 self.show_line_numbers = Some(show_line_numbers);
14487 cx.notify();
14488 }
14489
14490 pub fn set_show_git_diff_gutter(&mut self, show_git_diff_gutter: bool, cx: &mut Context<Self>) {
14491 self.show_git_diff_gutter = Some(show_git_diff_gutter);
14492 cx.notify();
14493 }
14494
14495 pub fn set_show_code_actions(&mut self, show_code_actions: bool, cx: &mut Context<Self>) {
14496 self.show_code_actions = Some(show_code_actions);
14497 cx.notify();
14498 }
14499
14500 pub fn set_show_runnables(&mut self, show_runnables: bool, cx: &mut Context<Self>) {
14501 self.show_runnables = Some(show_runnables);
14502 cx.notify();
14503 }
14504
14505 pub fn set_masked(&mut self, masked: bool, cx: &mut Context<Self>) {
14506 if self.display_map.read(cx).masked != masked {
14507 self.display_map.update(cx, |map, _| map.masked = masked);
14508 }
14509 cx.notify()
14510 }
14511
14512 pub fn set_show_wrap_guides(&mut self, show_wrap_guides: bool, cx: &mut Context<Self>) {
14513 self.show_wrap_guides = Some(show_wrap_guides);
14514 cx.notify();
14515 }
14516
14517 pub fn set_show_indent_guides(&mut self, show_indent_guides: bool, cx: &mut Context<Self>) {
14518 self.show_indent_guides = Some(show_indent_guides);
14519 cx.notify();
14520 }
14521
14522 pub fn working_directory(&self, cx: &App) -> Option<PathBuf> {
14523 if let Some(buffer) = self.buffer().read(cx).as_singleton() {
14524 if let Some(file) = buffer.read(cx).file().and_then(|f| f.as_local()) {
14525 if let Some(dir) = file.abs_path(cx).parent() {
14526 return Some(dir.to_owned());
14527 }
14528 }
14529
14530 if let Some(project_path) = buffer.read(cx).project_path(cx) {
14531 return Some(project_path.path.to_path_buf());
14532 }
14533 }
14534
14535 None
14536 }
14537
14538 fn target_file<'a>(&self, cx: &'a App) -> Option<&'a dyn language::LocalFile> {
14539 self.active_excerpt(cx)?
14540 .1
14541 .read(cx)
14542 .file()
14543 .and_then(|f| f.as_local())
14544 }
14545
14546 pub fn target_file_abs_path(&self, cx: &mut Context<Self>) -> Option<PathBuf> {
14547 self.active_excerpt(cx).and_then(|(_, buffer, _)| {
14548 let buffer = buffer.read(cx);
14549 if let Some(project_path) = buffer.project_path(cx) {
14550 let project = self.project.as_ref()?.read(cx);
14551 project.absolute_path(&project_path, cx)
14552 } else {
14553 buffer
14554 .file()
14555 .and_then(|file| file.as_local().map(|file| file.abs_path(cx)))
14556 }
14557 })
14558 }
14559
14560 fn target_file_path(&self, cx: &mut Context<Self>) -> Option<PathBuf> {
14561 self.active_excerpt(cx).and_then(|(_, buffer, _)| {
14562 let project_path = buffer.read(cx).project_path(cx)?;
14563 let project = self.project.as_ref()?.read(cx);
14564 let entry = project.entry_for_path(&project_path, cx)?;
14565 let path = entry.path.to_path_buf();
14566 Some(path)
14567 })
14568 }
14569
14570 pub fn reveal_in_finder(
14571 &mut self,
14572 _: &RevealInFileManager,
14573 _window: &mut Window,
14574 cx: &mut Context<Self>,
14575 ) {
14576 if let Some(target) = self.target_file(cx) {
14577 cx.reveal_path(&target.abs_path(cx));
14578 }
14579 }
14580
14581 pub fn copy_path(
14582 &mut self,
14583 _: &zed_actions::workspace::CopyPath,
14584 _window: &mut Window,
14585 cx: &mut Context<Self>,
14586 ) {
14587 if let Some(path) = self.target_file_abs_path(cx) {
14588 if let Some(path) = path.to_str() {
14589 cx.write_to_clipboard(ClipboardItem::new_string(path.to_string()));
14590 }
14591 }
14592 }
14593
14594 pub fn copy_relative_path(
14595 &mut self,
14596 _: &zed_actions::workspace::CopyRelativePath,
14597 _window: &mut Window,
14598 cx: &mut Context<Self>,
14599 ) {
14600 if let Some(path) = self.target_file_path(cx) {
14601 if let Some(path) = path.to_str() {
14602 cx.write_to_clipboard(ClipboardItem::new_string(path.to_string()));
14603 }
14604 }
14605 }
14606
14607 pub fn copy_file_name_without_extension(
14608 &mut self,
14609 _: &CopyFileNameWithoutExtension,
14610 _: &mut Window,
14611 cx: &mut Context<Self>,
14612 ) {
14613 if let Some(file) = self.target_file(cx) {
14614 if let Some(file_stem) = file.path().file_stem() {
14615 if let Some(name) = file_stem.to_str() {
14616 cx.write_to_clipboard(ClipboardItem::new_string(name.to_string()));
14617 }
14618 }
14619 }
14620 }
14621
14622 pub fn copy_file_name(&mut self, _: &CopyFileName, _: &mut Window, cx: &mut Context<Self>) {
14623 if let Some(file) = self.target_file(cx) {
14624 if let Some(file_name) = file.path().file_name() {
14625 if let Some(name) = file_name.to_str() {
14626 cx.write_to_clipboard(ClipboardItem::new_string(name.to_string()));
14627 }
14628 }
14629 }
14630 }
14631
14632 pub fn toggle_git_blame(
14633 &mut self,
14634 _: &::git::Blame,
14635 window: &mut Window,
14636 cx: &mut Context<Self>,
14637 ) {
14638 self.show_git_blame_gutter = !self.show_git_blame_gutter;
14639
14640 if self.show_git_blame_gutter && !self.has_blame_entries(cx) {
14641 self.start_git_blame(true, window, cx);
14642 }
14643
14644 cx.notify();
14645 }
14646
14647 pub fn toggle_git_blame_inline(
14648 &mut self,
14649 _: &ToggleGitBlameInline,
14650 window: &mut Window,
14651 cx: &mut Context<Self>,
14652 ) {
14653 self.toggle_git_blame_inline_internal(true, window, cx);
14654 cx.notify();
14655 }
14656
14657 pub fn git_blame_inline_enabled(&self) -> bool {
14658 self.git_blame_inline_enabled
14659 }
14660
14661 pub fn toggle_selection_menu(
14662 &mut self,
14663 _: &ToggleSelectionMenu,
14664 _: &mut Window,
14665 cx: &mut Context<Self>,
14666 ) {
14667 self.show_selection_menu = self
14668 .show_selection_menu
14669 .map(|show_selections_menu| !show_selections_menu)
14670 .or_else(|| Some(!EditorSettings::get_global(cx).toolbar.selections_menu));
14671
14672 cx.notify();
14673 }
14674
14675 pub fn selection_menu_enabled(&self, cx: &App) -> bool {
14676 self.show_selection_menu
14677 .unwrap_or_else(|| EditorSettings::get_global(cx).toolbar.selections_menu)
14678 }
14679
14680 fn start_git_blame(
14681 &mut self,
14682 user_triggered: bool,
14683 window: &mut Window,
14684 cx: &mut Context<Self>,
14685 ) {
14686 if let Some(project) = self.project.as_ref() {
14687 let Some(buffer) = self.buffer().read(cx).as_singleton() else {
14688 return;
14689 };
14690
14691 if buffer.read(cx).file().is_none() {
14692 return;
14693 }
14694
14695 let focused = self.focus_handle(cx).contains_focused(window, cx);
14696
14697 let project = project.clone();
14698 let blame = cx.new(|cx| GitBlame::new(buffer, project, user_triggered, focused, cx));
14699 self.blame_subscription =
14700 Some(cx.observe_in(&blame, window, |_, _, _, cx| cx.notify()));
14701 self.blame = Some(blame);
14702 }
14703 }
14704
14705 fn toggle_git_blame_inline_internal(
14706 &mut self,
14707 user_triggered: bool,
14708 window: &mut Window,
14709 cx: &mut Context<Self>,
14710 ) {
14711 if self.git_blame_inline_enabled {
14712 self.git_blame_inline_enabled = false;
14713 self.show_git_blame_inline = false;
14714 self.show_git_blame_inline_delay_task.take();
14715 } else {
14716 self.git_blame_inline_enabled = true;
14717 self.start_git_blame_inline(user_triggered, window, cx);
14718 }
14719
14720 cx.notify();
14721 }
14722
14723 fn start_git_blame_inline(
14724 &mut self,
14725 user_triggered: bool,
14726 window: &mut Window,
14727 cx: &mut Context<Self>,
14728 ) {
14729 self.start_git_blame(user_triggered, window, cx);
14730
14731 if ProjectSettings::get_global(cx)
14732 .git
14733 .inline_blame_delay()
14734 .is_some()
14735 {
14736 self.start_inline_blame_timer(window, cx);
14737 } else {
14738 self.show_git_blame_inline = true
14739 }
14740 }
14741
14742 pub fn blame(&self) -> Option<&Entity<GitBlame>> {
14743 self.blame.as_ref()
14744 }
14745
14746 pub fn show_git_blame_gutter(&self) -> bool {
14747 self.show_git_blame_gutter
14748 }
14749
14750 pub fn render_git_blame_gutter(&self, cx: &App) -> bool {
14751 self.show_git_blame_gutter && self.has_blame_entries(cx)
14752 }
14753
14754 pub fn render_git_blame_inline(&self, window: &Window, cx: &App) -> bool {
14755 self.show_git_blame_inline
14756 && (self.focus_handle.is_focused(window)
14757 || self
14758 .git_blame_inline_tooltip
14759 .as_ref()
14760 .and_then(|t| t.upgrade())
14761 .is_some())
14762 && !self.newest_selection_head_on_empty_line(cx)
14763 && self.has_blame_entries(cx)
14764 }
14765
14766 fn has_blame_entries(&self, cx: &App) -> bool {
14767 self.blame()
14768 .map_or(false, |blame| blame.read(cx).has_generated_entries())
14769 }
14770
14771 fn newest_selection_head_on_empty_line(&self, cx: &App) -> bool {
14772 let cursor_anchor = self.selections.newest_anchor().head();
14773
14774 let snapshot = self.buffer.read(cx).snapshot(cx);
14775 let buffer_row = MultiBufferRow(cursor_anchor.to_point(&snapshot).row);
14776
14777 snapshot.line_len(buffer_row) == 0
14778 }
14779
14780 fn get_permalink_to_line(&self, cx: &mut Context<Self>) -> Task<Result<url::Url>> {
14781 let buffer_and_selection = maybe!({
14782 let selection = self.selections.newest::<Point>(cx);
14783 let selection_range = selection.range();
14784
14785 let multi_buffer = self.buffer().read(cx);
14786 let multi_buffer_snapshot = multi_buffer.snapshot(cx);
14787 let buffer_ranges = multi_buffer_snapshot.range_to_buffer_ranges(selection_range);
14788
14789 let (buffer, range, _) = if selection.reversed {
14790 buffer_ranges.first()
14791 } else {
14792 buffer_ranges.last()
14793 }?;
14794
14795 let selection = text::ToPoint::to_point(&range.start, &buffer).row
14796 ..text::ToPoint::to_point(&range.end, &buffer).row;
14797 Some((
14798 multi_buffer.buffer(buffer.remote_id()).unwrap().clone(),
14799 selection,
14800 ))
14801 });
14802
14803 let Some((buffer, selection)) = buffer_and_selection else {
14804 return Task::ready(Err(anyhow!("failed to determine buffer and selection")));
14805 };
14806
14807 let Some(project) = self.project.as_ref() else {
14808 return Task::ready(Err(anyhow!("editor does not have project")));
14809 };
14810
14811 project.update(cx, |project, cx| {
14812 project.get_permalink_to_line(&buffer, selection, cx)
14813 })
14814 }
14815
14816 pub fn copy_permalink_to_line(
14817 &mut self,
14818 _: &CopyPermalinkToLine,
14819 window: &mut Window,
14820 cx: &mut Context<Self>,
14821 ) {
14822 let permalink_task = self.get_permalink_to_line(cx);
14823 let workspace = self.workspace();
14824
14825 cx.spawn_in(window, |_, mut cx| async move {
14826 match permalink_task.await {
14827 Ok(permalink) => {
14828 cx.update(|_, cx| {
14829 cx.write_to_clipboard(ClipboardItem::new_string(permalink.to_string()));
14830 })
14831 .ok();
14832 }
14833 Err(err) => {
14834 let message = format!("Failed to copy permalink: {err}");
14835
14836 Err::<(), anyhow::Error>(err).log_err();
14837
14838 if let Some(workspace) = workspace {
14839 workspace
14840 .update_in(&mut cx, |workspace, _, cx| {
14841 struct CopyPermalinkToLine;
14842
14843 workspace.show_toast(
14844 Toast::new(
14845 NotificationId::unique::<CopyPermalinkToLine>(),
14846 message,
14847 ),
14848 cx,
14849 )
14850 })
14851 .ok();
14852 }
14853 }
14854 }
14855 })
14856 .detach();
14857 }
14858
14859 pub fn copy_file_location(
14860 &mut self,
14861 _: &CopyFileLocation,
14862 _: &mut Window,
14863 cx: &mut Context<Self>,
14864 ) {
14865 let selection = self.selections.newest::<Point>(cx).start.row + 1;
14866 if let Some(file) = self.target_file(cx) {
14867 if let Some(path) = file.path().to_str() {
14868 cx.write_to_clipboard(ClipboardItem::new_string(format!("{path}:{selection}")));
14869 }
14870 }
14871 }
14872
14873 pub fn open_permalink_to_line(
14874 &mut self,
14875 _: &OpenPermalinkToLine,
14876 window: &mut Window,
14877 cx: &mut Context<Self>,
14878 ) {
14879 let permalink_task = self.get_permalink_to_line(cx);
14880 let workspace = self.workspace();
14881
14882 cx.spawn_in(window, |_, mut cx| async move {
14883 match permalink_task.await {
14884 Ok(permalink) => {
14885 cx.update(|_, cx| {
14886 cx.open_url(permalink.as_ref());
14887 })
14888 .ok();
14889 }
14890 Err(err) => {
14891 let message = format!("Failed to open permalink: {err}");
14892
14893 Err::<(), anyhow::Error>(err).log_err();
14894
14895 if let Some(workspace) = workspace {
14896 workspace
14897 .update(&mut cx, |workspace, cx| {
14898 struct OpenPermalinkToLine;
14899
14900 workspace.show_toast(
14901 Toast::new(
14902 NotificationId::unique::<OpenPermalinkToLine>(),
14903 message,
14904 ),
14905 cx,
14906 )
14907 })
14908 .ok();
14909 }
14910 }
14911 }
14912 })
14913 .detach();
14914 }
14915
14916 pub fn insert_uuid_v4(
14917 &mut self,
14918 _: &InsertUuidV4,
14919 window: &mut Window,
14920 cx: &mut Context<Self>,
14921 ) {
14922 self.insert_uuid(UuidVersion::V4, window, cx);
14923 }
14924
14925 pub fn insert_uuid_v7(
14926 &mut self,
14927 _: &InsertUuidV7,
14928 window: &mut Window,
14929 cx: &mut Context<Self>,
14930 ) {
14931 self.insert_uuid(UuidVersion::V7, window, cx);
14932 }
14933
14934 fn insert_uuid(&mut self, version: UuidVersion, window: &mut Window, cx: &mut Context<Self>) {
14935 self.transact(window, cx, |this, window, cx| {
14936 let edits = this
14937 .selections
14938 .all::<Point>(cx)
14939 .into_iter()
14940 .map(|selection| {
14941 let uuid = match version {
14942 UuidVersion::V4 => uuid::Uuid::new_v4(),
14943 UuidVersion::V7 => uuid::Uuid::now_v7(),
14944 };
14945
14946 (selection.range(), uuid.to_string())
14947 });
14948 this.edit(edits, cx);
14949 this.refresh_inline_completion(true, false, window, cx);
14950 });
14951 }
14952
14953 pub fn open_selections_in_multibuffer(
14954 &mut self,
14955 _: &OpenSelectionsInMultibuffer,
14956 window: &mut Window,
14957 cx: &mut Context<Self>,
14958 ) {
14959 let multibuffer = self.buffer.read(cx);
14960
14961 let Some(buffer) = multibuffer.as_singleton() else {
14962 return;
14963 };
14964
14965 let Some(workspace) = self.workspace() else {
14966 return;
14967 };
14968
14969 let locations = self
14970 .selections
14971 .disjoint_anchors()
14972 .iter()
14973 .map(|range| Location {
14974 buffer: buffer.clone(),
14975 range: range.start.text_anchor..range.end.text_anchor,
14976 })
14977 .collect::<Vec<_>>();
14978
14979 let title = multibuffer.title(cx).to_string();
14980
14981 cx.spawn_in(window, |_, mut cx| async move {
14982 workspace.update_in(&mut cx, |workspace, window, cx| {
14983 Self::open_locations_in_multibuffer(
14984 workspace,
14985 locations,
14986 format!("Selections for '{title}'"),
14987 false,
14988 MultibufferSelectionMode::All,
14989 window,
14990 cx,
14991 );
14992 })
14993 })
14994 .detach();
14995 }
14996
14997 /// Adds a row highlight for the given range. If a row has multiple highlights, the
14998 /// last highlight added will be used.
14999 ///
15000 /// If the range ends at the beginning of a line, then that line will not be highlighted.
15001 pub fn highlight_rows<T: 'static>(
15002 &mut self,
15003 range: Range<Anchor>,
15004 color: Hsla,
15005 should_autoscroll: bool,
15006 cx: &mut Context<Self>,
15007 ) {
15008 let snapshot = self.buffer().read(cx).snapshot(cx);
15009 let row_highlights = self.highlighted_rows.entry(TypeId::of::<T>()).or_default();
15010 let ix = row_highlights.binary_search_by(|highlight| {
15011 Ordering::Equal
15012 .then_with(|| highlight.range.start.cmp(&range.start, &snapshot))
15013 .then_with(|| highlight.range.end.cmp(&range.end, &snapshot))
15014 });
15015
15016 if let Err(mut ix) = ix {
15017 let index = post_inc(&mut self.highlight_order);
15018
15019 // If this range intersects with the preceding highlight, then merge it with
15020 // the preceding highlight. Otherwise insert a new highlight.
15021 let mut merged = false;
15022 if ix > 0 {
15023 let prev_highlight = &mut row_highlights[ix - 1];
15024 if prev_highlight
15025 .range
15026 .end
15027 .cmp(&range.start, &snapshot)
15028 .is_ge()
15029 {
15030 ix -= 1;
15031 if prev_highlight.range.end.cmp(&range.end, &snapshot).is_lt() {
15032 prev_highlight.range.end = range.end;
15033 }
15034 merged = true;
15035 prev_highlight.index = index;
15036 prev_highlight.color = color;
15037 prev_highlight.should_autoscroll = should_autoscroll;
15038 }
15039 }
15040
15041 if !merged {
15042 row_highlights.insert(
15043 ix,
15044 RowHighlight {
15045 range: range.clone(),
15046 index,
15047 color,
15048 should_autoscroll,
15049 },
15050 );
15051 }
15052
15053 // If any of the following highlights intersect with this one, merge them.
15054 while let Some(next_highlight) = row_highlights.get(ix + 1) {
15055 let highlight = &row_highlights[ix];
15056 if next_highlight
15057 .range
15058 .start
15059 .cmp(&highlight.range.end, &snapshot)
15060 .is_le()
15061 {
15062 if next_highlight
15063 .range
15064 .end
15065 .cmp(&highlight.range.end, &snapshot)
15066 .is_gt()
15067 {
15068 row_highlights[ix].range.end = next_highlight.range.end;
15069 }
15070 row_highlights.remove(ix + 1);
15071 } else {
15072 break;
15073 }
15074 }
15075 }
15076 }
15077
15078 /// Remove any highlighted row ranges of the given type that intersect the
15079 /// given ranges.
15080 pub fn remove_highlighted_rows<T: 'static>(
15081 &mut self,
15082 ranges_to_remove: Vec<Range<Anchor>>,
15083 cx: &mut Context<Self>,
15084 ) {
15085 let snapshot = self.buffer().read(cx).snapshot(cx);
15086 let row_highlights = self.highlighted_rows.entry(TypeId::of::<T>()).or_default();
15087 let mut ranges_to_remove = ranges_to_remove.iter().peekable();
15088 row_highlights.retain(|highlight| {
15089 while let Some(range_to_remove) = ranges_to_remove.peek() {
15090 match range_to_remove.end.cmp(&highlight.range.start, &snapshot) {
15091 Ordering::Less | Ordering::Equal => {
15092 ranges_to_remove.next();
15093 }
15094 Ordering::Greater => {
15095 match range_to_remove.start.cmp(&highlight.range.end, &snapshot) {
15096 Ordering::Less | Ordering::Equal => {
15097 return false;
15098 }
15099 Ordering::Greater => break,
15100 }
15101 }
15102 }
15103 }
15104
15105 true
15106 })
15107 }
15108
15109 /// Clear all anchor ranges for a certain highlight context type, so no corresponding rows will be highlighted.
15110 pub fn clear_row_highlights<T: 'static>(&mut self) {
15111 self.highlighted_rows.remove(&TypeId::of::<T>());
15112 }
15113
15114 /// For a highlight given context type, gets all anchor ranges that will be used for row highlighting.
15115 pub fn highlighted_rows<T: 'static>(&self) -> impl '_ + Iterator<Item = (Range<Anchor>, Hsla)> {
15116 self.highlighted_rows
15117 .get(&TypeId::of::<T>())
15118 .map_or(&[] as &[_], |vec| vec.as_slice())
15119 .iter()
15120 .map(|highlight| (highlight.range.clone(), highlight.color))
15121 }
15122
15123 /// Merges all anchor ranges for all context types ever set, picking the last highlight added in case of a row conflict.
15124 /// Returns a map of display rows that are highlighted and their corresponding highlight color.
15125 /// Allows to ignore certain kinds of highlights.
15126 pub fn highlighted_display_rows(
15127 &self,
15128 window: &mut Window,
15129 cx: &mut App,
15130 ) -> BTreeMap<DisplayRow, LineHighlight> {
15131 let snapshot = self.snapshot(window, cx);
15132 let mut used_highlight_orders = HashMap::default();
15133 self.highlighted_rows
15134 .iter()
15135 .flat_map(|(_, highlighted_rows)| highlighted_rows.iter())
15136 .fold(
15137 BTreeMap::<DisplayRow, LineHighlight>::new(),
15138 |mut unique_rows, highlight| {
15139 let start = highlight.range.start.to_display_point(&snapshot);
15140 let end = highlight.range.end.to_display_point(&snapshot);
15141 let start_row = start.row().0;
15142 let end_row = if highlight.range.end.text_anchor != text::Anchor::MAX
15143 && end.column() == 0
15144 {
15145 end.row().0.saturating_sub(1)
15146 } else {
15147 end.row().0
15148 };
15149 for row in start_row..=end_row {
15150 let used_index =
15151 used_highlight_orders.entry(row).or_insert(highlight.index);
15152 if highlight.index >= *used_index {
15153 *used_index = highlight.index;
15154 unique_rows.insert(DisplayRow(row), highlight.color.into());
15155 }
15156 }
15157 unique_rows
15158 },
15159 )
15160 }
15161
15162 pub fn highlighted_display_row_for_autoscroll(
15163 &self,
15164 snapshot: &DisplaySnapshot,
15165 ) -> Option<DisplayRow> {
15166 self.highlighted_rows
15167 .values()
15168 .flat_map(|highlighted_rows| highlighted_rows.iter())
15169 .filter_map(|highlight| {
15170 if highlight.should_autoscroll {
15171 Some(highlight.range.start.to_display_point(snapshot).row())
15172 } else {
15173 None
15174 }
15175 })
15176 .min()
15177 }
15178
15179 pub fn set_search_within_ranges(&mut self, ranges: &[Range<Anchor>], cx: &mut Context<Self>) {
15180 self.highlight_background::<SearchWithinRange>(
15181 ranges,
15182 |colors| colors.editor_document_highlight_read_background,
15183 cx,
15184 )
15185 }
15186
15187 pub fn set_breadcrumb_header(&mut self, new_header: String) {
15188 self.breadcrumb_header = Some(new_header);
15189 }
15190
15191 pub fn clear_search_within_ranges(&mut self, cx: &mut Context<Self>) {
15192 self.clear_background_highlights::<SearchWithinRange>(cx);
15193 }
15194
15195 pub fn highlight_background<T: 'static>(
15196 &mut self,
15197 ranges: &[Range<Anchor>],
15198 color_fetcher: fn(&ThemeColors) -> Hsla,
15199 cx: &mut Context<Self>,
15200 ) {
15201 self.background_highlights
15202 .insert(TypeId::of::<T>(), (color_fetcher, Arc::from(ranges)));
15203 self.scrollbar_marker_state.dirty = true;
15204 cx.notify();
15205 }
15206
15207 pub fn clear_background_highlights<T: 'static>(
15208 &mut self,
15209 cx: &mut Context<Self>,
15210 ) -> Option<BackgroundHighlight> {
15211 let text_highlights = self.background_highlights.remove(&TypeId::of::<T>())?;
15212 if !text_highlights.1.is_empty() {
15213 self.scrollbar_marker_state.dirty = true;
15214 cx.notify();
15215 }
15216 Some(text_highlights)
15217 }
15218
15219 pub fn highlight_gutter<T: 'static>(
15220 &mut self,
15221 ranges: &[Range<Anchor>],
15222 color_fetcher: fn(&App) -> Hsla,
15223 cx: &mut Context<Self>,
15224 ) {
15225 self.gutter_highlights
15226 .insert(TypeId::of::<T>(), (color_fetcher, Arc::from(ranges)));
15227 cx.notify();
15228 }
15229
15230 pub fn clear_gutter_highlights<T: 'static>(
15231 &mut self,
15232 cx: &mut Context<Self>,
15233 ) -> Option<GutterHighlight> {
15234 cx.notify();
15235 self.gutter_highlights.remove(&TypeId::of::<T>())
15236 }
15237
15238 #[cfg(feature = "test-support")]
15239 pub fn all_text_background_highlights(
15240 &self,
15241 window: &mut Window,
15242 cx: &mut Context<Self>,
15243 ) -> Vec<(Range<DisplayPoint>, Hsla)> {
15244 let snapshot = self.snapshot(window, cx);
15245 let buffer = &snapshot.buffer_snapshot;
15246 let start = buffer.anchor_before(0);
15247 let end = buffer.anchor_after(buffer.len());
15248 let theme = cx.theme().colors();
15249 self.background_highlights_in_range(start..end, &snapshot, theme)
15250 }
15251
15252 #[cfg(feature = "test-support")]
15253 pub fn search_background_highlights(&mut self, cx: &mut Context<Self>) -> Vec<Range<Point>> {
15254 let snapshot = self.buffer().read(cx).snapshot(cx);
15255
15256 let highlights = self
15257 .background_highlights
15258 .get(&TypeId::of::<items::BufferSearchHighlights>());
15259
15260 if let Some((_color, ranges)) = highlights {
15261 ranges
15262 .iter()
15263 .map(|range| range.start.to_point(&snapshot)..range.end.to_point(&snapshot))
15264 .collect_vec()
15265 } else {
15266 vec![]
15267 }
15268 }
15269
15270 fn document_highlights_for_position<'a>(
15271 &'a self,
15272 position: Anchor,
15273 buffer: &'a MultiBufferSnapshot,
15274 ) -> impl 'a + Iterator<Item = &'a Range<Anchor>> {
15275 let read_highlights = self
15276 .background_highlights
15277 .get(&TypeId::of::<DocumentHighlightRead>())
15278 .map(|h| &h.1);
15279 let write_highlights = self
15280 .background_highlights
15281 .get(&TypeId::of::<DocumentHighlightWrite>())
15282 .map(|h| &h.1);
15283 let left_position = position.bias_left(buffer);
15284 let right_position = position.bias_right(buffer);
15285 read_highlights
15286 .into_iter()
15287 .chain(write_highlights)
15288 .flat_map(move |ranges| {
15289 let start_ix = match ranges.binary_search_by(|probe| {
15290 let cmp = probe.end.cmp(&left_position, buffer);
15291 if cmp.is_ge() {
15292 Ordering::Greater
15293 } else {
15294 Ordering::Less
15295 }
15296 }) {
15297 Ok(i) | Err(i) => i,
15298 };
15299
15300 ranges[start_ix..]
15301 .iter()
15302 .take_while(move |range| range.start.cmp(&right_position, buffer).is_le())
15303 })
15304 }
15305
15306 pub fn has_background_highlights<T: 'static>(&self) -> bool {
15307 self.background_highlights
15308 .get(&TypeId::of::<T>())
15309 .map_or(false, |(_, highlights)| !highlights.is_empty())
15310 }
15311
15312 pub fn background_highlights_in_range(
15313 &self,
15314 search_range: Range<Anchor>,
15315 display_snapshot: &DisplaySnapshot,
15316 theme: &ThemeColors,
15317 ) -> Vec<(Range<DisplayPoint>, Hsla)> {
15318 let mut results = Vec::new();
15319 for (color_fetcher, ranges) in self.background_highlights.values() {
15320 let color = color_fetcher(theme);
15321 let start_ix = match ranges.binary_search_by(|probe| {
15322 let cmp = probe
15323 .end
15324 .cmp(&search_range.start, &display_snapshot.buffer_snapshot);
15325 if cmp.is_gt() {
15326 Ordering::Greater
15327 } else {
15328 Ordering::Less
15329 }
15330 }) {
15331 Ok(i) | Err(i) => i,
15332 };
15333 for range in &ranges[start_ix..] {
15334 if range
15335 .start
15336 .cmp(&search_range.end, &display_snapshot.buffer_snapshot)
15337 .is_ge()
15338 {
15339 break;
15340 }
15341
15342 let start = range.start.to_display_point(display_snapshot);
15343 let end = range.end.to_display_point(display_snapshot);
15344 results.push((start..end, color))
15345 }
15346 }
15347 results
15348 }
15349
15350 pub fn background_highlight_row_ranges<T: 'static>(
15351 &self,
15352 search_range: Range<Anchor>,
15353 display_snapshot: &DisplaySnapshot,
15354 count: usize,
15355 ) -> Vec<RangeInclusive<DisplayPoint>> {
15356 let mut results = Vec::new();
15357 let Some((_, ranges)) = self.background_highlights.get(&TypeId::of::<T>()) else {
15358 return vec![];
15359 };
15360
15361 let start_ix = match ranges.binary_search_by(|probe| {
15362 let cmp = probe
15363 .end
15364 .cmp(&search_range.start, &display_snapshot.buffer_snapshot);
15365 if cmp.is_gt() {
15366 Ordering::Greater
15367 } else {
15368 Ordering::Less
15369 }
15370 }) {
15371 Ok(i) | Err(i) => i,
15372 };
15373 let mut push_region = |start: Option<Point>, end: Option<Point>| {
15374 if let (Some(start_display), Some(end_display)) = (start, end) {
15375 results.push(
15376 start_display.to_display_point(display_snapshot)
15377 ..=end_display.to_display_point(display_snapshot),
15378 );
15379 }
15380 };
15381 let mut start_row: Option<Point> = None;
15382 let mut end_row: Option<Point> = None;
15383 if ranges.len() > count {
15384 return Vec::new();
15385 }
15386 for range in &ranges[start_ix..] {
15387 if range
15388 .start
15389 .cmp(&search_range.end, &display_snapshot.buffer_snapshot)
15390 .is_ge()
15391 {
15392 break;
15393 }
15394 let end = range.end.to_point(&display_snapshot.buffer_snapshot);
15395 if let Some(current_row) = &end_row {
15396 if end.row == current_row.row {
15397 continue;
15398 }
15399 }
15400 let start = range.start.to_point(&display_snapshot.buffer_snapshot);
15401 if start_row.is_none() {
15402 assert_eq!(end_row, None);
15403 start_row = Some(start);
15404 end_row = Some(end);
15405 continue;
15406 }
15407 if let Some(current_end) = end_row.as_mut() {
15408 if start.row > current_end.row + 1 {
15409 push_region(start_row, end_row);
15410 start_row = Some(start);
15411 end_row = Some(end);
15412 } else {
15413 // Merge two hunks.
15414 *current_end = end;
15415 }
15416 } else {
15417 unreachable!();
15418 }
15419 }
15420 // We might still have a hunk that was not rendered (if there was a search hit on the last line)
15421 push_region(start_row, end_row);
15422 results
15423 }
15424
15425 pub fn gutter_highlights_in_range(
15426 &self,
15427 search_range: Range<Anchor>,
15428 display_snapshot: &DisplaySnapshot,
15429 cx: &App,
15430 ) -> Vec<(Range<DisplayPoint>, Hsla)> {
15431 let mut results = Vec::new();
15432 for (color_fetcher, ranges) in self.gutter_highlights.values() {
15433 let color = color_fetcher(cx);
15434 let start_ix = match ranges.binary_search_by(|probe| {
15435 let cmp = probe
15436 .end
15437 .cmp(&search_range.start, &display_snapshot.buffer_snapshot);
15438 if cmp.is_gt() {
15439 Ordering::Greater
15440 } else {
15441 Ordering::Less
15442 }
15443 }) {
15444 Ok(i) | Err(i) => i,
15445 };
15446 for range in &ranges[start_ix..] {
15447 if range
15448 .start
15449 .cmp(&search_range.end, &display_snapshot.buffer_snapshot)
15450 .is_ge()
15451 {
15452 break;
15453 }
15454
15455 let start = range.start.to_display_point(display_snapshot);
15456 let end = range.end.to_display_point(display_snapshot);
15457 results.push((start..end, color))
15458 }
15459 }
15460 results
15461 }
15462
15463 /// Get the text ranges corresponding to the redaction query
15464 pub fn redacted_ranges(
15465 &self,
15466 search_range: Range<Anchor>,
15467 display_snapshot: &DisplaySnapshot,
15468 cx: &App,
15469 ) -> Vec<Range<DisplayPoint>> {
15470 display_snapshot
15471 .buffer_snapshot
15472 .redacted_ranges(search_range, |file| {
15473 if let Some(file) = file {
15474 file.is_private()
15475 && EditorSettings::get(
15476 Some(SettingsLocation {
15477 worktree_id: file.worktree_id(cx),
15478 path: file.path().as_ref(),
15479 }),
15480 cx,
15481 )
15482 .redact_private_values
15483 } else {
15484 false
15485 }
15486 })
15487 .map(|range| {
15488 range.start.to_display_point(display_snapshot)
15489 ..range.end.to_display_point(display_snapshot)
15490 })
15491 .collect()
15492 }
15493
15494 pub fn highlight_text<T: 'static>(
15495 &mut self,
15496 ranges: Vec<Range<Anchor>>,
15497 style: HighlightStyle,
15498 cx: &mut Context<Self>,
15499 ) {
15500 self.display_map.update(cx, |map, _| {
15501 map.highlight_text(TypeId::of::<T>(), ranges, style)
15502 });
15503 cx.notify();
15504 }
15505
15506 pub(crate) fn highlight_inlays<T: 'static>(
15507 &mut self,
15508 highlights: Vec<InlayHighlight>,
15509 style: HighlightStyle,
15510 cx: &mut Context<Self>,
15511 ) {
15512 self.display_map.update(cx, |map, _| {
15513 map.highlight_inlays(TypeId::of::<T>(), highlights, style)
15514 });
15515 cx.notify();
15516 }
15517
15518 pub fn text_highlights<'a, T: 'static>(
15519 &'a self,
15520 cx: &'a App,
15521 ) -> Option<(HighlightStyle, &'a [Range<Anchor>])> {
15522 self.display_map.read(cx).text_highlights(TypeId::of::<T>())
15523 }
15524
15525 pub fn clear_highlights<T: 'static>(&mut self, cx: &mut Context<Self>) {
15526 let cleared = self
15527 .display_map
15528 .update(cx, |map, _| map.clear_highlights(TypeId::of::<T>()));
15529 if cleared {
15530 cx.notify();
15531 }
15532 }
15533
15534 pub fn show_local_cursors(&self, window: &mut Window, cx: &mut App) -> bool {
15535 (self.read_only(cx) || self.blink_manager.read(cx).visible())
15536 && self.focus_handle.is_focused(window)
15537 }
15538
15539 pub fn set_show_cursor_when_unfocused(&mut self, is_enabled: bool, cx: &mut Context<Self>) {
15540 self.show_cursor_when_unfocused = is_enabled;
15541 cx.notify();
15542 }
15543
15544 fn on_buffer_changed(&mut self, _: Entity<MultiBuffer>, cx: &mut Context<Self>) {
15545 cx.notify();
15546 }
15547
15548 fn on_buffer_event(
15549 &mut self,
15550 multibuffer: &Entity<MultiBuffer>,
15551 event: &multi_buffer::Event,
15552 window: &mut Window,
15553 cx: &mut Context<Self>,
15554 ) {
15555 match event {
15556 multi_buffer::Event::Edited {
15557 singleton_buffer_edited,
15558 edited_buffer: buffer_edited,
15559 } => {
15560 self.scrollbar_marker_state.dirty = true;
15561 self.active_indent_guides_state.dirty = true;
15562 self.refresh_active_diagnostics(cx);
15563 self.refresh_code_actions(window, cx);
15564 if self.has_active_inline_completion() {
15565 self.update_visible_inline_completion(window, cx);
15566 }
15567 if let Some(buffer) = buffer_edited {
15568 let buffer_id = buffer.read(cx).remote_id();
15569 if !self.registered_buffers.contains_key(&buffer_id) {
15570 if let Some(project) = self.project.as_ref() {
15571 project.update(cx, |project, cx| {
15572 self.registered_buffers.insert(
15573 buffer_id,
15574 project.register_buffer_with_language_servers(&buffer, cx),
15575 );
15576 })
15577 }
15578 }
15579 }
15580 cx.emit(EditorEvent::BufferEdited);
15581 cx.emit(SearchEvent::MatchesInvalidated);
15582 if *singleton_buffer_edited {
15583 if let Some(project) = &self.project {
15584 #[allow(clippy::mutable_key_type)]
15585 let languages_affected = multibuffer.update(cx, |multibuffer, cx| {
15586 multibuffer
15587 .all_buffers()
15588 .into_iter()
15589 .filter_map(|buffer| {
15590 buffer.update(cx, |buffer, cx| {
15591 let language = buffer.language()?;
15592 let should_discard = project.update(cx, |project, cx| {
15593 project.is_local()
15594 && !project.has_language_servers_for(buffer, cx)
15595 });
15596 should_discard.not().then_some(language.clone())
15597 })
15598 })
15599 .collect::<HashSet<_>>()
15600 });
15601 if !languages_affected.is_empty() {
15602 self.refresh_inlay_hints(
15603 InlayHintRefreshReason::BufferEdited(languages_affected),
15604 cx,
15605 );
15606 }
15607 }
15608 }
15609
15610 let Some(project) = &self.project else { return };
15611 let (telemetry, is_via_ssh) = {
15612 let project = project.read(cx);
15613 let telemetry = project.client().telemetry().clone();
15614 let is_via_ssh = project.is_via_ssh();
15615 (telemetry, is_via_ssh)
15616 };
15617 refresh_linked_ranges(self, window, cx);
15618 telemetry.log_edit_event("editor", is_via_ssh);
15619 }
15620 multi_buffer::Event::ExcerptsAdded {
15621 buffer,
15622 predecessor,
15623 excerpts,
15624 } => {
15625 self.tasks_update_task = Some(self.refresh_runnables(window, cx));
15626 let buffer_id = buffer.read(cx).remote_id();
15627 if self.buffer.read(cx).diff_for(buffer_id).is_none() {
15628 if let Some(project) = &self.project {
15629 get_uncommitted_diff_for_buffer(
15630 project,
15631 [buffer.clone()],
15632 self.buffer.clone(),
15633 cx,
15634 )
15635 .detach();
15636 }
15637 }
15638 cx.emit(EditorEvent::ExcerptsAdded {
15639 buffer: buffer.clone(),
15640 predecessor: *predecessor,
15641 excerpts: excerpts.clone(),
15642 });
15643 self.refresh_inlay_hints(InlayHintRefreshReason::NewLinesShown, cx);
15644 }
15645 multi_buffer::Event::ExcerptsRemoved { ids } => {
15646 self.refresh_inlay_hints(InlayHintRefreshReason::ExcerptsRemoved(ids.clone()), cx);
15647 let buffer = self.buffer.read(cx);
15648 self.registered_buffers
15649 .retain(|buffer_id, _| buffer.buffer(*buffer_id).is_some());
15650 jsx_tag_auto_close::refresh_enabled_in_any_buffer(self, multibuffer, cx);
15651 cx.emit(EditorEvent::ExcerptsRemoved { ids: ids.clone() })
15652 }
15653 multi_buffer::Event::ExcerptsEdited {
15654 excerpt_ids,
15655 buffer_ids,
15656 } => {
15657 self.display_map.update(cx, |map, cx| {
15658 map.unfold_buffers(buffer_ids.iter().copied(), cx)
15659 });
15660 cx.emit(EditorEvent::ExcerptsEdited {
15661 ids: excerpt_ids.clone(),
15662 })
15663 }
15664 multi_buffer::Event::ExcerptsExpanded { ids } => {
15665 self.refresh_inlay_hints(InlayHintRefreshReason::NewLinesShown, cx);
15666 cx.emit(EditorEvent::ExcerptsExpanded { ids: ids.clone() })
15667 }
15668 multi_buffer::Event::Reparsed(buffer_id) => {
15669 self.tasks_update_task = Some(self.refresh_runnables(window, cx));
15670 jsx_tag_auto_close::refresh_enabled_in_any_buffer(self, multibuffer, cx);
15671
15672 cx.emit(EditorEvent::Reparsed(*buffer_id));
15673 }
15674 multi_buffer::Event::DiffHunksToggled => {
15675 self.tasks_update_task = Some(self.refresh_runnables(window, cx));
15676 }
15677 multi_buffer::Event::LanguageChanged(buffer_id) => {
15678 linked_editing_ranges::refresh_linked_ranges(self, window, cx);
15679 jsx_tag_auto_close::refresh_enabled_in_any_buffer(self, multibuffer, cx);
15680 cx.emit(EditorEvent::Reparsed(*buffer_id));
15681 cx.notify();
15682 }
15683 multi_buffer::Event::DirtyChanged => cx.emit(EditorEvent::DirtyChanged),
15684 multi_buffer::Event::Saved => cx.emit(EditorEvent::Saved),
15685 multi_buffer::Event::FileHandleChanged
15686 | multi_buffer::Event::Reloaded
15687 | multi_buffer::Event::BufferDiffChanged => cx.emit(EditorEvent::TitleChanged),
15688 multi_buffer::Event::Closed => cx.emit(EditorEvent::Closed),
15689 multi_buffer::Event::DiagnosticsUpdated => {
15690 self.refresh_active_diagnostics(cx);
15691 self.refresh_inline_diagnostics(true, window, cx);
15692 self.scrollbar_marker_state.dirty = true;
15693 cx.notify();
15694 }
15695 _ => {}
15696 };
15697 }
15698
15699 fn on_display_map_changed(
15700 &mut self,
15701 _: Entity<DisplayMap>,
15702 _: &mut Window,
15703 cx: &mut Context<Self>,
15704 ) {
15705 cx.notify();
15706 }
15707
15708 fn settings_changed(&mut self, window: &mut Window, cx: &mut Context<Self>) {
15709 self.tasks_update_task = Some(self.refresh_runnables(window, cx));
15710 self.update_edit_prediction_settings(cx);
15711 self.refresh_inline_completion(true, false, window, cx);
15712 self.refresh_inlay_hints(
15713 InlayHintRefreshReason::SettingsChange(inlay_hint_settings(
15714 self.selections.newest_anchor().head(),
15715 &self.buffer.read(cx).snapshot(cx),
15716 cx,
15717 )),
15718 cx,
15719 );
15720
15721 let old_cursor_shape = self.cursor_shape;
15722
15723 {
15724 let editor_settings = EditorSettings::get_global(cx);
15725 self.scroll_manager.vertical_scroll_margin = editor_settings.vertical_scroll_margin;
15726 self.show_breadcrumbs = editor_settings.toolbar.breadcrumbs;
15727 self.cursor_shape = editor_settings.cursor_shape.unwrap_or_default();
15728 }
15729
15730 if old_cursor_shape != self.cursor_shape {
15731 cx.emit(EditorEvent::CursorShapeChanged);
15732 }
15733
15734 let project_settings = ProjectSettings::get_global(cx);
15735 self.serialize_dirty_buffers = project_settings.session.restore_unsaved_buffers;
15736
15737 if self.mode == EditorMode::Full {
15738 let show_inline_diagnostics = project_settings.diagnostics.inline.enabled;
15739 let inline_blame_enabled = project_settings.git.inline_blame_enabled();
15740 if self.show_inline_diagnostics != show_inline_diagnostics {
15741 self.show_inline_diagnostics = show_inline_diagnostics;
15742 self.refresh_inline_diagnostics(false, window, cx);
15743 }
15744
15745 if self.git_blame_inline_enabled != inline_blame_enabled {
15746 self.toggle_git_blame_inline_internal(false, window, cx);
15747 }
15748 }
15749
15750 cx.notify();
15751 }
15752
15753 pub fn set_searchable(&mut self, searchable: bool) {
15754 self.searchable = searchable;
15755 }
15756
15757 pub fn searchable(&self) -> bool {
15758 self.searchable
15759 }
15760
15761 fn open_proposed_changes_editor(
15762 &mut self,
15763 _: &OpenProposedChangesEditor,
15764 window: &mut Window,
15765 cx: &mut Context<Self>,
15766 ) {
15767 let Some(workspace) = self.workspace() else {
15768 cx.propagate();
15769 return;
15770 };
15771
15772 let selections = self.selections.all::<usize>(cx);
15773 let multi_buffer = self.buffer.read(cx);
15774 let multi_buffer_snapshot = multi_buffer.snapshot(cx);
15775 let mut new_selections_by_buffer = HashMap::default();
15776 for selection in selections {
15777 for (buffer, range, _) in
15778 multi_buffer_snapshot.range_to_buffer_ranges(selection.start..selection.end)
15779 {
15780 let mut range = range.to_point(buffer);
15781 range.start.column = 0;
15782 range.end.column = buffer.line_len(range.end.row);
15783 new_selections_by_buffer
15784 .entry(multi_buffer.buffer(buffer.remote_id()).unwrap())
15785 .or_insert(Vec::new())
15786 .push(range)
15787 }
15788 }
15789
15790 let proposed_changes_buffers = new_selections_by_buffer
15791 .into_iter()
15792 .map(|(buffer, ranges)| ProposedChangeLocation { buffer, ranges })
15793 .collect::<Vec<_>>();
15794 let proposed_changes_editor = cx.new(|cx| {
15795 ProposedChangesEditor::new(
15796 "Proposed changes",
15797 proposed_changes_buffers,
15798 self.project.clone(),
15799 window,
15800 cx,
15801 )
15802 });
15803
15804 window.defer(cx, move |window, cx| {
15805 workspace.update(cx, |workspace, cx| {
15806 workspace.active_pane().update(cx, |pane, cx| {
15807 pane.add_item(
15808 Box::new(proposed_changes_editor),
15809 true,
15810 true,
15811 None,
15812 window,
15813 cx,
15814 );
15815 });
15816 });
15817 });
15818 }
15819
15820 pub fn open_excerpts_in_split(
15821 &mut self,
15822 _: &OpenExcerptsSplit,
15823 window: &mut Window,
15824 cx: &mut Context<Self>,
15825 ) {
15826 self.open_excerpts_common(None, true, window, cx)
15827 }
15828
15829 pub fn open_excerpts(&mut self, _: &OpenExcerpts, window: &mut Window, cx: &mut Context<Self>) {
15830 self.open_excerpts_common(None, false, window, cx)
15831 }
15832
15833 fn open_excerpts_common(
15834 &mut self,
15835 jump_data: Option<JumpData>,
15836 split: bool,
15837 window: &mut Window,
15838 cx: &mut Context<Self>,
15839 ) {
15840 let Some(workspace) = self.workspace() else {
15841 cx.propagate();
15842 return;
15843 };
15844
15845 if self.buffer.read(cx).is_singleton() {
15846 cx.propagate();
15847 return;
15848 }
15849
15850 let mut new_selections_by_buffer = HashMap::default();
15851 match &jump_data {
15852 Some(JumpData::MultiBufferPoint {
15853 excerpt_id,
15854 position,
15855 anchor,
15856 line_offset_from_top,
15857 }) => {
15858 let multi_buffer_snapshot = self.buffer.read(cx).snapshot(cx);
15859 if let Some(buffer) = multi_buffer_snapshot
15860 .buffer_id_for_excerpt(*excerpt_id)
15861 .and_then(|buffer_id| self.buffer.read(cx).buffer(buffer_id))
15862 {
15863 let buffer_snapshot = buffer.read(cx).snapshot();
15864 let jump_to_point = if buffer_snapshot.can_resolve(anchor) {
15865 language::ToPoint::to_point(anchor, &buffer_snapshot)
15866 } else {
15867 buffer_snapshot.clip_point(*position, Bias::Left)
15868 };
15869 let jump_to_offset = buffer_snapshot.point_to_offset(jump_to_point);
15870 new_selections_by_buffer.insert(
15871 buffer,
15872 (
15873 vec![jump_to_offset..jump_to_offset],
15874 Some(*line_offset_from_top),
15875 ),
15876 );
15877 }
15878 }
15879 Some(JumpData::MultiBufferRow {
15880 row,
15881 line_offset_from_top,
15882 }) => {
15883 let point = MultiBufferPoint::new(row.0, 0);
15884 if let Some((buffer, buffer_point, _)) =
15885 self.buffer.read(cx).point_to_buffer_point(point, cx)
15886 {
15887 let buffer_offset = buffer.read(cx).point_to_offset(buffer_point);
15888 new_selections_by_buffer
15889 .entry(buffer)
15890 .or_insert((Vec::new(), Some(*line_offset_from_top)))
15891 .0
15892 .push(buffer_offset..buffer_offset)
15893 }
15894 }
15895 None => {
15896 let selections = self.selections.all::<usize>(cx);
15897 let multi_buffer = self.buffer.read(cx);
15898 for selection in selections {
15899 for (snapshot, range, _, anchor) in multi_buffer
15900 .snapshot(cx)
15901 .range_to_buffer_ranges_with_deleted_hunks(selection.range())
15902 {
15903 if let Some(anchor) = anchor {
15904 // selection is in a deleted hunk
15905 let Some(buffer_id) = anchor.buffer_id else {
15906 continue;
15907 };
15908 let Some(buffer_handle) = multi_buffer.buffer(buffer_id) else {
15909 continue;
15910 };
15911 let offset = text::ToOffset::to_offset(
15912 &anchor.text_anchor,
15913 &buffer_handle.read(cx).snapshot(),
15914 );
15915 let range = offset..offset;
15916 new_selections_by_buffer
15917 .entry(buffer_handle)
15918 .or_insert((Vec::new(), None))
15919 .0
15920 .push(range)
15921 } else {
15922 let Some(buffer_handle) = multi_buffer.buffer(snapshot.remote_id())
15923 else {
15924 continue;
15925 };
15926 new_selections_by_buffer
15927 .entry(buffer_handle)
15928 .or_insert((Vec::new(), None))
15929 .0
15930 .push(range)
15931 }
15932 }
15933 }
15934 }
15935 }
15936
15937 if new_selections_by_buffer.is_empty() {
15938 return;
15939 }
15940
15941 // We defer the pane interaction because we ourselves are a workspace item
15942 // and activating a new item causes the pane to call a method on us reentrantly,
15943 // which panics if we're on the stack.
15944 window.defer(cx, move |window, cx| {
15945 workspace.update(cx, |workspace, cx| {
15946 let pane = if split {
15947 workspace.adjacent_pane(window, cx)
15948 } else {
15949 workspace.active_pane().clone()
15950 };
15951
15952 for (buffer, (ranges, scroll_offset)) in new_selections_by_buffer {
15953 let editor = buffer
15954 .read(cx)
15955 .file()
15956 .is_none()
15957 .then(|| {
15958 // Handle file-less buffers separately: those are not really the project items, so won't have a project path or entity id,
15959 // so `workspace.open_project_item` will never find them, always opening a new editor.
15960 // Instead, we try to activate the existing editor in the pane first.
15961 let (editor, pane_item_index) =
15962 pane.read(cx).items().enumerate().find_map(|(i, item)| {
15963 let editor = item.downcast::<Editor>()?;
15964 let singleton_buffer =
15965 editor.read(cx).buffer().read(cx).as_singleton()?;
15966 if singleton_buffer == buffer {
15967 Some((editor, i))
15968 } else {
15969 None
15970 }
15971 })?;
15972 pane.update(cx, |pane, cx| {
15973 pane.activate_item(pane_item_index, true, true, window, cx)
15974 });
15975 Some(editor)
15976 })
15977 .flatten()
15978 .unwrap_or_else(|| {
15979 workspace.open_project_item::<Self>(
15980 pane.clone(),
15981 buffer,
15982 true,
15983 true,
15984 window,
15985 cx,
15986 )
15987 });
15988
15989 editor.update(cx, |editor, cx| {
15990 let autoscroll = match scroll_offset {
15991 Some(scroll_offset) => Autoscroll::top_relative(scroll_offset as usize),
15992 None => Autoscroll::newest(),
15993 };
15994 let nav_history = editor.nav_history.take();
15995 editor.change_selections(Some(autoscroll), window, cx, |s| {
15996 s.select_ranges(ranges);
15997 });
15998 editor.nav_history = nav_history;
15999 });
16000 }
16001 })
16002 });
16003 }
16004
16005 fn marked_text_ranges(&self, cx: &App) -> Option<Vec<Range<OffsetUtf16>>> {
16006 let snapshot = self.buffer.read(cx).read(cx);
16007 let (_, ranges) = self.text_highlights::<InputComposition>(cx)?;
16008 Some(
16009 ranges
16010 .iter()
16011 .map(move |range| {
16012 range.start.to_offset_utf16(&snapshot)..range.end.to_offset_utf16(&snapshot)
16013 })
16014 .collect(),
16015 )
16016 }
16017
16018 fn selection_replacement_ranges(
16019 &self,
16020 range: Range<OffsetUtf16>,
16021 cx: &mut App,
16022 ) -> Vec<Range<OffsetUtf16>> {
16023 let selections = self.selections.all::<OffsetUtf16>(cx);
16024 let newest_selection = selections
16025 .iter()
16026 .max_by_key(|selection| selection.id)
16027 .unwrap();
16028 let start_delta = range.start.0 as isize - newest_selection.start.0 as isize;
16029 let end_delta = range.end.0 as isize - newest_selection.end.0 as isize;
16030 let snapshot = self.buffer.read(cx).read(cx);
16031 selections
16032 .into_iter()
16033 .map(|mut selection| {
16034 selection.start.0 =
16035 (selection.start.0 as isize).saturating_add(start_delta) as usize;
16036 selection.end.0 = (selection.end.0 as isize).saturating_add(end_delta) as usize;
16037 snapshot.clip_offset_utf16(selection.start, Bias::Left)
16038 ..snapshot.clip_offset_utf16(selection.end, Bias::Right)
16039 })
16040 .collect()
16041 }
16042
16043 fn report_editor_event(
16044 &self,
16045 event_type: &'static str,
16046 file_extension: Option<String>,
16047 cx: &App,
16048 ) {
16049 if cfg!(any(test, feature = "test-support")) {
16050 return;
16051 }
16052
16053 let Some(project) = &self.project else { return };
16054
16055 // If None, we are in a file without an extension
16056 let file = self
16057 .buffer
16058 .read(cx)
16059 .as_singleton()
16060 .and_then(|b| b.read(cx).file());
16061 let file_extension = file_extension.or(file
16062 .as_ref()
16063 .and_then(|file| Path::new(file.file_name(cx)).extension())
16064 .and_then(|e| e.to_str())
16065 .map(|a| a.to_string()));
16066
16067 let vim_mode = cx
16068 .global::<SettingsStore>()
16069 .raw_user_settings()
16070 .get("vim_mode")
16071 == Some(&serde_json::Value::Bool(true));
16072
16073 let edit_predictions_provider = all_language_settings(file, cx).edit_predictions.provider;
16074 let copilot_enabled = edit_predictions_provider
16075 == language::language_settings::EditPredictionProvider::Copilot;
16076 let copilot_enabled_for_language = self
16077 .buffer
16078 .read(cx)
16079 .language_settings(cx)
16080 .show_edit_predictions;
16081
16082 let project = project.read(cx);
16083 telemetry::event!(
16084 event_type,
16085 file_extension,
16086 vim_mode,
16087 copilot_enabled,
16088 copilot_enabled_for_language,
16089 edit_predictions_provider,
16090 is_via_ssh = project.is_via_ssh(),
16091 );
16092 }
16093
16094 /// Copy the highlighted chunks to the clipboard as JSON. The format is an array of lines,
16095 /// with each line being an array of {text, highlight} objects.
16096 fn copy_highlight_json(
16097 &mut self,
16098 _: &CopyHighlightJson,
16099 window: &mut Window,
16100 cx: &mut Context<Self>,
16101 ) {
16102 #[derive(Serialize)]
16103 struct Chunk<'a> {
16104 text: String,
16105 highlight: Option<&'a str>,
16106 }
16107
16108 let snapshot = self.buffer.read(cx).snapshot(cx);
16109 let range = self
16110 .selected_text_range(false, window, cx)
16111 .and_then(|selection| {
16112 if selection.range.is_empty() {
16113 None
16114 } else {
16115 Some(selection.range)
16116 }
16117 })
16118 .unwrap_or_else(|| 0..snapshot.len());
16119
16120 let chunks = snapshot.chunks(range, true);
16121 let mut lines = Vec::new();
16122 let mut line: VecDeque<Chunk> = VecDeque::new();
16123
16124 let Some(style) = self.style.as_ref() else {
16125 return;
16126 };
16127
16128 for chunk in chunks {
16129 let highlight = chunk
16130 .syntax_highlight_id
16131 .and_then(|id| id.name(&style.syntax));
16132 let mut chunk_lines = chunk.text.split('\n').peekable();
16133 while let Some(text) = chunk_lines.next() {
16134 let mut merged_with_last_token = false;
16135 if let Some(last_token) = line.back_mut() {
16136 if last_token.highlight == highlight {
16137 last_token.text.push_str(text);
16138 merged_with_last_token = true;
16139 }
16140 }
16141
16142 if !merged_with_last_token {
16143 line.push_back(Chunk {
16144 text: text.into(),
16145 highlight,
16146 });
16147 }
16148
16149 if chunk_lines.peek().is_some() {
16150 if line.len() > 1 && line.front().unwrap().text.is_empty() {
16151 line.pop_front();
16152 }
16153 if line.len() > 1 && line.back().unwrap().text.is_empty() {
16154 line.pop_back();
16155 }
16156
16157 lines.push(mem::take(&mut line));
16158 }
16159 }
16160 }
16161
16162 let Some(lines) = serde_json::to_string_pretty(&lines).log_err() else {
16163 return;
16164 };
16165 cx.write_to_clipboard(ClipboardItem::new_string(lines));
16166 }
16167
16168 pub fn open_context_menu(
16169 &mut self,
16170 _: &OpenContextMenu,
16171 window: &mut Window,
16172 cx: &mut Context<Self>,
16173 ) {
16174 self.request_autoscroll(Autoscroll::newest(), cx);
16175 let position = self.selections.newest_display(cx).start;
16176 mouse_context_menu::deploy_context_menu(self, None, position, window, cx);
16177 }
16178
16179 pub fn inlay_hint_cache(&self) -> &InlayHintCache {
16180 &self.inlay_hint_cache
16181 }
16182
16183 pub fn replay_insert_event(
16184 &mut self,
16185 text: &str,
16186 relative_utf16_range: Option<Range<isize>>,
16187 window: &mut Window,
16188 cx: &mut Context<Self>,
16189 ) {
16190 if !self.input_enabled {
16191 cx.emit(EditorEvent::InputIgnored { text: text.into() });
16192 return;
16193 }
16194 if let Some(relative_utf16_range) = relative_utf16_range {
16195 let selections = self.selections.all::<OffsetUtf16>(cx);
16196 self.change_selections(None, window, cx, |s| {
16197 let new_ranges = selections.into_iter().map(|range| {
16198 let start = OffsetUtf16(
16199 range
16200 .head()
16201 .0
16202 .saturating_add_signed(relative_utf16_range.start),
16203 );
16204 let end = OffsetUtf16(
16205 range
16206 .head()
16207 .0
16208 .saturating_add_signed(relative_utf16_range.end),
16209 );
16210 start..end
16211 });
16212 s.select_ranges(new_ranges);
16213 });
16214 }
16215
16216 self.handle_input(text, window, cx);
16217 }
16218
16219 pub fn supports_inlay_hints(&self, cx: &mut App) -> bool {
16220 let Some(provider) = self.semantics_provider.as_ref() else {
16221 return false;
16222 };
16223
16224 let mut supports = false;
16225 self.buffer().update(cx, |this, cx| {
16226 this.for_each_buffer(|buffer| {
16227 supports |= provider.supports_inlay_hints(buffer, cx);
16228 });
16229 });
16230
16231 supports
16232 }
16233
16234 pub fn is_focused(&self, window: &Window) -> bool {
16235 self.focus_handle.is_focused(window)
16236 }
16237
16238 fn handle_focus(&mut self, window: &mut Window, cx: &mut Context<Self>) {
16239 cx.emit(EditorEvent::Focused);
16240
16241 if let Some(descendant) = self
16242 .last_focused_descendant
16243 .take()
16244 .and_then(|descendant| descendant.upgrade())
16245 {
16246 window.focus(&descendant);
16247 } else {
16248 if let Some(blame) = self.blame.as_ref() {
16249 blame.update(cx, GitBlame::focus)
16250 }
16251
16252 self.blink_manager.update(cx, BlinkManager::enable);
16253 self.show_cursor_names(window, cx);
16254 self.buffer.update(cx, |buffer, cx| {
16255 buffer.finalize_last_transaction(cx);
16256 if self.leader_peer_id.is_none() {
16257 buffer.set_active_selections(
16258 &self.selections.disjoint_anchors(),
16259 self.selections.line_mode,
16260 self.cursor_shape,
16261 cx,
16262 );
16263 }
16264 });
16265 }
16266 }
16267
16268 fn handle_focus_in(&mut self, _: &mut Window, cx: &mut Context<Self>) {
16269 cx.emit(EditorEvent::FocusedIn)
16270 }
16271
16272 fn handle_focus_out(
16273 &mut self,
16274 event: FocusOutEvent,
16275 _window: &mut Window,
16276 cx: &mut Context<Self>,
16277 ) {
16278 if event.blurred != self.focus_handle {
16279 self.last_focused_descendant = Some(event.blurred);
16280 }
16281 self.refresh_inlay_hints(InlayHintRefreshReason::ModifiersChanged(false), cx);
16282 }
16283
16284 pub fn handle_blur(&mut self, window: &mut Window, cx: &mut Context<Self>) {
16285 self.blink_manager.update(cx, BlinkManager::disable);
16286 self.buffer
16287 .update(cx, |buffer, cx| buffer.remove_active_selections(cx));
16288
16289 if let Some(blame) = self.blame.as_ref() {
16290 blame.update(cx, GitBlame::blur)
16291 }
16292 if !self.hover_state.focused(window, cx) {
16293 hide_hover(self, cx);
16294 }
16295 if !self
16296 .context_menu
16297 .borrow()
16298 .as_ref()
16299 .is_some_and(|context_menu| context_menu.focused(window, cx))
16300 {
16301 self.hide_context_menu(window, cx);
16302 }
16303 self.discard_inline_completion(false, cx);
16304 cx.emit(EditorEvent::Blurred);
16305 cx.notify();
16306 }
16307
16308 pub fn register_action<A: Action>(
16309 &mut self,
16310 listener: impl Fn(&A, &mut Window, &mut App) + 'static,
16311 ) -> Subscription {
16312 let id = self.next_editor_action_id.post_inc();
16313 let listener = Arc::new(listener);
16314 self.editor_actions.borrow_mut().insert(
16315 id,
16316 Box::new(move |window, _| {
16317 let listener = listener.clone();
16318 window.on_action(TypeId::of::<A>(), move |action, phase, window, cx| {
16319 let action = action.downcast_ref().unwrap();
16320 if phase == DispatchPhase::Bubble {
16321 listener(action, window, cx)
16322 }
16323 })
16324 }),
16325 );
16326
16327 let editor_actions = self.editor_actions.clone();
16328 Subscription::new(move || {
16329 editor_actions.borrow_mut().remove(&id);
16330 })
16331 }
16332
16333 pub fn file_header_size(&self) -> u32 {
16334 FILE_HEADER_HEIGHT
16335 }
16336
16337 pub fn restore(
16338 &mut self,
16339 revert_changes: HashMap<BufferId, Vec<(Range<text::Anchor>, Rope)>>,
16340 window: &mut Window,
16341 cx: &mut Context<Self>,
16342 ) {
16343 let workspace = self.workspace();
16344 let project = self.project.as_ref();
16345 let save_tasks = self.buffer().update(cx, |multi_buffer, cx| {
16346 let mut tasks = Vec::new();
16347 for (buffer_id, changes) in revert_changes {
16348 if let Some(buffer) = multi_buffer.buffer(buffer_id) {
16349 buffer.update(cx, |buffer, cx| {
16350 buffer.edit(
16351 changes
16352 .into_iter()
16353 .map(|(range, text)| (range, text.to_string())),
16354 None,
16355 cx,
16356 );
16357 });
16358
16359 if let Some(project) =
16360 project.filter(|_| multi_buffer.all_diff_hunks_expanded())
16361 {
16362 project.update(cx, |project, cx| {
16363 tasks.push((buffer.clone(), project.save_buffer(buffer, cx)));
16364 })
16365 }
16366 }
16367 }
16368 tasks
16369 });
16370 cx.spawn_in(window, |_, mut cx| async move {
16371 for (buffer, task) in save_tasks {
16372 let result = task.await;
16373 if result.is_err() {
16374 let Some(path) = buffer
16375 .read_with(&cx, |buffer, cx| buffer.project_path(cx))
16376 .ok()
16377 else {
16378 continue;
16379 };
16380 if let Some((workspace, path)) = workspace.as_ref().zip(path) {
16381 let Some(task) = cx
16382 .update_window_entity(&workspace, |workspace, window, cx| {
16383 workspace
16384 .open_path_preview(path, None, false, false, false, window, cx)
16385 })
16386 .ok()
16387 else {
16388 continue;
16389 };
16390 task.await.log_err();
16391 }
16392 }
16393 }
16394 })
16395 .detach();
16396 self.change_selections(None, window, cx, |selections| selections.refresh());
16397 }
16398
16399 pub fn to_pixel_point(
16400 &self,
16401 source: multi_buffer::Anchor,
16402 editor_snapshot: &EditorSnapshot,
16403 window: &mut Window,
16404 ) -> Option<gpui::Point<Pixels>> {
16405 let source_point = source.to_display_point(editor_snapshot);
16406 self.display_to_pixel_point(source_point, editor_snapshot, window)
16407 }
16408
16409 pub fn display_to_pixel_point(
16410 &self,
16411 source: DisplayPoint,
16412 editor_snapshot: &EditorSnapshot,
16413 window: &mut Window,
16414 ) -> Option<gpui::Point<Pixels>> {
16415 let line_height = self.style()?.text.line_height_in_pixels(window.rem_size());
16416 let text_layout_details = self.text_layout_details(window);
16417 let scroll_top = text_layout_details
16418 .scroll_anchor
16419 .scroll_position(editor_snapshot)
16420 .y;
16421
16422 if source.row().as_f32() < scroll_top.floor() {
16423 return None;
16424 }
16425 let source_x = editor_snapshot.x_for_display_point(source, &text_layout_details);
16426 let source_y = line_height * (source.row().as_f32() - scroll_top);
16427 Some(gpui::Point::new(source_x, source_y))
16428 }
16429
16430 pub fn has_visible_completions_menu(&self) -> bool {
16431 !self.edit_prediction_preview_is_active()
16432 && self.context_menu.borrow().as_ref().map_or(false, |menu| {
16433 menu.visible() && matches!(menu, CodeContextMenu::Completions(_))
16434 })
16435 }
16436
16437 pub fn register_addon<T: Addon>(&mut self, instance: T) {
16438 self.addons
16439 .insert(std::any::TypeId::of::<T>(), Box::new(instance));
16440 }
16441
16442 pub fn unregister_addon<T: Addon>(&mut self) {
16443 self.addons.remove(&std::any::TypeId::of::<T>());
16444 }
16445
16446 pub fn addon<T: Addon>(&self) -> Option<&T> {
16447 let type_id = std::any::TypeId::of::<T>();
16448 self.addons
16449 .get(&type_id)
16450 .and_then(|item| item.to_any().downcast_ref::<T>())
16451 }
16452
16453 fn character_size(&self, window: &mut Window) -> gpui::Size<Pixels> {
16454 let text_layout_details = self.text_layout_details(window);
16455 let style = &text_layout_details.editor_style;
16456 let font_id = window.text_system().resolve_font(&style.text.font());
16457 let font_size = style.text.font_size.to_pixels(window.rem_size());
16458 let line_height = style.text.line_height_in_pixels(window.rem_size());
16459 let em_width = window.text_system().em_width(font_id, font_size).unwrap();
16460
16461 gpui::Size::new(em_width, line_height)
16462 }
16463
16464 pub fn wait_for_diff_to_load(&self) -> Option<Shared<Task<()>>> {
16465 self.load_diff_task.clone()
16466 }
16467
16468 fn read_selections_from_db(
16469 &mut self,
16470 item_id: u64,
16471 workspace_id: WorkspaceId,
16472 window: &mut Window,
16473 cx: &mut Context<Editor>,
16474 ) {
16475 if !self.is_singleton(cx)
16476 || WorkspaceSettings::get(None, cx).restore_on_startup == RestoreOnStartupBehavior::None
16477 {
16478 return;
16479 }
16480 let Some(selections) = DB.get_editor_selections(item_id, workspace_id).log_err() else {
16481 return;
16482 };
16483 if selections.is_empty() {
16484 return;
16485 }
16486
16487 let snapshot = self.buffer.read(cx).snapshot(cx);
16488 self.change_selections(None, window, cx, |s| {
16489 s.select_ranges(selections.into_iter().map(|(start, end)| {
16490 snapshot.clip_offset(start, Bias::Left)..snapshot.clip_offset(end, Bias::Right)
16491 }));
16492 });
16493 }
16494}
16495
16496fn insert_extra_newline_brackets(
16497 buffer: &MultiBufferSnapshot,
16498 range: Range<usize>,
16499 language: &language::LanguageScope,
16500) -> bool {
16501 let leading_whitespace_len = buffer
16502 .reversed_chars_at(range.start)
16503 .take_while(|c| c.is_whitespace() && *c != '\n')
16504 .map(|c| c.len_utf8())
16505 .sum::<usize>();
16506 let trailing_whitespace_len = buffer
16507 .chars_at(range.end)
16508 .take_while(|c| c.is_whitespace() && *c != '\n')
16509 .map(|c| c.len_utf8())
16510 .sum::<usize>();
16511 let range = range.start - leading_whitespace_len..range.end + trailing_whitespace_len;
16512
16513 language.brackets().any(|(pair, enabled)| {
16514 let pair_start = pair.start.trim_end();
16515 let pair_end = pair.end.trim_start();
16516
16517 enabled
16518 && pair.newline
16519 && buffer.contains_str_at(range.end, pair_end)
16520 && buffer.contains_str_at(range.start.saturating_sub(pair_start.len()), pair_start)
16521 })
16522}
16523
16524fn insert_extra_newline_tree_sitter(buffer: &MultiBufferSnapshot, range: Range<usize>) -> bool {
16525 let (buffer, range) = match buffer.range_to_buffer_ranges(range).as_slice() {
16526 [(buffer, range, _)] => (*buffer, range.clone()),
16527 _ => return false,
16528 };
16529 let pair = {
16530 let mut result: Option<BracketMatch> = None;
16531
16532 for pair in buffer
16533 .all_bracket_ranges(range.clone())
16534 .filter(move |pair| {
16535 pair.open_range.start <= range.start && pair.close_range.end >= range.end
16536 })
16537 {
16538 let len = pair.close_range.end - pair.open_range.start;
16539
16540 if let Some(existing) = &result {
16541 let existing_len = existing.close_range.end - existing.open_range.start;
16542 if len > existing_len {
16543 continue;
16544 }
16545 }
16546
16547 result = Some(pair);
16548 }
16549
16550 result
16551 };
16552 let Some(pair) = pair else {
16553 return false;
16554 };
16555 pair.newline_only
16556 && buffer
16557 .chars_for_range(pair.open_range.end..range.start)
16558 .chain(buffer.chars_for_range(range.end..pair.close_range.start))
16559 .all(|c| c.is_whitespace() && c != '\n')
16560}
16561
16562fn get_uncommitted_diff_for_buffer(
16563 project: &Entity<Project>,
16564 buffers: impl IntoIterator<Item = Entity<Buffer>>,
16565 buffer: Entity<MultiBuffer>,
16566 cx: &mut App,
16567) -> Task<()> {
16568 let mut tasks = Vec::new();
16569 project.update(cx, |project, cx| {
16570 for buffer in buffers {
16571 tasks.push(project.open_uncommitted_diff(buffer.clone(), cx))
16572 }
16573 });
16574 cx.spawn(|mut cx| async move {
16575 let diffs = future::join_all(tasks).await;
16576 buffer
16577 .update(&mut cx, |buffer, cx| {
16578 for diff in diffs.into_iter().flatten() {
16579 buffer.add_diff(diff, cx);
16580 }
16581 })
16582 .ok();
16583 })
16584}
16585
16586fn char_len_with_expanded_tabs(offset: usize, text: &str, tab_size: NonZeroU32) -> usize {
16587 let tab_size = tab_size.get() as usize;
16588 let mut width = offset;
16589
16590 for ch in text.chars() {
16591 width += if ch == '\t' {
16592 tab_size - (width % tab_size)
16593 } else {
16594 1
16595 };
16596 }
16597
16598 width - offset
16599}
16600
16601#[cfg(test)]
16602mod tests {
16603 use super::*;
16604
16605 #[test]
16606 fn test_string_size_with_expanded_tabs() {
16607 let nz = |val| NonZeroU32::new(val).unwrap();
16608 assert_eq!(char_len_with_expanded_tabs(0, "", nz(4)), 0);
16609 assert_eq!(char_len_with_expanded_tabs(0, "hello", nz(4)), 5);
16610 assert_eq!(char_len_with_expanded_tabs(0, "\thello", nz(4)), 9);
16611 assert_eq!(char_len_with_expanded_tabs(0, "abc\tab", nz(4)), 6);
16612 assert_eq!(char_len_with_expanded_tabs(0, "hello\t", nz(4)), 8);
16613 assert_eq!(char_len_with_expanded_tabs(0, "\t\t", nz(8)), 16);
16614 assert_eq!(char_len_with_expanded_tabs(0, "x\t", nz(8)), 8);
16615 assert_eq!(char_len_with_expanded_tabs(7, "x\t", nz(8)), 9);
16616 }
16617}
16618
16619/// Tokenizes a string into runs of text that should stick together, or that is whitespace.
16620struct WordBreakingTokenizer<'a> {
16621 input: &'a str,
16622}
16623
16624impl<'a> WordBreakingTokenizer<'a> {
16625 fn new(input: &'a str) -> Self {
16626 Self { input }
16627 }
16628}
16629
16630fn is_char_ideographic(ch: char) -> bool {
16631 use unicode_script::Script::*;
16632 use unicode_script::UnicodeScript;
16633 matches!(ch.script(), Han | Tangut | Yi)
16634}
16635
16636fn is_grapheme_ideographic(text: &str) -> bool {
16637 text.chars().any(is_char_ideographic)
16638}
16639
16640fn is_grapheme_whitespace(text: &str) -> bool {
16641 text.chars().any(|x| x.is_whitespace())
16642}
16643
16644fn should_stay_with_preceding_ideograph(text: &str) -> bool {
16645 text.chars().next().map_or(false, |ch| {
16646 matches!(ch, '。' | '、' | ',' | '?' | '!' | ':' | ';' | '…')
16647 })
16648}
16649
16650#[derive(PartialEq, Eq, Debug, Clone, Copy)]
16651struct WordBreakToken<'a> {
16652 token: &'a str,
16653 grapheme_len: usize,
16654 is_whitespace: bool,
16655}
16656
16657impl<'a> Iterator for WordBreakingTokenizer<'a> {
16658 /// Yields a span, the count of graphemes in the token, and whether it was
16659 /// whitespace. Note that it also breaks at word boundaries.
16660 type Item = WordBreakToken<'a>;
16661
16662 fn next(&mut self) -> Option<Self::Item> {
16663 use unicode_segmentation::UnicodeSegmentation;
16664 if self.input.is_empty() {
16665 return None;
16666 }
16667
16668 let mut iter = self.input.graphemes(true).peekable();
16669 let mut offset = 0;
16670 let mut graphemes = 0;
16671 if let Some(first_grapheme) = iter.next() {
16672 let is_whitespace = is_grapheme_whitespace(first_grapheme);
16673 offset += first_grapheme.len();
16674 graphemes += 1;
16675 if is_grapheme_ideographic(first_grapheme) && !is_whitespace {
16676 if let Some(grapheme) = iter.peek().copied() {
16677 if should_stay_with_preceding_ideograph(grapheme) {
16678 offset += grapheme.len();
16679 graphemes += 1;
16680 }
16681 }
16682 } else {
16683 let mut words = self.input[offset..].split_word_bound_indices().peekable();
16684 let mut next_word_bound = words.peek().copied();
16685 if next_word_bound.map_or(false, |(i, _)| i == 0) {
16686 next_word_bound = words.next();
16687 }
16688 while let Some(grapheme) = iter.peek().copied() {
16689 if next_word_bound.map_or(false, |(i, _)| i == offset) {
16690 break;
16691 };
16692 if is_grapheme_whitespace(grapheme) != is_whitespace {
16693 break;
16694 };
16695 offset += grapheme.len();
16696 graphemes += 1;
16697 iter.next();
16698 }
16699 }
16700 let token = &self.input[..offset];
16701 self.input = &self.input[offset..];
16702 if is_whitespace {
16703 Some(WordBreakToken {
16704 token: " ",
16705 grapheme_len: 1,
16706 is_whitespace: true,
16707 })
16708 } else {
16709 Some(WordBreakToken {
16710 token,
16711 grapheme_len: graphemes,
16712 is_whitespace: false,
16713 })
16714 }
16715 } else {
16716 None
16717 }
16718 }
16719}
16720
16721#[test]
16722fn test_word_breaking_tokenizer() {
16723 let tests: &[(&str, &[(&str, usize, bool)])] = &[
16724 ("", &[]),
16725 (" ", &[(" ", 1, true)]),
16726 ("Ʒ", &[("Ʒ", 1, false)]),
16727 ("Ǽ", &[("Ǽ", 1, false)]),
16728 ("⋑", &[("⋑", 1, false)]),
16729 ("⋑⋑", &[("⋑⋑", 2, false)]),
16730 (
16731 "原理,进而",
16732 &[
16733 ("原", 1, false),
16734 ("理,", 2, false),
16735 ("进", 1, false),
16736 ("而", 1, false),
16737 ],
16738 ),
16739 (
16740 "hello world",
16741 &[("hello", 5, false), (" ", 1, true), ("world", 5, false)],
16742 ),
16743 (
16744 "hello, world",
16745 &[("hello,", 6, false), (" ", 1, true), ("world", 5, false)],
16746 ),
16747 (
16748 " hello world",
16749 &[
16750 (" ", 1, true),
16751 ("hello", 5, false),
16752 (" ", 1, true),
16753 ("world", 5, false),
16754 ],
16755 ),
16756 (
16757 "这是什么 \n 钢笔",
16758 &[
16759 ("这", 1, false),
16760 ("是", 1, false),
16761 ("什", 1, false),
16762 ("么", 1, false),
16763 (" ", 1, true),
16764 ("钢", 1, false),
16765 ("笔", 1, false),
16766 ],
16767 ),
16768 (" mutton", &[(" ", 1, true), ("mutton", 6, false)]),
16769 ];
16770
16771 for (input, result) in tests {
16772 assert_eq!(
16773 WordBreakingTokenizer::new(input).collect::<Vec<_>>(),
16774 result
16775 .iter()
16776 .copied()
16777 .map(|(token, grapheme_len, is_whitespace)| WordBreakToken {
16778 token,
16779 grapheme_len,
16780 is_whitespace,
16781 })
16782 .collect::<Vec<_>>()
16783 );
16784 }
16785}
16786
16787fn wrap_with_prefix(
16788 line_prefix: String,
16789 unwrapped_text: String,
16790 wrap_column: usize,
16791 tab_size: NonZeroU32,
16792) -> String {
16793 let line_prefix_len = char_len_with_expanded_tabs(0, &line_prefix, tab_size);
16794 let mut wrapped_text = String::new();
16795 let mut current_line = line_prefix.clone();
16796
16797 let tokenizer = WordBreakingTokenizer::new(&unwrapped_text);
16798 let mut current_line_len = line_prefix_len;
16799 for WordBreakToken {
16800 token,
16801 grapheme_len,
16802 is_whitespace,
16803 } in tokenizer
16804 {
16805 if current_line_len + grapheme_len > wrap_column && current_line_len != line_prefix_len {
16806 wrapped_text.push_str(current_line.trim_end());
16807 wrapped_text.push('\n');
16808 current_line.truncate(line_prefix.len());
16809 current_line_len = line_prefix_len;
16810 if !is_whitespace {
16811 current_line.push_str(token);
16812 current_line_len += grapheme_len;
16813 }
16814 } else if !is_whitespace {
16815 current_line.push_str(token);
16816 current_line_len += grapheme_len;
16817 } else if current_line_len != line_prefix_len {
16818 current_line.push(' ');
16819 current_line_len += 1;
16820 }
16821 }
16822
16823 if !current_line.is_empty() {
16824 wrapped_text.push_str(¤t_line);
16825 }
16826 wrapped_text
16827}
16828
16829#[test]
16830fn test_wrap_with_prefix() {
16831 assert_eq!(
16832 wrap_with_prefix(
16833 "# ".to_string(),
16834 "abcdefg".to_string(),
16835 4,
16836 NonZeroU32::new(4).unwrap()
16837 ),
16838 "# abcdefg"
16839 );
16840 assert_eq!(
16841 wrap_with_prefix(
16842 "".to_string(),
16843 "\thello world".to_string(),
16844 8,
16845 NonZeroU32::new(4).unwrap()
16846 ),
16847 "hello\nworld"
16848 );
16849 assert_eq!(
16850 wrap_with_prefix(
16851 "// ".to_string(),
16852 "xx \nyy zz aa bb cc".to_string(),
16853 12,
16854 NonZeroU32::new(4).unwrap()
16855 ),
16856 "// xx yy zz\n// aa bb cc"
16857 );
16858 assert_eq!(
16859 wrap_with_prefix(
16860 String::new(),
16861 "这是什么 \n 钢笔".to_string(),
16862 3,
16863 NonZeroU32::new(4).unwrap()
16864 ),
16865 "这是什\n么 钢\n笔"
16866 );
16867}
16868
16869pub trait CollaborationHub {
16870 fn collaborators<'a>(&self, cx: &'a App) -> &'a HashMap<PeerId, Collaborator>;
16871 fn user_participant_indices<'a>(&self, cx: &'a App) -> &'a HashMap<u64, ParticipantIndex>;
16872 fn user_names(&self, cx: &App) -> HashMap<u64, SharedString>;
16873}
16874
16875impl CollaborationHub for Entity<Project> {
16876 fn collaborators<'a>(&self, cx: &'a App) -> &'a HashMap<PeerId, Collaborator> {
16877 self.read(cx).collaborators()
16878 }
16879
16880 fn user_participant_indices<'a>(&self, cx: &'a App) -> &'a HashMap<u64, ParticipantIndex> {
16881 self.read(cx).user_store().read(cx).participant_indices()
16882 }
16883
16884 fn user_names(&self, cx: &App) -> HashMap<u64, SharedString> {
16885 let this = self.read(cx);
16886 let user_ids = this.collaborators().values().map(|c| c.user_id);
16887 this.user_store().read_with(cx, |user_store, cx| {
16888 user_store.participant_names(user_ids, cx)
16889 })
16890 }
16891}
16892
16893pub trait SemanticsProvider {
16894 fn hover(
16895 &self,
16896 buffer: &Entity<Buffer>,
16897 position: text::Anchor,
16898 cx: &mut App,
16899 ) -> Option<Task<Vec<project::Hover>>>;
16900
16901 fn inlay_hints(
16902 &self,
16903 buffer_handle: Entity<Buffer>,
16904 range: Range<text::Anchor>,
16905 cx: &mut App,
16906 ) -> Option<Task<anyhow::Result<Vec<InlayHint>>>>;
16907
16908 fn resolve_inlay_hint(
16909 &self,
16910 hint: InlayHint,
16911 buffer_handle: Entity<Buffer>,
16912 server_id: LanguageServerId,
16913 cx: &mut App,
16914 ) -> Option<Task<anyhow::Result<InlayHint>>>;
16915
16916 fn supports_inlay_hints(&self, buffer: &Entity<Buffer>, cx: &mut App) -> bool;
16917
16918 fn document_highlights(
16919 &self,
16920 buffer: &Entity<Buffer>,
16921 position: text::Anchor,
16922 cx: &mut App,
16923 ) -> Option<Task<Result<Vec<DocumentHighlight>>>>;
16924
16925 fn definitions(
16926 &self,
16927 buffer: &Entity<Buffer>,
16928 position: text::Anchor,
16929 kind: GotoDefinitionKind,
16930 cx: &mut App,
16931 ) -> Option<Task<Result<Vec<LocationLink>>>>;
16932
16933 fn range_for_rename(
16934 &self,
16935 buffer: &Entity<Buffer>,
16936 position: text::Anchor,
16937 cx: &mut App,
16938 ) -> Option<Task<Result<Option<Range<text::Anchor>>>>>;
16939
16940 fn perform_rename(
16941 &self,
16942 buffer: &Entity<Buffer>,
16943 position: text::Anchor,
16944 new_name: String,
16945 cx: &mut App,
16946 ) -> Option<Task<Result<ProjectTransaction>>>;
16947}
16948
16949pub trait CompletionProvider {
16950 fn completions(
16951 &self,
16952 buffer: &Entity<Buffer>,
16953 buffer_position: text::Anchor,
16954 trigger: CompletionContext,
16955 window: &mut Window,
16956 cx: &mut Context<Editor>,
16957 ) -> Task<Result<Option<Vec<Completion>>>>;
16958
16959 fn resolve_completions(
16960 &self,
16961 buffer: Entity<Buffer>,
16962 completion_indices: Vec<usize>,
16963 completions: Rc<RefCell<Box<[Completion]>>>,
16964 cx: &mut Context<Editor>,
16965 ) -> Task<Result<bool>>;
16966
16967 fn apply_additional_edits_for_completion(
16968 &self,
16969 _buffer: Entity<Buffer>,
16970 _completions: Rc<RefCell<Box<[Completion]>>>,
16971 _completion_index: usize,
16972 _push_to_history: bool,
16973 _cx: &mut Context<Editor>,
16974 ) -> Task<Result<Option<language::Transaction>>> {
16975 Task::ready(Ok(None))
16976 }
16977
16978 fn is_completion_trigger(
16979 &self,
16980 buffer: &Entity<Buffer>,
16981 position: language::Anchor,
16982 text: &str,
16983 trigger_in_words: bool,
16984 cx: &mut Context<Editor>,
16985 ) -> bool;
16986
16987 fn sort_completions(&self) -> bool {
16988 true
16989 }
16990}
16991
16992pub trait CodeActionProvider {
16993 fn id(&self) -> Arc<str>;
16994
16995 fn code_actions(
16996 &self,
16997 buffer: &Entity<Buffer>,
16998 range: Range<text::Anchor>,
16999 window: &mut Window,
17000 cx: &mut App,
17001 ) -> Task<Result<Vec<CodeAction>>>;
17002
17003 fn apply_code_action(
17004 &self,
17005 buffer_handle: Entity<Buffer>,
17006 action: CodeAction,
17007 excerpt_id: ExcerptId,
17008 push_to_history: bool,
17009 window: &mut Window,
17010 cx: &mut App,
17011 ) -> Task<Result<ProjectTransaction>>;
17012}
17013
17014impl CodeActionProvider for Entity<Project> {
17015 fn id(&self) -> Arc<str> {
17016 "project".into()
17017 }
17018
17019 fn code_actions(
17020 &self,
17021 buffer: &Entity<Buffer>,
17022 range: Range<text::Anchor>,
17023 _window: &mut Window,
17024 cx: &mut App,
17025 ) -> Task<Result<Vec<CodeAction>>> {
17026 self.update(cx, |project, cx| {
17027 project.code_actions(buffer, range, None, cx)
17028 })
17029 }
17030
17031 fn apply_code_action(
17032 &self,
17033 buffer_handle: Entity<Buffer>,
17034 action: CodeAction,
17035 _excerpt_id: ExcerptId,
17036 push_to_history: bool,
17037 _window: &mut Window,
17038 cx: &mut App,
17039 ) -> Task<Result<ProjectTransaction>> {
17040 self.update(cx, |project, cx| {
17041 project.apply_code_action(buffer_handle, action, push_to_history, cx)
17042 })
17043 }
17044}
17045
17046fn snippet_completions(
17047 project: &Project,
17048 buffer: &Entity<Buffer>,
17049 buffer_position: text::Anchor,
17050 cx: &mut App,
17051) -> Task<Result<Vec<Completion>>> {
17052 let language = buffer.read(cx).language_at(buffer_position);
17053 let language_name = language.as_ref().map(|language| language.lsp_id());
17054 let snippet_store = project.snippets().read(cx);
17055 let snippets = snippet_store.snippets_for(language_name, cx);
17056
17057 if snippets.is_empty() {
17058 return Task::ready(Ok(vec![]));
17059 }
17060 let snapshot = buffer.read(cx).text_snapshot();
17061 let chars: String = snapshot
17062 .reversed_chars_for_range(text::Anchor::MIN..buffer_position)
17063 .collect();
17064
17065 let scope = language.map(|language| language.default_scope());
17066 let executor = cx.background_executor().clone();
17067
17068 cx.background_spawn(async move {
17069 let classifier = CharClassifier::new(scope).for_completion(true);
17070 let mut last_word = chars
17071 .chars()
17072 .take_while(|c| classifier.is_word(*c))
17073 .collect::<String>();
17074 last_word = last_word.chars().rev().collect();
17075
17076 if last_word.is_empty() {
17077 return Ok(vec![]);
17078 }
17079
17080 let as_offset = text::ToOffset::to_offset(&buffer_position, &snapshot);
17081 let to_lsp = |point: &text::Anchor| {
17082 let end = text::ToPointUtf16::to_point_utf16(point, &snapshot);
17083 point_to_lsp(end)
17084 };
17085 let lsp_end = to_lsp(&buffer_position);
17086
17087 let candidates = snippets
17088 .iter()
17089 .enumerate()
17090 .flat_map(|(ix, snippet)| {
17091 snippet
17092 .prefix
17093 .iter()
17094 .map(move |prefix| StringMatchCandidate::new(ix, &prefix))
17095 })
17096 .collect::<Vec<StringMatchCandidate>>();
17097
17098 let mut matches = fuzzy::match_strings(
17099 &candidates,
17100 &last_word,
17101 last_word.chars().any(|c| c.is_uppercase()),
17102 100,
17103 &Default::default(),
17104 executor,
17105 )
17106 .await;
17107
17108 // Remove all candidates where the query's start does not match the start of any word in the candidate
17109 if let Some(query_start) = last_word.chars().next() {
17110 matches.retain(|string_match| {
17111 split_words(&string_match.string).any(|word| {
17112 // Check that the first codepoint of the word as lowercase matches the first
17113 // codepoint of the query as lowercase
17114 word.chars()
17115 .flat_map(|codepoint| codepoint.to_lowercase())
17116 .zip(query_start.to_lowercase())
17117 .all(|(word_cp, query_cp)| word_cp == query_cp)
17118 })
17119 });
17120 }
17121
17122 let matched_strings = matches
17123 .into_iter()
17124 .map(|m| m.string)
17125 .collect::<HashSet<_>>();
17126
17127 let result: Vec<Completion> = snippets
17128 .into_iter()
17129 .filter_map(|snippet| {
17130 let matching_prefix = snippet
17131 .prefix
17132 .iter()
17133 .find(|prefix| matched_strings.contains(*prefix))?;
17134 let start = as_offset - last_word.len();
17135 let start = snapshot.anchor_before(start);
17136 let range = start..buffer_position;
17137 let lsp_start = to_lsp(&start);
17138 let lsp_range = lsp::Range {
17139 start: lsp_start,
17140 end: lsp_end,
17141 };
17142 Some(Completion {
17143 old_range: range,
17144 new_text: snippet.body.clone(),
17145 source: CompletionSource::Lsp {
17146 server_id: LanguageServerId(usize::MAX),
17147 resolved: true,
17148 lsp_completion: Box::new(lsp::CompletionItem {
17149 label: snippet.prefix.first().unwrap().clone(),
17150 kind: Some(CompletionItemKind::SNIPPET),
17151 label_details: snippet.description.as_ref().map(|description| {
17152 lsp::CompletionItemLabelDetails {
17153 detail: Some(description.clone()),
17154 description: None,
17155 }
17156 }),
17157 insert_text_format: Some(InsertTextFormat::SNIPPET),
17158 text_edit: Some(lsp::CompletionTextEdit::InsertAndReplace(
17159 lsp::InsertReplaceEdit {
17160 new_text: snippet.body.clone(),
17161 insert: lsp_range,
17162 replace: lsp_range,
17163 },
17164 )),
17165 filter_text: Some(snippet.body.clone()),
17166 sort_text: Some(char::MAX.to_string()),
17167 ..lsp::CompletionItem::default()
17168 }),
17169 lsp_defaults: None,
17170 },
17171 label: CodeLabel {
17172 text: matching_prefix.clone(),
17173 runs: Vec::new(),
17174 filter_range: 0..matching_prefix.len(),
17175 },
17176 documentation: snippet
17177 .description
17178 .clone()
17179 .map(|description| CompletionDocumentation::SingleLine(description.into())),
17180 confirm: None,
17181 })
17182 })
17183 .collect();
17184
17185 Ok(result)
17186 })
17187}
17188
17189impl CompletionProvider for Entity<Project> {
17190 fn completions(
17191 &self,
17192 buffer: &Entity<Buffer>,
17193 buffer_position: text::Anchor,
17194 options: CompletionContext,
17195 _window: &mut Window,
17196 cx: &mut Context<Editor>,
17197 ) -> Task<Result<Option<Vec<Completion>>>> {
17198 self.update(cx, |project, cx| {
17199 let snippets = snippet_completions(project, buffer, buffer_position, cx);
17200 let project_completions = project.completions(buffer, buffer_position, options, cx);
17201 cx.background_spawn(async move {
17202 let snippets_completions = snippets.await?;
17203 match project_completions.await? {
17204 Some(mut completions) => {
17205 completions.extend(snippets_completions);
17206 Ok(Some(completions))
17207 }
17208 None => {
17209 if snippets_completions.is_empty() {
17210 Ok(None)
17211 } else {
17212 Ok(Some(snippets_completions))
17213 }
17214 }
17215 }
17216 })
17217 })
17218 }
17219
17220 fn resolve_completions(
17221 &self,
17222 buffer: Entity<Buffer>,
17223 completion_indices: Vec<usize>,
17224 completions: Rc<RefCell<Box<[Completion]>>>,
17225 cx: &mut Context<Editor>,
17226 ) -> Task<Result<bool>> {
17227 self.update(cx, |project, cx| {
17228 project.lsp_store().update(cx, |lsp_store, cx| {
17229 lsp_store.resolve_completions(buffer, completion_indices, completions, cx)
17230 })
17231 })
17232 }
17233
17234 fn apply_additional_edits_for_completion(
17235 &self,
17236 buffer: Entity<Buffer>,
17237 completions: Rc<RefCell<Box<[Completion]>>>,
17238 completion_index: usize,
17239 push_to_history: bool,
17240 cx: &mut Context<Editor>,
17241 ) -> Task<Result<Option<language::Transaction>>> {
17242 self.update(cx, |project, cx| {
17243 project.lsp_store().update(cx, |lsp_store, cx| {
17244 lsp_store.apply_additional_edits_for_completion(
17245 buffer,
17246 completions,
17247 completion_index,
17248 push_to_history,
17249 cx,
17250 )
17251 })
17252 })
17253 }
17254
17255 fn is_completion_trigger(
17256 &self,
17257 buffer: &Entity<Buffer>,
17258 position: language::Anchor,
17259 text: &str,
17260 trigger_in_words: bool,
17261 cx: &mut Context<Editor>,
17262 ) -> bool {
17263 let mut chars = text.chars();
17264 let char = if let Some(char) = chars.next() {
17265 char
17266 } else {
17267 return false;
17268 };
17269 if chars.next().is_some() {
17270 return false;
17271 }
17272
17273 let buffer = buffer.read(cx);
17274 let snapshot = buffer.snapshot();
17275 if !snapshot.settings_at(position, cx).show_completions_on_input {
17276 return false;
17277 }
17278 let classifier = snapshot.char_classifier_at(position).for_completion(true);
17279 if trigger_in_words && classifier.is_word(char) {
17280 return true;
17281 }
17282
17283 buffer.completion_triggers().contains(text)
17284 }
17285}
17286
17287impl SemanticsProvider for Entity<Project> {
17288 fn hover(
17289 &self,
17290 buffer: &Entity<Buffer>,
17291 position: text::Anchor,
17292 cx: &mut App,
17293 ) -> Option<Task<Vec<project::Hover>>> {
17294 Some(self.update(cx, |project, cx| project.hover(buffer, position, cx)))
17295 }
17296
17297 fn document_highlights(
17298 &self,
17299 buffer: &Entity<Buffer>,
17300 position: text::Anchor,
17301 cx: &mut App,
17302 ) -> Option<Task<Result<Vec<DocumentHighlight>>>> {
17303 Some(self.update(cx, |project, cx| {
17304 project.document_highlights(buffer, position, cx)
17305 }))
17306 }
17307
17308 fn definitions(
17309 &self,
17310 buffer: &Entity<Buffer>,
17311 position: text::Anchor,
17312 kind: GotoDefinitionKind,
17313 cx: &mut App,
17314 ) -> Option<Task<Result<Vec<LocationLink>>>> {
17315 Some(self.update(cx, |project, cx| match kind {
17316 GotoDefinitionKind::Symbol => project.definition(&buffer, position, cx),
17317 GotoDefinitionKind::Declaration => project.declaration(&buffer, position, cx),
17318 GotoDefinitionKind::Type => project.type_definition(&buffer, position, cx),
17319 GotoDefinitionKind::Implementation => project.implementation(&buffer, position, cx),
17320 }))
17321 }
17322
17323 fn supports_inlay_hints(&self, buffer: &Entity<Buffer>, cx: &mut App) -> bool {
17324 // TODO: make this work for remote projects
17325 self.update(cx, |this, cx| {
17326 buffer.update(cx, |buffer, cx| {
17327 this.any_language_server_supports_inlay_hints(buffer, cx)
17328 })
17329 })
17330 }
17331
17332 fn inlay_hints(
17333 &self,
17334 buffer_handle: Entity<Buffer>,
17335 range: Range<text::Anchor>,
17336 cx: &mut App,
17337 ) -> Option<Task<anyhow::Result<Vec<InlayHint>>>> {
17338 Some(self.update(cx, |project, cx| {
17339 project.inlay_hints(buffer_handle, range, cx)
17340 }))
17341 }
17342
17343 fn resolve_inlay_hint(
17344 &self,
17345 hint: InlayHint,
17346 buffer_handle: Entity<Buffer>,
17347 server_id: LanguageServerId,
17348 cx: &mut App,
17349 ) -> Option<Task<anyhow::Result<InlayHint>>> {
17350 Some(self.update(cx, |project, cx| {
17351 project.resolve_inlay_hint(hint, buffer_handle, server_id, cx)
17352 }))
17353 }
17354
17355 fn range_for_rename(
17356 &self,
17357 buffer: &Entity<Buffer>,
17358 position: text::Anchor,
17359 cx: &mut App,
17360 ) -> Option<Task<Result<Option<Range<text::Anchor>>>>> {
17361 Some(self.update(cx, |project, cx| {
17362 let buffer = buffer.clone();
17363 let task = project.prepare_rename(buffer.clone(), position, cx);
17364 cx.spawn(|_, mut cx| async move {
17365 Ok(match task.await? {
17366 PrepareRenameResponse::Success(range) => Some(range),
17367 PrepareRenameResponse::InvalidPosition => None,
17368 PrepareRenameResponse::OnlyUnpreparedRenameSupported => {
17369 // Fallback on using TreeSitter info to determine identifier range
17370 buffer.update(&mut cx, |buffer, _| {
17371 let snapshot = buffer.snapshot();
17372 let (range, kind) = snapshot.surrounding_word(position);
17373 if kind != Some(CharKind::Word) {
17374 return None;
17375 }
17376 Some(
17377 snapshot.anchor_before(range.start)
17378 ..snapshot.anchor_after(range.end),
17379 )
17380 })?
17381 }
17382 })
17383 })
17384 }))
17385 }
17386
17387 fn perform_rename(
17388 &self,
17389 buffer: &Entity<Buffer>,
17390 position: text::Anchor,
17391 new_name: String,
17392 cx: &mut App,
17393 ) -> Option<Task<Result<ProjectTransaction>>> {
17394 Some(self.update(cx, |project, cx| {
17395 project.perform_rename(buffer.clone(), position, new_name, cx)
17396 }))
17397 }
17398}
17399
17400fn inlay_hint_settings(
17401 location: Anchor,
17402 snapshot: &MultiBufferSnapshot,
17403 cx: &mut Context<Editor>,
17404) -> InlayHintSettings {
17405 let file = snapshot.file_at(location);
17406 let language = snapshot.language_at(location).map(|l| l.name());
17407 language_settings(language, file, cx).inlay_hints
17408}
17409
17410fn consume_contiguous_rows(
17411 contiguous_row_selections: &mut Vec<Selection<Point>>,
17412 selection: &Selection<Point>,
17413 display_map: &DisplaySnapshot,
17414 selections: &mut Peekable<std::slice::Iter<Selection<Point>>>,
17415) -> (MultiBufferRow, MultiBufferRow) {
17416 contiguous_row_selections.push(selection.clone());
17417 let start_row = MultiBufferRow(selection.start.row);
17418 let mut end_row = ending_row(selection, display_map);
17419
17420 while let Some(next_selection) = selections.peek() {
17421 if next_selection.start.row <= end_row.0 {
17422 end_row = ending_row(next_selection, display_map);
17423 contiguous_row_selections.push(selections.next().unwrap().clone());
17424 } else {
17425 break;
17426 }
17427 }
17428 (start_row, end_row)
17429}
17430
17431fn ending_row(next_selection: &Selection<Point>, display_map: &DisplaySnapshot) -> MultiBufferRow {
17432 if next_selection.end.column > 0 || next_selection.is_empty() {
17433 MultiBufferRow(display_map.next_line_boundary(next_selection.end).0.row + 1)
17434 } else {
17435 MultiBufferRow(next_selection.end.row)
17436 }
17437}
17438
17439impl EditorSnapshot {
17440 pub fn remote_selections_in_range<'a>(
17441 &'a self,
17442 range: &'a Range<Anchor>,
17443 collaboration_hub: &dyn CollaborationHub,
17444 cx: &'a App,
17445 ) -> impl 'a + Iterator<Item = RemoteSelection> {
17446 let participant_names = collaboration_hub.user_names(cx);
17447 let participant_indices = collaboration_hub.user_participant_indices(cx);
17448 let collaborators_by_peer_id = collaboration_hub.collaborators(cx);
17449 let collaborators_by_replica_id = collaborators_by_peer_id
17450 .iter()
17451 .map(|(_, collaborator)| (collaborator.replica_id, collaborator))
17452 .collect::<HashMap<_, _>>();
17453 self.buffer_snapshot
17454 .selections_in_range(range, false)
17455 .filter_map(move |(replica_id, line_mode, cursor_shape, selection)| {
17456 let collaborator = collaborators_by_replica_id.get(&replica_id)?;
17457 let participant_index = participant_indices.get(&collaborator.user_id).copied();
17458 let user_name = participant_names.get(&collaborator.user_id).cloned();
17459 Some(RemoteSelection {
17460 replica_id,
17461 selection,
17462 cursor_shape,
17463 line_mode,
17464 participant_index,
17465 peer_id: collaborator.peer_id,
17466 user_name,
17467 })
17468 })
17469 }
17470
17471 pub fn hunks_for_ranges(
17472 &self,
17473 ranges: impl IntoIterator<Item = Range<Point>>,
17474 ) -> Vec<MultiBufferDiffHunk> {
17475 let mut hunks = Vec::new();
17476 let mut processed_buffer_rows: HashMap<BufferId, HashSet<Range<text::Anchor>>> =
17477 HashMap::default();
17478 for query_range in ranges {
17479 let query_rows =
17480 MultiBufferRow(query_range.start.row)..MultiBufferRow(query_range.end.row + 1);
17481 for hunk in self.buffer_snapshot.diff_hunks_in_range(
17482 Point::new(query_rows.start.0, 0)..Point::new(query_rows.end.0, 0),
17483 ) {
17484 // Include deleted hunks that are adjacent to the query range, because
17485 // otherwise they would be missed.
17486 let mut intersects_range = hunk.row_range.overlaps(&query_rows);
17487 if hunk.status().is_deleted() {
17488 intersects_range |= hunk.row_range.start == query_rows.end;
17489 intersects_range |= hunk.row_range.end == query_rows.start;
17490 }
17491 if intersects_range {
17492 if !processed_buffer_rows
17493 .entry(hunk.buffer_id)
17494 .or_default()
17495 .insert(hunk.buffer_range.start..hunk.buffer_range.end)
17496 {
17497 continue;
17498 }
17499 hunks.push(hunk);
17500 }
17501 }
17502 }
17503
17504 hunks
17505 }
17506
17507 fn display_diff_hunks_for_rows<'a>(
17508 &'a self,
17509 display_rows: Range<DisplayRow>,
17510 folded_buffers: &'a HashSet<BufferId>,
17511 ) -> impl 'a + Iterator<Item = DisplayDiffHunk> {
17512 let buffer_start = DisplayPoint::new(display_rows.start, 0).to_point(self);
17513 let buffer_end = DisplayPoint::new(display_rows.end, 0).to_point(self);
17514
17515 self.buffer_snapshot
17516 .diff_hunks_in_range(buffer_start..buffer_end)
17517 .filter_map(|hunk| {
17518 if folded_buffers.contains(&hunk.buffer_id) {
17519 return None;
17520 }
17521
17522 let hunk_start_point = Point::new(hunk.row_range.start.0, 0);
17523 let hunk_end_point = Point::new(hunk.row_range.end.0, 0);
17524
17525 let hunk_display_start = self.point_to_display_point(hunk_start_point, Bias::Left);
17526 let hunk_display_end = self.point_to_display_point(hunk_end_point, Bias::Right);
17527
17528 let display_hunk = if hunk_display_start.column() != 0 {
17529 DisplayDiffHunk::Folded {
17530 display_row: hunk_display_start.row(),
17531 }
17532 } else {
17533 let mut end_row = hunk_display_end.row();
17534 if hunk_display_end.column() > 0 {
17535 end_row.0 += 1;
17536 }
17537 let is_created_file = hunk.is_created_file();
17538 DisplayDiffHunk::Unfolded {
17539 status: hunk.status(),
17540 diff_base_byte_range: hunk.diff_base_byte_range,
17541 display_row_range: hunk_display_start.row()..end_row,
17542 multi_buffer_range: Anchor::range_in_buffer(
17543 hunk.excerpt_id,
17544 hunk.buffer_id,
17545 hunk.buffer_range,
17546 ),
17547 is_created_file,
17548 }
17549 };
17550
17551 Some(display_hunk)
17552 })
17553 }
17554
17555 pub fn language_at<T: ToOffset>(&self, position: T) -> Option<&Arc<Language>> {
17556 self.display_snapshot.buffer_snapshot.language_at(position)
17557 }
17558
17559 pub fn is_focused(&self) -> bool {
17560 self.is_focused
17561 }
17562
17563 pub fn placeholder_text(&self) -> Option<&Arc<str>> {
17564 self.placeholder_text.as_ref()
17565 }
17566
17567 pub fn scroll_position(&self) -> gpui::Point<f32> {
17568 self.scroll_anchor.scroll_position(&self.display_snapshot)
17569 }
17570
17571 fn gutter_dimensions(
17572 &self,
17573 font_id: FontId,
17574 font_size: Pixels,
17575 max_line_number_width: Pixels,
17576 cx: &App,
17577 ) -> Option<GutterDimensions> {
17578 if !self.show_gutter {
17579 return None;
17580 }
17581
17582 let descent = cx.text_system().descent(font_id, font_size);
17583 let em_width = cx.text_system().em_width(font_id, font_size).log_err()?;
17584 let em_advance = cx.text_system().em_advance(font_id, font_size).log_err()?;
17585
17586 let show_git_gutter = self.show_git_diff_gutter.unwrap_or_else(|| {
17587 matches!(
17588 ProjectSettings::get_global(cx).git.git_gutter,
17589 Some(GitGutterSetting::TrackedFiles)
17590 )
17591 });
17592 let gutter_settings = EditorSettings::get_global(cx).gutter;
17593 let show_line_numbers = self
17594 .show_line_numbers
17595 .unwrap_or(gutter_settings.line_numbers);
17596 let line_gutter_width = if show_line_numbers {
17597 // Avoid flicker-like gutter resizes when the line number gains another digit and only resize the gutter on files with N*10^5 lines.
17598 let min_width_for_number_on_gutter = em_advance * 4.0;
17599 max_line_number_width.max(min_width_for_number_on_gutter)
17600 } else {
17601 0.0.into()
17602 };
17603
17604 let show_code_actions = self
17605 .show_code_actions
17606 .unwrap_or(gutter_settings.code_actions);
17607
17608 let show_runnables = self.show_runnables.unwrap_or(gutter_settings.runnables);
17609
17610 let git_blame_entries_width =
17611 self.git_blame_gutter_max_author_length
17612 .map(|max_author_length| {
17613 const MAX_RELATIVE_TIMESTAMP: &str = "60 minutes ago";
17614
17615 /// The number of characters to dedicate to gaps and margins.
17616 const SPACING_WIDTH: usize = 4;
17617
17618 let max_char_count = max_author_length
17619 .min(GIT_BLAME_MAX_AUTHOR_CHARS_DISPLAYED)
17620 + ::git::SHORT_SHA_LENGTH
17621 + MAX_RELATIVE_TIMESTAMP.len()
17622 + SPACING_WIDTH;
17623
17624 em_advance * max_char_count
17625 });
17626
17627 let mut left_padding = git_blame_entries_width.unwrap_or(Pixels::ZERO);
17628 left_padding += if show_code_actions || show_runnables {
17629 em_width * 3.0
17630 } else if show_git_gutter && show_line_numbers {
17631 em_width * 2.0
17632 } else if show_git_gutter || show_line_numbers {
17633 em_width
17634 } else {
17635 px(0.)
17636 };
17637
17638 let right_padding = if gutter_settings.folds && show_line_numbers {
17639 em_width * 4.0
17640 } else if gutter_settings.folds {
17641 em_width * 3.0
17642 } else if show_line_numbers {
17643 em_width
17644 } else {
17645 px(0.)
17646 };
17647
17648 Some(GutterDimensions {
17649 left_padding,
17650 right_padding,
17651 width: line_gutter_width + left_padding + right_padding,
17652 margin: -descent,
17653 git_blame_entries_width,
17654 })
17655 }
17656
17657 pub fn render_crease_toggle(
17658 &self,
17659 buffer_row: MultiBufferRow,
17660 row_contains_cursor: bool,
17661 editor: Entity<Editor>,
17662 window: &mut Window,
17663 cx: &mut App,
17664 ) -> Option<AnyElement> {
17665 let folded = self.is_line_folded(buffer_row);
17666 let mut is_foldable = false;
17667
17668 if let Some(crease) = self
17669 .crease_snapshot
17670 .query_row(buffer_row, &self.buffer_snapshot)
17671 {
17672 is_foldable = true;
17673 match crease {
17674 Crease::Inline { render_toggle, .. } | Crease::Block { render_toggle, .. } => {
17675 if let Some(render_toggle) = render_toggle {
17676 let toggle_callback =
17677 Arc::new(move |folded, window: &mut Window, cx: &mut App| {
17678 if folded {
17679 editor.update(cx, |editor, cx| {
17680 editor.fold_at(&crate::FoldAt { buffer_row }, window, cx)
17681 });
17682 } else {
17683 editor.update(cx, |editor, cx| {
17684 editor.unfold_at(
17685 &crate::UnfoldAt { buffer_row },
17686 window,
17687 cx,
17688 )
17689 });
17690 }
17691 });
17692 return Some((render_toggle)(
17693 buffer_row,
17694 folded,
17695 toggle_callback,
17696 window,
17697 cx,
17698 ));
17699 }
17700 }
17701 }
17702 }
17703
17704 is_foldable |= self.starts_indent(buffer_row);
17705
17706 if folded || (is_foldable && (row_contains_cursor || self.gutter_hovered)) {
17707 Some(
17708 Disclosure::new(("gutter_crease", buffer_row.0), !folded)
17709 .toggle_state(folded)
17710 .on_click(window.listener_for(&editor, move |this, _e, window, cx| {
17711 if folded {
17712 this.unfold_at(&UnfoldAt { buffer_row }, window, cx);
17713 } else {
17714 this.fold_at(&FoldAt { buffer_row }, window, cx);
17715 }
17716 }))
17717 .into_any_element(),
17718 )
17719 } else {
17720 None
17721 }
17722 }
17723
17724 pub fn render_crease_trailer(
17725 &self,
17726 buffer_row: MultiBufferRow,
17727 window: &mut Window,
17728 cx: &mut App,
17729 ) -> Option<AnyElement> {
17730 let folded = self.is_line_folded(buffer_row);
17731 if let Crease::Inline { render_trailer, .. } = self
17732 .crease_snapshot
17733 .query_row(buffer_row, &self.buffer_snapshot)?
17734 {
17735 let render_trailer = render_trailer.as_ref()?;
17736 Some(render_trailer(buffer_row, folded, window, cx))
17737 } else {
17738 None
17739 }
17740 }
17741}
17742
17743impl Deref for EditorSnapshot {
17744 type Target = DisplaySnapshot;
17745
17746 fn deref(&self) -> &Self::Target {
17747 &self.display_snapshot
17748 }
17749}
17750
17751#[derive(Clone, Debug, PartialEq, Eq)]
17752pub enum EditorEvent {
17753 InputIgnored {
17754 text: Arc<str>,
17755 },
17756 InputHandled {
17757 utf16_range_to_replace: Option<Range<isize>>,
17758 text: Arc<str>,
17759 },
17760 ExcerptsAdded {
17761 buffer: Entity<Buffer>,
17762 predecessor: ExcerptId,
17763 excerpts: Vec<(ExcerptId, ExcerptRange<language::Anchor>)>,
17764 },
17765 ExcerptsRemoved {
17766 ids: Vec<ExcerptId>,
17767 },
17768 BufferFoldToggled {
17769 ids: Vec<ExcerptId>,
17770 folded: bool,
17771 },
17772 ExcerptsEdited {
17773 ids: Vec<ExcerptId>,
17774 },
17775 ExcerptsExpanded {
17776 ids: Vec<ExcerptId>,
17777 },
17778 BufferEdited,
17779 Edited {
17780 transaction_id: clock::Lamport,
17781 },
17782 Reparsed(BufferId),
17783 Focused,
17784 FocusedIn,
17785 Blurred,
17786 DirtyChanged,
17787 Saved,
17788 TitleChanged,
17789 DiffBaseChanged,
17790 SelectionsChanged {
17791 local: bool,
17792 },
17793 ScrollPositionChanged {
17794 local: bool,
17795 autoscroll: bool,
17796 },
17797 Closed,
17798 TransactionUndone {
17799 transaction_id: clock::Lamport,
17800 },
17801 TransactionBegun {
17802 transaction_id: clock::Lamport,
17803 },
17804 Reloaded,
17805 CursorShapeChanged,
17806}
17807
17808impl EventEmitter<EditorEvent> for Editor {}
17809
17810impl Focusable for Editor {
17811 fn focus_handle(&self, _cx: &App) -> FocusHandle {
17812 self.focus_handle.clone()
17813 }
17814}
17815
17816impl Render for Editor {
17817 fn render(&mut self, _: &mut Window, cx: &mut Context<'_, Self>) -> impl IntoElement {
17818 let settings = ThemeSettings::get_global(cx);
17819
17820 let mut text_style = match self.mode {
17821 EditorMode::SingleLine { .. } | EditorMode::AutoHeight { .. } => TextStyle {
17822 color: cx.theme().colors().editor_foreground,
17823 font_family: settings.ui_font.family.clone(),
17824 font_features: settings.ui_font.features.clone(),
17825 font_fallbacks: settings.ui_font.fallbacks.clone(),
17826 font_size: rems(0.875).into(),
17827 font_weight: settings.ui_font.weight,
17828 line_height: relative(settings.buffer_line_height.value()),
17829 ..Default::default()
17830 },
17831 EditorMode::Full => TextStyle {
17832 color: cx.theme().colors().editor_foreground,
17833 font_family: settings.buffer_font.family.clone(),
17834 font_features: settings.buffer_font.features.clone(),
17835 font_fallbacks: settings.buffer_font.fallbacks.clone(),
17836 font_size: settings.buffer_font_size(cx).into(),
17837 font_weight: settings.buffer_font.weight,
17838 line_height: relative(settings.buffer_line_height.value()),
17839 ..Default::default()
17840 },
17841 };
17842 if let Some(text_style_refinement) = &self.text_style_refinement {
17843 text_style.refine(text_style_refinement)
17844 }
17845
17846 let background = match self.mode {
17847 EditorMode::SingleLine { .. } => cx.theme().system().transparent,
17848 EditorMode::AutoHeight { max_lines: _ } => cx.theme().system().transparent,
17849 EditorMode::Full => cx.theme().colors().editor_background,
17850 };
17851
17852 EditorElement::new(
17853 &cx.entity(),
17854 EditorStyle {
17855 background,
17856 local_player: cx.theme().players().local(),
17857 text: text_style,
17858 scrollbar_width: EditorElement::SCROLLBAR_WIDTH,
17859 syntax: cx.theme().syntax().clone(),
17860 status: cx.theme().status().clone(),
17861 inlay_hints_style: make_inlay_hints_style(cx),
17862 inline_completion_styles: make_suggestion_styles(cx),
17863 unnecessary_code_fade: ThemeSettings::get_global(cx).unnecessary_code_fade,
17864 },
17865 )
17866 }
17867}
17868
17869impl EntityInputHandler for Editor {
17870 fn text_for_range(
17871 &mut self,
17872 range_utf16: Range<usize>,
17873 adjusted_range: &mut Option<Range<usize>>,
17874 _: &mut Window,
17875 cx: &mut Context<Self>,
17876 ) -> Option<String> {
17877 let snapshot = self.buffer.read(cx).read(cx);
17878 let start = snapshot.clip_offset_utf16(OffsetUtf16(range_utf16.start), Bias::Left);
17879 let end = snapshot.clip_offset_utf16(OffsetUtf16(range_utf16.end), Bias::Right);
17880 if (start.0..end.0) != range_utf16 {
17881 adjusted_range.replace(start.0..end.0);
17882 }
17883 Some(snapshot.text_for_range(start..end).collect())
17884 }
17885
17886 fn selected_text_range(
17887 &mut self,
17888 ignore_disabled_input: bool,
17889 _: &mut Window,
17890 cx: &mut Context<Self>,
17891 ) -> Option<UTF16Selection> {
17892 // Prevent the IME menu from appearing when holding down an alphabetic key
17893 // while input is disabled.
17894 if !ignore_disabled_input && !self.input_enabled {
17895 return None;
17896 }
17897
17898 let selection = self.selections.newest::<OffsetUtf16>(cx);
17899 let range = selection.range();
17900
17901 Some(UTF16Selection {
17902 range: range.start.0..range.end.0,
17903 reversed: selection.reversed,
17904 })
17905 }
17906
17907 fn marked_text_range(&self, _: &mut Window, cx: &mut Context<Self>) -> Option<Range<usize>> {
17908 let snapshot = self.buffer.read(cx).read(cx);
17909 let range = self.text_highlights::<InputComposition>(cx)?.1.first()?;
17910 Some(range.start.to_offset_utf16(&snapshot).0..range.end.to_offset_utf16(&snapshot).0)
17911 }
17912
17913 fn unmark_text(&mut self, _: &mut Window, cx: &mut Context<Self>) {
17914 self.clear_highlights::<InputComposition>(cx);
17915 self.ime_transaction.take();
17916 }
17917
17918 fn replace_text_in_range(
17919 &mut self,
17920 range_utf16: Option<Range<usize>>,
17921 text: &str,
17922 window: &mut Window,
17923 cx: &mut Context<Self>,
17924 ) {
17925 if !self.input_enabled {
17926 cx.emit(EditorEvent::InputIgnored { text: text.into() });
17927 return;
17928 }
17929
17930 self.transact(window, cx, |this, window, cx| {
17931 let new_selected_ranges = if let Some(range_utf16) = range_utf16 {
17932 let range_utf16 = OffsetUtf16(range_utf16.start)..OffsetUtf16(range_utf16.end);
17933 Some(this.selection_replacement_ranges(range_utf16, cx))
17934 } else {
17935 this.marked_text_ranges(cx)
17936 };
17937
17938 let range_to_replace = new_selected_ranges.as_ref().and_then(|ranges_to_replace| {
17939 let newest_selection_id = this.selections.newest_anchor().id;
17940 this.selections
17941 .all::<OffsetUtf16>(cx)
17942 .iter()
17943 .zip(ranges_to_replace.iter())
17944 .find_map(|(selection, range)| {
17945 if selection.id == newest_selection_id {
17946 Some(
17947 (range.start.0 as isize - selection.head().0 as isize)
17948 ..(range.end.0 as isize - selection.head().0 as isize),
17949 )
17950 } else {
17951 None
17952 }
17953 })
17954 });
17955
17956 cx.emit(EditorEvent::InputHandled {
17957 utf16_range_to_replace: range_to_replace,
17958 text: text.into(),
17959 });
17960
17961 if let Some(new_selected_ranges) = new_selected_ranges {
17962 this.change_selections(None, window, cx, |selections| {
17963 selections.select_ranges(new_selected_ranges)
17964 });
17965 this.backspace(&Default::default(), window, cx);
17966 }
17967
17968 this.handle_input(text, window, cx);
17969 });
17970
17971 if let Some(transaction) = self.ime_transaction {
17972 self.buffer.update(cx, |buffer, cx| {
17973 buffer.group_until_transaction(transaction, cx);
17974 });
17975 }
17976
17977 self.unmark_text(window, cx);
17978 }
17979
17980 fn replace_and_mark_text_in_range(
17981 &mut self,
17982 range_utf16: Option<Range<usize>>,
17983 text: &str,
17984 new_selected_range_utf16: Option<Range<usize>>,
17985 window: &mut Window,
17986 cx: &mut Context<Self>,
17987 ) {
17988 if !self.input_enabled {
17989 return;
17990 }
17991
17992 let transaction = self.transact(window, cx, |this, window, cx| {
17993 let ranges_to_replace = if let Some(mut marked_ranges) = this.marked_text_ranges(cx) {
17994 let snapshot = this.buffer.read(cx).read(cx);
17995 if let Some(relative_range_utf16) = range_utf16.as_ref() {
17996 for marked_range in &mut marked_ranges {
17997 marked_range.end.0 = marked_range.start.0 + relative_range_utf16.end;
17998 marked_range.start.0 += relative_range_utf16.start;
17999 marked_range.start =
18000 snapshot.clip_offset_utf16(marked_range.start, Bias::Left);
18001 marked_range.end =
18002 snapshot.clip_offset_utf16(marked_range.end, Bias::Right);
18003 }
18004 }
18005 Some(marked_ranges)
18006 } else if let Some(range_utf16) = range_utf16 {
18007 let range_utf16 = OffsetUtf16(range_utf16.start)..OffsetUtf16(range_utf16.end);
18008 Some(this.selection_replacement_ranges(range_utf16, cx))
18009 } else {
18010 None
18011 };
18012
18013 let range_to_replace = ranges_to_replace.as_ref().and_then(|ranges_to_replace| {
18014 let newest_selection_id = this.selections.newest_anchor().id;
18015 this.selections
18016 .all::<OffsetUtf16>(cx)
18017 .iter()
18018 .zip(ranges_to_replace.iter())
18019 .find_map(|(selection, range)| {
18020 if selection.id == newest_selection_id {
18021 Some(
18022 (range.start.0 as isize - selection.head().0 as isize)
18023 ..(range.end.0 as isize - selection.head().0 as isize),
18024 )
18025 } else {
18026 None
18027 }
18028 })
18029 });
18030
18031 cx.emit(EditorEvent::InputHandled {
18032 utf16_range_to_replace: range_to_replace,
18033 text: text.into(),
18034 });
18035
18036 if let Some(ranges) = ranges_to_replace {
18037 this.change_selections(None, window, cx, |s| s.select_ranges(ranges));
18038 }
18039
18040 let marked_ranges = {
18041 let snapshot = this.buffer.read(cx).read(cx);
18042 this.selections
18043 .disjoint_anchors()
18044 .iter()
18045 .map(|selection| {
18046 selection.start.bias_left(&snapshot)..selection.end.bias_right(&snapshot)
18047 })
18048 .collect::<Vec<_>>()
18049 };
18050
18051 if text.is_empty() {
18052 this.unmark_text(window, cx);
18053 } else {
18054 this.highlight_text::<InputComposition>(
18055 marked_ranges.clone(),
18056 HighlightStyle {
18057 underline: Some(UnderlineStyle {
18058 thickness: px(1.),
18059 color: None,
18060 wavy: false,
18061 }),
18062 ..Default::default()
18063 },
18064 cx,
18065 );
18066 }
18067
18068 // Disable auto-closing when composing text (i.e. typing a `"` on a Brazilian keyboard)
18069 let use_autoclose = this.use_autoclose;
18070 let use_auto_surround = this.use_auto_surround;
18071 this.set_use_autoclose(false);
18072 this.set_use_auto_surround(false);
18073 this.handle_input(text, window, cx);
18074 this.set_use_autoclose(use_autoclose);
18075 this.set_use_auto_surround(use_auto_surround);
18076
18077 if let Some(new_selected_range) = new_selected_range_utf16 {
18078 let snapshot = this.buffer.read(cx).read(cx);
18079 let new_selected_ranges = marked_ranges
18080 .into_iter()
18081 .map(|marked_range| {
18082 let insertion_start = marked_range.start.to_offset_utf16(&snapshot).0;
18083 let new_start = OffsetUtf16(new_selected_range.start + insertion_start);
18084 let new_end = OffsetUtf16(new_selected_range.end + insertion_start);
18085 snapshot.clip_offset_utf16(new_start, Bias::Left)
18086 ..snapshot.clip_offset_utf16(new_end, Bias::Right)
18087 })
18088 .collect::<Vec<_>>();
18089
18090 drop(snapshot);
18091 this.change_selections(None, window, cx, |selections| {
18092 selections.select_ranges(new_selected_ranges)
18093 });
18094 }
18095 });
18096
18097 self.ime_transaction = self.ime_transaction.or(transaction);
18098 if let Some(transaction) = self.ime_transaction {
18099 self.buffer.update(cx, |buffer, cx| {
18100 buffer.group_until_transaction(transaction, cx);
18101 });
18102 }
18103
18104 if self.text_highlights::<InputComposition>(cx).is_none() {
18105 self.ime_transaction.take();
18106 }
18107 }
18108
18109 fn bounds_for_range(
18110 &mut self,
18111 range_utf16: Range<usize>,
18112 element_bounds: gpui::Bounds<Pixels>,
18113 window: &mut Window,
18114 cx: &mut Context<Self>,
18115 ) -> Option<gpui::Bounds<Pixels>> {
18116 let text_layout_details = self.text_layout_details(window);
18117 let gpui::Size {
18118 width: em_width,
18119 height: line_height,
18120 } = self.character_size(window);
18121
18122 let snapshot = self.snapshot(window, cx);
18123 let scroll_position = snapshot.scroll_position();
18124 let scroll_left = scroll_position.x * em_width;
18125
18126 let start = OffsetUtf16(range_utf16.start).to_display_point(&snapshot);
18127 let x = snapshot.x_for_display_point(start, &text_layout_details) - scroll_left
18128 + self.gutter_dimensions.width
18129 + self.gutter_dimensions.margin;
18130 let y = line_height * (start.row().as_f32() - scroll_position.y);
18131
18132 Some(Bounds {
18133 origin: element_bounds.origin + point(x, y),
18134 size: size(em_width, line_height),
18135 })
18136 }
18137
18138 fn character_index_for_point(
18139 &mut self,
18140 point: gpui::Point<Pixels>,
18141 _window: &mut Window,
18142 _cx: &mut Context<Self>,
18143 ) -> Option<usize> {
18144 let position_map = self.last_position_map.as_ref()?;
18145 if !position_map.text_hitbox.contains(&point) {
18146 return None;
18147 }
18148 let display_point = position_map.point_for_position(point).previous_valid;
18149 let anchor = position_map
18150 .snapshot
18151 .display_point_to_anchor(display_point, Bias::Left);
18152 let utf16_offset = anchor.to_offset_utf16(&position_map.snapshot.buffer_snapshot);
18153 Some(utf16_offset.0)
18154 }
18155}
18156
18157trait SelectionExt {
18158 fn display_range(&self, map: &DisplaySnapshot) -> Range<DisplayPoint>;
18159 fn spanned_rows(
18160 &self,
18161 include_end_if_at_line_start: bool,
18162 map: &DisplaySnapshot,
18163 ) -> Range<MultiBufferRow>;
18164}
18165
18166impl<T: ToPoint + ToOffset> SelectionExt for Selection<T> {
18167 fn display_range(&self, map: &DisplaySnapshot) -> Range<DisplayPoint> {
18168 let start = self
18169 .start
18170 .to_point(&map.buffer_snapshot)
18171 .to_display_point(map);
18172 let end = self
18173 .end
18174 .to_point(&map.buffer_snapshot)
18175 .to_display_point(map);
18176 if self.reversed {
18177 end..start
18178 } else {
18179 start..end
18180 }
18181 }
18182
18183 fn spanned_rows(
18184 &self,
18185 include_end_if_at_line_start: bool,
18186 map: &DisplaySnapshot,
18187 ) -> Range<MultiBufferRow> {
18188 let start = self.start.to_point(&map.buffer_snapshot);
18189 let mut end = self.end.to_point(&map.buffer_snapshot);
18190 if !include_end_if_at_line_start && start.row != end.row && end.column == 0 {
18191 end.row -= 1;
18192 }
18193
18194 let buffer_start = map.prev_line_boundary(start).0;
18195 let buffer_end = map.next_line_boundary(end).0;
18196 MultiBufferRow(buffer_start.row)..MultiBufferRow(buffer_end.row + 1)
18197 }
18198}
18199
18200impl<T: InvalidationRegion> InvalidationStack<T> {
18201 fn invalidate<S>(&mut self, selections: &[Selection<S>], buffer: &MultiBufferSnapshot)
18202 where
18203 S: Clone + ToOffset,
18204 {
18205 while let Some(region) = self.last() {
18206 let all_selections_inside_invalidation_ranges =
18207 if selections.len() == region.ranges().len() {
18208 selections
18209 .iter()
18210 .zip(region.ranges().iter().map(|r| r.to_offset(buffer)))
18211 .all(|(selection, invalidation_range)| {
18212 let head = selection.head().to_offset(buffer);
18213 invalidation_range.start <= head && invalidation_range.end >= head
18214 })
18215 } else {
18216 false
18217 };
18218
18219 if all_selections_inside_invalidation_ranges {
18220 break;
18221 } else {
18222 self.pop();
18223 }
18224 }
18225 }
18226}
18227
18228impl<T> Default for InvalidationStack<T> {
18229 fn default() -> Self {
18230 Self(Default::default())
18231 }
18232}
18233
18234impl<T> Deref for InvalidationStack<T> {
18235 type Target = Vec<T>;
18236
18237 fn deref(&self) -> &Self::Target {
18238 &self.0
18239 }
18240}
18241
18242impl<T> DerefMut for InvalidationStack<T> {
18243 fn deref_mut(&mut self) -> &mut Self::Target {
18244 &mut self.0
18245 }
18246}
18247
18248impl InvalidationRegion for SnippetState {
18249 fn ranges(&self) -> &[Range<Anchor>] {
18250 &self.ranges[self.active_index]
18251 }
18252}
18253
18254pub fn diagnostic_block_renderer(
18255 diagnostic: Diagnostic,
18256 max_message_rows: Option<u8>,
18257 allow_closing: bool,
18258) -> RenderBlock {
18259 let (text_without_backticks, code_ranges) =
18260 highlight_diagnostic_message(&diagnostic, max_message_rows);
18261
18262 Arc::new(move |cx: &mut BlockContext| {
18263 let group_id: SharedString = cx.block_id.to_string().into();
18264
18265 let mut text_style = cx.window.text_style().clone();
18266 text_style.color = diagnostic_style(diagnostic.severity, cx.theme().status());
18267 let theme_settings = ThemeSettings::get_global(cx);
18268 text_style.font_family = theme_settings.buffer_font.family.clone();
18269 text_style.font_style = theme_settings.buffer_font.style;
18270 text_style.font_features = theme_settings.buffer_font.features.clone();
18271 text_style.font_weight = theme_settings.buffer_font.weight;
18272
18273 let multi_line_diagnostic = diagnostic.message.contains('\n');
18274
18275 let buttons = |diagnostic: &Diagnostic| {
18276 if multi_line_diagnostic {
18277 v_flex()
18278 } else {
18279 h_flex()
18280 }
18281 .when(allow_closing, |div| {
18282 div.children(diagnostic.is_primary.then(|| {
18283 IconButton::new("close-block", IconName::XCircle)
18284 .icon_color(Color::Muted)
18285 .size(ButtonSize::Compact)
18286 .style(ButtonStyle::Transparent)
18287 .visible_on_hover(group_id.clone())
18288 .on_click(move |_click, window, cx| {
18289 window.dispatch_action(Box::new(Cancel), cx)
18290 })
18291 .tooltip(|window, cx| {
18292 Tooltip::for_action("Close Diagnostics", &Cancel, window, cx)
18293 })
18294 }))
18295 })
18296 .child(
18297 IconButton::new("copy-block", IconName::Copy)
18298 .icon_color(Color::Muted)
18299 .size(ButtonSize::Compact)
18300 .style(ButtonStyle::Transparent)
18301 .visible_on_hover(group_id.clone())
18302 .on_click({
18303 let message = diagnostic.message.clone();
18304 move |_click, _, cx| {
18305 cx.write_to_clipboard(ClipboardItem::new_string(message.clone()))
18306 }
18307 })
18308 .tooltip(Tooltip::text("Copy diagnostic message")),
18309 )
18310 };
18311
18312 let icon_size = buttons(&diagnostic).into_any_element().layout_as_root(
18313 AvailableSpace::min_size(),
18314 cx.window,
18315 cx.app,
18316 );
18317
18318 h_flex()
18319 .id(cx.block_id)
18320 .group(group_id.clone())
18321 .relative()
18322 .size_full()
18323 .block_mouse_down()
18324 .pl(cx.gutter_dimensions.width)
18325 .w(cx.max_width - cx.gutter_dimensions.full_width())
18326 .child(
18327 div()
18328 .flex()
18329 .w(cx.anchor_x - cx.gutter_dimensions.width - icon_size.width)
18330 .flex_shrink(),
18331 )
18332 .child(buttons(&diagnostic))
18333 .child(div().flex().flex_shrink_0().child(
18334 StyledText::new(text_without_backticks.clone()).with_default_highlights(
18335 &text_style,
18336 code_ranges.iter().map(|range| {
18337 (
18338 range.clone(),
18339 HighlightStyle {
18340 font_weight: Some(FontWeight::BOLD),
18341 ..Default::default()
18342 },
18343 )
18344 }),
18345 ),
18346 ))
18347 .into_any_element()
18348 })
18349}
18350
18351fn inline_completion_edit_text(
18352 current_snapshot: &BufferSnapshot,
18353 edits: &[(Range<Anchor>, String)],
18354 edit_preview: &EditPreview,
18355 include_deletions: bool,
18356 cx: &App,
18357) -> HighlightedText {
18358 let edits = edits
18359 .iter()
18360 .map(|(anchor, text)| {
18361 (
18362 anchor.start.text_anchor..anchor.end.text_anchor,
18363 text.clone(),
18364 )
18365 })
18366 .collect::<Vec<_>>();
18367
18368 edit_preview.highlight_edits(current_snapshot, &edits, include_deletions, cx)
18369}
18370
18371pub fn highlight_diagnostic_message(
18372 diagnostic: &Diagnostic,
18373 mut max_message_rows: Option<u8>,
18374) -> (SharedString, Vec<Range<usize>>) {
18375 let mut text_without_backticks = String::new();
18376 let mut code_ranges = Vec::new();
18377
18378 if let Some(source) = &diagnostic.source {
18379 text_without_backticks.push_str(source);
18380 code_ranges.push(0..source.len());
18381 text_without_backticks.push_str(": ");
18382 }
18383
18384 let mut prev_offset = 0;
18385 let mut in_code_block = false;
18386 let has_row_limit = max_message_rows.is_some();
18387 let mut newline_indices = diagnostic
18388 .message
18389 .match_indices('\n')
18390 .filter(|_| has_row_limit)
18391 .map(|(ix, _)| ix)
18392 .fuse()
18393 .peekable();
18394
18395 for (quote_ix, _) in diagnostic
18396 .message
18397 .match_indices('`')
18398 .chain([(diagnostic.message.len(), "")])
18399 {
18400 let mut first_newline_ix = None;
18401 let mut last_newline_ix = None;
18402 while let Some(newline_ix) = newline_indices.peek() {
18403 if *newline_ix < quote_ix {
18404 if first_newline_ix.is_none() {
18405 first_newline_ix = Some(*newline_ix);
18406 }
18407 last_newline_ix = Some(*newline_ix);
18408
18409 if let Some(rows_left) = &mut max_message_rows {
18410 if *rows_left == 0 {
18411 break;
18412 } else {
18413 *rows_left -= 1;
18414 }
18415 }
18416 let _ = newline_indices.next();
18417 } else {
18418 break;
18419 }
18420 }
18421 let prev_len = text_without_backticks.len();
18422 let new_text = &diagnostic.message[prev_offset..first_newline_ix.unwrap_or(quote_ix)];
18423 text_without_backticks.push_str(new_text);
18424 if in_code_block {
18425 code_ranges.push(prev_len..text_without_backticks.len());
18426 }
18427 prev_offset = last_newline_ix.unwrap_or(quote_ix) + 1;
18428 in_code_block = !in_code_block;
18429 if first_newline_ix.map_or(false, |newline_ix| newline_ix < quote_ix) {
18430 text_without_backticks.push_str("...");
18431 break;
18432 }
18433 }
18434
18435 (text_without_backticks.into(), code_ranges)
18436}
18437
18438fn diagnostic_style(severity: DiagnosticSeverity, colors: &StatusColors) -> Hsla {
18439 match severity {
18440 DiagnosticSeverity::ERROR => colors.error,
18441 DiagnosticSeverity::WARNING => colors.warning,
18442 DiagnosticSeverity::INFORMATION => colors.info,
18443 DiagnosticSeverity::HINT => colors.info,
18444 _ => colors.ignored,
18445 }
18446}
18447
18448pub fn styled_runs_for_code_label<'a>(
18449 label: &'a CodeLabel,
18450 syntax_theme: &'a theme::SyntaxTheme,
18451) -> impl 'a + Iterator<Item = (Range<usize>, HighlightStyle)> {
18452 let fade_out = HighlightStyle {
18453 fade_out: Some(0.35),
18454 ..Default::default()
18455 };
18456
18457 let mut prev_end = label.filter_range.end;
18458 label
18459 .runs
18460 .iter()
18461 .enumerate()
18462 .flat_map(move |(ix, (range, highlight_id))| {
18463 let style = if let Some(style) = highlight_id.style(syntax_theme) {
18464 style
18465 } else {
18466 return Default::default();
18467 };
18468 let mut muted_style = style;
18469 muted_style.highlight(fade_out);
18470
18471 let mut runs = SmallVec::<[(Range<usize>, HighlightStyle); 3]>::new();
18472 if range.start >= label.filter_range.end {
18473 if range.start > prev_end {
18474 runs.push((prev_end..range.start, fade_out));
18475 }
18476 runs.push((range.clone(), muted_style));
18477 } else if range.end <= label.filter_range.end {
18478 runs.push((range.clone(), style));
18479 } else {
18480 runs.push((range.start..label.filter_range.end, style));
18481 runs.push((label.filter_range.end..range.end, muted_style));
18482 }
18483 prev_end = cmp::max(prev_end, range.end);
18484
18485 if ix + 1 == label.runs.len() && label.text.len() > prev_end {
18486 runs.push((prev_end..label.text.len(), fade_out));
18487 }
18488
18489 runs
18490 })
18491}
18492
18493pub(crate) fn split_words(text: &str) -> impl std::iter::Iterator<Item = &str> + '_ {
18494 let mut prev_index = 0;
18495 let mut prev_codepoint: Option<char> = None;
18496 text.char_indices()
18497 .chain([(text.len(), '\0')])
18498 .filter_map(move |(index, codepoint)| {
18499 let prev_codepoint = prev_codepoint.replace(codepoint)?;
18500 let is_boundary = index == text.len()
18501 || !prev_codepoint.is_uppercase() && codepoint.is_uppercase()
18502 || !prev_codepoint.is_alphanumeric() && codepoint.is_alphanumeric();
18503 if is_boundary {
18504 let chunk = &text[prev_index..index];
18505 prev_index = index;
18506 Some(chunk)
18507 } else {
18508 None
18509 }
18510 })
18511}
18512
18513pub trait RangeToAnchorExt: Sized {
18514 fn to_anchors(self, snapshot: &MultiBufferSnapshot) -> Range<Anchor>;
18515
18516 fn to_display_points(self, snapshot: &EditorSnapshot) -> Range<DisplayPoint> {
18517 let anchor_range = self.to_anchors(&snapshot.buffer_snapshot);
18518 anchor_range.start.to_display_point(snapshot)..anchor_range.end.to_display_point(snapshot)
18519 }
18520}
18521
18522impl<T: ToOffset> RangeToAnchorExt for Range<T> {
18523 fn to_anchors(self, snapshot: &MultiBufferSnapshot) -> Range<Anchor> {
18524 let start_offset = self.start.to_offset(snapshot);
18525 let end_offset = self.end.to_offset(snapshot);
18526 if start_offset == end_offset {
18527 snapshot.anchor_before(start_offset)..snapshot.anchor_before(end_offset)
18528 } else {
18529 snapshot.anchor_after(self.start)..snapshot.anchor_before(self.end)
18530 }
18531 }
18532}
18533
18534pub trait RowExt {
18535 fn as_f32(&self) -> f32;
18536
18537 fn next_row(&self) -> Self;
18538
18539 fn previous_row(&self) -> Self;
18540
18541 fn minus(&self, other: Self) -> u32;
18542}
18543
18544impl RowExt for DisplayRow {
18545 fn as_f32(&self) -> f32 {
18546 self.0 as f32
18547 }
18548
18549 fn next_row(&self) -> Self {
18550 Self(self.0 + 1)
18551 }
18552
18553 fn previous_row(&self) -> Self {
18554 Self(self.0.saturating_sub(1))
18555 }
18556
18557 fn minus(&self, other: Self) -> u32 {
18558 self.0 - other.0
18559 }
18560}
18561
18562impl RowExt for MultiBufferRow {
18563 fn as_f32(&self) -> f32 {
18564 self.0 as f32
18565 }
18566
18567 fn next_row(&self) -> Self {
18568 Self(self.0 + 1)
18569 }
18570
18571 fn previous_row(&self) -> Self {
18572 Self(self.0.saturating_sub(1))
18573 }
18574
18575 fn minus(&self, other: Self) -> u32 {
18576 self.0 - other.0
18577 }
18578}
18579
18580trait RowRangeExt {
18581 type Row;
18582
18583 fn len(&self) -> usize;
18584
18585 fn iter_rows(&self) -> impl DoubleEndedIterator<Item = Self::Row>;
18586}
18587
18588impl RowRangeExt for Range<MultiBufferRow> {
18589 type Row = MultiBufferRow;
18590
18591 fn len(&self) -> usize {
18592 (self.end.0 - self.start.0) as usize
18593 }
18594
18595 fn iter_rows(&self) -> impl DoubleEndedIterator<Item = MultiBufferRow> {
18596 (self.start.0..self.end.0).map(MultiBufferRow)
18597 }
18598}
18599
18600impl RowRangeExt for Range<DisplayRow> {
18601 type Row = DisplayRow;
18602
18603 fn len(&self) -> usize {
18604 (self.end.0 - self.start.0) as usize
18605 }
18606
18607 fn iter_rows(&self) -> impl DoubleEndedIterator<Item = DisplayRow> {
18608 (self.start.0..self.end.0).map(DisplayRow)
18609 }
18610}
18611
18612/// If select range has more than one line, we
18613/// just point the cursor to range.start.
18614fn collapse_multiline_range(range: Range<Point>) -> Range<Point> {
18615 if range.start.row == range.end.row {
18616 range
18617 } else {
18618 range.start..range.start
18619 }
18620}
18621pub struct KillRing(ClipboardItem);
18622impl Global for KillRing {}
18623
18624const UPDATE_DEBOUNCE: Duration = Duration::from_millis(50);
18625
18626fn all_edits_insertions_or_deletions(
18627 edits: &Vec<(Range<Anchor>, String)>,
18628 snapshot: &MultiBufferSnapshot,
18629) -> bool {
18630 let mut all_insertions = true;
18631 let mut all_deletions = true;
18632
18633 for (range, new_text) in edits.iter() {
18634 let range_is_empty = range.to_offset(&snapshot).is_empty();
18635 let text_is_empty = new_text.is_empty();
18636
18637 if range_is_empty != text_is_empty {
18638 if range_is_empty {
18639 all_deletions = false;
18640 } else {
18641 all_insertions = false;
18642 }
18643 } else {
18644 return false;
18645 }
18646
18647 if !all_insertions && !all_deletions {
18648 return false;
18649 }
18650 }
18651 all_insertions || all_deletions
18652}
18653
18654struct MissingEditPredictionKeybindingTooltip;
18655
18656impl Render for MissingEditPredictionKeybindingTooltip {
18657 fn render(&mut self, window: &mut Window, cx: &mut Context<'_, Self>) -> impl IntoElement {
18658 ui::tooltip_container(window, cx, |container, _, cx| {
18659 container
18660 .flex_shrink_0()
18661 .max_w_80()
18662 .min_h(rems_from_px(124.))
18663 .justify_between()
18664 .child(
18665 v_flex()
18666 .flex_1()
18667 .text_ui_sm(cx)
18668 .child(Label::new("Conflict with Accept Keybinding"))
18669 .child("Your keymap currently overrides the default accept keybinding. To continue, assign one keybinding for the `editor::AcceptEditPrediction` action.")
18670 )
18671 .child(
18672 h_flex()
18673 .pb_1()
18674 .gap_1()
18675 .items_end()
18676 .w_full()
18677 .child(Button::new("open-keymap", "Assign Keybinding").size(ButtonSize::Compact).on_click(|_ev, window, cx| {
18678 window.dispatch_action(zed_actions::OpenKeymap.boxed_clone(), cx)
18679 }))
18680 .child(Button::new("see-docs", "See Docs").size(ButtonSize::Compact).on_click(|_ev, _window, cx| {
18681 cx.open_url("https://zed.dev/docs/completions#edit-predictions-missing-keybinding");
18682 })),
18683 )
18684 })
18685 }
18686}
18687
18688#[derive(Debug, Clone, Copy, PartialEq)]
18689pub struct LineHighlight {
18690 pub background: Background,
18691 pub border: Option<gpui::Hsla>,
18692}
18693
18694impl From<Hsla> for LineHighlight {
18695 fn from(hsla: Hsla) -> Self {
18696 Self {
18697 background: hsla.into(),
18698 border: None,
18699 }
18700 }
18701}
18702
18703impl From<Background> for LineHighlight {
18704 fn from(background: Background) -> Self {
18705 Self {
18706 background,
18707 border: None,
18708 }
18709 }
18710}